PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.5.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.5.0
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / markbaker / matrix / classes / src / Decomposition / QR.php

QR.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.5.0, at vendor/markbaker/matrix/classes/src/Decomposition/QR.php

195 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Matrix\Decomposition;
4
5 use Matrix\Exception;
6 use Matrix\Matrix;
7
8 class QR
9 {
10 private $qrMatrix;
11 private $rows;
12 private $columns;
13
14 private $rDiagonal = [];
15
16 public function __construct(Matrix $matrix)
17 {
18 $this->qrMatrix = $matrix->toArray();
19 $this->rows = $matrix->rows;
20 $this->columns = $matrix->columns;
21
22 $this->decompose();
23 }
24
25 public function getHouseholdVectors()
26 {
27 $householdVectors = [];
28 for ($row = 0; $row < $this->rows; ++$row) {
29 for ($column = 0; $column < $this->columns; ++$column) {
30 if ($row >= $column) {
31 $householdVectors[$row][$column] = $this->qrMatrix[$row][$column];
32 } else {
33 $householdVectors[$row][$column] = 0.0;
34 }
35 }
36 }
37
38 return new Matrix($householdVectors);
39 }
40
41 public function getQ()
42 {
43 $qGrid = [];
44
45 $rowCount = $this->rows;
46 for ($k = $this->columns - 1; $k >= 0; --$k) {
47 for ($i = 0; $i < $this->rows; ++$i) {
48 $qGrid[$i][$k] = 0.0;
49 }
50 $qGrid[$k][$k] = 1.0;
51 if ($this->columns > $this->rows) {
52 $qGrid = array_slice($qGrid, 0, $this->rows);
53 }
54
55 for ($j = $k; $j < $this->columns; ++$j) {
56 if (isset($this->qrMatrix[$k], $this->qrMatrix[$k][$k]) && $this->qrMatrix[$k][$k] != 0.0) {
57 $s = 0.0;
58 for ($i = $k; $i < $this->rows; ++$i) {
59 $s += $this->qrMatrix[$i][$k] * $qGrid[$i][$j];
60 }
61 $s = -$s / $this->qrMatrix[$k][$k];
62 for ($i = $k; $i < $this->rows; ++$i) {
63 $qGrid[$i][$j] += $s * $this->qrMatrix[$i][$k];
64 }
65 }
66 }
67 }
68
69 array_walk(
70 $qGrid,
71 function (&$row) use ($rowCount) {
72 $row = array_reverse($row);
73 $row = array_slice($row, 0, $rowCount);
74 }
75 );
76
77 return new Matrix($qGrid);
78 }
79
80 public function getR()
81 {
82 $rGrid = [];
83
84 for ($row = 0; $row < $this->columns; ++$row) {
85 for ($column = 0; $column < $this->columns; ++$column) {
86 if ($row < $column) {
87 $rGrid[$row][$column] = isset($this->qrMatrix[$row][$column]) ? $this->qrMatrix[$row][$column] : 0.0;
88 } elseif ($row === $column) {
89 $rGrid[$row][$column] = isset($this->rDiagonal[$row]) ? $this->rDiagonal[$row] : 0.0;
90 } else {
91 $rGrid[$row][$column] = 0.0;
92 }
93 }
94 }
95
96 if ($this->columns > $this->rows) {
97 $rGrid = array_slice($rGrid, 0, $this->rows);
98 }
99
100 return new Matrix($rGrid);
101 }
102
103 private function hypo($a, $b)
104 {
105 if (abs($a) > abs($b)) {
106 $r = $b / $a;
107 $r = abs($a) * sqrt(1 + $r * $r);
108 } elseif ($b != 0.0) {
109 $r = $a / $b;
110 $r = abs($b) * sqrt(1 + $r * $r);
111 } else {
112 $r = 0.0;
113 }
114
115 return $r;
116 }
117
118 /**
119 * QR Decomposition computed by Householder reflections.
120 */
121 private function decompose()
122 {
123 for ($k = 0; $k < $this->columns; ++$k) {
124 // Compute 2-norm of k-th column without under/overflow.
125 $norm = 0.0;
126 for ($i = $k; $i < $this->rows; ++$i) {
127 $norm = $this->hypo($norm, $this->qrMatrix[$i][$k]);
128 }
129 if ($norm != 0.0) {
130 // Form k-th Householder vector.
131 if ($this->qrMatrix[$k][$k] < 0.0) {
132 $norm = -$norm;
133 }
134 for ($i = $k; $i < $this->rows; ++$i) {
135 $this->qrMatrix[$i][$k] /= $norm;
136 }
137 $this->qrMatrix[$k][$k] += 1.0;
138 // Apply transformation to remaining columns.
139 for ($j = $k + 1; $j < $this->columns; ++$j) {
140 $s = 0.0;
141 for ($i = $k; $i < $this->rows; ++$i) {
142 $s += $this->qrMatrix[$i][$k] * $this->qrMatrix[$i][$j];
143 }
144 $s = -$s / $this->qrMatrix[$k][$k];
145 for ($i = $k; $i < $this->rows; ++$i) {
146 $this->qrMatrix[$i][$j] += $s * $this->qrMatrix[$i][$k];
147 }
148 }
149 }
150 $this->rDiagonal[$k] = -$norm;
151 }
152 }
153
154 /**
155 * @return bool
156 */
157 public function isFullRank()
158 {
159 for ($j = 0; $j < $this->columns; ++$j) {
160 if ($this->rDiagonal[$j] == 0.0) {
161 return false;
162 }
163 }
164
165 return true;
166 }
167
168 /**
169 * Least squares solution of A*X = B.
170 *
171 * @param Matrix $B a Matrix with as many rows as A and any number of columns
172 *
173 * @throws Exception
174 *
175 * @return Matrix matrix that minimizes the two norm of Q*R*X-B
176 */
177 public function solve(Matrix $B)
178 {
179 if ($B->rows !== $this->rows) {
180 throw new Exception('Matrix row dimensions are not equal');
181 }
182
183 if (!$this->isFullRank()) {
184 throw new Exception('Can only perform this operation on a full-rank matrix');
185 }
186
187 // Compute Y = transpose(Q)*B
188 $Y = $this->getQ()->transpose()
189 ->multiply($B);
190 // Solve R*X = Y;
191 return $this->getR()->inverse()
192 ->multiply($Y);
193 }
194 }
195