PluginProbe
TablePress – Tables in WordPress made easy / 2.3.1
TablePress – Tables in WordPress made easy v2.3.1
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / libraries / vendor / Matrix / Matrix.php

Matrix.php in TablePress – Tables in WordPress made easy 2.3.1, at libraries/vendor/Matrix/Matrix.php

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