CodeBeautifier for PSR-12 standard.

This commit is contained in:
Jens True 2023-08-01 13:53:19 +00:00
parent da8125be94
commit c8c126962d
27 changed files with 307 additions and 213 deletions

@ -8,18 +8,18 @@ use Exception;
abstract class Factor implements \Stringable
{
private array $_messages = [];
private array $messages = [];
private $_messageToVariableBinding;
private $messageToVariableBinding;
private string $_name;
private string $name;
private array $_variables = [];
private array $variables = [];
protected function __construct(string $name)
{
$this->_name = 'Factor['.$name.']';
$this->_messageToVariableBinding = new HashMap();
$this->name = 'Factor[' . $name . ']';
$this->messageToVariableBinding = new HashMap();
}
/**
@ -35,17 +35,17 @@ abstract class Factor implements \Stringable
*/
public function getNumberOfMessages(): int
{
return count($this->_messages);
return count($this->messages);
}
protected function getVariables(): array
{
return $this->_variables;
return $this->variables;
}
protected function getMessages(): array
{
return $this->_messages;
return $this->messages;
}
/**
@ -57,9 +57,9 @@ abstract class Factor implements \Stringable
*/
public function updateMessageIndex(int $messageIndex)
{
Guard::argumentIsValidIndex($messageIndex, count($this->_messages), 'messageIndex');
$message = $this->_messages[$messageIndex];
$variable = $this->_messageToVariableBinding->getValue($message);
Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex');
$message = $this->messages[$messageIndex];
$variable = $this->messageToVariableBinding->getValue($message);
return $this->updateMessageVariable($message, $variable);
}
@ -74,7 +74,7 @@ abstract class Factor implements \Stringable
*/
public function resetMarginals()
{
$allValues = $this->_messageToVariableBinding->getAllValues();
$allValues = $this->messageToVariableBinding->getAllValues();
foreach ($allValues as $currentVariable) {
$currentVariable->resetToPrior();
}
@ -83,17 +83,17 @@ abstract class Factor implements \Stringable
/**
* Sends the ith message to the marginal and returns the log-normalization constant
*
* @param $messageIndex
* @param $messageIndex
* @return
*
* @throws Exception
*/
public function sendMessageIndex($messageIndex)
{
Guard::argumentIsValidIndex($messageIndex, count($this->_messages), 'messageIndex');
Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex');
$message = $this->_messages[$messageIndex];
$variable = $this->_messageToVariableBinding->getValue($message);
$message = $this->messages[$messageIndex];
$variable = $this->messageToVariableBinding->getValue($message);
return $this->sendMessageVariable($message, $variable);
}
@ -104,15 +104,15 @@ abstract class Factor implements \Stringable
protected function createVariableToMessageBindingWithMessage(Variable $variable, Message $message): Message
{
$this->_messageToVariableBinding->setValue($message, $variable);
$this->_messages[] = $message;
$this->_variables[] = $variable;
$this->messageToVariableBinding->setValue($message, $variable);
$this->messages[] = $message;
$this->variables[] = $variable;
return $message;
}
public function __toString(): string
{
return $this->_name;
return $this->name;
}
}

@ -4,15 +4,15 @@ namespace DNW\Skills\FactorGraphs;
class FactorGraph
{
private $_variableFactory;
private $variableFactory;
public function getVariableFactory()
{
return $this->_variableFactory;
return $this->variableFactory;
}
public function setVariableFactory(VariableFactory $factory)
{
$this->_variableFactory = $factory;
$this->variableFactory = $factory;
}
}

@ -4,33 +4,33 @@ namespace DNW\Skills\FactorGraphs;
class Variable implements \Stringable
{
private string $_name;
private string $name;
private mixed $_value;
private mixed $value;
public function __construct(string $name, private mixed $_prior)
public function __construct(string $name, private mixed $prior)
{
$this->_name = 'Variable['.$name.']';
$this->name = 'Variable[' . $name . ']';
$this->resetToPrior();
}
public function getValue(): mixed
{
return $this->_value;
return $this->value;
}
public function setValue(mixed $value): void
{
$this->_value = $value;
$this->value = $value;
}
public function resetToPrior(): void
{
$this->_value = $this->_prior;
$this->value = $this->prior;
}
public function __toString(): string
{
return $this->_name;
return $this->name;
}
}

@ -5,15 +5,15 @@ namespace DNW\Skills\Numerics;
/**
* Basic math functions.
*
* @author Jeff Moser <jeff@moserware.com>
* @copyright 2010 Jeff Moser
* @author Jeff Moser <jeff@moserware.com>
* @copyright 2010 Jeff Moser
*/
class BasicMath
{
/**
* Squares the input (x^2 = x * x)
*
* @param number $x Value to square (x)
* @param number $x Value to square (x)
* @return number The squared value (x^2)
*/
public static function square($x): float|int
@ -24,8 +24,8 @@ class BasicMath
/**
* Sums the items in $itemsToSum
*
* @param array $itemsToSum The items to sum,
* @param callable $callback The function to apply to each array element before summing.
* @param array $itemsToSum The items to sum,
* @param callable $callback The function to apply to each array element before summing.
* @return number The sum.
*/
public static function sum(array $itemsToSum, \Closure $callback): float|int

@ -5,8 +5,8 @@ namespace DNW\Skills\Numerics;
/**
* Computes Gaussian (bell curve) values.
*
* @author Jeff Moser <jeff@moserware.com>
* @copyright 2010 Jeff Moser
* @author Jeff Moser <jeff@moserware.com>
* @copyright 2010 Jeff Moser
*/
class GaussianDistribution implements \Stringable
{

@ -70,12 +70,16 @@ class Matrix
$transposeMatrix = [];
$rowMatrixData = $this->_matrixRowData;
for ($currentRowTransposeMatrix = 0;
for (
$currentRowTransposeMatrix = 0;
$currentRowTransposeMatrix < $this->_columnCount;
$currentRowTransposeMatrix++) {
for ($currentColumnTransposeMatrix = 0;
$currentRowTransposeMatrix++
) {
for (
$currentColumnTransposeMatrix = 0;
$currentColumnTransposeMatrix < $this->_rowCount;
$currentColumnTransposeMatrix++) {
$currentColumnTransposeMatrix++
) {
$transposeMatrix[$currentRowTransposeMatrix][$currentColumnTransposeMatrix] =
$rowMatrixData[$currentColumnTransposeMatrix][$currentRowTransposeMatrix];
}
@ -155,8 +159,12 @@ class Matrix
$c = $this->_matrixRowData[1][0];
$d = $this->_matrixRowData[1][1];
return new SquareMatrix($d, -$b,
-$c, $a);
return new SquareMatrix(
$d,
-$b,
-$c,
$a
);
}
// The idea is that it's the transpose of the cofactors
@ -204,8 +212,8 @@ class Matrix
{
if (
($left->getRowCount() != $right->getRowCount())
||
($left->getColumnCount() != $right->getColumnCount())
|| ($left->getColumnCount() != $right->getColumnCount())
) {
throw new Exception('Matrices must be of the same size');
}
@ -318,8 +326,10 @@ class Matrix
for ($currentRow = 0; $currentRow < $this->_rowCount; $currentRow++) {
for ($currentColumn = 0; $currentColumn < $this->_columnCount; $currentColumn++) {
$delta =
abs($this->_matrixRowData[$currentRow][$currentColumn] -
$otherMatrix->getValue($currentRow, $currentColumn));
abs(
$this->_matrixRowData[$currentRow][$currentColumn] -
$otherMatrix->getValue($currentRow, $currentColumn)
);
if ($delta > self::ERROR_TOLERANCE) {
return false;

@ -18,14 +18,15 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate, \Stringable
/**
* Constructs a player.
*
* @param mixed $_Id The identifier for the player, such as a name.
* @param number $partialPlayPercentage The weight percentage to give this player when calculating a new rank.
* @param number $partialUpdatePercentage Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update.
* @param mixed $_Id The identifier for the player, such as a name.
* @param number $partialPlayPercentage The weight percentage to give this player when calculating a new rank.
* @param number $partialUpdatePercentage Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update.
*/
public function __construct(private $_Id,
$partialPlayPercentage = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
$partialUpdatePercentage = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE)
{
public function __construct(
private $_Id,
$partialPlayPercentage = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
$partialUpdatePercentage = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE
) {
// If they don't want to give a player an id, that's ok...
Guard::argumentInRangeInclusive($partialPlayPercentage, 0.0, 1.0, 'partialPlayPercentage');
Guard::argumentInRangeInclusive($partialUpdatePercentage, 0, 1.0, 'partialUpdatePercentage');

@ -10,8 +10,8 @@ class RankSorter
/**
* Performs an in-place sort of the items in according to the ranks in non-decreasing order.
*
* @param array $teams The items to sort according to the order specified by ranks.
* @param array $teamRanks The ranks for each item where 1 is first place.
* @param array $teams The items to sort according to the order specified by ranks.
* @param array $teamRanks The ranks for each item where 1 is first place.
* @return array
*/
public static function sort(array &$teams, array &$teamRanks)

@ -12,9 +12,9 @@ class Rating implements \Stringable
/**
* Constructs a rating.
*
* @param float $_mean The statistical mean value of the rating (also known as mu).
* @param float $_standardDeviation The standard deviation of the rating (also known as s).
* @param float|int $_conservativeStandardDeviationMultiplier optional The number of standardDeviations to subtract from the mean to achieve a conservative rating.
* @param float $_mean The statistical mean value of the rating (also known as mu).
* @param float $_standardDeviation The standard deviation of the rating (also known as s).
* @param float|int $_conservativeStandardDeviationMultiplier optional The number of standardDeviations to subtract from the mean to achieve a conservative rating.
*/
public function __construct(private $_mean, private $_standardDeviation, private $_conservativeStandardDeviationMultiplier = self::CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER)
{
@ -47,7 +47,7 @@ class Rating implements \Stringable
public function getPartialUpdate(Rating $prior, Rating $fullPosterior, $updatePercentage)
{
$priorGaussian = new GaussianDistribution($prior->getMean(), $prior->getStandardDeviation());
$posteriorGaussian = new GaussianDistribution($fullPosterior->getMean(), $fullPosterior.getStandardDeviation());
$posteriorGaussian = new GaussianDistribution($fullPosterior->getMean(), $fullPosterior . getStandardDeviation());
// From a clarification email from Ralf Herbrich:
// "the idea is to compute a linear interpolation between the prior and posterior skills of each player
@ -56,7 +56,7 @@ class Rating implements \Stringable
$precisionDifference = $posteriorGaussian->getPrecision() - $priorGaussian->getPrecision();
$partialPrecisionDifference = $updatePercentage * $precisionDifference;
$precisionMeanDifference = $posteriorGaussian->getPrecisionMean() - $priorGaussian.getPrecisionMean();
$precisionMeanDifference = $posteriorGaussian->getPrecisionMean() - $priorGaussian . getPrecisionMean();
$partialPrecisionMeanDifference = $updatePercentage * $precisionMeanDifference;
$partialPosteriorGaussion = GaussianDistribution::fromPrecisionMean(

@ -19,21 +19,22 @@ abstract class SkillCalculator
/**
* Calculates new ratings based on the prior ratings and team ranks.
*
* @param GameInfo $gameInfo Parameters for the game.
* @param array $teamsOfPlayerToRatings A mapping of team players and their ratings.
* @param array $teamRanks The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2).
* @param GameInfo $gameInfo Parameters for the game.
* @param array $teamsOfPlayerToRatings A mapping of team players and their ratings.
* @param array $teamRanks The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2).
* @return All the players and their new ratings.
*/
abstract public function calculateNewRatings(
GameInfo $gameInfo,
array $teamsOfPlayerToRatings,
array $teamRanks);
array $teamRanks
);
/**
* Calculates the match quality as the likelihood of all teams drawing.
*
* @param GameInfo $gameInfo Parameters for the game.
* @param array $teamsOfPlayerToRatings A mapping of team players and their ratings.
* @param GameInfo $gameInfo Parameters for the game.
* @param array $teamsOfPlayerToRatings A mapping of team players and their ratings.
* @return float The quality of the match between the teams as a percentage (0% = bad, 100% = well matched).
*/
abstract public function calculateMatchQuality(GameInfo $gameInfo, array $teamsOfPlayerToRatings): float;
@ -49,7 +50,7 @@ abstract class SkillCalculator
}
/**
* @param array<\DNW\Skills\Team> $teams
* @param array<\DNW\Skills\Team> $teams
*
* @throws \Exception
*/
@ -69,4 +70,3 @@ abstract class SkillCalculator
}
}
}

@ -1,4 +1,4 @@
<?php
<?php
namespace DNW\Skills;

@ -26,10 +26,11 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
parent::__construct(SkillCalculatorSupportedOptions::PARTIAL_PLAY | SkillCalculatorSupportedOptions::PARTIAL_UPDATE, TeamsRange::atLeast(2), PlayersRange::atLeast(1));
}
public function calculateNewRatings(GameInfo $gameInfo,
array $teams,
array $teamRanks): RatingContainer
{
public function calculateNewRatings(
GameInfo $gameInfo,
array $teams,
array $teamRanks
): RatingContainer {
Guard::argumentNotNull($gameInfo, 'gameInfo');
$this->validateTeamCountAndPlayersCountPerTeam($teams);
@ -88,8 +89,12 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
private static function getPlayerMeansVector(array $teamAssignmentsList)
{
// A simple vector of all the player means.
return new Vector(self::getPlayerRatingValues($teamAssignmentsList,
fn ($rating) => $rating->getMean()));
return new Vector(
self::getPlayerRatingValues(
$teamAssignmentsList,
fn ($rating) => $rating->getMean()
)
);
}
private static function getPlayerCovarianceMatrix(array $teamAssignmentsList)
@ -97,8 +102,11 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
// This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all
// players.
return new DiagonalMatrix(
self::getPlayerRatingValues($teamAssignmentsList,
fn ($rating) => BasicMath::square($rating->getStandardDeviation())));
self::getPlayerRatingValues(
$teamAssignmentsList,
fn ($rating) => BasicMath::square($rating->getStandardDeviation())
)
);
}
// Helper function that gets a list of values for all player ratings

@ -26,9 +26,12 @@ abstract class GaussianFactor extends Factor
{
$newDistribution = GaussianDistribution::fromPrecisionMean(0, 0);
return parent::createVariableToMessageBindingWithMessage($variable,
return parent::createVariableToMessageBindingWithMessage(
$variable,
new Message(
$newDistribution,
sprintf('message from %s to %s', $this, $variable)));
sprintf('message from %s to %s', $this, $variable)
)
);
}
}

@ -14,22 +14,26 @@ use DNW\Skills\TrueSkill\TruncatedGaussianCorrectionFunctions;
*/
class GaussianGreaterThanFactor extends GaussianFactor
{
private $_epsilon;
private $epsilon;
public function __construct($epsilon, Variable $variable)
{
parent::__construct(\sprintf('%s > %.2f', $variable, $epsilon));
$this->_epsilon = $epsilon;
$this->epsilon = $epsilon;
$this->createVariableToMessageBinding($variable);
}
public function getLogNormalization()
{
/** @var Variable[] $vars */
/**
* @var Variable[] $vars
*/
$vars = $this->getVariables();
$marginal = $vars[0]->getValue();
/** @var Message[] $messages */
/**
* @var Message[] $messages
*/
$messages = $this->getMessages();
$message = $messages[0]->getValue();
$messageFromVariable = GaussianDistribution::divide($marginal, $message);
@ -38,7 +42,7 @@ class GaussianGreaterThanFactor extends GaussianFactor
+
log(
GaussianDistribution::cumulativeTo(
($messageFromVariable->getMean() - $this->_epsilon) /
($messageFromVariable->getMean() - $this->epsilon) /
$messageFromVariable->getStandardDeviation()
)
);
@ -57,7 +61,7 @@ class GaussianGreaterThanFactor extends GaussianFactor
$dOnSqrtC = $d / $sqrtC;
$epsilsonTimesSqrtC = $this->_epsilon * $sqrtC;
$epsilsonTimesSqrtC = $this->epsilon * $sqrtC;
$d = $messageFromVar->getPrecisionMean();
$denom = 1.0 - TruncatedGaussianCorrectionFunctions::wExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC);

@ -15,21 +15,25 @@ use Exception;
*/
class GaussianLikelihoodFactor extends GaussianFactor
{
private $_precision;
private $precision;
public function __construct($betaSquared, Variable $variable1, Variable $variable2)
{
parent::__construct(sprintf('Likelihood of %s going to %s', $variable2, $variable1));
$this->_precision = 1.0 / $betaSquared;
$this->precision = 1.0 / $betaSquared;
$this->createVariableToMessageBinding($variable1);
$this->createVariableToMessageBinding($variable2);
}
public function getLogNormalization(): float
{
/** @var KeyedVariable[]|mixed $vars */
/**
* @var KeyedVariable[]|mixed $vars
*/
$vars = $this->getVariables();
/** @var Message[] $messages */
/**
* @var Message[] $messages
*/
$messages = $this->getMessages();
return GaussianDistribution::logRatioNormalization(
@ -46,7 +50,7 @@ class GaussianLikelihoodFactor extends GaussianFactor
$marginal1 = clone $variable1->getValue();
$marginal2 = clone $variable2->getValue();
$a = $this->_precision / ($this->_precision + $marginal2->getPrecision() - $message2Value->getPrecision());
$a = $this->precision / ($this->precision + $marginal2->getPrecision() - $message2Value->getPrecision());
$newMessage = GaussianDistribution::fromPrecisionMean(
$a * ($marginal2->getPrecisionMean() - $message2Value->getPrecisionMean()),
@ -72,10 +76,16 @@ class GaussianLikelihoodFactor extends GaussianFactor
$vars = $this->getVariables();
return match ($messageIndex) {
0 => $this->updateHelper($messages[0], $messages[1],
$vars[0], $vars[1]),
1 => $this->updateHelper($messages[1], $messages[0],
$vars[1], $vars[0]),
0 => $this->updateHelper(
$messages[0],
$messages[1],
$vars[0], $vars[1]
),
1 => $this->updateHelper(
$messages[1],
$messages[0],
$vars[1], $vars[0]
),
default => throw new Exception(),
};
}

@ -19,8 +19,10 @@ class GaussianPriorFactor extends GaussianFactor
{
parent::__construct(sprintf('Prior value going to %s', $variable));
$this->_newMessage = new GaussianDistribution($mean, sqrt($variance));
$newMessage = new Message(GaussianDistribution::fromPrecisionMean(0, 0),
sprintf('message from %s to %s', $this, $variable));
$newMessage = new Message(
GaussianDistribution::fromPrecisionMean(0, 0),
sprintf('message from %s to %s', $this, $variable)
);
$this->createVariableToMessageBindingWithMessage($variable, $newMessage);
}

@ -15,13 +15,13 @@ use DNW\Skills\Numerics\GaussianDistribution;
*/
class GaussianWeightedSumFactor extends GaussianFactor
{
private array $_variableIndexOrdersForWeights = [];
private array $variableIndexOrdersForWeights = [];
// 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 array $_weights = [];
private array $weights = [];
private array $_weightsSquared = [];
private array $weightsSquared = [];
public function __construct(Variable $sumVariable, array $variablesToSum, array $variableWeights = null)
{
@ -30,20 +30,20 @@ class GaussianWeightedSumFactor extends GaussianFactor
// The first weights are a straightforward copy
// v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n
$variableWeightsLength = count((array) $variableWeights);
$this->_weights[0] = array_fill(0, count((array) $variableWeights), 0);
$this->weights[0] = array_fill(0, count((array) $variableWeights), 0);
for ($i = 0; $i < $variableWeightsLength; $i++) {
$weight = &$variableWeights[$i];
$this->_weights[0][$i] = $weight;
$this->_weightsSquared[0][$i] = BasicMath::square($weight);
$this->weights[0][$i] = $weight;
$this->weightsSquared[0][$i] = BasicMath::square($weight);
}
$variablesToSumLength = count($variablesToSum);
// 0..n-1
$this->_variableIndexOrdersForWeights[0] = [];
$this->variableIndexOrdersForWeights[0] = [];
for ($i = 0; $i < ($variablesToSumLength + 1); $i++) {
$this->_variableIndexOrdersForWeights[0][] = $i;
$this->variableIndexOrdersForWeights[0][] = $i;
}
$variableWeightsLength = count((array) $variableWeights);
@ -66,9 +66,11 @@ class GaussianWeightedSumFactor extends GaussianFactor
// This is helpful since we skip over one of the spots
$currentDestinationWeightIndex = 0;
for ($currentWeightSourceIndex = 0;
for (
$currentWeightSourceIndex = 0;
$currentWeightSourceIndex < $variableWeightsLength;
$currentWeightSourceIndex++) {
$currentWeightSourceIndex++
) {
if ($currentWeightSourceIndex === $weightsIndex - 1) {
continue;
}
@ -97,10 +99,10 @@ class GaussianWeightedSumFactor extends GaussianFactor
$currentWeights[$currentDestinationWeightIndex] = $finalWeight;
$currentWeightsSquared[$currentDestinationWeightIndex] = BasicMath::square($finalWeight);
$variableIndices[count((array) $variableWeights)] = 0;
$this->_variableIndexOrdersForWeights[] = $variableIndices;
$this->variableIndexOrdersForWeights[] = $variableIndices;
$this->_weights[$weightsIndex] = $currentWeights;
$this->_weightsSquared[$weightsIndex] = $currentWeightsSquared;
$this->weights[$weightsIndex] = $currentWeights;
$this->weightsSquared[$weightsIndex] = $currentWeightsSquared;
}
$this->createVariableToMessageBinding($sumVariable);
@ -192,7 +194,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
$updatedMessages = [];
$updatedVariables = [];
$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
// order as the weights. Thankfully, the weights and messages share the same index numbers,
@ -203,10 +205,12 @@ class GaussianWeightedSumFactor extends GaussianFactor
$updatedVariables[] = $allVariables[$indicesToUse[$i]];
}
return $this->updateHelper($this->_weights[$messageIndex],
$this->_weightsSquared[$messageIndex],
return $this->updateHelper(
$this->weights[$messageIndex],
$this->weightsSquared[$messageIndex],
$updatedMessages,
$updatedVariables);
$updatedVariables
);
}
private static function createName($sumVariable, $variablesToSum, $weights)

@ -14,30 +14,34 @@ use DNW\Skills\TrueSkill\TruncatedGaussianCorrectionFunctions;
*/
class GaussianWithinFactor extends GaussianFactor
{
private $_epsilon;
private $epsilon;
public function __construct($epsilon, Variable $variable)
{
parent::__construct(sprintf('%s <= %.2f', $variable, $epsilon));
$this->_epsilon = $epsilon;
$this->epsilon = $epsilon;
$this->createVariableToMessageBinding($variable);
}
public function getLogNormalization()
{
/** @var Variable[] $variables */
/**
* @var Variable[] $variables
*/
$variables = $this->getVariables();
$marginal = $variables[0]->getValue();
/** @var Message[] $messages */
/**
* @var Message[] $messages
*/
$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)
$z = GaussianDistribution::cumulativeTo(($this->epsilon - $mean) / $std)
-
GaussianDistribution::cumulativeTo((-$this->_epsilon - $mean) / $std);
GaussianDistribution::cumulativeTo((-$this->epsilon - $mean) / $std);
return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z);
}
@ -54,7 +58,7 @@ class GaussianWithinFactor extends GaussianFactor
$sqrtC = sqrt($c);
$dOnSqrtC = $d / $sqrtC;
$epsilonTimesSqrtC = $this->_epsilon * $sqrtC;
$epsilonTimesSqrtC = $this->epsilon * $sqrtC;
$d = $messageFromVariable->getPrecisionMean();
$denominator = 1.0 - TruncatedGaussianCorrectionFunctions::wWithinMargin($dOnSqrtC, $epsilonTimesSqrtC);

@ -13,28 +13,29 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
{
public function __construct(
TrueSkillFactorGraph $parentGraph,
private readonly TeamPerformancesToTeamPerformanceDifferencesLayer $_TeamPerformancesToTeamPerformanceDifferencesLayer,
private readonly TeamDifferencesComparisonLayer $_TeamDifferencesComparisonLayer
private readonly TeamPerformancesToTeamPerformanceDifferencesLayer $TeamPerformancesToTeamPerformanceDifferencesLayer,
private readonly TeamDifferencesComparisonLayer $TeamDifferencesComparisonLayer
) {
parent::__construct($parentGraph);
}
public function getLocalFactors(): array
{
return array_merge($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(),
$this->_TeamDifferencesComparisonLayer->getLocalFactors()
return array_merge(
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(),
$this->TeamDifferencesComparisonLayer->getLocalFactors()
);
}
public function buildLayer()
{
$inputVariablesGroups = $this->getInputVariablesGroups();
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups);
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer();
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups);
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer();
$teamDifferencesOutputVariablesGroups = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups();
$this->_TeamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups);
$this->_TeamDifferencesComparisonLayer->buildLayer();
$teamDifferencesOutputVariablesGroups = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups();
$this->TeamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups);
$this->TeamDifferencesComparisonLayer->buildLayer();
}
public function createPriorSchedule(): ScheduleSequence
@ -52,9 +53,9 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
}
// When dealing with differences, there are always (n-1) differences, so add in the 1
$totalTeamDifferences = is_countable($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) ? count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) : 0;
$totalTeamDifferences = is_countable($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) ? count($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) : 0;
$localFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$localFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$firstDifferencesFactor = $localFactors[0];
$lastDifferencesFactor = $localFactors[$totalTeamDifferences - 1];
@ -65,18 +66,22 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$loop,
new ScheduleStep(
'teamPerformanceToPerformanceDifferenceFactors[0] @ 1',
$firstDifferencesFactor, 1),
$firstDifferencesFactor,
1
),
new ScheduleStep(
sprintf('teamPerformanceToPerformanceDifferenceFactors[teamTeamDifferences = %d - 1] @ 2', $totalTeamDifferences),
$lastDifferencesFactor, 2),
$lastDifferencesFactor,
2
),
]
);
}
private function createTwoTeamInnerPriorLoopSchedule()
{
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors();
$firstPerfToTeamDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[0];
$firstTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[0];
@ -84,27 +89,30 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
new ScheduleStep(
'send team perf to perf differences',
$firstPerfToTeamDiff,
0),
0
),
new ScheduleStep(
'send to greater than or within factor',
$firstTeamDiffComparison,
0),
0
),
];
return $this->scheduleSequence(
$itemsToSequence,
'loop of just two teams inner sequence');
'loop of just two teams inner sequence'
);
}
private function createMultipleTeamInnerPriorLoopSchedule()
{
$totalTeamDifferences = is_countable($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) ? count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) : 0;
$totalTeamDifferences = is_countable($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) ? count($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()) : 0;
$forwardScheduleList = [];
for ($i = 0; $i < $totalTeamDifferences - 1; $i++) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors();
$currentTeamPerfToTeamPerfDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$i];
$currentTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[$i];
@ -114,14 +122,22 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
[
new ScheduleStep(
sprintf('team perf to perf diff %d', $i),
$currentTeamPerfToTeamPerfDiff, 0),
$currentTeamPerfToTeamPerfDiff,
0
),
new ScheduleStep(
sprintf('greater than or within result factor %d', $i),
$currentTeamDiffComparison, 0),
$currentTeamDiffComparison,
0
),
new ScheduleStep(
sprintf('team perf to perf diff factors [%d], 2', $i),
$currentTeamPerfToTeamPerfDiff, 2),
], sprintf('current forward schedule piece %d', $i));
$currentTeamPerfToTeamPerfDiff,
2
),
],
sprintf('current forward schedule piece %d', $i)
);
$forwardScheduleList[] = $currentForwardSchedulePiece;
}
@ -131,8 +147,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$backwardScheduleList = [];
for ($i = 0; $i < $totalTeamDifferences - 1; $i++) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->_TeamDifferencesComparisonLayer->getLocalFactors();
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors();
$differencesFactor = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i];
$comparisonFactor = $teamDifferencesComparisonLayerLocalFactors[$totalTeamDifferences - 1 - $i];
@ -143,17 +159,21 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
[
new ScheduleStep(
sprintf('teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 0', $i),
$differencesFactor, 0
$differencesFactor,
0
),
new ScheduleStep(
sprintf('greaterThanOrWithinResultFactors[totalTeamDifferences - 1 - %d] @ 0', $i),
$comparisonFactor, 0
$comparisonFactor,
0
),
new ScheduleStep(
sprintf('teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 1', $i),
$performancesToDifferencesFactor, 1
$performancesToDifferencesFactor,
1
),
]);
]
);
$backwardScheduleList[] = $currentBackwardSchedulePiece;
}

