Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.30% covered (success)
97.30%
36 / 37
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CholeskyDecomposition
97.30% covered (success)
97.30%
36 / 37
75.00% covered (warning)
75.00%
3 / 4
19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
8
 isSpd
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getL
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 solve
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
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 */
14declare(strict_types=1);
15
16namespace phpOMS\Math\Matrix;
17
18use 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 */
30final 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}