/** * 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; }
/** * Invert the matrix * * $mA must be square and nonsingular. * An empty $mA returns an empty matrix * A single entry matrix e.g. [[n]] returns [[1/n]] * A zero single item matrix e.g. [[0]] throws an exception (Division by zero) * * @param Chippyash\Math\Matrix\NumericMatrix $mA First matrix operand - required * @param mixed $extra ignored * * @return Chippyash\Math\Matrix\NumericMatrix * * @throws Chippyash\Matrix\Exceptions\ComputationException * @throws Chippyash\Matrix\Exceptions\UndefinedComputationException */ protected function doTransform(Matrix $mA, $extra = null) { $this->assertMatrixIsNumeric($mA); if ($mA->is('empty')) { return $this->createCorrectMatrixType($mA); } if ($mA->is('singleitem')) { $item = $mA->get(1, 1)->get(); if ($item == 0 || $item == '0+0i') { throw new ComputationException('Division by zero'); } else { $calc = new Calculator(); return $this->createCorrectMatrixType($mA, [$calc->reciprocal($mA->get(1, 1))]); } } $this->assertMatrixIsNonSingular($mA, 'Can only perform inversion on non singular matrix'); $I = $this->invert($mA); return $I; }