/* * Copyright 2007-2013 * Licensed under GNU Lesser General Public License * * This file is part of EpochX: genetic programming software for research * * EpochX is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EpochX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with EpochX. If not, see <http://www.gnu.org/licenses/>. * * The latest version is available from: http://www.epochx.org */ package org.epochx.fitness; import org.epochx.AbstractFitnessFunction; import org.epochx.Config; import org.epochx.Fitness; import org.epochx.Individual; import org.epochx.Config.ConfigKey; import org.epochx.event.ConfigEvent; import org.epochx.event.EventManager; import org.epochx.event.Listener; /** * A fitness function for that calculates and assigns adjusted fitness scores. The fitness scores * are calculated by passing evaluation to a delegate fitness function, which must return an * instance of <code>DoubleFitness.Minimise</code>. This value is then converted to standardised * fitness by removing any offset of the values (so the minimum possible score is 0.0) and then * converted to adjusted fitness using the following formula: * * <code><blockquote> * adjusted-fitness = 1 / (1 + standardised-fitness) * </blockquote></code> * * When using this fitness function the {@link #MINIMUM_FITNESS_SCORE} config option can optionally be * set, or the same value set using the mutator method provided. If set, the MINIMUM_FITNESS_SCORE is * used to convert the delegate's calculated values to standardised fitness. For example, if the * minimum fitness score is 10.0, and the delegate assigns a fitness value of 11.0, then the * standardised fitness will be 1.0. In the same example, the adjusted fitness would then be calculated * as 0.5 using the formula above. * * If the MINIMUM_FITNESS_SCORE config option is not set, then it is assumed the delegates calculation * is already standardised. * * @since 2.0 */ public class AdjustedFitnessFunction extends AbstractFitnessFunction implements Listener<ConfigEvent> { /** * The key for setting the minimum fitness score possible, used when calculating * the adjusted fitness score. */ public static final ConfigKey<Double> MINIMUM_FITNESS_SCORE = new ConfigKey<Double>(); // The delegate that fitness calculations will be passed to private AbstractFitnessFunction delegate; // Configuration settings private double minFitnessScore; /** * Constructs a <code>AdjustedFitnessFunction</code> fitness function with control parameters * automatically loaded from the config. */ public AdjustedFitnessFunction(AbstractFitnessFunction delegate) { this(delegate, true); } /** * Constructs a <code>AdjustedFitnessFunction</code> fitness function with control parameters initially * loaded from the config. If the <code>autoConfig</code> argument is set to <code>true</code> * then the configuration will be automatically updated when the config is modified. * * @param autoConfig whether this operator should automatically update its * configuration settings from the config */ public AdjustedFitnessFunction(AbstractFitnessFunction delegate, boolean autoConfig) { setup(); this.delegate = delegate; if (autoConfig) { EventManager.getInstance().add(ConfigEvent.class, this); } } /** * Sets up this operator with the appropriate configuration settings. * This method is called whenever a <code>ConfigEvent</code> occurs for a * change in any of the following configuration parameters: * <ul> * <li>{@link #MINIMUM_FITNESS_SCORE} * </ul> */ protected void setup() { minFitnessScore = Config.getInstance().get(MINIMUM_FITNESS_SCORE, minFitnessScore); } /** * Receives configuration events and triggers this fitness function to * configure its parameters if the <code>ConfigEvent</code> is for one of * its required parameters. * * @param event {@inheritDoc} */ @Override public void onEvent(ConfigEvent event) { if (event.isKindOf(MINIMUM_FITNESS_SCORE)) { setup(); } } /** * Calculates the adjusted fitness of the given individual. The adjusted fitness is a double * value between 0.0 and 1.0, where 1.0 is a correct solution. The evaluation of individuals is * delegated to the <code>AbstractFitnessFunction</code> supplied to the constructor as the * delegate. The values returned by the delegate are converted to adjusted fitness before being * returned. The fitness returned will be an instance of DoubleFitness.Maximise. * * @param individual the program to evaluate * @return a DoubleFitness.Maximise which is the adjusted fitness of the given individual * @throws IllegalStateException if the delegate returns anything other than an instance of * DoubleFitness.Minimise */ @Override public DoubleFitness.Maximise evaluate(Individual individual) { Fitness fitness = delegate.evaluate(individual); if (!(fitness instanceof DoubleFitness.Minimise)) { throw new IllegalStateException("Delegate must return instances of DoubleFitness.Minimise"); } DoubleFitness.Minimise minimised = (DoubleFitness.Minimise) fitness; // Convert to standardised fitness (minimised with 0.0 as lowest value) double standardised = minimised.getValue() - minFitnessScore; return new DoubleFitness.Maximise(adjustedFitness(standardised)); } /** * Converts the given standardised fitness score to an adjusted fitness score, between 0.0 and 1.0. * This is calculated as <code>1.0 / (1.0 + standardised)</code> as defined by Koza in Genetic Programming. * * @param standardised the standardised fitness score - this must be 0.0 or greater * @return a double which is the adjusted fitness score between 0.0 and 1.0, where 1.0 is more fit * than 0.0 */ protected double adjustedFitness(double standardised) { if (standardised < 0.0) { throw new IllegalArgumentException("Standardised fitness must be 0.0 or greater"); } double adjusted = 1.0 / (1.0 + standardised); return adjusted; } /** * Returns the minimum fitness score that an individual can obtain using the delegate. This is * used to convert the delegate's fitness scores to standardised fitness by removing the offset. * * @return the minimum fitness score that the delegate can assign */ public double getMinimumFitnessScore() { return minFitnessScore; } /** * Sets the minimum fitness score than an individual can be assigned by the delegate. this is * used to convert the delegate's fitness score to standardised fitness by removing the amount the * scores are offset from 0.0. * * If automatic configuration is enabled then any value set here will be overwritten by the * {@link #MINIMUM_FITNESS_SCORE} configuration setting on the next config event. * * @param minFitnessScore the minimum fitness score that the delegate can assign */ public void setMinimumFitnessScore(double minFitnessScore) { this.minFitnessScore = minFitnessScore; } }