General cleanup and removal of all unnecessary references

This commit is contained in:
Alexander Liljengård 2016-05-24 16:31:21 +02:00
parent 519ad85fad
commit a65f2aa9f3
43 changed files with 366 additions and 386 deletions

1
.gitignore vendored

@ -1,3 +1,4 @@
coverage
nbproject nbproject
.idea .idea
vendor vendor

@ -1,5 +1,7 @@
<?php namespace Moserware\Skills\Elo; <?php namespace Moserware\Skills\Elo;
use Moserware\Skills\GameInfo;
/** /**
* Including Elo's scheme as a simple comparison. * Including Elo's scheme as a simple comparison.
* See http://en.wikipedia.org/wiki/Elo_rating_system#Theory * See http://en.wikipedia.org/wiki/Elo_rating_system#Theory
@ -22,7 +24,7 @@ class FideEloCalculator extends TwoPlayerEloCalculator
return new FideEloCalculator(new ProvisionalFideKFactor()); return new FideEloCalculator(new ProvisionalFideKFactor());
} }
public function getPlayerWinProbability($gameInfo, $playerRating, $opponentRating) public function getPlayerWinProbability(GameInfo $gameInfo, $playerRating, $opponentRating)
{ {
$ratingDifference = $opponentRating - $playerRating; $ratingDifference = $opponentRating - $playerRating;

@ -1,7 +1,7 @@
<?php namespace Moserware\Skills\Elo; <?php namespace Moserware\Skills\Elo;
use Moserware\Skills\GameInfo; use Moserware\Skills\GameInfo;
use Moserware\Numerics\GaussianDistribution; use Moserware\Skills\Numerics\GaussianDistribution;
class GaussianEloCalculator extends TwoPlayerEloCalculator class GaussianEloCalculator extends TwoPlayerEloCalculator
{ {
@ -21,6 +21,7 @@ class GaussianEloCalculator extends TwoPlayerEloCalculator
return GaussianDistribution::cumulativeTo( return GaussianDistribution::cumulativeTo(
$ratingDifference $ratingDifference
/ /
(sqrt(2) * $gameInfo->getBeta())); (sqrt(2) * $gameInfo->getBeta())
);
} }
} }

@ -6,7 +6,6 @@ use Moserware\Skills\PairwiseComparison;
use Moserware\Skills\RankSorter; use Moserware\Skills\RankSorter;
use Moserware\Skills\SkillCalculator; use Moserware\Skills\SkillCalculator;
use Moserware\Skills\SkillCalculatorSupportedOptions; use Moserware\Skills\SkillCalculatorSupportedOptions;
use Moserware\Skills\PlayersRange; use Moserware\Skills\PlayersRange;
use Moserware\Skills\TeamsRange; use Moserware\Skills\TeamsRange;
@ -20,9 +19,7 @@ abstract class TwoPlayerEloCalculator extends SkillCalculator
$this->_kFactor = $kFactor; $this->_kFactor = $kFactor;
} }
public function calculateNewRatings(GameInfo $gameInfo, public function calculateNewRatings(GameInfo $gameInfo, array $teamsOfPlayerToRatings, array $teamRanks)
array $teamsOfPlayerToRatings,
array $teamRanks)
{ {
$this->validateTeamCountAndPlayersCountPerTeam($teamsOfPlayerToRatings); $this->validateTeamCountAndPlayersCountPerTeam($teamsOfPlayerToRatings);
RankSorter::sort($teamsOfPlayerToRatings, $teamRanks); RankSorter::sort($teamsOfPlayerToRatings, $teamRanks);
@ -71,9 +68,9 @@ abstract class TwoPlayerEloCalculator extends SkillCalculator
} }
} }
public abstract function getPlayerWinProbability($gameInfo, $playerRating, $opponentRating); public abstract function getPlayerWinProbability(GameInfo $gameInfo, $playerRating, $opponentRating);
public function calculateMatchQuality(GameInfo $gameInfo, array &$teamsOfPlayerToRatings) public function calculateMatchQuality(GameInfo $gameInfo, array $teamsOfPlayerToRatings)
{ {
$this->validateTeamCountAndPlayersCountPerTeam($teamsOfPlayerToRatings); $this->validateTeamCountAndPlayersCountPerTeam($teamsOfPlayerToRatings);
$team1 = $teamsOfPlayerToRatings[0]; $team1 = $teamsOfPlayerToRatings[0];

@ -2,6 +2,7 @@
use Exception; use Exception;
// XXX: This class is not used anywhere
class DefaultVariable extends Variable class DefaultVariable extends Variable
{ {
public function __construct() public function __construct()
@ -9,12 +10,12 @@ class DefaultVariable extends Variable
parent::__construct("Default", null); parent::__construct("Default", null);
} }
public function &getValue() public function getValue()
{ {
return null; return null;
} }
public function setValue(&$value) public function setValue($value)
{ {
throw new Exception(); throw new Exception();
} }

@ -34,24 +34,26 @@ abstract class Factor
return count($this->_messages); return count($this->_messages);
} }
protected function &getVariables() protected function getVariables()
{ {
return $this->_variables; return $this->_variables;
} }
protected function &getMessages() protected function getMessages()
{ {
return $this->_messages; return $this->_messages;
} }
/** /**
* Update the message and marginal of the i-th variable that the factor is connected to * Update the message and marginal of the i-th variable that the factor is connected to
* @param $messageIndex
* @throws Exception
*/ */
public function updateMessageIndex($messageIndex) public function updateMessageIndex($messageIndex)
{ {
Guard::argumentIsValidIndex($messageIndex, count($this->_messages), "messageIndex"); Guard::argumentIsValidIndex($messageIndex, count($this->_messages), "messageIndex");
$message = &$this->_messages[$messageIndex]; $message = $this->_messages[$messageIndex];
$variable = &$this->_messageToVariableBinding->getValue($message); $variable = $this->_messageToVariableBinding->getValue($message);
return $this->updateMessageVariable($message, $variable); return $this->updateMessageVariable($message, $variable);
} }
@ -65,41 +67,41 @@ abstract class Factor
*/ */
public function resetMarginals() public function resetMarginals()
{ {
$allValues = &$this->_messageToVariableBinding->getAllValues(); $allValues = $this->_messageToVariableBinding->getAllValues();
foreach ($allValues as &$currentVariable) { foreach ($allValues as $currentVariable) {
$currentVariable->resetToPrior(); $currentVariable->resetToPrior();
} }
} }
/** /**
* Sends the ith message to the marginal and returns the log-normalization constant * Sends the ith message to the marginal and returns the log-normalization constant
* @param $messageIndex
* @return
* @throws Exception
*/ */
public function sendMessageIndex($messageIndex) public function sendMessageIndex($messageIndex)
{ {
Guard::argumentIsValidIndex($messageIndex, count($this->_messages), "messageIndex"); Guard::argumentIsValidIndex($messageIndex, count($this->_messages), "messageIndex");
$message = &$this->_messages[$messageIndex]; $message = $this->_messages[$messageIndex];
$variable = &$this->_messageToVariableBinding->getValue($message); $variable = $this->_messageToVariableBinding->getValue($message);
return $this->sendMessageVariable($message, $variable); return $this->sendMessageVariable($message, $variable);
} }
protected abstract function sendMessageVariable(Message &$message, Variable &$variable); protected abstract function sendMessageVariable(Message $message, Variable $variable);
public abstract function &createVariableToMessageBinding(Variable &$variable); public abstract function createVariableToMessageBinding(Variable $variable);
protected function &createVariableToMessageBindingWithMessage(Variable &$variable, Message &$message) protected function createVariableToMessageBindingWithMessage(Variable $variable, Message $message)
{ {
$index = count($this->_messages);
$localMessages = &$this->_messages;
$localMessages[] = &$message;
$this->_messageToVariableBinding->setValue($message, $variable); $this->_messageToVariableBinding->setValue($message, $variable);
$localVariables = &$this->_variables; $this->_messages[] = $message;
$localVariables[] = &$variable; $this->_variables[] = $variable;
return $message; return $message;
} }
public function __toString() public function __toString()
{ {
return ($this->_name != null) ? $this->_name : base::__toString(); return ($this->_name != null) ? $this->_name : null;
} }
} }

@ -4,14 +4,13 @@ class FactorGraph
{ {
private $_variableFactory; private $_variableFactory;
public function &getVariableFactory() public function getVariableFactory()
{ {
$factory = &$this->_variableFactory; return $this->_variableFactory;
return $factory;
} }
public function setVariableFactory(VariableFactory &$factory) public function setVariableFactory(VariableFactory $factory)
{ {
$this->_variableFactory = &$factory; $this->_variableFactory = $factory;
} }
} }

@ -1,5 +1,5 @@
<?php namespace Moserware\Skills\FactorGraphs; <?php namespace Moserware\Skills\FactorGraphs;
// edit this
abstract class FactorGraphLayer abstract class FactorGraphLayer
{ {
private $_localFactors = array(); private $_localFactors = array();
@ -7,38 +7,39 @@ abstract class FactorGraphLayer
private $_inputVariablesGroups = array(); private $_inputVariablesGroups = array();
private $_parentFactorGraph; private $_parentFactorGraph;
protected function __construct(FactorGraph &$parentGraph) protected function __construct(FactorGraph $parentGraph)
{ {
$this->_parentFactorGraph = &$parentGraph; $this->_parentFactorGraph = $parentGraph;
} }
protected function &getInputVariablesGroups() protected function getInputVariablesGroups()
{ {
$inputVariablesGroups = &$this->_inputVariablesGroups; return $this->_inputVariablesGroups;
return $inputVariablesGroups;
} }
// HACK // HACK
public function &getParentFactorGraph() public function getParentFactorGraph()
{ {
$parentFactorGraph = &$this->_parentFactorGraph; return $this->_parentFactorGraph;
return $parentFactorGraph;
} }
/**
* This reference is still needed
*
* @return array
*/
public function &getOutputVariablesGroups() public function &getOutputVariablesGroups()
{ {
$outputVariablesGroups = &$this->_outputVariablesGroups; return $this->_outputVariablesGroups;
return $outputVariablesGroups;
} }
public function &getLocalFactors() public function getLocalFactors()
{ {
$localFactors = &$this->_localFactors; return $this->_localFactors;
return $localFactors;
} }
public function setInputVariablesGroups(&$value) public function setInputVariablesGroups($value)
{ {
$this->_inputVariablesGroups = $value; $this->_inputVariablesGroups = $value;
} }
@ -48,7 +49,7 @@ abstract class FactorGraphLayer
return new ScheduleSequence($name, $itemsToSequence); return new ScheduleSequence($name, $itemsToSequence);
} }
protected function addLayerFactor(Factor &$factor) protected function addLayerFactor(Factor $factor)
{ {
$this->_localFactors[] = $factor; $this->_localFactors[] = $factor;
} }

