mirror of
				https://github.com/furyfire/trueskill.git
				synced 2025-10-31 00:12:29 +01:00 
			
		
		
		
	Start of factor graph port. Things don't work yet, but a lot of syntax updates towards PHP
This commit is contained in:
		
							
								
								
									
										46
									
								
								HashMap.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								HashMap.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
|  | ||||
| class HashMap | ||||
| { | ||||
|     private $_hashToValue = array(); | ||||
|     private $_hashToKey = array(); | ||||
|  | ||||
|     public function getValue($key) | ||||
|     { | ||||
|         return $this->_hashToValue[self::getHash($key)]; | ||||
|     } | ||||
|  | ||||
|     public function setValue($key, $value) | ||||
|     { | ||||
|         $hash = self::getHash($key); | ||||
|         $this->_hashToKey[$hash] = $key; | ||||
|         $this->_hashToValue[$hash] = $value; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getAllKeys() | ||||
|     { | ||||
|         return \array_values($this->_hashToKey); | ||||
|     } | ||||
|  | ||||
|     public function getAllValues() | ||||
|     { | ||||
|         return \array_values($this->_hashToValue); | ||||
|     } | ||||
|  | ||||
|     public function count() | ||||
|     { | ||||
|         return \count($this->_hashToKey); | ||||
|     } | ||||
|  | ||||
|     private static function getHash($key) | ||||
|     { | ||||
|         if(\is_object($key)) | ||||
|         { | ||||
|             return \spl_object_hash($key); | ||||
|         } | ||||
|  | ||||
|         return $key; | ||||
|     } | ||||
| } | ||||
| ?> | ||||
							
								
								
									
										96
									
								
								PHPSkills/FactorGraphs/Factor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								PHPSkills/FactorGraphs/Factor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| require_once(dirname(__FILE__) . "../Guard.php"); | ||||
| require_once(dirname(__FILE__) . "../HashMap.php"); | ||||
|  | ||||
| use Moserware\Skills\Guard; | ||||
| use Moserware\Skills\HashMap; | ||||
|  | ||||
| class Factor | ||||
| { | ||||
|     private $_messages = array(); | ||||
|     private $_messageToVariableBinding; | ||||
|  | ||||
|     private $_name; | ||||
|     private $_variables = array(); | ||||
|  | ||||
|     protected function __construct($name) | ||||
|     { | ||||
|         $this->_name = "Factor[" . $name . "]"; | ||||
|         $this->_messagesToVariableBinding = new HashMap(); | ||||
|     } | ||||
|  | ||||
|     /// Returns the log-normalization constant of that factor | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /// Returns the number of messages that the factor has | ||||
|     public function getNumberOfMessages() | ||||
|     { | ||||
|         return count($this->_messages); | ||||
|     } | ||||
|  | ||||
|     protected function getVariables() | ||||
|     { | ||||
|         return $this->_variables; | ||||
|     } | ||||
|  | ||||
|     protected function getMessages() | ||||
|     { | ||||
|         return $this->_messages; | ||||
|     } | ||||
|  | ||||
|     /// Update the message and marginal of the i-th variable that the factor is connected to | ||||
|     public function updateMessageIndex($messageIndex) | ||||
|     { | ||||
|         Guard::argumentIsValidIndex($messageIndex, count($this->_messages), "messageIndex"); | ||||
|         return $this->updateMessageVariable($this->_messages[$messageIndex], $this->_messageToVariableBinding->getValue($messageIndex)); | ||||
|     } | ||||
|  | ||||
|     protected function updateMessageVariable($message, $variable) | ||||
|     { | ||||
|         throw new Exception(); | ||||
|     } | ||||
|  | ||||
|     /// Resets the marginal of the variables a factor is connected to | ||||
|     public function resetMarginals() | ||||
|     { | ||||
|         foreach ($this->_messageToVariableBindings->getAllValues() as $currentVariable) | ||||
|         { | ||||
|             $currentVariable->resetToPrior(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Sends the ith message to the marginal and returns the log-normalization constant | ||||
|     public function sendMessageIndex($messageIndex) | ||||
|     { | ||||
|         Guard::argumentIsValidIndex($messageIndex, count($_messages), "messageIndex"); | ||||
|  | ||||
|         $message = $this->_messages[$messageIndex]; | ||||
|         $variable = $this->_messageToVariableBinding->getValue($message); | ||||
|         return $this->sendMessageVariable($message, $variable); | ||||
|     } | ||||
|  | ||||
|     protected abstract function sendMessageVariable($message, $variable); | ||||
|  | ||||
|     public abstract function createVariableToMessageBinding($variable); | ||||
|  | ||||
|     protected function createVariableToMessageBinding($variable, $message) | ||||
|     { | ||||
|         $index = count($this->_messages); | ||||
|         $this->_messages[] = $message; | ||||
|         $this->_messageToVariableBinding->setValue($message) = $variable; | ||||
|         $this->_variables[] = $variable; | ||||
|         return $message; | ||||
|     } | ||||
|  | ||||
|     public function __toString() | ||||
|     { | ||||
|         return ($this->_name != null) ? $this->_name : base::__toString(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										18
									
								
								PHPSkills/FactorGraphs/FactorGraph.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								PHPSkills/FactorGraphs/FactorGraph.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| class FactorGraph | ||||
| { | ||||
|     private $_variableFactory; | ||||
|  | ||||
|     public function getVariableFactory() | ||||
|     { | ||||
|         return $this->_variableFactory; | ||||
|     } | ||||
|  | ||||
|     public function setVariableFactory($factory) | ||||
|     { | ||||
|         $this->_variableFactory = $factory; | ||||
|     } | ||||
| } | ||||
| ?> | ||||
							
								
								
									
										67
									
								
								PHPSkills/FactorGraphs/FactorGraphLayer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								PHPSkills/FactorGraphs/FactorGraphLayer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| abstract class FactorGraphLayer | ||||
| { | ||||
|     private $_localFactors = array(); | ||||
|     private $_outputVariablesGroups = array();     | ||||
|     private $_inputVariablesGroups = array(); | ||||
|     private $_parentFactorGraph; | ||||
|  | ||||
|     protected function __construct($parentGraph) | ||||
|     { | ||||
|         $this->_parentFactorGraph = $parentGraph; | ||||
|     } | ||||
|  | ||||
|     protected function getInputVariablesGroups() | ||||
|     { | ||||
|         return $this->_inputVariablesGroups;         | ||||
|     } | ||||
|  | ||||
|     // HACK | ||||
|  | ||||
|     public function getParentFactorGraph() | ||||
|     { | ||||
|         return $this->_parentFactorGraph; | ||||
|     } | ||||
|  | ||||
|     public function getOutputVariablesGroups() | ||||
|     { | ||||
|         return $this->_outputVariablesGroups;         | ||||
|     } | ||||
|  | ||||
|     public function getLocalFactors() | ||||
|     { | ||||
|         return $this->_localFactors;         | ||||
|     } | ||||
|  | ||||
|     public function setInputVariablesGroups($value) | ||||
|     { | ||||
|         $this->_inputVariablesGroups = $value; | ||||
|     } | ||||
|  | ||||
|     protected function scheduleSequence($itemsToSequence) | ||||
|     { | ||||
|         return new ScheduleSequence($itemsToSequence); | ||||
|     } | ||||
|  | ||||
|     protected function addLayerFactor($factor) | ||||
|     { | ||||
|         $this->_localFactors[] = $factor; | ||||
|     } | ||||
|  | ||||
|     public abstract function buildLayer(); | ||||
|  | ||||
|     public function createPriorSchedule() | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public function createPosteriorSchedule() | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										57
									
								
								PHPSkills/FactorGraphs/FactorList.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								PHPSkills/FactorGraphs/FactorList.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| /// <summary> | ||||
| /// Helper class for computing the factor graph's normalization constant. | ||||
| /// </summary> | ||||
| class FactorList | ||||
| { | ||||
|     private $_list = array(); | ||||
|  | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         foreach($this->_list as $currentFactor) | ||||
|         { | ||||
|             $currentFactor->resetMarginals(); | ||||
|         } | ||||
|  | ||||
|         $sumLogZ = 0.0; | ||||
|  | ||||
|         $listCount = count($this->_list); | ||||
|  | ||||
|         for ($i = 0; $i < $listCount; $i++) | ||||
|         { | ||||
|             $f = $this->_list[$i]; | ||||
|  | ||||
|             $numberOfMessages = $f->getNumberOfMessages(); | ||||
|  | ||||
|             for ($j = 0; $j < $numberOfMessages; $j++) | ||||
|             { | ||||
|                 $sumLogZ += $f->sendMessageIndex($j); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $sumLogS = 0; | ||||
|  | ||||
|         foreach($this->_list as $currentFactor) | ||||
|         { | ||||
|             $sumLogS = $sumLogS + $currentFactor->getLogNormalization(); | ||||
|         } | ||||
|  | ||||
|         return $sumLogZ + $sumLogS; | ||||
|     } | ||||
|  | ||||
|     public function count() | ||||
|     { | ||||
|         return count($this->_list); | ||||
|     } | ||||
|  | ||||
|     public function addFactor(Factor $factor) | ||||
|     { | ||||
|         $this->_list[] = $factor; | ||||
|         return $factor; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										28
									
								
								PHPSkills/FactorGraphs/Message.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								PHPSkills/FactorGraphs/Message.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| class Message | ||||
| { | ||||
|     private $_nameFormat; | ||||
|     private $_nameFormatArgs; | ||||
|     private $_value; | ||||
|  | ||||
|     public function __construct($value = null, $nameFormat = null, $args = null) | ||||
|     { | ||||
|         $this->_nameFormat = $nameFormat; | ||||
|         $this->_nameFormatArgs = $args; | ||||
|         $this->_value = $value; | ||||
|     } | ||||
|  | ||||
|     public function getValue() | ||||
|     { | ||||
|         return $this->_value; | ||||
|     } | ||||
|  | ||||
|     public function __toString() | ||||
|     { | ||||
|         return $this->_nameFormat; //return (_NameFormat == null) ? base.ToString() : String.Format(_NameFormat, _NameFormatArgs); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										89
									
								
								PHPSkills/FactorGraphs/Schedule.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								PHPSkills/FactorGraphs/Schedule.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| abstract class Schedule | ||||
| { | ||||
|     private $_name; | ||||
|  | ||||
|     protected function __construct($name) | ||||
|     { | ||||
|         $this->_name = $name; | ||||
|     } | ||||
|  | ||||
|     public abstract function visit($depth = -1, $maxDepth = 0); | ||||
|  | ||||
|     public function __toString() | ||||
|     { | ||||
|         return $this->_name; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class ScheduleStep extends Schedule | ||||
| { | ||||
|     private $_factor; | ||||
|     private $_index; | ||||
|  | ||||
|     public function __construct($name, $factor, $index) | ||||
|     { | ||||
|         parent::__construct($name); | ||||
|         $this->_factor = $factor; | ||||
|         $this->_index = $index; | ||||
|     } | ||||
|  | ||||
|     public function visit($depth, $maxDepth) | ||||
|     { | ||||
|         $delta = $this->_factor->updateMessageIndex($this->_index); | ||||
|         return $delta; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class ScheduleSequence extends Schedule | ||||
| { | ||||
|     private $_schedules; | ||||
|  | ||||
|     public function __construct($name, $schedules) | ||||
|     { | ||||
|         parent::__construct($name); | ||||
|         $this->_schedules = $schedules; | ||||
|     } | ||||
|  | ||||
|     public function visit($depth, $maxDepth) | ||||
|     { | ||||
|         $maxDelta = 0; | ||||
|  | ||||
|         foreach ($this->_schedules as $currentSchedule) | ||||
|         { | ||||
|             $maxDelta = max($currentSchedule->visit($depth + 1, $maxDepth), $maxDelta); | ||||
|         } | ||||
|  | ||||
|         return $maxDelta; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class ScheduleLoop extends Schedule | ||||
| { | ||||
|     private $_maxDelta; | ||||
|     private $_scheduleToLoop; | ||||
|  | ||||
|     public function __construct($name, Schedule $scheduleToLoop, $maxDelta) | ||||
|     { | ||||
|         parent::__construct($name); | ||||
|         $this->_scheduleToLoop = $scheduleToLoop; | ||||
|         $this->_maxDelta = $maxDelta; | ||||
|     } | ||||
|  | ||||
|     public function visit($depth, $maxDepth) | ||||
|     { | ||||
|         $totalIterations = 1; | ||||
|         $delta = $this->_scheduleToLoop->visit($depth + 1, $maxDepth); | ||||
|         while ($delta > $this->_maxDelta) | ||||
|         { | ||||
|             $delta = $this->_scheduleToLoop->visit($depth + 1, $maxDepth); | ||||
|             $totalIterations++; | ||||
|         } | ||||
|  | ||||
|         return $delta; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										71
									
								
								PHPSkills/FactorGraphs/Variable.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								PHPSkills/FactorGraphs/Variable.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| class Variable | ||||
| { | ||||
|     private $_name; | ||||
|     private $_prior; | ||||
|     private $_value; | ||||
|  | ||||
|     public function __construct($name, $prior) | ||||
|     { | ||||
|         $this->_name = "Variable[" . $name . "]"; | ||||
|         $this->_prior = $prior; | ||||
|         $this->resetToPrior(); | ||||
|     } | ||||
|  | ||||
|     public function getValue() | ||||
|     { | ||||
|         return $this->_value; | ||||
|     } | ||||
|  | ||||
|     public function setValue($value) | ||||
|     { | ||||
|         $this->_value = $value; | ||||
|     } | ||||
|  | ||||
|     public function resetToPrior() | ||||
|     { | ||||
|         $this->_value = $this->_prior; | ||||
|     } | ||||
|  | ||||
|     public function __toString() | ||||
|     { | ||||
|         return $this->_name; | ||||
|     } | ||||
| } | ||||
|  | ||||
| class DefaultVariable extends Variable | ||||
| { | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct("Default", null); | ||||
|     } | ||||
|  | ||||
|     public function getValue() | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public function setValue($value) | ||||
|     { | ||||
|         throw new Exception(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| class KeyedVariable extends Variable | ||||
| { | ||||
|     private $_key; | ||||
|     public function __construct($key, $name, $prior) | ||||
|     { | ||||
|         parent::__construct($name, $prior); | ||||
|         $this->_key = $key; | ||||
|     } | ||||
|  | ||||
|     public function getKey() | ||||
|     { | ||||
|         return $this->_key; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										28
									
								
								PHPSkills/FactorGraphs/VariableFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								PHPSkills/FactorGraphs/VariableFactory.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\FactorGraphs; | ||||
|  | ||||
| class VariableFactory | ||||
| { | ||||
|     // using a Func<TValue> to encourage fresh copies in case it's overwritten | ||||
|     private $_variablePriorInitializer; | ||||
|  | ||||
|     public function __construct($variablePriorInitializer) | ||||
|     { | ||||
|         $this->_variablePriorInitializer = $variablePriorInitializer; | ||||
|     } | ||||
|  | ||||
|     public function createBasicVariable() | ||||
|     { | ||||
|         $newVar = new Variable($this->_variablePriorInitializer()); | ||||
|         return $newVar; | ||||
|     } | ||||
|  | ||||
|     public function createKeyedVariable($key) | ||||
|     { | ||||
|         $newVar = new KeyedVariable($key, $this->_variablePriorInitializer()); | ||||
|         return $newVar; | ||||
|     } | ||||
| }s | ||||
|  | ||||
| ?> | ||||
| @@ -48,6 +48,16 @@ class GaussianDistribution | ||||
|     { | ||||
|         return $this->_standardDeviation; | ||||
|     } | ||||
|  | ||||
|     public function getPrecision() | ||||
|     { | ||||
|         return $this->_precision; | ||||
|     } | ||||
|  | ||||
|     public function getPrecisionMean() | ||||
|     { | ||||
|         return $this->_precisionMean; | ||||
|     } | ||||
|      | ||||
|     public function getNormalizationConstant() | ||||
|     {         | ||||
|   | ||||
| @@ -1,46 +1,40 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills; | ||||
|  | ||||
| require_once(dirname(__FILE__) . "/HashMap.php"); | ||||
|  | ||||
| class RatingContainer | ||||
| { | ||||
|     private $_playerHashToRating = array(); | ||||
|     private $_playerHashToPlayer = array(); | ||||
|     private $_playerToRating; | ||||
|  | ||||
|     public function __construct() | ||||
|     { | ||||
|         $this->_playerToRating = new \HashMap(); | ||||
|     } | ||||
|  | ||||
|     public function getRating($player) | ||||
|     { | ||||
|         return $this->_playerHashToRating[self::getHash($player)]; | ||||
|         return $this->_playerToRating->getValue($player); | ||||
|     } | ||||
|  | ||||
|     public function setRating($player, $rating) | ||||
|     { | ||||
|         $hash = self::getHash($player); | ||||
|         $this->_playerHashToPlayer[$hash] = $player; | ||||
|         $this->_playerHashToRating[$hash] = $rating; | ||||
|         return $this; | ||||
|         return $this->_playerToRating->setValue($player, $rating); | ||||
|     } | ||||
|      | ||||
|     public function getAllPlayers() | ||||
|     { | ||||
|         return \array_values($this->_playerHashToPlayer); | ||||
|         return $this->_playerToRating->getAllKeys(); | ||||
|     } | ||||
|      | ||||
|     public function getAllRatings() | ||||
|     { | ||||
|         return \array_values($this->_playerHashToRating); | ||||
|         return $this->_playerToRating->getAllValues(); | ||||
|     } | ||||
|  | ||||
|     public function count() | ||||
|     { | ||||
|         return \count($this->_playerHashToPlayer); | ||||
|     } | ||||
|     private static function getHash($player) | ||||
|     { | ||||
|         if(\is_object($player)) | ||||
|         { | ||||
|             return \spl_object_hash($player); | ||||
|         } | ||||
|  | ||||
|         return $player; | ||||
|     } | ||||
|         return \count($this->_playerToRating->count()); | ||||
|     }     | ||||
| } | ||||
| ?> | ||||
|   | ||||
							
								
								
									
										168
									
								
								PHPSkills/TrueSkill/FactorGraphTrueSkillCalculator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								PHPSkills/TrueSkill/FactorGraphTrueSkillCalculator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill; | ||||
|  | ||||
| /// <summary> | ||||
| /// Calculates TrueSkill using a full factor graph. | ||||
| /// </summary> | ||||
| class FactorGraphTrueSkillCalculator extends SkillCalculator | ||||
| { | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(SkillCalculatorSupportedOptions::PARTIAL_PLAY | SkillCalculatorSupportedOptions::PARTIAL_UPDATE, TeamsRange::atLeast(2), PlayersRange::atLeast(1)); | ||||
|     } | ||||
|  | ||||
|     public function CalculateNewRatings(GameInfo $gameInfo, | ||||
|                                         array $teams, | ||||
|                                         array $teamRanks) | ||||
|     { | ||||
|         Guard::argumentNotNull($gameInfo, "gameInfo"); | ||||
|         $this->validateTeamCountAndPlayersCountPerTeam($teams); | ||||
|  | ||||
|         RankSorter::sort($teams, $teamRanks); | ||||
|  | ||||
|         $factorGraph = new TrueSkillFactorGraph($gameInfo, $teams, $teamRanks); | ||||
|         $factorGraph->buildGraph(); | ||||
|         $factorGraph->runSchedule(); | ||||
|  | ||||
|         $probabilityOfOutcome = $factorGraph->getProbabilityOfRanking(); | ||||
|  | ||||
|         return $factorGraph->getUpdatedRatings(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function calculateMatchQuality(GameInfo $gameInfo, | ||||
|                                           array $teams) | ||||
|     { | ||||
|         // We need to create the A matrix which is the player team assigments. | ||||
|         $teamAssignmentsList = $teams.ToList(); | ||||
|         $skillsMatrix = $this->getPlayerCovarianceMatrix($teamAssignmentsList); | ||||
|         $meanVector = $this->getPlayerMeansVector($teamAssignmentsList); | ||||
|         $meanVectorTranspose = $meanVector->getTranspose(); | ||||
|  | ||||
|         $playerTeamAssignmentsMatrix = $this->createPlayerTeamAssignmentMatrix($teamAssignmentsList, $meanVector->getRowCount()); | ||||
|         $playerTeamAssignmentsMatrixTranspose = $playerTeamAssignmentsMatrix->getTranspose(); | ||||
|  | ||||
|         $betaSquared = square($gameInfo->getBeta()); | ||||
|  | ||||
|         $start = Matrix::multiply($meanVectorTranspose, $playerTeamAssignmentsMatrix); | ||||
|         $aTa = Matrix::multiply( | ||||
|                         Matrix::scalarMultiply($betaSquared, | ||||
|                                                $playerTeamAssignmentsMatrixTranspose), | ||||
|                         $playerTeamAssignmentsMatrix); | ||||
|  | ||||
|         $aTSA = Matrix::multiply( | ||||
|                     Matrix::multiply($playerTeamAssignmentsMatrixTranspose, $skillsMatrix), | ||||
|                     $playerTeamAssignmentsMatrix); | ||||
|  | ||||
|         $middle = Matrix::add($aTa, $aTSA); | ||||
|  | ||||
|         $middleInverse = $middle->getInverse(); | ||||
|  | ||||
|         $end = Matrix::multiply($playerTeamAssignmentsMatrixTranspose, $meanVector); | ||||
|  | ||||
|         $expPartMatrix = Matrix::scalarMultiply(-0.5, (Matrix::multiply(Matrix::multiply($start, $middleInverse), $end))); | ||||
|         $expPart = $expPartMatrix->getDeterminant(); | ||||
|  | ||||
|         $sqrtPartNumerator = $aTa->getDeterminant(); | ||||
|         $sqrtPartDenominator = $middle->getDeterminant(); | ||||
|         $sqrtPart = $sqrtPartNumerator / $sqrtPartDenominator; | ||||
|  | ||||
|         $result = exp($expPart) * sqrt($sqrtPart); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     private static function getPlayerMeansVector(array $teamAssignmentsList) | ||||
|     { | ||||
|         // A simple vector of all the player means. | ||||
|         return new Vector($this->getPlayerRatingValues($teamAssignmentsList, | ||||
|                                                        function($rating)  | ||||
|                                                        {  | ||||
|                                                             return $rating->getMean(); | ||||
|                                                        })); | ||||
|     } | ||||
|  | ||||
|     private static function getPlayerCovarianceMatrix(array $teamAssignmentsList) | ||||
|     { | ||||
|         // This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all | ||||
|         // players. | ||||
|         return new DiagonalMatrix( | ||||
|                         $this->getPlayerRatingValues($teamAssignmentsList, | ||||
|                                                      function($rating) | ||||
|                                                      { | ||||
|                                                         return square($rating->getStandardDeviation()); | ||||
|                                                      })); | ||||
|     } | ||||
|  | ||||
|     // Helper function that gets a list of values for all player ratings | ||||
|     private static function getPlayerRatingValues(array $teamAssignmentsList, | ||||
|                                                   $playerRatingFunction) | ||||
|     { | ||||
|         $playerRatingValues = array(); | ||||
|  | ||||
|         foreach ($teamAssignmentsList as $currentTeam) | ||||
|         { | ||||
|             foreach (currentTeam.Values as $currentRating) | ||||
|             { | ||||
|                 $playerRatingValues[] = $playerRatingFunction($currentRating); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $playerRatingValues; | ||||
|     } | ||||
|  | ||||
|     private static function createPlayerTeamAssignmentMatrix($teamAssignmentsList, $totalPlayers) | ||||
|     { | ||||
|         // The team assignment matrix is often referred to as the "A" matrix. It's a matrix whose rows represent the players | ||||
|         // and the columns represent teams. At Matrix[row, column] represents that player[row] is on team[col] | ||||
|         // Positive values represent an assignment and a negative value means that we subtract the value of the next | ||||
|         // team since we're dealing with pairs. This means that this matrix always has teams - 1 columns. | ||||
|         // The only other tricky thing is that values represent the play percentage. | ||||
|  | ||||
|         // For example, consider a 3 team game where team1 is just player1, team 2 is player 2 and player 3, and | ||||
|         // team3 is just player 4. Furthermore, player 2 and player 3 on team 2 played 25% and 75% of the time | ||||
|         // (e.g. partial play), the A matrix would be: | ||||
|  | ||||
|         // A = this 4x2 matrix: | ||||
|         // |  1.00  0.00 | | ||||
|         // | -0.25  0.25 | | ||||
|         // | -0.75  0.75 | | ||||
|         // |  0.00 -1.00 | | ||||
|  | ||||
|         $playerAssignments = array(); | ||||
|         $totalPreviousPlayers = 0; | ||||
|  | ||||
|         $teamAssignmentsListCount = count($teamAssignmentsList); | ||||
|  | ||||
|         for ($i = 0; $i < $teamAssignmentsListCount - 1; $i++) | ||||
|         { | ||||
|             $currentTeam = $teamAssignmentsList[$i]; | ||||
|  | ||||
|             // Need to add in 0's for all the previous players, since they're not | ||||
|             // on this team | ||||
|             $currentRowValues = array(); | ||||
|             $playerAssignments[] = &$currentRowValues; | ||||
|  | ||||
|             foreach ($currentTeam as $currentRating) | ||||
|             { | ||||
|                 $currentRowValues[] = PartialPlay::getPartialPlayPercentage($currentRating->getKey()); | ||||
|                 // indicates the player is on the team | ||||
|                 $totalPreviousPlayers++; | ||||
|             } | ||||
|  | ||||
|             $nextTeam = $teamAssignmentsList[$i + 1]; | ||||
|             foreach ($nextTeam as $nextTeamPlayerPair) | ||||
|             { | ||||
|                 // Add a -1 * playing time to represent the difference | ||||
|                 $currentRowValues[] = -1 * PartialPlay::getPartialPlayPercentage($nextTeamPlayerPair->getKey()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $playerTeamAssignmentsMatrix = new Matrix($totalPlayers, $teamAssignmentsListCount - 1, $playerAssignments); | ||||
|  | ||||
|         return $playerTeamAssignmentsMatrix; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										32
									
								
								PHPSkills/TrueSkill/Factors/GaussianFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								PHPSkills/TrueSkill/Factors/GaussianFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| abstract class GaussianFactor extends Factor | ||||
| { | ||||
|     protected function __construct($name) | ||||
|     { | ||||
|         parent::__construct($name); | ||||
|     } | ||||
|  | ||||
|     /// Sends the factor-graph message with and returns the log-normalization constant | ||||
|     protected function sendMessageVariable(Message $message, Variable $variable) | ||||
|     { | ||||
|         $marginal = $variable->getValue(); | ||||
|         $messageValue = $message->getValue(); | ||||
|         $logZ = GaussianDistribution::logProductNormalization($marginal, $messageValue); | ||||
|         $variable->setValue($marginal*$messageValue); | ||||
|         return $logZ; | ||||
|     } | ||||
|  | ||||
|     public function createVariableToMessageBinding(Variable $variable) | ||||
|     { | ||||
|         return parent::createVariableToMessageBinding($variable, | ||||
|                                                       new Message( | ||||
|                                                           GaussianDistribution::fromPrecisionMean(0, 0), | ||||
|                                                           "message from {0} to {1}", $this)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										73
									
								
								PHPSkills/TrueSkill/Factors/GaussianGreaterThanFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								PHPSkills/TrueSkill/Factors/GaussianGreaterThanFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <?php | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| /// <summary> | ||||
| /// Factor representing a team difference that has exceeded the draw margin. | ||||
| /// </summary> | ||||
| /// <remarks>See the accompanying math paper for more details.</remarks> | ||||
| class GaussianGreaterThanFactor extends GaussianFactor | ||||
| { | ||||
|     private $_epsilon; | ||||
|  | ||||
|     public function __construct($epsilon, Variable $variable) | ||||
|     { | ||||
|         parent::_construct("{0} > {1:0.000}"); | ||||
|         $this->_epsilon = $epsilon; | ||||
|         $this->createVariableToMessageBinding($variable); | ||||
|     } | ||||
|  | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         $vars = $this->getVariables(); | ||||
|         $marginal = $vars[0]->getValue(); | ||||
|         $messages = $this->getMessages(); | ||||
|         $message = $messages[0]->getValue(); | ||||
|         $messageFromVariable = GaussianDistribution::divide($marginal, $message); | ||||
|         return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) | ||||
|                + | ||||
|                log( | ||||
|                    GaussianDistribution::cumulativeTo(($messageFromVariable->getMean() - $this->_epsilon)/ | ||||
|                                                      $messageFromVariable->getStandardDeviation())); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected function updateMessageVariable(Message $message, Variable $variable) | ||||
|     { | ||||
|         $oldMarginal = clone $variable->getValue(); | ||||
|         $oldMessage = clone $message->getValue(); | ||||
|         $messageFromVar = GaussianDistribution::divide($oldMarginal, $oldMessage); | ||||
|  | ||||
|         $c = $messageFromVar->getPrecision(); | ||||
|         $d = $messageFromVar->getPrecisionMean(); | ||||
|  | ||||
|         $sqrtC = sqrt($c); | ||||
|  | ||||
|         $dOnSqrtC = $d/$sqrtC; | ||||
|  | ||||
|         $epsilsonTimesSqrtC = $this->_epsilon*$sqrtC; | ||||
|         $d = $messageFromVar->getPrecisionMean(); | ||||
|  | ||||
|         $denom = 1.0 - TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC); | ||||
|  | ||||
|         $newPrecision = $c/$denom; | ||||
|         $newPrecisionMean = ($d + | ||||
|                              $sqrtC* | ||||
|                              TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC))/ | ||||
|                              $denom; | ||||
|  | ||||
|         $newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision); | ||||
|  | ||||
|         $newMessage = GaussianDistribution::divide( | ||||
|                               GaussianDistribution::multiply($oldMessage, $newMarginal), | ||||
|                               $oldMarginal); | ||||
|  | ||||
|         /// Update the message and marginal | ||||
|         $message->setValue($newMessage); | ||||
|  | ||||
|         $variable->setValue($newMarginal); | ||||
|  | ||||
|         /// Return the difference in the new marginal | ||||
|         return GaussianDistribution::subtract($newMarginal, $oldMarginal); | ||||
|     } | ||||
| } | ||||
| ?> | ||||
							
								
								
									
										78
									
								
								PHPSkills/TrueSkill/Factors/GaussianLikelihoodFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								PHPSkills/TrueSkill/Factors/GaussianLikelihoodFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| /// <summary> | ||||
| /// Connects two variables and adds uncertainty. | ||||
| /// </summary> | ||||
| /// <remarks>See the accompanying math paper for more details.</remarks> | ||||
| class GaussianLikelihoodFactor extends GaussianFactor | ||||
| { | ||||
|     private $_precision; | ||||
|  | ||||
|     public function __construct($betaSquared, Variable $variable1, Variable $variable2) | ||||
|     { | ||||
|         parent::__construct("Likelihood of {0} going to {1}"); | ||||
|         $this->_precision = 1.0/$betaSquared; | ||||
|         $this->createVariableToMessageBinding($variable1); | ||||
|         $this->createVariableToMessageBinding($variable2); | ||||
|     } | ||||
|  | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         $vars = $this->getVariables(); | ||||
|         $messages = $this->getMessages(); | ||||
|  | ||||
|         return GaussianDistribution::logRatioNormalization( | ||||
|                 $vars[0]->getValue(), | ||||
|                 $messages[0]->getValue()); | ||||
|     } | ||||
|  | ||||
|     private function updateHelper(Message $message1, Message $message2, | ||||
|                                   Variable $variable1, Variable $variable2) | ||||
|     { | ||||
|         $message1Value = clone $message1->getValue(); | ||||
|         $message2Value = clone $message2->getValue(); | ||||
|  | ||||
|         $marginal1 = clone $variable1->getValue(); | ||||
|         $marginal2 = clone $variable2->getValue(); | ||||
|  | ||||
|         $a = $this->_precision/($this->_precision + $marginal2->getPrecision() - $message2Value->getPrecision()); | ||||
|  | ||||
|         $newMessage = GaussianDistribution::fromPrecisionMean( | ||||
|             $a*($marginal2->getPrecisionMean() - $message2Value->getPrecisionMean()), | ||||
|             $a*($marginal2->getPrecision() - $message2Value->getPrecision())); | ||||
|  | ||||
|         $oldMarginalWithoutMessage = GaussianDistribution::divide($marginal1, $message1Value); | ||||
|  | ||||
|         $newMarginal = GaussianDistribution::multiply($oldMarginalWithoutMessage, $newMessage); | ||||
|  | ||||
|         /// Update the message and marginal | ||||
|  | ||||
|         $message1->setValue($newMessage); | ||||
|         $variable1->setValue($newMarginal); | ||||
|  | ||||
|         /// Return the difference in the new marginal | ||||
|         return GaussianDistribution::subtract($newMarginal, $marginal1); | ||||
|     } | ||||
|  | ||||
|     public function updateMessageIndex($messageIndex) | ||||
|     { | ||||
|         $messages = $this->getMessages(); | ||||
|         $vars = $this->getVariables(); | ||||
|  | ||||
|         switch ($messageIndex) | ||||
|         { | ||||
|             case 0: | ||||
|                 return $this->updateHelper($messages[0], $messages[1], | ||||
|                                            $vars[0], $vars[1]); | ||||
|             case 1: | ||||
|                 return $this->updateHelper($messages[1], $messages[0], | ||||
|                                            $vars[1], $vars[0]); | ||||
|             default: | ||||
|                 throw new Exception(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										40
									
								
								PHPSkills/TrueSkill/Factors/GaussianPriorFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								PHPSkills/TrueSkill/Factors/GaussianPriorFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| /// <summary> | ||||
| /// Supplies the factor graph with prior information. | ||||
| /// </summary> | ||||
| /// <remarks>See the accompanying math paper for more details.</remarks> | ||||
| class GaussianPriorFactor extends GaussianFactor | ||||
| { | ||||
|     private $_newMessage; | ||||
|  | ||||
|     public function __construct($mean, $variance, Variable $variable) | ||||
|     { | ||||
|         parent::__construct("Prior value going to {0}"); | ||||
|         $this->_newMessage = new GaussianDistribution($mean, sqrt($variance)); | ||||
|         $this->createVariableToMessageBinding($variable, | ||||
|                                                new Message( | ||||
|                                                    GaussianDistribution::fromPrecisionMean(0, 0), | ||||
|                                                        "message from {0} to {1}", | ||||
|                                                    this, variable)); | ||||
|     } | ||||
|  | ||||
|     protected function updateMessageVariable(Message $message, Variable $variable) | ||||
|     { | ||||
|         $oldMarginal = clone $variable->getValue(); | ||||
|         $oldMessage = $message; | ||||
|         $newMarginal = | ||||
|             GaussianDistribution::fromPrecisionMean( | ||||
|                 $oldMarginal->getPrecisionMean() + $this->_newMessage->getPrecisionMean() - $oldMessage->getValue()->getPrecisionMean(), | ||||
|                 $oldMarginal->getPrecision() + $this->_newMessage->getPrecision() - $oldMessage->getValue()->getPrecision()); | ||||
|  | ||||
|         $variable->setValue($newMarginal); | ||||
|         $message->setValue($this->_newMessage); | ||||
|         return GaussianDistribution::subtract($oldMarginal, $newMarginal); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										216
									
								
								PHPSkills/TrueSkill/Factors/GaussianWeightedSumFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								PHPSkills/TrueSkill/Factors/GaussianWeightedSumFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| /// <summary> | ||||
| /// Factor that sums together multiple Gaussians. | ||||
| /// </summary> | ||||
| /// <remarks>See the accompanying math paper for more details.</remarks> | ||||
| class GaussianWeightedSumFactor extends GaussianFactor | ||||
| { | ||||
|     private $_variableIndexOrdersForWeights = array(); | ||||
|  | ||||
|     // This following is used for convenience, for example, the first entry is [0, 1, 2] | ||||
|     // corresponding to v[0] = a1*v[1] + a2*v[2] | ||||
|     private $_weights; | ||||
|     private $_weightsSquared; | ||||
|  | ||||
|     public function __construct(Variable $sumVariable, array $variablesToSum, array $variableWeights = null) | ||||
|     { | ||||
|         parent::__construct($this->createName($sumVariable, $variablesToSum, $variableWeights)); | ||||
|         $this->_weights = array(); | ||||
|         $this->_weightsSquared = array(); | ||||
|  | ||||
|         // The first weights are a straightforward copy | ||||
|         // v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n | ||||
|         $this->_weights[0] = array(); | ||||
|  | ||||
|         $variableWeightsLength = count($variableWeights); | ||||
|  | ||||
|         for($i = 0; $i < $variableWeightsLength; $i++) | ||||
|         { | ||||
|             $weight = $variableWeights[$i]; | ||||
|             $this->_weights[0][$i] = $weight; | ||||
|             $this->_weightsSquared[0][i] = $weight * $weight; | ||||
|         } | ||||
|  | ||||
|         $variablesToSumLength = count($variablesToSum); | ||||
|  | ||||
|         // 0..n-1 | ||||
|         for($i = 0; $i < ($variablesToSumLength + 1); $i++) | ||||
|         { | ||||
|             $this->_variableIndexOrdersForWeights[] = $i; | ||||
|         } | ||||
|          | ||||
|         // The rest move the variables around and divide out the constant. | ||||
|         // For example: | ||||
|         // v_1 = (-a_2 / a_1) * v_2 + (-a3/a1) * v_3 + ... + (1.0 / a_1) * v_0 | ||||
|         // By convention, we'll put the v_0 term at the end | ||||
|  | ||||
|         $weightsLength = $variableWeightsLength + 1; | ||||
|         for ($weightsIndex = 1; $weightsIndex < $weightsLength; $weightsIndex++) | ||||
|         {             | ||||
|             $this->_weights[$weightsIndex] = array(); | ||||
|  | ||||
|             $variableIndices = array(); | ||||
|             $variableIndices[] = $weightsIndex; | ||||
|  | ||||
|             $currentWeightsSquared = array(); | ||||
|             $this->_WeightsSquared[$weightsIndex] = $currentWeightsSquared; | ||||
|  | ||||
|             // keep a single variable to keep track of where we are in the array. | ||||
|             // This is helpful since we skip over one of the spots | ||||
|             $currentDestinationWeightIndex = 0; | ||||
|  | ||||
|             for ($currentWeightSourceIndex = 0; | ||||
|                  $currentWeightSourceIndex < $variableWeights.Length; | ||||
|                  $currentWeightSourceIndex++) | ||||
|             { | ||||
|                 if ($currentWeightSourceIndex == ($weightsIndex - 1)) | ||||
|                 { | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 $currentWeight = (-$variableWeights[$currentWeightSourceIndex]/$variableWeights[$weightsIndex - 1]); | ||||
|  | ||||
|                 if ($variableWeights[$weightsIndex - 1] == 0) | ||||
|                 { | ||||
|                     // HACK: Getting around division by zero | ||||
|                     $currentWeight = 0; | ||||
|                 } | ||||
|  | ||||
|                 $currentWeights[$currentDestinationWeightIndex] = $currentWeight; | ||||
|                 $currentWeightsSquared[$currentDestinationWeightIndex] = $currentWeight*currentWeight; | ||||
|  | ||||
|                 $variableIndices[$currentDestinationWeightIndex + 1] = $currentWeightSourceIndex + 1; | ||||
|                 $currentDestinationWeightIndex++; | ||||
|             } | ||||
|  | ||||
|             // And the final one | ||||
|             $finalWeight = 1.0/$variableWeights[$weightsIndex - 1]; | ||||
|  | ||||
|             if ($variableWeights[$weightsIndex - 1] == 0) | ||||
|             { | ||||
|                 // HACK: Getting around division by zero | ||||
|                 $finalWeight = 0; | ||||
|             } | ||||
|             $currentWeights[$currentDestinationWeightIndex] = finalWeight; | ||||
|             $currentWeightsSquared[currentDestinationWeightIndex] = finalWeight*finalWeight; | ||||
|             $variableIndices[$variableIndices.Length - 1] = 0; | ||||
|             $this->_variableIndexOrdersForWeights[] = $variableIndices; | ||||
|         } | ||||
|  | ||||
|         $this->createVariableToMessageBinding($sumVariable); | ||||
|  | ||||
|         foreach ($variablesToSum as $currentVariable) | ||||
|         { | ||||
|             $this->createVariableToMessageBinding($currentVariable); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         $vars = $this->getVariables(); | ||||
|         $messages = $this->getMessages(); | ||||
|  | ||||
|         $result = 0.0; | ||||
|  | ||||
|         // We start at 1 since offset 0 has the sum | ||||
|         $varCount = count($vars); | ||||
|         for ($i = 1; $i < $varCount; $i++) | ||||
|         { | ||||
|             $result += GaussianDistribution::logRatioNormalization($vars[i]->getValue(), $messages[i]->getValue()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     private function updateHelper(array $weights, array $weightsSquared, | ||||
|                                   array $messages, | ||||
|                                   array $variables) | ||||
|     { | ||||
|         // Potentially look at http://mathworld.wolfram.com/NormalSumDistribution.html for clues as | ||||
|         // to what it's doing | ||||
|  | ||||
|         $messages = $this->getMessages(); | ||||
|         $message0 = clone $messages[0]->getValue(); | ||||
|         $marginal0 = clone $variables[0]->getValue(); | ||||
|  | ||||
|         // The math works out so that 1/newPrecision = sum of a_i^2 /marginalsWithoutMessages[i] | ||||
|         $inverseOfNewPrecisionSum = 0.0; | ||||
|         $anotherInverseOfNewPrecisionSum = 0.0; | ||||
|         $weightedMeanSum = 0.0; | ||||
|         $anotherWeightedMeanSum = 0.0; | ||||
|  | ||||
|         $weightsSquaredLength = count($weightsSquared); | ||||
|  | ||||
|         for ($i = 0; $i < $weightsSquaredLength; $i++) | ||||
|         { | ||||
|             // These flow directly from the paper | ||||
|  | ||||
|             $inverseOfNewPrecisionSum += $weightsSquared[i]/ | ||||
|                                          ($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision()); | ||||
|  | ||||
|             $diff = GaussianDistribution::divide($variables[$i + 1]->getValue(), $messages[$i + 1]->getValue()); | ||||
|             $anotherInverseOfNewPrecisionSum += $weightsSquared[i]/$diff->getPrecision(); | ||||
|  | ||||
|             $weightedMeanSum += $weights[i] | ||||
|                                 * | ||||
|                                 ($variables[$i + 1]->getValue()->getPrecisionMean() - $messages[$i + 1]->getValue()->getPrecisionMean()) | ||||
|                                 / | ||||
|                                 ($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision()); | ||||
|  | ||||
|             $anotherWeightedMeanSum += $weights[$i]*$diff->getPrecisionMean()/$diff->getPrecision(); | ||||
|         } | ||||
|  | ||||
|         $newPrecision = 1.0/$inverseOfNewPrecisionSum; | ||||
|         $anotherNewPrecision = 1.0/$anotherInverseOfNewPrecisionSum; | ||||
|  | ||||
|         $newPrecisionMean = $newPrecision*$weightedMeanSum; | ||||
|         $anotherNewPrecisionMean = $anotherNewPrecision*$anotherWeightedMeanSum; | ||||
|  | ||||
|         $newMessage = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision); | ||||
|         $oldMarginalWithoutMessage = GaussianDistribution::divide($marginal0, $message0); | ||||
|  | ||||
|         $newMarginal = GaussianDistribution::multiply($oldMarginalWithoutMessage, $newMessage); | ||||
|  | ||||
|         /// Update the message and marginal | ||||
|  | ||||
|         $messages[0]->setValue($newMessage); | ||||
|         $variables[0]->setValue($newMarginal); | ||||
|  | ||||
|         /// Return the difference in the new marginal | ||||
|         $finalDiff = GaussianDistribution::subtract($newMarginal, $marginal0); | ||||
|         return $finalDiff; | ||||
|     } | ||||
|  | ||||
|     public function updateMessageIndex($messageIndex) | ||||
|     { | ||||
|         $allMessages = $this->getMessages(); | ||||
|         $allVariables = $this->getVariables(); | ||||
|  | ||||
|         Guard::argumentIsValidIndex($messageIndex, count($allMessages),"messageIndex"); | ||||
|  | ||||
|         $updatedMessages = array(); | ||||
|         $updatedVariables = array(); | ||||
|  | ||||
|         $indicesToUse = $this->_variableIndexOrdersForWeights[$messageIndex]; | ||||
|  | ||||
|         // The tricky part here is that we have to put the messages and variables in the same | ||||
|         // order as the weights. Thankfully, the weights and messages share the same index numbers, | ||||
|         // so we just need to make sure they're consistent | ||||
|         $allMessagesCount = count($allMessages); | ||||
|         for ($i = 0; i < $allMessagesCount; $i++) | ||||
|         { | ||||
|             $updatedMessages[] =$allMessages[$indicesToUse[$i]]; | ||||
|             $updatedVariables[] = $allVariables[$indicesToUse[$i]]; | ||||
|         } | ||||
|  | ||||
|         return updateHelper($this->_weights[$messageIndex], | ||||
|                             $this->_weightsSquared[$messageIndex], | ||||
|                             $updatedMessages, | ||||
|                             $updatedVariables); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										72
									
								
								PHPSkills/TrueSkill/Factors/GaussianWithinFactor.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								PHPSkills/TrueSkill/Factors/GaussianWithinFactor.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Factors; | ||||
|  | ||||
| /// <summary> | ||||
| /// Factor representing a team difference that has not exceeded the draw margin. | ||||
| /// </summary> | ||||
| /// <remarks>See the accompanying math paper for more details.</remarks> | ||||
| class GaussianWithinFactor extends GaussianFactor | ||||
| { | ||||
|     private $_epsilon; | ||||
|  | ||||
|     public function __construct($epsilon, Variable $variable) | ||||
|     { | ||||
|         $this->_epsilon = $epsilon; | ||||
|         $this->createVariableToMessageBinding($variable); | ||||
|     } | ||||
|  | ||||
|     public function getLogNormalization() | ||||
|     { | ||||
|         $variables = $this->getVariables(); | ||||
|         $marginal = $variables[0]->getValue(); | ||||
|  | ||||
|         $messages = $this->getMessages(); | ||||
|         $message = $messages[0]->getValue(); | ||||
|         $messageFromVariable = GaussianDistribution::divide($marginal, $message); | ||||
|         $mean = $messageFromVariable->getMean(); | ||||
|         $std = $messageFromVariable->getStandardDeviation(); | ||||
|         $z = GaussianDistribution::cumulativeTo(($this->_epsilon - $mean)/$std) | ||||
|              - | ||||
|              GaussianDistribution::cumulativeTo((-$this->_epsilon - $mean)/$std); | ||||
|  | ||||
|         return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z); | ||||
|     } | ||||
|  | ||||
|     protected function updateMessage(Message $message, Variable $variable) | ||||
|     { | ||||
|         $oldMarginal = clone $variable->getValue(); | ||||
|         $oldMessage = clone $message->getValue(); | ||||
|         $messageFromVariable = GaussianDistribution::divide($oldMarginal, $oldMessage); | ||||
|  | ||||
|         $c = $messageFromVariable->getPrecision(); | ||||
|         $d = $messageFromVariable->getPrecisionMean(); | ||||
|  | ||||
|         $sqrtC = sqrt($c); | ||||
|         $dOnSqrtC = $d/$sqrtC; | ||||
|  | ||||
|         $epsilonTimesSqrtC = $this->_epsilon*$sqrtC; | ||||
|         $d = $messageFromVariable->getPrecisionMean(); | ||||
|  | ||||
|         $denominator = 1.0 - TruncatedGaussianCorrectionFunctions::wWithinMargin($dOnSqrtC, $epsilonTimesSqrtC); | ||||
|         $newPrecision = $c/$denominator; | ||||
|         $newPrecisionMean = ($d + | ||||
|                                    $sqrtC* | ||||
|                                    TruncatedGaussianCorrectionFunctions::vWithinMargin($dOnSqrtC, $epsilonTimesSqrtC))/ | ||||
|                                   $denominator; | ||||
|  | ||||
|         $newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision); | ||||
|         $newMessage = GaussianDistribution::divide( | ||||
|                         GaussianDistribution::multiply($oldMessage, $newMarginal), | ||||
|                         $oldMarginal); | ||||
|  | ||||
|         /// Update the message and marginal | ||||
|         $message->setValue($newMessage); | ||||
|         $variable->setValue($newMarginal); | ||||
|  | ||||
|         /// Return the difference in the new marginal | ||||
|         return GaussianDistribution::subtract($newMarginal, $oldMarginal); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										147
									
								
								PHPSkills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								PHPSkills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| // The whole purpose of this is to do a loop on the bottom | ||||
| class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     private $_TeamDifferencesComparisonLayer; | ||||
|     private $_TeamPerformancesToTeamPerformanceDifferencesLayer; | ||||
|  | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph, | ||||
|                                 TeamPerformancesToTeamPerformanceDifferencesLayer $teamPerformancesToPerformanceDifferences, | ||||
|                                 TeamDifferencesComparisonLayer $teamDifferencesComparisonLayer) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|         $this->_TeamPerformancesToTeamPerformanceDifferencesLayer = $teamPerformancesToPerformanceDifferences; | ||||
|         $this->_TeamDifferencesComparisonLayer = $teamDifferencesComparisonLayer; | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->setRawInputVariablesGroups($this->getInputVariablesGroups()); | ||||
|         $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer(); | ||||
|  | ||||
|         $this->_TeamDifferencesComparisonLayer->setRawInputVariablesGroups( | ||||
|             $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getRawOutputVariablesGroups()); | ||||
|         $this->_TeamDifferencesComparisonLayer->buildLayer(); | ||||
|     } | ||||
|  | ||||
|     public function createPriorSchedule() | ||||
|     { | ||||
|         // BLOG about $loop | ||||
|         switch (count($this->getInputVariablesGroups())) | ||||
|         { | ||||
|             case 0: | ||||
|             case 1: | ||||
|                 throw new InvalidOperationException(); | ||||
|             case 2: | ||||
|                 $loop = $this->createTwoTeamInnerPriorLoopSchedule(); | ||||
|                 break; | ||||
|             default: | ||||
|                 $loop = $this->createMultipleTeamInnerPriorLoopSchedule(); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         // When dealing with differences, there are always (n-1) differences, so add in the 1 | ||||
|         $totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()); | ||||
|         $totalTeams = $totalTeamDifferences + 1; | ||||
|  | ||||
|         $innerSchedule = new ScheduleSequence( | ||||
|             "inner schedule", | ||||
|             array( | ||||
|                     $loop, | ||||
|                     new ScheduleStep( | ||||
|                         "teamPerformanceToPerformanceDifferenceFactors[0] @ 1", | ||||
|                         ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[0], 1), | ||||
|                     new ScheduleStep( | ||||
|                         "teamPerformanceToPerformanceDifferenceFactors[teamTeamDifferences = {0} - 1] @ 2",                                       | ||||
|                         ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1], 2) | ||||
|                 ) | ||||
|             ); | ||||
|  | ||||
|         return innerSchedule; | ||||
|     } | ||||
|  | ||||
|     private function createTwoTeamInnerPriorLoopSchedule() | ||||
|     { | ||||
|         return $this->scheduleSequence( | ||||
|             array( | ||||
|                     new ScheduleStep( | ||||
|                         "send team perf to perf differences", | ||||
|                         ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[0], | ||||
|                         0), | ||||
|                     new ScheduleStep( | ||||
|                         "send to greater than or within factor", | ||||
|                         ($this->_TeamDifferencesComparisonLayer->getLocalFactors())[0], | ||||
|                         0) | ||||
|                 ), | ||||
|             "loop of just two teams inner sequence"); | ||||
|     } | ||||
|  | ||||
|     private function createMultipleTeamInnerPriorLoopSchedule() | ||||
|     { | ||||
|         $totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()); | ||||
|  | ||||
|         $forwardScheduleList = array(); | ||||
|  | ||||
|         for ($i = 0; $i < $totalTeamDifferences - 1; $i++) | ||||
|         { | ||||
|             $currentForwardSchedulePiece = | ||||
|                 $this->scheduleSequence( | ||||
|                     array( | ||||
|                             new ScheduleStep( | ||||
|                                 sprintf("team perf to perf diff %d", $i), | ||||
|                                 ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$i], 0), | ||||
|                             new ScheduleStep( | ||||
|                                 sprintf("greater than or within result factor %d", $i), | ||||
|                                 ($this->_TeamDifferencesComparisonLayer->getLocalFactors())[$i], 0), | ||||
|                             new ScheduleStep( | ||||
|                                 sprintf("team perf to perf diff factors [%d], 2", $i), | ||||
|                                 ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$i], 2) | ||||
|                         ), sprintf("current forward schedule piece %d", $i); | ||||
|  | ||||
|             $forwardScheduleList[] = $currentForwardSchedulePiece; | ||||
|         } | ||||
|  | ||||
|         $forwardSchedule = new ScheduleSequence("forward schedule", $forwardScheduleList); | ||||
|  | ||||
|         $backwardScheduleList = array(); | ||||
|  | ||||
|         for ($i = 0; $i < $totalTeamDifferences - 1; $i++) | ||||
|         { | ||||
|             $currentBackwardSchedulePiece = new ScheduleSequence( | ||||
|                 "current backward schedule piece", | ||||
|                 array( | ||||
|                         new ScheduleStep( | ||||
|                             sprintf("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 0", $i), | ||||
|                             ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 0), | ||||
|                         new ScheduleStep( | ||||
|                             sprintf("greaterThanOrWithinResultFactors[totalTeamDifferences - 1 - %d] @ 0", $i), | ||||
|                             ($this->_TeamDifferencesComparisonLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 0), | ||||
|                         new ScheduleStep( | ||||
|                             sprintf("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 1", $i), | ||||
|                             ($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 1) | ||||
|                 ); | ||||
|             $backwardScheduleList[] = $currentBackwardSchedulePiece; | ||||
|         } | ||||
|  | ||||
|         $backwardSchedule = new ScheduleSequence("backward schedule", $backwardScheduleList); | ||||
|  | ||||
|         $forwardBackwardScheduleToLoop = | ||||
|             new ScheduleSequence( | ||||
|                 "forward Backward Schedule To Loop", | ||||
|                 array($forwardSchedule, $backwardSchedule)); | ||||
|  | ||||
|         $initialMaxDelta = 0.0001; | ||||
|  | ||||
|         $loop = new ScheduleLoop( | ||||
|             sprintf("loop with max delta of %f", $initialMaxDelta), | ||||
|             $forwardBackwardScheduleToLoop, | ||||
|             $initialMaxDelta); | ||||
|  | ||||
|         return $loop; | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,71 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         foreach ($this->getInputVariablesGroups() as $currentTeam) | ||||
|         { | ||||
|             $teamPerformance = $this->createOutputVariable($currentTeam); | ||||
|             $this->addLayerFactor($this->createPlayerToTeamSumFactor($currentTeam, $teamPerformance)); | ||||
|  | ||||
|             // REVIEW: Does it make sense to have groups of one? | ||||
|             $this->getOutputVariablesGroups() = $teamPerformance; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function createPriorSchedule() | ||||
|     { | ||||
|         return $this->scheduleSequence( | ||||
|                 array_map( | ||||
|                         function($weightedSumFactor) | ||||
|                         { | ||||
|                             return new ScheduleStep("Perf to Team Perf Step", $weightedSumFactor, 0); | ||||
|                         }, | ||||
|                         $this->getLocalFactors()), | ||||
|                 "all player perf to team perf schedule"); | ||||
|     } | ||||
|  | ||||
|     protected function createPlayerToTeamSumFactor($teamMembers, $sumVariable) | ||||
|     { | ||||
|         return new GaussianWeightedSumFactor( | ||||
|                 $sumVariable, | ||||
|                 $teamMembers, | ||||
|                 array_map( | ||||
|                         function($v) | ||||
|                         { | ||||
|                             return PartialPlay::getPartialPlayPercentage($v->getKey()); | ||||
|                         }, | ||||
|                         $teamMembers)); | ||||
|                                                   | ||||
|     } | ||||
|  | ||||
|     public function createPosteriorSchedule() | ||||
|     { | ||||
|         // BLOG | ||||
|         return $this->scheduleSequence( | ||||
|                 from currentFactor in LocalFactors | ||||
|                                 from currentIteration in | ||||
|                                     Enumerable.Range(1, currentFactor.NumberOfMessages - 1) | ||||
|                                 select new ScheduleStep<GaussianDistribution>( | ||||
|                                     "team sum perf @" + currentIteration, | ||||
|                                     currentFactor, | ||||
|                                     currentIteration), | ||||
|                                 "all of the team's sum iterations"); | ||||
|     } | ||||
|  | ||||
|     private function createOutputVariable($team) | ||||
|     { | ||||
|         $teamMemberNames = String.Join(", ", team.Select(teamMember => teamMember.Key.ToString()).ToArray()); | ||||
|         return ParentFactorGraph.VariableFactory.CreateBasicVariable("Team[{0}]'s performance", teamMemberNames); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,59 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| // We intentionally have no Posterior schedule since the only purpose here is to | ||||
| class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     private $_teams; | ||||
|  | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph, $teams) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|         $this->_teams = $teams; | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         foreach ($this->_teams as $currentTeam) | ||||
|         { | ||||
|             $currentTeamSkills = array(); | ||||
|  | ||||
|             foreach ($currentTeam as $currentTeamPlayer) | ||||
|             { | ||||
|                 $playerSkill = $this->createSkillOutputVariable($currentTeamPlayer.Key); | ||||
|                 $this->addLayerFactor($this->createPriorFactor($currentTeamPlayer.Key, $currentTeamPlayer.Value, $playerSkill)); | ||||
|                 $currentTeamSkills[] = $playerSkill; | ||||
|             } | ||||
|  | ||||
|             OutputVariablesGroups.Add(currentTeamSkills); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function createPriorSchedule() | ||||
|     { | ||||
|         return $this->scheduleSequence( | ||||
|                 array_map( | ||||
|                         function($prior) | ||||
|                         { | ||||
|                             return new ScheduleStep("Prior to Skill Step", $prior, 0); | ||||
|                         }, | ||||
|                         $this->getLocalFactors()), | ||||
|                  "All priors"); | ||||
|     } | ||||
|  | ||||
|     private function createPriorFactor($player, $priorRating, $skillsVariable) | ||||
|     { | ||||
|         return new GaussianPriorFactor($priorRating->getMean(), | ||||
|                                        square($priorRating->getStandardDeviation()) + | ||||
|                                        square($this->getParentFactorGraph()->getGameInfo()->getDynamicsFactor()), | ||||
|                                        $skillsVariable); | ||||
|     } | ||||
|  | ||||
|     private function createSkillOutputVariable($key) | ||||
|     { | ||||
|         return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, "{0}'s skill", $key); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,64 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         foreach ($this->getInputVariablesGroups() as $currentTeam) | ||||
|         { | ||||
|             $currentTeamPlayerPerformances = array(); | ||||
|  | ||||
|             foreach ($currentTeam as $playerSkillVariable) | ||||
|             { | ||||
|                 $playerPerformance = $this->createOutputVariable($playerSkillVariable->getKey()); | ||||
|                 $this->addLayerFactor($this->createLikelihood($playerSkillVariable, $playerPerformance)); | ||||
|                 $currentTeamPlayerPerformances[] = $playerPerformance; | ||||
|             } | ||||
|  | ||||
|             $this->getOutputVariablesGroups()[] = $currentTeamPlayerPerformances; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function createLikelihood($playerSkill, $playerPerformance) | ||||
|     { | ||||
|         return new GaussianLikelihoodFactor(square($this->getParentFactorGraph()->getGameInfo()->getBeta()), $playerPerformance, $playerSkill); | ||||
|     } | ||||
|  | ||||
|     private function createOutputVariable($key) | ||||
|     { | ||||
|         return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, "{0}'s performance", $key); | ||||
|     } | ||||
|  | ||||
|     public function createPriorSchedule() | ||||
|     { | ||||
|         return $this->scheduleSequence( | ||||
|                 array_map( | ||||
|                         function($likelihood) | ||||
|                         { | ||||
|                             return $this->scheduleStep("Skill to Perf step", $likelihood, 0); | ||||
|                         }, | ||||
|                         $this->getLocalFactors()), | ||||
|                 "All skill to performance sending"); | ||||
|     } | ||||
|  | ||||
|     public function createPosteriorSchedule() | ||||
|     { | ||||
|         return $this->scheduleSequence( | ||||
|                 array_map( | ||||
|                         function($likelihood) | ||||
|                         { | ||||
|                             return new ScheduleStep("name", $likelihood, 1);                     | ||||
|                         }, | ||||
|                         $this->getLocalFactors()), | ||||
|                 "All skill to performance sending"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     private $_epsilon; | ||||
|     private $_teamRanks; | ||||
|  | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph, array $teamRanks) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|         $this->_teamRanks = $teamRanks; | ||||
|         $gameInfo = $this->getParentFactorGraph()->getGameInfo(); | ||||
|         $this->_epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta()); | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         $inputVarGroups = $this->getInputVariablesGroups(); | ||||
|         $inputVarGroupsCount = count($inputVarGroups); | ||||
|  | ||||
|         for ($i = 0; $i < $inputVarGroupsCount; $i++) | ||||
|         { | ||||
|             $isDraw = ($this->_teamRanks[$i] == $this->_teamRanks[$i + 1]); | ||||
|             $teamDifference = $inputVarGroups[$i][0]; | ||||
|  | ||||
|             $factor = | ||||
|                 $isDraw | ||||
|                     ? new GaussianWithinFactor($this->_epsilon, $teamDifference) | ||||
|                     : new GaussianGreaterThanFactor($this->_epsilon, $teamDifference); | ||||
|  | ||||
|             $this->addLayerFactor($factor); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer | ||||
| { | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|     } | ||||
|  | ||||
|     public function buildLayer() | ||||
|     { | ||||
|         $inputVariablesGroup = $this->getInputVariablesGroups(); | ||||
|         $inputVariablesGroupCount = count($inputVariablesGroup); | ||||
|  | ||||
|         for ($i = 0; $i < $inputVariablesGroupCount - 1; $i++) | ||||
|         { | ||||
|             $strongerTeam = $inputVariablesGroups[$i][0]; | ||||
|             $weakerTeam = $inputVariablesGroups[$i + 1][0]; | ||||
|  | ||||
|             $currentDifference = $this->createOutputVariable(); | ||||
|             $this->addLayerFactor($this->createTeamPerformanceToDifferenceFactor($strongerTeam, $weakerTeam, currentDifference)); | ||||
|  | ||||
|             // REVIEW: Does it make sense to have groups of one? | ||||
|             $this->getOutputVariablesGroups()[] = $currentDifference; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private function createTeamPerformanceToDifferenceFactor( | ||||
|         Variable $strongerTeam, Variable $weakerTeam, Variable $output) | ||||
|     { | ||||
|         return new GaussianWeightedSumFactor($output, array($strongerTeam, $weakerTeam), array(1.0, -1.0)); | ||||
|     } | ||||
|  | ||||
|     private function createOutputVariable() | ||||
|     { | ||||
|         return $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team performance difference"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
							
								
								
									
										13
									
								
								PHPSkills/TrueSkill/Layers/TrueSkillFactorGraphLayer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								PHPSkills/TrueSkill/Layers/TrueSkillFactorGraphLayer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Moserware\Skills\TrueSkill\Layers; | ||||
|  | ||||
| abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer | ||||
| { | ||||
|     public function __construct(TrueSkillFactorGraph $parentGraph) | ||||
|     { | ||||
|         parent::__construct($parentGraph); | ||||
|     } | ||||
| } | ||||
|  | ||||
| ?> | ||||
		Reference in New Issue
	
	Block a user
	 Jeff Moser
					Jeff Moser