public function testMatrixGetReturnsCorrectValue() { $testArray = array(array(1, 2, 3), array(0, 2, 1), array(2.5, 1, 3)); $this->object = new RationalMatrix($testArray); for ($r = 1; $r < 4; $r++) { for ($c = 1; $c < 4; $c++) { $this->assertEquals(RationalTypeFactory::create($testArray[$r - 1][$c - 1]), $this->object->get($r, $c)); } } }
/** * Multiply the given matrices. * * @param matrix left matrix * @param matrix right matrix * @return matrix product matrix $a*$b */ public static function multiply($a, $b) { // First check dimensions. new Assertion($a instanceof Matrix, 'Given first matrix not of class Matrix.'); new Assertion($b instanceof Matrix, 'Given second matrix not of class Matrix.'); new Assertion($a->columns() == $b->rows(), 'Given dimensions are not compatible.'); $c = new Matrix($a->rows(), $b->columns()); $c->setAll(0.0); for ($i = 0; $i < $a->rows(); $i++) { for ($j = 0; $j < $b->columns(); $j++) { for ($k = 0; $k < $a->columns(); $k++) { $c->set($i, $j, $c->get($i, $j) + $a->get($i, $k) * $b->get($k, $j)); } } } return $c; }
/** * plus * A + B * @param mixed $B Matrix/Array * @return Matrix Sum */ function plus() { if (func_num_args() > 0) { $args = func_get_args(); $match = implode(",", array_map('gettype', $args)); switch ($match) { case 'object': $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR); //$this->checkMatrixDimensions($M); for ($i = 0; $i < $this->m; $i++) { for ($j = 0; $j < $this->n; $j++) { $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]); } } return $M; break; case 'array': $M = new Matrix($args[0]); //$this->checkMatrixDimensions($M); for ($i = 0; $i < $this->m; $i++) { for ($j = 0; $j < $this->n; $j++) { $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]); } } return $M; break; default: trigger_error(PolymorphicArgumentException, ERROR); break; } } else { trigger_error(PolymorphicArgumentException, ERROR); } }
/** * setMatrix * * Set a submatrix * @param int $i0 Initial row index * @param int $j0 Initial column index * @param mixed $S Matrix/Array submatrix * ($i0, $j0, $S) $S = Matrix * ($i0, $j0, $S) $S = Array */ public function setMatrix() { if (func_num_args() > 0) { $args = func_get_args(); $match = implode(",", array_map('gettype', $args)); switch ($match) { case 'integer,integer,object': if ($args[2] instanceof Matrix) { $M = $args[2]; } else { throw new Exception(JAMAError(ArgumentTypeException)); } if ($args[0] + $M->m <= $this->m) { $i0 = $args[0]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); } if ($args[1] + $M->n <= $this->n) { $j0 = $args[1]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); } for ($i = $i0; $i < $i0 + $M->m; ++$i) { for ($j = $j0; $j < $j0 + $M->n; ++$j) { $this->A[$i][$j] = $M->get($i - $i0, $j - $j0); } } break; case 'integer,integer,array': $M = new Matrix($args[2]); if ($args[0] + $M->m <= $this->m) { $i0 = $args[0]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); } if ($args[1] + $M->n <= $this->n) { $j0 = $args[1]; } else { throw new Exception(JAMAError(ArgumentBoundsException)); } for ($i = $i0; $i < $i0 + $M->m; ++$i) { for ($j = $j0; $j < $j0 + $M->n; ++$j) { $this->A[$i][$j] = $M->get($i - $i0, $j - $j0); } } break; default: throw new Exception(JAMAError(PolymorphicArgumentException)); break; } } else { throw new Exception(JAMAError(PolymorphicArgumentException)); } }
function TestMatrix() { // define test variables $errorCount = 0; $warningCount = 0; $columnwise = array(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0); $rowwise = array(1.0, 4.0, 7.0, 10.0, 2.0, 5.0, 8.0, 11.0, 3.0, 6.0, 9.0, 12.0); $avals = array(array(1.0, 4.0, 7.0, 10.0), array(2.0, 5.0, 8.0, 11.0), array(3.0, 6.0, 9.0, 12.0)); $rankdef = $avals; $tvals = array(array(1.0, 2.0, 3.0), array(4.0, 5.0, 6.0), array(7.0, 8.0, 9.0), array(10.0, 11.0, 12.0)); $subavals = array(array(5.0, 8.0, 11.0), array(6.0, 9.0, 12.0)); $rvals = array(array(1.0, 4.0, 7.0), array(2.0, 5.0, 8.0, 11.0), array(3.0, 6.0, 9.0, 12.0)); $pvals = array(array(1.0, 1.0, 1.0), array(1.0, 2.0, 3.0), array(1.0, 3.0, 6.0)); $ivals = array(array(1.0, 0.0, 0.0, 0.0), array(0.0, 1.0, 0.0, 0.0), array(0.0, 0.0, 1.0, 0.0)); $evals = array(array(0.0, 1.0, 0.0, 0.0), array(1.0, 0.0, 2.0E-7, 0.0), array(0.0, -2.0E-7, 0.0, 1.0), array(0.0, 0.0, 1.0, 0.0)); $square = array(array(166.0, 188.0, 210.0), array(188.0, 214.0, 240.0), array(210.0, 240.0, 270.0)); $sqSolution = array(array(13.0), array(15.0)); $condmat = array(array(1.0, 3.0), array(7.0, 9.0)); $rows = 3; $cols = 4; $invalidID = 5; /* should trigger bad shape for construction with val */ $raggedr = 0; /* (raggedr,raggedc) should be out of bounds in ragged array */ $raggedc = 4; $validID = 3; /* leading dimension of intended test Matrices */ $nonconformld = 4; /* leading dimension which is valid, but nonconforming */ $ib = 1; /* index ranges for sub Matrix */ $ie = 2; $jb = 1; $je = 3; $rowindexset = array(1, 2); $badrowindexset = array(1, 3); $columnindexset = array(1, 2, 3); $badcolumnindexset = array(1, 2, 4); $columnsummax = 33.0; $rowsummax = 30.0; $sumofdiagonals = 15; $sumofsquares = 650; /** * Test matrix methods */ /** * Constructors and constructor-like methods: * * Matrix(double[], int) * Matrix(double[][]) * Matrix(int, int) * Matrix(int, int, double) * Matrix(int, int, double[][]) * constructWithCopy(double[][]) * random(int,int) * identity(int) */ echo "<p>Testing constructors and constructor-like methods...</p>"; $A = new Matrix($columnwise, 3); if ($A instanceof Matrix) { $this->try_success("Column-packed constructor..."); } else { $errorCount = $this->try_failure($errorCount, "Column-packed constructor...", "Unable to construct Matrix"); } $T = new Matrix($tvals); if ($T instanceof Matrix) { $this->try_success("2D array constructor..."); } else { $errorCount = $this->try_failure($errorCount, "2D array constructor...", "Unable to construct Matrix"); } $A = new Matrix($columnwise, $validID); $B = new Matrix($avals); $tmp = $B->get(0, 0); $avals[0][0] = 0.0; $C = $B->minus($A); $avals[0][0] = $tmp; $B = Matrix::constructWithCopy($avals); $tmp = $B->get(0, 0); $avals[0][0] = 0.0; /** check that constructWithCopy behaves properly **/ if ($tmp - $B->get(0, 0) != 0.0) { $errorCount = $this->try_failure($errorCount, "constructWithCopy... ", "copy not effected... data visible outside"); } else { $this->try_success("constructWithCopy... ", ""); } $I = new Matrix($ivals); if ($this->checkMatrices($I, Matrix::identity(3, 4))) { $this->try_success("identity... ", ""); } else { $errorCount = $this->try_failure($errorCount, "identity... ", "identity Matrix not successfully created"); } /** * Access Methods: * * getColumnDimension() * getRowDimension() * getArray() * getArrayCopy() * getColumnPackedCopy() * getRowPackedCopy() * get(int,int) * getMatrix(int,int,int,int) * getMatrix(int,int,int[]) * getMatrix(int[],int,int) * getMatrix(int[],int[]) * set(int,int,double) * setMatrix(int,int,int,int,Matrix) * setMatrix(int,int,int[],Matrix) * setMatrix(int[],int,int,Matrix) * setMatrix(int[],int[],Matrix) */ print "<p>Testing access methods...</p>"; $B = new Matrix($avals); if ($B->getRowDimension() == $rows) { $this->try_success("getRowDimension..."); } else { $errorCount = $this->try_failure($errorCount, "getRowDimension..."); } if ($B->getColumnDimension() == $cols) { $this->try_success("getColumnDimension..."); } else { $errorCount = $this->try_failure($errorCount, "getColumnDimension..."); } $barray = $B->getArray(); if ($this->checkArrays($barray, $avals)) { $this->try_success("getArray..."); } else { $errorCount = $this->try_failure($errorCount, "getArray..."); } $bpacked = $B->getColumnPackedCopy(); if ($this->checkArrays($bpacked, $columnwise)) { $this->try_success("getColumnPackedCopy..."); } else { $errorCount = $this->try_failure($errorCount, "getColumnPackedCopy..."); } $bpacked = $B->getRowPackedCopy(); if ($this->checkArrays($bpacked, $rowwise)) { $this->try_success("getRowPackedCopy..."); } else { $errorCount = $this->try_failure($errorCount, "getRowPackedCopy..."); } /** * Array-like methods: * minus * minusEquals * plus * plusEquals * arrayLeftDivide * arrayLeftDivideEquals * arrayRightDivide * arrayRightDivideEquals * arrayTimes * arrayTimesEquals * uminus */ print "<p>Testing array-like methods...</p>"; /** * I/O methods: * read * print * serializable: * writeObject * readObject */ print "<p>Testing I/O methods...</p>"; /** * Test linear algebra methods */ echo "<p>Testing linear algebra methods...<p>"; $A = new Matrix($columnwise, 3); if ($this->checkMatrices($A->transpose(), $T)) { $this->try_success("Transpose check..."); } else { $errorCount = $this->try_failure($errorCount, "Transpose check...", "Matrices are not equal"); } if ($this->checkScalars($A->norm1(), $columnsummax)) { $this->try_success("Maximum column sum..."); } else { $errorCount = $this->try_failure($errorCount, "Maximum column sum...", "Incorrect: " . $A->norm1() . " != " . $columnsummax); } if ($this->checkScalars($A->normInf(), $rowsummax)) { $this->try_success("Maximum row sum..."); } else { $errorCount = $this->try_failure($errorCount, "Maximum row sum...", "Incorrect: " . $A->normInf() . " != " . $rowsummax); } if ($this->checkScalars($A->normF(), sqrt($sumofsquares))) { $this->try_success("Frobenius norm..."); } else { $errorCount = $this->try_failure($errorCount, "Frobenius norm...", "Incorrect:" . $A->normF() . " != " . sqrt($sumofsquares)); } if ($this->checkScalars($A->trace(), $sumofdiagonals)) { $this->try_success("Matrix trace..."); } else { $errorCount = $this->try_failure($errorCount, "Matrix trace...", "Incorrect: " . $A->trace() . " != " . $sumofdiagonals); } $B = $A->getMatrix(0, $A->getRowDimension(), 0, $A->getRowDimension()); if ($B->det() == 0) { $this->try_success("Matrix determinant..."); } else { $errorCount = $this->try_failure($errorCount, "Matrix determinant...", "Incorrect: " . $B->det() . " != " . 0); } $A = new Matrix($columnwise, 3); $SQ = new Matrix($square); if ($this->checkMatrices($SQ, $A->times($A->transpose()))) { $this->try_success("times(Matrix)..."); } else { $errorCount = $this->try_failure($errorCount, "times(Matrix)...", "Unable to multiply matrices"); $SQ->toHTML(); $AT->toHTML(); } $A = new Matrix($columnwise, 4); $QR = $A->qr(); $R = $QR->getR(); $Q = $QR->getQ(); if ($this->checkMatrices($A, $Q->times($R))) { $this->try_success("QRDecomposition...", ""); } else { $errorCount = $this->try_failure($errorCount, "QRDecomposition...", "incorrect qr decomposition calculation"); } $A = new Matrix($columnwise, 4); $SVD = $A->svd(); $U = $SVD->getU(); $S = $SVD->getS(); $V = $SVD->getV(); if ($this->checkMatrices($A, $U->times($S->times($V->transpose())))) { $this->try_success("SingularValueDecomposition...", ""); } else { $errorCount = $this->try_failure($errorCount, "SingularValueDecomposition...", "incorrect singular value decomposition calculation"); } $n = $A->getColumnDimension(); $A = $A->getMatrix(0, $n - 1, 0, $n - 1); $A->set(0, 0, 0.0); $LU = $A->lu(); $L = $LU->getL(); if ($this->checkMatrices($A->getMatrix($LU->getPivot(), 0, $n - 1), $L->times($LU->getU()))) { $this->try_success("LUDecomposition...", ""); } else { $errorCount = $this->try_failure($errorCount, "LUDecomposition...", "incorrect LU decomposition calculation"); } $X = $A->inverse(); if ($this->checkMatrices($A->times($X), Matrix::identity(3, 3))) { $this->try_success("inverse()...", ""); } else { $errorCount = $this->try_failure($errorCount, "inverse()...", "incorrect inverse calculation"); } $DEF = new Matrix($rankdef); if ($this->checkScalars($DEF->rank(), min($DEF->getRowDimension(), $DEF->getColumnDimension()) - 1)) { $this->try_success("Rank..."); } else { $this->try_failure("Rank...", "incorrect rank calculation"); } $B = new Matrix($condmat); $SVD = $B->svd(); $singularvalues = $SVD->getSingularValues(); if ($this->checkScalars($B->cond(), $singularvalues[0] / $singularvalues[min($B->getRowDimension(), $B->getColumnDimension()) - 1])) { $this->try_success("Condition number..."); } else { $this->try_failure("Condition number...", "incorrect condition number calculation"); } $SUB = new Matrix($subavals); $O = new Matrix($SUB->getRowDimension(), 1, 1.0); $SOL = new Matrix($sqSolution); $SQ = $SUB->getMatrix(0, $SUB->getRowDimension() - 1, 0, $SUB->getRowDimension() - 1); if ($this->checkMatrices($SQ->solve($SOL), $O)) { $this->try_success("solve()...", ""); } else { $errorCount = $this->try_failure($errorCount, "solve()...", "incorrect lu solve calculation"); } $A = new Matrix($pvals); $Chol = $A->chol(); $L = $Chol->getL(); if ($this->checkMatrices($A, $L->times($L->transpose()))) { $this->try_success("CholeskyDecomposition...", ""); } else { $errorCount = $this->try_failure($errorCount, "CholeskyDecomposition...", "incorrect Cholesky decomposition calculation"); } $X = $Chol->solve(Matrix::identity(3, 3)); if ($this->checkMatrices($A->times($X), Matrix::identity(3, 3))) { $this->try_success("CholeskyDecomposition solve()...", ""); } else { $errorCount = $this->try_failure($errorCount, "CholeskyDecomposition solve()...", "incorrect Choleskydecomposition solve calculation"); } $Eig = $A->eig(); $D = $Eig->getD(); $V = $Eig->getV(); if ($this->checkMatrices($A->times($V), $V->times($D))) { $this->try_success("EigenvalueDecomposition (symmetric)...", ""); } else { $errorCount = $this->try_failure($errorCount, "EigenvalueDecomposition (symmetric)...", "incorrect symmetric Eigenvalue decomposition calculation"); } $A = new Matrix($evals); $Eig = $A->eig(); $D = $Eig->getD(); $V = $Eig->getV(); if ($this->checkMatrices($A->times($V), $V->times($D))) { $this->try_success("EigenvalueDecomposition (nonsymmetric)...", ""); } else { $errorCount = $this->try_failure($errorCount, "EigenvalueDecomposition (nonsymmetric)...", "incorrect nonsymmetric Eigenvalue decomposition calculation"); } print "<b>{$errorCount} total errors</b>."; }
/** * Assembles Q using the single givens rotations. * * @return matrix Q */ public function getQ() { // Q is an mxm matrix if m is the maximum of the number of rows and thenumber of columns. $m = max($this->_matrix->columns(), $this->_matrix->rows()); $Q = new Matrix($m, $m); $Q->setAll(0.0); // Begin with the identity matrix. for ($i = 0; $i < $Q->rows(); $i++) { $Q->set($i, $i, 1.0); } for ($j = $this->_matrix->columns() - 1; $j >= 0; $j--) { for ($i = $this->_matrix->rows() - 1; $i > $j; $i--) { // Get c and s which are stored in the i-th row, j-th column. $aij = $this->_matrix->get($i, $j); $c = 0; $s = 0; if ($aij == 0) { $c = 0.0; $s = 1.0; } else { if (abs($aij) < 1) { $s = 2.0 * abs($aij); $c = sqrt(1 - pow($s, 2)); if ($aij < 0) { $c = -$c; } } else { $c = 2.0 / $aij; $s = sqrt(1 - pow($c, 2)); } } for ($k = 0; $k < $this->_matrix->columns(); $k++) { $jk = $Q->get($j, $k); $ik = $Q->get($i, $k); $Q->set($j, $k, $c * $jk - $s * $ik); $Q->set($i, $k, $s * $jk + $c * $ik); } } } return $Q; }
/** * Assembles Q using the single givens rotations. * * @return matrix Q */ public function getQ() { // Q is an m x m matrix if m is the maximum of the number of rows and thenumber of columns. $m = max($this->_matrix->columns(), $this->_matrix->rows()); $Q = new Matrix($m, $m); $Q->setAll(0.0); // Begin with the identity matrix. for ($i = 0; $i < min($Q->rows(), $Q->columns()); $i++) { $Q->set($i, $i, 1.0); } // Got backwards through all householder transformations and apply them on for ($k = $this->_matrix->columns() - 1; $k >= 0; $k--) { for ($j = $k; $j < $Q->columns(); $j++) { // First compute w^T(j) = v^T * Q(j) where Q(j) is the j-th column of Q. $w = $Q->get($k, $j) * 1.0; for ($i = $k + 1; $i < $Q->rows(); $i++) { $w += $this->_matrix->get($i, $k) * $Q->get($i, $j); } // Now : Q(i,j) = Q(i,j) - tau(k)*v(i)*w(j). $Q->set($k, $j, $Q->get($k, $j) - $this->_tau->get($k) * 1.0 * $w); for ($i = $k + 1; $i < $Q->rows(); $i++) { $Q->set($i, $j, $Q->get($i, $j) - $this->_tau->get($k) * $this->_matrix->get($i, $k) * $w); } } } return $Q; }