Compare commits

...

5 Commits

Author SHA1 Message Date
c18ccd38e2 Bunch of generic code standard items. Will need a cleanup.
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-02-02 15:16:11 +00:00
3dddfc05db More code standards 2024-02-02 14:53:38 +00:00
968c78d989 More code standards 2024-02-02 14:09:07 +00:00
769514b38e Porting more tools to Phar to lower depend problems 2024-02-02 13:42:48 +00:00
36dea4ea03 Quality improvements 2024-02-02 11:04:31 +00:00
84 changed files with 605 additions and 177 deletions

2
.gitignore vendored

@ -1,6 +1,6 @@
.vscode
vendor
.phpunit.cache
.*.cache/
*.phar
.phpdoc/
output/

53
.phpcs.xml Normal file

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PHP_CodeSniffer" xsi:noNamespaceSchemaLocation="phpcs.xsd">
<description>Coding standard</description>
<file>src/</file>
<file>tests/</file>
<file>benchmark/</file>
<rule ref="PSR1">
<exclude name="Generic.Files.LineLength"/>
</rule>
<rule ref="PSR2"></rule>
<rule ref="PSR12"></rule>
<rule ref="Generic">
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
<exclude name="Generic.Files.LowercasedFilename.NotFound"/>
<exclude name="Generic.PHP.ClosingPHPTag.NotFound"/>
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine"/>
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine"/>
<exclude name="Generic.PHP.LowerCaseConstant.Found"/>
<exclude name="Generic.Formatting.SpaceAfterCast"/>
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning"/>
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
<exclude name="Generic.NamingConventions.AbstractClassNamePrefix.Missing"/>
<exclude name="Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed"/>
<exclude name="Generic.NamingConventions.InterfaceNameSuffix.Missing"/>
<exclude name="Generic.Commenting.Todo.TaskFound"/>
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUse"/>
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed"/>
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed"/>
<exclude name="Generic.Formatting.SpaceBeforeCast.NoSpace"/>
<exclude name="Generic.CodeAnalysis.UselessOverridingMethod.Found"/>
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
</rule>
<!-- Ban some functions -->
<rule ref="Generic.PHP.ForbiddenFunctions">
<properties>
<property name="forbiddenFunctions" type="array">
<element key="sizeof" value="count"/>
<element key="delete" value="unset"/>
<element key="print" value="echo"/>
<element key="is_null" value="null"/>
<element key="create_function" value="null"/>
</property>
</properties>
</rule>
</ruleset>

13
.phplint.yml Normal file

@ -0,0 +1,13 @@
path:
- src/
- tests/
- benchmark/
jobs: 10
extensions:
- php
exclude:
- vendor
warning: true
memory-limit: -1
no-cache: true
log-junit: "output/lint.xml"

@ -12,12 +12,4 @@ pipeline:
image: composer
commands:
- composer install
- composer analyze
test:
image: php:cli-bookworm
commands:
- vendor/bin/phpunit tests
# document:
# image: phpdoc/phpdoc
# commands:
# - phpdoc

144
benchmark/BasicBench.php Normal file

