Math_Matrix
[ class tree: Math_Matrix ] [ index: Math_Matrix ] [ all elements ]

Source for file Matrix.php

Documentation is available at Matrix.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4.0                                                      |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2001 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license,      |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Jesus M. Castagnetto <jmcastagnetto@php.net>                |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // Matrix definition and manipulation package
  20. //
  21. // $Id: Matrix.php 304049 2010-10-05 00:30:02Z clockwerx $
  22. //
  23.  
  24. require_once 'PEAR.php';
  25. require_once 'Math/Vector.php';
  26.  
  27. /**
  28.  * Defines a matrix object.
  29.  *
  30.  * A matrix is implemented as an array of arrays such that:
  31.  *
  32.  * <pre>
  33.  * [0][0] [0][1] [0][2] ... [0][M]
  34.  * [1][0] [1][1] [1][2] ... [1][M]
  35.  * ...
  36.  * [N][0] [n][1] [n][2] ... [n][M]
  37.  * </pre>
  38.  *
  39.  * i.e. N rows, M colums
  40.  *
  41.  * Originally this class was part of NumPHP (Numeric PHP package)
  42.  *
  43.  * @author      Jesus M. Castagnetto <jmcastagnetto@php.net>
  44.  * @access      public
  45.  * @version     1.0
  46.  * @package     Math_Matrix
  47.  */
  48. class Math_Matrix {/*{{{*/
  49.  
  50.     // Properties /*{{{*/
  51.  
  52.     /**#@+
  53.      * @access private
  54.      */
  55.  
  56.     /**
  57.      * Contains the array of arrays defining the matrix
  58.      *
  59.      * @var     array 
  60.      * @see     getData()
  61.      */
  62.     var $_data = null;
  63.  
  64.     /**
  65.      * The number of rows in the matrix
  66.      *
  67.      * @var     integer 
  68.      * @see     getSize()
  69.      */
  70.     var $_num_rows = null;
  71.  
  72.     /**
  73.      * The number of columns in the matrix
  74.      *
  75.      * @var     integer 
  76.      * @see     getSize()
  77.      */
  78.     var $_num_cols = null;
  79.  
  80.     /**
  81.      * A flag indicating if the matrix is square
  82.      * i.e. if $this->_num_cols == $this->_num_rows
  83.      *
  84.      * @var     boolean 
  85.      * @see     isSquare()
  86.      */
  87.     var $_square = false;
  88.  
  89.     /**#@+
  90.      * @access private
  91.      * @var    float
  92.      */
  93.     /**
  94.      * The smallest value of all matrix cells
  95.      *
  96.      * @see     getMin()
  97.      * @see     getMinMax()
  98.      */
  99.     var $_min = null;
  100.  
  101.     /**
  102.      * The biggest value of all matrix cells
  103.      *
  104.      * @see     getMax()
  105.      * @see     getMinMax()
  106.      */
  107.     var $_max = null;
  108.  
  109.     /**
  110.      * The Euclidean norm for the matrix: sqrt(sum(e[i][j]^2))
  111.      *
  112.      * @see norm()
  113.      */
  114.     var $_norm = null;
  115.  
  116.     /**
  117.      * The matrix determinant
  118.      *
  119.      * @see determinant()
  120.      */
  121.     var $_det = null;
  122.  
  123.     /**
  124.      * Cutoff error used to test for singular or ill-conditioned matrices
  125.      *
  126.      * @see determinant();
  127.      * @see invert()
  128.      */
  129.     var $_epsilon = 1E-18;
  130.  
  131.     /*}}}*/
  132.  
  133.     /**#@+
  134.      * @access  public
  135.      */
  136.  
  137.     /**
  138.      * Constructor for the matrix object
  139.      *
  140.      * @param   array|Math_Matrix  $data a numeric array of arrays of a Math_Matrix object
  141.      * @return    object    Math_Matrix 
  142.      * @see     $_data
  143.      * @see     setData()
  144.      */
  145.     function Math_Matrix($data = null{/*{{{*/
  146.         if (!is_null($data)) {
  147.             $this->setData($data);
  148.         }
  149.     }/*}}}*/
  150.  
  151.     /**
  152.      * Validates the data and initializes the internal variables (except for the determinant).
  153.      *
  154.      * The validation is performed by by checking that
  155.      * each row (first dimension in the array of arrays)
  156.      * contains the same number of colums (e.g. arrays of the
  157.      * same size)
  158.      *
  159.      * @param   array   $data array of arrays of numbers or a valid Math_Matrix object
  160.      * @return    boolean|PEAR_Error   true on success, a PEAR_Error object otherwise
  161.      */
  162.     function setData($data{/*{{{*/
  163.         if (Math_Matrix::isMatrix($data)) {
  164.             if (!$data->isEmpty()) {
  165.                 $this->_data $data->getData();
  166.             else {
  167.                 return $errObj;
  168.             }
  169.         elseif (is_array($data|| is_array($data[0])) {
  170.             // check that we got a numeric bidimensional array
  171.             // and that all rows are of the same size
  172.             $nc = 0;
  173.             if (!empty($data[0])) {
  174.                 $nc count($data[0]);
  175.             }
  176.  
  177.             $nr count($data);
  178.             $eucnorm = 0;
  179.             $tmp = array();
  180.             for ($i=0; $i $nr$i++{
  181.                 if (count($data[$i]!= $nc{
  182.                     return PEAR::raiseError('Invalid data, cannot create/modify matrix.'.
  183.                         ' Expecting an array of arrays or an initialized Math_Matrix object');
  184.                 }
  185.                 for ($j=0; $j $nc$j++{
  186.                     if (!is_numeric($data[$i][$j])) {
  187.                         return PEAR::raiseError('Invalid data, cannot create/modify matrix.'.
  188.                             ' Expecting an array of arrays or an initialized Math_Matrix object');
  189.                     }
  190.                     $data[$i][$j= (float) $data[$i][$j];
  191.                     $tmp[$data[$i][$j];
  192.                     $eucnorm += $data[$i][$j$data[$i][$j];
  193.                 }
  194.             }
  195.             $this->_num_rows $nr;
  196.             $this->_num_cols $nc;
  197.             $this->_square ($nr == $nc);
  198.             $this->_min !empty($tmp)min($tmp: null;
  199.             $this->_max !empty($tmp)max($tmp: null;
  200.             $this->_norm sqrt($eucnorm);
  201.             $this->_data $data;
  202.             $this->_det = null; // lazy initialization ;-)
  203.             return true;
  204.         else {
  205.             return PEAR::raiseError('Invalid data, cannot create/modify matrix.'.
  206.                 ' Expecting an array of arrays or an initialized Math_Matrix object');
  207.         }
  208.     }/*}}}*/
  209.  
  210.     /**
  211.      * Returns the array of arrays.
  212.      *
  213.      * @return array|PEAR_Erroran array of array of numbers on success, a PEAR_Error otherwise
  214.      */
  215.     function getData ({/*{{{*/
  216.         if ($this->isEmpty()) {
  217.             return PEAR::raiseError('Matrix has not been populated');
  218.         else {
  219.             return $this->_data;
  220.         }
  221.     }/*}}}*/
  222.  
  223.     /**
  224.      * Sets the threshold to consider a numeric value as zero:
  225.      * if number <= epsilon then number = 0
  226.      *
  227.      * @acess public
  228.      * @param number $epsilon the upper bound value
  229.      * @return boolean|PEAR_Errortrue if successful, a PEAR_Error otherwise
  230.      */
  231.     function setZeroThreshold($epsilon{/*{{{*/
  232.         if (!is_numeric($epsilon)) {
  233.             return PEAR::raisError('Expection a number for threshold, using the old value: '.$this->_epsilon);
  234.         else {
  235.             $this->_epsilon $epsilon;
  236.             return true;
  237.         }
  238.     }/*}}}*/
  239.  
  240.     /**
  241.      * Returns the value of the upper bound used to minimize round off errors
  242.      *
  243.      * @return float 
  244.      */
  245.     function getZeroThreshold({/*{{{*/
  246.         return $this->_epsilon;
  247.     }/*}}}*/
  248.  
  249.     /**
  250.      * Checks if the matrix has been initialized.
  251.      *
  252.      * @return boolean TRUE on success, FALSE otherwise
  253.      */
  254.     function isEmpty({/*{{{*/
  255.         return empty($this->_data|| is_null($this->_data) );
  256.     }/*}}}*/
  257.  
  258.  
  259.     /**
  260.      * Returns an array with the number of rows and columns in the matrix
  261.      *
  262.      * @return    array|PEAR_Error   an array of integers on success, a PEAR_Error object otherwise
  263.      */
  264.     function getSize({/*{{{*/
  265.         if ($this->isEmpty())
  266.             return PEAR::raiseError('Matrix has not been populated');
  267.         else
  268.             return array($this->_num_rows$this->_num_cols);
  269.     }/*}}}*/
  270.  
  271.     /**
  272.      * Checks if it is a square matrix (i.e. num rows == num cols)
  273.      *
  274.      * @return boolean TRUE on success, FALSE otherwise
  275.      */
  276.     function isSquare ({/*{{{*/
  277.         if ($this->isEmpty()) {
  278.             return PEAR::raiseError('Matrix has not been populated');
  279.         else {
  280.             return $this->_square;
  281.         }
  282.     }/*}}}*/
  283.  
  284.     /**
  285.      * Returns the Euclidean norm of the matrix.
  286.      *
  287.      * Euclidean norm = sqrt( sum( e[i][j]^2 ) )
  288.      *
  289.      * @return float|PEAR_Errora number on success, a PEAR_Error otherwise
  290.      */
  291.     function norm({/*{{{*/
  292.         if (!is_null($this->_norm)) {
  293.             return $this->_norm;
  294.         else {
  295.             return PEAR::raiseError('Uninitialized Math_Matrix object');
  296.         }
  297.     }/*}}}*/
  298.  
  299.     /**
  300.      * Returns a new Math_Matrix object with the same data as the current one
  301.      *
  302.      * @return object Math_Matrix|PEAR_Errora Math_Matrix objects on succes, a
  303.      *                                 PEAR_Error otherwise.
  304.      */
  305.     function cloneMatrix({/*{{{*/
  306.         if ($this->isEmpty()) {
  307.             return PEAR::raiseError('Matrix has not been populated');
  308.         else {
  309.             return new Math_Matrix($this->_data);
  310.         }
  311.     }/*}}}*/
  312.  
  313.  
  314.     /**
  315.      * Sets the value of the element at (row,col)
  316.      *
  317.      * @param integer $row 
  318.      * @param integer $col 
  319.      * @param numeric $value 
  320.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  321.      */
  322.     function setElement($row$col$value{/*{{{*/
  323.         if ($this->isEmpty()) {
  324.             return PEAR::raiseError('Matrix has not been populated');
  325.         }
  326.         if ($row >= $this->_num_rows && $col >= $this->_num_cols{
  327.             return PEAR::raiseError('Incorrect row and column values');
  328.         }
  329.         if (!is_numeric($value)) {
  330.             return PEAR::raiseError('Incorrect value, expecting a number');
  331.         }
  332.         $this->_data[$row][$col$value;
  333.         return true;
  334.     }/*}}}*/
  335.  
  336.     /**
  337.      * Returns the value of the element at (row,col)
  338.      *
  339.      * @param integer $row 
  340.      * @param integer $col 
  341.      * @return number|PEAR_Errora number on success, a PEAR_Error otherwise
  342.      */
  343.     function getElement($row$col{/*{{{*/
  344.         if ($this->isEmpty()) {
  345.             return PEAR::raiseError('Matrix has not been populated');
  346.         }
  347.         if ($row >= $this->_num_rows && $col >= $this->_num_cols{
  348.             return PEAR::raiseError('Incorrect row and column values');
  349.         }
  350.         return $this->_data[$row][$col];
  351.     }/*}}}*/
  352.  
  353.     /**
  354.      * Returns the row with the given index
  355.      *
  356.      * This method checks that matrix has been initialized and that the
  357.      * row requested is not outside the range of rows.
  358.      *
  359.      * @param integer $row 
  360.      * @param optional boolean $asVector whether to return a Math_Vector or a simple array. Default = false.
  361.      * @return array|Math_Vector|PEAR_Erroran array of numbers or a Math_Vector on success, a PEAR_Error otherwise
  362.      */
  363.     function getRow ($row$asVector = false{/*{{{*/
  364.         if ($this->isEmpty()) {
  365.             return PEAR::raiseError('Matrix has not been populated');
  366.         }
  367.         if (is_integer($row&& $row >= $this->_num_rows{
  368.             return PEAR::raiseError('Incorrect row value');
  369.         }
  370.         if ($asVector{
  371.             $classes get_declared_classes();
  372.             if (!in_array("math_vector"$classes|| !in_array("math_vectopop"$classes)) {
  373.                 return PEAR::raiseError ("Classes Math_Vector and Math_VectorOp undefined".
  374.                                     " add \"require_once 'Math/Vector/Vector.php'\" to your script");
  375.             }
  376.             return new Math_Vector($this->_data[$row]);
  377.         else {
  378.             return $this->_data[$row];
  379.         }
  380.     }/*}}}*/
  381.  
  382.     /**
  383.      * Sets the row with the given index to the array
  384.      *
  385.      * This method checks that the row is less than the size of the matrix
  386.      * rows, and that the array size equals the number of columns in the
  387.      * matrix.
  388.      *
  389.      * @param integer $row index of the row
  390.      * @param array $arr array of numbers
  391.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  392.      */
  393.     function setRow ($row$arr{/*{{{*/
  394.         if ($this->isEmpty()) {
  395.             return PEAR::raiseError('Matrix has not been populated');
  396.         }
  397.         if ($row >= $this->_num_rows{
  398.             return PEAR::raiseError('Row index out of bounds');
  399.         }
  400.         if (count($arr!= $this->_num_cols{
  401.             return PEAR::raiseError('Incorrect size for matrix row: expecting '.$this->_num_cols
  402.                     .' columns, got '.count($arr).' columns');
  403.         }
  404.         for ($i=0; $i $this->_num_cols$i++{
  405.             if (!is_numeric($arr[$i])) {
  406.                 return PEAR::raiseError('Incorrect values, expecting numbers');
  407.             }
  408.         }
  409.         $this->_data[$row$arr;
  410.         return true;
  411.     }/*}}}*/
  412.  
  413.     /**
  414.      * Returns the column with the given index
  415.      *
  416.      * This method checks that matrix has been initialized and that the
  417.      * column requested is not outside the range of column.
  418.      *
  419.      * @param integer $col 
  420.      * @param optional boolean $asVector whether to return a Math_Vector or a simple array. Default = false.
  421.      * @return array|Math_Vector|PEAR_Erroran array of numbers or a Math_Vector on success, a PEAR_Error otherwise
  422.      */
  423.     function getCol ($col$asVector=false{/*{{{*/
  424.         if ($this->isEmpty()) {
  425.             return PEAR::raiseError('Matrix has not been populated');
  426.         }
  427.         if (is_integer($col&& $col >= $this->_num_cols{
  428.             return PEAR::raiseError('Incorrect column value');
  429.         }
  430.         for ($i=0; $i $this->_num_rows$i++{
  431.             $ret[$i$this->getElement($i,$col);
  432.         }
  433.         if ($asVector{
  434.             $classes get_declared_classes();
  435.             if (!in_array("math_vector"$classes|| !in_array("math_vectopop"$classes)) {
  436.                 return PEAR::raiseError ("Classes Math_Vector and Math_VectorOp undefined".
  437.                                     " add \"require_once 'Math/Vector/Vector.php'\" to your script");
  438.             }
  439.             return new Math_Vector($ret);
  440.         else {
  441.             return $ret;
  442.         }
  443.     }/*}}}*/
  444.  
  445.     /**
  446.      * Sets the column with the given index to the array
  447.      *
  448.      * This method checks that the column is less than the size of the matrix
  449.      * columns, and that the array size equals the number of rows in the
  450.      * matrix.
  451.      *
  452.      * @param integer $col index of the column
  453.      * @param array $arr array of numbers
  454.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  455.      */
  456.     function setCol ($col$arr{/*{{{*/
  457.         if ($this->isEmpty()) {
  458.             return PEAR::raiseError('Matrix has not been populated');
  459.         }
  460.         if ($col >= $this->_num_cols{
  461.             return PEAR::raiseError('Incorrect column value');
  462.         }
  463.         if (count($arr!= $this->_num_cols{
  464.             return PEAR::raiseError('Incorrect size for matrix column');
  465.         }
  466.         for ($i=0; $i $this->_num_rows$i++{
  467.             if (!is_numeric($arr[$i])) {
  468.                 return PEAR::raiseError('Incorrect values, expecting numbers');
  469.             else {
  470.                 $err $this->setElement($i$col$arr[$i]);
  471.                 if (PEAR::isError($err)) {
  472.                     return $err;
  473.                 }
  474.             }
  475.  
  476.         }
  477.         return true;
  478.     }/*}}}*/
  479.  
  480.     /**
  481.      * Swaps the rows with the given indices
  482.      *
  483.      * @param integer $i 
  484.      * @param integer $j 
  485.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  486.      */
  487.     function swapRows($i$j{/*{{{*/
  488.         $r1 $this->getRow($i);
  489.         if (PEAR::isError($r1)) {
  490.             return $r1;
  491.         }
  492.         $r2 $this->getRow($j);
  493.         if (PEAR::isError($r2)) {
  494.             return $r2;
  495.         }
  496.         $e $this->setRow($j$r1);
  497.         if (PEAR::isError($e)) {
  498.             return $e;
  499.         }
  500.         $e $this->setRow($i$r2);
  501.         if (PEAR::isError($e)) {
  502.             return $e;
  503.         }
  504.         return true;
  505.     }/*}}}*/
  506.  
  507.     /**
  508.      * Swaps the columns with the given indices
  509.      *
  510.      * @param integer $i 
  511.      * @param integer $j 
  512.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  513.      */
  514.     function swapCols($i$j{/*{{{*/
  515.         $r1 $this->getCol($i);
  516.         if (PEAR::isError($r1)) {
  517.             return $r1;
  518.         }
  519.         $r2 $this->getCol($j);
  520.         if (PEAR::isError($r2)) {
  521.             return $r2;
  522.         }
  523.         $e $this->setCol($j$r1);
  524.         if (PEAR::isError($e)) {
  525.             return $e;
  526.         }
  527.         $e $this->setCol($i$r2);
  528.         if (PEAR::isError($e)) {
  529.             return $e;
  530.         }
  531.         return true;
  532.     }/*}}}*/
  533.  
  534.     /**
  535.      * Swaps a given row with a given column. Only valid for square matrices.
  536.      *
  537.      * @param integer $row index of row
  538.      * @param integer $col index of column
  539.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  540.      */
  541.     function swapRowCol ($row$col{/*{{{*/
  542.         if (!$this->isSquare(|| !is_int($row|| !is_int($col)) {
  543.             return PEAR::raiseError("Parameters must be row and a column indices");
  544.         }
  545.         $c $this->getCol($col);
  546.         if (PEAR::isError($c)) {
  547.             return $c;
  548.         }
  549.         $r $this->getRow($row);
  550.         if (PEAR::isError($r)) {
  551.             return $r;
  552.         }
  553.         $e $this->setCol($col$r);
  554.         if (PEAR::isError($e)) {
  555.             return $e;
  556.         }
  557.         $e $this->setRow($row$c);
  558.         if (PEAR::isError($e)) {
  559.             return $e;
  560.         }
  561.         return true;
  562.     }/*}}}*/
  563.  
  564.     /**
  565.      * Returns the minimum value of the elements in the matrix
  566.      *
  567.      * @return number|PEAR_Errora number on success, a PEAR_Error otherwise
  568.      */
  569.     function getMin ({/*{{{*/
  570.         if ($this->isEmpty()) {
  571.             return PEAR::raiseError('Matrix has not been populated');
  572.         else {
  573.             return $this->_min;
  574.         }
  575.     }/*}}}*/
  576.  
  577.     /**
  578.      * Returns the maximum value of the elements in the matrix
  579.      *
  580.      * @return number|PEAR_Errora number on success, a PEAR_Error otherwise
  581.      */
  582.     function getMax ({/*{{{*/
  583.         if ($this->isEmpty()) {
  584.             return PEAR::raiseError('Matrix has not been populated');
  585.         else {
  586.             return $this->_max;
  587.         }
  588.     }/*}}}*/
  589.  
  590.     /**
  591.      * Gets the position of the first element with the given value
  592.      *
  593.      * @param numeric $val 
  594.      * @return array|PEAR_Erroran array of two numbers on success, FALSE if value is not found, and PEAR_Error otherwise
  595.      */
  596.     function getValueIndex ($val{/*{{{*/
  597.         if ($this->isEmpty()) {
  598.             return PEAR::raiseError('Matrix has not been populated');
  599.         }
  600.         for ($i=0; $i $this->_num_rows$i++{
  601.             for ($j=0; $j $this->_num_cols$j++{
  602.                 if ($this->_data[$i][$j== $val{
  603.                     return array($i$j);
  604.                 }
  605.             }
  606.         }
  607.         return false;
  608.     }/*}}}*/
  609.  
  610.     /**
  611.      * Gets the position of the element with the minimum value
  612.      *
  613.      * @return array|PEAR_Erroran array of two numbers on success, FALSE if value is not found, and PEAR_Error otherwise
  614.      * @see getValueIndex()
  615.      */
  616.     function getMinIndex ({/*{{{*/
  617.         if ($this->isEmpty()) {
  618.             return PEAR::raiseError('Matrix has not been populated');
  619.         else {
  620.             return $this->getValueIndex($this->_min);
  621.         }
  622.     }/*}}}*/
  623.  
  624.     /**
  625.      * Gets the position of the element with the maximum value
  626.      *
  627.      * @return array|PEAR_Erroran array of two numbers on success, FALSE if value is not found, and PEAR_Error otherwise
  628.      * @see getValueIndex()
  629.      */
  630.     function getMaxIndex ({/*{{{*/
  631.         if ($this->isEmpty()) {
  632.             return PEAR::raiseError('Matrix has not been populated');
  633.         else {
  634.             return $this->getValueIndex($this->_max);
  635.         }
  636.     }/*}}}*/
  637.  
  638.     /**
  639.      * Transpose the matrix rows and columns
  640.      *
  641.      * @return boolean|PEAR_ErrorTRUE on success, PEAR_Error otherwise
  642.      */
  643.     function transpose ({/*{{{*/
  644.         /* John Pye noted that this operation is defined for
  645.          * any matrix
  646.         if (!$this->isSquare()) {
  647.             return PEAR::raiseError("Transpose is undefined for non-sqaure matrices");
  648.         }
  649.         */
  650.         list($nr$nc$this->getSize();
  651.         $data = array();
  652.         for ($i=0; $i $nc$i++{
  653.             $col $this->getCol($i);
  654.             if (PEAR::isError($col)) {
  655.                 return $col;
  656.             else {
  657.                 $data[$col;
  658.             }
  659.         }
  660.         return $this->setData($data);
  661.     }/*}}}*/
  662.  
  663.     /**
  664.      * Returns the trace of the matrix. Trace = sum(e[i][j]), for all i == j
  665.      *
  666.      * @return number|PEAR_Errora number on success, PEAR_Error otherwise
  667.      */
  668.     function trace({/*{{{*/
  669.         if ($this->isEmpty()) {
  670.             return PEAR::raiseError('Matrix has not been populated');
  671.         }
  672.         if (!$this->isSquare()) {
  673.             return PEAR::raiseError('Trace undefined for non-square matrices');
  674.         }
  675.         $trace = 0;
  676.         for ($i=0; $i $this->_num_rows$i++{
  677.             $trace += $this->getElement($i$i);
  678.         }
  679.         return $trace;
  680.     }/*}}}*/
  681.  
  682.     /**
  683.      * Calculates the matrix determinant using Gaussian elimination with partial pivoting.
  684.      *
  685.      * At each step of the pivoting proccess, it checks that the normalized
  686.      * determinant calculated so far is less than 10^-18, trying to detect
  687.      * singular or ill-conditioned matrices
  688.      *
  689.      * @return number|PEAR_Errora number on success, a PEAR_Error otherwise
  690.      */
  691.     function determinant({/*{{{*/
  692.         if (!is_null($this->_det&& is_numeric($this->_det)) {
  693.             return $this->_det;
  694.         }
  695.         if ($this->isEmpty()) {
  696.             return PEAR::raiseError('Matrix has not been populated');
  697.         }
  698.         if (!$this->isSquare()) {
  699.             return PEAR::raiseError('Determinant undefined for non-square matrices');
  700.         }
  701.         $norm $this->norm();
  702.         if (PEAR::isError($norm)) {
  703.             return $norm;
  704.         }
  705.         $det = 1.0;
  706.         $sign = 1;
  707.         // work on a copy
  708.         $m $this->cloneMatrix();
  709.         list($nr$nc$m->getSize();
  710.         for ($r=0; $r<$nr$r++{
  711.             // find the maximum element in the column under the current diagonal element
  712.             $ridx $m->_maxElementIndex($r);
  713.             if (PEAR::isError($ridx)) {
  714.                 return $ridx;
  715.             }
  716.             if ($ridx != $r{
  717.                 $sign = -$sign;
  718.                 $e $m->swapRows($r$ridx);
  719.                 if (PEAR::isError($e)) {
  720.                     return $e;
  721.                 }
  722.             }
  723.             // pivoting element
  724.             $pelement $m->getElement($r$r);
  725.             if (PEAR::isError($pelement)) {
  726.                 return $pelement;
  727.             }
  728.             $det *= $pelement;
  729.             // Is this an singular or ill-conditioned matrix?
  730.             // i.e. is the normalized determinant << 1 and -> 0?
  731.             if ((abs($det)/$norm$this->_epsilon{
  732.                 return PEAR::raiseError('Probable singular or ill-conditioned matrix, normalized determinant = '
  733.                         .(abs($det)/$norm));
  734.             }
  735.             if ($pelement == 0{
  736.                 return PEAR::raiseError('Cannot continue, pivoting element is zero');
  737.             }
  738.             // zero all elements in column below the pivoting element
  739.             for ($i $r + 1; $i $nr$i++{
  740.                 $factor $m->getElement($i$r$pelement;
  741.                 for ($j=$r$j $nc$j++{
  742.                     $val $m->getElement($i$j$factor*$m->getElement($r$j);
  743.                     $e $m->setElement($i$j$val);
  744.                     if (PEAR::isError($e)) {
  745.                         return $e;
  746.                     }
  747.                 }
  748.             }
  749.             // for debugging
  750.             //echo "COLUMN: $r\n";
  751.             //echo $m->toString()."\n";
  752.         }
  753.         unset($m);
  754.         if ($sign < 0{
  755.             $det = -$det;
  756.         }
  757.         // save the value
  758.         $this->_det $det;
  759.         return $det;
  760.     }/*}}}*/
  761.  
  762.     /**
  763.      * Returns the normalized determinant = abs(determinant)/(euclidean norm)
  764.      *
  765.      * @return number|PEAR_Errora positive number on success, a PEAR_Error otherwise
  766.      */
  767.     function normalizedDeterminant({/*{{{*/
  768.         $det $this->determinant();
  769.         if (PEAR::isError($det)) {
  770.             return $det;
  771.         }
  772.         $norm $this->norm();
  773.         if (PEAR::isError($norm)) {
  774.             return $norm;
  775.         }
  776.         if ($norm == 0{
  777.             return PEAR::raiseError('Undefined normalized determinant, euclidean norm is zero');
  778.         }
  779.         return abs($det $norm);
  780.     }/*}}}*/
  781.  
  782.     /**
  783.      * Inverts a matrix using Gauss-Jordan elimination with partial pivoting
  784.      *
  785.      * @return number|PEAR_Errorthe value of the matrix determinant on success, PEAR_Error otherwise
  786.      * @see scaleRow()
  787.      */
  788.     function invert({/*{{{*/
  789.         if ($this->isEmpty()) {
  790.             return PEAR::raiseError('Matrix has not been populated');
  791.         }
  792.         if (!$this->isSquare()) {
  793.             return PEAR::raiseError('Determinant undefined for non-square matrices');
  794.         }
  795.         $norm $this->norm();
  796.         $sign = 1;
  797.         $det = 1.0;
  798.         // work on a copy to be safe
  799.         $m $this->cloneMatrix();
  800.         if (PEAR::isError($m)) {
  801.             return $m;
  802.         }
  803.         list($nr$nc$m->getSize();
  804.         // Unit matrix to use as target
  805.         $q Math_Matrix::makeUnit($nr);
  806.         if (PEAR::isError($q)) {
  807.             return $q;
  808.         }
  809.         for ($i=0; $i<$nr$i++{
  810.             $ridx $this->_maxElementIndex($i);
  811.             if ($i != $ridx{
  812.                 $sign = -$sign;
  813.                 $e $m->swapRows($i$ridx);
  814.                 if (PEAR::isError($e)) {
  815.                     return $e;
  816.                 }
  817.                 $e $q->swapRows($i$ridx);
  818.                 if (PEAR::isError($e)) {
  819.                     return $e;
  820.                 }
  821.             }
  822.             $pelement $m->getElement($i$i);
  823.             if (PEAR::isError($pelement)) {
  824.                 return $pelement;
  825.             }
  826.             if ($pelement == 0{
  827.                 return PEAR::raiseError('Cannot continue inversion, pivoting element is zero');
  828.             }
  829.             $det *= $pelement;
  830.             if ((abs($det)/$norm$this->_epsilon{
  831.                 return PEAR::raiseError('Probable singular or ill-conditioned matrix, normalized determinant = '
  832.                         .(abs($det)/$norm));
  833.             }
  834.             $e $m->scaleRow($i1/$pelement);
  835.             if (PEAR::isError($e)) {
  836.                 return $e;
  837.             }
  838.             $e $q->scaleRow($i1/$pelement);
  839.             if (PEAR::isError($e)) {
  840.                 return $e;
  841.             }
  842.             // zero all column elements execpt for the current one
  843.             $tpelement $m->getElement($i$i);
  844.             for ($j=0; $j<$nr$j++{
  845.                 if ($j == $i{
  846.                     continue;
  847.                 }
  848.                 $factor $m->getElement($j$i$tpelement;
  849.                 for ($k=0; $k<$nc$k++{
  850.                     $vm $m->getElement($j$k$factor $m->getElement($i$k);
  851.                     $vq $q->getElement($j$k$factor $q->getElement($i$k);
  852.                     $m->setElement($j$k$vm);
  853.                     $q->setElement($j$k$vq);
  854.                 }
  855.             }
  856.             // for debugging
  857.             /*
  858.             echo "COLUMN: $i\n";
  859.             echo $m->toString()."\n";
  860.             echo $q->toString()."\n";
  861.             */
  862.         }
  863.         $data $q->getData();
  864.         /*
  865.         // for debugging
  866.         echo $m->toString()."\n";
  867.         echo $q->toString()."\n";
  868.         */
  869.         unset($m);
  870.         unset($q);
  871.         $e $this->setData($data);
  872.         if (PEAR::isError($e)) {
  873.             return $e;
  874.         }
  875.         if ($sign < 0{
  876.             $det = -$det;
  877.         }
  878.         $this->_det $det;
  879.         return $det;
  880.     }/*}}}*/
  881.  
  882.     /**
  883.      * Returns a submatrix from the position (row, col), with nrows and ncols
  884.      *
  885.      * @return object Math_Matrix|PEAR_ErrorMath_Matrix on success, PEAR_Error otherwise
  886.      */
  887.     function &getSubMatrix ($row$col$nrows$ncols{/*{{{*/
  888.         if (!is_numeric($row|| !is_numeric($col)
  889.             || !is_numeric($nrows|| !is_numeric($ncols)) {
  890.             return PEAR::raiseError('Parameters must be a initial row and column, and number of rows and columns in submatrix');
  891.         }
  892.         list($nr$nc$this->getSize();
  893.         if ($row $nrows $nr{
  894.             return PEAR::raiseError('Rows in submatrix more than in original matrix');
  895.         }
  896.         if ($col $ncols $nc{
  897.             return PEAR::raiseError('Columns in submatrix more than in original matrix');
  898.         }
  899.         $data = array();
  900.         for ($i=0; $i $nrows$i++{
  901.             for ($j=0; $j $ncols$j++{
  902.                 $data[$i][$j$this->getElement($i $row$j $col);
  903.             }
  904.         }
  905.         $obj = new Math_Matrix($data);
  906.         return $obj;
  907.     }/*}}}*/
  908.  
  909.  
  910.     /**
  911.      * Returns the diagonal of a square matrix as a Math_Vector
  912.      *
  913.      * @return object Math_Vector|PEAR_ErrorMath_Vector on success, PEAR_Error otherwise
  914.      */
  915.     function &getDiagonal({/*{{{*/
  916.         if ($this->isEmpty()) {
  917.             return PEAR::raiseError('Matrix has not been populated');
  918.         }
  919.         if (!$this->isSquare()) {
  920.             return PEAR::raiseError('Cannot get diagonal vector of a non-square matrix');
  921.         }
  922.         list($n,$this->getSize();
  923.         $vals = array();
  924.         for ($i=0; $i<$n$i++{
  925.             $vals[$i$this->getElement($i$i);
  926.         }
  927.         return new Math_Vector($vals);
  928.     }/*}}}*/
  929.  
  930.     /**
  931.      * Returns a simple string representation of the matrix
  932.      *
  933.      * @param optional string $format a sprintf() format used to print the matrix elements (default = '%6.2f')
  934.      * @return string|PEAR_Errora string on success, PEAR_Error otherwise
  935.      */
  936.     function toString ($format='%6.2f'{/*{{{*/
  937.         if ($this->isEmpty()) {
  938.             return PEAR::raiseError('Matrix has not been populated');
  939.         }
  940.         $out "";
  941.         for ($i=0; $i $this->_num_rows$i++{
  942.             for ($j=0; $j $this->_num_cols$j++{
  943.                 // remove the -0.0 output
  944.                 $entry =  $this->_data[$i][$j];
  945.                 if (sprintf('%2.1f',$entry== '-0.0'{
  946.                     $entry = 0;
  947.                 }
  948.                 $out .= sprintf($format$entry);
  949.             }
  950.             $out .= "\n";
  951.         }
  952.         return $out;
  953.     }/*}}}*/
  954.  
  955.     /**
  956.      * Returns an HTML table representation of the matrix elements
  957.      *
  958.      * @return string on success, PEAR_Error otherwise
  959.      */
  960.     function toHTML({/*{{{*/
  961.         if ($this->isEmpty()) {
  962.             return PEAR::raiseError('Matrix has not been populated');
  963.         }
  964.         $out "<table border>\n\t<caption align=\"top\"><b>Matrix</b>";
  965.         $out .= "</caption>\n\t<tr align=\"center\">\n\t\t<th>";
  966.         $out .= $this->_num_rows."x".$this->_num_cols."</th>";
  967.         for ($i=0; $i $this->_num_cols$i++{
  968.             $out .= "<th>".$i."</th>";
  969.         }
  970.         $out .= "\n\t</tr>\n";
  971.         for ($i=0; $i $this->_num_rows$i++{
  972.             $out .= "\t<tr align=\"center\">\n\t\t<th>".$i."</th>";
  973.             for ($j=0; $j $this->_num_cols$j++{
  974.                 $out .= "<td bgcolor=\"#ffffdd\">".$this->_data[$i][$j]."</td>";
  975.             }
  976.             $out .= "\n\t</tr>";
  977.         }
  978.         return $out."\n</table>\n";
  979.     }/*}}}*/
  980.  
  981.     // private methods
  982.  
  983.     /**
  984.      * Returns the index of the row with the maximum value under column of the element e[i][i]
  985.      *
  986.      * @access private
  987.      * @return an integer
  988.      */
  989.     function _maxElementIndex($r{/*{{{*/
  990.         $max = 0;
  991.         $idx = -1;
  992.         list($nr$nc$this->getSize();
  993.         $arr = array();
  994.         for ($i=$r$i<$nr$i++{
  995.             $val abs($this->_data[$i][$r]);
  996.             if ($val $max{
  997.                 $max $val;
  998.                 $idx $i;
  999.             }
  1000.         }
  1001.         if ($idx == -1{
  1002.             $idx $r;
  1003.         }
  1004.         return $idx;
  1005.     }/*}}}*/
  1006.  
  1007.  
  1008.     // Binary operations
  1009.  
  1010.     /**#@+
  1011.      * @access public
  1012.      */
  1013.  
  1014.     /**
  1015.      * Adds a matrix to this one
  1016.      *
  1017.      * @param object Math_Matrix $m1 
  1018.      * @return boolean|PEAR_ErrorTRUE on success, PEAR_Error otherwise
  1019.      * @see getSize()
  1020.      * @see getElement()
  1021.      * @see setData()
  1022.      */
  1023.     function add ($m1{/*{{{*/
  1024.         if (!Math_Matrix::isMatrix($m1)) {
  1025.             return PEAR::raiseError("Parameter must be a Math_Matrix object");
  1026.         }
  1027.         if ($this->getSize(!= $m1->getSize()) {
  1028.             return PEAR::raiseError("Matrices must have the same dimensions");
  1029.         }
  1030.         list($nr$nc$m1->getSize();
  1031.         $data = array();
  1032.         for ($i=0; $i $nr$i++{
  1033.             for ($j=0; $j $nc$j++{
  1034.                 $el1 $m1->getElement($i,$j);
  1035.                 if (PEAR::isError($el1)) {
  1036.                     return $el1;
  1037.                 }
  1038.                 $el $this->getElement($i,$j);
  1039.                 if (PEAR::isError($el)) {
  1040.                     return $el;
  1041.                 }
  1042.                 $data[$i][$j$el $el1;
  1043.             }
  1044.         }
  1045.         if (!empty($data)) {
  1046.             return $this->setData($data);
  1047.         else {
  1048.             return PEAR::raiseError('Undefined error');
  1049.         }
  1050.     }/*}}}*/
  1051.  
  1052.     /**
  1053.      * Substracts a matrix from this one
  1054.      *
  1055.      * @param object Math_Matrix $m1 
  1056.      * @return boolean|PEAR_ErrorTRUE on success, PEAR_Error otherwise
  1057.      * @see getSize()
  1058.      * @see getElement()
  1059.      * @see setData()
  1060.      */
  1061.     function sub (&$m1{/*{{{*/
  1062.         if (!Math_Matrix::isMatrix($m1)) {
  1063.             return PEAR::raiseError("Parameter must be a Math_Matrix object");
  1064.         }
  1065.         if ($this->getSize(!= $m1->getSize()) {
  1066.             return PEAR::raiseError("Matrices must have the same dimensions");
  1067.         }
  1068.         list($nr$nc$m1->getSize();
  1069.         $data = array();
  1070.         for ($i=0; $i $nr$i++{
  1071.             for ($j=0; $j $nc$j++{
  1072.                 $el1 $m1->getElement($i,$j);
  1073.                 if (PEAR::isError($el1)) {
  1074.                     return $el1;
  1075.                 }
  1076.                 $el $this->getElement($i,$j);
  1077.                 if (PEAR::isError($el)) {
  1078.                     return $el;
  1079.                 }
  1080.                 $data[$i][$j$el $el1;
  1081.             }
  1082.         }
  1083.         if (!empty($data)) {
  1084.             return $this->setData($data);
  1085.         else {
  1086.             return PEAR::raiseError('Undefined error');
  1087.         }
  1088.     }/*}}}*/
  1089.  
  1090.     /**
  1091.      * Scales the matrix by a given factor
  1092.      *
  1093.      * @param numeric $scale the scaling factor
  1094.      * @return boolean|PEAR_ErrorTRUE on success, PEAR_Error otherwise
  1095.      * @see getSize()
  1096.      * @see getElement()
  1097.      * @see setData()
  1098.      */
  1099.     function scale ($scale{/*{{{*/
  1100.         if (!is_numeric($scale)) {
  1101.             return PEAR::raiseError("Parameter must be a number");
  1102.         }
  1103.         list($nr$nc$this->getSize();
  1104.         $data = array();
  1105.         for ($i=0; $i $nr$i++{
  1106.             for ($j=0; $j $nc$j++{
  1107.                 $data[$i][$j$scale $this->getElement($i,$j);
  1108.             }
  1109.         }
  1110.         if (!empty($data)) {
  1111.             return $this->setData($data);
  1112.         else {
  1113.             return PEAR::raiseError('Undefined error');
  1114.         }
  1115.     }/*}}}*/
  1116.  
  1117.     /**
  1118.      * Multiplies (scales) a row by the given factor
  1119.      *
  1120.      * @param integer $row the row index
  1121.      * @param numeric $factor the scaling factor
  1122.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  1123.      * @see invert()
  1124.      */
  1125.     function scaleRow($row$factor{/*{{{*/
  1126.         if ($this->isEmpty()) {
  1127.             return PEAR::raiseError('Uninitialized Math_Matrix object');
  1128.         }
  1129.         if (!is_integer($row|| !is_numeric($factor)) {
  1130.             return PEAR::raiseError('Row index must be an integer, and factor a valid number');
  1131.         }
  1132.         if ($row >= $this->_num_rows{
  1133.             return PEAR::raiseError('Row index out of bounds');
  1134.         }
  1135.         $r $this->getRow($row);
  1136.         if (PEAR::isError($r)) {
  1137.             return $r;
  1138.         }
  1139.         $nr count($r);
  1140.         for ($i=0; $i<$nr$i++{
  1141.             $r[$i*= $factor;
  1142.         }
  1143.         return $this->setRow($row$r);
  1144.     }/*}}}*/
  1145.  
  1146.     /**
  1147.      * Multiplies this matrix (A) by another one (B), and stores
  1148.      * the result back in A
  1149.      *
  1150.      * @param object Math_Matrix $m1 
  1151.      * @return boolean|PEAR_ErrorTRUE on success, PEAR_Error otherwise
  1152.      * @see getSize()
  1153.      * @see getRow()
  1154.      * @see getCol()
  1155.      * @see setData()
  1156.      * @see setZeroThreshold()
  1157.      */
  1158.     function multiply(&$B{/*{{{*/
  1159.         if (!Math_Matrix::isMatrix($B)) {
  1160.             return PEAR::raiseError ('Wrong parameter, expected a Math_Matrix object');
  1161.         }
  1162.         list($nrA$ncA$this->getSize();
  1163.         list($nrB$ncB$B->getSize();
  1164.         if ($ncA != $nrB{
  1165.             return PEAR::raiseError('Incompatible sizes columns in matrix must be the same as rows in parameter matrix');
  1166.         }
  1167.         $data = array();
  1168.         for ($i=0; $i $nrA$i++{
  1169.             $data[$i= array();
  1170.             for ($j=0; $j $ncB$j++{
  1171.                 $rctot = 0;
  1172.                 for ($k=0; $k $ncA$k++{
  1173.                     $rctot += $this->getElement($i,$k$B->getElement($k$j);
  1174.                 }
  1175.                 // take care of some round-off errors
  1176.                 if (abs($rctot<= $this->_epsilon{
  1177.                     $rctot = 0.0;
  1178.                 }
  1179.                 $data[$i][$j$rctot;
  1180.             }
  1181.         }
  1182.         if (!empty($data)) {
  1183.             return $this->setData($data);
  1184.         else {
  1185.             return PEAR::raiseError('Undefined error');
  1186.         }
  1187.     }/*}}}*/
  1188.  
  1189.     /**
  1190.      * Multiplies a vector by this matrix
  1191.      *
  1192.      * @param object Math_Vector $v1 
  1193.      * @return object Math_Vector|PEAR_ErrorMath_Vector on success, PEAR_Error otherwise
  1194.      * @see getSize()
  1195.      * @see getRow()
  1196.      * @see Math_Vector::get()
  1197.      */
  1198.     function &vectorMultiply(&$v1{/*{{{*/
  1199.         if (!Math_VectorOp::isVector($v1)) {
  1200.             return PEAR::raiseError ("Wrong parameter, a Math_Vector object");
  1201.         }
  1202.         list($nr$nc$this->getSize();
  1203.         $nv $v1->size();
  1204.         if ($nc != $nv{
  1205.             return PEAR::raiseError("Incompatible number of columns in matrix ($nc) must ".
  1206.                         "be the same as the number of elements ($nv) in the vector");
  1207.         }
  1208.         $data = array();
  1209.         for ($i=0; $i $nr$i++{
  1210.             $data[$i= 0;
  1211.             for ($j=0; $j $nv$j++{
  1212.                 $data[$i+= $this->getElement($i,$j$v1->get($j);
  1213.             }
  1214.         }
  1215.         $obj = new Math_Vector($data);
  1216.         return $obj;
  1217.     }/*}}}*/
  1218.  
  1219.     // Static operations
  1220.  
  1221.     /**@+
  1222.      * @static
  1223.      * @access public
  1224.      */
  1225.  
  1226.     /**
  1227.      * Create a matrix from a file, using data stored in the given format
  1228.      */
  1229.     function &readFromFile ($filename$format='serialized'{/*{{{*/
  1230.         if (!file_exists($filename|| !is_readable($filename)) {
  1231.             return PEAR::raiseError('File cannot be opened for reading');
  1232.         }
  1233.         if (filesize($filename== 0{
  1234.             return PEAR::raiseError('File is empty');
  1235.         }
  1236.         if ($format == 'serialized'{
  1237.             if (function_exists("file_get_contents")) {
  1238.                 $objser file_get_contents($filename);
  1239.             else {
  1240.                 $objser implode("",file($filename));
  1241.             }
  1242.             $obj unserialize($objser);
  1243.             if (Math_Matrix::isMatrix($obj)) {
  1244.                 return $obj;
  1245.             else {
  1246.                 return PEAR::raiseError('File did not contain a Math_Matrix object');
  1247.             }
  1248.         else // assume CSV data
  1249.             $data = array();
  1250.             $lines file($filename);
  1251.             foreach ($lines as $line{
  1252.                 if (preg_match('/^#/'$line)) {
  1253.                     continue;
  1254.                 else {
  1255.                     $data[explode(',',trim($line));
  1256.                 }
  1257.             }
  1258.             $m =new Math_Matrix();
  1259.             $e $m->setData($data);
  1260.             if (PEAR::isError($e)) {
  1261.                 return $e;
  1262.             else {
  1263.                 return $m;
  1264.             }
  1265.         }
  1266.     }/*}}}*/
  1267.  
  1268.     /**
  1269.      * Writes matrix object to a file using the given format
  1270.      *
  1271.      * @param object Math_Matrix $matrix the matrix object to store
  1272.      * @param string $filename name of file to contain the matrix data
  1273.      * @param optional string $format one of 'serialized' (default) or 'csv'
  1274.      * @return boolean|PEAR_ErrorTRUE on success, a PEAR_Error otherwise
  1275.      */
  1276.     function writeToFile($matrix$filename$format='serialized'{/*{{{*/
  1277.         if (!Math_Matrix::isMatrix($matrix)) {
  1278.             return PEAR::raiseError("Parameter must be a Math_Matrix object");
  1279.         }
  1280.         if ($matrix->isEmpty()) {
  1281.             return PEAR::raiseError("Math_Matrix object is empty");
  1282.         }
  1283.         if ($format == 'serialized'{
  1284.             $data serialize($matrix);
  1285.         else {
  1286.             $data '';
  1287.             list($nr$nc$matrix->getSize();
  1288.             for ($i=0; $i<$nr$i++{
  1289.                 $row $matrix->getRow($i);
  1290.                 if (PEAR::isError($row)) {
  1291.                     return $row;
  1292.                 }
  1293.                 $data .= implode(','$row)."\n";
  1294.             }
  1295.         }
  1296.         $fp fopen($filename"w");
  1297.         if (!$fp{
  1298.             return PEAR::raiseError("Cannot write matrix to file $filename");
  1299.         }
  1300.         fwrite($fp$data);
  1301.         fclose($fp);
  1302.         return true;
  1303.     }/*}}}*/
  1304.  
  1305.     /**
  1306.      * Checks if the object is a Math_Matrix instance
  1307.      *
  1308.      * @param object Math_Matrix $matrix 
  1309.      * @return boolean TRUE on success, FALSE otherwise
  1310.      */
  1311.     function isMatrix (&$matrix{/*{{{*/
  1312.         if (function_exists("is_a")) {
  1313.             return is_object($matrix&& is_a($matrix"Math_Matrix");
  1314.         else {
  1315.             return is_object($matrix&& (strtolower(get_class($matrix)) == "math_matrix");
  1316.         }
  1317.     }/*}}}*/
  1318.  
  1319.     /**
  1320.      * Returns a Math_Matrix object of size (nrows, ncols) filled with a value
  1321.      *
  1322.      * @param integer $nrows number of rows in the generated matrix
  1323.      * @param integer $ncols number of columns in the generated matrix
  1324.      * @param numeric $value the fill value
  1325.      * @return object Math_Matrix|PEAR_ErrorMath_Matrix instance on success, PEAR_Error otherwise
  1326.      */
  1327.     function &makeMatrix ($nrows$ncols$value{/*{{{*/
  1328.         if (!is_int($nrows&& is_int($ncols&& !is_numeric($value)) {
  1329.             return PEAR::raiseError('Number of rows, columns, and a numeric fill value expected');
  1330.         }
  1331.         for ($i=0; $i<$nrows$i++{
  1332.             $m[$iexplode(":",substr(str_repeat($value.":",$ncols),0,-1));
  1333.         }
  1334.  
  1335.         $obj = new Math_Matrix($m);
  1336.         return $obj;
  1337.  
  1338.     }/*}}}*/
  1339.  
  1340.     /**
  1341.      * Returns the Math_Matrix object of size (nrows, ncols), filled with the value 1 (one)
  1342.      *
  1343.      * @param integer $nrows number of rows in the generated matrix
  1344.      * @param integer $ncols number of columns in the generated matrix
  1345.      * @return object Math_Matrix|PEAR_ErrorMath_Matrix instance on success, PEAR_Error otherwise
  1346.      * @see Math_Matrix::makeMatrix()
  1347.      */
  1348.     function &makeOne ($nrows$ncols{/*{{{*/
  1349.         return Math_Matrix::makeMatrix ($nrows$ncols1);
  1350.     }/*}}}*/
  1351.  
  1352.     /**
  1353.      * Returns the Math_Matrix object of size (nrows, ncols), filled with the value 0 (zero)
  1354.      *
  1355.      * @param integer $nrows number of rows in the generated matrix
  1356.      * @param integer $ncols number of columns in the generated matrix
  1357.      * @return object Math_Matrix|PEAR_ErrorMath_Matrix instance on success, PEAR_Error otherwise
  1358.      * @see Math_Matrix::makeMatrix()
  1359.      */
  1360.     function &makeZero ($nrows$ncols{/*{{{*/
  1361.         return Math_Matrix::makeMatrix ($nrows$ncols0);
  1362.     }/*}}}*/
  1363.  
  1364.     /**
  1365.      * Returns a square unit Math_Matrix object of the given size
  1366.      *
  1367.      * A unit matrix is one in which the elements follow the rules:
  1368.      *  e[i][j] = 1, if i == j
  1369.      *  e[i][j] = 0, if i != j
  1370.      * Such a matrix is also called an 'identity matrix'
  1371.      *
  1372.      * @param integer $size number of rows and columns in the generated matrix
  1373.      * @return object Math_Matrix|PEAR_Errora square unit Math_Matrix instance on success, PEAR_Error otherwise
  1374.      * @see Math_Matrix::makeIdentity()
  1375.      */
  1376.     function &makeUnit ($size{/*{{{*/
  1377.         if (!is_integer($size)) {
  1378.             return PEAR::raiseError('An integer expected for the size of the Identity matrix');
  1379.         }
  1380.         for ($i=0; $i<$size$i++{
  1381.             for ($j=0; $j<$size$j++{
  1382.                 if ($i == $j{
  1383.                     $data[$i][$j= (float) 1.0;
  1384.                 else {
  1385.                     $data[$i][$j= (float) 0.0;
  1386.                 }
  1387.             }
  1388.         }
  1389.  
  1390.         $obj = new Math_Matrix($data);
  1391.         return $obj;
  1392.     }/*}}}*/
  1393.  
  1394.     /**
  1395.      * Returns the identity matrix of the given size. An alias of Math_Matrix::makeUnit()
  1396.      *
  1397.      * @param integer $size number of rows and columns in the generated matrix
  1398.      * @return object Math_Matrix|PEAR_Errora square unit Math_Matrix instance on success, PEAR_Error otherwise
  1399.      * @see Math_Matrix::makeUnit()
  1400.      */
  1401.     function &makeIdentity($size{/*{{{*/
  1402.         return Math_Matrix::makeUnit($size);
  1403.     }/*}}}*/
  1404.  
  1405.     // famous matrices
  1406.  
  1407.     /**
  1408.      * Returns a Hilbert matrix of the given size: H(i,j) = 1 / (i + j - 1) where {i,j = 1..n}
  1409.      *
  1410.      * @param integer $size number of rows and columns in the Hilbert matrix
  1411.      * @return object Math_Matrix|PEAR_Errora Hilber matrix on success, a PEAR_Error otherwise
  1412.      */
  1413.     function &makeHilbert($size{/*{{{*/
  1414.         if (!is_integer($size)) {
  1415.             return PEAR::raiseError('An integer expected for the size of the Hilbert matrix');
  1416.         }
  1417.         $data = array();
  1418.         for ($i=1; $i <= $size$i++{
  1419.             for ($j=1; $j <= $size$j++{
  1420.                 $data[$i - 1][$j - 1= 1 / ($i $j - 1);
  1421.             }
  1422.         }
  1423.         $obj = new Math_Matrix($data);
  1424.         return $obj;
  1425.     }/*}}}*/
  1426.  
  1427.     /**
  1428.      * Returns a Hankel matrix from a array of size m (C), and (optionally) of
  1429.      * an array if size n (R). C will define the first column and R the last
  1430.      * row. If R is not defined, C will be used. Also, if the last element of C
  1431.      * is not the same to the first element of R, the last element of C is
  1432.      * used.
  1433.      *
  1434.      * H(i,j) = C(i+j-1), i+j-1 <= m
  1435.      * H(i,j) = R(i+j-m), otherwise
  1436.      * where:
  1437.      *   i = 1..m
  1438.      *   j = 1..n
  1439.      *
  1440.      * @param array $c first column of Hankel matrix
  1441.      * @param optional array $r last row of Hankel matrix
  1442.      * @return object Math_Matrix|PEAR_Errora Hankel matrix on success, a PEAR_Error otherwise
  1443.      */
  1444.     function &makeHankel($c$r=null{/*{{{*/
  1445.         if (!is_array($c)) {
  1446.             return PEAR::raiseError('Expecting an array of values for the first column of the Hankel matrix');
  1447.         }
  1448.  
  1449.         if (is_null($r)) {
  1450.             $r == $c;
  1451.         }
  1452.  
  1453.         if (!is_array($r)) {
  1454.             return PEAR::raiseError('Expecting an array of values for the last row of the Hankel matrix');
  1455.         }
  1456.  
  1457.         $nc count($c);
  1458.         $nr count($r);
  1459.  
  1460.         // make sure that the first element of r is the same as the last element of c
  1461.         $r[0$c[$nc - 1];
  1462.  
  1463.         $data = array();
  1464.         for ($i=1; $i <= $nc$i++{
  1465.             for ($j=1; $j <= $nr$j++{
  1466.                 if (($i $j - 1<= $nc{
  1467.                     $val $c[($i $j - 1- 1];
  1468.                 else {
  1469.                     $val $r[($i $j $nc- 1];
  1470.                 }
  1471.                 $data[($i - 1)][($j - 1)$val;
  1472.             }
  1473.         }
  1474.         $obj = new Math_Matrix($data);
  1475.         return $obj;
  1476.  
  1477.     }/*}}}*/
  1478.  
  1479.  
  1480.     // methods for solving linear equations
  1481.  
  1482.     /**
  1483.      * Solves a system of linear equations: Ax = b
  1484.      *
  1485.      * A system such as:
  1486.      * <pre>
  1487.      *     a11*x1 + a12*x2 + ... + a1n*xn = b1
  1488.      *     a21*x1 + a22*x2 + ... + a2n*xn = b2
  1489.      *     ...
  1490.      *     ak1*x1 + ak2*x2 + ... + akn*xn = bk
  1491.      * </pre>
  1492.      * can be rewritten as:
  1493.      * <pre>
  1494.      *     Ax = b
  1495.      * </pre>
  1496.      * where:
  1497.      * - A is matrix of coefficients (aij, i=1..k, j=1..n),
  1498.      * - b a vector of values (bi, i=1..k),
  1499.      * - x the vector of unkowns (xi, i=1..n)
  1500.      * Using: x = (Ainv)*b
  1501.      * where:
  1502.      * - Ainv is the inverse of A
  1503.      *
  1504.      * @param object Math_Matrix $a the matrix of coefficients
  1505.      * @param object Math_Vector $b the vector of values
  1506.      * @return object Math_Vector|PEAR_Errora Math_Vector object on succcess, PEAR_Error otherwise
  1507.      * @see vectorMultiply()
  1508.      */
  1509.     function solve($a$b{/*{{{*/
  1510.         // check that the vector classes are defined
  1511.         if (!Math_Matrix::isMatrix($a&& !Math_VectorOp::isVector($b)) {
  1512.             return PEAR::raiseError('Incorrect parameters, expecting a Math_Matrix and a Math_Vector');
  1513.         }
  1514.         $e $a->invert();
  1515.         if (PEAR::isError($e)) {
  1516.             return $e;
  1517.         }
  1518.         return $a->vectorMultiply($b);
  1519.     }/*}}}*/
  1520.  
  1521.     /**
  1522.      * Solves a system of linear equations: Ax = b, using an iterative error correction algorithm
  1523.      *
  1524.      * A system such as:
  1525.      * <pre>
  1526.      *     a11*x1 + a12*x2 + ... + a1n*xn = b1
  1527.      *     a21*x1 + a22*x2 + ... + a2n*xn = b2
  1528.      *     ...
  1529.      *     ak1*x1 + ak2*x2 + ... + akn*xn = bk
  1530.      * </pre>
  1531.      * can be rewritten as:
  1532.      * <pre>
  1533.      *     Ax = b
  1534.      * </pre>
  1535.      * where:
  1536.      * - A is matrix of coefficients (aij, i=1..k, j=1..n),
  1537.      * - b a vector of values (bi, i=1..k),
  1538.      * - x the vector of unkowns (xi, i=1..n)
  1539.      * Using: x = (Ainv)*b
  1540.      * where:
  1541.      * - Ainv is the inverse of A
  1542.      *
  1543.      * The error correction algorithm uses the approach that if:
  1544.      * - xp is the approximate solution
  1545.      * - bp the values obtained from pluging xp into the original equation
  1546.      * We obtain
  1547.      * <pre>
  1548.      *     A(x - xp) = (b - bp),
  1549.      *     or
  1550.      *     A*xadj = (b - bp)
  1551.      * </pr>
  1552.      * where:
  1553.      * - xadj is the adjusted value (= Ainv*(b - bp))
  1554.      * therefore, we calculate iteratively new values of x using the estimated
  1555.      * xadj and testing to check if we have decreased the error.
  1556.      *
  1557.      * @param object Math_Matrix $a the matrix of coefficients
  1558.      * @param object Math_Vector $b the vector of values
  1559.      * @return object Math_Vector|PEAR_Errora Math_Vector object on succcess, PEAR_Error otherwise
  1560.      * @see vectorMultiply()
  1561.      * @see invert()
  1562.      * @see Math_VectorOp::add()
  1563.      * @see Math_VectorOp::substract()
  1564.      * @see Math_VectorOp::length()
  1565.      */
  1566.     function solveEC($a$b{/*{{{*/
  1567.         $ainv $a->cloneMatrix();
  1568.         $e $ainv->invert();
  1569.         if (PEAR::isError($e)) {
  1570.             return $e;
  1571.         }
  1572.         $x $ainv->vectorMultiply($b);
  1573.         if (PEAR::isError($x)) {
  1574.             return $x;
  1575.         }
  1576.         // initial guesses
  1577.         $bprime $a->vectorMultiply($x);
  1578.         if (PEAR::isError($bprime)) {
  1579.             return $bprime;
  1580.         }
  1581.         $err = Math_VectorOp::substract($b$bprime);
  1582.         $adj $ainv->vectorMultiply($err);
  1583.         if (PEAR::isError($adj)) {
  1584.             return $adj;
  1585.         }
  1586.         $adjnorm $adj->length();
  1587.         $xnew $x;
  1588.  
  1589.         // compute new solutions and test for accuracy
  1590.         // iterate no more than 10 times
  1591.         for ($i=0; $i<10; $i++{
  1592.             $xnew = Math_VectorOp::add($x$adj);
  1593.             $bprime $a->vectorMultiply($xnew);
  1594.             $err = Math_VectorOp::substract($b$bprime);
  1595.             $newadj $ainv->vectorMultiply($err);
  1596.             $newadjnorm $newadj->length();
  1597.             // did we improve the accuracy?
  1598.             if ($newadjnorm $adjnorm{
  1599.                 $adjnorm $newadjnorm;
  1600.                 $x $xnew;
  1601.                 $adj $newadj;
  1602.             else // we did improve the accuracy, so break;
  1603.                 break;
  1604.             }
  1605.         }
  1606.         return $x;
  1607.     }/*}}}*/
  1608.  
  1609. // end of Math_Matrix class /*}}}*/
  1610.  
  1611. // vim: ts=4:sw=4:et:
  1612. // vim6: fdl=1:
  1613.  
  1614. ?>

Documentation generated on Mon, 11 Mar 2019 15:39:23 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.