anneal method

Future<List<num>> anneal({
  1. bool isRecursive = false,
  2. num ratio = 0.5,
  3. bool isVerbose = false,
})

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;
}