/** * Calculates lower triangular matrix L of given symmetric positive definite matrix * * @param NumArray $squareMatrix symmetric positive definite matrix * * @return NumArray * * @throws InvalidArgumentException will be thrown, when `$squareMatrix` is not positive definite * @throws NoSquareMatrixException will be thrown, when `$squareMatrix` is not square * @throws NoSymmetricMatrixException will be thrown, when `$squareMatrix` is not symmetric * * @since 1.0.2 */ public static function cholesky(NumArray $squareMatrix) { if (!Helper::isSquareMatrix($squareMatrix)) { throw new NoSquareMatrixException(sprintf("Matrix with shape (%s) given, matrix has to be square", implode(', ', $squareMatrix->getShape()))); } $shape = $squareMatrix->getShape(); $size = $shape[0]; $aMatrix = clone $squareMatrix; $lMatrix = NumPHP::zerosLike($aMatrix); for ($k = 0; $k < $size; $k++) { $diaElem = $aMatrix->get($k, $k)->getData(); if ($diaElem <= 0) { throw new InvalidArgumentException("Matrix is not positive definite"); } $diaElem = sqrt($diaElem); $lMatrix->set($k, $k, $diaElem); for ($i = $k + 1; $i < $size; $i++) { if ($squareMatrix->get($i, $k) != $squareMatrix->get($k, $i)) { throw new NoSymmetricMatrixException("Matrix is not symmetric"); } $lMatrix->set($i, $k, $aMatrix->get($i, $k)->div($diaElem)); for ($j = $k + 1; $j <= $i; $j++) { $aMatrix->set($i, $j, $aMatrix->get($i, $j)->sub($lMatrix->get($i, $k)->mult($lMatrix->get($j, $k)))); } } } return $lMatrix; }
/** * Factors a matrix into a pivot matrix, a lower and upper triangular matrix * * @param NumArray $array matrix * * @throws NoMatrixException will be thrown, if `array` is no matrix * * @return array * * @since 1.0.0 */ public static function lud(NumArray $array) { if (!Helper::isMatrix($array)) { throw new NoMatrixException(sprintf("NumArray with dimension %d given, NumArray should have 2 dimensions", $array->getNDim())); } $numArray = clone $array; $shape = $numArray->getShape(); $mAxis = $shape[0]; $nAxis = $shape[1]; $size = min($mAxis, $nAxis); $pArray = range(0, $mAxis - 1); $lMatrix = NumPHP::zeros($mAxis, $size); for ($i = 0; $i < $size; $i++) { // pivoting $maxIndex = self::getPivotIndex($numArray, $i); if ($maxIndex !== $i) { $temp = $numArray->get($i); $numArray->set($i, $numArray->get($maxIndex)); $numArray->set($maxIndex, $temp); $temp = $lMatrix->get($i); $lMatrix->set($i, $lMatrix->get($maxIndex)); $lMatrix->set($maxIndex, $temp); $temp = $pArray[$i]; $pArray[$i] = $pArray[$maxIndex]; $pArray[$maxIndex] = $temp; } // elimination for ($j = $i + 1; $j < $mAxis; $j++) { $fac = 0; $facNumerator = $numArray->get($j, $i)->getData(); $facDenominator = $numArray->get($i, $i)->getData(); if ($facDenominator) { $fac = $facNumerator / $facDenominator; } $lMatrix->set($j, $i, $fac); $slice = sprintf("%d:", $i + 1); $numArray->set($j, $slice, $numArray->get($j, $slice)->sub($numArray->get($i, $slice)->mult($fac))); } } return [self::buildPivotMatrix($pArray), self::buildLMatrix($lMatrix), self::buildUMatrix($numArray)]; }
/** * Calculates the inverse of a not singular square matrix * * @param mixed $squareMatrix not singular matrix * * @throws SingularMatrixException will be thrown, when `$squareMatrix` is singular * * @api * @since 1.0.0 * * @return NumArray */ public static function inv($squareMatrix) { if (!$squareMatrix instanceof NumArray) { $squareMatrix = new NumArray($squareMatrix); } elseif ($squareMatrix->inCache(self::CACHE_KEY_INVERSE)) { return $squareMatrix->getCache(self::CACHE_KEY_INVERSE); } if (!Helper::isNotSingularMatrix($squareMatrix)) { throw new SingularMatrixException("Matrix is singular"); } $shape = $squareMatrix->getShape(); $inv = self::solve($squareMatrix, NumPHP::identity($shape[0])); $squareMatrix->setCache(self::CACHE_KEY_INVERSE, $inv); return self::inv($squareMatrix); }
/** * Tests if Helper::isNotSingularMatrix works with invalid not singular matrix */ public function testCheckNotSingularMatrixInvalid() { $numArray = NumPHP::identity(4); $numArray->set(2, 2, 0); $this->assertFalse(Helper::isNotSingularMatrix($numArray)); }
/** * Back Substitution solves a linear system with a upper triangular matrix of * size n*n and a vector of size n or a matrix of size n*m * * @param NumArray $uMatrix upper triangular matrix of size n*n * @param NumArray $numArray vector of size n or matrix of size n*m * * @return NumArray * * @since 1.0.0 */ protected static function backSubstitution(NumArray $uMatrix, NumArray $numArray) { $shape = $numArray->getShape(); if (Helper::isVector($numArray)) { $xVector = NumPHP::zerosLike($numArray); for ($i = $shape[0] - 1; $i >= 0; $i--) { $slice = sprintf("%d:%d", $i + 1, $shape[0]); $sum = $uMatrix->get($i, $slice)->dot($xVector->get($slice)); $xVector->set($i, $numArray->get($i)->sub($sum)->div($uMatrix->get($i, $i))); } return $xVector; } // $numArray is a matrix $copy = clone $numArray; for ($i = 0; $i < $shape[1]; $i++) { $copy->set(':', $i, self::backSubstitution($uMatrix, $copy->get(':', $i))); } return $copy; }