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