Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
97.30% |
36 / 37 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
CholeskyDecomposition | |
97.30% |
36 / 37 |
|
75.00% |
3 / 4 |
19 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
8 | |||
isSpd | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getL | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
solve | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
9.01 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Math\Matrix |
8 | * @copyright Dennis Eichhorn |
9 | * @copyright JAMA - https://math.nist.gov/javanumerics/jama/ |
10 | * @license OMS License 2.0 |
11 | * @version 1.0.0 |
12 | * @link https://jingga.app |
13 | */ |
14 | declare(strict_types=1); |
15 | |
16 | namespace phpOMS\Math\Matrix; |
17 | |
18 | use phpOMS\Math\Matrix\Exception\InvalidDimensionException; |
19 | |
20 | /** |
21 | * Cholesky decomposition |
22 | * |
23 | * A is syymetric, positive definite then A = L*L' |
24 | * |
25 | * @package phpOMS\Math\Matrix |
26 | * @license OMS License 2.0 |
27 | * @link https://jingga.app |
28 | * @since 1.0.0 |
29 | */ |
30 | final class CholeskyDecomposition |
31 | { |
32 | /** |
33 | * L matrix. |
34 | * |
35 | * @var array |
36 | * @since 1.0.0 |
37 | */ |
38 | private array $L = []; |
39 | |
40 | /** |
41 | * Dimension of L |
42 | * |
43 | * @var int |
44 | * @since 1.0.0 |
45 | */ |
46 | private int $m = 0; |
47 | |
48 | /** |
49 | * Is symmetric positiv definite |
50 | * |
51 | * @var bool |
52 | * @since 1.0.0 |
53 | */ |
54 | private bool $isSpd = true; |
55 | |
56 | /** |
57 | * Constructor. |
58 | * |
59 | * @param Matrix $M Matrix |
60 | * |
61 | * @since 1.0.0 |
62 | */ |
63 | public function __construct(Matrix $M) |
64 | { |
65 | $this->L = $M->toArray(); |
66 | $this->m = $M->getM(); |
67 | |
68 | for ($i = 0; $i < $this->m; ++$i) { |
69 | for ($j = $i; $j < $this->m; ++$j) { |
70 | for ($sum = $this->L[$i][$j], $k = $i - 1; $k >= 0; --$k) { |
71 | $sum -= $this->L[$i][$k] * $this->L[$j][$k]; |
72 | } |
73 | |
74 | if ($i === $j) { |
75 | if ($sum >= 0) { |
76 | $this->L[$i][$i] = \sqrt($sum); |
77 | } else { |
78 | $this->isSpd = false; |
79 | } |
80 | } elseif ($this->L[$i][$i] !== 0) { |
81 | $this->L[$j][$i] = $sum / $this->L[$i][$i]; |
82 | } |
83 | } |
84 | |
85 | for ($k = $i + 1; $k < $this->m; ++$k) { |
86 | $this->L[$i][$k] = 0.0; |
87 | } |
88 | } |
89 | } |
90 | |
91 | /** |
92 | * Is matrix symmetric positiv definite. |
93 | * |
94 | * @return bool |
95 | * |
96 | * @since 1.0.0 |
97 | */ |
98 | public function isSpd() : bool |
99 | { |
100 | return $this->isSpd; |
101 | } |
102 | |
103 | /** |
104 | * Get L matrix |
105 | * |
106 | * @return Matrix |
107 | * |
108 | * @since 1.0.0 |
109 | */ |
110 | public function getL() : Matrix |
111 | { |
112 | $matrix = new Matrix(); |
113 | $matrix->setMatrix($this->L); |
114 | |
115 | return $matrix; |
116 | } |
117 | |
118 | /** |
119 | * Solve Ax = b |
120 | * |
121 | * @param Matrix $B Matrix |
122 | * |
123 | * @return Matrix |
124 | * |
125 | * @throws InvalidDimensionException |
126 | * @throws \Exception |
127 | * |
128 | * @since 1.0.0 |
129 | */ |
130 | public function solve(Matrix $B) : Matrix |
131 | { |
132 | if ($B->getM() !== $this->m) { |
133 | throw new InvalidDimensionException((string) $B->getM()); |
134 | } |
135 | |
136 | if (!$this->isSpd) { |
137 | throw new \Exception(); |
138 | } |
139 | |
140 | $X = $B->toArray(); |
141 | $n = $B->getN(); |
142 | |
143 | // Solve L*Y = B; |
144 | for ($k = 0; $k < $this->m; ++$k) { |
145 | for ($j = 0; $j < $n; ++$j) { |
146 | for ($i = 0; $i < $k; ++$i) { |
147 | $X[$k][$j] -= $X[$i][$j] * $this->L[$k][$i]; |
148 | } |
149 | |
150 | $X[$k][$j] /= $this->L[$k][$k]; |
151 | } |
152 | } |
153 | |
154 | // Solve L'*X = Y; |
155 | for ($k = $this->m - 1; $k >= 0; --$k) { |
156 | for ($j = 0; $j < $n; ++$j) { |
157 | for ($i = $k + 1; $i < $this->m; ++$i) { |
158 | $X[$k][$j] -= $X[$i][$j] * $this->L[$i][$k]; |
159 | } |
160 | |
161 | $X[$k][$j] /= $this->L[$k][$k]; |
162 | } |
163 | } |
164 | |
165 | $solution = new Matrix(); |
166 | $solution->setMatrix($X); |
167 | |
168 | return $solution; |
169 | } |
170 | } |