More tests more resistance to mutation testing.
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful

This commit is contained in:
Jens True 2024-07-25 11:23:59 +00:00
parent 1b7e26a6b5
commit f6acee18e5
6 changed files with 184 additions and 25 deletions

22
composer.lock generated

@ -1636,16 +1636,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.11.7", "version": "1.11.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d" "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/52d2bbfdcae7f895915629e4694e9497d0f8e28d", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec",
"reference": "52d2bbfdcae7f895915629e4694e9497d0f8e28d", "reference": "6adbd118e6c0515dd2f36b06cde1d6da40f1b8ec",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1690,7 +1690,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-07-06T11:17:41+00:00" "time": "2024-07-24T07:01:22+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -2280,16 +2280,16 @@
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
"version": "1.2.1", "version": "1.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rectorphp/rector.git", "url": "https://github.com/rectorphp/rector.git",
"reference": "b38a3eed3ce2046f40c001255e2fec9d2746bacf" "reference": "044e6364017882d1e346da8690eeabc154da5495"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/b38a3eed3ce2046f40c001255e2fec9d2746bacf", "url": "https://api.github.com/repos/rectorphp/rector/zipball/044e6364017882d1e346da8690eeabc154da5495",
"reference": "b38a3eed3ce2046f40c001255e2fec9d2746bacf", "reference": "044e6364017882d1e346da8690eeabc154da5495",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2327,7 +2327,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/rectorphp/rector/issues", "issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/1.2.1" "source": "https://github.com/rectorphp/rector/tree/1.2.2"
}, },
"funding": [ "funding": [
{ {
@ -2335,7 +2335,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-07-16T00:22:54+00:00" "time": "2024-07-25T07:44:34+00:00"
}, },
{ {
"name": "revolt/event-loop", "name": "revolt/event-loop",

@ -60,13 +60,34 @@ class Matrix
return $this->columnCount; return $this->columnCount;
} }
private function checkRowCol(int $row, int $col): void
{
if ($row < 0) {
throw new Exception("Row negative");
}
if ($row >= $this->getRowCount()) {
throw new Exception("Row beyond range");
}
if ($col < 0) {
throw new Exception("Column negative");
}
if ($col >= $this->getColumnCount()) {
throw new Exception("Column beyond range");
}
}
public function getValue(int $row, int $col): float|int public function getValue(int $row, int $col): float|int
{ {
$this->checkRowCol($row, $col);
return $this->matrixRowData[$row][$col]; return $this->matrixRowData[$row][$col];
} }
public function setValue(int $row, int $col, float|int $value): void public function setValue(int $row, int $col, float|int $value): void
{ {
$this->checkRowCol($row, $col);
$this->matrixRowData[$row][$col] = $value; $this->matrixRowData[$row][$col] = $value;
} }
@ -100,7 +121,7 @@ class Matrix
if ($this->rowCount == 1) { if ($this->rowCount == 1) {
// Really happy path :) // Really happy path :)
return $this->matrixRowData[0][0]; return $this->getValue(0, 0);
} }
if ($this->rowCount == 2) { if ($this->rowCount == 2) {
@ -109,10 +130,10 @@ class Matrix
// | a b | // | a b |
// | c d | // | c d |
// The determinant is ad - bc // The determinant is ad - bc
$a = $this->matrixRowData[0][0]; $a = $this->getValue(0, 0);
$b = $this->matrixRowData[0][1]; $b = $this->getValue(0, 1);
$c = $this->matrixRowData[1][0]; $c = $this->getValue(1, 0);
$d = $this->matrixRowData[1][1]; $d = $this->getValue(1, 1);
return $a * $d - $b * $c; return $a * $d - $b * $c;
} }
@ -127,7 +148,7 @@ class Matrix
// I expand along the first row // I expand along the first row
for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) { for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) {
$firstRowColValue = $this->matrixRowData[0][$currentColumn]; $firstRowColValue = $this->getValue(0, $currentColumn);
$cofactor = $this->getCofactor(0, $currentColumn); $cofactor = $this->getCofactor(0, $currentColumn);
$itemToAdd = $firstRowColValue * $cofactor; $itemToAdd = $firstRowColValue * $cofactor;
$result += $itemToAdd; $result += $itemToAdd;
@ -152,10 +173,10 @@ class Matrix
// | d -b | // | d -b |
// | -c a | // | -c a |
$a = $this->matrixRowData[0][0]; $a = $this->getValue(0, 0);
$b = $this->matrixRowData[0][1]; $b = $this->getValue(0, 1);
$c = $this->matrixRowData[1][0]; $c = $this->getValue(1, 0);
$d = $this->matrixRowData[1][1]; $d = $this->getValue(1, 1);
return new SquareMatrix( return new SquareMatrix(
$d, $d,
@ -180,7 +201,7 @@ class Matrix
public function getInverse(): Matrix|SquareMatrix public function getInverse(): Matrix|SquareMatrix
{ {
if (($this->rowCount == 1) && ($this->columnCount == 1)) { if (($this->rowCount == 1) && ($this->columnCount == 1)) {
return new SquareMatrix(1.0 / $this->matrixRowData[0][0]); return new SquareMatrix(1.0 / $this->getValue(0, 0));
} }
// Take the simple approach: // Take the simple approach:
@ -262,6 +283,7 @@ class Matrix
private function getMinorMatrix(int $rowToRemove, int $columnToRemove): Matrix private function getMinorMatrix(int $rowToRemove, int $columnToRemove): Matrix
{ {
$this->checkRowCol($rowToRemove, $columnToRemove);
// See http://en.wikipedia.org/wiki/Minor_(linear_algebra) // See http://en.wikipedia.org/wiki/Minor_(linear_algebra)
// I'm going to use a horribly naïve algorithm... because I can :) // I'm going to use a horribly naïve algorithm... because I can :)
@ -281,7 +303,7 @@ class Matrix
continue; continue;
} }
$result[$actualRow][$actualCol] = $this->matrixRowData[$currentRow][$currentColumn]; $result[$actualRow][$actualCol] = $this->getValue($currentRow, $currentColumn);
++$actualCol; ++$actualCol;
} }
@ -294,6 +316,7 @@ class Matrix
public function getCofactor(int $rowToRemove, int $columnToRemove): float public function getCofactor(int $rowToRemove, int $columnToRemove): float
{ {
$this->checkRowCol($rowToRemove, $columnToRemove);
// See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details // See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details
// REVIEW: should things be reversed since I'm 0 indexed? // REVIEW: should things be reversed since I'm 0 indexed?
$sum = $rowToRemove + $columnToRemove; $sum = $rowToRemove + $columnToRemove;
@ -316,7 +339,7 @@ class Matrix
for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) { for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) {
$delta = $delta =
abs( abs(
$this->matrixRowData[$currentRow][$currentColumn] - $this->getValue($currentRow, $currentColumn) -
$otherMatrix->getValue($currentRow, $currentColumn) $otherMatrix->getValue($currentRow, $currentColumn)
); );

@ -28,7 +28,9 @@ class GuardTest extends TestCase
public function testargumentIsValidIndexArgumentValid(): void public function testargumentIsValidIndexArgumentValid(): void
{ {
Guard::argumentIsValidIndex(5, 10, "dummy"); Guard::argumentIsValidIndex(0, 10, "dummy");
Guard::argumentIsValidIndex(1, 10, "dummy");
Guard::argumentIsValidIndex(9, 10, "dummy");
$this->expectNotToPerformAssertions(); $this->expectNotToPerformAssertions();
} }
@ -48,7 +50,12 @@ class GuardTest extends TestCase
public function testargumentInRangeInclusiveValid(): void public function testargumentInRangeInclusiveValid(): void
{ {
Guard::argumentInRangeInclusive(0, 0, 100, "dummy");
Guard::argumentInRangeInclusive(1, 0, 100, "dummy");
Guard::argumentInRangeInclusive(50, 0, 100, "dummy"); Guard::argumentInRangeInclusive(50, 0, 100, "dummy");
Guard::argumentInRangeInclusive(99, 0, 100, "dummy");
Guard::argumentInRangeInclusive(100, 0, 100, "dummy");
$this->expectNotToPerformAssertions(); $this->expectNotToPerformAssertions();
} }
} }

@ -27,6 +27,7 @@ class HashMapTest extends TestCase
$h->setvalue($o2, 2); $h->setvalue($o2, 2);
$this->assertEquals([1, 2], $h->getAllValues()); $this->assertEquals([1, 2], $h->getAllValues());
$this->assertEquals([$o1, $o2], $h->getAllKeys());
$this->assertEquals(1, $h->getvalue($o1)); $this->assertEquals(1, $h->getvalue($o1));
$this->assertEquals(2, $h->getvalue($o2)); $this->assertEquals(2, $h->getvalue($o2));

@ -92,6 +92,20 @@ class GaussianDistributionTest extends TestCase
$m3s4 = new GaussianDistribution(3, 4); $m3s4 = new GaussianDistribution(3, 4);
$lpn2 = GaussianDistribution::logProductNormalization($m1s2, $m3s4); $lpn2 = GaussianDistribution::logProductNormalization($m1s2, $m3s4);
$this->assertEqualsWithDelta(-2.5168046699816684, $lpn2, GaussianDistributionTest::ERROR_TOLERANCE); $this->assertEqualsWithDelta(-2.5168046699816684, $lpn2, GaussianDistributionTest::ERROR_TOLERANCE);
$numerator = GaussianDistribution::fromPrecisionMean(1, 0);
$denominator = GaussianDistribution::fromPrecisionMean(1, 0);
$lrn = GaussianDistribution::logProductNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
$numerator = GaussianDistribution::fromPrecisionMean(1, 1);
$denominator = GaussianDistribution::fromPrecisionMean(1, 0);
$lrn = GaussianDistribution::logProductNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
$numerator = GaussianDistribution::fromPrecisionMean(1, 0);
$denominator = GaussianDistribution::fromPrecisionMean(1, 1);
$lrn = GaussianDistribution::logProductNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
} }
public function testLogRatioNormalization(): void public function testLogRatioNormalization(): void
@ -101,6 +115,20 @@ class GaussianDistributionTest extends TestCase
$m3s4 = new GaussianDistribution(3, 4); $m3s4 = new GaussianDistribution(3, 4);
$lrn = GaussianDistribution::logRatioNormalization($m1s2, $m3s4); $lrn = GaussianDistribution::logRatioNormalization($m1s2, $m3s4);
$this->assertEqualsWithDelta(2.6157405972171204, $lrn, GaussianDistributionTest::ERROR_TOLERANCE); $this->assertEqualsWithDelta(2.6157405972171204, $lrn, GaussianDistributionTest::ERROR_TOLERANCE);
$numerator = GaussianDistribution::fromPrecisionMean(1, 0);
$denominator = GaussianDistribution::fromPrecisionMean(1, 0);
$lrn = GaussianDistribution::logRatioNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
$numerator = GaussianDistribution::fromPrecisionMean(1, 1);
$denominator = GaussianDistribution::fromPrecisionMean(1, 0);
$lrn = GaussianDistribution::logRatioNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
$numerator = GaussianDistribution::fromPrecisionMean(1, 0);
$denominator = GaussianDistribution::fromPrecisionMean(1, 1);
$lrn = GaussianDistribution::logRatioNormalization($numerator, $denominator);
$this->assertEquals(0, $lrn);
} }
public function testAbsoluteDifference(): void public function testAbsoluteDifference(): void
@ -115,4 +143,27 @@ class GaussianDistributionTest extends TestCase
$absDiff2 = GaussianDistribution::absoluteDifference($m1s2, $m3s4); $absDiff2 = GaussianDistribution::absoluteDifference($m1s2, $m3s4);
$this->assertEqualsWithDelta(0.4330127018922193, $absDiff2, GaussianDistributionTest::ERROR_TOLERANCE); $this->assertEqualsWithDelta(0.4330127018922193, $absDiff2, GaussianDistributionTest::ERROR_TOLERANCE);
} }
public function testSubtract(): void
{
// Verified with Ralf Herbrich's F# implementation
$standardNormal = new GaussianDistribution(0, 1);
$absDiff = GaussianDistribution::subtract($standardNormal, $standardNormal);
$this->assertEqualsWithDelta(0.0, $absDiff, GaussianDistributionTest::ERROR_TOLERANCE);
$m1s2 = new GaussianDistribution(1, 2);
$m3s4 = new GaussianDistribution(3, 4);
$absDiff2 = GaussianDistribution::subtract($m1s2, $m3s4);
$this->assertEqualsWithDelta(0.4330127018922193, $absDiff2, GaussianDistributionTest::ERROR_TOLERANCE);
}
public function testfromPrecisionMean(): void
{
$gd = GaussianDistribution::fromPrecisionMean(0, 0);
$this->assertInfinite($gd->getVariance());
$this->assertInfinite($gd->getStandardDeviation());
$this->assertNan($gd->getMean());
$this->assertEquals(0, $gd->getPrecisionMean());
$this->assertEquals(0, $gd->getPrecision());
}
} }

