2017-04-23 07:03:30 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
2018-01-06 12:09:33 +00:00
|
|
|
namespace Phpml\Tests\Math\LinearAlgebra;
|
2017-04-23 07:03:30 +00:00
|
|
|
|
|
|
|
use Phpml\Math\LinearAlgebra\EigenvalueDecomposition;
|
|
|
|
use Phpml\Math\Matrix;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
2018-02-16 06:25:24 +00:00
|
|
|
class EigenvalueDecompositionTest extends TestCase
|
2017-04-23 07:03:30 +00:00
|
|
|
{
|
2018-02-17 23:09:24 +00:00
|
|
|
public function testKnownSymmetricMatrixDecomposition(): void
|
2017-04-23 07:03:30 +00:00
|
|
|
{
|
|
|
|
// First a simple example whose result is known and given in
|
|
|
|
// http://www.cs.otago.ac.nz/cosc453/student_tutorials/principal_components.pdf
|
|
|
|
$matrix = [
|
|
|
|
[0.616555556, 0.615444444],
|
2017-11-22 21:16:10 +00:00
|
|
|
[0.614444444, 0.716555556],
|
2017-04-23 07:03:30 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
$decomp = new EigenvalueDecomposition($matrix);
|
|
|
|
|
2019-04-10 18:42:59 +00:00
|
|
|
self::assertEqualsWithDelta([0.0490833989, 1.28402771], $decomp->getRealEigenvalues(), 0.001);
|
|
|
|
self::assertEqualsWithDelta([
|
2018-02-17 23:09:24 +00:00
|
|
|
[-0.735178656, 0.677873399],
|
|
|
|
[-0.677873399, -0.735178656],
|
2019-04-10 18:42:59 +00:00
|
|
|
], $decomp->getEigenvectors(), 0.001);
|
2018-02-17 23:09:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testMatrixWithAllZeroRow(): void
|
|
|
|
{
|
|
|
|
// http://www.wolframalpha.com/widgets/view.jsp?id=9aa01caf50c9307e9dabe159c9068c41
|
|
|
|
$matrix = [
|
|
|
|
[10, 0, 0],
|
|
|
|
[0, 6, 0],
|
|
|
|
[0, 0, 0],
|
|
|
|
];
|
|
|
|
|
|
|
|
$decomp = new EigenvalueDecomposition($matrix);
|
|
|
|
|
2019-04-10 18:42:59 +00:00
|
|
|
self::assertEqualsWithDelta([0.0, 6.0, 10.0], $decomp->getRealEigenvalues(), 0.0001);
|
|
|
|
self::assertEqualsWithDelta([
|
2018-02-17 23:09:24 +00:00
|
|
|
[0, 0, 1],
|
|
|
|
[0, 1, 0],
|
|
|
|
[1, 0, 0],
|
2019-04-10 18:42:59 +00:00
|
|
|
], $decomp->getEigenvectors(), 0.0001);
|
2018-02-17 23:09:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testMatrixThatCauseErrorWithStrictComparision(): void
|
|
|
|
{
|
|
|
|
// http://www.wolframalpha.com/widgets/view.jsp?id=9aa01caf50c9307e9dabe159c9068c41
|
|
|
|
$matrix = [
|
|
|
|
[1, 0, 3],
|
|
|
|
[0, 1, 7],
|
|
|
|
[3, 7, 4],
|
|
|
|
];
|
|
|
|
|
|
|
|
$decomp = new EigenvalueDecomposition($matrix);
|
|
|
|
|
2019-04-10 18:42:59 +00:00
|
|
|
self::assertEqualsWithDelta([-5.2620873481, 1.0, 10.2620873481], $decomp->getRealEigenvalues(), 0.000001);
|
|
|
|
self::assertEqualsWithDelta([
|
2018-02-17 23:09:24 +00:00
|
|
|
[-0.3042688, -0.709960552, 0.63511928],
|
|
|
|
[-0.9191450, 0.393919298, 0.0],
|
|
|
|
[0.25018574, 0.5837667, 0.7724140],
|
2019-04-10 18:42:59 +00:00
|
|
|
], $decomp->getEigenvectors(), 0.0001);
|
2018-02-17 23:09:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testRandomSymmetricMatrixEigenPairs(): void
|
|
|
|
{
|
|
|
|
// Acceptable error
|
|
|
|
$epsilon = 0.001;
|
2017-04-23 07:03:30 +00:00
|
|
|
// Secondly, generate a symmetric square matrix
|
|
|
|
// and test for A.v=λ.v
|
|
|
|
// (We, for now, omit non-symmetric matrices whose eigenvalues can be complex numbers)
|
|
|
|
$len = 3;
|
2018-02-17 23:09:24 +00:00
|
|
|
srand((int) microtime(true) * 1000);
|
2017-04-23 07:03:30 +00:00
|
|
|
$A = array_fill(0, $len, array_fill(0, $len, 0.0));
|
2017-08-17 06:50:37 +00:00
|
|
|
for ($i = 0; $i < $len; ++$i) {
|
|
|
|
for ($k = 0; $k < $len; ++$k) {
|
2017-04-23 07:03:30 +00:00
|
|
|
if ($i > $k) {
|
|
|
|
$A[$i][$k] = $A[$k][$i];
|
|
|
|
} else {
|
2017-11-22 21:16:10 +00:00
|
|
|
$A[$i][$k] = random_int(0, 10);
|
2017-04-23 07:03:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$decomp = new EigenvalueDecomposition($A);
|
|
|
|
$eigValues = $decomp->getRealEigenvalues();
|
2017-08-17 06:50:37 +00:00
|
|
|
$eigVectors = $decomp->getEigenvectors();
|
2017-04-23 07:03:30 +00:00
|
|
|
|
|
|
|
foreach ($eigValues as $index => $lambda) {
|
|
|
|
$m1 = new Matrix($A);
|
|
|
|
$m2 = (new Matrix($eigVectors[$index]))->transpose();
|
|
|
|
|
|
|
|
// A.v=λ.v
|
|
|
|
$leftSide = $m1->multiply($m2)->toArray();
|
2017-08-17 06:50:37 +00:00
|
|
|
$rightSide = $m2->multiplyByScalar($lambda)->toArray();
|
2017-04-23 07:03:30 +00:00
|
|
|
|
2019-04-10 18:42:59 +00:00
|
|
|
self::assertEqualsWithDelta($leftSide, $rightSide, $epsilon);
|
2017-04-23 07:03:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|