Skip to content

Commit 8886d15

Browse files
committed
Add eigen axioms unit tests.
1 parent bf79e49 commit 8886d15

File tree

1 file changed

+291
-0
lines changed

1 file changed

+291
-0
lines changed
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
<?php
2+
3+
namespace MathPHP\Tests\LinearAlgebra\Eigen;
4+
5+
use MathPHP\LinearAlgebra\MatrixFactory;
6+
use MathPHP\LinearAlgebra\Vector;
7+
use MathPHP\Tests;
8+
9+
class EigenAxiomsTest extends \PHPUnit\Framework\TestCase
10+
{
11+
use Tests\LinearAlgebra\Fixture\MatrixDataProvider;
12+
13+
/**
14+
* Tests of Matrix eigenvector and eigenvalue axioms
15+
* These tests don't test specific functions,
16+
* but rather matrix axioms which in term make use of multiple functions.
17+
* If all the Matrix math is implemented properly, these tests should
18+
* all work out according to the axioms.
19+
*
20+
* Axioms tested:
21+
* - Eigenvalues and eigenvectors
22+
* - Av = λv (basic eigenvalue equation)
23+
* - det(A - λI) = 0 (characteristic equation)
24+
* - tr(A) = Σλᵢ (trace equals sum of eigenvalues)
25+
* - det(A) = Πλᵢ (determinant equals product of eigenvalues)
26+
* - Aⁿv = λⁿv (matrix power property)
27+
*/
28+
29+
/**************************************************************************
30+
* EIGENVALUE AND EIGENVECTOR AXIOMS
31+
**************************************************************************/
32+
33+
/**
34+
* @test Axiom: Av = λv (basic eigenvalue equation)
35+
* For each eigenvalue λ and corresponding eigenvector v, the equation Av = λv must hold
36+
*
37+
* @dataProvider dataProviderForEigenvalueAxioms
38+
* @param array $A
39+
* @throws \Exception
40+
*/
41+
public function testBasicEigenvalueEquation(array $A)
42+
{
43+
// Given
44+
$A = MatrixFactory::create($A);
45+
46+
// When
47+
$eigenvalues = $A->eigenvalues();
48+
$eigenvectors = $A->eigenvectors();
49+
50+
// Then
51+
for ($i = 0; $i < \count($eigenvalues); $i++) {
52+
$λ = $eigenvalues[$i];
53+
$v = new Vector($eigenvectors->getColumn($i));
54+
55+
$Av = $A->vectorMultiply($v);
56+
$λv = $v->scalarMultiply($λ);
57+
58+
// Av should equal λv
59+
$this->assertEqualsWithDelta($Av->getVector(), $λv->getVector(), 1e-6);
60+
}
61+
}
62+
63+
/**
64+
* @test Axiom: det(A - λI) = 0 (characteristic equation)
65+
* For each eigenvalue λ, the determinant of (A - λI) should be zero
66+
*
67+
* @dataProvider dataProviderForEigenvalueAxioms
68+
* @param array $A
69+
* @throws \Exception
70+
*/
71+
public function testCharacteristicEquation(array $A)
72+
{
73+
// Given
74+
$A = MatrixFactory::create($A);
75+
$I = MatrixFactory::identity($A->getM());
76+
77+
// When
78+
$eigenvalues = $A->eigenvalues();
79+
80+
// Then
81+
foreach ($eigenvalues as $λ) {
82+
$λI = $I->scalarMultiply($λ);
83+
$A_minus_λI = $A->subtract($λI);
84+
$det = $A_minus_λI->det();
85+
86+
$this->assertEqualsWithDelta(0, $det, 1e-6);
87+
}
88+
}
89+
90+
/**
91+
* @test Axiom: tr(A) = Σλᵢ (trace equals sum of eigenvalues)
92+
* The trace of a matrix equals the sum of its eigenvalues
93+
*
94+
* @dataProvider dataProviderForEigenvalueAxioms
95+
* @param array $A
96+
* @throws \Exception
97+
*/
98+
public function testTraceEqualsSumOfEigenvalues(array $A)
99+
{
100+
// Given
101+
$A = MatrixFactory::create($A);
102+
103+
// When
104+
$trace = $A->trace();
105+
$eigenvalues = $A->eigenvalues();
106+
$sum_of_eigenvalues = \array_sum($eigenvalues);
107+
108+
// Then
109+
$this->assertEqualsWithDelta($trace, $sum_of_eigenvalues, 1e-6);
110+
}
111+
112+
/**
113+
* @test Axiom: det(A) = Πλᵢ (determinant equals product of eigenvalues)
114+
* The determinant of a matrix equals the product of its eigenvalues
115+
*
116+
* @dataProvider dataProviderForEigenvalueAxioms
117+
* @param array $A
118+
* @throws \Exception
119+
*/
120+
public function testDeterminantEqualsProductOfEigenvalues(array $A)
121+
{
122+
// Given
123+
$A = MatrixFactory::create($A);
124+
125+
// When
126+
$det = $A->det();
127+
$eigenvalues = $A->eigenvalues();
128+
$product_of_eigenvalues = \array_product($eigenvalues);
129+
130+
// Then
131+
$this->assertEqualsWithDelta($det, $product_of_eigenvalues, 1e-6);
132+
}
133+
134+
/**
135+
* @test Axiom: Aⁿv = λⁿv (matrix power property)
136+
* For eigenvalue λ and eigenvector v, raising the matrix to power n gives A^n v = λ^n v
137+
*
138+
* @dataProvider dataProviderForEigenvalueMatrixPower
139+
* @param array $A
140+
* @param int $n
141+
* @throws \Exception
142+
*/
143+
public function testMatrixPowerProperty(array $A, int $n)
144+
{
145+
// Given
146+
$A = MatrixFactory::create($A);
147+
148+
// When
149+
$eigenvalues = $A->eigenvalues();
150+
$eigenvectors = $A->eigenvectors();
151+
152+
// Calculate Aⁿ by repeated multiplication
153+
$An = MatrixFactory::identity($A->getM());
154+
for ($i = 0; $i < $n; $i++) {
155+
$An = $An->multiply($A);
156+
}
157+
158+
// Then
159+
for ($i = 0; $i < \count($eigenvalues); $i++) {
160+
$λ = $eigenvalues[$i];
161+
$v = new Vector($eigenvectors->getColumn($i));
162+
163+
$Aⁿv = $An->vectorMultiply($v);
164+
$λⁿv = $v->scalarMultiply(pow($λ, $n));
165+
166+
// Aⁿv should equal λⁿv
167+
for ($j = 0; $j < $Aⁿv->getN(); $j++) {
168+
$this->assertEqualsWithDelta($λⁿv->get($j), $Aⁿv->get($j), 1e-5);
169+
}
170+
}
171+
}
172+
173+
/**************************************************************************
174+
* DATA PROVIDERS FOR EIGENVALUE TESTS
175+
**************************************************************************/
176+
177+
/**
178+
* Data provider for eigenvalue axiom tests with well-conditioned matrices
179+
* @return array
180+
*/
181+
public function dataProviderForEigenvalueAxioms(): array
182+
{
183+
return [
184+
// 2x2 diagonal matrix (simple eigenvalues)
185+
[
186+
[
187+
[2, 0],
188+
[0, 3],
189+
],
190+
],
191+
// 2x2 symmetric matrix
192+
[
193+
[
194+
[1, 2],
195+
[2, 1],
196+
],
197+
],
198+
// 2x2 general matrix
199+
[
200+
[
201+
[0, 1],
202+
[-2, -3],
203+
],
204+
],
205+
// 3x3 diagonal matrix
206+
[
207+
[
208+
[1, 0, 0],
209+
[0, 2, 0],
210+
[0, 0, 3],
211+
],
212+
],
213+
// 3x3 symmetric matrix
214+
[
215+
[
216+
[2, -1, 0],
217+
[-1, 2, -1],
218+
[0, -1, 2],
219+
],
220+
],
221+
// 3x3 upper triangular matrix
222+
[
223+
[
224+
[1, 2, 3],
225+
[0, 4, 5],
226+
[0, 0, 6],
227+
],
228+
],
229+
// 3x3 general matrix
230+
[
231+
[
232+
[-2, -4, 2],
233+
[-2, 1, 2],
234+
[4, 2, 5],
235+
],
236+
],
237+
];
238+
}
239+
240+
/**
241+
* Data provider for matrix power eigenvalue tests
242+
* @return array
243+
*/
244+
public function dataProviderForEigenvalueMatrixPower(): array
245+
{
246+
return [
247+
// 2x2 diagonal matrix with power 2
248+
[
249+
[
250+
[2, 0],
251+
[0, 3],
252+
],
253+
2,
254+
],
255+
// 2x2 diagonal matrix with power 3
256+
[
257+
[
258+
[2, 0],
259+
[0, 3],
260+
],
261+
3,
262+
],
263+
// 2x2 symmetric matrix with power 2
264+
[
265+
[
266+
[1, 2],
267+
[2, 1],
268+
],
269+
2,
270+
],
271+
// 3x3 diagonal matrix with power 2
272+
[
273+
[
274+
[1, 0, 0],
275+
[0, 2, 0],
276+
[0, 0, 3],
277+
],
278+
2,
279+
],
280+
// 3x3 upper triangular matrix with power 2
281+
[
282+
[
283+
[1, 1, 0],
284+
[0, 2, 1],
285+
[0, 0, 3],
286+
],
287+
2,
288+
],
289+
];
290+
}
291+
}

0 commit comments

Comments
 (0)