mirror of
https://github.com/furyfire/trueskill.git
synced 2025-03-20 16:48:04 +00:00
Start of factor graph port. Things don't work yet, but a lot of syntax updates towards PHP
This commit is contained in:
147
PHPSkills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.php
Normal file
147
PHPSkills/TrueSkill/Layers/IteratedTeamDifferencesInnerLayer.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
// The whole purpose of this is to do a loop on the bottom
|
||||
class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
private $_TeamDifferencesComparisonLayer;
|
||||
private $_TeamPerformancesToTeamPerformanceDifferencesLayer;
|
||||
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph,
|
||||
TeamPerformancesToTeamPerformanceDifferencesLayer $teamPerformancesToPerformanceDifferences,
|
||||
TeamDifferencesComparisonLayer $teamDifferencesComparisonLayer)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer = $teamPerformancesToPerformanceDifferences;
|
||||
$this->_TeamDifferencesComparisonLayer = $teamDifferencesComparisonLayer;
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->setRawInputVariablesGroups($this->getInputVariablesGroups());
|
||||
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer();
|
||||
|
||||
$this->_TeamDifferencesComparisonLayer->setRawInputVariablesGroups(
|
||||
$this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getRawOutputVariablesGroups());
|
||||
$this->_TeamDifferencesComparisonLayer->buildLayer();
|
||||
}
|
||||
|
||||
public function createPriorSchedule()
|
||||
{
|
||||
// BLOG about $loop
|
||||
switch (count($this->getInputVariablesGroups()))
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
throw new InvalidOperationException();
|
||||
case 2:
|
||||
$loop = $this->createTwoTeamInnerPriorLoopSchedule();
|
||||
break;
|
||||
default:
|
||||
$loop = $this->createMultipleTeamInnerPriorLoopSchedule();
|
||||
break;
|
||||
}
|
||||
|
||||
// When dealing with differences, there are always (n-1) differences, so add in the 1
|
||||
$totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors());
|
||||
$totalTeams = $totalTeamDifferences + 1;
|
||||
|
||||
$innerSchedule = new ScheduleSequence(
|
||||
"inner schedule",
|
||||
array(
|
||||
$loop,
|
||||
new ScheduleStep(
|
||||
"teamPerformanceToPerformanceDifferenceFactors[0] @ 1",
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[0], 1),
|
||||
new ScheduleStep(
|
||||
"teamPerformanceToPerformanceDifferenceFactors[teamTeamDifferences = {0} - 1] @ 2",
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1], 2)
|
||||
)
|
||||
);
|
||||
|
||||
return innerSchedule;
|
||||
}
|
||||
|
||||
private function createTwoTeamInnerPriorLoopSchedule()
|
||||
{
|
||||
return $this->scheduleSequence(
|
||||
array(
|
||||
new ScheduleStep(
|
||||
"send team perf to perf differences",
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[0],
|
||||
0),
|
||||
new ScheduleStep(
|
||||
"send to greater than or within factor",
|
||||
($this->_TeamDifferencesComparisonLayer->getLocalFactors())[0],
|
||||
0)
|
||||
),
|
||||
"loop of just two teams inner sequence");
|
||||
}
|
||||
|
||||
private function createMultipleTeamInnerPriorLoopSchedule()
|
||||
{
|
||||
$totalTeamDifferences = count($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors());
|
||||
|
||||
$forwardScheduleList = array();
|
||||
|
||||
for ($i = 0; $i < $totalTeamDifferences - 1; $i++)
|
||||
{
|
||||
$currentForwardSchedulePiece =
|
||||
$this->scheduleSequence(
|
||||
array(
|
||||
new ScheduleStep(
|
||||
sprintf("team perf to perf diff %d", $i),
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$i], 0),
|
||||
new ScheduleStep(
|
||||
sprintf("greater than or within result factor %d", $i),
|
||||
($this->_TeamDifferencesComparisonLayer->getLocalFactors())[$i], 0),
|
||||
new ScheduleStep(
|
||||
sprintf("team perf to perf diff factors [%d], 2", $i),
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$i], 2)
|
||||
), sprintf("current forward schedule piece %d", $i);
|
||||
|
||||
$forwardScheduleList[] = $currentForwardSchedulePiece;
|
||||
}
|
||||
|
||||
$forwardSchedule = new ScheduleSequence("forward schedule", $forwardScheduleList);
|
||||
|
||||
$backwardScheduleList = array();
|
||||
|
||||
for ($i = 0; $i < $totalTeamDifferences - 1; $i++)
|
||||
{
|
||||
$currentBackwardSchedulePiece = new ScheduleSequence(
|
||||
"current backward schedule piece",
|
||||
array(
|
||||
new ScheduleStep(
|
||||
sprintf("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 0", $i),
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 0),
|
||||
new ScheduleStep(
|
||||
sprintf("greaterThanOrWithinResultFactors[totalTeamDifferences - 1 - %d] @ 0", $i),
|
||||
($this->_TeamDifferencesComparisonLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 0),
|
||||
new ScheduleStep(
|
||||
sprintf("teamPerformanceToPerformanceDifferenceFactors[totalTeamDifferences - 1 - %d] @ 1", $i),
|
||||
($this->_TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors())[$totalTeamDifferences - 1 - $i], 1)
|
||||
);
|
||||
$backwardScheduleList[] = $currentBackwardSchedulePiece;
|
||||
}
|
||||
|
||||
$backwardSchedule = new ScheduleSequence("backward schedule", $backwardScheduleList);
|
||||
|
||||
$forwardBackwardScheduleToLoop =
|
||||
new ScheduleSequence(
|
||||
"forward Backward Schedule To Loop",
|
||||
array($forwardSchedule, $backwardSchedule));
|
||||
|
||||
$initialMaxDelta = 0.0001;
|
||||
|
||||
$loop = new ScheduleLoop(
|
||||
sprintf("loop with max delta of %f", $initialMaxDelta),
|
||||
$forwardBackwardScheduleToLoop,
|
||||
$initialMaxDelta);
|
||||
|
||||
return $loop;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
foreach ($this->getInputVariablesGroups() as $currentTeam)
|
||||
{
|
||||
$teamPerformance = $this->createOutputVariable($currentTeam);
|
||||
$this->addLayerFactor($this->createPlayerToTeamSumFactor($currentTeam, $teamPerformance));
|
||||
|
||||
// REVIEW: Does it make sense to have groups of one?
|
||||
$this->getOutputVariablesGroups() = $teamPerformance;
|
||||
}
|
||||
}
|
||||
|
||||
public function createPriorSchedule()
|
||||
{
|
||||
return $this->scheduleSequence(
|
||||
array_map(
|
||||
function($weightedSumFactor)
|
||||
{
|
||||
return new ScheduleStep("Perf to Team Perf Step", $weightedSumFactor, 0);
|
||||
},
|
||||
$this->getLocalFactors()),
|
||||
"all player perf to team perf schedule");
|
||||
}
|
||||
|
||||
protected function createPlayerToTeamSumFactor($teamMembers, $sumVariable)
|
||||
{
|
||||
return new GaussianWeightedSumFactor(
|
||||
$sumVariable,
|
||||
$teamMembers,
|
||||
array_map(
|
||||
function($v)
|
||||
{
|
||||
return PartialPlay::getPartialPlayPercentage($v->getKey());
|
||||
},
|
||||
$teamMembers));
|
||||
|
||||
}
|
||||
|
||||
public function createPosteriorSchedule()
|
||||
{
|
||||
// BLOG
|
||||
return $this->scheduleSequence(
|
||||
from currentFactor in LocalFactors
|
||||
from currentIteration in
|
||||
Enumerable.Range(1, currentFactor.NumberOfMessages - 1)
|
||||
select new ScheduleStep<GaussianDistribution>(
|
||||
"team sum perf @" + currentIteration,
|
||||
currentFactor,
|
||||
currentIteration),
|
||||
"all of the team's sum iterations");
|
||||
}
|
||||
|
||||
private function createOutputVariable($team)
|
||||
{
|
||||
$teamMemberNames = String.Join(", ", team.Select(teamMember => teamMember.Key.ToString()).ToArray());
|
||||
return ParentFactorGraph.VariableFactory.CreateBasicVariable("Team[{0}]'s performance", teamMemberNames);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
// We intentionally have no Posterior schedule since the only purpose here is to
|
||||
class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
private $_teams;
|
||||
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph, $teams)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
$this->_teams = $teams;
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
foreach ($this->_teams as $currentTeam)
|
||||
{
|
||||
$currentTeamSkills = array();
|
||||
|
||||
foreach ($currentTeam as $currentTeamPlayer)
|
||||
{
|
||||
$playerSkill = $this->createSkillOutputVariable($currentTeamPlayer.Key);
|
||||
$this->addLayerFactor($this->createPriorFactor($currentTeamPlayer.Key, $currentTeamPlayer.Value, $playerSkill));
|
||||
$currentTeamSkills[] = $playerSkill;
|
||||
}
|
||||
|
||||
OutputVariablesGroups.Add(currentTeamSkills);
|
||||
}
|
||||
}
|
||||
|
||||
public function createPriorSchedule()
|
||||
{
|
||||
return $this->scheduleSequence(
|
||||
array_map(
|
||||
function($prior)
|
||||
{
|
||||
return new ScheduleStep("Prior to Skill Step", $prior, 0);
|
||||
},
|
||||
$this->getLocalFactors()),
|
||||
"All priors");
|
||||
}
|
||||
|
||||
private function createPriorFactor($player, $priorRating, $skillsVariable)
|
||||
{
|
||||
return new GaussianPriorFactor($priorRating->getMean(),
|
||||
square($priorRating->getStandardDeviation()) +
|
||||
square($this->getParentFactorGraph()->getGameInfo()->getDynamicsFactor()),
|
||||
$skillsVariable);
|
||||
}
|
||||
|
||||
private function createSkillOutputVariable($key)
|
||||
{
|
||||
return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, "{0}'s skill", $key);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
foreach ($this->getInputVariablesGroups() as $currentTeam)
|
||||
{
|
||||
$currentTeamPlayerPerformances = array();
|
||||
|
||||
foreach ($currentTeam as $playerSkillVariable)
|
||||
{
|
||||
$playerPerformance = $this->createOutputVariable($playerSkillVariable->getKey());
|
||||
$this->addLayerFactor($this->createLikelihood($playerSkillVariable, $playerPerformance));
|
||||
$currentTeamPlayerPerformances[] = $playerPerformance;
|
||||
}
|
||||
|
||||
$this->getOutputVariablesGroups()[] = $currentTeamPlayerPerformances;
|
||||
}
|
||||
}
|
||||
|
||||
private function createLikelihood($playerSkill, $playerPerformance)
|
||||
{
|
||||
return new GaussianLikelihoodFactor(square($this->getParentFactorGraph()->getGameInfo()->getBeta()), $playerPerformance, $playerSkill);
|
||||
}
|
||||
|
||||
private function createOutputVariable($key)
|
||||
{
|
||||
return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key, "{0}'s performance", $key);
|
||||
}
|
||||
|
||||
public function createPriorSchedule()
|
||||
{
|
||||
return $this->scheduleSequence(
|
||||
array_map(
|
||||
function($likelihood)
|
||||
{
|
||||
return $this->scheduleStep("Skill to Perf step", $likelihood, 0);
|
||||
},
|
||||
$this->getLocalFactors()),
|
||||
"All skill to performance sending");
|
||||
}
|
||||
|
||||
public function createPosteriorSchedule()
|
||||
{
|
||||
return $this->scheduleSequence(
|
||||
array_map(
|
||||
function($likelihood)
|
||||
{
|
||||
return new ScheduleStep("name", $likelihood, 1);
|
||||
},
|
||||
$this->getLocalFactors()),
|
||||
"All skill to performance sending");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
private $_epsilon;
|
||||
private $_teamRanks;
|
||||
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph, array $teamRanks)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
$this->_teamRanks = $teamRanks;
|
||||
$gameInfo = $this->getParentFactorGraph()->getGameInfo();
|
||||
$this->_epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
$inputVarGroups = $this->getInputVariablesGroups();
|
||||
$inputVarGroupsCount = count($inputVarGroups);
|
||||
|
||||
for ($i = 0; $i < $inputVarGroupsCount; $i++)
|
||||
{
|
||||
$isDraw = ($this->_teamRanks[$i] == $this->_teamRanks[$i + 1]);
|
||||
$teamDifference = $inputVarGroups[$i][0];
|
||||
|
||||
$factor =
|
||||
$isDraw
|
||||
? new GaussianWithinFactor($this->_epsilon, $teamDifference)
|
||||
: new GaussianGreaterThanFactor($this->_epsilon, $teamDifference);
|
||||
|
||||
$this->addLayerFactor($factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer
|
||||
{
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
}
|
||||
|
||||
public function buildLayer()
|
||||
{
|
||||
$inputVariablesGroup = $this->getInputVariablesGroups();
|
||||
$inputVariablesGroupCount = count($inputVariablesGroup);
|
||||
|
||||
for ($i = 0; $i < $inputVariablesGroupCount - 1; $i++)
|
||||
{
|
||||
$strongerTeam = $inputVariablesGroups[$i][0];
|
||||
$weakerTeam = $inputVariablesGroups[$i + 1][0];
|
||||
|
||||
$currentDifference = $this->createOutputVariable();
|
||||
$this->addLayerFactor($this->createTeamPerformanceToDifferenceFactor($strongerTeam, $weakerTeam, currentDifference));
|
||||
|
||||
// REVIEW: Does it make sense to have groups of one?
|
||||
$this->getOutputVariablesGroups()[] = $currentDifference;
|
||||
}
|
||||
}
|
||||
|
||||
private function createTeamPerformanceToDifferenceFactor(
|
||||
Variable $strongerTeam, Variable $weakerTeam, Variable $output)
|
||||
{
|
||||
return new GaussianWeightedSumFactor($output, array($strongerTeam, $weakerTeam), array(1.0, -1.0));
|
||||
}
|
||||
|
||||
private function createOutputVariable()
|
||||
{
|
||||
return $this->getParentFactorGraph()->getVariableFactory()->createBasicVariable("Team performance difference");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
13
PHPSkills/TrueSkill/Layers/TrueSkillFactorGraphLayer.php
Normal file
13
PHPSkills/TrueSkill/Layers/TrueSkillFactorGraphLayer.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Moserware\Skills\TrueSkill\Layers;
|
||||
|
||||
abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer
|
||||
{
|
||||
public function __construct(TrueSkillFactorGraph $parentGraph)
|
||||
{
|
||||
parent::__construct($parentGraph);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Reference in New Issue
Block a user