Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
SimulatedAnnealing
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
1
 optimize
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Algorithm\Optimization
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Algorithm\Optimization;
16
17/**
18 * Perform simulated annealing (SA).
19 *
20 * @package phpOMS\Algorithm\Optimization
21 * @license OMS License 2.0
22 * @link    https://jingga.app
23 * @since   1.0.0
24 */
25class SimulatedAnnealing
26{
27    /**
28     * Constructor
29     *
30     * @since 1.0.0
31     * @codeCoverageIgnore
32     */
33    private function __construct()
34    {
35    }
36
37    /*
38    public static function costFunction($x)
39    {
40        return $x;
41    }
42
43    // can be many things, e.g. swapping parameters, increasing/decrising, random generation
44    public static function neighbor(array $generation, $parameterCount)
45    {
46        $newGeneration = $generation;
47        $randomIndex1 = \mt_rand(0, $parameterCount - 1);
48        $randomIndex2 = \mt_rand(0, $parameterCount - 1);
49
50        // Swap two cities in the route
51        $temp = $newGeneration[$randomIndex1];
52        $newGeneration[$randomIndex1] = $newGeneration[$randomIndex2];
53        $newGeneration[$randomIndex2] = $temp;
54
55        return $newGeneration;
56    }
57    */
58
59    // Simulated Annealing algorithm
60    // @todo allow to create a solution space (currently all soluctions need to be in space)
61    // @todo: currently only replacing generations, not altering them
62    /**
63     * Perform optimization
64     *
65     * @example See unit test for example use case
66     *
67     * @param array    $space              List of all elements with ther parameters (i.e. list of "objects" as arrays).
68     *                                     The constraints are defined as array values.
69     * @param int      $initialTemperature Starting temperature
70     * @param \Closure $costFunction       Fitness function calculates score/feasability of solution
71     * @param \Closure $neighbor           Neighbor function to find a new solution/neighbor
72     * @param float    $coolingRate        Rate at which cooling takes place
73     * @param int      $iterations         Number of iterations
74     *
75     * @return array{solutions:array, costs:float[]}
76     *
77     * @since 1.0.0
78     */
79    public function optimize(
80        array $space,
81        int $initialTemperature,
82        \Closure $costFunction,
83        \Closure $neighbor,
84        float $coolingRate = 0.98,
85        int $iterations = 1000
86    ) : array
87    {
88        $parameterCount    = \count($space);
89        $currentGeneration = \reset($space);
90
91        $currentCost = ($costFunction)($currentGeneration);
92
93        for ($i = 0; $i < $iterations; ++$i) {
94            $newGeneration = ($neighbor)($currentGeneration, $parameterCount);
95
96            $newCost = ($costFunction)($newGeneration);
97
98            $temperature = $initialTemperature * \pow($coolingRate, $i);
99
100            if ($newCost < $currentCost
101                || \mt_rand() / \mt_getrandmax() < \exp(($currentCost - $newCost) / $temperature)
102            ) {
103                $currentGeneration = $newGeneration;
104                $currentCost       = $newCost;
105            }
106        }
107
108        return [
109            'solutions' => $currentGeneration,
110            'costs'     => $currentCost,
111        ];
112    }
113}