@ -32,11 +32,13 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
return $this->scheduleSequence(
array_map(
fn ($weightedSumFactor) => new ScheduleStep('Perf to Team Perf Step', $weightedSumFactor, 0),
$localFactors),
'all player perf to team perf schedule');
$localFactors
),
'all player perf to team perf schedule'
);
}
protected function createPlayerToTeamSumFactor($teamMembers, $sumVariable): GaussianWeightedSumFactor
protected function createPlayerToTeamSumFactor($teamMembers, $sumVariable): GaussianWeightedSumFactor
{
$weights = array_map(
function ($v) {
@ -44,12 +46,14 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
return PartialPlay::getPartialPlayPercentage($player);
},
$teamMembers);
$teamMembers
);
return new GaussianWeightedSumFactor(
$sumVariable,
$teamMembers,
$weights);
$weights
);
}
public function createPosteriorSchedule(): ScheduleSequence
@ -60,8 +64,11 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
$localCurrentFactor = $currentFactor;
$numberOfMessages = $localCurrentFactor->getNumberOfMessages();
for ($currentIteration = 1; $currentIteration < $numberOfMessages; $currentIteration++) {
$allFactors[] = new ScheduleStep('team sum perf @'.$currentIteration,
$localCurrentFactor, $currentIteration);
$allFactors[] = new ScheduleStep(
'team sum perf @' . $currentIteration,
$localCurrentFactor,
$currentIteration
);
}
}
@ -74,6 +81,6 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
$teamMemberNames = \implode(', ', $memberNames);
return $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable('Team['.$teamMemberNames."]'s performance");
return $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable('Team[' . $teamMemberNames . "]'s performance");
}
}