@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Benchmark;
use DNW\Skills\TrueSkill\TwoPlayerTrueSkillCalculator;
use DNW\Skills\TrueSkill\TwoTeamTrueSkillCalculator;
use DNW\Skills\TrueSkill\FactorGraphTrueSkillCalculator;
use DNW\Skills\GameInfo;
use DNW\Skills\Player;
use DNW\Skills\Team;
use DNW\Skills\Teams;
class BasicBench
{
/**
* To benchmark performance when using TwoPlayerTrueSkillCalculator
*
* @Revs(20)
* @Iterations(20)
*/
public function benchBasic2PlayersUsingTwoPlayerTrueSkillCalculator(): void
{
$gameInfo = new GameInfo();
$p1 = new Player("Winner");
$p2 = new Player("Average");
$team1 = new Team($p1, $gameInfo->getDefaultRating());
$team2 = new Team($p2, $gameInfo->getDefaultRating());
for ($i = 0; $i < 10; $i++) {
$teams = Teams::concat($team1, $team2);
$calculator = new TwoPlayerTrueSkillCalculator();
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, [1, 2]);
$team1 = new Team($p1, $newRatings->getRating($p1));
$team2 = new Team($p2, $newRatings->getRating($p2));
$newRatings->getRating($p1)->getConservativeRating();
$newRatings->getRating($p2)->getConservativeRating();
}
}
/**
* To benchmark performance when using TwoTeamTrueSkillCalculator for just two players in two teams
*
* @Revs(20)
* @Iterations(20)
*/
public function benchBasic2PlayersUsingTwoTeamTrueSkillCalculator(): void
{
$gameInfo = new GameInfo();
$p1 = new Player("Winner");
$p2 = new Player("Average");
$team1 = new Team($p1, $gameInfo->getDefaultRating());
$team2 = new Team($p2, $gameInfo->getDefaultRating());
for ($i = 0; $i < 10; $i++) {
$teams = Teams::concat($team1, $team2);
$calculator = new TwoTeamTrueSkillCalculator();
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, [1, 2]);
$team1 = new Team($p1, $newRatings->getRating($p1));
$team2 = new Team($p2, $newRatings->getRating($p2));
$newRatings->getRating($p1)->getConservativeRating();
$newRatings->getRating($p2)->getConservativeRating();
}
}
/**
* To benchmark performance when using FactorGraphTrueSkillCalculator for just two players in two teams
*
* @Revs(20)
* @Iterations(20)
*/
public function benchBasic2PlayersUsingFactorGraphTrueSkillCalculator(): void
{
$gameInfo = new GameInfo();
$p1 = new Player("Winner");
$p2 = new Player("Average");
$team1 = new Team($p1, $gameInfo->getDefaultRating());
$team2 = new Team($p2, $gameInfo->getDefaultRating());
for ($i = 0; $i < 10; $i++) {
$teams = Teams::concat($team1, $team2);
$calculator = new FactorGraphTrueSkillCalculator();
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, [1, 2]);
$team1 = new Team($p1, $newRatings->getRating($p1));
$team2 = new Team($p2, $newRatings->getRating($p2));
$newRatings->getRating($p1)->getConservativeRating();
$newRatings->getRating($p2)->getConservativeRating();
}
}
/**
* To benchmark performance when using FactorGraphTrueSkillCalculator with 3 players in 3 teams
*
* @Revs(20)
* @Iterations(20)
*/
public function bench3Teams(): void
{
$gameInfo = new GameInfo();
$p1 = new Player("Winner");
$p2 = new Player("Average");
$p3 = new Player("Looser");
$team1 = new Team($p1, $gameInfo->getDefaultRating());
$team2 = new Team($p2, $gameInfo->getDefaultRating());
$team3 = new Team($p3, $gameInfo->getDefaultRating());
for ($i = 0; $i < 10; $i++) {
$teams = Teams::concat($team1, $team2, $team3);
$calculator = new FactorGraphTrueSkillCalculator();
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, [1, 2, 3]);
$team1 = new Team($p1, $newRatings->getRating($p1));
$team2 = new Team($p2, $newRatings->getRating($p2));
$team3 = new Team($p3, $newRatings->getRating($p3));
$newRatings->getRating($p1)->getConservativeRating();
$newRatings->getRating($p2)->getConservativeRating();
$newRatings->getRating($p3)->getConservativeRating();
}
}
}

@ -6,10 +6,10 @@
"php": "^8.2"
},
"require-dev": {
"phpunit/phpunit": "^10",
"phpstan/phpstan": "^1",
"squizlabs/php_codesniffer": "*",
"vimeo/psalm": "^5.14"
"phpstan/phpstan": "^1.0",
"vimeo/psalm": "^5.21.1",
"phpmetrics/phpmetrics": "^3.0-dev",
"phpunit/phpunit": "^10.5"
},
"autoload": {
"psr-4": {
@ -22,16 +22,26 @@
}
},
"scripts": {
"test": "vendor/bin/phpunit tests --display-warnings",
"test-coverage": "vendor/bin/phpunit tests --testdox --coverage-filter src --coverage-html output/coverage --coverage-text --testdox-html output/test.html --log-junit output/test.xml",
"document": "phpDocumentor --setting=graphs.enabled=true",
"analyze": [
"test": "phpunit",
"document": "phpDocumentor",
"benchmark": "phpbench run --report=default --output=build-artifact",
"metrics": "vendor/bin/phpmetrics --config=phpmetrics.json",
"lint": [
"phplint",
"phpcs"
],
"static": [
"@analyze-phpstan",
"@analyze-psalm",
"@analyze-phpcs"
"@analyze-psalm"
],
"analyze-phpstan":"vendor/bin/phpstan analyze --error-format=raw",
"analyze-psalm": "vendor/bin/psalm --no-cache",
"analyze-phpcs": "vendor/bin/phpcs --report=emacs --standard=PSR1,PSR2,PSR12 --exclude=Generic.Files.LineLength src tests"
"all": [
"@test",
"@document",
"@benchmark",
"@lint",
"@static"
]
}
}

