Informed (Heuristic) Search

Simulated Annealing Search

Simulated Annealing (SA) is an optimization algorithm inspired by the annealing process in metallurgy, where materials are heated and slowly cooled to reach a stable state with minimal energy. It is used to find approximate solutions to optimization problems by iteratively exploring potential solutions and occasionally accepting worse solutions to escape local optima.
The goal in this example here is to find the minimum point of $f(x) = x²$, which is clearly at $x = 0$, where the function value is also $0$.

Example of class SimulatedAnnealing:

 
<?php

namespace app\classes\search;

class 
SimulatedAnnealing {
    private 
$temperature;
    private 
$coolingRate;
    private 
$stopTemperature;
    private 
$iterationLog = [];

    public function 
__construct($initialTemperature$coolingRate$stopTemperature) {
        
$this->temperature $initialTemperature;
        
$this->coolingRate $coolingRate;
        
$this->stopTemperature $stopTemperature;
    }

    private function 
objectiveFunction($x) {
        
// Example: Finding the minimum of x^2
        
return pow($x2);
    }

    private function 
getRandomNeighbor($x) {
        
// Generate a small random step
        
return $x + ((rand(01000) / 1000) - 0.5) * 2;
    }

    private function 
acceptanceProbability($currentEnergy$newEnergy) {
        if (
$newEnergy $currentEnergy) {
            return 
1.0;
        }
        return 
exp(($currentEnergy $newEnergy) / $this->temperature);
    }

    public function 
optimize($initialSolutionint $precision) {
        
$currentSolution $initialSolution;
        
$currentEnergy $this->objectiveFunction($currentSolution);
        
$iteration 0;

        
// Log initial state
        
$this->logIteration($iteration$currentSolution$currentEnergy$this->temperaturenullnullfalse);

        while (
$this->temperature $this->stopTemperature) {
            
$iteration++;
            
$newSolution $this->getRandomNeighbor($currentSolution);
            
$newEnergy $this->objectiveFunction($newSolution);

            
$acceptanceProbability $this->acceptanceProbability($currentEnergy$newEnergy);
            
$accepted false;

            if (
$acceptanceProbability mt_rand() / mt_getrandmax()) {
                
$accepted true;
                
$currentSolution $newSolution;
                
$currentEnergy $newEnergy;
            }

            
// Log this iteration
            
$this->logIteration(
                
$iteration,
                
$currentSolution,
                
$currentEnergy,
                
$this->temperature,
                
$newSolution,
                
$newEnergy,
                
$accepted
            
);

            
$this->temperature *= $this->coolingRate;
        }

        return 
round($currentSolution$precision);
    }

    
/**
     * Logs information about each iteration
     *
     * @param int $iteration Iteration number
     * @param float $currentSolution Current solution
     * @param float $currentEnergy Energy of current solution
     * @param float $temperature Current temperature
     * @param float|null $triedSolution Solution that was tried
     * @param float|null $triedEnergy Energy of tried solution
     * @param bool $accepted Whether the tried solution was accepted
     */
    
private function logIteration(
        
$iteration,
        
$currentSolution,
        
$currentEnergy,
        
$temperature,
        
$triedSolution,
        
$triedEnergy,
        
$accepted
    
) {
        
$this->iterationLog[] = [
            
'iteration' => $iteration,
            
'solution' => $currentSolution,
            
'energy' => $currentEnergy,
            
'temperature' => $temperature,
            
'tried_solution' => $triedSolution,
            
'tried_energy' => $triedEnergy,
            
'accepted' => $accepted,
        ];
    }

    
/**
     * Returns the log of iterations
     *
     * @return array Array of iteration data
     */
    
public function getIterationLog() {
        return 
$this->iterationLog;
    }

    
/**
     * Format the iteration log as a string
     *
     * @param bool $detailed Whether to include detailed information about tried solutions
     * @return string Formatted log as a string
     */
    
public function printIterationLog($detailed false) {
        
$output "Simulated Annealing Iterations Log:\n\n";
        
$output .= str_pad('#'3) . str_pad('Solution'12) . str_pad('Energy'12) .
            
str_pad('Temp'12);

        if (
$detailed) {
            
$output .= str_pad('Tried'11) . str_pad('Tried Energy'15) . 'Accepted';
        }

        
$output .= "\n" str_repeat('-'$detailed 75 50) . "\n";

        foreach (
$this->iterationLog as $log) {
            
$output .= str_pad($log['iteration']. '. '3) .
                
str_pad(number_format($log['solution'], 3), 12) .
                
str_pad(number_format($log['energy'], 3), 12) .
                
str_pad(number_format($log['temperature'], 3), 12);

            if (
$detailed && $log['iteration'] > 0) {
                
$output .= str_pad(number_format($log['tried_solution'], 3), 11) .
                    
str_pad(number_format($log['tried_energy'], 3), 15) .
                    (
$log['accepted'] ? 'Yes' 'No');
            }

            
$output .= "\n";
        }

        return 
$output;
    }
}