@ -9,7 +9,7 @@ class FactorList
public function getLogNormalization() public function getLogNormalization()
{ {
$list = &$this->_list; $list = $this->_list;
foreach ($list as &$currentFactor) { foreach ($list as &$currentFactor) {
$currentFactor->resetMarginals(); $currentFactor->resetMarginals();
} }
@ -42,7 +42,7 @@ class FactorList
return count($this->_list); return count($this->_list);
} }
public function &addFactor(Factor &$factor) public function addFactor(Factor $factor)
{ {
$this->_list[] = $factor; $this->_list[] = $factor;
return $factor; return $factor;

@ -4,15 +4,14 @@ class KeyedVariable extends Variable
{ {
private $_key; private $_key;
public function __construct(&$key, $name, &$prior) public function __construct($key, $name, $prior)
{ {
parent::__construct($name, $prior); parent::__construct($name, $prior);
$this->_key = &$key; $this->_key = $key;
} }
public function &getKey() public function getKey()
{ {
$key = &$this->_key; return $this->_key;
return $key;
} }
} }

@ -5,21 +5,20 @@ class Message
private $_name; private $_name;
private $_value; private $_value;
public function __construct(&$value = null, $name = null) public function __construct($value = null, $name = null)
{ {
$this->_name = $name; $this->_name = $name;
$this->_value = $value; $this->_value = $value;
} }
public function& getValue() public function getValue()
{ {
$value = &$this->_value; return $this->_value;
return $value;
} }
public function setValue(&$value) public function setValue($value)
{ {
$this->_value = &$value; $this->_value = $value;
} }
public function __toString() public function __toString()

@ -5,7 +5,7 @@ class ScheduleLoop extends Schedule
private $_maxDelta; private $_maxDelta;
private $_scheduleToLoop; private $_scheduleToLoop;
public function __construct($name, Schedule &$scheduleToLoop, $maxDelta) public function __construct($name, Schedule $scheduleToLoop, $maxDelta)
{ {
parent::__construct($name); parent::__construct($name);
$this->_scheduleToLoop = $scheduleToLoop; $this->_scheduleToLoop = $scheduleToLoop;

@ -14,8 +14,8 @@ class ScheduleSequence extends Schedule
{ {
$maxDelta = 0; $maxDelta = 0;
$schedules = &$this->_schedules; $schedules = $this->_schedules;
foreach ($schedules as &$currentSchedule) { foreach ($schedules as $currentSchedule) {
$currentVisit = $currentSchedule->visit($depth + 1, $maxDepth); $currentVisit = $currentSchedule->visit($depth + 1, $maxDepth);
$maxDelta = max($currentVisit, $maxDelta); $maxDelta = max($currentVisit, $maxDelta);
} }

@ -5,7 +5,7 @@ class ScheduleStep extends Schedule
private $_factor; private $_factor;
private $_index; private $_index;
public function __construct($name, Factor &$factor, $index) public function __construct($name, Factor $factor, $index)
{ {
parent::__construct($name); parent::__construct($name);
$this->_factor = $factor; $this->_factor = $factor;
@ -14,7 +14,7 @@ class ScheduleStep extends Schedule
public function visit($depth = -1, $maxDepth = 0) public function visit($depth = -1, $maxDepth = 0)
{ {
$currentFactor = &$this->_factor; $currentFactor = $this->_factor;
$delta = $currentFactor->updateMessageIndex($this->_index); $delta = $currentFactor->updateMessageIndex($this->_index);
return $delta; return $delta;
} }

@ -7,22 +7,21 @@ class Variable
private $_prior; private $_prior;
private $_value; private $_value;
public function __construct($name, &$prior) public function __construct($name, $prior)
{ {
$this->_name = "Variable[" . $name . "]"; $this->_name = "Variable[" . $name . "]";
$this->_prior = $prior; $this->_prior = $prior;
$this->resetToPrior(); $this->resetToPrior();
} }
public function &getValue() public function getValue()
{ {
$value = &$this->_value; return $this->_value;
return $value;
} }
public function setValue(&$value) public function setValue($value)
{ {
$this->_value = &$value; $this->_value = $value;
} }
public function resetToPrior() public function resetToPrior()

@ -7,17 +7,17 @@ class VariableFactory
public function __construct($variablePriorInitializer) public function __construct($variablePriorInitializer)
{ {
$this->_variablePriorInitializer = &$variablePriorInitializer; $this->_variablePriorInitializer = $variablePriorInitializer;
} }
public function &createBasicVariable($name) public function createBasicVariable($name)
{ {
$initializer = $this->_variablePriorInitializer; $initializer = $this->_variablePriorInitializer;
$newVar = new Variable($name, $initializer()); $newVar = new Variable($name, $initializer());
return $newVar; return $newVar;
} }
public function &createKeyedVariable(&$key, $name) public function createKeyedVariable($key, $name)
{ {
$initializer = $this->_variablePriorInitializer; $initializer = $this->_variablePriorInitializer;
$newVar = new KeyedVariable($key, $name, $initializer()); $newVar = new KeyedVariable($key, $name, $initializer());

@ -8,30 +8,30 @@ class HashMap
private $_hashToValue = array(); private $_hashToValue = array();
private $_hashToKey = array(); private $_hashToKey = array();
public function &getValue(&$key) public function getValue($key)
{ {
$hash = self::getHash($key); $hash = self::getHash($key);
$hashValue = &$this->_hashToValue[$hash]; $hashValue = $this->_hashToValue[$hash];
return $hashValue; return $hashValue;
} }
public function setValue(&$key, &$value) public function setValue($key, $value)
{ {
$hash = self::getHash($key); $hash = self::getHash($key);
$this->_hashToKey[$hash] = &$key; $this->_hashToKey[$hash] = $key;
$this->_hashToValue[$hash] = &$value; $this->_hashToValue[$hash] = $value;
return $this; return $this;
} }
public function &getAllKeys() public function getAllKeys()
{ {
$keys = &array_values($this->_hashToKey); $keys = array_values($this->_hashToKey);
return $keys; return $keys;
} }
public function getAllValues() public function getAllValues()
{ {
$values = &array_values($this->_hashToValue); $values = array_values($this->_hashToValue);
return $values; return $values;
} }
@ -40,7 +40,7 @@ class HashMap
return count($this->_hashToKey); return count($this->_hashToKey);
} }
private static function getHash(&$key) private static function getHash($key)
{ {
if (is_object($key)) { if (is_object($key)) {
return spl_object_hash($key); return spl_object_hash($key);

@ -109,7 +109,8 @@ class GaussianDistribution
{ {
return max( return max(
abs($left->_precisionMean - $right->_precisionMean), abs($left->_precisionMean - $right->_precisionMean),
sqrt(abs($left->_precision - $right->_precision))); sqrt(abs($left->_precision - $right->_precision))
);
} }
// Computes the absolute difference between two Gaussians // Computes the absolute difference between two Gaussians
@ -133,8 +134,10 @@ class GaussianDistribution
public static function divide(GaussianDistribution $numerator, GaussianDistribution $denominator) public static function divide(GaussianDistribution $numerator, GaussianDistribution $denominator)
{ {
return GaussianDistribution::fromPrecisionMean($numerator->_precisionMean - $denominator->_precisionMean, return GaussianDistribution::fromPrecisionMean(
$numerator->_precision - $denominator->_precision); $numerator->_precisionMean - $denominator->_precisionMean,
$numerator->_precision - $denominator->_precision
);
} }
public static function logRatioNormalization(GaussianDistribution $numerator, GaussianDistribution $denominator) public static function logRatioNormalization(GaussianDistribution $numerator, GaussianDistribution $denominator)

@ -1,5 +1,7 @@
<?php namespace Moserware\Skills\Numerics; <?php namespace Moserware\Skills\Numerics;
use Exception;
class Matrix class Matrix
{ {
const ERROR_TOLERANCE = 0.0000000001; const ERROR_TOLERANCE = 0.0000000001;
@ -20,12 +22,10 @@ class Matrix
$data = array(); $data = array();
$result = new Matrix($rows, $columns, $data); $result = new Matrix($rows, $columns, $data);
for($currentColumn = 0; $currentColumn < $columns; $currentColumn++) for ($currentColumn = 0; $currentColumn < $columns; $currentColumn++) {
{
$currentColumnData = $columnValues[$currentColumn]; $currentColumnData = $columnValues[$currentColumn];
for($currentRow = 0; $currentRow < $rows; $currentRow++) for ($currentRow = 0; $currentRow < $rows; $currentRow++) {
{
$result->setValue($currentRow, $currentColumn, $currentColumnData[$currentRow]); $result->setValue($currentRow, $currentColumn, $currentColumnData[$currentRow]);
} }
} }
@ -41,10 +41,8 @@ class Matrix
$result = new Matrix($rows, $cols); $result = new Matrix($rows, $cols);
$currentIndex = 2; $currentIndex = 2;
for($currentRow = 0; $currentRow < $rows; $currentRow++) for ($currentRow = 0; $currentRow < $rows; $currentRow++) {
{ for ($currentCol = 0; $currentCol < $cols; $currentCol++) {
for($currentCol = 0; $currentCol < $cols; $currentCol++)
{
$result->setValue($currentRow, $currentCol, $args[$currentIndex++]); $result->setValue($currentRow, $currentCol, $args[$currentIndex++]);
} }
} }
@ -80,12 +78,10 @@ class Matrix
$rowMatrixData = $this->_matrixRowData; $rowMatrixData = $this->_matrixRowData;
for ($currentRowTransposeMatrix = 0; for ($currentRowTransposeMatrix = 0;
$currentRowTransposeMatrix < $this->_columnCount; $currentRowTransposeMatrix < $this->_columnCount;
$currentRowTransposeMatrix++) $currentRowTransposeMatrix++) {
{
for ($currentColumnTransposeMatrix = 0; for ($currentColumnTransposeMatrix = 0;
$currentColumnTransposeMatrix < $this->_rowCount; $currentColumnTransposeMatrix < $this->_rowCount;
$currentColumnTransposeMatrix++) $currentColumnTransposeMatrix++) {
{
$transposeMatrix[$currentRowTransposeMatrix][$currentColumnTransposeMatrix] = $transposeMatrix[$currentRowTransposeMatrix][$currentColumnTransposeMatrix] =
$rowMatrixData[$currentColumnTransposeMatrix][$currentRowTransposeMatrix]; $rowMatrixData[$currentColumnTransposeMatrix][$currentRowTransposeMatrix];
} }
@ -102,19 +98,16 @@ class Matrix
public function getDeterminant() public function getDeterminant()
{ {
// Basic argument checking // Basic argument checking
if (!$this->isSquare()) if (!$this->isSquare()) {
{
throw new Exception("Matrix must be square!"); throw new Exception("Matrix must be square!");
} }
if ($this->_rowCount == 1) if ($this->_rowCount == 1) {
{
// Really happy path :) // Really happy path :)
return $this->_matrixRowData[0][0]; return $this->_matrixRowData[0][0];
} }
if ($this->_rowCount == 2) if ($this->_rowCount == 2) {
{
// Happy path! // Happy path!
// Given: // Given:
// | a b | // | a b |
@ -124,7 +117,7 @@ class Matrix
$b = $this->_matrixRowData[0][1]; $b = $this->_matrixRowData[0][1];
$c = $this->_matrixRowData[1][0]; $c = $this->_matrixRowData[1][0];
$d = $this->_matrixRowData[1][1]; $d = $this->_matrixRowData[1][1];
return $a*$d - $b*$c; return $a * $d - $b * $c;
} }
// I use the Laplace expansion here since it's straightforward to implement. // I use the Laplace expansion here since it's straightforward to implement.
@ -136,11 +129,10 @@ class Matrix
$result = 0.0; $result = 0.0;
// I expand along the first row // I expand along the first row
for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) {
{
$firstRowColValue = $this->_matrixRowData[0][$currentColumn]; $firstRowColValue = $this->_matrixRowData[0][$currentColumn];
$cofactor = $this->getCofactor(0, $currentColumn); $cofactor = $this->getCofactor(0, $currentColumn);
$itemToAdd = $firstRowColValue*$cofactor; $itemToAdd = $firstRowColValue * $cofactor;
$result = $result + $itemToAdd; $result = $result + $itemToAdd;
} }
@ -149,14 +141,12 @@ class Matrix
public function getAdjugate() public function getAdjugate()
{ {
if (!$this->isSquare()) if (!$this->isSquare()) {
{
throw new Exception("Matrix must be square!"); throw new Exception("Matrix must be square!");
} }
// See http://en.wikipedia.org/wiki/Adjugate_matrix // See http://en.wikipedia.org/wiki/Adjugate_matrix
if ($this->_rowCount == 2) if ($this->_rowCount == 2) {
{
// Happy path! // Happy path!
// Adjugate of: // Adjugate of:
// | a b | // | a b |
@ -170,17 +160,15 @@ class Matrix
$c = $this->_matrixRowData[1][0]; $c = $this->_matrixRowData[1][0];
$d = $this->_matrixRowData[1][1]; $d = $this->_matrixRowData[1][1];
return new SquareMatrix( $d, -$b, return new SquareMatrix($d, -$b,
-$c, $a); -$c, $a);
} }
// The idea is that it's the transpose of the cofactors // The idea is that it's the transpose of the cofactors
$result = array(); $result = array();
for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) {
{ for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) {
for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++)
{
$result[$currentColumn][$currentRow] = $this->getCofactor($currentRow, $currentColumn); $result[$currentColumn][$currentRow] = $this->getCofactor($currentRow, $currentColumn);
} }
} }
@ -190,9 +178,8 @@ class Matrix
public function getInverse() public function getInverse()
{ {
if (($this->_rowCount == 1) && ($this->_columnCount == 1)) if (($this->_rowCount == 1) && ($this->_columnCount == 1)) {
{ return new SquareMatrix(1.0 / $this->_matrixRowData[0][0]);
return new SquareMatrix(1.0/$this->_matrixRowData[0][0]);
} }
// Take the simple approach: // Take the simple approach:
@ -209,11 +196,9 @@ class Matrix
$columns = $matrix->getColumnCount(); $columns = $matrix->getColumnCount();
$newValues = array(); $newValues = array();
for ($currentRow = 0; $currentRow < $rows; $currentRow++) for ($currentRow = 0; $currentRow < $rows; $currentRow++) {
{ for ($currentColumn = 0; $currentColumn < $columns; $currentColumn++) {
for ($currentColumn = 0; $currentColumn < $columns; $currentColumn++) $newValues[$currentRow][$currentColumn] = $scalarValue * $matrix->getValue($currentRow, $currentColumn);
{
$newValues[$currentRow][$currentColumn] = $scalarValue*$matrix->getValue($currentRow, $currentColumn);
} }
} }
@ -223,11 +208,10 @@ class Matrix
public static function add($left, $right) public static function add($left, $right)
{ {
if ( if (
($left->getRowCount() != $right->getRowCount()) ($left->getRowCount() != $right->getRowCount())
|| ||
($left->getColumnCount() != $right->getColumnCount()) ($left->getColumnCount() != $right->getColumnCount())
) ) {
{
throw new Exception("Matrices must be of the same size"); throw new Exception("Matrices must be of the same size");
} }
@ -235,14 +219,12 @@ class Matrix
$resultMatrix = array(); $resultMatrix = array();
for ($currentRow = 0; $currentRow < $left->getRowCount(); $currentRow++) for ($currentRow = 0; $currentRow < $left->getRowCount(); $currentRow++) {
{ for ($currentColumn = 0; $currentColumn < $right->getColumnCount(); $currentColumn++) {
for ($currentColumn = 0; $currentColumn < $right->getColumnCount(); $currentColumn++)
{
$resultMatrix[$currentRow][$currentColumn] = $resultMatrix[$currentRow][$currentColumn] =
$left->getValue($currentRow, $currentColumn) $left->getValue($currentRow, $currentColumn)
+ +
$right->getValue($currentRow, $currentColumn); $right->getValue($currentRow, $currentColumn);
} }
} }
@ -254,8 +236,7 @@ class Matrix
// Just your standard matrix multiplication. // Just your standard matrix multiplication.
// See http://en.wikipedia.org/wiki/Matrix_multiplication for details // See http://en.wikipedia.org/wiki/Matrix_multiplication for details
if ($left->getColumnCount() != $right->getRowCount()) if ($left->getColumnCount() != $right->getRowCount()) {
{
throw new Exception("The width of the left matrix must match the height of the right matrix"); throw new Exception("The width of the left matrix must match the height of the right matrix");
} }
@ -264,17 +245,14 @@ class Matrix
$resultMatrix = array(); $resultMatrix = array();
for ($currentRow = 0; $currentRow < $resultRows; $currentRow++) for ($currentRow = 0; $currentRow < $resultRows; $currentRow++) {
{ for ($currentColumn = 0; $currentColumn < $resultColumns; $currentColumn++) {
for ($currentColumn = 0; $currentColumn < $resultColumns; $currentColumn++)
{
$productValue = 0; $productValue = 0;
for ($vectorIndex = 0; $vectorIndex < $left->getColumnCount(); $vectorIndex++) for ($vectorIndex = 0; $vectorIndex < $left->getColumnCount(); $vectorIndex++) {
{
$leftValue = $left->getValue($currentRow, $vectorIndex); $leftValue = $left->getValue($currentRow, $vectorIndex);
$rightValue = $right->getValue($vectorIndex, $currentColumn); $rightValue = $right->getValue($vectorIndex, $currentColumn);
$vectorIndexProduct = $leftValue*$rightValue; $vectorIndexProduct = $leftValue * $rightValue;
$productValue = $productValue + $vectorIndexProduct; $productValue = $productValue + $vectorIndexProduct;
} }
@ -294,19 +272,15 @@ class Matrix
$actualRow = 0; $actualRow = 0;
for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) {
{ if ($currentRow == $rowToRemove) {
if ($currentRow == $rowToRemove)
{
continue; continue;
} }
$actualCol = 0; $actualCol = 0;
for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) {
{ if ($currentColumn == $columnToRemove) {
if ($currentColumn == $columnToRemove)
{
continue; continue;
} }
@ -326,41 +300,33 @@ class Matrix
// See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details // See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details
// REVIEW: should things be reversed since I'm 0 indexed? // REVIEW: should things be reversed since I'm 0 indexed?
$sum = $rowToRemove + $columnToRemove; $sum = $rowToRemove + $columnToRemove;
$isEven = ($sum%2 == 0); $isEven = ($sum % 2 == 0);
if ($isEven) if ($isEven) {
{
return $this->getMinorMatrix($rowToRemove, $columnToRemove)->getDeterminant(); return $this->getMinorMatrix($rowToRemove, $columnToRemove)->getDeterminant();
} } else {
else return -1.0 * $this->getMinorMatrix($rowToRemove, $columnToRemove)->getDeterminant();
{
return -1.0*$this->getMinorMatrix($rowToRemove, $columnToRemove)->getDeterminant();
} }
} }
public function equals($otherMatrix) public function equals($otherMatrix)
{ {
// If one is null, but not both, return false. // If one is null, but not both, return false.
if ($otherMatrix == null) if ($otherMatrix == null) {
{
return false; return false;
} }
if (($this->_rowCount != $otherMatrix->getRowCount()) || ($this->_columnCount != $otherMatrix->getColumnCount())) if (($this->_rowCount != $otherMatrix->getRowCount()) || ($this->_columnCount != $otherMatrix->getColumnCount())) {
{
return false; return false;
} }
for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) {
{ for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) {
for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++)
{
$delta = $delta =
abs($this->_matrixRowData[$currentRow][$currentColumn] - abs($this->_matrixRowData[$currentRow][$currentColumn] -
$otherMatrix->getValue($currentRow, $currentColumn)); $otherMatrix->getValue($currentRow, $currentColumn));
if ($delta > self::ERROR_TOLERANCE) if ($delta > self::ERROR_TOLERANCE) {
{
return false; return false;
} }
} }

@ -5,8 +5,7 @@ class Vector extends Matrix
public function __construct(array $vectorValues) public function __construct(array $vectorValues)
{ {
$columnValues = array(); $columnValues = array();
foreach($vectorValues as $currentVectorValue) foreach ($vectorValues as $currentVectorValue) {
{
$columnValues[] = array($currentVectorValue); $columnValues[] = array($currentVectorValue);
} }
parent::__construct(count($vectorValues), 1, $columnValues); parent::__construct(count($vectorValues), 1, $columnValues);

@ -34,9 +34,8 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
/** /**
* The identifier for the player, such as a name. * The identifier for the player, such as a name.
*/ */
public function &getId() public function getId()
{ {
$id = &$this->_Id;
return $this->_Id; return $this->_Id;
} }

@ -9,27 +9,24 @@ class RatingContainer
$this->_playerToRating = new HashMap(); $this->_playerToRating = new HashMap();
} }
public function &getRating(Player &$player) public function getRating(Player $player)
{ {
$rating = &$this->_playerToRating->getValue($player); return $this->_playerToRating->getValue($player);
return $rating;
} }
public function setRating(Player &$player, Rating $rating) public function setRating(Player $player, Rating $rating)
{ {
return $this->_playerToRating->setValue($player, $rating); return $this->_playerToRating->setValue($player, $rating);
} }
public function &getAllPlayers() public function getAllPlayers()
{ {
$allPlayers = &$this->_playerToRating->getAllKeys(); return $this->_playerToRating->getAllKeys();
return $allPlayers;
} }
public function &getAllRatings() public function getAllRatings()
{ {
$allRatings = &$this->_playerToRating->getAllValues(); return $this->_playerToRating->getAllValues();
return $allRatings;
} }
public function count() public function count()

@ -37,23 +37,21 @@ abstract class SkillCalculator
* @param array $teamsOfPlayerToRatings A mapping of team players and their ratings. * @param array $teamsOfPlayerToRatings A mapping of team players and their ratings.
* @return The quality of the match between the teams as a percentage (0% = bad, 100% = well matched). * @return The quality of the match between the teams as a percentage (0% = bad, 100% = well matched).
*/ */
public abstract function calculateMatchQuality(GameInfo $gameInfo, public abstract function calculateMatchQuality(GameInfo $gameInfo, array $teamsOfPlayerToRatings);
array &$teamsOfPlayerToRatings);
public function isSupported($option) public function isSupported($option)
{ {
return ($this->_supportedOptions & $option) == $option; return ($this->_supportedOptions & $option) == $option;
} }
protected function validateTeamCountAndPlayersCountPerTeam(array &$teamsOfPlayerToRatings) protected function validateTeamCountAndPlayersCountPerTeam(array $teamsOfPlayerToRatings)
{ {
self::validateTeamCountAndPlayersCountPerTeamWithRanges($teamsOfPlayerToRatings, $this->_totalTeamsAllowed, $this->_playersPerTeamAllowed); self::validateTeamCountAndPlayersCountPerTeamWithRanges($teamsOfPlayerToRatings, $this->_totalTeamsAllowed, $this->_playersPerTeamAllowed);
} }
private static function validateTeamCountAndPlayersCountPerTeamWithRanges( private static function validateTeamCountAndPlayersCountPerTeamWithRanges(array $teams,
array &$teams, TeamsRange $totalTeams,
TeamsRange &$totalTeams, PlayersRange $playersPerTeam)
PlayersRange &$playersPerTeam)
{ {
$countOfTeams = 0; $countOfTeams = 0;

@ -2,7 +2,7 @@
class Team extends RatingContainer class Team extends RatingContainer
{ {
public function __construct(Player &$player = null, Rating $rating = null) public function __construct(Player $player = null, Rating $rating = null)
{ {
parent::__construct(); parent::__construct();
@ -12,7 +12,7 @@ class Team extends RatingContainer
} }
} }
public function addPlayer(Player &$player, Rating $rating) public function addPlayer(Player $player, Rating $rating)
{ {
$this->setRating($player, $rating); $this->setRating($player, $rating);
return $this; return $this;

@ -7,8 +7,8 @@ class Teams
$args = func_get_args(); $args = func_get_args();
$result = array(); $result = array();
foreach ($args as &$currentTeam) { foreach ($args as $currentTeam) {
$localCurrentTeam = &$currentTeam; $localCurrentTeam = $currentTeam;
$result[] = $localCurrentTeam; $result[] = $localCurrentTeam;
} }

@ -43,8 +43,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
return $factorGraph->getUpdatedRatings(); return $factorGraph->getUpdatedRatings();
} }
public function calculateMatchQuality(GameInfo $gameInfo, public function calculateMatchQuality(GameInfo $gameInfo, array $teams)
array &$teams)
{ {
// We need to create the A matrix which is the player team assigments. // We need to create the A matrix which is the player team assigments.
$teamAssignmentsList = $teams; $teamAssignmentsList = $teams;
@ -87,7 +86,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
return $result; return $result;
} }
private static function getPlayerMeansVector(array &$teamAssignmentsList) private static function getPlayerMeansVector(array $teamAssignmentsList)
{ {
// A simple vector of all the player means. // A simple vector of all the player means.
return new Vector(self::getPlayerRatingValues($teamAssignmentsList, return new Vector(self::getPlayerRatingValues($teamAssignmentsList,
@ -96,7 +95,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
})); }));
} }
private static function getPlayerCovarianceMatrix(array &$teamAssignmentsList) private static function getPlayerCovarianceMatrix(array $teamAssignmentsList)
{ {
// This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all // This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all
// players. // players.
@ -108,8 +107,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
} }
// Helper function that gets a list of values for all player ratings // Helper function that gets a list of values for all player ratings
private static function getPlayerRatingValues(array &$teamAssignmentsList, private static function getPlayerRatingValues(array $teamAssignmentsList, $playerRatingFunction)
$playerRatingFunction)
{ {
$playerRatingValues = array(); $playerRatingValues = array();
@ -122,7 +120,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
return $playerRatingValues; return $playerRatingValues;
} }
private static function createPlayerTeamAssignmentMatrix(&$teamAssignmentsList, $totalPlayers) 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 // 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] // and the columns represent teams. At Matrix[row, column] represents that player[row] is on team[col]

@ -18,19 +18,19 @@ abstract class GaussianFactor extends Factor
* @param Variable $variable * @param Variable $variable
* @return float|int * @return float|int
*/ */
protected function sendMessageVariable(Message &$message, Variable &$variable) protected function sendMessageVariable(Message $message, Variable $variable)
{ {
$marginal = &$variable->getValue(); $marginal = $variable->getValue();
$messageValue = &$message->getValue(); $messageValue = $message->getValue();
$logZ = GaussianDistribution::logProductNormalization($marginal, $messageValue); $logZ = GaussianDistribution::logProductNormalization($marginal, $messageValue);
$variable->setValue(GaussianDistribution::multiply($marginal, $messageValue)); $variable->setValue(GaussianDistribution::multiply($marginal, $messageValue));
return $logZ; return $logZ;
} }
public function &createVariableToMessageBinding(Variable &$variable) public function createVariableToMessageBinding(Variable $variable)
{ {
$newDistribution = GaussianDistribution::fromPrecisionMean(0, 0); $newDistribution = GaussianDistribution::fromPrecisionMean(0, 0);
$binding = &parent::createVariableToMessageBindingWithMessage($variable, $binding = parent::createVariableToMessageBindingWithMessage($variable,
new Message( new Message(
$newDistribution, $newDistribution,
sprintf("message from %s to %s", $this, $variable))); sprintf("message from %s to %s", $this, $variable)));

@ -14,7 +14,7 @@ class GaussianGreaterThanFactor extends GaussianFactor
{ {
private $_epsilon; private $_epsilon;
public function __construct($epsilon, Variable &$variable) public function __construct($epsilon, Variable $variable)
{ {
parent::__construct(\sprintf("%s > %.2f", $variable, $epsilon)); parent::__construct(\sprintf("%s > %.2f", $variable, $epsilon));
$this->_epsilon = $epsilon; $this->_epsilon = $epsilon;
@ -23,20 +23,26 @@ class GaussianGreaterThanFactor extends GaussianFactor
public function getLogNormalization() public function getLogNormalization()
{ {
$vars = &$this->getVariables(); /** @var Variable[] $vars */
$marginal = &$vars[0]->getValue(); $vars = $this->getVariables();
$messages = &$this->getMessages(); $marginal = $vars[0]->getValue();
$message = &$messages[0]->getValue();
/** @var Message[] $messages */
$messages = $this->getMessages();
$message = $messages[0]->getValue();
$messageFromVariable = GaussianDistribution::divide($marginal, $message); $messageFromVariable = GaussianDistribution::divide($marginal, $message);
return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) return -GaussianDistribution::logProductNormalization($messageFromVariable, $message)
+ +
log( log(
GaussianDistribution::cumulativeTo(($messageFromVariable->getMean() - $this->_epsilon) / GaussianDistribution::cumulativeTo(
$messageFromVariable->getStandardDeviation())); ($messageFromVariable->getMean() - $this->_epsilon) /
$messageFromVariable->getStandardDeviation()
)
);
} }
protected function updateMessageVariable(Message &$message, Variable &$variable) protected function updateMessageVariable(Message $message, Variable $variable)
{ {
$oldMarginal = clone $variable->getValue(); $oldMarginal = clone $variable->getValue();
$oldMessage = clone $message->getValue(); $oldMessage = clone $message->getValue();
@ -55,16 +61,18 @@ class GaussianGreaterThanFactor extends GaussianFactor
$denom = 1.0 - TruncatedGaussianCorrectionFunctions::wExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC); $denom = 1.0 - TruncatedGaussianCorrectionFunctions::wExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC);
$newPrecision = $c / $denom; $newPrecision = $c / $denom;
$newPrecisionMean = ($d + $newPrecisionMean = (
$d +
$sqrtC * $sqrtC *
TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC)) / TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC)
$denom; ) / $denom;
$newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision); $newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision);
$newMessage = GaussianDistribution::divide( $newMessage = GaussianDistribution::divide(
GaussianDistribution::multiply($oldMessage, $newMarginal), GaussianDistribution::multiply($oldMessage, $newMarginal),
$oldMarginal); $oldMarginal
);
// Update the message and marginal // Update the message and marginal
$message->setValue($newMessage); $message->setValue($newMessage);

@ -1,6 +1,7 @@
<?php namespace Moserware\Skills\TrueSkill\Factors; <?php namespace Moserware\Skills\TrueSkill\Factors;
use Exception; use Exception;
use Moserware\Skills\FactorGraphs\KeyedVariable;
use Moserware\Skills\FactorGraphs\Message; use Moserware\Skills\FactorGraphs\Message;
use Moserware\Skills\FactorGraphs\Variable; use Moserware\Skills\FactorGraphs\Variable;
use Moserware\Skills\Numerics\GaussianDistribution; use Moserware\Skills\Numerics\GaussianDistribution;
@ -14,7 +15,7 @@ class GaussianLikelihoodFactor extends GaussianFactor
{ {
private $_precision; private $_precision;
public function __construct($betaSquared, Variable &$variable1, Variable &$variable2) public function __construct($betaSquared, Variable $variable1, Variable $variable2)
{ {
parent::__construct(sprintf("Likelihood of %s going to %s", $variable2, $variable1)); parent::__construct(sprintf("Likelihood of %s going to %s", $variable2, $variable1));
$this->_precision = 1.0 / $betaSquared; $this->_precision = 1.0 / $betaSquared;
@ -24,16 +25,18 @@ class GaussianLikelihoodFactor extends GaussianFactor
public function getLogNormalization() public function getLogNormalization()
{ {
$vars = &$this->getVariables(); /** @var KeyedVariable[]|mixed $vars */
$messages = &$this->getMessages(); $vars = $this->getVariables();
/** @var Message[] $messages */
$messages = $this->getMessages();
return GaussianDistribution::logRatioNormalization( return GaussianDistribution::logRatioNormalization(
$vars[0]->getValue(), $vars[0]->getValue(),
$messages[0]->getValue()); $messages[0]->getValue()
);
} }
private function updateHelper(Message &$message1, Message &$message2, private function updateHelper(Message $message1, Message $message2, Variable $variable1, Variable $variable2)
Variable &$variable1, Variable &$variable2)
{ {
$message1Value = clone $message1->getValue(); $message1Value = clone $message1->getValue();
$message2Value = clone $message2->getValue(); $message2Value = clone $message2->getValue();
@ -45,7 +48,8 @@ class GaussianLikelihoodFactor extends GaussianFactor
$newMessage = GaussianDistribution::fromPrecisionMean( $newMessage = GaussianDistribution::fromPrecisionMean(
$a * ($marginal2->getPrecisionMean() - $message2Value->getPrecisionMean()), $a * ($marginal2->getPrecisionMean() - $message2Value->getPrecisionMean()),
$a * ($marginal2->getPrecision() - $message2Value->getPrecision())); $a * ($marginal2->getPrecision() - $message2Value->getPrecision())
);
$oldMarginalWithoutMessage = GaussianDistribution::divide($marginal1, $message1Value); $oldMarginalWithoutMessage = GaussianDistribution::divide($marginal1, $message1Value);
@ -62,8 +66,8 @@ class GaussianLikelihoodFactor extends GaussianFactor
public function updateMessageIndex($messageIndex) public function updateMessageIndex($messageIndex)
{ {
$messages = &$this->getMessages(); $messages = $this->getMessages();
$vars = &$this->getVariables(); $vars = $this->getVariables();
switch ($messageIndex) { switch ($messageIndex) {
case 0: case 0:

@ -13,7 +13,7 @@ class GaussianPriorFactor extends GaussianFactor
{ {
private $_newMessage; private $_newMessage;
public function __construct($mean, $variance, Variable &$variable) public function __construct($mean, $variance, Variable $variable)
{ {
parent::__construct(sprintf("Prior value going to %s", $variable)); parent::__construct(sprintf("Prior value going to %s", $variable));
$this->_newMessage = new GaussianDistribution($mean, sqrt($variance)); $this->_newMessage = new GaussianDistribution($mean, sqrt($variance));
@ -23,18 +23,17 @@ class GaussianPriorFactor extends GaussianFactor
$this->createVariableToMessageBindingWithMessage($variable, $newMessage); $this->createVariableToMessageBindingWithMessage($variable, $newMessage);
} }
protected function updateMessageVariable(Message &$message, Variable &$variable) protected function updateMessageVariable(Message $message, Variable $variable)
{ {
$oldMarginal = clone $variable->getValue(); $oldMarginal = clone $variable->getValue();
$oldMessage = $message; $oldMessage = $message;
$newMarginal = $newMarginal = GaussianDistribution::fromPrecisionMean(
GaussianDistribution::fromPrecisionMean( $oldMarginal->getPrecisionMean() + $this->_newMessage->getPrecisionMean() - $oldMessage->getValue()->getPrecisionMean(),
$oldMarginal->getPrecisionMean() + $this->_newMessage->getPrecisionMean() - $oldMessage->getValue()->getPrecisionMean(), $oldMarginal->getPrecision() + $this->_newMessage->getPrecision() - $oldMessage->getValue()->getPrecision()
$oldMarginal->getPrecision() + $this->_newMessage->getPrecision() - $oldMessage->getValue()->getPrecision()); );
$variable->setValue($newMarginal); $variable->setValue($newMarginal);
$newMessage = &$this->_newMessage; $message->setValue($this->_newMessage);
$message->setValue($newMessage);
return GaussianDistribution::subtract($oldMarginal, $newMarginal); return GaussianDistribution::subtract($oldMarginal, $newMarginal);
} }
} }

@ -20,7 +20,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
private $_weights; private $_weights;
private $_weightsSquared; private $_weightsSquared;
public function __construct(Variable &$sumVariable, array &$variablesToSum, array &$variableWeights = null) public function __construct(Variable $sumVariable, array $variablesToSum, array $variableWeights = null)
{ {
parent::__construct(self::createName($sumVariable, $variablesToSum, $variableWeights)); parent::__construct(self::createName($sumVariable, $variablesToSum, $variableWeights));
$this->_weights = array(); $this->_weights = array();
@ -29,7 +29,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
// The first weights are a straightforward copy // The first weights are a straightforward copy
// v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n // v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n
$variableWeightsLength = count($variableWeights); $variableWeightsLength = count($variableWeights);
$this->_weights[0] = \array_fill(0, count($variableWeights), 0); $this->_weights[0] = array_fill(0, count($variableWeights), 0);
for ($i = 0; $i < $variableWeightsLength; $i++) { for ($i = 0; $i < $variableWeightsLength; $i++) {
$weight = &$variableWeights[$i]; $weight = &$variableWeights[$i];
@ -104,16 +104,16 @@ class GaussianWeightedSumFactor extends GaussianFactor
$this->createVariableToMessageBinding($sumVariable); $this->createVariableToMessageBinding($sumVariable);
foreach ($variablesToSum as &$currentVariable) { foreach ($variablesToSum as $currentVariable) {
$localCurrentVariable = &$currentVariable; $localCurrentVariable = $currentVariable;
$this->createVariableToMessageBinding($localCurrentVariable); $this->createVariableToMessageBinding($localCurrentVariable);
} }
} }
public function getLogNormalization() public function getLogNormalization()
{ {
$vars = &$this->getVariables(); $vars = $this->getVariables();
$messages = &$this->getMessages(); $messages = $this->getMessages();
$result = 0.0; $result = 0.0;
@ -126,9 +126,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
return $result; return $result;
} }
private function updateHelper(array &$weights, array &$weightsSquared, private function updateHelper(array $weights, array $weightsSquared, array $messages, array $variables)
array &$messages,
array &$variables)
{ {
// Potentially look at http://mathworld.wolfram.com/NormalSumDistribution.html for clues as // Potentially look at http://mathworld.wolfram.com/NormalSumDistribution.html for clues as
// to what it's doing // to what it's doing
@ -185,23 +183,23 @@ class GaussianWeightedSumFactor extends GaussianFactor
public function updateMessageIndex($messageIndex) public function updateMessageIndex($messageIndex)
{ {
$allMessages = &$this->getMessages(); $allMessages = $this->getMessages();
$allVariables = &$this->getVariables(); $allVariables = $this->getVariables();
Guard::argumentIsValidIndex($messageIndex, count($allMessages), "messageIndex"); Guard::argumentIsValidIndex($messageIndex, count($allMessages), "messageIndex");
$updatedMessages = array(); $updatedMessages = array();
$updatedVariables = array(); $updatedVariables = array();
$indicesToUse = &$this->_variableIndexOrdersForWeights[$messageIndex]; $indicesToUse = $this->_variableIndexOrdersForWeights[$messageIndex];
// The tricky part here is that we have to put the messages and variables in the same // 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, // order as the weights. Thankfully, the weights and messages share the same index numbers,
// so we just need to make sure they're consistent // so we just need to make sure they're consistent
$allMessagesCount = count($allMessages); $allMessagesCount = count($allMessages);
for ($i = 0; $i < $allMessagesCount; $i++) { for ($i = 0; $i < $allMessagesCount; $i++) {
$updatedMessages[] = &$allMessages[$indicesToUse[$i]]; $updatedMessages[] = $allMessages[$indicesToUse[$i]];
$updatedVariables[] = &$allVariables[$indicesToUse[$i]]; $updatedVariables[] = $allVariables[$indicesToUse[$i]];
} }
return $this->updateHelper($this->_weights[$messageIndex], return $this->updateHelper($this->_weights[$messageIndex],

@ -14,7 +14,7 @@ class GaussianWithinFactor extends GaussianFactor
{ {
private $_epsilon; private $_epsilon;
public function __construct($epsilon, Variable &$variable) public function __construct($epsilon, Variable $variable)
{ {
parent::__construct(sprintf("%s <= %.2f", $variable, $epsilon)); parent::__construct(sprintf("%s <= %.2f", $variable, $epsilon));
$this->_epsilon = $epsilon; $this->_epsilon = $epsilon;
@ -23,11 +23,13 @@ class GaussianWithinFactor extends GaussianFactor
public function getLogNormalization() public function getLogNormalization()
{ {
$variables = &$this->getVariables(); /** @var Variable[] $variables */
$marginal = &$variables[0]->getValue(); $variables = $this->getVariables();
$marginal = $variables[0]->getValue();
$messages = &$this->getMessages(); /** @var Message[] $messages */
$message = &$messages[0]->getValue(); $messages = $this->getMessages();
$message = $messages[0]->getValue();
$messageFromVariable = GaussianDistribution::divide($marginal, $message); $messageFromVariable = GaussianDistribution::divide($marginal, $message);
$mean = $messageFromVariable->getMean(); $mean = $messageFromVariable->getMean();
$std = $messageFromVariable->getStandardDeviation(); $std = $messageFromVariable->getStandardDeviation();
@ -38,7 +40,7 @@ class GaussianWithinFactor extends GaussianFactor
return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z); return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z);
} }
protected function updateMessageVariable(Message &$message, Variable &$variable) protected function updateMessageVariable(Message $message, Variable $variable)
{ {
$oldMarginal = clone $variable->getValue(); $oldMarginal = clone $variable->getValue();
$oldMessage = clone $message->getValue(); $oldMessage = clone $message->getValue();
@ -55,15 +57,16 @@ class GaussianWithinFactor extends GaussianFactor
$denominator = 1.0 - TruncatedGaussianCorrectionFunctions::wWithinMargin($dOnSqrtC, $epsilonTimesSqrtC); $denominator = 1.0 - TruncatedGaussianCorrectionFunctions::wWithinMargin($dOnSqrtC, $epsilonTimesSqrtC);
$newPrecision = $c / $denominator; $newPrecision = $c / $denominator;
$newPrecisionMean = ($d + $newPrecisionMean = ( $d +
$sqrtC * $sqrtC *
TruncatedGaussianCorrectionFunctions::vWithinMargin($dOnSqrtC, $epsilonTimesSqrtC)) / TruncatedGaussianCorrectionFunctions::vWithinMargin($dOnSqrtC, $epsilonTimesSqrtC)
$denominator; ) / $denominator;
$newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision); $newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision);
$newMessage = GaussianDistribution::divide( $newMessage = GaussianDistribution::divide(
GaussianDistribution::multiply($oldMessage, $newMarginal), GaussianDistribution::multiply($oldMessage, $newMarginal),
$oldMarginal); $oldMarginal
);
// Update the message and marginal // Update the message and marginal
$message->setValue($newMessage); $message->setValue($newMessage);

@ -1,5 +1,6 @@
<?php namespace Moserware\Skills\TrueSkill\Layers; <?php namespace Moserware\Skills\TrueSkill\Layers;
use Exception;
use Moserware\Skills\FactorGraphs\ScheduleLoop; use Moserware\Skills\FactorGraphs\ScheduleLoop;
use Moserware\Skills\FactorGraphs\ScheduleSequence; use Moserware\Skills\FactorGraphs\ScheduleSequence;
use Moserware\Skills\FactorGraphs\ScheduleStep; use Moserware\Skills\FactorGraphs\ScheduleStep;
@ -11,30 +12,31 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
private $_TeamDifferencesComparisonLayer; private $_TeamDifferencesComparisonLayer;
private $_TeamPerformancesToTeamPerformanceDifferencesLayer; private $_TeamPerformancesToTeamPerformanceDifferencesLayer;
public function __construct(TrueSkillFactorGraph &$parentGraph, public function __construct(TrueSkillFactorGraph $parentGraph,
TeamPerformancesToTeamPerformanceDifferencesLayer &$teamPerformancesToPerformanceDifferences, TeamPerformancesToTeamPerformanceDifferencesLayer $teamPerformancesToPerformanceDifferences,
TeamDifferencesComparisonLayer &$teamDifferencesComparisonLayer) TeamDifferencesComparisonLayer $teamDifferencesComparisonLayer)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer = $teamPerformancesToPerformanceDifferences; $this->_TeamPerformancesToTeamPerformanceDifferencesLayer = $teamPerformancesToPerformanceDifferences;
$this->_TeamDifferencesComparisonLayer = $teamDifferencesComparisonLayer; $this->_TeamDifferencesComparisonLayer = $teamDifferencesComparisonLayer;
} }
public function &getLocalFactors() public function getLocalFactors()
{ {
$localFactors = $localFactors = array_merge($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(),
array_merge($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(), $this->_TeamDifferencesComparisonLayer->getLocalFactors()
$this->_TeamDifferencesComparisonLayer->getLocalFactors()); );
return $localFactors; return $localFactors;
} }
public function buildLayer() public function buildLayer()
{ {
$inputVariablesGroups = &$this->getInputVariablesGroups(); $inputVariablesGroups = $this->getInputVariablesGroups();
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups); $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups);
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer(); $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer();
$teamDifferencesOutputVariablesGroups = &$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups(); $teamDifferencesOutputVariablesGroups = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups();
$this->_TeamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups); $this->_TeamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups);
$this->_TeamDifferencesComparisonLayer->buildLayer(); $this->_TeamDifferencesComparisonLayer->buildLayer();
} }
@ -44,7 +46,7 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
switch (count($this->getInputVariablesGroups())) { switch (count($this->getInputVariablesGroups())) {
case 0: case 0:
case 1: case 1:
throw new InvalidOperationException(); throw new Exception('InvalidOperation');
case 2: case 2:
$loop = $this->createTwoTeamInnerPriorLoopSchedule(); $loop = $this->createTwoTeamInnerPriorLoopSchedule();
break; break;
@ -57,10 +59,11 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()); $totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors());
$totalTeams = $totalTeamDifferences + 1; $totalTeams = $totalTeamDifferences + 1;
$localFactors = &$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $localFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$firstDifferencesFactor = $localFactors[0];
$lastDifferencesFactor = $localFactors[$totalTeamDifferences - 1];
$firstDifferencesFactor = &$localFactors[0];
$lastDifferencesFactor = &$localFactors[$totalTeamDifferences - 1];
$innerSchedule = new ScheduleSequence( $innerSchedule = new ScheduleSequence(
"inner schedule", "inner schedule",
array( array(
@ -79,11 +82,11 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
private function createTwoTeamInnerPriorLoopSchedule() private function createTwoTeamInnerPriorLoopSchedule()
{ {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = &$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = &$this->_TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$firstPerfToTeamDiff = &$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[0]; $firstPerfToTeamDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[0];
$firstTeamDiffComparison = &$teamDifferencesComparisonLayerLocalFactors[0]; $firstTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[0];
$itemsToSequence = array( $itemsToSequence = array(
new ScheduleStep( new ScheduleStep(
"send team perf to perf differences", "send team perf to perf differences",
@ -107,11 +110,11 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$forwardScheduleList = array(); $forwardScheduleList = array();
for ($i = 0; $i < $totalTeamDifferences - 1; $i++) { for ($i = 0; $i < $totalTeamDifferences - 1; $i++) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = &$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = &$this->_TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$currentTeamPerfToTeamPerfDiff = &$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$i]; $currentTeamPerfToTeamPerfDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$i];
$currentTeamDiffComparison = &$teamDifferencesComparisonLayerLocalFactors[$i]; $currentTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[$i];
$currentForwardSchedulePiece = $currentForwardSchedulePiece =
$this->scheduleSequence( $this->scheduleSequence(
@ -135,12 +138,12 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$backwardScheduleList = array(); $backwardScheduleList = array();
for ($i = 0; $i < $totalTeamDifferences - 1; $i++) { for ($i = 0; $i < $totalTeamDifferences - 1; $i++) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = &$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = &$this->_TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$differencesFactor = &$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i]; $differencesFactor = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i];
$comparisonFactor = &$teamDifferencesComparisonLayerLocalFactors[$totalTeamDifferences - 1 - $i]; $comparisonFactor = $teamDifferencesComparisonLayerLocalFactors[$totalTeamDifferences - 1 - $i];
$performancesToDifferencesFactor = &$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i]; $performancesToDifferencesFactor = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i];
$currentBackwardSchedulePiece = new ScheduleSequence( $currentBackwardSchedulePiece = new ScheduleSequence(
"current backward schedule piece", "current backward schedule piece",

@ -9,17 +9,17 @@ use Moserware\Skills\TrueSkill\TrueSkillFactorGraph;
class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer
{ {
public function __construct(TrueSkillFactorGraph &$parentGraph) public function __construct(TrueSkillFactorGraph $parentGraph)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
} }
public function buildLayer() public function buildLayer()
{ {
$inputVariablesGroups = &$this->getInputVariablesGroups(); $inputVariablesGroups = $this->getInputVariablesGroups();
foreach ($inputVariablesGroups as &$currentTeam) { foreach ($inputVariablesGroups as $currentTeam) {
$localCurrentTeam = &$currentTeam; $localCurrentTeam = $currentTeam;
$teamPerformance = &$this->createOutputVariable($localCurrentTeam); $teamPerformance = $this->createOutputVariable($localCurrentTeam);
$newSumFactor = $this->createPlayerToTeamSumFactor($localCurrentTeam, $teamPerformance); $newSumFactor = $this->createPlayerToTeamSumFactor($localCurrentTeam, $teamPerformance);
$this->addLayerFactor($newSumFactor); $this->addLayerFactor($newSumFactor);
@ -32,9 +32,9 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
public function createPriorSchedule() public function createPriorSchedule()
{ {
$localFactors = &$this->getLocalFactors(); $localFactors = $this->getLocalFactors();
$sequence = &$this->scheduleSequence( $sequence = $this->scheduleSequence(
array_map( array_map(
function ($weightedSumFactor) { function ($weightedSumFactor) {
return new ScheduleStep("Perf to Team Perf Step", $weightedSumFactor, 0); return new ScheduleStep("Perf to Team Perf Step", $weightedSumFactor, 0);
@ -44,11 +44,11 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
return $sequence; return $sequence;
} }
protected function createPlayerToTeamSumFactor(&$teamMembers, &$sumVariable) protected function createPlayerToTeamSumFactor($teamMembers, $sumVariable)
{ {
$weights = array_map( $weights = array_map(
function ($v) { function ($v) {
$player = &$v->getKey(); $player = $v->getKey();
return PartialPlay::getPartialPlayPercentage($player); return PartialPlay::getPartialPlayPercentage($player);
}, },
$teamMembers); $teamMembers);
@ -63,9 +63,9 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
public function createPosteriorSchedule() public function createPosteriorSchedule()
{ {
$allFactors = array(); $allFactors = array();
$localFactors = &$this->getLocalFactors(); $localFactors = $this->getLocalFactors();
foreach ($localFactors as &$currentFactor) { foreach ($localFactors as $currentFactor) {
$localCurrentFactor = &$currentFactor; $localCurrentFactor = $currentFactor;
$numberOfMessages = $localCurrentFactor->getNumberOfMessages(); $numberOfMessages = $localCurrentFactor->getNumberOfMessages();
for ($currentIteration = 1; $currentIteration < $numberOfMessages; $currentIteration++) { for ($currentIteration = 1; $currentIteration < $numberOfMessages; $currentIteration++) {
$allFactors[] = new ScheduleStep("team sum perf @" . $currentIteration, $allFactors[] = new ScheduleStep("team sum perf @" . $currentIteration,
@ -75,15 +75,14 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
return $this->scheduleSequence($allFactors, "all of the team's sum iterations"); return $this->scheduleSequence($allFactors, "all of the team's sum iterations");
} }
private function &createOutputVariable(&$team) private function createOutputVariable($team)
{ {
$memberNames = \array_map(function ($currentPlayer) { $memberNames = array_map(function ($currentPlayer) {
return (string)($currentPlayer->getKey()); return (string)($currentPlayer->getKey());
}, }, $team);
$team);
$teamMemberNames = \join(", ", $memberNames); $teamMemberNames = \join(", ", $memberNames);
$outputVariable = &$this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team[" . $teamMemberNames . "]'s performance"); $outputVariable = $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team[" . $teamMemberNames . "]'s performance");
return $outputVariable; return $outputVariable;
} }
} }

@ -13,7 +13,7 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
{ {
private $_teams; private $_teams;
public function __construct(TrueSkillFactorGraph &$parentGraph, array &$teams) public function __construct(TrueSkillFactorGraph $parentGraph, array $teams)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
$this->_teams = $teams; $this->_teams = $teams;
@ -21,17 +21,17 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
public function buildLayer() public function buildLayer()
{ {
$teams = &$this->_teams; $teams = $this->_teams;
foreach ($teams as &$currentTeam) { foreach ($teams as $currentTeam) {
$localCurrentTeam = &$currentTeam; $localCurrentTeam = $currentTeam;
$currentTeamSkills = array(); $currentTeamSkills = array();
$currentTeamAllPlayers = $localCurrentTeam->getAllPlayers(); $currentTeamAllPlayers = $localCurrentTeam->getAllPlayers();
foreach ($currentTeamAllPlayers as &$currentTeamPlayer) { foreach ($currentTeamAllPlayers as $currentTeamPlayer) {
$localCurrentTeamPlayer = &$currentTeamPlayer; $localCurrentTeamPlayer = $currentTeamPlayer;
$currentTeamPlayerRating = $currentTeam->getRating($localCurrentTeamPlayer); $currentTeamPlayerRating = $currentTeam->getRating($localCurrentTeamPlayer);
$playerSkill = &$this->createSkillOutputVariable($localCurrentTeamPlayer); $playerSkill = $this->createSkillOutputVariable($localCurrentTeamPlayer);
$priorFactor = &$this->createPriorFactor($localCurrentTeamPlayer, $currentTeamPlayerRating, $playerSkill); $priorFactor = $this->createPriorFactor($localCurrentTeamPlayer, $currentTeamPlayerRating, $playerSkill);
$this->addLayerFactor($priorFactor); $this->addLayerFactor($priorFactor);
$currentTeamSkills[] = $playerSkill; $currentTeamSkills[] = $playerSkill;
} }
@ -43,17 +43,17 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
public function createPriorSchedule() public function createPriorSchedule()
{ {
$localFactors = &$this->getLocalFactors(); $localFactors = $this->getLocalFactors();
return $this->scheduleSequence( return $this->scheduleSequence(
array_map( array_map(
function (&$prior) { function ($prior) {
return new ScheduleStep("Prior to Skill Step", $prior, 0); return new ScheduleStep("Prior to Skill Step", $prior, 0);
}, },
$localFactors), $localFactors),
"All priors"); "All priors");
} }
private function createPriorFactor(&$player, Rating &$priorRating, Variable &$skillsVariable) private function createPriorFactor($player, Rating $priorRating, Variable $skillsVariable)
{ {
return new GaussianPriorFactor( return new GaussianPriorFactor(
$priorRating->getMean(), $priorRating->getMean(),
@ -63,11 +63,11 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
); );
} }
private function &createSkillOutputVariable(&$key) private function createSkillOutputVariable($key)
{ {
$parentFactorGraph = &$this->getParentFactorGraph(); $parentFactorGraph = $this->getParentFactorGraph();
$variableFactory = &$parentFactorGraph->getVariableFactory(); $variableFactory = $parentFactorGraph->getVariableFactory();
$skillOutputVariable = &$variableFactory->createKeyedVariable($key, $key . "'s skill"); $skillOutputVariable = $variableFactory->createKeyedVariable($key, $key . "'s skill");
return $skillOutputVariable; return $skillOutputVariable;
} }
} }

@ -8,23 +8,23 @@ use Moserware\Skills\TrueSkill\Factors\GaussianLikelihoodFactor;
class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
{ {
public function __construct(TrueSkillFactorGraph &$parentGraph) public function __construct(TrueSkillFactorGraph $parentGraph)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
} }
public function buildLayer() public function buildLayer()
{ {
$inputVariablesGroups = &$this->getInputVariablesGroups(); $inputVariablesGroups = $this->getInputVariablesGroups();
$outputVariablesGroups = &$this->getOutputVariablesGroups(); $outputVariablesGroups = &$this->getOutputVariablesGroups();
foreach ($inputVariablesGroups as &$currentTeam) { foreach ($inputVariablesGroups as $currentTeam) {
$currentTeamPlayerPerformances = array(); $currentTeamPlayerPerformances = array();
foreach ($currentTeam as &$playerSkillVariable) { foreach ($currentTeam as $playerSkillVariable) {
$localPlayerSkillVariable = &$playerSkillVariable; $localPlayerSkillVariable = $playerSkillVariable;
$currentPlayer = &$localPlayerSkillVariable->getKey(); $currentPlayer = $localPlayerSkillVariable->getKey();
$playerPerformance = &$this->createOutputVariable($currentPlayer); $playerPerformance = $this->createOutputVariable($currentPlayer);
$newLikelihoodFactor = $this->createLikelihood($localPlayerSkillVariable, $playerPerformance); $newLikelihoodFactor = $this->createLikelihood($localPlayerSkillVariable, $playerPerformance);
$this->addLayerFactor($newLikelihoodFactor); $this->addLayerFactor($newLikelihoodFactor);
$currentTeamPlayerPerformances[] = $playerPerformance; $currentTeamPlayerPerformances[] = $playerPerformance;
@ -34,7 +34,7 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
} }
} }
private function createLikelihood(KeyedVariable &$playerSkill, KeyedVariable &$playerPerformance) private function createLikelihood(KeyedVariable $playerSkill, KeyedVariable $playerPerformance)
{ {
return new GaussianLikelihoodFactor( return new GaussianLikelihoodFactor(
BasicMath::square($this->getParentFactorGraph()->getGameInfo()->getBeta()), BasicMath::square($this->getParentFactorGraph()->getGameInfo()->getBeta()),
@ -43,15 +43,15 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
); );
} }
private function &createOutputVariable(&$key) private function createOutputVariable($key)
{ {
$outputVariable = &$this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, $key . "'s performance"); $outputVariable = $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, $key . "'s performance");
return $outputVariable; return $outputVariable;
} }
public function createPriorSchedule() public function createPriorSchedule()
{ {
$localFactors = &$this->getLocalFactors(); $localFactors = $this->getLocalFactors();
return $this->scheduleSequence( return $this->scheduleSequence(
array_map( array_map(
function ($likelihood) { function ($likelihood) {
@ -63,7 +63,7 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
public function createPosteriorSchedule() public function createPosteriorSchedule()
{ {
$localFactors = &$this->getLocalFactors(); $localFactors = $this->getLocalFactors();
return $this->scheduleSequence( return $this->scheduleSequence(
array_map( array_map(
function ($likelihood) { function ($likelihood) {

@ -10,22 +10,22 @@ class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
private $_epsilon; private $_epsilon;
private $_teamRanks; private $_teamRanks;
public function __construct(TrueSkillFactorGraph &$parentGraph, array &$teamRanks) public function __construct(TrueSkillFactorGraph $parentGraph, array $teamRanks)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
$this->_teamRanks = $teamRanks; $this->_teamRanks = $teamRanks;
$gameInfo = &$this->getParentFactorGraph()->getGameInfo(); $gameInfo = $this->getParentFactorGraph()->getGameInfo();
$this->_epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta()); $this->_epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
} }
public function buildLayer() public function buildLayer()
{ {
$inputVarGroups = &$this->getInputVariablesGroups(); $inputVarGroups = $this->getInputVariablesGroups();
$inputVarGroupsCount = count($inputVarGroups); $inputVarGroupsCount = count($inputVarGroups);
for ($i = 0; $i < $inputVarGroupsCount; $i++) { for ($i = 0; $i < $inputVarGroupsCount; $i++) {
$isDraw = ($this->_teamRanks[$i] == $this->_teamRanks[$i + 1]); $isDraw = ($this->_teamRanks[$i] == $this->_teamRanks[$i + 1]);
$teamDifference = &$inputVarGroups[$i][0]; $teamDifference = $inputVarGroups[$i][0];
$factor = $factor =
$isDraw $isDraw

@ -6,23 +6,23 @@ use Moserware\Skills\TrueSkill\Factors\GaussianWeightedSumFactor;
class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer
{ {
public function __construct(TrueSkillFactorGraph &$parentGraph) public function __construct(TrueSkillFactorGraph $parentGraph)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
} }
public function buildLayer() public function buildLayer()
{ {
$inputVariablesGroups = &$this->getInputVariablesGroups(); $inputVariablesGroups = $this->getInputVariablesGroups();
$inputVariablesGroupsCount = count($inputVariablesGroups); $inputVariablesGroupsCount = count($inputVariablesGroups);
$outputVariablesGroup = &$this->getOutputVariablesGroups(); $outputVariablesGroup = &$this->getOutputVariablesGroups();
for ($i = 0; $i < $inputVariablesGroupsCount - 1; $i++) for ($i = 0; $i < $inputVariablesGroupsCount - 1; $i++)
{ {
$strongerTeam = &$inputVariablesGroups[$i][0]; $strongerTeam = $inputVariablesGroups[$i][0];
$weakerTeam = &$inputVariablesGroups[$i + 1][0]; $weakerTeam = $inputVariablesGroups[$i + 1][0];
$currentDifference = &$this->createOutputVariable(); $currentDifference = $this->createOutputVariable();
$newDifferencesFactor = $this->createTeamPerformanceToDifferenceFactor($strongerTeam, $weakerTeam, $currentDifference); $newDifferencesFactor = $this->createTeamPerformanceToDifferenceFactor($strongerTeam, $weakerTeam, $currentDifference);
$this->addLayerFactor($newDifferencesFactor); $this->addLayerFactor($newDifferencesFactor);
@ -31,17 +31,18 @@ class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorG
} }
} }
private function createTeamPerformanceToDifferenceFactor( private function createTeamPerformanceToDifferenceFactor(Variable $strongerTeam,
Variable &$strongerTeam, Variable &$weakerTeam, Variable &$output) Variable $weakerTeam,
Variable $output)
{ {
$teams = array($strongerTeam, $weakerTeam); $teams = array($strongerTeam, $weakerTeam);
$weights = array(1.0, -1.0); $weights = array(1.0, -1.0);
return new GaussianWeightedSumFactor($output, $teams, $weights); return new GaussianWeightedSumFactor($output, $teams, $weights);
} }
private function &createOutputVariable() private function createOutputVariable()
{ {
$outputVariable = &$this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team performance difference"); $outputVariable = $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team performance difference");
return $outputVariable; return $outputVariable;
} }
} }

@ -5,7 +5,7 @@ use Moserware\Skills\TrueSkill\TrueSkillFactorGraph;
abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer
{ {
public function __construct(TrueSkillFactorGraph &$parentGraph) public function __construct(TrueSkillFactorGraph $parentGraph)
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
} }

@ -1,6 +1,7 @@
<?php namespace Moserware\Skills\TrueSkill; <?php namespace Moserware\Skills\TrueSkill;
use Moserware\Skills\GameInfo; use Moserware\Skills\GameInfo;
use Moserware\Skills\Numerics\GaussianDistribution;
use Moserware\Skills\Rating; use Moserware\Skills\Rating;
use Moserware\Skills\RatingContainer; use Moserware\Skills\RatingContainer;
use Moserware\Skills\FactorGraphs\FactorGraph; use Moserware\Skills\FactorGraphs\FactorGraph;
@ -20,7 +21,7 @@ class TrueSkillFactorGraph extends FactorGraph
private $_layers; private $_layers;
private $_priorLayer; private $_priorLayer;
public function __construct(GameInfo &$gameInfo, array &$teams, array $teamRanks) public function __construct(GameInfo $gameInfo, array $teams, array $teamRanks)
{ {
$this->_priorLayer = new PlayerPriorValuesToSkillsLayer($this, $teams); $this->_priorLayer = new PlayerPriorValuesToSkillsLayer($this, $teams);
$this->_gameInfo = $gameInfo; $this->_gameInfo = $gameInfo;
@ -50,15 +51,15 @@ class TrueSkillFactorGraph extends FactorGraph
{ {
$lastOutput = null; $lastOutput = null;
$layers = &$this->_layers; $layers = $this->_layers;
foreach ($layers as &$currentLayer) { foreach ($layers as $currentLayer) {
if ($lastOutput != null) { if ($lastOutput != null) {
$currentLayer->setInputVariablesGroups($lastOutput); $currentLayer->setInputVariablesGroups($lastOutput);
} }
$currentLayer->buildLayer(); $currentLayer->buildLayer();
$lastOutput = &$currentLayer->getOutputVariablesGroups(); $lastOutput = $currentLayer->getOutputVariablesGroups();
} }
} }
@ -72,11 +73,11 @@ class TrueSkillFactorGraph extends FactorGraph
{ {
$factorList = new FactorList(); $factorList = new FactorList();
$layers = &$this->_layers; $layers = $this->_layers;
foreach ($layers as &$currentLayer) { foreach ($layers as $currentLayer) {
$localFactors = &$currentLayer->getLocalFactors(); $localFactors = $currentLayer->getLocalFactors();
foreach ($localFactors as &$currentFactor) { foreach ($localFactors as $currentFactor) {
$localCurrentFactor = &$currentFactor; $localCurrentFactor = $currentFactor;
$factorList->addFactor($localCurrentFactor); $factorList->addFactor($localCurrentFactor);
} }
} }
@ -89,17 +90,17 @@ class TrueSkillFactorGraph extends FactorGraph
{ {
$fullSchedule = array(); $fullSchedule = array();
$layers = &$this->_layers; $layers = $this->_layers;
foreach ($layers as &$currentLayer) { foreach ($layers as $currentLayer) {
$currentPriorSchedule = $currentLayer->createPriorSchedule(); $currentPriorSchedule = $currentLayer->createPriorSchedule();
if ($currentPriorSchedule != null) { if ($currentPriorSchedule != null) {
$fullSchedule[] = $currentPriorSchedule; $fullSchedule[] = $currentPriorSchedule;
} }
} }
$allLayersReverse = \array_reverse($this->_layers); $allLayersReverse = array_reverse($this->_layers);
foreach ($allLayersReverse as &$currentLayer) { foreach ($allLayersReverse as $currentLayer) {
$currentPosteriorSchedule = $currentLayer->createPosteriorSchedule(); $currentPosteriorSchedule = $currentLayer->createPosteriorSchedule();
if ($currentPosteriorSchedule != null) { if ($currentPosteriorSchedule != null) {
$fullSchedule[] = $currentPosteriorSchedule; $fullSchedule[] = $currentPosteriorSchedule;
@ -113,10 +114,10 @@ class TrueSkillFactorGraph extends FactorGraph
{ {
$result = new RatingContainer(); $result = new RatingContainer();
$priorLayerOutputVariablesGroups = &$this->_priorLayer->getOutputVariablesGroups(); $priorLayerOutputVariablesGroups = $this->_priorLayer->getOutputVariablesGroups();
foreach ($priorLayerOutputVariablesGroups as &$currentTeam) { foreach ($priorLayerOutputVariablesGroups as $currentTeam) {
foreach ($currentTeam as &$currentPlayer) { foreach ($currentTeam as $currentPlayer) {
$localCurrentPlayer = &$currentPlayer->getKey(); $localCurrentPlayer = $currentPlayer->getKey();
$newRating = new Rating($currentPlayer->getValue()->getMean(), $newRating = new Rating($currentPlayer->getValue()->getMean(),
$currentPlayer->getValue()->getStandardDeviation()); $currentPlayer->getValue()->getStandardDeviation());

@ -38,8 +38,12 @@ class TruncatedGaussianCorrectionFunctions
* *
* In the reference F# implementation, this is referred to as "the multiplicative * In the reference F# implementation, this is referred to as "the multiplicative
* correction of a single-sided truncated Gaussian with unit variance." * correction of a single-sided truncated Gaussian with unit variance."
*
* @param $teamPerformanceDifference
* @param $drawMargin
* @param $c
* @return float
*/ */
public static function wExceedsMarginScaled($teamPerformanceDifference, $drawMargin, $c) public static function wExceedsMarginScaled($teamPerformanceDifference, $drawMargin, $c)
{ {
return self::wExceedsMargin($teamPerformanceDifference / $c, $drawMargin / $c); return self::wExceedsMargin($teamPerformanceDifference / $c, $drawMargin / $c);

@ -9,7 +9,6 @@ use Moserware\Skills\Rating;
use Moserware\Skills\RatingContainer; use Moserware\Skills\RatingContainer;
use Moserware\Skills\SkillCalculator; use Moserware\Skills\SkillCalculator;
use Moserware\Skills\SkillCalculatorSupportedOptions; use Moserware\Skills\SkillCalculatorSupportedOptions;
use Moserware\Skills\PlayersRange; use Moserware\Skills\PlayersRange;
use Moserware\Skills\TeamsRange; use Moserware\Skills\TeamsRange;
@ -126,7 +125,7 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
/** /**
* {@inheritdoc } * {@inheritdoc }
*/ */
public function calculateMatchQuality(GameInfo $gameInfo, array &$teams) public function calculateMatchQuality(GameInfo $gameInfo, array $teams)
{ {
Guard::argumentNotNull($gameInfo, "gameInfo"); Guard::argumentNotNull($gameInfo, "gameInfo");
$this->validateTeamCountAndPlayersCountPerTeam($teams); $this->validateTeamCountAndPlayersCountPerTeam($teams);
@ -146,18 +145,18 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
$player2SigmaSquared = BasicMath::square($player2Rating->getStandardDeviation()); $player2SigmaSquared = BasicMath::square($player2Rating->getStandardDeviation());
// This is the square root part of the equation: // This is the square root part of the equation:
$sqrtPart = $sqrtPart = sqrt(
sqrt( (2*$betaSquared)
(2*$betaSquared) /
/ (2*$betaSquared + $player1SigmaSquared + $player2SigmaSquared)
(2*$betaSquared + $player1SigmaSquared + $player2SigmaSquared)); );
// This is the exponent part of the equation: // This is the exponent part of the equation:
$expPart = $expPart = exp(
exp( (-1*BasicMath::square($player1Rating->getMean() - $player2Rating->getMean()))
(-1*BasicMath::square($player1Rating->getMean() - $player2Rating->getMean())) /
/ (2*(2*$betaSquared + $player1SigmaSquared + $player2SigmaSquared))
(2*(2*$betaSquared + $player1SigmaSquared + $player2SigmaSquared))); );
return $sqrtPart*$expPart; return $sqrtPart*$expPart;
} }

@ -57,7 +57,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
} }
private static function updatePlayerRatings(GameInfo $gameInfo, private static function updatePlayerRatings(GameInfo $gameInfo,
RatingContainer &$newPlayerRatings, RatingContainer $newPlayerRatings,
Team $selfTeam, Team $selfTeam,
Team $otherTeam, Team $otherTeam,
$selfToOtherTeamComparison) $selfToOtherTeamComparison)
@ -119,9 +119,9 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
$rankMultiplier = 1; $rankMultiplier = 1;
} }
$selfTeamAllPlayers = &$selfTeam->getAllPlayers(); $selfTeamAllPlayers = $selfTeam->getAllPlayers();
foreach ($selfTeamAllPlayers as &$selfTeamCurrentPlayer) { foreach ($selfTeamAllPlayers as $selfTeamCurrentPlayer) {
$localSelfTeamCurrentPlayer = &$selfTeamCurrentPlayer; $localSelfTeamCurrentPlayer = $selfTeamCurrentPlayer;
$previousPlayerRating = $selfTeam->getRating($localSelfTeamCurrentPlayer); $previousPlayerRating = $selfTeam->getRating($localSelfTeamCurrentPlayer);
$meanMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / $c; $meanMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / $c;
@ -141,7 +141,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function calculateMatchQuality(GameInfo $gameInfo, array &$teams) public function calculateMatchQuality(GameInfo $gameInfo, array $teams)
{ {
Guard::argumentNotNull($gameInfo, "gameInfo"); Guard::argumentNotNull($gameInfo, "gameInfo");
$this->validateTeamCountAndPlayersCountPerTeam($teams); $this->validateTeamCountAndPlayersCountPerTeam($teams);