@ -47,8 +47,10 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
return $this->scheduleSequence(
array_map(
fn ($prior) => new ScheduleStep('Prior to Skill Step', $prior, 0),
$localFactors),
'All priors');
$localFactors
),
'All priors'
);
}
private function createPriorFactor(Rating $priorRating, Variable $skillsVariable)
@ -66,6 +68,6 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
$parentFactorGraph = $this->getParentFactorGraph();
$variableFactory = $parentFactorGraph->getVariableFactory();
return $variableFactory->createKeyedVariable($key, $key."'s skill");
return $variableFactory->createKeyedVariable($key, $key . "'s skill");
}
}

@ -9,13 +9,13 @@ use DNW\Skills\TrueSkill\TrueSkillFactorGraph;
class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
{
private $_epsilon;
private $epsilon;
public function __construct(TrueSkillFactorGraph $parentGraph, private readonly array $_teamRanks)
public function __construct(TrueSkillFactorGraph $parentGraph, private readonly array $teamRanks)
{
parent::__construct($parentGraph);
$gameInfo = $this->getParentFactorGraph()->getGameInfo();
$this->_epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
$this->epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
}
public function buildLayer()
@ -24,13 +24,13 @@ class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
$inputVarGroupsCount = is_countable($inputVarGroups) ? count($inputVarGroups) : 0;
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];
$factor =
$isDraw
? new GaussianWithinFactor($this->_epsilon, $teamDifference)
: new GaussianGreaterThanFactor($this->_epsilon, $teamDifference);
? new GaussianWithinFactor($this->epsilon, $teamDifference)
: new GaussianGreaterThanFactor($this->epsilon, $teamDifference);
$this->addLayerFactor($factor);
}

