anneal method
Starts the simulated annealing process and returns the best solution found.
- isRecursive: Flag used to call the method recursively if the algorithm converges towards a local minimum. (The algorithm keeps track of the lowest energy values previously visited.)
- ratio: Factor reducing the length of the initial temperature sequence during recursive calls. The parameter is used to model repeated annealing cycles with decreasing initial temperature.
- Note:
0.0 < ratio < 1.0
.
Implementation
Future<List<num>> anneal({
bool isRecursive = false,
num ratio = 0.5,
bool isVerbose = false,
}) async {
/// Initialize parameters:
final temperatures = await this.temperatures;
final perturbationMagnitudes = await this.perturbationMagnitudes;
int nInner(num t) => markovChainLength(t,
tStart: temperatures.first,
tEnd: temperatures.last,
chainLengthStart: innerIterationsStart,
chainLengthEnd: innerIterationsEnd);
num dE = 0;
if (_recursionCounter == 0) {
_t = temperatures.first;
_deltaPosition = perturbationMagnitudes.first;
prepareLog();
recordLog();
}
// During the first iteration pow(ratio.abs(), _recursionCounter) = 1.0
// and therefore i = 0.
var i = (temperatures.length * (1.0 - pow(ratio.abs(), _recursionCounter)))
.toInt();
if (_recursionCounter > 0 && isVerbose) {
print('Restarted annealing at:');
print(' temperature: ${temperatures[i]},');
print(' position: $_currentMinPosition, ');
print(' energy: $_currentMinEnergy');
}
++_recursionCounter;
// Outer iteration loop.
for (i; i < temperatures.length; i++) {
_t = temperatures[i];
_deltaPosition = perturbationMagnitudes[i];
// Inner iteration loop.
for (var j = 0; j < nInner(_t); j++) {
// Choose next random point and calculate energy difference.
dE = field.perturb(
_currentMinPosition,
_deltaPosition,
) -
_currentMinEnergy;
if (dE < 0) {
_currentMinEnergy = field.value;
_currentMinPosition = field.position;
_acceptanceProbability = 1.0;
} else {
_acceptanceProbability = exp(-dE / _t);
if (_acceptanceProbability > Interval.random.nextDouble()) {
_currentMinEnergy = field.value;
_currentMinPosition = field.position;
}
}
recordLog();
}
}
if (globalMinEnergy < _currentMinEnergy) {
if ((globalMinPosition - _currentMinPosition).abs() < _deltaPositionEnd) {
if (isVerbose) {
print('------------------------------------------------');
print('E_min_global($globalMinPosition) = $globalMinEnergy ');
print('E_min($_currentMinPosition) = $_currentMinEnergy!');
print('x_min_global - x_min_local = '
'${globalMinPosition - _currentMinPosition}.');
print('deltaPositionEnd = $deltaPositionEnd');
print('Returning global minimum solution!');
print('');
}
// return globalMinPosition;
}
_currentMinPosition = globalMinPosition;
_currentMinEnergy = globalMinEnergy;
if (isRecursive) {
_currentMinPosition = await anneal(
isRecursive: isRecursive,
ratio: ratio,
);
}
}
return _currentMinPosition;
}