package cz.cvut.felk.cig.jcop.algorithm.noisingmethod; import cz.cvut.felk.cig.jcop.algorithm.BaseAlgorithm; import cz.cvut.felk.cig.jcop.algorithm.CannotContinueException; import cz.cvut.felk.cig.jcop.algorithm.ChainAlgorithm; import cz.cvut.felk.cig.jcop.algorithm.InvalidProblemException; import cz.cvut.felk.cig.jcop.problem.Configuration; import cz.cvut.felk.cig.jcop.problem.ObjectiveProblem; import cz.cvut.felk.cig.jcop.problem.Operation; import cz.cvut.felk.cig.jcop.util.JcopRandom; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Noising method. * <p/> * Negative noise is added to the fitness function. Value of the noise variable is decreasing - in the beginning * the algorithm tries to explore fitness surface with large jumps. As the noise decreases, algorithm * has more accurate information and tends to move only to better solutions. * * Source: * Handbook of Metaheuristics * http://www.springer.com/business+%26+management/operations+research/book/978-1-4419-1663-1 * * @author Oleg Kovarik */ public class NoisingMethod extends BaseAlgorithm implements ChainAlgorithm { private static final Logger LOG = LoggerFactory.getLogger(NoisingMethod.class); /** * Active configuration to be expanded */ protected Configuration activeConfiguration; /** * Fitness of active configuration */ protected double activeFitness; /** * Normalized fitness of active configuration */ protected double activeNormalizedFitness; /** * Current noise level */ protected double noise; /** * Noise coefficient */ protected double noiseCoefficient; /** * Backup of {@link #noise} in case algorithm will be used repeatedly */ protected double startingNoise; /** * Creates a new noising method instance with given noise coefficient and noise level. * * @param noiseCoefficient noise coefficient * @param startingNoise starting noise */ public NoisingMethod(double startingNoise, double noiseCoefficient) { this.startingNoise = startingNoise; this.noiseCoefficient = noiseCoefficient; } /** * Creates new annealing with default anneal coefficient (0.999) and starting temperature (10.0). */ public NoisingMethod() { this(1.0, 0.9); } public void init(ObjectiveProblem problem) throws InvalidProblemException { this.problem = problem; // NM requires either startingConfiguration or RandomStartingConfiguration problem if (!problem.hasStartingConfiguration() && !problem.hasRandomConfiguration()) throw new InvalidProblemException( "NoisingMethod algorithm requires either StartingConfigurationProblem or RandomConfigurationProblem"); // fetch starting configuration if (problem.hasRandomConfiguration()) { this.activeConfiguration = problem.getRandomConfiguration(); } else { this.activeConfiguration = problem.getStartingConfiguration(); } this.initCommon(); } public void init(ObjectiveProblem problem, Configuration activeConfiguration) { this.problem = problem; this.activeConfiguration = activeConfiguration; this.initCommon(); } /** * Part of init common to all initialization types. */ protected void initCommon () { this.fitness = problem.getDefaultFitness(); this.noise = this.startingNoise; this.setLabel("N=" + this.noise + ", C=" + this.noiseCoefficient); this.bestConfiguration = this.activeConfiguration; this.bestFitness = addNoise(this.fitness.getValue(this.activeConfiguration)); // calculate fitness this.activeFitness = this.bestFitness; this.activeNormalizedFitness = this.fitness.normalize(activeFitness); } public double addNoise(double fitness) { double result; if (fitness > 0) { result = ((JcopRandom.nextDouble() * noise) * fitness); } else { result = (1.0 + (JcopRandom.nextDouble() * noise)) * fitness; } return result; } public void optimize() throws CannotContinueException { // fetch random operation Operation operation = this.problem.getOperationIterator(this.activeConfiguration).getRandomOperation(); if (operation == null) throw new CannotContinueException("Unable to get random operation"); // expand to new configuration Configuration newConfiguration = operation.execute(this.activeConfiguration); // calculate fitness double newFitnessNoNoise = this.fitness.getValue(newConfiguration); double newFitness = addNoise(newFitnessNoNoise); double newNormalizedFitness = this.fitness.normalize(newFitness); // new configuration is better or passes temperature test if (newNormalizedFitness > this.activeNormalizedFitness) { // set new configuration as active this.activeConfiguration = newConfiguration; this.activeFitness = newFitness; this.activeNormalizedFitness = newNormalizedFitness; // if it is best, set it as best } if (newFitnessNoNoise > this.bestFitness) { LOG.debug("Better solution {}, {}", newFitnessNoNoise, newConfiguration); this.bestConfiguration = newConfiguration; this.bestFitness = newFitnessNoNoise; } // anneal this.noise *= this.noiseCoefficient; } }