@ -22,6 +22,83 @@ use Exception;
// phpcs:disable PSR2.Methods.FunctionCallSignature,Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma // phpcs:disable PSR2.Methods.FunctionCallSignature,Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma
class MatrixTest extends TestCase class MatrixTest extends TestCase
{ {
public function testEmptyMatrix(): void
{
$m1 = new Matrix();
$this->assertEquals(0, $m1->getRowCount());
$this->assertEquals(0, $m1->getColumnCount());
$m2 = new Matrix(0, 0);
$this->assertEquals(0, $m2->getRowCount());
$this->assertEquals(0, $m2->getColumnCount());
$this->assertEquals(new Matrix(), Matrix::multiply($m1, $m2));
}
public function testIndexing(): void
{
$m = new Matrix(5, 5);
$m->setValue(0, 0, 1);
$this->assertEquals(1, $m->getValue(0, 0));
$m->setValue(0, 1, 2);
$this->assertEquals(2, $m->getValue(0, 1));
$m->setValue(1, 0, 3);
$this->assertEquals(3, $m->getValue(1, 0));
$m->setValue(1, 1, 4);
$this->assertEquals(4, $m->getValue(1, 1));
$m->setValue(3, 3, 11);
$this->assertEquals(11, $m->getValue(3, 3));
$m->setValue(4, 3, 22);
$this->assertEquals(22, $m->getValue(4, 3));
$m->setValue(3, 4, 33);
$this->assertEquals(33, $m->getValue(3, 4));
$m->setValue(4, 4, 44);
$this->assertEquals(44, $m->getValue(4, 4));
try {
$m->getValue(-1, -1);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
try {
$m->getValue(-1, 0);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
try {
$m->getValue(0, -1);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
try {
$m->getValue(5, 5);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
try {
$m->getValue(5, 4);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
try {
$m->getValue(4, 5);
$this->fail("No exception");
} catch (Exception $exception) {
$this->assertInstanceOf(Exception::class, $exception);
}
}
public function testOneByOneDeterminant(): void public function testOneByOneDeterminant(): void
{ {
$a = new SquareMatrix(1); $a = new SquareMatrix(1);