From 0aaf7bc84ee766db197a8cc80c555de120376fe8 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Sat, 19 Apr 2014 11:43:32 +0200 Subject: [PATCH 01/22] Some news methods renaming. No change on the API --- lib/Condorcet/algorithms/Copeland.algo.php | 9 ++++----- lib/Condorcet/algorithms/Minimax.algo.php | 22 +++++++++++----------- lib/Condorcet/algorithms/Schulze.algo.php | 12 ++++++------ 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/lib/Condorcet/algorithms/Copeland.algo.php b/lib/Condorcet/algorithms/Copeland.algo.php index 7b1a455b..fc9edd21 100644 --- a/lib/Condorcet/algorithms/Copeland.algo.php +++ b/lib/Condorcet/algorithms/Copeland.algo.php @@ -50,10 +50,10 @@ public function getResult () ////// // Comparison calculation - $this->Copeland_make_comparison() ; + $this->makeComparison() ; // Ranking calculation - $this->Copeland_make_ranking() ; + $this->makeRanking() ; // Return @@ -85,7 +85,7 @@ public function getStats () //:: COPELAND ALGORITHM. ::// - protected function Copeland_make_comparison () + protected function makeComparison () { foreach ($this->_Pairwise as $candidate_key => $candidate_data) { @@ -115,7 +115,7 @@ protected function Copeland_make_comparison () } } - protected function Copeland_make_ranking () + protected function makeRanking () { $this->_Result = array() ; @@ -143,7 +143,6 @@ protected function Copeland_make_ranking () } } - $this->_Result[$rank] = implode(',', $this->_Result[$rank]) ; $rank++ ; } diff --git a/lib/Condorcet/algorithms/Minimax.algo.php b/lib/Condorcet/algorithms/Minimax.algo.php index 61aa9891..9439dda1 100644 --- a/lib/Condorcet/algorithms/Minimax.algo.php +++ b/lib/Condorcet/algorithms/Minimax.algo.php @@ -50,10 +50,10 @@ public function getResult () ////// // Computing - $this->ComputeMinimax (); + $this->computeMinimax (); // Ranking calculation - $this->calc_ranking () ; + $this->makeRanking () ; // Return return $this->_Result ; @@ -81,7 +81,7 @@ public function getStats () /////////// COMPUTE /////////// - protected function ComputeMinimax () + protected function computeMinimax () { $this->_Stats = array() ; @@ -120,9 +120,9 @@ protected function ComputeMinimax () } } - abstract protected function calc_ranking () ; + abstract protected function makeRanking () ; - protected static function calc_ranking_method ($type, array $stats, $options) + protected static function makeRanking_method ($type, array $stats, $options) { $result = array() ; $values = array() ; @@ -158,25 +158,25 @@ protected static function calc_ranking_method ($type, array $stats, $options) class Minimax_Winning extends Minimax { - protected function calc_ranking () + protected function makeRanking () { - $this->_Result = self::calc_ranking_method('winning', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('winning', $this->_Stats, $this->_Candidates) ; } } class Minimax_Margin extends Minimax { - protected function calc_ranking () + protected function makeRanking () { - $this->_Result = self::calc_ranking_method('margin', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('margin', $this->_Stats, $this->_Candidates) ; } } // Beware, this method is not a Condorcet method ! Winner can be different than Condorcet Basic method class Minimax_Opposition extends Minimax { - protected function calc_ranking () + protected function makeRanking () { - $this->_Result = self::calc_ranking_method('opposition', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('opposition', $this->_Stats, $this->_Candidates) ; } } \ No newline at end of file diff --git a/lib/Condorcet/algorithms/Schulze.algo.php b/lib/Condorcet/algorithms/Schulze.algo.php index c4f5df8b..08d1a00b 100644 --- a/lib/Condorcet/algorithms/Schulze.algo.php +++ b/lib/Condorcet/algorithms/Schulze.algo.php @@ -50,14 +50,14 @@ public function getResult () ////// // Format array - $this->Schulze_strongest_array() ; + $this->prepareStrongestPath() ; // Strongest Paths calculation - $this->strongest_paths() ; + $this->makeStrongestPaths() ; // Ranking calculation - $this->Schulze_make_ranking() ; + $this->makeRanking() ; // Return @@ -96,7 +96,7 @@ public function getStats () // Calculate the strongest Paths for Schulze Method - protected function Schulze_strongest_array () + protected function prepareStrongestPath () { $this->_StrongestPaths = array() ; @@ -117,7 +117,7 @@ protected function Schulze_strongest_array () // Calculate the Strongest Paths - protected function strongest_paths () + protected function makeStrongestPaths () { foreach ($this->_Candidates as $i => $i_value) { @@ -162,7 +162,7 @@ protected function strongest_paths () // Calculate && Format human readable ranking - protected function Schulze_make_ranking () + protected function makeRanking () { $this->_Result = array() ; From 29b162e4c3bcc77399a285b4ab83b954d585aa35 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Sat, 19 Apr 2014 12:37:04 +0200 Subject: [PATCH 02/22] Prepare v0.8 --- README.md | 4 ++-- lib/Condorcet/Condorcet.php | 4 ++-- lib/Condorcet/algorithms/Condorcet_Basic.algo.php | 2 +- lib/Condorcet/algorithms/Copeland.algo.php | 2 +- lib/Condorcet/algorithms/Minimax.algo.php | 2 +- lib/Condorcet/algorithms/Schulze.algo.php | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a78f6eb8..07bbb349 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,11 @@ As a courtesy, I will thank you to inform me about your project wearing this cod ### Project State -To date, the 0.7 is a stable version, but still suffers from a lack of testing, especially on an advanced use of functional pannel. +To date, the 0.8 is a stable version, but still suffers from a lack of testing, especially on an advanced use of functional pannel. **This open software project is beginning and needs your help for testing, improved documentation and features** #### Specifications and standards -**Stable Version : 0.7** +**Stable Version : 0.8** **PHP Requirement :** PHP 5.4 with Ctype and MB_String common extensions **Autoloading** : This project is consistent with the standard-PSR 0 and can be loaded easily and without modification in most framework. Namespace \Condorcet is used. diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index a7bafeb8..7fdbd450 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -2,7 +2,7 @@ /* Condorcet PHP Class, with Schulze Methods and others ! - Version : 0.7 + Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class @@ -28,7 +28,7 @@ class Condorcet /////////// CLASS /////////// - const VERSION = '0.7' ; + const VERSION = '0.8' ; const MAX_LENGTH_CANDIDATE_ID = 10 ; // Max length for option identifiant string protected static $_classMethod = null ; diff --git a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php index 1eede3be..e9f75a98 100644 --- a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php +++ b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php @@ -2,7 +2,7 @@ /* Part of the Condorcet PHP Class, with Schulze Methods and others ! - Version : 0.7 + Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class diff --git a/lib/Condorcet/algorithms/Copeland.algo.php b/lib/Condorcet/algorithms/Copeland.algo.php index fc9edd21..53586ca1 100644 --- a/lib/Condorcet/algorithms/Copeland.algo.php +++ b/lib/Condorcet/algorithms/Copeland.algo.php @@ -2,7 +2,7 @@ /* Part of the Condorcet PHP Class, with Copeland Methods and others ! - Version : 0.7 + Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) https://github.com/julien-boudry/Condorcet_Copeland-PHP_Class diff --git a/lib/Condorcet/algorithms/Minimax.algo.php b/lib/Condorcet/algorithms/Minimax.algo.php index 9439dda1..092f5273 100644 --- a/lib/Condorcet/algorithms/Minimax.algo.php +++ b/lib/Condorcet/algorithms/Minimax.algo.php @@ -2,7 +2,7 @@ /* Part of the Condorcet PHP Class, with Schulze Methods and others ! - Version : 0.7 + Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class diff --git a/lib/Condorcet/algorithms/Schulze.algo.php b/lib/Condorcet/algorithms/Schulze.algo.php index 08d1a00b..c21afd4e 100644 --- a/lib/Condorcet/algorithms/Schulze.algo.php +++ b/lib/Condorcet/algorithms/Schulze.algo.php @@ -2,7 +2,7 @@ /* Part of the Condorcet PHP Class, with Schulze Methods and others ! - Version : 0.7 + Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class From dc3fee610a3efa553f25bc08286ba4cabbddd78b Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Sat, 19 Apr 2014 12:51:14 +0200 Subject: [PATCH 03/22] Init for KemenyYoung --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 lib/Condorcet/algorithms/KemenyYoung.algo.php diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php new file mode 100644 index 00000000..3ddbb7af --- /dev/null +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -0,0 +1,81 @@ +_Pairwise = $config['_Pairwise'] ; + $this->_CandidatesCount = $config['_CandidatesCount'] ; + $this->_Candidates = $config['_Candidates'] ; + } + + +/////////// PUBLIC /////////// + + + // Get the Coepland ranking + public function getResult () + { + // Cache + if ( $this->_Result !== null ) + { + return $this->_Result ; + } + + ////// + + + // Return + return $this->_Result ; + } + + + // Get the Copeland ranking + public function getStats () + { + $this->getResult(); + + ////// + + $explicit = array() ; + + return $explicit ; + } + + + +/////////// COMPUTE /////////// + + + //:: COPELAND ALGORITHM. ::// + + protected function makeRanking () + { + + } + +} From 072333d8a6c69ed064d114dfa906a305fbb20606 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Sun, 20 Apr 2014 23:17:23 +0200 Subject: [PATCH 04/22] Compute all possible ranking for Kemeny-Young algorithm --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 106 +++++++++++++++++- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 3ddbb7af..9d85dc56 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -23,6 +23,8 @@ class KemenyYoung protected $_Candidates ; // Kemeny Young + protected $_PossibleRanking ; + protected $_RankingScore ; protected $_Result ; @@ -37,7 +39,7 @@ public function __construct (array $config) /////////// PUBLIC /////////// - // Get the Coepland ranking + // Get the Kemeny ranking public function getResult () { // Cache @@ -48,13 +50,14 @@ public function getResult () ////// + $this->calcPossibleRanking(); + $this->calcRankingScore(); // Return return $this->_Result ; } - // Get the Copeland ranking public function getStats () { $this->getResult(); @@ -67,11 +70,106 @@ public function getStats () } - /////////// COMPUTE /////////// - //:: COPELAND ALGORITHM. ::// + //:: Kemeny-Young ALGORITHM. ::// + + protected function calcPossibleRanking () + { + $this->_PossibleRanking = array() ; + + $arrangements = $this->calcPermutation($this->_CandidatesCount); + + $i_arrangement = 1 ; + foreach ($this->_Candidates as $CandidateId => $CandidateName) + { + $start = $i_arrangement ; + + // Create the possible and the first place + + for ($i = 1 ; $i <= ($arrangements / $this->_CandidatesCount) ; $i++) + { + $this->_PossibleRanking[$i_arrangement][1] = $CandidateId ; + + for ($ir = 2 ; $ir <= $this->_CandidatesCount ; $ir++ ) + { + $this->_PossibleRanking[$i_arrangement][$ir] = null ; + } + + $i_arrangement++ ; + } + + // Populate the nexts + $this->rPossibleRanking($start, $i_arrangement) ; + } + + // Tested the integrity of the calculation of possible classifications + /* + $test = $this->_PossibleRanking ; + foreach ($test as $key => $value) + { + unset($test[$key]); + + if (array_search($value, $test, true)) + { + echo '

