ja / en
Overview

SimulatedAnnealing

2024/08/13
3 min read

Purpose

Find an xDx \in D such that the return value of f:DRf: D \rightarrow \mathbb{R} is maximized in the domain DD. This is Simulated Annealing.

Time Complexity

O(epochlog(target temp)log(init temp)log(cooling coef))O\left(\text{epoch} \left \lceil \dfrac{\log \text{(target temp)} - \log {\text{(init temp)}}}{\log \text{(cooling coef)}} \right \rceil \right )

Usage

Declaration

struct State{ // Structure representing the state
double x;
// f(x) = -x^4 + x^3 + x^2 + x
// max f(x): 2.33.... (x = 1.28...)
double eval(){ // Evaluation function
return -pow(x,4)+pow(x,3)+pow(x,2)+x;
}
void modify(){ // State transition
x += ((double)rand() / (RAND_MAX))-0.5;
}
};
ESAnnealing<State> sa;
// Initialize the state inside sa
sa.state.x=0;

Please rewrite the contents of State according to your purpose.

Hyperparameter Setup & Annealing

// (trials per epoch, initial temperature, target temperature, cooling coefficient)
sa.annealing(100,100000,0.1,0.99);

Result Reference

// The result can be referenced by 'state' in the class
std::cout << "Solution: " << "f(x) = " << sa.state.eval() << ", x = " << sa.state.x << std::endl;

Implementation

template <class T>
class ESAnnealing
{
public:
T state;
void annealing(double epoch, double temperature, double temperature_target, double coolingcoef, unsigned int seed = 42, bool verbose = true)
{
assert(0 <= coolingcoef and coolingcoef < 1);
assert(temperature >= temperature_target);
srand(seed);
double initscore = state.eval();
double beststate_score = initscore;
T beststate = state;
int estimeted_epoch = std::ceil((std::log(temperature_target) - std::log(temperature)) / std::log(coolingcoef));
if (verbose)
std::cout << std::fixed << std::setprecision(6)
<< "ESAnnealing started with:" << "\n"
<< "| epoch " << epoch << "\n"
<< "| temperature " << temperature << "\n"
<< "| target temp " << temperature_target << "\n"
<< "| coolingcoef " << coolingcoef << "\n"
<< "| seed " << seed << "\n"
<< "| est epoch " << estimeted_epoch << "\n"
<< std::flush;
int cnt = 0;
while (temperature > temperature_target)
{
for (int i = 0; i < epoch; i++)
{
T newstate = state;
newstate.modify();
double statescore = state.eval();
double newstatescore = newstate.eval();
double delta = newstatescore - statescore;
if (0 < delta)
{
state = newstate;
}
else if (frand() > std::exp(delta / temperature))
{
state = newstate;
}
if (beststate_score < newstatescore)
{
beststate_score = newstatescore;
beststate = newstate;
}
}
temperature *= coolingcoef;
if (verbose)
std::cout << cnt << "/" << estimeted_epoch - 1 << " temp: " << temperature << " eval: " << beststate_score << std::endl;
cnt++;
}
state = beststate;
if (verbose)
std::cout << "annealed: " << initscore << "->" << beststate_score << std::endl;
}
private:
double frand()
{
return ((double)rand() / (RAND_MAX));
}
};

from github.com/romophic/ESAnnealing

Verify

//TODO