/**
* Copyright (c) 2013 Oculus Info Inc.
* http://www.oculusinfo.com/
*
* Released under the MIT License.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package spimedb.cluster.search.stochastic;
import spimedb.cluster.search.ObjectiveFunction;
import spimedb.cluster.search.SearchException;
import spimedb.cluster.search.Solution;
import java.util.Random;
/***
* Simulated Annealing for stochastically searching for optimum solution given an objective function.
*
* A global optimum is not guaranteed and the solution may converge on a local optimum, however the stochastic nature
* can avoid becoming "stuck" in a local optimum by accepting inferior solutions with a transition probability.
*
* @author slangevin
*
*/
public class SimulatedAnnealing {
// initial temperature
protected double initTemp = 20;
// current temperature
protected double curTemp = 20;
// minimum temperature
protected double minTemp = 1e-10;
// change in temperature at every run
protected double coolingRate = 0.005;
// maximum search iterations to perform
protected int maxIterations = 10000;
// score of best solution found
protected double score = 0;
// best solution found
protected Solution solution;
// Objective function used to calculate score of a solution
protected ObjectiveFunction objfunc;
// Random number generated for accepting new solutions
protected final Random rnd = new Random();
public SimulatedAnnealing(ObjectiveFunction func) {
setObjectiveFunction(func);
}
private boolean acceptNewSolution(double newScore, double curScore) {
double delta = curScore - newScore;
// new solution is better, accept it
if (newScore < curScore) return true;
// solution is inferior, randomly determine whether to move to the new solution
double p = Math.exp(delta / curTemp);
return ( rnd.nextDouble() < p );
}
private void coolTemperature(int step) {
// Alternate: reduce temperature using a linear schedule
// curTemp -= curTemp * coolingRate;
// Annealing scheme cools temperature as a function of time and cooling rate
curTemp = initTemp * Math.exp((-1.0) * coolingRate * step);
}
public Solution search() throws SearchException {
return search(false);
}
public Solution search(boolean outputProgress) throws SearchException {
if (solution == null) throw new SearchException("You must specifiy an initial solution.");
// start with the intial solution
Solution curSolution = solution;
// calculate the initial solution score and init our best score
score = objfunc.score(curSolution);
if (outputProgress) printSolution("Initial Solution", solution, score);
// set the current temperature to the initial temperature
curTemp = initTemp;
// set the current score to the initial solution score
double curScore = score;
int i = 0;
// iteratively search for solution
while (i < maxIterations && curTemp > minTemp) {
// find a neighbor of the current solution
Solution newSolution = curSolution.neighbor(curTemp);
// calculate the score of the new solution
double newScore = objfunc.score(newSolution);
// move to the new solution if it is accepted
if ( acceptNewSolution(newScore, curScore) ) {
curSolution = newSolution;
curScore = newScore;
if (outputProgress) printSolution("Accepting New Current Solution", curSolution, curScore);
}
// if the current solution is better then the best, switch to it
if (curScore < score) {
score = curScore;
solution = curSolution;
if (outputProgress) printSolution("* New Best Solution Found * ", solution, score);
}
// reduce the temperature
coolTemperature(i);
// move to next iteration
i++;
}
return solution;
}
private static void printSolution(String msg, Solution sln, double s) {
System.out.println("--------------------");
System.out.println(msg);
System.out.println("Score: " + s);
System.out.println(sln.toString());
System.out.println("--------------------");
}
public ObjectiveFunction getObjectiveFunction() {
return this.objfunc;
}
public void setObjectiveFunction(ObjectiveFunction func) {
this.objfunc = func;
}
public void setInitialSolution(Solution initial) {
this.solution = initial;
}
public void setCoolingRate(double rate) {
this.coolingRate = rate;
}
public void setMaxIterations(int max) {
this.maxIterations = max;
}
public void setInitialTemperature(double temp) {
this.initTemp = temp;
}
public void setMinTemperature(double min) {
this.minTemp = min;
}
}