ALERTE

'; + } + } + */ + } + + protected function calcPermutation ($n) + { + $result = $n ; + + for ($iteration = 1 ; $iteration < $n ; $iteration++) + { + $result = $result * ($n - $iteration) ; + } + + return $result ; + } + + protected function rPossibleRanking ($start, $end, $rank = 2) + { + $nbrCandidates = $this->_CandidatesCount - ($rank - 1) ; + $each = $this->calcPermutation($nbrCandidates) / $nbrCandidates ; + + foreach ($this->_Candidates as $CandidateId => $CandidateName) + { + $do = 0 ; + + // Parcours des possibles + for ($i = $start ; $i < $end ; $i++) + { + if ( $do < $each && + is_null($this->_PossibleRanking[$i][$rank]) && + array_search($CandidateId, $this->_PossibleRanking[$i], true) === FALSE + ) + { + $this->_PossibleRanking[$i][$rank] = $CandidateId ; + $do++; + } + } + } + + // Recursive + if ($rank < $this->_CandidatesCount) + { + $rank++ ; + + for ($partielEnd = $start + $each ; $partielEnd <= $end ; $partielEnd += $each) + { + $this->rPossibleRanking($start, $partielEnd, $rank); + } + } + } + + protected function calcRankingScore () + { + + } protected function makeRanking () { From 7e44b3a70c7fa12c6a0b1c00201b8b88eaac80c5 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Mon, 21 Apr 2014 13:46:04 +0200 Subject: [PATCH 05/22] More elegant appeal (rPossibleRanking) --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 9d85dc56..15b03f2b 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -101,7 +101,7 @@ protected function calcPossibleRanking () } // Populate the nexts - $this->rPossibleRanking($start, $i_arrangement) ; + $this->rPossibleRanking($start, $i_arrangement - 1) ; } // Tested the integrity of the calculation of possible classifications @@ -141,7 +141,7 @@ protected function rPossibleRanking ($start, $end, $rank = 2) $do = 0 ; // Parcours des possibles - for ($i = $start ; $i < $end ; $i++) + for ($i = $start ; $i <= $end ; $i++) { if ( $do < $each && is_null($this->_PossibleRanking[$i][$rank]) && @@ -159,7 +159,7 @@ protected function rPossibleRanking ($start, $end, $rank = 2) { $rank++ ; - for ($partielEnd = $start + $each ; $partielEnd <= $end ; $partielEnd += $each) + for ($partielEnd = $start + $each - 1 ; $partielEnd <= $end ; $partielEnd += $each) { $this->rPossibleRanking($start, $partielEnd, $rank); } From f531695cfe92e775aad757b918ae31e852526ac3 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Mon, 21 Apr 2014 15:54:12 +0200 Subject: [PATCH 06/22] Kemeny-Young : Compute ranking score --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 15b03f2b..bbadc86c 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -168,7 +168,27 @@ protected function rPossibleRanking ($start, $end, $rank = 2) protected function calcRankingScore () { + $this->_RankingScore = array() ; + foreach ($this->_PossibleRanking as $keyScore => $ranking) + { + $this->_RankingScore[$keyScore] = 0 ; + + $do = array() ; + + foreach ($ranking as $candidateId) + { + $do[] = $candidateId ; + + foreach ($ranking as $rank => $rankCandidate) + { + if (!in_array($rankCandidate, $do)) + { + $this->_RankingScore[$keyScore] += $this->_Pairwise[$candidateId]['win'][$rankCandidate]; + } + } + } + } } protected function makeRanking () From 61cf9596f424a5e3bbe8729979d569b3d2fd1e22 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Mon, 21 Apr 2014 15:54:44 +0200 Subject: [PATCH 07/22] Kemeny-Young : Optimize and inline documentation --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index bbadc86c..0cfd8405 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -90,8 +90,10 @@ protected function calcPossibleRanking () for ($i = 1 ; $i <= ($arrangements / $this->_CandidatesCount) ; $i++) { + // Less clean than to start the recursion from the beginning, but really much faster, and that from 4 candidates! $this->_PossibleRanking[$i_arrangement][1] = $CandidateId ; + // Prepare empty arrays for ($ir = 2 ; $ir <= $this->_CandidatesCount ; $ir++ ) { $this->_PossibleRanking[$i_arrangement][$ir] = null ; @@ -100,7 +102,7 @@ protected function calcPossibleRanking () $i_arrangement++ ; } - // Populate the nexts + // Recursive function to populate rank 2 to x rank. $this->rPossibleRanking($start, $i_arrangement - 1) ; } @@ -111,7 +113,7 @@ protected function calcPossibleRanking () { unset($test[$key]); - if (array_search($value, $test, true)) + if (in_array($value, $test, true)) { echo '