@ -26,10 +26,11 @@ class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorG
}
}
private function createTeamPerformanceToDifferenceFactor(Variable $strongerTeam,
Variable $weakerTeam,
Variable $output)
{
private function createTeamPerformanceToDifferenceFactor(
Variable $strongerTeam,
Variable $weakerTeam,
Variable $output
) {
$teams = [$strongerTeam, $weakerTeam];
$weights = [1.0, -1.0];

@ -19,38 +19,40 @@ use DNW\Skills\TrueSkill\Layers\TeamPerformancesToTeamPerformanceDifferencesLaye
class TrueSkillFactorGraph extends FactorGraph
{
private $_layers;
private $layers;
private $_priorLayer;
private $priorLayer;
public function __construct(private readonly GameInfo $_gameInfo, array $teams, array $teamRanks)
public function __construct(private readonly GameInfo $gameInfo, array $teams, array $teamRanks)
{
$this->_priorLayer = new PlayerPriorValuesToSkillsLayer($this, $teams);
$this->priorLayer = new PlayerPriorValuesToSkillsLayer($this, $teams);
$newFactory = new VariableFactory(
fn () => GaussianDistribution::fromPrecisionMean(0, 0));
fn () => GaussianDistribution::fromPrecisionMean(0, 0)
);
$this->setVariableFactory($newFactory);
$this->_layers = [
$this->_priorLayer,
$this->layers = [
$this->priorLayer,
new PlayerSkillsToPerformancesLayer($this),
new PlayerPerformancesToTeamPerformancesLayer($this),
new IteratedTeamDifferencesInnerLayer(
$this,
new TeamPerformancesToTeamPerformanceDifferencesLayer($this),
new TeamDifferencesComparisonLayer($this, $teamRanks)),
new TeamDifferencesComparisonLayer($this, $teamRanks)
),
];
}
public function getGameInfo()
{
return $this->_gameInfo;
return $this->gameInfo;
}
public function buildGraph(): void
{
$lastOutput = null;
$layers = $this->_layers;
$layers = $this->layers;
foreach ($layers as $currentLayer) {
if ($lastOutput != null) {
$currentLayer->setInputVariablesGroups($lastOutput);
@ -72,7 +74,7 @@ class TrueSkillFactorGraph extends FactorGraph
{
$factorList = new FactorList();
$layers = $this->_layers;
$layers = $this->layers;
foreach ($layers as $currentLayer) {
$localFactors = $currentLayer->getLocalFactors();
foreach ($localFactors as $currentFactor) {
@ -90,7 +92,7 @@ class TrueSkillFactorGraph extends FactorGraph
{
$fullSchedule = [];
$layers = $this->_layers;
$layers = $this->layers;
foreach ($layers as $currentLayer) {
$currentPriorSchedule = $currentLayer->createPriorSchedule();
if ($currentPriorSchedule != null) {
@ -98,7 +100,7 @@ class TrueSkillFactorGraph extends FactorGraph
}
}
$allLayersReverse = array_reverse($this->_layers);
$allLayersReverse = array_reverse($this->layers);
foreach ($allLayersReverse as $currentLayer) {
$currentPosteriorSchedule = $currentLayer->createPosteriorSchedule();
@ -114,12 +116,14 @@ class TrueSkillFactorGraph extends FactorGraph
{
$result = new RatingContainer();
$priorLayerOutputVariablesGroups = $this->_priorLayer->getOutputVariablesGroups();
$priorLayerOutputVariablesGroups = $this->priorLayer->getOutputVariablesGroups();
foreach ($priorLayerOutputVariablesGroups as $currentTeam) {
foreach ($currentTeam as $currentPlayer) {
$localCurrentPlayer = $currentPlayer->getKey();
$newRating = new Rating($currentPlayer->getValue()->getMean(),
$currentPlayer->getValue()->getStandardDeviation());
$newRating = new Rating(
$currentPlayer->getValue()->getMean(),
$currentPlayer->getValue()->getStandardDeviation()
);
$result->setRating($localCurrentPlayer, $newRating);
}

@ -15,7 +15,8 @@ class TruncatedGaussianCorrectionFunctions
* correction of a single-sided truncated Gaussian with unit variance."
*
* @param $teamPerformanceDifference
* @param $drawMargin In the paper, it's referred to as just "ε".
* @param $drawMargin In the paper, it's referred to as just
* "ε".
* @param $c
*/
public static function vExceedsMarginScaled(float $teamPerformanceDifference, float $drawMargin, float $c): float
@ -123,7 +124,8 @@ class TruncatedGaussianCorrectionFunctions
($drawMargin - $teamPerformanceDifferenceAbsoluteValue)
*
GaussianDistribution::at(
$drawMargin - $teamPerformanceDifferenceAbsoluteValue)
$drawMargin - $teamPerformanceDifferenceAbsoluteValue
)
- (-$drawMargin - $teamPerformanceDifferenceAbsoluteValue)
*
GaussianDistribution::at(-$drawMargin - $teamPerformanceDifferenceAbsoluteValue)) / $denominator;

@ -27,10 +27,11 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
parent::__construct(SkillCalculatorSupportedOptions::NONE, TeamsRange::exactly(2), PlayersRange::exactly(1));
}
public function calculateNewRatings(GameInfo $gameInfo,
array $teams,
array $teamRanks): RatingContainer
{
public function calculateNewRatings(
GameInfo $gameInfo,
array $teams,
array $teamRanks
): RatingContainer {
// Basic argument checking
Guard::argumentNotNull($gameInfo, 'gameInfo');
$this->validateTeamCountAndPlayersCountPerTeam($teams);
@ -51,17 +52,27 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
$results = new RatingContainer();
$results->setRating($winner, self::calculateNewRating($gameInfo,
$winnerPreviousRating,
$loserPreviousRating,
$wasDraw ? PairwiseComparison::DRAW
: PairwiseComparison::WIN));
$results->setRating(
$winner,
self::calculateNewRating(
$gameInfo,
$winnerPreviousRating,
$loserPreviousRating,
$wasDraw ? PairwiseComparison::DRAW
: PairwiseComparison::WIN
)
);
$results->setRating($loser, self::calculateNewRating($gameInfo,
$loserPreviousRating,
$winnerPreviousRating,
$wasDraw ? PairwiseComparison::DRAW
: PairwiseComparison::LOSE));
$results->setRating(
$loser,
self::calculateNewRating(
$gameInfo,
$loserPreviousRating,
$winnerPreviousRating,
$wasDraw ? PairwiseComparison::DRAW
: PairwiseComparison::LOSE
)
);
// And we're done!
return $results;

@ -60,12 +60,13 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
return $results;
}
private static function updatePlayerRatings(GameInfo $gameInfo,
RatingContainer $newPlayerRatings,
Team $selfTeam,
Team $otherTeam,
PairwiseComparison $selfToOtherTeamComparison): void
{
private static function updatePlayerRatings(
GameInfo $gameInfo,
RatingContainer $newPlayerRatings,
Team $selfTeam,
Team $otherTeam,
PairwiseComparison $selfToOtherTeamComparison
): void {
$drawMargin = DrawMargin::getDrawMarginFromDrawProbability(
$gameInfo->getDrawProbability(),
$gameInfo->getBeta()