/**
*
*/
package vroom.common.utilities.optimization;
import java.util.HashSet;
import java.util.Set;
import umontreal.iro.lecuyer.rng.RandomStream;
/**
* <code>SAAcceptanceCriterion</code> is an implementation of {@link IAcceptanceCriterion} based on a simulated
* annealing acceptance criterion.
* <p>
* Creation date: May 18, 2011 - 4:40:02 PM
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a
* href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0
*/
public class SAAcceptanceCriterion implements IAcceptanceCriterion {
/** the accept already visited solution flag **/
private final boolean mAcceptAlreadyVisited;
/**
* Getter for the accept already visited solution flag
*
* @return <code>true</code> if the criterion should accept solutions that were already visited
*/
public boolean isAcceptAlreadyVisited() {
return this.mAcceptAlreadyVisited;
}
/** optimization sense **/
private final OptimizationSense mOptimizationSense;
/**
* Getter for optimization sense
*
* @return the optimization sense
*/
public OptimizationSense getOptimizationSense() {
return mOptimizationSense;
}
/** the random stream */
private final RandomStream mRandomStream;
/** The cooling rate */
private final double mCoolingRate;
/** The current temperature */
private double mTemp;
/** The initial temperature */
private final double mInitialTemp;
/** The set of already visited solutions */
private final Set<Integer> mVisitedSolutions;
/**
* Creates a new <code>SAAcceptanceCriterion</code>
*
* @param optimizationSense
* the optimization sense
* @param rndStream
* the random stream
* @param coolingRate
* the SA cooling rate
* @param initTemp
* the initial temperature
* @param acceptRepeated
* <code>true</code> if the criterion should accept solutions that were already visited
*/
public SAAcceptanceCriterion(OptimizationSense optimizationSense, RandomStream rndStream, double coolingRate,
double initTemp, boolean acceptRepeated) {
mOptimizationSense = optimizationSense;
mRandomStream = rndStream;
mCoolingRate = coolingRate;
mTemp = initTemp;
mInitialTemp = initTemp;
mAcceptAlreadyVisited = acceptRepeated;
mVisitedSolutions = mAcceptAlreadyVisited ? null : new HashSet<Integer>();
}
/**
* Creates a new parametric <code>SAAcceptanceCriterion</code>.
* <p>
* The initial temperature <code>T<sub>0</sub></code> is chosen such as a solution with value <code>z*w</code> is
* accepted with probability <code>p</code> <br/>
* The cooling rate <code>c</code> is defined such as the temperature after <code>n</code> iterations is equal to
* <code>alpha*T<sub>0</sub></code>
* </p>
* <p>
* More precisely: <code>T<sub>0</sub> = -w*z/log(p)</code> and <code>c=alpha<sup>1/n</sup></code>
* </p>
*
* @param optimizationSense
* the optimization sense
* @param rndStream
* the random stream
* @param z
* the objective value of the initial solution
* @param w
* the reference degradation of the objective function (between 0 and 1, e.g., 0.05 for 5%)
* @param p
* the probability of accepting a solution with the reference degradation (between 0 and 1, e.g., 0.5 for
* 50%)
* @param n
* the expected number of iterations
* @param alpha
* the desired proportion of initial temperature at the last iteration (between 0 and 1, e.g. 0.002 for
* 0.2%)
* @param acceptRepeated
* <code>true</code> if the criterion should accept solutions that were already visited
*/
public SAAcceptanceCriterion(OptimizationSense optimizationSense, RandomStream rndStream, double z, double w,
double p, int n, double alpha, boolean acceptRepeated) {
this(optimizationSense, rndStream, Math.pow(alpha, 1d / n), -w * z / Math.log(p), acceptRepeated);
}
/*
* (non-Javadoc)
* @see vroom.common.utilities.optimization.IAcceptanceCriterion#initialize()
*/
@Override
public void initialize() {
mTemp = mInitialTemp;
}
/*
* (non-Javadoc)
* @see vroom.common.utilities.optimization.IAcceptanceCriterion#reset()
*/
@Override
public void reset() {
mTemp = mInitialTemp;
}
/**
* Check the improvement against the current temperature and updates the temperature
*
* @param improvement
* the value of the improvement
* @return <code>true</code> if a solution with the given <code>improvement</code> should be accepted
*/
public boolean accept(double improvement) {
boolean accept = mRandomStream.nextDouble() < Math.exp(improvement / mTemp);
// Update temperature
mTemp *= mCoolingRate;
return accept;
}
/*
* (non-Javadoc)
* @see
* vroom.common.utilities.optimization.IAcceptanceCriterion#accept(vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.ISolution)
*/
@Override
public boolean accept(ISolution oldSolution, ISolution newSolution) {
boolean accept = (isAcceptAlreadyVisited() || !mVisitedSolutions.contains(newSolution.hashCode()))
&& accept(mOptimizationSense.getImprovement(oldSolution.getObjectiveValue(),
newSolution.getObjectiveValue()));
// We assume that the solution WILL be accepted
if (!isAcceptAlreadyVisited() && accept)
mVisitedSolutions.add(newSolution.hashCode());
return accept;
}
@Override
public double getImprovement(ISolution oldSolution, ISolution newSolution) {
return mOptimizationSense.getImprovement(oldSolution.getObjectiveValue(), newSolution.getObjectiveValue());
}
/*
* (non-Javadoc)
* @see
* vroom.common.utilities.optimization.IAcceptanceCriterion#accept(vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IMove)
*/
@Override
public boolean accept(ISolution solution, IMove move) {
return accept(move.getImprovement());
}
/*
* (non-Javadoc)
* @see
* vroom.common.utilities.optimization.IAcceptanceCriterion#accept(vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.INeighborhood, vroom.common.utilities.optimization.IMove)
*/
@Override
public boolean accept(ISolution solution, INeighborhood<?, ?> neighborhood, IMove move) {
return accept(move.getImprovement());
}
/**
* Returns the current temperature
*
* @return the current temperature
*/
public double getTemperature() {
return mTemp;
}
/**
* Getter for <code>coolingRate</code>
*
* @return the coolingRate
*/
public double getCoolingRate() {
return mCoolingRate;
}
/**
* Getter for <code>initialTemp</code>
*
* @return the initialTemp
*/
public double getInitialTemp() {
return mInitialTemp;
}
@Override
public String toString() {
return String.format("SA(t:%s t0:%s c:%s)", getTemperature(), getInitialTemp(), getCoolingRate());
}
@Override
public SAAcceptanceCriterion clone() {
SAAcceptanceCriterion clone = new SAAcceptanceCriterion(mOptimizationSense, mRandomStream, mCoolingRate,
mInitialTemp, mAcceptAlreadyVisited);
clone.mTemp = mTemp;
if (mVisitedSolutions != null)
clone.mVisitedSolutions.addAll(mVisitedSolutions);
return clone;
}
}