FitnessNullifier.java
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     extends Gene<?, G>,
062     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 }