PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.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 3.10.4 All 148 releases
visualizer / vendor / markbaker / matrix / classes / src / Matrix.php

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

424 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 *
5 * Class for the management of Matrices
6 *
7 * @copyright Copyright (c) 2018 Mark Baker (https://github.com/MarkBaker/PHPMatrix)
8 * @license https://opensource.org/licenses/MIT MIT
9 */
10
11 namespace Matrix;
12
13 use Generator;
14 use Matrix\Decomposition\LU;
15 use Matrix\Decomposition\QR;
16
17 /**
18 * Matrix object.
19 *
20 * @package Matrix
21 *
22 * @property-read int $rows The number of rows in the matrix
23 * @property-read int $columns The number of columns in the matrix
24 * @method Matrix antidiagonal()
25 * @method Matrix adjoint()
26 * @method Matrix cofactors()
27 * @method float determinant()
28 * @method Matrix diagonal()
29 * @method Matrix identity()
30 * @method Matrix inverse()
31 * @method Matrix pseudoInverse()
32 * @method Matrix minors()
33 * @method float trace()
34 * @method Matrix transpose()
35 * @method Matrix add(...$matrices)
36 * @method Matrix subtract(...$matrices)
37 * @method Matrix multiply(...$matrices)
38 * @method Matrix divideby(...$matrices)
39 * @method Matrix divideinto(...$matrices)
40 * @method Matrix directsum(...$matrices)
41 */
42 class Matrix
43 {
44 protected $rows;
45 protected $columns;
46 protected $grid = [];
47
48 /*
49 * Create a new Matrix object from an array of values
50 *
51 * @param array $grid
52 */
53 final public function __construct(array $grid)
54 {
55 $this->buildFromArray(array_values($grid));
56 }
57
58 /*
59 * Create a new Matrix object from an array of values
60 *
61 * @param array $grid
62 */
63 protected function buildFromArray(array $grid)
64 {
65 $this->rows = count($grid);
66 $columns = array_reduce(
67 $grid,
68 function ($carry, $value) {
69 return max($carry, is_array($value) ? count($value) : 1);
70 }
71 );
72 $this->columns = $columns;
73
74 array_walk(
75 $grid,
76 function (&$value) use ($columns) {
77 if (!is_array($value)) {
78 $value = [$value];
79 }
80 $value = array_pad(array_values($value), $columns, null);
81 }
82 );
83
84 $this->grid = $grid;
85 }
86
87 /**
88 * Validate that a row number is a positive integer
89 *
90 * @param int $row
91 * @return int
92 * @throws Exception
93 */
94 public static function validateRow($row)
95 {
96 if ((!is_numeric($row)) || (intval($row) < 1)) {
97 throw new Exception('Invalid Row');
98 }
99
100 return (int)$row;
101 }
102
103 /**
104 * Validate that a column number is a positive integer
105 *
106 * @param int $column
107 * @return int
108 * @throws Exception
109 */
110 public static function validateColumn($column)
111 {
112 if ((!is_numeric($column)) || (intval($column) < 1)) {
113 throw new Exception('Invalid Column');
114 }
115
116 return (int)$column;
117 }
118
119 /**
120 * Validate that a row number falls within the set of rows for this matrix
121 *
122 * @param int $row
123 * @return int
124 * @throws Exception
125 */
126 protected function validateRowInRange($row)
127 {
128 $row = static::validateRow($row);
129 if ($row > $this->rows) {
130 throw new Exception('Requested Row exceeds matrix size');
131 }
132
133 return $row;
134 }
135
136 /**
137 * Validate that a column number falls within the set of columns for this matrix
138 *
139 * @param int $column
140 * @return int
141 * @throws Exception
142 */
143 protected function validateColumnInRange($column)
144 {
145 $column = static::validateColumn($column);
146 if ($column > $this->columns) {
147 throw new Exception('Requested Column exceeds matrix size');
148 }
149
150 return $column;
151 }
152
153 /**
154 * Return a new matrix as a subset of rows from this matrix, starting at row number $row, and $rowCount rows
155 * A $rowCount value of 0 will return all rows of the matrix from $row
156 * A negative $rowCount value will return rows until that many rows from the end of the matrix
157 *
158 * Note that row numbers start from 1, not from 0
159 *
160 * @param int $row
161 * @param int $rowCount
162 * @return static
163 * @throws Exception
164 */
165 public function getRows($row, $rowCount = 1)
166 {
167 $row = $this->validateRowInRange($row);
168 if ($rowCount === 0) {
169 $rowCount = $this->rows - $row + 1;
170 }
171
172 return new static(array_slice($this->grid, $row - 1, (int)$rowCount));
173 }
174
175 /**
176 * Return a new matrix as a subset of columns from this matrix, starting at column number $column, and $columnCount columns
177 * A $columnCount value of 0 will return all columns of the matrix from $column
178 * A negative $columnCount value will return columns until that many columns from the end of the matrix
179 *
180 * Note that column numbers start from 1, not from 0
181 *
182 * @param int $column
183 * @param int $columnCount
184 * @return Matrix
185 * @throws Exception
186 */
187 public function getColumns($column, $columnCount = 1)
188 {
189 $column = $this->validateColumnInRange($column);
190 if ($columnCount < 1) {
191 $columnCount = $this->columns + $columnCount - $column + 1;
192 }
193
194 $grid = [];
195 for ($i = $column - 1; $i < $column + $columnCount - 1; ++$i) {
196 $grid[] = array_column($this->grid, $i);
197 }
198
199 return (new static($grid))->transpose();
200 }
201
202 /**
203 * Return a new matrix as a subset of rows from this matrix, dropping rows starting at row number $row,
204 * and $rowCount rows
205 * A negative $rowCount value will drop rows until that many rows from the end of the matrix
206 * A $rowCount value of 0 will remove all rows of the matrix from $row
207 *
208 * Note that row numbers start from 1, not from 0
209 *
210 * @param int $row
211 * @param int $rowCount
212 * @return static
213 * @throws Exception
214 */
215 public function dropRows($row, $rowCount = 1)
216 {
217 $this->validateRowInRange($row);
218 if ($rowCount === 0) {
219 $rowCount = $this->rows - $row + 1;
220 }
221
222 $grid = $this->grid;
223 array_splice($grid, $row - 1, (int)$rowCount);
224
225 return new static($grid);
226 }
227
228 /**
229 * Return a new matrix as a subset of columns from this matrix, dropping columns starting at column number $column,
230 * and $columnCount columns
231 * A negative $columnCount value will drop columns until that many columns from the end of the matrix
232 * A $columnCount value of 0 will remove all columns of the matrix from $column
233 *
234 * Note that column numbers start from 1, not from 0
235 *
236 * @param int $column
237 * @param int $columnCount
238 * @return static
239 * @throws Exception
240 */
241 public function dropColumns($column, $columnCount = 1)
242 {
243 $this->validateColumnInRange($column);
244 if ($columnCount < 1) {
245 $columnCount = $this->columns + $columnCount - $column + 1;
246 }
247
248 $grid = $this->grid;
249 array_walk(
250 $grid,
251 function (&$row) use ($column, $columnCount) {
252 array_splice($row, $column - 1, (int)$columnCount);
253 }
254 );
255
256 return new static($grid);
257 }
258
259 /**
260 * Return a value from this matrix, from the "cell" identified by the row and column numbers
261 * Note that row and column numbers start from 1, not from 0
262 *
263 * @param int $row
264 * @param int $column
265 * @return mixed
266 * @throws Exception
267 */
268 public function getValue($row, $column)
269 {
270 $row = $this->validateRowInRange($row);
271 $column = $this->validateColumnInRange($column);
272
273 return $this->grid[$row - 1][$column - 1];
274 }
275
276 /**
277 * Returns a Generator that will yield each row of the matrix in turn as a vector matrix
278 * or the value of each cell if the matrix is a column vector
279 *
280 * @return Generator|Matrix[]|mixed[]
281 */
282 public function rows()
283 {
284 foreach ($this->grid as $i => $row) {
285 yield $i + 1 => ($this->columns == 1)
286 ? $row[0]
287 : new static([$row]);
288 }
289 }
290
291 /**
292 * Returns a Generator that will yield each column of the matrix in turn as a vector matrix
293 * or the value of each cell if the matrix is a row vector
294 *
295 * @return Generator|Matrix[]|mixed[]
296 */
297 public function columns()
298 {
299 for ($i = 0; $i < $this->columns; ++$i) {
300 yield $i + 1 => ($this->rows == 1)
301 ? $this->grid[0][$i]
302 : new static(array_column($this->grid, $i));
303 }
304 }
305
306 /**
307 * Identify if the row and column dimensions of this matrix are equal,
308 * i.e. if it is a "square" matrix
309 *
310 * @return bool
311 */
312 public function isSquare()
313 {
314 return $this->rows == $this->columns;
315 }
316
317 /**
318 * Identify if this matrix is a vector
319 * i.e. if it comprises only a single row or a single column
320 *
321 * @return bool
322 */
323 public function isVector()
324 {
325 return $this->rows == 1 || $this->columns == 1;
326 }
327
328 /**
329 * Return the matrix as a 2-dimensional array
330 *
331 * @return array
332 */
333 public function toArray()
334 {
335 return $this->grid;
336 }
337
338 /**
339 * Solve A*X = B.
340 *
341 * @param Matrix $B Right hand side
342 *
343 * @throws Exception
344 *
345 * @return Matrix ... Solution if A is square, least squares solution otherwise
346 */
347 public function solve(Matrix $B)
348 {
349 if ($this->columns === $this->rows) {
350 return (new LU($this))->solve($B);
351 }
352
353 return (new QR($this))->solve($B);
354 }
355
356 protected static $getters = [
357 'rows',
358 'columns',
359 ];
360
361 /**
362 * Access specific properties as read-only (no setters)
363 *
364 * @param string $propertyName
365 * @return mixed
366 * @throws Exception
367 */
368 public function __get($propertyName)
369 {
370 $propertyName = strtolower($propertyName);
371
372 // Test for function calls
373 if (in_array($propertyName, self::$getters)) {
374 return $this->$propertyName;
375 }
376
377 throw new Exception('Property does not exist');
378 }
379
380 protected static $functions = [
381 'antidiagonal',
382 'adjoint',
383 'cofactors',
384 'determinant',
385 'diagonal',
386 'identity',
387 'inverse',
388 'minors',
389 'trace',
390 'transpose',
391 ];
392
393 protected static $operations = [
394 'add',
395 'subtract',
396 'multiply',
397 'divideby',
398 'divideinto',
399 'directsum',
400 ];
401
402 /**
403 * Returns the result of the function call or operation
404 *
405 * @param string $functionName
406 * @param mixed[] $arguments
407 * @return Matrix|float
408 * @throws Exception
409 */
410 public function __call($functionName, $arguments)
411 {
412 $functionName = strtolower(str_replace('_', '', $functionName));
413
414 if (in_array($functionName, self::$functions, true) || in_array($functionName, self::$operations, true)) {
415 $functionName = "\\" . __NAMESPACE__ . "\\{$functionName}";
416 if (is_callable($functionName)) {
417 $arguments = array_values(array_merge([$this], $arguments));
418 return call_user_func_array($functionName, $arguments);
419 }
420 }
421 throw new Exception('Function or Operation does not exist');
422 }
423 }
424