/** * @param Matrix $source * @throws MatrixException */ private function decompose(Matrix $source) { $decompositionLiteral = $source->toArray(); if (!$source->isSquare()) { throw new MatrixException('Operation can only be called on square matrix: ' . print_r($decompositionLiteral, true)); } $size = $source->getRowCount(); for ($k = 0; $k < $size; $k++) { for ($i = $k + 1; $i < $size; $i++) { $decompositionLiteral[$i][$k] = $decompositionLiteral[$i][$k] / $decompositionLiteral[$k][$k]; } for ($i = $k + 1; $i < $size; $i++) { for ($j = $k + 1; $j < $size; $j++) { $decompositionLiteral[$i][$j] = $decompositionLiteral[$i][$j] - $decompositionLiteral[$i][$k] * $decompositionLiteral[$k][$j]; } } } $this->decomposition = new Matrix($decompositionLiteral); }
/** * @param Matrix $source * @throws MatrixException */ private function decompose(Matrix $source) { $sourceLiteral = $source->toArray(); $decompositionLiteral = $sourceLiteral; if (!$source->isSquare()) { throw new MatrixException('Operation can only be called on square matrix: ' . print_r($decompositionLiteral, true)); } $size = $source->getRowCount(); $this->permutation = range(0, $size - 1); for ($k = 0; $k < $size; $k++) { $p = 0.0; $kPrime = $k; for ($i = $k; $i < $size; $i++) { $absolute = abs($decompositionLiteral[$i][$k]); if ($absolute > $p) { $p = $absolute; $kPrime = $i; } } if ($p === 0.0) { throw new MatrixException('Cannot take the LUP decomposition of a singular matrix: ' . print_r($sourceLiteral, true)); } if ($k !== $kPrime) { list($this->permutation[$k], $this->permutation[$kPrime]) = [$this->permutation[$kPrime], $this->permutation[$k]]; $this->parity++; } for ($i = 0; $i < $size; $i++) { list($decompositionLiteral[$k][$i], $decompositionLiteral[$kPrime][$i]) = [$decompositionLiteral[$kPrime][$i], $decompositionLiteral[$k][$i]]; } for ($i = $k + 1; $i < $size; $i++) { $decompositionLiteral[$i][$k] = $decompositionLiteral[$i][$k] / $decompositionLiteral[$k][$k]; for ($j = $k + 1; $j < $size; $j++) { $decompositionLiteral[$i][$j] = $decompositionLiteral[$i][$j] - $decompositionLiteral[$i][$k] * $decompositionLiteral[$k][$j]; } } } $this->decomposition = new Matrix($decompositionLiteral); }
/** * @param self $source * @return self */ private function recursiveSolveInverse(self $source) : self { $size = $source->getRowCount(); if ($size === 1) { return new static([[1 / $source->get(0, 0)]]); } $half = (int) ($size / 2); // Partition source matrix. $B = $source->sliceRows(0, $half)->sliceColumns(0, $half); $CT = $source->sliceRows(0, $half)->sliceColumns($half); $D = $source->sliceRows($half)->sliceColumns($half); $C = $source->sliceRows($half)->sliceColumns(0, $half); // Handle intermediate calculations. $Binv = $this->recursiveSolveInverse($B); $W = $C->multiplyMatrix($Binv); $WT = $W->transpose(); $X = $W->multiplyMatrix($CT); $S = $D->subtractMatrix($X); $Sinv = $this->recursiveSolveInverse($S); $V = $Sinv; $Y = $Sinv->multiplyMatrix($W); $YT = $Y->transpose(); $T = $YT->multiplyScalar(-1); $U = $Y->multiplyScalar(-1); $Z = $WT->multiplyMatrix($Y); $R = $Binv->addMatrix($Z); // Stitch together intermediate results into the final result return $R->concatenateRight($T)->concatenateBottom($U->concatenateRight($V)); }
/** * @param Observations $observations * @return array * @throws InvalidArgumentException */ public function regress(Observations $observations) : array { $design = new Matrix($observations->getFeatures()); $observed = (new Matrix([$observations->getOutcomes()]))->transpose(); if ($design->getRowCount() < $design->getColumnCount()) { throw new InvalidArgumentException('Not enough observations to perform regression. You need to have more observations than explanatory variables.'); } $designTranspose = $design->transpose(); $prediction = $designTranspose->multiplyMatrix($design)->inverse()->multiplyMatrix($designTranspose->multiplyMatrix($observed)); return $prediction->transpose()->toArray()[0]; }