ALERTE

'; } @@ -145,7 +147,7 @@ protected function rPossibleRanking ($start, $end, $rank = 2) { if ( $do < $each && is_null($this->_PossibleRanking[$i][$rank]) && - array_search($CandidateId, $this->_PossibleRanking[$i], true) === FALSE + !in_array($CandidateId, $this->_PossibleRanking[$i], true) ) { $this->_PossibleRanking[$i][$rank] = $CandidateId ; From 7e1a789d074db847567121d243a25212d137446e Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Mon, 21 Apr 2014 15:56:14 +0200 Subject: [PATCH 08/22] Max option length++ --- lib/Condorcet/Condorcet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 7fdbd450..84e5e0d2 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -29,7 +29,7 @@ class Condorcet const VERSION = '0.8' ; - const MAX_LENGTH_CANDIDATE_ID = 10 ; // Max length for option identifiant string + const MAX_LENGTH_CANDIDATE_ID = 30 ; // Max length for option identifiant string protected static $_classMethod = null ; protected static $_authMethods = '' ; From 7f52b80af0d89fbeff214cc8cc05de6ba3499773 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 20:32:39 +0200 Subject: [PATCH 09/22] Kemeny-Young : Make Result --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 0cfd8405..28b17194 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -52,6 +52,7 @@ public function getResult () $this->calcPossibleRanking(); $this->calcRankingScore(); + $this->makeRanking(); // Return return $this->_Result ; @@ -195,7 +196,7 @@ protected function calcRankingScore () protected function makeRanking () { - + $this->_Result =& $this->_PossibleRanking[ array_search(max($this->_RankingScore), $this->_RankingScore, true) ]; } } From 15edb7d953a08b99984b0179b4e6afcbfb02f6a4 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 21:10:37 +0200 Subject: [PATCH 10/22] Kemeny-Young : Stats --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 28b17194..6e3929b1 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -67,6 +67,12 @@ public function getStats () $explicit = array() ; + foreach ($this->_PossibleRanking as $key => $value) + { + $explicit[$key] = $value ; + $explicit[$key]['score'] = $this->_RankingScore[$key] ; + } + return $explicit ; } From fc6ba47e2871be0d6e34804a2f29b0eb5db66bb9 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 21:15:25 +0200 Subject: [PATCH 11/22] Kemeny-Young : Fix result return format --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 6e3929b1..4ea8a899 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -202,7 +202,12 @@ protected function calcRankingScore () protected function makeRanking () { - $this->_Result =& $this->_PossibleRanking[ array_search(max($this->_RankingScore), $this->_RankingScore, true) ]; + $this->_Result = $this->_PossibleRanking[ array_search(max($this->_RankingScore), $this->_RankingScore, true) ]; + + foreach ($this->_Result as &$value) + { + $value = array($value); + } } } From a974dc6a0d108e9cc4dd90f7a562e8525d9bd321 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 22:16:03 +0200 Subject: [PATCH 12/22] Some optimizations & cleanup --- lib/Condorcet/Condorcet.php | 73 +++++++++++-------- .../algorithms/Condorcet_Basic.algo.php | 4 +- lib/Condorcet/algorithms/Copeland.algo.php | 3 +- lib/Condorcet/algorithms/KemenyYoung.algo.php | 5 -- lib/Condorcet/algorithms/Minimax.algo.php | 15 ++-- lib/Condorcet/algorithms/Schulze.algo.php | 15 ---- 6 files changed, 50 insertions(+), 65 deletions(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 84e5e0d2..862d228c 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -420,14 +420,17 @@ protected function getCandidateKey ($candidate_id) return array_search($candidate_id, $this->_Candidates, true) ; } - protected function getCandidateId ($option_key) + protected function getCandidateId ($candidate_key) { - self::getStatic_CandidateId($option_key, $this->_Candidates) ; + return self::getStatic_CandidateId($candidate_key, $this->_Candidates) ; } - public static function getStatic_CandidateId ($option_key, &$options) + public static function getStatic_CandidateId ($candidate_key, &$candidates) { - return $options[$option_key] ; + if (!array_key_exists($candidate_key, $candidates)) + { return false ; } + + return $candidates[$candidate_key] ; } protected function existCandidateId ($candidate_id) @@ -690,30 +693,52 @@ public function getResult ($method = null) { $this->initResult($this->_Method) ; - return $this->_Calculator[$this->_Method]->getResult() ; + $result = $this->_Calculator[$this->_Method]->getResult() ; } elseif (self::isAuthMethod($method)) { $this->initResult($method) ; - return $this->_Calculator[$method]->getResult() ; + $result = $this->_Calculator[$method]->getResult() ; } else { return self::error(8,$method) ; } + + return $this->humanResult($result) ; } + protected function humanResult ($robot) + { + $human = array() ; - public function getWinner ($substitution = false) - { - // Method - $this->setMethod() ; - // Prepare - $this->prepareResult() ; + foreach ( $robot as $key => $value ) + { + if (is_array($value)) + { + foreach ($value as $option_key) + { + $human[$key][] = $this->getCandidateId($option_key) ; + } + } + else + { + $human[$key][] = $this->getCandidateId($value) ; + } + } - ////// + foreach ( $human as $key => $value ) + { + $human[$key] = implode(',',$value); + } + + return $human ; + } + + public function getWinner ($substitution = false) + { if ( $substitution ) { if ($substitution === true) @@ -729,21 +754,12 @@ public function getWinner ($substitution = false) ////// - $this->initResult($algo) ; - - return $this->_Calculator[$algo]->getResult()[1] ; + return $this->getResult($algo)[1] ; } public function getLoser ($substitution = false) { - // Method - $this->setMethod() ; - // Prepare - $this->prepareResult() ; - - ////// - if ( $substitution ) { if ($substitution === true) @@ -759,9 +775,7 @@ public function getLoser ($substitution = false) ////// - $this->initResult($algo) ; - - $result = $this->_Calculator[$algo]->getResult() ; + $result = $this->getResult($algo) ; return $result[count($result)] ; } @@ -941,21 +955,18 @@ protected function doPairwise () foreach ( $this->_Candidates as $g_option_key => $g_CandidateId ) { // Win - if ( - $option_key !== $g_option_key && + if ( $option_key !== $g_option_key && !in_array($g_option_key, $done_Candidates, true) && !in_array($g_option_key, $options_in_rank_keys, true) ) { - $this->_Pairwise[$option_key]['win'][$g_option_key]++ ; $done_Candidates[] = $option_key ; } // Null - if ( - $option_key !== $g_option_key && + if ( $option_key !== $g_option_key && count($options_in_rank) > 1 && in_array($g_option_key, $options_in_rank_keys) ) diff --git a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php index e9f75a98..7821b6d3 100644 --- a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php +++ b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php @@ -80,7 +80,7 @@ public function getWinner () if ($winner) { - $this->_CondorcetWinner = namespace\Condorcet::getStatic_CandidateId($candidate_key, $this->_Candidates) ; + $this->_CondorcetWinner = $candidate_key ; return $this->_CondorcetWinner ; } @@ -116,7 +116,7 @@ public function getLoser () if ($loser) { - $this->_CondorcetLoser = namespace\Condorcet::getStatic_CandidateId($candidate_key, $this->_Candidates) ; + $this->_CondorcetLoser = $candidate_key ; return $this->_CondorcetLoser ; } diff --git a/lib/Condorcet/algorithms/Copeland.algo.php b/lib/Condorcet/algorithms/Copeland.algo.php index 53586ca1..23237108 100644 --- a/lib/Condorcet/algorithms/Copeland.algo.php +++ b/lib/Condorcet/algorithms/Copeland.algo.php @@ -137,13 +137,12 @@ protected function makeRanking () { if ($value === $looking) { - $this->_Result[$rank][] = namespace\Condorcet::getStatic_CandidateId($candidate, $this->_Candidates) ; + $this->_Result[$rank][] = $candidate ; $done++ ; unset($challenge[$candidate]) ; } } - $this->_Result[$rank] = implode(',', $this->_Result[$rank]) ; $rank++ ; } } diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 4ea8a899..9d2bd7d1 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -203,11 +203,6 @@ protected function calcRankingScore () protected function makeRanking () { $this->_Result = $this->_PossibleRanking[ array_search(max($this->_RankingScore), $this->_RankingScore, true) ]; - - foreach ($this->_Result as &$value) - { - $value = array($value); - } } } diff --git a/lib/Condorcet/algorithms/Minimax.algo.php b/lib/Condorcet/algorithms/Minimax.algo.php index 092f5273..6ff021c1 100644 --- a/lib/Condorcet/algorithms/Minimax.algo.php +++ b/lib/Condorcet/algorithms/Minimax.algo.php @@ -122,7 +122,7 @@ protected function computeMinimax () abstract protected function makeRanking () ; - protected static function makeRanking_method ($type, array $stats, $options) + protected static function makeRanking_method ($type, array $stats) { $result = array() ; $values = array() ; @@ -140,18 +140,13 @@ protected static function makeRanking_method ($type, array $stats, $options) { if ($candidate_Stats === $looking) { - $result[$rank][] = namespace\Condorcet::getStatic_CandidateId ($candidate_key, $options) ; + $result[$rank][] = $candidate_key ; unset($values[$candidate_key]); } } } - foreach ($result as $rank => $options_name) - { - $result[$rank] = implode(',', $options_name); - } - return $result ; } } @@ -160,7 +155,7 @@ class Minimax_Winning extends Minimax { protected function makeRanking () { - $this->_Result = self::makeRanking_method('winning', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('winning', $this->_Stats) ; } } @@ -168,7 +163,7 @@ class Minimax_Margin extends Minimax { protected function makeRanking () { - $this->_Result = self::makeRanking_method('margin', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('margin', $this->_Stats) ; } } @@ -177,6 +172,6 @@ class Minimax_Opposition extends Minimax { protected function makeRanking () { - $this->_Result = self::makeRanking_method('opposition', $this->_Stats, $this->_Candidates) ; + $this->_Result = self::makeRanking_method('opposition', $this->_Stats) ; } } \ No newline at end of file diff --git a/lib/Condorcet/algorithms/Schulze.algo.php b/lib/Condorcet/algorithms/Schulze.algo.php index c21afd4e..7d1431da 100644 --- a/lib/Condorcet/algorithms/Schulze.algo.php +++ b/lib/Condorcet/algorithms/Schulze.algo.php @@ -208,21 +208,6 @@ protected function makeRanking () $rank++ ; } - - - // Format ranking - foreach ( $this->_Result as $key => $value ) - { - foreach ($value as $ord => $option_key) - { - $this->_Result[$key][$ord] = namespace\Condorcet::getStatic_CandidateId($option_key, $this->_Candidates) ; - } - } - - foreach ( $this->_Result as $key => $value ) - { - $this->_Result[$key] = implode(',',$value); - } } } From 34be52ec77879b821147c61028cdd1d45f2e215a Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 22:53:18 +0200 Subject: [PATCH 13/22] Optimize getResultStats --- lib/Condorcet/Condorcet.php | 9 +++++++-- lib/Condorcet/algorithms/Condorcet_Basic.algo.php | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 862d228c..03aa29dc 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -794,18 +794,23 @@ public function getResultStats ($method = null) { $this->initResult($this->_Method) ; - return $this->_Calculator[$this->_Method]->getStats() ; + $stats = $this->_Calculator[$this->_Method]->getStats() ; } elseif (self::isAuthMethod($method)) { $this->initResult($method) ; - return $this->_Calculator[$method]->getStats() ; + $stats = $this->_Calculator[$method]->getStats() ; } else { return self::error(8,$candidate_id) ; } + + if (!is_null($stats)) + { return $stats ; } + else + { return $this->getPairwise(); } } diff --git a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php index 7821b6d3..bb1809e6 100644 --- a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php +++ b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php @@ -49,7 +49,7 @@ public function getResult () // Get the Schulze ranking public function getStats () { - return Condorcet::getStatic_Pairwise($this->_Pairwise, $this->_Candidates) ; + return null ; } From b8dfa722b91a448516768e5f4c63990a74facec3 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 23:31:26 +0200 Subject: [PATCH 14/22] Limitations for KemenyYoung && Condorcet::error() improvement --- lib/Condorcet/Condorcet.php | 11 +++++-- lib/Condorcet/algorithms/KemenyYoung.algo.php | 29 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 03aa29dc..4ba3e7f3 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -166,7 +166,7 @@ public static function setError ($param = true) } - protected static function error ($code, $infos = null) + public static function error ($code, $infos = null, $level = E_USER_WARNING) { $error[1] = array('text'=>'Bad option format', 'level'=>E_USER_WARNING) ; $error[2] = array('text'=>'The voting process has already started', 'level'=>E_USER_WARNING) ; @@ -190,7 +190,14 @@ protected static function error ($code, $infos = null) } elseif (self::$_showError) { - trigger_error( 'Mysterious Error : '.$infos, E_USER_NOTICE ); + if (!is_null($infos)) + { + trigger_error( $infos, $level ); + } + else + { + trigger_error( 'Mysterious Error', $level ); + } } return false ; diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 9d2bd7d1..1e9831ff 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -13,10 +13,32 @@ // Registering algorithm namespace\Condorcet::addAlgos('KemenyYoung') ; +/* +* Maximum number of candidates for this algorithm. +* The script can support six candidates in less than twenty seconds +* found in PHP 5.5 (Linux) * against fifty on a Windows system. +* But because five candidates calculation always under 150ms, +* we will use this number as a limit by default. +* +* The number of voters is indifferent. +*/ +namespace\KemenyYoung::setMaxCandidates(5); + -// Kemeny Young is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Kemeny%E2%80%93Young_method +// Kemeny-Young is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Kemeny%E2%80%93Young_method class KemenyYoung { + // Limits + public static $_maxCandidates = 6 ; // Beyond, and for the performance of PHP on recursive functions, it would be folly for this implementation. + + public static function setMaxCandidates ($max) + { + if (is_int($max)) + { + self::$_maxCandidates = $max ; + } + } + // Config protected $_Pairwise ; protected $_CandidatesCount ; @@ -33,6 +55,11 @@ public function __construct (array $config) $this->_Pairwise = $config['_Pairwise'] ; $this->_CandidatesCount = $config['_CandidatesCount'] ; $this->_Candidates = $config['_Candidates'] ; + + if ($this->_CandidatesCount > self::$_maxCandidates) + { + namespace\Condorcet::error('','KemenyYoung is configured to accept only '.self::$_maxCandidates.' candidates',E_USER_ERROR) ; + } } From 74cc9942380f74a7bceb9833d50d81cde0541cf1 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 23:45:56 +0200 Subject: [PATCH 15/22] Note : Inline doc. about kemeny-Young --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 1e9831ff..0069343b 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -202,6 +202,7 @@ protected function rPossibleRanking ($start, $end, $rank = 2) } } + protected function calcRankingScore () { $this->_RankingScore = array() ; @@ -227,6 +228,13 @@ protected function calcRankingScore () } } + + /* + I do not know how in the very unlikely event that several possible classifications have the same highest score. + In the current state, one of them is chosen arbitrarily. + + See issue on Github : https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class/issues/6 + */ protected function makeRanking () { $this->_Result = $this->_PossibleRanking[ array_search(max($this->_RankingScore), $this->_RankingScore, true) ]; From 36763bd9637b8e306fed8b732e5e1cfa78270f5e Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Tue, 22 Apr 2014 23:49:27 +0200 Subject: [PATCH 16/22] Header comment --- lib/Condorcet/Condorcet.php | 2 +- lib/Condorcet/algorithms/Condorcet_Basic.algo.php | 4 ++-- lib/Condorcet/algorithms/Copeland.algo.php | 4 ++-- lib/Condorcet/algorithms/KemenyYoung.algo.php | 4 ++-- lib/Condorcet/algorithms/Minimax.algo.php | 4 ++-- lib/Condorcet/algorithms/Schulze.algo.php | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 4ba3e7f3..13cc4bfd 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -5,7 +5,7 @@ Version : 0.8 By Julien Boudry - MIT LICENSE (Please read LICENSE.txt) - https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class + https://github.com/julien-boudry/Condorcet_Schulze-PHP_Class */ namespace Condorcet ; diff --git a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php index bb1809e6..9d1ed93f 100644 --- a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php +++ b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php @@ -1,11 +1,11 @@ Date: Wed, 23 Apr 2014 23:10:04 +0200 Subject: [PATCH 17/22] Add interface for algo class --- lib/Condorcet/Condorcet.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 13cc4bfd..2c6eda26 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -1003,3 +1003,10 @@ protected function doPairwise () } } } + + +interface Condorcet_Algo +{ + public function getResult(); + public function getStats(); +} From 4e023379ed1dcaaa6252940c436cc3e972c2665b Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Thu, 24 Apr 2014 10:56:59 +0200 Subject: [PATCH 18/22] Implement Condorcet_Algo interface --- README.md | 7 ++--- lib/Condorcet/Condorcet.php | 15 ++++------ .../algorithms/Condorcet_Basic.algo.php | 8 ++--- lib/Condorcet/algorithms/Copeland.algo.php | 8 ++--- lib/Condorcet/algorithms/KemenyYoung.algo.php | 30 +++++++++---------- lib/Condorcet/algorithms/Minimax.algo.php | 10 +++---- lib/Condorcet/algorithms/Schulze.algo.php | 8 ++--- 7 files changed, 39 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 07bbb349..6c7f3360 100644 --- a/README.md +++ b/README.md @@ -312,10 +312,7 @@ $condorcet->getResultStats('Schulze') ; // Same thing with a specific method. ##### Customize the code : Add new algorithm(s) Look at how existing algorithm work in the "algorithms" folder, because the algorithms formally included using the same modular schema. *(Exept Condorcet_Basic, which is the only core algorithm and a little bit special.)* -###### Each new class of algorithm must include the publics methods:** - -1. getResult -2. getStats +**Each new class of algorithm must implements the Condorcet_Algo interface:** ###### Constructor take an array as follow: @@ -329,7 +326,7 @@ $param['_votes'] = (Vote details) ; // In case you would need to do more complic The class name should be in this format: ```php -class AlgorithmName +class AlgorithmName implements namespace\Condorcet_Algo ``` File on disk must follow this format: `AlgorithmName.algo.php` diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 2c6eda26..b9877601 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -106,21 +106,16 @@ public static function addAlgos ($algos) // Check if the class Algo. exist and ready to be used protected static function testAlgos ($algos) { - if ( !class_exists(__NAMESPACE__.'\\'.$algos) ) + if ( !class_exists(__NAMESPACE__.'\\'.$algos, false) ) { self::error(9) ; return false ; } - $to_checks = array ('getResult', 'getStats') ; - - foreach ($to_checks as $method) + if ( !in_array(__NAMESPACE__.'\\'.'Condorcet_Algo', class_implements(__NAMESPACE__.'\\'.$algos), false) ) { - if ( !method_exists(__NAMESPACE__.'\\'.$algos , $method) ) - { - self::error(10) ; - return false ; - } + self::error(10) ; + return false ; } return true ; @@ -1004,7 +999,7 @@ protected function doPairwise () } } - +// Interface with the aim of verifying the good modular implementation of algorithms. interface Condorcet_Algo { public function getResult(); diff --git a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php index 9d1ed93f..4c47d479 100644 --- a/lib/Condorcet/algorithms/Condorcet_Basic.algo.php +++ b/lib/Condorcet/algorithms/Condorcet_Basic.algo.php @@ -10,12 +10,9 @@ namespace Condorcet ; -// Registering algorithm -namespace\Condorcet::addAlgos('Condorcet_Basic') ; - // Condorcet Basic Class, provide natural Condorcet winner or looser -class Condorcet_Basic +class Condorcet_Basic implements namespace\Condorcet_Algo { // Config protected $_Pairwise ; @@ -126,3 +123,6 @@ public function getLoser () } } + +// Registering algorithm +namespace\Condorcet::addAlgos('Condorcet_Basic') ; diff --git a/lib/Condorcet/algorithms/Copeland.algo.php b/lib/Condorcet/algorithms/Copeland.algo.php index 7ba33ec5..85140a51 100644 --- a/lib/Condorcet/algorithms/Copeland.algo.php +++ b/lib/Condorcet/algorithms/Copeland.algo.php @@ -10,12 +10,9 @@ namespace Condorcet ; -// Registering algorithm -namespace\Condorcet::addAlgos('Copeland') ; - // Copeland is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Copeland_method -class Copeland +class Copeland implements namespace\Condorcet_Algo { // Config protected $_Pairwise ; @@ -148,3 +145,6 @@ protected function makeRanking () } } + +// Registering algorithm +namespace\Condorcet::addAlgos('Copeland') ; diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 40cb6dd0..6530efbe 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -10,23 +10,9 @@ namespace Condorcet ; -// Registering algorithm -namespace\Condorcet::addAlgos('KemenyYoung') ; - -/* -* Maximum number of candidates for this algorithm. -* The script can support six candidates in less than twenty seconds -* found in PHP 5.5 (Linux) * against fifty on a Windows system. -* But because five candidates calculation always under 150ms, -* we will use this number as a limit by default. -* -* The number of voters is indifferent. -*/ -namespace\KemenyYoung::setMaxCandidates(5); - // Kemeny-Young is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Kemeny%E2%80%93Young_method -class KemenyYoung +class KemenyYoung implements namespace\Condorcet_Algo { // Limits public static $_maxCandidates = 6 ; // Beyond, and for the performance of PHP on recursive functions, it would be folly for this implementation. @@ -241,3 +227,17 @@ protected function makeRanking () } } + +// Registering algorithm +namespace\Condorcet::addAlgos('KemenyYoung') ; + +/* +* Maximum number of candidates for this algorithm. +* The script can support six candidates in less than twenty seconds +* found in PHP 5.5 (Linux) * against fifty on a Windows system. +* But because five candidates calculation always under 150ms, +* we will use this number as a limit by default. +* +* The number of voters is indifferent. +*/ +namespace\KemenyYoung::setMaxCandidates(5); diff --git a/lib/Condorcet/algorithms/Minimax.algo.php b/lib/Condorcet/algorithms/Minimax.algo.php index 3e6de056..5bac03f5 100644 --- a/lib/Condorcet/algorithms/Minimax.algo.php +++ b/lib/Condorcet/algorithms/Minimax.algo.php @@ -10,12 +10,9 @@ namespace Condorcet ; -// Registering algorithm -namespace\Condorcet::addAlgos( array('Minimax_Winning','Minimax_Margin', 'Minimax_Opposition') ) ; - // Schulze is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Schulze_method -abstract class Minimax +abstract class Minimax implements namespace\Condorcet_Algo { // Config protected $_Pairwise ; @@ -174,4 +171,7 @@ protected function makeRanking () { $this->_Result = self::makeRanking_method('opposition', $this->_Stats) ; } -} \ No newline at end of file +} + +// Registering algorithm +namespace\Condorcet::addAlgos( array('Minimax_Winning','Minimax_Margin', 'Minimax_Opposition') ) ; diff --git a/lib/Condorcet/algorithms/Schulze.algo.php b/lib/Condorcet/algorithms/Schulze.algo.php index 66656a2e..e5f1c13a 100644 --- a/lib/Condorcet/algorithms/Schulze.algo.php +++ b/lib/Condorcet/algorithms/Schulze.algo.php @@ -10,12 +10,9 @@ namespace Condorcet ; -// Registering algorithm -namespace\Condorcet::addAlgos('Schulze') ; - // Schulze is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Schulze_method -class Schulze +class Schulze implements namespace\Condorcet_Algo { // Config protected $_Pairwise ; @@ -211,3 +208,6 @@ protected function makeRanking () } } + +// Registering algorithm +namespace\Condorcet::addAlgos('Schulze') ; From 6bed851424e87114ed9047c518ab48ee1a483984 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Thu, 24 Apr 2014 18:39:24 +0200 Subject: [PATCH 19/22] Upgrade reset_all method --- lib/Condorcet/Condorcet.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index 2c6eda26..049ba8a5 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -317,9 +317,10 @@ public function resetAll () { $this->cleanupResult() ; - $this->_Candidates = null ; + $this->_Candidates = array() ; $this->_CandidatesCount = 0 ; - $this->_Votes = null ; + $this->_nextVoteTag = 0 ; + $this->_Votes = array() ; $this->_i_CandidateId = 'A' ; $this->_State = 1 ; From 4c5c1b98135440a239a0995e9b816f2ec3fce227 Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Thu, 24 Apr 2014 20:03:32 +0200 Subject: [PATCH 20/22] Kemeny-Young explicit stats --- lib/Condorcet/algorithms/KemenyYoung.algo.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index 6530efbe..da0d1084 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -83,6 +83,13 @@ public function getStats () foreach ($this->_PossibleRanking as $key => $value) { $explicit[$key] = $value ; + + // Human redable + foreach ($explicit[$key] as &$candidate_key) + { + $candidate_key = namespace\Condorcet::getStatic_CandidateId($candidate_key, $this->_Candidates); + } + $explicit[$key]['score'] = $this->_RankingScore[$key] ; } From a41100e600a9d0c00dd8db30109732ea3974602a Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Thu, 24 Apr 2014 22:18:05 +0200 Subject: [PATCH 21/22] Many doc. improvements --- README.md | 120 ++++++++++-------- lib/Condorcet/algorithms/KemenyYoung.algo.php | 3 +- 2 files changed, 68 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 6c7f3360..fd972e80 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ The literature also provides you an easy example of free implementation with or **Coding standards** : The code is very close to the respect of PSR-1 (lacks only the naming of methods), and freely influenced by PSR-2 when it is not unnecessarily authoritarian. -* * * +--------------------------------------- ## Features @@ -33,51 +33,63 @@ The literature also provides you an easy example of free implementation with or You should be able to find as much functionality and flexibility than you might expect. #### Supported Condorcet Methods - -##### Provides : * **Condorcet Basic** Give you the natural winner or looser of Condorcet, if there is one. *(This method is the only core method, you can't remove it)* -* **Schulze** http://en.wikipedia.org/wiki/Schulze_method + * **Copeland** *(Since v0.4)* http://en.wikipedia.org/wiki/Copeland%27s_method + +* **Kemeny-Young** *(Since v0.8)* http://en.wikipedia.org/wiki/Kemeny-Young_method +*Neither perfect nor quick, however this implementation is operating normally. You are invited to read the corresponding issue Github, and if you are able to comment on issues in Github. +Because this implementation uses no heuristic means, it is deliberately restrained to vote involving no more than 5 candidates; however, you can easily bypass this limitation via the appropriate static method, but your processor could turn longptemps to exceed six candidates. It is not against by a limited number of voters.* + * **Minimax Family** *(Since v0.6)* http://en.wikipedia.org/wiki/Minimax_Condorcet - * **Minimax_Winning** *(Does not satisfy the Condorcet loser criterion.)* - * **Minimax_Margin** *(Does not satisfy the Condorcet loser criterion.)* - * **Minimax_Opposition** :warning: *Does not satisfy the Condorcet criterion.* + * **Minimax_Winning** *(Does not satisfy the Condorcet loser criterion)* + * **Minimax_Margin** *(Does not satisfy the Condorcet loser criterion)* + * **Minimax_Opposition** :warning: *By nature, this alternative does not meet any criterion of Condorcet.* +* **Schulze** http://en.wikipedia.org/wiki/Schulze_method _The name of the above methods must be observed when you make calls, case sensitive._ -##### Add new ? -This class is designed to be easily extensible with new algorithms. A modular schematic is used to totu algorithms provided, so you can easily help, do not forget to make a pull request! -*More explanations in the documentation below.* +#### Add new ? +This class is designed to be easily extensible with new algorithms. A modular schematic is already used for all algorithms provided, so you can easily help, do not forget to make a pull request! +[*More explanations in the documentation below*](#newAlgo) ### Roadmap for futher releases - Better cache system to prevent any full computing of the Pairwise on new vote / remove vote - - Official support for exporting object with caching - - Perhaps new Condorcet methods *(according dificulties thereof)* - - **Looking for testers !** - + - Perhaps Ranked pairs Condorcet methods *(need help!)* + - Non-Condorcet fun methods ? (majority, points ?) + - **Looking for testers !** + +--------------------------------------- ## How to use it ? Soon you will find a complete example.php. The most important methods are listed bellow. We encourage you to read the code, and help to improve inline documentation ! +1. [Install it](#install-it) +2. [Configure it if needed](#configure-it-if-needed) +3. [1: Manage Candidates](#1-manage-candidates) +4. [2: Start voting](#2-start-voting) +5. [3: Get results & Stats](#3-get-results--stats) +6. [Customize the code : Add new algorithm(s)](#customize-the-code--add-new-algorithms-) -#### Create new object & Class configuration +--------------------------------------- +### Install it -##### Basic and free implementation +#### Basically ```php require_once 'Condorcet.php' ; // Customize the path for your use. -use Condorcet\Condorcet ; +use Condorcet\Condorcet ; // Optionnal if you prefer to use the full namespace length $condorcet = new Condorcet () ; // You can specify as an argument, the name string of method instead of default Schulze Method. ``` -##### Example with the official PSR-0 example of Autoloader +#### Example with the official PSR-0 example of Autoloader ```php // PSR-0 style Loader @@ -110,39 +122,37 @@ $condorcet = new Condorcet (); // If you omit the previous line, do : new Condor ``` -##### With Frameworks +#### With Frameworks *Read the doc ! The Condorcet folder inside the lib directory can be move into your solution lib directory* -##### With Composer +#### With Composer `composer require julien-boudry/condorcet` Look https://packagist.org/packages/julien-boudry/condorcet +--------------------------------------- +### Configure it if needed -##### Change the object default method if needed - +#### Change the object default method if needed ```php $condorcet->setMethod('Schulze') ; // Argument : A supported method ``` -##### Change the class default method if needed +#### Change the class default method if needed ```php Condorcet::setClassMethod('Schulze') ; // Argument : A supported method Condorcet::setClassMethod('Schulze', true) ; // Will force actual and futher object to use this by default. Condorcet::forceMethod(false) ; // Unforce actual and futher object to use the class default method (or force it if argument is true) ``` - -##### About errors +#### About errors ```php Condorcet::setError(false) ; // _(true by default)_ Unactive or active trigger_error() usage. Can be unefficent depending of your configuration environnement. ``` - - -##### Get informations +#### Get informations ```php $condorcet->getConfig (); // Will return an explicit array about the object and Class Constant. @@ -151,7 +161,7 @@ $condorcet->getMethod (); // Return a string with the name of the default method Condorcet::getAuthMethods (); // Get an array of authorized methods to use with the correct string to use as parameter. ``` -##### Get library version / Get object version +#### Get library version / Get object version The distinction may be useful in the case of a storage of the object in the database. ```php @@ -159,15 +169,15 @@ Condorcet::getClassVersion(); // Return the Class engine $condorcet->getObjectVersion(); // Return the Class engine who build this object ``` -##### Reset object without destroy it (discouraged pratice) +#### Reset object without destroy it (discouraged pratice) ```php $condorcet->resetAll (); ``` +--------------------------------------- +### 1: Manage Candidates -#### 1- Manage Candidates - -##### Registering +#### Registering Enter (or not) an Option_Identifiant @@ -179,13 +189,13 @@ $condorcet->addCandidate(2) ; // A numeric argument ``` -##### Removing +#### Removing ```php $condorcet->removeCandidate('Wagner') ; ``` -##### Verify the Candidates list +#### Verify the Candidates list ```php $condorcet->getCandidatesList(); // Will return an array with Option_ID as value. ``` @@ -193,13 +203,14 @@ $condorcet->getCandidatesList(); // Will return an array with Option_ID as value _Note : When you start voting, you will never be able to edit the options list._ -#### 2- Start voting +--------------------------------------- +### 2: Start voting _Note: All votes are adjusted to estimate all candidates. The pairwise is calculated accordingly._ -##### Add a vote +#### Add a vote _Note : You can add new vote after the results have already been given_ -###### With an array +##### With an array ```php $vote[1] = 'A' ; $vote[2] = 'Debussy' ; @@ -217,7 +228,7 @@ $condorcet->addVote($vote) ; *The last rank is optionnal, it will be automatically deducted.* -###### With a string +##### With a string You can do like this: ```php @@ -237,7 +248,7 @@ $vote = 'A>BC>D' ; // It's not correct *The last rank is optionnal too, it will be automatically deducted.* -##### Add a tag +#### Add a tag You can add the same or different tag for each vote : ```php $condorcet->addVote($vote, 'Charlie') ; // Please note that a single tag is always created for each vote. @@ -246,7 +257,7 @@ $condorcet->addVote($vote, 'Charlie,Claude') ; // You can also add multiple tags -##### Verify the registered votes list +#### Verify the registered votes list ```php $condorcet->getVotesList (); // Will return an array where key is the internal numeric vote_id and value an other array like your input. $condorcet->getVotesList ('Charlie'); // Will return an array where each vote with this tag. @@ -256,7 +267,7 @@ $condorcet->countVotes (); // Return a numeric value about the number of registe ``` -##### Remove vote +#### Remove vote ```php $condorcet->removeVote('Charlie') ; // Remove vote(s) with tag Charlie $condorcet->removeVote('Charlie', false) ; // Remove votes without tag Charlie @@ -265,20 +276,20 @@ $condorcet->removeVote('Charlie', false) ; // Remove votes without tag Charlie _Note : You can remove a vote after the results have already been given_ - -#### 3- Get results & Stats +--------------------------------------- +### 3: Get results & Stats When you have finished to processing vote, you would like to have the results. -##### Just get the natural Condorcet Winner +#### Just get the natural Condorcet Winner -###### Regular +##### Regular ```php $condorcet->getWinner() ; // Will return a string with the Option_Identifiant $condorcet->getLoser() ; // Will return a string with the Option_Identifiant ``` -###### Special +##### Special If there is not a regular Condorcet Winner or Loser, process to a special winner(s) using an advanced method. ```php @@ -292,14 +303,14 @@ $condorcet->getLoser('Schulze') ; // Name of an valid method Will return a string with the Option_Identifiant or many Option identifiants separated by commas -##### Get a complete ranking from advanced methods +#### Get a complete ranking from advanced methods ```php $condorcet->getResult() ; // Set of results with ranking from the default method. (Class Default : Schulze) $condorcet->getResult('Schulze') ; // Get a the result for a valid method. ``` -##### Get compute details +#### Get compute details ```php $condorcet->getPairwise() ; // Return an explicit array using your Option_ID as keys. @@ -308,13 +319,13 @@ $condorcet->getResultStats('Schulze') ; // Same thing with a specific method. ``` - -##### Customize the code : Add new algorithm(s) +--------------------------------------- +### Customize the code : Add new algorithm(s) Look at how existing algorithm work in the "algorithms" folder, because the algorithms formally included using the same modular schema. *(Exept Condorcet_Basic, which is the only core algorithm and a little bit special.)* **Each new class of algorithm must implements the Condorcet_Algo interface:** -###### Constructor take an array as follow: +##### Constructor take an array as follow: ```php $param['_Pairwise'] = (Calculated Pairwise) ; @@ -322,7 +333,8 @@ $param['_optionsCount'] = (Number of vote option) ; $param['_options'] = (List of option) ; $param['_votes'] = (Vote details) ; // In case you would need to do more complicated things than just work on Pairwise... ``` -###### Link your algorithm + +##### Link your algorithm The class name should be in this format: ```php @@ -337,4 +349,4 @@ Condorcet::addAlgos('AlgorithmName') ; You can specify it as default algorithm: -_See the appropriate instructions at the top._ +_[See the appropriate instructions at the top](#change-the-class-default-method-if-needed)_ diff --git a/lib/Condorcet/algorithms/KemenyYoung.algo.php b/lib/Condorcet/algorithms/KemenyYoung.algo.php index da0d1084..30b8783b 100644 --- a/lib/Condorcet/algorithms/KemenyYoung.algo.php +++ b/lib/Condorcet/algorithms/KemenyYoung.algo.php @@ -10,6 +10,7 @@ namespace Condorcet ; +// Note : This class use some configuration method preset at the bottom of this file. // Kemeny-Young is a Condorcet Algorithm | http://en.wikipedia.org/wiki/Kemeny%E2%80%93Young_method class KemenyYoung implements namespace\Condorcet_Algo @@ -84,7 +85,7 @@ public function getStats () { $explicit[$key] = $value ; - // Human redable + // Human readable foreach ($explicit[$key] as &$candidate_key) { $candidate_key = namespace\Condorcet::getStatic_CandidateId($candidate_key, $this->_Candidates); From a02a157df2187d96b13524955a633cdd192c201a Mon Sep 17 00:00:00 2001 From: Julien Boudry Date: Thu, 24 Apr 2014 22:36:32 +0200 Subject: [PATCH 22/22] Remove the ridiculous internal error handling --- README.md | 6 ------ lib/Condorcet/Condorcet.php | 26 +++----------------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index fd972e80..9a777971 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,6 @@ Look https://packagist.org/packages/julien-boudry/condorcet $condorcet->setMethod('Schulze') ; // Argument : A supported method ``` - #### Change the class default method if needed ```php Condorcet::setClassMethod('Schulze') ; // Argument : A supported method @@ -147,11 +146,6 @@ Condorcet::setClassMethod('Schulze', true) ; // Will force actual and futher obj Condorcet::forceMethod(false) ; // Unforce actual and futher object to use the class default method (or force it if argument is true) ``` -#### About errors -```php -Condorcet::setError(false) ; // _(true by default)_ Unactive or active trigger_error() usage. Can be unefficent depending of your configuration environnement. -``` - #### Get informations ```php $condorcet->getConfig (); // Will return an explicit array about the object and Class Constant. diff --git a/lib/Condorcet/Condorcet.php b/lib/Condorcet/Condorcet.php index ecbf0ab3..1f3c5255 100644 --- a/lib/Condorcet/Condorcet.php +++ b/lib/Condorcet/Condorcet.php @@ -35,7 +35,6 @@ class Condorcet protected static $_authMethods = '' ; protected static $_forceMethod = false ; - protected static $_showError = true ; // Return library version numer public static function getClassVersion () @@ -147,20 +146,6 @@ public static function forceMethod ($force = true) } - // Active trigger_error() - True by default - public static function setError ($param = true) - { - if ($param) - { - self::$_showError = true ; - } - else - { - self::$_showError = false ; - } - } - - public static function error ($code, $infos = null, $level = E_USER_WARNING) { $error[1] = array('text'=>'Bad option format', 'level'=>E_USER_WARNING) ; @@ -178,12 +163,9 @@ public static function error ($code, $infos = null, $level = E_USER_WARNING) if ( array_key_exists($code, $error) ) { - if ( self::$_showError || $error[$code]['level'] < E_USER_WARNING ) - { - trigger_error( $error[$code]['text'].' : '.$infos, $error[$code]['level'] ); - } + trigger_error( $error[$code]['text'].' : '.$infos, $error[$code]['level'] ); } - elseif (self::$_showError) + else { if (!is_null($infos)) { @@ -294,8 +276,6 @@ public function getConfig () 'class_authMethods'=> self::getAuthMethods(), 'force_classMethod'=> self::$_forceMethod, - 'class_showError' => self::$_showError, - 'object_state' => $this->_State ); } @@ -807,7 +787,7 @@ public function getResultStats ($method = null) } else { - return self::error(8,$candidate_id) ; + return self::error(8) ; } if (!is_null($stats))