001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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 */ 020package io.jenetics.engine; 021 022import java.util.concurrent.atomic.AtomicBoolean; 023 024import io.jenetics.Gene; 025import 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 * <pre>{@code 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 * }</pre> 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 */ 060public 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}