001 /*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020 package io.jenetics.engine;
021
022 import java.util.concurrent.atomic.AtomicBoolean;
023
024 import io.jenetics.Gene;
025 import io.jenetics.Phenotype;
026
027 /**
028 * This class allows forcing a reevaluation of the fitness function. A
029 * reevaluation is necessary if the fitness function changes. Changing the
030 * fitness function is not the usual use case, but is necessary for some
031 * problems, like symbolic regression analyses with changing input data (time
032 * series).
033 *
034 * {@snippet lang="java":
035 * final var nullifier = new FitnessNullifier<DoubleGene, Double>();
036 *
037 * final Engine<DoubleGene, Double> engine = Engine.builder(problem)
038 * .interceptor(nullifier)
039 * .build();
040 *
041 * // Invalidate fitness value by calling the 'nullifyFitness' method,
042 * // possible from a different thread. This forces the reevaluation of
043 * // the fitness values at the start of the next generation.
044 * nullifier.nullifyFitness();
045 * }
046 *
047 * @implNote
048 * This interceptor is thread-safe and can be used from different threads. No
049 * additional synchronization is needed.
050 *
051 * @see Phenotype#nullifyFitness()
052 *
053 * @param <G> the gene type
054 * @param <C> the fitness result type
055 *
056 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
057 * @since 6.0
058 * @version 6.0
059 */
060 public final class FitnessNullifier<
061 G extends Gene<?, G>,
062 C extends Comparable<? super C>
063 >
064 implements EvolutionInterceptor<G, C>
065 {
066
067 private final AtomicBoolean _invalid = new AtomicBoolean(false);
068
069 /**
070 * Nullifies the fitness values of the population, if requested. The
071 * <em>nullification flag</em> is reset after this call. Two consecutive
072 * calls of this method might lead to two different results.
073 *
074 * @see #nullifyFitness()
075 *
076 * @param start the evolution start object
077 * @return the evolution start object with the nullified fitness values,
078 * if the nullification has been triggered
079 */
080 @Override
081 public EvolutionStart<G, C> before(final EvolutionStart<G, C> start) {
082 final boolean invalid = _invalid.getAndSet(false);
083 return invalid ? invalidate(start) : start;
084 }
085
086 private EvolutionStart<G, C> invalidate(final EvolutionStart<G, C> start) {
087 return EvolutionStart.of(
088 start.population().map(Phenotype::nullifyFitness),
089 start.generation()
090 );
091 }
092
093 /**
094 * Triggers the nullification of the fitness values of the population for
095 * the next generation.
096 *
097 * @see #before(EvolutionStart)
098 *
099 * @return {@code true} if the <em>nullification</em> request will trigger
100 * a new fitness nullification. @{code false} if the fitness
101 * <em>nullification</em> has been requested before, without
102 * actually executing it.
103 */
104 public boolean nullifyFitness() {
105 return !_invalid.getAndSet(true);
106 }
107
108 }
|