162
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "667bb75c25a72b3d35f64ada9d9d9e0d",
"content-hash": "129eec96c91a9ee234640b9d6bcd0f43",
"packages": [],
"packages-dev": [
{
@ -1082,6 +1082,82 @@
},
"time": "2024-01-11T11:49:22+00:00"
},
{
"name": "phpmetrics/phpmetrics",
"version": "v3.0.0rc5",
"source": {
"type": "git",
"url": "https://github.com/phpmetrics/PhpMetrics.git",
"reference": "7a4ee5d6a8233f449fb9125ad586d3880c8a1b2f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/7a4ee5d6a8233f449fb9125ad586d3880c8a1b2f",
"reference": "7a4ee5d6a8233f449fb9125ad586d3880c8a1b2f",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"nikic/php-parser": "^4",
"php": ">=8.1"
},
"replace": {
"halleck45/php-metrics": "*",
"halleck45/phpmetrics": "*"
},
"require-dev": {
"phake/phake": "^4.4.0",
"phpstan/extension-installer": "^1.3",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-deprecation-rules": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpstan/phpstan-strict-rules": "^1.5",
"phpunit/phpunit": "^10.3",
"roave/security-advisories": "dev-latest",
"sebastian/comparator": ">=5.0.0",
"squizlabs/php_codesniffer": "^3.7",
"symfony/dom-crawler": "^6.3",
"vimeo/psalm": "^5.15"
},
"suggest": {
"ext-dom": "To allow XML parsing and report results.",
"ext-yaml": "To allow yaml parsing of configuration files."
},
"bin": [
"bin/phpmetrics"
],
"type": "library",
"autoload": {
"psr-4": {
"Hal\\": "src/Hal"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jean-François Lépine",
"email": "lepinejeanfrancois@yahoo.fr",
"homepage": "http://www.lepine.pro",
"role": "Copyright Holder"
}
],
"description": "Static analyzer tool for PHP : Coupling, Cyclomatic complexity, Maintainability Index, Halstead's metrics... and more !",
"homepage": "http://www.phpmetrics.org",
"keywords": [
"analysis",
"qa",
"quality",
"testing"
],
"support": {
"issues": "https://github.com/PhpMetrics/PhpMetrics/issues",
"source": "https://github.com/phpmetrics/PhpMetrics/tree/v3.0.0rc5"
},
"time": "2024-01-17T10:59:12+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.25.0",
@ -2694,86 +2770,6 @@
],
"time": "2023-11-14T14:08:51+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.8.1",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "14f5fff1e64118595db5408e946f3a22c75807f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7",
"reference": "14f5fff1e64118595db5408e946f3a22c75807f7",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
},
"bin": [
"bin/phpcbf",
"bin/phpcs"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "Former lead"
},
{
"name": "Juliette Reinders Folmer",
"role": "Current lead"
},
{
"name": "Contributors",
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards",
"static analysis"
],
"support": {
"issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
"security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
"source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
"wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
},
"funding": [
{
"url": "https://github.com/PHPCSStandards",
"type": "github"
},
{
"url": "https://github.com/jrfnl",
"type": "github"
},
{
"url": "https://opencollective.com/php_codesniffer",
"type": "open_collective"
}
],
"time": "2024-01-11T20:47:48+00:00"
},
{
"name": "symfony/console",
"version": "v7.0.3",
@ -3649,7 +3645,9 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"phpmetrics/phpmetrics": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

11
phpbench.json Normal file

@ -0,0 +1,11 @@
{
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "benchmark/",
"report.outputs": {
"build-artifact": {
"renderer": "html",
"path": "output/benchmark.html",
"title": "Benchmarking"
}
}
}

20
phpmetrics.json Normal file

@ -0,0 +1,20 @@
{
"composer": true,
"includes": [
"src"
],
"excludes": [
"tests"
],
"report": {
"html": "output/metrics/"
},
"plugins": {
"git": {
"binary": "git"
},
"junit": {
"file": "output/test.xml"
}
}
}

@ -3,3 +3,4 @@ parameters:
paths:
- src
- tests
- benchmark

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<coverage/>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false" displayDetailsOnTestsThatTriggerWarnings="true">
<testsuites>
<testsuite name="PHPSkills Test Suite">
<directory>./tests/</directory>
@ -11,4 +10,14 @@
<directory suffix=".php">src/</directory>
</include>
</source>
<logging>
<junit outputFile="output/test/junit.xml"/>
<testdoxHtml outputFile="output/test/index.html"/>
</logging>
<coverage>
<report>
<html outputDirectory="output/coverage" />
</report>
</coverage>
</phpunit>

@ -11,6 +11,7 @@
<projectFiles>
<directory name="src" />
<directory name="tests" />
<directory name="benchmark" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
use DNW\Skills\Guard;
@ -94,6 +96,7 @@ abstract class Factor implements \Stringable
/**
* Sends the ith message to the marginal and returns the log-normalization constant
*
* @throws Exception
*/
public function sendMessageIndex(int $messageIndex): float|int

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class FactorGraph

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
use DNW\Skills\FactorGraphs\ScheduleSequence;
@ -42,6 +44,7 @@ abstract class FactorGraphLayer
/**
* This reference is still needed
*
* @return array<int,array<int,object>>
*/
public function &getOutputVariablesGroups(): array
@ -82,11 +85,11 @@ abstract class FactorGraphLayer
public function createPriorSchedule(): ?ScheduleSequence
{
return null;
return NULL;
}
public function createPosteriorSchedule(): ?ScheduleSequence
{
return null;
return NULL;
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
/**

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class KeyedVariable extends Variable

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
use DNW\Skills\Numerics\GaussianDistribution;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
abstract class Schedule implements \Stringable

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class ScheduleLoop extends Schedule

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class ScheduleSequence extends Schedule

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class ScheduleStep extends Schedule

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
use DNW\Skills\Numerics\GaussianDistribution;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\FactorGraphs;
class VariableFactory

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**
@ -23,7 +25,8 @@ class GameInfo
private float $beta = self::DEFAULT_BETA,
private float $dynamicsFactor = self::DEFAULT_DYNAMICS_FACTOR,
private float $drawProbability = self::DEFAULT_DRAW_PROBABILITY
) {
)
{
}
public function getInitialMean(): float

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
use Exception;
@ -13,7 +15,7 @@ class Guard
{
public static function argumentNotNull(mixed $value, string $parameterName): void
{
if ($value == null) {
if ($value == NULL) {
throw new Exception($parameterName . ' can not be null');
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
interface ISupportPartialUpdate

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
/**
@ -14,6 +16,7 @@ class BasicMath
* Squares the input (x^2 = x * x)
*
* @param float $x Value to square (x)
*
* @return float The squared value (x^2)
*/
public static function square($x): float
@ -26,6 +29,7 @@ class BasicMath
*
* @param mixed[] $itemsToSum The items to sum,
* @param \Closure $callback The function to apply to each array element before summing.
*
* @return float The sum.
*/
public static function sum(array $itemsToSum, \Closure $callback): float

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
class DiagonalMatrix extends Matrix

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
/**
@ -198,7 +200,8 @@ class GaussianDistribution implements \Stringable
-1.523e-15,
-9.4e-17,
1.21e-16,
-2.8e-17, ];
-2.8e-17,
];
$ncof = count($coefficients);
$d = 0.0;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
class IdentityMatrix extends DiagonalMatrix

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
use Exception;
@ -11,7 +13,7 @@ class Matrix
/**
* @param array<int,array<int,float>> $matrixRowData
*/
public function __construct(private int $rowCount = 0, private int $columnCount = 0, private array $matrixRowData = array())
public function __construct(private int $rowCount = 0, private int $columnCount = 0, private array $matrixRowData = [])
{
}
@ -307,7 +309,7 @@ class Matrix
public function equals(Matrix $otherMatrix): bool
{
if (($this->rowCount != $otherMatrix->getRowCount()) || ($this->columnCount != $otherMatrix->getColumnCount())) {
return false;
return FALSE;
}
for ($currentRow = 0; $currentRow < $this->rowCount; $currentRow++) {
@ -319,11 +321,11 @@ class Matrix
);
if ($delta > self::ERROR_TOLERANCE) {
return false;
return FALSE;
}
}
}
return true;
return TRUE;
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
// The whole purpose of this class is to make the code for the SkillCalculator(s)

@ -1,12 +1,14 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
class SquareMatrix extends Matrix
{
public function __construct(float|int ...$allValues)
{
$rows = (int) sqrt(count($allValues));
$rows = (int)sqrt(count($allValues));
$cols = $rows;
$matrixData = [];

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Numerics;
class Vector extends Matrix

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
class PartialPlay

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**
@ -26,7 +28,8 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate, \Stringable
private mixed $Id,
float $partialPlayPercentage = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
float $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');
@ -60,6 +63,6 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate, \Stringable
public function __toString(): string
{
return (string) $this->Id;
return (string)$this->Id;
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
use DNW\Skills\Numerics\Range;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
/**
@ -12,6 +14,7 @@ class RankSorter
*
* @param array<mixed> $teams The items to sort according to the order specified by ranks.
* @param array<int> $teamRanks The ranks for each item where 1 is first place.
*
* @return array<int>
*/
public static function sort(array &$teams, array &$teamRanks): array

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
// Container for a player's rating.

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
class RatingContainer

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
use Exception;
@ -13,7 +15,8 @@ abstract class SkillCalculator
private int $supportedOptions,
private readonly TeamsRange $totalTeamsAllowed,
private readonly PlayersRange $playersPerTeamAllowed
) {
)
{
}
/**
@ -22,6 +25,7 @@ abstract class SkillCalculator
* @param GameInfo $gameInfo Parameters for the game.
* @param Team[] $teams A mapping of team players and their ratings.
* @param int[] $teamRanks The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2).
*
* @return RatingContainer All the players and their new ratings.
*/
abstract public function calculateNewRatings(
@ -35,6 +39,7 @@ abstract class SkillCalculator
*
* @param GameInfo $gameInfo Parameters for the game.
* @param Team[] $teams 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 $teams): float;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
class SkillCalculatorSupportedOptions

@ -1,14 +1,16 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
class Team extends RatingContainer
{
public function __construct(Player $player = null, Rating $rating = null)
public function __construct(Player $player = NULL, Rating $rating = NULL)
{
parent::__construct();
if (! is_null($player) && ! is_null($rating)) {
if ($player && $rating) {
$this->addPlayer($player, $rating);
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
class Teams

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills;
use DNW\Skills\Numerics\Range;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\Numerics\GaussianDistribution;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\GameInfo;
@ -34,7 +36,8 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
GameInfo $gameInfo,
array $teams,
array $teamRanks
): RatingContainer {
): RatingContainer
{
Guard::argumentNotNull($gameInfo, 'gameInfo');
$this->validateTeamCountAndPlayersCountPerTeam($teams);
@ -124,7 +127,9 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
/**
* Helper function that gets a list of values for all player ratings
*
* @param Team[] $teamAssignmentsList
*
* @return int[]
*/
private static function getPlayerRatingValues(array $teamAssignmentsList, \Closure $playerRatingFunction): array

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\Factor;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\Message;
@ -62,11 +64,9 @@ class GaussianGreaterThanFactor extends GaussianFactor
$denom = 1.0 - TruncatedGaussianCorrectionFunctions::wExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC);
$newPrecision = $c / $denom;
$newPrecisionMean = (
$d +
$newPrecisionMean = ($d +
$sqrtC *
TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC)
) / $denom;
TruncatedGaussianCorrectionFunctions::vExceedsMargin($dOnSqrtC, $epsilsonTimesSqrtC)) / $denom;
$newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision);

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\KeyedVariable;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\Message;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\Message;
@ -24,6 +26,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
/**
* 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]
*
* @var array<float[]> $weights
*/
private array $weights = [];
@ -247,7 +250,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
$absValue = sprintf('%.2f', \abs($weights[$i])); // 0.00?
$result .= $absValue;
$result .= '*[';
$result .= (string) $variablesToSum[$i];
$result .= (string)$variablesToSum[$i];
$result .= ']';
$isLast = ($i === $totalVars - 1);

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Factors;
use DNW\Skills\FactorGraphs\Message;
@ -65,8 +67,7 @@ class GaussianWithinFactor extends GaussianFactor
$newPrecision = $c / $denominator;
$newPrecisionMean = ($d +
$sqrtC *
TruncatedGaussianCorrectionFunctions::vWithinMargin($dOnSqrtC, $epsilonTimesSqrtC)
) / $denominator;
TruncatedGaussianCorrectionFunctions::vWithinMargin($dOnSqrtC, $epsilonTimesSqrtC)) / $denominator;
$newMarginal = GaussianDistribution::fromPrecisionMean($newPrecisionMean, $newPrecision);
$newMessage = GaussianDistribution::divide(

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\ScheduleLoop;
@ -15,7 +17,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
TrueSkillFactorGraph $parentGraph,
private readonly TeamPerformancesToTeamPerformanceDifferencesLayer $TeamPerformancesToTeamPerformanceDifferencesLayer,
private readonly TeamDifferencesComparisonLayer $TeamDifferencesComparisonLayer
) {
)
{
parent::__construct($parentGraph);
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\ScheduleStep;
@ -87,7 +89,7 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
*/
private function createOutputVariable(array $team): Variable
{
$memberNames = array_map(fn ($currentPlayer) => (string) ($currentPlayer->getKey()), $team);
$memberNames = array_map(fn ($currentPlayer) => (string)($currentPlayer->getKey()), $team);
$teamMemberNames = \implode(', ', $memberNames);

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\ScheduleStep;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\KeyedVariable;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\TrueSkill\DrawMargin;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\Variable;
@ -30,7 +32,8 @@ class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorG
Variable $strongerTeam,
Variable $weakerTeam,
Variable $output
): GaussianWeightedSumFactor {
): GaussianWeightedSumFactor
{
$teams = [$strongerTeam, $weakerTeam];
$weights = [1.0, -1.0];

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill\Layers;
use DNW\Skills\FactorGraphs\FactorGraphLayer;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\FactorGraphs\FactorGraph;
@ -60,11 +62,11 @@ class TrueSkillFactorGraph extends FactorGraph
public function buildGraph(): void
{
$lastOutput = null;
$lastOutput = NULL;
$layers = $this->layers;
foreach ($layers as $currentLayer) {
if ($lastOutput != null) {
if ($lastOutput != NULL) {
$currentLayer->setInputVariablesGroups($lastOutput);
}
@ -105,7 +107,7 @@ class TrueSkillFactorGraph extends FactorGraph
$layers = $this->layers;
foreach ($layers as $currentLayer) {
$currentPriorSchedule = $currentLayer->createPriorSchedule();
if ($currentPriorSchedule != null) {
if ($currentPriorSchedule != NULL) {
$fullSchedule[] = $currentPriorSchedule;
}
}
@ -114,7 +116,7 @@ class TrueSkillFactorGraph extends FactorGraph
foreach ($allLayersReverse as $currentLayer) {
$currentPosteriorSchedule = $currentLayer->createPosteriorSchedule();
if ($currentPosteriorSchedule != null) {
if ($currentPosteriorSchedule != NULL) {
$fullSchedule[] = $currentPosteriorSchedule;
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\Numerics\GaussianDistribution;
@ -120,8 +122,7 @@ class TruncatedGaussianCorrectionFunctions
$vt = self::vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin);
return $vt * $vt +
(
($drawMargin - $teamPerformanceDifferenceAbsoluteValue)
(($drawMargin - $teamPerformanceDifferenceAbsoluteValue)
*
GaussianDistribution::at(
$drawMargin - $teamPerformanceDifferenceAbsoluteValue

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\GameInfo;
@ -35,7 +37,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
GameInfo $gameInfo,
array $teams,
array $teamRanks
): RatingContainer {
): RatingContainer
{
// Basic argument checking
Guard::argumentNotNull($gameInfo, 'gameInfo');
$this->validateTeamCountAndPlayersCountPerTeam($teams);

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\TrueSkill;
use DNW\Skills\GameInfo;
@ -68,7 +70,8 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
Team $selfTeam,
Team $otherTeam,
PairwiseComparison $selfToOtherTeamComparison
): void {
): void
{
$drawMargin = DrawMargin::getDrawMarginFromDrawProbability(
$gameInfo->getDrawProbability(),
$gameInfo->getBeta()

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\FactorGraphs;
use DNW\Skills\FactorGraphs\ScheduleStep;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\FactorGraphs;
use DNW\Skills\FactorGraphs\Variable;
@ -13,6 +15,7 @@ class VariableTest extends TestCase
$gd_prior = new GaussianDistribution();
$var = new Variable('dummy', $gd_prior);
$this->assertEquals($gd_prior, $var->getValue());
$gd_new = new GaussianDistribution();
$this->assertEquals($gd_new, $var->getValue());
$var->resetToPrior();

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests;
use DNW\Skills\Guard;
@ -12,7 +14,7 @@ class GuardTest extends TestCase
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('dummy can not be null');
Guard::argumentNotNull(null, "dummy");
Guard::argumentNotNull(NULL, "dummy");
}
public function testargumentIsValidIndex(): void

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\Numerics;
use DNW\Skills\Numerics\BasicMath;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\Numerics;
use DNW\Skills\Numerics\BasicMath;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\Numerics;
use DNW\Skills\Numerics\IdentityMatrix;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\Numerics;
use DNW\Skills\Numerics\Range;

19
tests/PlayerTest.php Normal file

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests;
use DNW\Skills\Player;
use PHPUnit\Framework\TestCase;
class PlayerTest extends TestCase
{
public function test(): void
{
$p = new Player('dummy', 0.1, 0.2);
$this->assertEquals('dummy', (string)$p);
$this->assertEquals(0.1, $p->getPartialPlayPercentage());
$this->assertEquals(0.2, $p->getPartialUpdatePercentage());
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests;
use DNW\Skills\RankSorter;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests;
use DNW\Skills\Rating;
@ -19,10 +21,10 @@ class RatingTest extends TestCase
public function testPartialUpdate(): void
{
$rating = new Rating(100, 10, 5);
$rating_prior = new Rating(100, 10, 5);
$rating_new = new Rating(200, 10, 5);
$ratingOld = new Rating(100, 10, 5);
$ratingNew = new Rating(200, 10, 5);
$rating_partial = $rating ->getPartialUpdate($rating_prior, $rating_new, 0.5);
$rating_partial = $rating->getPartialUpdate($ratingOld, $ratingNew, 0.5);
$this->assertEquals(150, $rating_partial->getMean());

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\TrueSkill\DrawMargin;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\TrueSkill\FactorGraphTrueSkillCalculator;
@ -21,6 +23,6 @@ class FactorGraphTeamTrueSkillCalculatorTest extends TestCase
public function testMethodisSupported(): void
{
$calculator = new FactorGraphTrueSkillCalculator();
$this->assertEquals(true, $calculator->isSupported(SkillCalculatorSupportedOptions::PARTIAL_PLAY));
$this->assertEquals(TRUE, $calculator->isSupported(SkillCalculatorSupportedOptions::PARTIAL_PLAY));
}
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\GameInfo;
@ -25,7 +27,7 @@ class FactorGraphTrueSkillCalculatorTest extends TestCase
new Player("hillary"),
];
$teams = array();
$teams = [];
foreach ($players as $player) {
$teams[] = new Team($player, $gameInfo->getDefaultRating());
}

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\GameInfo;
@ -108,6 +110,7 @@ class TrueSkillCalculatorTests
$team2 = new Team($player2, $gameInfo->getDefaultRating());
$teams = Teams::concat($team1, $team2);
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, [1, 1]);
$player1NewRating = $newRatings->getRating($player1);
@ -170,9 +173,7 @@ class TrueSkillCalculatorTests
private static function oneOnTwoSimpleTest(TestCase $testClass, SkillCalculator $calculator): void
{
$player1 = new Player(1);
$gameInfo = new GameInfo();
$team1 = new Team();
$team1->addPlayer($player1, $gameInfo->getDefaultRating());

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\TrueSkill\TwoPlayerTrueSkillCalculator;

@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace DNW\Skills\Tests\TrueSkill;
use DNW\Skills\TrueSkill\TwoTeamTrueSkillCalculator;