綟N5NRvBomԄjFq9yΣ]a+23.w2RXT:w^^+ jc0~P)5Q!\^+BͿ;[Bff_QagG0nw%9JB Λ㌒*>mQ:{͐'Nl)m7WK >upu%,Ř՛#4 $!ZSzaǬTV2?S~"vFj{,Ō&5e6:^j:7{RCYͭS mlY t`3-w7HQZReIkZmiwlA{m7kʌ`Ze6T,h.A٣{ap"k7G;0%r.29RF5'0c{RCYͭSi^ļqW A٣{ap"kw5!6L135ha$:ZӴzNkY*VfZx~x5&r4= ¾bvpBe 4J@lP˭٤ñC( ;!G.Ua=xΣ@`U#8kvi i-dnb' dݐkړΪ|A*"-&^:60KRz_2R/!΢KY}H)EtD@>Smy!hݰ J+6ejVMfG/O7q1nkM2wY 0668I vXe{κ|} ?iZl9DžK?'}Q>M36-jܩyxf_A_pb9.X#uBsUYzҰpN[G9kq~j[zew^l祡ӏ(P ;䐤Q|06Z?Ƞ5SxNT$_lJ3dž;)[۶z=A&q&YV"H/>Z8@ԁk|[lJs`}XF#Jp9~G Eȍ2 9OӜXVպG"o~7t!bAˠJ^m{ 4U{I?_O낵V{k**MOJiPl)wD@zt#/-Dm^kvbc4D)Q. IfIUє+=+a_p UOjaV( ,gah&p7BS{ja3A7S]PrQt#IXΙXB^mE5틔RMY6A@ݠl'>Ӹ]ة * @return static * @throws Exception */ public function getRows(int $row, int $rowCount = 1): Matrix { $row = $this->validateRowInRange($row); if ($rowCount === 0) { $rowCount = $this->rows - $row + 1; } return new static(array_slice($this->grid, $row - 1, (int)$rowCount)); } /** * Return a new matrix as a subset of columns from this matrix, starting at column number $column, and $columnCount columns * A $columnCount value of 0 will return all columns of the matrix from $column * A negative $columnCount value will return columns until that many columns from the end of the matrix * * Note that column numbers start from 1, not from 0 * * @param int $column * @param int $columnCount * @return Matrix * @throws Exception */ public function getColumns(int $column, int $columnCount = 1): Matrix { $column = $this->validateColumnInRange($column); if ($columnCount < 1) { $columnCount = $this->columns + $columnCount - $column + 1; } $grid = []; for ($i = $column - 1; $i < $column + $columnCount - 1; ++$i) { $grid[] = array_column($this->grid, $i); } return (new static($grid))->transpose(); } /** * Return a new matrix as a subset of rows from this matrix, dropping rows starting at row number $row, * and $rowCount rows * A negative $rowCount value will drop rows until that many rows from the end of the matrix * A $rowCount value of 0 will remove all rows of the matrix from $row * * Note that row numbers start from 1, not from 0 * * @param int $row * @param int $rowCount * @return static * @throws Exception */ public function dropRows(int $row, int $rowCount = 1): Matrix { $this->validateRowInRange($row); if ($rowCount === 0) { $rowCount = $this->rows - $row + 1; } $grid = $this->grid; array_splice($grid, $row - 1, (int)$rowCount); return new static($grid); } /** * Return a new matrix as a subset of columns from this matrix, dropping columns starting at column number $column, * and $columnCount columns * A negative $columnCount value will drop columns until that many columns from the end of the matrix * A $columnCount value of 0 will remove all columns of the matrix from $column * * Note that column numbers start from 1, not from 0 * * @param int $column * @param int $columnCount * @return static * @throws Exception */ public function dropColumns(int $column, int $columnCount = 1): Matrix { $this->validateColumnInRange($column); if ($columnCount < 1) { $columnCount = $this->columns + $columnCount - $column + 1; } $grid = $this->grid; array_walk( $grid, function (&$row) use ($column, $columnCount) { array_splice($row, $column - 1, (int)$columnCount); } ); return new static($grid); } /** * Return a value from this matrix, from the "cell" identified by the row and column numbers * Note that row and column numbers start from 1, not from 0 * * @param int $row * @param int $column * @return mixed * @throws Exception */ public function getValue(int $row, int $column) { $row = $this->validateRowInRange($row); $column = $this->validateColumnInRange($column); return $this->grid[$row - 1][$column - 1]; } /** * Returns a Generator that will yield each row of the matrix in turn as a vector matrix * or the value of each cell if the matrix is a column vector * * @return Generator|Matrix[]|mixed[] */ public function rows(): Generator { foreach ($this->grid as $i => $row) { yield $i + 1 => ($this->columns == 1) ? $row[0] : new static([$row]); } } /** * Returns a Generator that will yield each column of the matrix in turn as a vector matrix * or the value of each cell if the matrix is a row vector * * @return Generator|Matrix[]|mixed[] */ public function columns(): Generator { for ($i = 0; $i < $this->columns; ++$i) { yield $i + 1 => ($this->rows == 1) ? $this->grid[0][$i] : new static(array_column($this->grid, $i)); } } /** * Identify if the row and column dimensions of this matrix are equal, * i.e. if it is a "square" matrix * * @return bool */ public function isSquare(): bool { return $this->rows === $this->columns; } /** * Identify if this matrix is a vector * i.e. if it comprises only a single row or a single column * * @return bool */ public function isVector(): bool { return $this->rows === 1 || $this->columns === 1; } /** * Return the matrix as a 2-dimensional array * * @return array */ public function toArray(): array { return $this->grid; } /** * Solve A*X = B. * * @param Matrix $B Right hand side * * @throws Exception * * @return Matrix ... Solution if A is square, least squares solution otherwise */ public function solve(Matrix $B): Matrix { if ($this->columns === $this->rows) { return (new LU($this))->solve($B); } return (new QR($this))->solve($B); } protected static $getters = [ 'rows', 'columns', ]; /** * Access specific properties as read-only (no setters) * * @param string $propertyName * @return mixed * @throws Exception */ public function __get(string $propertyName) { $propertyName = strtolower($propertyName); // Test for function calls if (in_array($propertyName, self::$getters)) { return $this->$propertyName; } throw new Exception('Property does not exist'); } protected static $functions = [ 'adjoint', 'antidiagonal', 'cofactors', 'determinant', 'diagonal', 'identity', 'inverse', 'minors', 'trace', 'transpose', ]; protected static $operations = [ 'add', 'subtract', 'multiply', 'divideby', 'divideinto', 'directsum', ]; /** * Returns the result of the function call or operation * * @param string $functionName * @param mixed[] $arguments * @return Matrix|float * @throws Exception */ public function __call(string $functionName, $arguments) { $functionName = strtolower(str_replace('_', '', $functionName)); // Test for function calls if (in_array($functionName, self::$functions, true)) { return Functions::$functionName($this, ...$arguments); } // Test for operation calls if (in_array($functionName, self::$operations, true)) { return Operations::$functionName($this, ...$arguments); } throw new Exception('Function or Operation does not exist'); } }