/** * Perform Guass Jordan Elimination on the two supplied matrices * * @param NumericMatrix $mA First matrix to act on - required * @param NumericMatrix $extra Second matrix to act upon - required * * @return \Chippyash\Math\Matrix\DecompositionAbstractDecomposition Fluent Interface * * @throws \Chippyash\Math\Matrix\Exceptions\SingularMatrixException */ public function decompose(NumericMatrix $mA, $extra = null) { $this->assertParameterIsMatrix($extra, 'Parameter extra is not a matrix')->assertMatrixIsNumeric($extra, 'Parameter extra is not a numeric matrix')->assertMatrixIsSquare($mA, 'Parameter mA is not a square matrix')->assertMatrixRowsAreEqual($mA, $extra, 'mA->rows != extra->rows'); $rows = $mA->rows(); $dA = $mA->toArray(); $dB = $extra->toArray(); $zero = function () { return RationalTypeFactory::create(0, 1); }; $one = function () { return RationalTypeFactory::create(1, 1); }; $calc = new Calculator(); $comp = new Comparator(); $ipiv = array_fill(0, $rows, $zero()); $indxr = array_fill(0, $rows, 0); $indxc = array_fill(0, $rows, 0); // find the pivot element by searching the entire matrix for its largest value, but excluding columns already reduced. $irow = $icol = 0; for ($i = 0; $i < $rows; $i++) { $big = $zero(); for ($j = 0; $j < $rows; $j++) { if ($comp->neq($ipiv[$j], $one())) { for ($k = 0; $k < $rows; $k++) { if ($comp->eq($ipiv[$k], $zero())) { $absVal = $dA[$j][$k]->abs(); if ($comp->gt($absVal, $big)) { unset($big); $big = clone $absVal; $irow = $j; $icol = $k; } } elseif ($comp->gt($ipiv[$k], $one())) { throw new SingularMatrixException('GaussJordanElimination'); } } } } //Now interchange row to move the pivot element to a diagonal $ipiv[$icol] = $calc->add($ipiv[$icol], $one()); if ($irow != $icol) { $this->swapRows($dA, $irow, $icol); $this->swapRows($dB, $irow, $icol); } // Remember permutations to later $indxr[$i] = $irow; $indxc[$i] = $icol; if ($comp->eq($dA[$icol][$icol], $zero())) { throw new SingularMatrixException('GaussJordanElimination'); } // Now divide the found row to make the pivot element 1 // To maintain arithmetic integrity we use the reciprocal to multiply by $pivinv = $calc->reciprocal($dA[$icol][$icol]); $this->multRow($dA, $icol, $pivinv, $calc); $this->multRow($dB, $icol, $pivinv, $calc); // And reduce all other rows but the pivoted row with the value of the pivot row for ($ll = 0; $ll < $rows; $ll++) { if ($ll != $icol) { $multiplier = clone $dA[$ll][$icol]; $multiplier->negate(); $this->addMultipleOfOtherRowToRow($dA, $multiplier, $icol, $ll, $calc); $this->addMultipleOfOtherRowToRow($dB, $multiplier, $icol, $ll, $calc); } } } $this->set('left', $this->createCorrectMatrixType($mA, $dA)); $this->set('right', $this->createCorrectMatrixType($extra, $dB)); return clone $this; }
/** * LU Decomposition constructor. * * @param \Chippyash\Math\Matrix\NumericMatrix $mA */ protected function LUDecomposition(NumericMatrix $mA) { // Use a "left-looking", dot-product, Crout/Doolittle algorithm. $LU = $mA->toArray(); $m = $this->rows = $mA->rows(); $n = $this->cols = $mA->columns(); for ($i = 0; $i < $m; $i++) { $this->piv[$i] = $i; } $this->pivsign = 1; $LUrowi = []; $LUcolj = []; $calc = new Calculator(); $comp = new Comparator(); $zeroInt = $this->createCorrectScalarType($mA, 0); // Outer loop. for ($j = 0; $j < $n; $j++) { // Make a copy of the j-th column to localize references. for ($i = 0; $i < $m; $i++) { $LUcolj[$i] =& $LU[$i][$j]; } // Apply previous transformations. for ($i = 0; $i < $m; $i++) { $LUrowi = $LU[$i]; // Most of the time is spent in the following dot product. $kmax = min($i, $j); $s = clone $zeroInt; for ($k = 0; $k < $kmax; $k++) { $s = $calc->add($s, $calc->mul($LUrowi[$k], $LUcolj[$k])); } $LUcolj[$i] = $calc->sub($LUcolj[$i], $s); $LUrowi[$j] = $LUcolj[$i]; } // Find pivot and exchange if necessary. $p = $j; for ($i = $j + 1; $i < $m; $i++) { if ($comp->gt($LUcolj[$i]->abs(), $LUcolj[$p]->abs())) { $p = $i; } } if ($p != $j) { for ($k = 0; $k < $n; $k++) { //swap $t = $LU[$p][$k]; $LU[$p][$k] = $LU[$j][$k]; $LU[$j][$k] = $t; } $k = $this->piv[$p]; $this->piv[$p] = $this->piv[$j]; $this->piv[$j] = $k; $this->pivsign = $this->pivsign * -1; } // Compute multipliers. if ($j < $m && $comp->neq($LU[$j][$j], $zeroInt)) { for ($i = $j + 1; $i < $m; $i++) { $LU[$i][$j] = $calc->div($LU[$i][$j], $LU[$j][$j]); } } } $this->set('LU', $this->createCorrectMatrixType($mA, $LU)); }