From 9801d86b61f2f644c34ef61c0da7cda6c1241732 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Fri, 20 Apr 2018 19:35:57 +0200 Subject: [PATCH] Fix complex and matrix --- Math/Matrix/Matrix.php | 240 +++++++++--------------------- Math/Number/Complex.php | 4 +- tests/Math/Matrix/MatrixTest.php | 51 ++++++- tests/Math/Number/ComplexTest.php | 2 +- 4 files changed, 119 insertions(+), 178 deletions(-) diff --git a/Math/Matrix/Matrix.php b/Math/Matrix/Matrix.php index d6ba2b464..0e7549a51 100644 --- a/Math/Matrix/Matrix.php +++ b/Math/Matrix/Matrix.php @@ -167,7 +167,74 @@ class Matrix implements \ArrayAccess, \Iterator */ public function rank() : int { - return 0; + $matrix = $this->matrix; + $mDim = $this->m; + $nDim = $this->n; + + if ($this->m > $this->n) { + $mDim = $this->n; + $nDim = $this->m; + $matrix = array_map(null, ...$matrix); + } + + $rank = $mDim; + + for ($row = 0; $row < $rank; ++$row) { + if (isset($matrix[$row][$row]) && $matrix[$row][$row] !== 0) { + for ($col = 0; $col < $mDim; ++$col) { + if ($col !== $row) { + $mult = $matrix[$col][$row] / $matrix[$row][$row]; + + for ($i = 0; $i < $rank; ++$i) { + $matrix[$col][$i] -= $mult * $matrix[$row][$i]; + } + } + } + } else { + $reduce = true; + + for ($i = $row + 1; $i < $mDim; ++$i) { + if (isset($matrix[$i][$row]) && $matrix[$i][$row] !== 0) { + $this->swapRow($matrix, $row, $i, $rank); + $reduce = false; + break; + } + } + + if ($reduce) { + --$rank; + + for ($i = 0; $i < $mDim; ++$i) { + $matrix[$i][$row] = $matrix[$i][$rank]; + } + + --$row; + } + } + } + + return $rank; + } + + /** + * Swap values in rows + * + * @param array $matrix Matrix reference to modify + * @param int $row1 Row to swap + * @param int $row2 Row to swap + * @param int $col Max col to swap to + * + * @return void + * + * @since 1.0.0 + */ + private function swapRow(array &$matrix, int $row1, int $row2, int $col) : void + { + for ($i = 0; $i < $col; ++$i) { + $temp = $matrix[$row1][$i]; + $matrix[$row1] = $matrix[$row2][$i]; + $matrix[$row2] = $temp; + } } /** @@ -436,7 +503,7 @@ class Matrix implements \ArrayAccess, \Iterator */ private function upperTrianglize(array &$arr) : int { - $n = count($arr); + $n = $this->n; $sign = 1; for ($i = 0; $i < $n; ++$i) { @@ -504,70 +571,6 @@ class Matrix implements \ArrayAccess, \Iterator return $this->solve(new IdentityMatrix($this->m)); } - /** - * Inverse matrix using gauss jordan. - * - * @return Matrix - * - * @since 1.0.0 - */ - private function inverseGaussJordan() : Matrix - { - $newMatrixArr = $this->matrix; - - // extending matrix by identity matrix - for ($i = 0; $i < $this->n; ++$i) { - for ($j = $this->n; $j < $this->n * 2; ++$j) { - - if ($j === ($i + $this->n)) { - $newMatrixArr[$i][$j] = 1; - } else { - $newMatrixArr[$i][$j] = 0; - } - } - } - - $mDim = count($newMatrixArr); - $nDim = count($newMatrixArr[0]); - - // pivoting - $newMatrixArr = $this->diag($newMatrixArr); - - /* create unit matrix */ - for ($i = 0; $i < $mDim; ++$i) { - $temp = $newMatrixArr[$i][$i]; - - for ($j = 0; $j < $nDim; ++$j) { - $newMatrixArr[$i][$j] = $newMatrixArr[$i][$j] / $temp; - } - } - - /* removing identity matrix */ - for ($i = 0; $i < $mDim; ++$i) { - $newMatrixArr[$i] = array_slice($newMatrixArr[$i], $mDim); - } - - $newMatrix = new Matrix($this->n, $this->n); - $newMatrix->setMatrix($newMatrixArr); - - return $newMatrix; - } - - /** - * Diagonalize matrix - * - * @return Matrix - * - * @since 1.0.0 - */ - public function diagonalize() : Matrix - { - $newMatrix = new Matrix($this->m, $this->n); - $newMatrix->setMatrix($this->diag($this->matrix)); - - return $newMatrix; - } - /** * Solve matrix * @@ -584,111 +587,6 @@ class Matrix implements \ArrayAccess, \Iterator return $M->solve($B); } - /** - * Perform gauss elimination on Matrix - * - * @param mixed $b Vector b - * - * @return Matrix - * - * @since 1.0.0 - */ - private function gaussElimination($b) : Matrix - { - $mDim = count($b); - $matrix = $this->matrix; - - for ($col = 0; $col < $mDim; $col++) { - $j = $col; - $max = $matrix[$j][$j]; - - for ($i = $col + 1; $i < $mDim; ++$i) { - $temp = abs($matrix[$i][$col]); - - if ($temp > $max) { - $j = $i; - $max = $temp; - } - } - - if ($col != $j) { - $temp = $matrix[$col]; - $matrix[$col] = $matrix[$j]; - $matrix[$j] = $temp; - - $temp = $b[$col]; - $b[$col] = $b[$j]; - $b[$j] = $temp; - } - - for ($i = $col + 1; $i < $mDim; ++$i) { - $temp = $matrix[$i][$col] / $matrix[$col][$col]; - - for ($j = $col + 1; $j < $mDim; ++$j) { - $matrix[$i][$j] -= $temp * $matrix[$col][$j]; - } - - $matrix[$i][$col] = 0; - $b[$i] -= $temp * $b[$col]; - } - } - - $x = []; - for ($col = $mDim - 1; $col >= 0; $col--) { - $temp = $b[$col]; - for ($j = $mDim - 1; $j > $col; $j--) { - $temp -= $x[$j] * $matrix[$col][$j]; - } - - $x[$col] = $temp / $matrix[$col][$col]; - } - - $solution = new self(count($x), count($x[0])); - $solution->setMatrix($x); - - return $solution; - } - - /** - * Diagonalize matrix. - * - * @param array $arr Matrix to diagonalize - * - * @return array - * - * @since 1.0.0 - */ - private function diag(array $arr) : array - { - $mDim = count($arr); - $nDim = count($arr[0]); - - for ($i = $mDim - 1; $i > 0; $i--) { - if ($arr[$i - 1][0] < $arr[$i][0]) { - for ($j = 0; $j < $nDim; ++$j) { - $temp = $arr[$i][$j]; - $arr[$i][$j] = $arr[$i - 1][$j]; - $arr[$i - 1][$j] = $temp; - } - } - } - - /* create diagonal matrix */ - for ($i = 0; $i < $mDim; ++$i) { - for ($j = 0; $j < $mDim; ++$j) { - if ($j !== $i) { - $temp = $arr[$j][$i] / $arr[$i][$i]; - - for ($c = 0; $c < $nDim; ++$c) { - $arr[$j][$c] -= $arr[$i][$c] * $temp; - } - } - } - } - - return $arr; - } - /** * Calculate det. * diff --git a/Math/Number/Complex.php b/Math/Number/Complex.php index 7cc19c2e3..6852ca032 100644 --- a/Math/Number/Complex.php +++ b/Math/Number/Complex.php @@ -181,9 +181,7 @@ class Complex return $this; } - for ($i = $value; $i > 0; --$i) { - return $this->multComplex($this->powInteger($i)); - } + return $this->multComplex($this->powInteger(--$value)); } public function powScalar() : Complex diff --git a/tests/Math/Matrix/MatrixTest.php b/tests/Math/Matrix/MatrixTest.php index db8e6053b..d36d85c77 100644 --- a/tests/Math/Matrix/MatrixTest.php +++ b/tests/Math/Matrix/MatrixTest.php @@ -69,14 +69,57 @@ class MatrixTest extends \PHPUnit\Framework\TestCase public function testDet() { - $this->B = new Matrix(); - $this->B->setMatrix([ + $B = new Matrix(); + $B->setMatrix([ [6, 1, 1], [4, -2, 5], [2, 8, 7], ]); - self::assertEquals(-306, $this->B->det()); + self::assertEquals(-306, $B->det()); + } + + public function testTranspose() + { + $B = new Matrix(); + $B->setMatrix([ + [6, 1, 1], + [4, -2, 5], + ]); + + self::assertEquals([[6, 4], [1, -2], [1, 5],], $B->transpose()->toArray()); + } + + public function testRank() + { + $B = new Matrix(); + $B->setMatrix([ + [0, 1, 2], + [1, 2, 1], + [2, 7, 8], + ]); + + self::assertEquals(2, $B->rank()); + + $B->setMatrix([ + [1, 0, 2], + [2, 1, 0], + [3, 2, 1], + ]); + self::assertEquals(3, $B->rank()); + + $B->setMatrix([ + [1, 0, 2], + [2, 1, 0], + ]); + self::assertEquals(2, $B->rank()); + + $B->setMatrix([ + [1, 2], + [0, 1], + [2, 0], + ]); + self::assertEquals(2, $B->rank()); } public function testInverse() @@ -88,6 +131,8 @@ class MatrixTest extends \PHPUnit\Framework\TestCase [2, 1, 1], ]); + self::markTestIncomplete(); + // todo: result column 0 and 1 are swapped. why? still correct? /*self::assertEquals([ [-0.9, -0.5, 2.2], [0.7, 0.5, -1.6], diff --git a/tests/Math/Number/ComplexTest.php b/tests/Math/Number/ComplexTest.php index d26aa6c95..15d2dc002 100644 --- a/tests/Math/Number/ComplexTest.php +++ b/tests/Math/Number/ComplexTest.php @@ -59,7 +59,7 @@ class ComplexTest extends \PHPUnit\Framework\TestCase self::assertEquals('7.00 + 24.00i', $cpl->square()->render()); self::assertEquals('7.00 + 24.00i', $cpl->pow(2)->render()); - self::assertEquals('-44.00 - 117.00i', $cpl->pow(3)->render()); + self::assertEquals('-44.00 + 117.00i', $cpl->pow(3)->render()); self::assertEquals(5, $cpl->abs(), '', 0.01);