1 | <?php |
||
19 | class KemenyYoung extends Method implements MethodInterface |
||
20 | { |
||
21 | // Method Name |
||
22 | public const METHOD_NAME = ['Kemeny–Young','Kemeny-Young','Kemeny Young','KemenyYoung','Kemeny rule','VoteFair popularity ranking','Maximum Likelihood Method','Median Relation']; |
||
23 | |||
24 | // Method Name |
||
25 | public const CONFLICT_WARNING_CODE = 42; |
||
26 | |||
27 | // Limits |
||
28 | /* If you need to put it on 9, You must use ini_set('memory_limit','1024M'); before. The first use will be slower because Kemeny-Young will work without pre-calculated data of Permutations. |
||
29 | Do not try to go to 10, it is not viable! */ |
||
30 | public static $_maxCandidates = 8; |
||
31 | |||
32 | // Cache |
||
33 | public static $useCache = true; |
||
34 | |||
35 | // Kemeny Young |
||
36 | protected $_PossibleRanking; |
||
37 | protected $_RankingScore; |
||
38 | |||
39 | |||
40 | |||
41 | /////////// PUBLIC /////////// |
||
42 | |||
43 | |||
44 | // Get the Kemeny ranking |
||
45 | 5 | public function getResult () : Result |
|
46 | { |
||
47 | // Cache |
||
48 | 5 | if ( $this->_Result === null ) : |
|
49 | 5 | $this->calcPossibleRanking(); |
|
50 | 5 | $this->calcRankingScore(); |
|
51 | 5 | $this->makeRanking(); |
|
52 | 5 | $this->conflictInfos(); |
|
53 | endif; |
||
54 | |||
55 | // Return |
||
56 | 5 | return $this->_Result; |
|
57 | } |
||
58 | |||
59 | |||
60 | 5 | protected function getStats () : array |
|
61 | { |
||
62 | 5 | $explicit = []; |
|
63 | |||
64 | 5 | foreach ($this->_PossibleRanking as $key => $value) : |
|
65 | 5 | $explicit[$key] = $value; |
|
66 | |||
67 | // Human readable |
||
68 | 5 | foreach ($explicit[$key] as &$candidate_key) : |
|
69 | 5 | $candidate_key = $this->_selfElection->getCandidateId($candidate_key); |
|
70 | endforeach; |
||
71 | |||
72 | 5 | $explicit[$key]['score'] = $this->_RankingScore[$key]; |
|
73 | endforeach; |
||
74 | |||
75 | 5 | $stats = []; |
|
76 | 5 | $stats['bestScore'] = max($this->_RankingScore); |
|
77 | 5 | $stats['rankingScore'] = $explicit; |
|
78 | |||
79 | 5 | return $stats; |
|
80 | } |
||
81 | |||
82 | 5 | protected function conflictInfos () : void |
|
83 | { |
||
84 | 5 | $max = max($this->_RankingScore); |
|
85 | |||
86 | 5 | $conflict = -1; |
|
87 | 5 | foreach ($this->_RankingScore as $value) : |
|
88 | 5 | if ($value === $max) : |
|
89 | 5 | $conflict++; |
|
90 | endif; |
||
91 | endforeach; |
||
92 | |||
93 | 5 | if ($conflict > 0) : |
|
94 | 1 | $this->_Result->addWarning(self::CONFLICT_WARNING_CODE, ($conflict + 1).';'.max($this->_RankingScore) ); |
|
95 | endif; |
||
96 | 5 | } |
|
97 | |||
98 | |||
99 | /////////// COMPUTE /////////// |
||
100 | |||
101 | |||
102 | //:: Kemeny-Young ALGORITHM. ::// |
||
103 | |||
104 | 5 | protected function calcPossibleRanking () : void |
|
126 | |||
127 | 1 | protected function doPossibleRanking (?string $path = null) |
|
137 | |||
138 | 5 | protected function calcRankingScore () : void |
|
159 | |||
160 | |||
161 | /* |
||
162 | I do not know how in the very unlikely event that several possible classifications have the same highest score. |
||
163 | In the current state, one of them is chosen arbitrarily. |
||
164 | |||
165 | See issue on Github : https://github.com/julien-boudry/Condorcet/issues/6 |
||
166 | */ |
||
167 | 5 | protected function makeRanking () : void |
|
171 | |||
172 | } |
||
173 |