Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  
   3  namespace Matrix;
   4  
   5  class Functions
   6  {
   7      /**
   8       * Calculate the adjoint of the matrix
   9       *
  10       * @param Matrix $matrix The matrix whose adjoint we wish to calculate
  11       * @return Matrix
  12       *
  13       * @throws Exception
  14       */
  15      private static function getAdjoint(Matrix $matrix)
  16      {
  17          return self::transpose(
  18              self::getCofactors($matrix)
  19          );
  20      }
  21  
  22      /**
  23       * Return the adjoint of this matrix
  24       * The adjugate, classical adjoint, or adjunct of a square matrix is the transpose of its cofactor matrix.
  25       * The adjugate has sometimes been called the "adjoint", but today the "adjoint" of a matrix normally refers
  26       *     to its corresponding adjoint operator, which is its conjugate transpose.
  27       *
  28       * @param Matrix $matrix The matrix whose adjoint we wish to calculate
  29       * @return Matrix
  30       * @throws Exception
  31       **/
  32      public static function adjoint(Matrix $matrix)
  33      {
  34          if (!$matrix->isSquare()) {
  35              throw new Exception('Adjoint can only be calculated for a square matrix');
  36          }
  37  
  38          return self::getAdjoint($matrix);
  39      }
  40  
  41      /**
  42       * Calculate the cofactors of the matrix
  43       *
  44       * @param Matrix $matrix The matrix whose cofactors we wish to calculate
  45       * @return Matrix
  46       *
  47       * @throws Exception
  48       */
  49      private static function getCofactors(Matrix $matrix)
  50      {
  51          $cofactors = self::getMinors($matrix);
  52          $dimensions = $matrix->rows;
  53  
  54          $cof = 1;
  55          for ($i = 0; $i < $dimensions; ++$i) {
  56              $cofs = $cof;
  57              for ($j = 0; $j < $dimensions; ++$j) {
  58                  $cofactors[$i][$j] *= $cofs;
  59                  $cofs = -$cofs;
  60              }
  61              $cof = -$cof;
  62          }
  63  
  64          return new Matrix($cofactors);
  65      }
  66  
  67      /**
  68       * Return the cofactors of this matrix
  69       *
  70       * @param Matrix $matrix The matrix whose cofactors we wish to calculate
  71       * @return Matrix
  72       *
  73       * @throws Exception
  74       */
  75      public static function cofactors(Matrix $matrix)
  76      {
  77          if (!$matrix->isSquare()) {
  78              throw new Exception('Cofactors can only be calculated for a square matrix');
  79          }
  80  
  81          return self::getCofactors($matrix);
  82      }
  83  
  84      /**
  85       * @param Matrix $matrix
  86       * @param int $row
  87       * @param int $column
  88       * @return float
  89       * @throws Exception
  90       */
  91      private static function getDeterminantSegment(Matrix $matrix, $row, $column)
  92      {
  93          $tmpMatrix = $matrix->toArray();
  94          unset($tmpMatrix[$row]);
  95          array_walk(
  96              $tmpMatrix,
  97              function (&$row) use ($column) {
  98                  unset($row[$column]);
  99              }
 100          );
 101  
 102          return self::getDeterminant(new Matrix($tmpMatrix));
 103      }
 104  
 105      /**
 106       * Calculate the determinant of the matrix
 107       *
 108       * @param Matrix $matrix The matrix whose determinant we wish to calculate
 109       * @return float
 110       *
 111       * @throws Exception
 112       */
 113      private static function getDeterminant(Matrix $matrix)
 114      {
 115          $dimensions = $matrix->rows;
 116          $determinant = 0;
 117  
 118          switch ($dimensions) {
 119              case 1:
 120                  $determinant = $matrix->getValue(1, 1);
 121                  break;
 122              case 2:
 123                  $determinant = $matrix->getValue(1, 1) * $matrix->getValue(2, 2) -
 124                      $matrix->getValue(1, 2) * $matrix->getValue(2, 1);
 125                  break;
 126              default:
 127                  for ($i = 1; $i <= $dimensions; ++$i) {
 128                      $det = $matrix->getValue(1, $i) * self::getDeterminantSegment($matrix, 0, $i - 1);
 129                      if (($i % 2) == 0) {
 130                          $determinant -= $det;
 131                      } else {
 132                          $determinant += $det;
 133                      }
 134                  }
 135                  break;
 136          }
 137  
 138          return $determinant;
 139      }
 140  
 141      /**
 142       * Return the determinant of this matrix
 143       *
 144       * @param Matrix $matrix The matrix whose determinant we wish to calculate
 145       * @return float
 146       * @throws Exception
 147       **/
 148      public static function determinant(Matrix $matrix)
 149      {
 150          if (!$matrix->isSquare()) {
 151              throw new Exception('Determinant can only be calculated for a square matrix');
 152          }
 153  
 154          return self::getDeterminant($matrix);
 155      }
 156  
 157      /**
 158       * Return the diagonal of this matrix
 159       *
 160       * @param Matrix $matrix The matrix whose diagonal we wish to calculate
 161       * @return Matrix
 162       * @throws Exception
 163       **/
 164      public static function diagonal(Matrix $matrix)
 165      {
 166          if (!$matrix->isSquare()) {
 167              throw new Exception('Diagonal can only be extracted from a square matrix');
 168          }
 169  
 170          $dimensions = $matrix->rows;
 171          $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
 172              ->toArray();
 173  
 174          for ($i = 0; $i < $dimensions; ++$i) {
 175              $grid[$i][$i] = $matrix->getValue($i + 1, $i + 1);
 176          }
 177  
 178          return new Matrix($grid);
 179      }
 180  
 181      /**
 182       * Return the antidiagonal of this matrix
 183       *
 184       * @param Matrix $matrix The matrix whose antidiagonal we wish to calculate
 185       * @return Matrix
 186       * @throws Exception
 187       **/
 188      public static function antidiagonal(Matrix $matrix)
 189      {
 190          if (!$matrix->isSquare()) {
 191              throw new Exception('Anti-Diagonal can only be extracted from a square matrix');
 192          }
 193  
 194          $dimensions = $matrix->rows;
 195          $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
 196              ->toArray();
 197  
 198          for ($i = 0; $i < $dimensions; ++$i) {
 199              $grid[$i][$dimensions - $i - 1] = $matrix->getValue($i + 1, $dimensions - $i);
 200          }
 201  
 202          return new Matrix($grid);
 203      }
 204  
 205      /**
 206       * Return the identity matrix
 207       * The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix
 208       *   with ones on the main diagonal and zeros elsewhere
 209       *
 210       * @param Matrix $matrix The matrix whose identity we wish to calculate
 211       * @return Matrix
 212       * @throws Exception
 213       **/
 214      public static function identity(Matrix $matrix)
 215      {
 216          if (!$matrix->isSquare()) {
 217              throw new Exception('Identity can only be created for a square matrix');
 218          }
 219  
 220          $dimensions = $matrix->rows;
 221  
 222          return Builder::createIdentityMatrix($dimensions);
 223      }
 224  
 225      /**
 226       * Return the inverse of this matrix
 227       *
 228       * @param Matrix $matrix The matrix whose inverse we wish to calculate
 229       * @return Matrix
 230       * @throws Exception
 231       **/
 232      public static function inverse(Matrix $matrix)
 233      {
 234          if (!$matrix->isSquare()) {
 235              throw new Exception('Inverse can only be calculated for a square matrix');
 236          }
 237  
 238          $determinant = self::getDeterminant($matrix);
 239          if ($determinant == 0.0) {
 240              throw new Exception('Inverse can only be calculated for a matrix with a non-zero determinant');
 241          }
 242  
 243          if ($matrix->rows == 1) {
 244              return new Matrix([[1 / $matrix->getValue(1, 1)]]);
 245          }
 246  
 247          return self::getAdjoint($matrix)
 248              ->multiply(1 / $determinant);
 249      }
 250  
 251      /**
 252       * Calculate the minors of the matrix
 253       *
 254       * @param Matrix $matrix The matrix whose minors we wish to calculate
 255       * @return array[]
 256       *
 257       * @throws Exception
 258       */
 259      protected static function getMinors(Matrix $matrix)
 260      {
 261          $minors = $matrix->toArray();
 262          $dimensions = $matrix->rows;
 263          if ($dimensions == 1) {
 264              return $minors;
 265          }
 266  
 267          for ($i = 0; $i < $dimensions; ++$i) {
 268              for ($j = 0; $j < $dimensions; ++$j) {
 269                  $minors[$i][$j] = self::getDeterminantSegment($matrix, $i, $j);
 270              }
 271          }
 272  
 273          return $minors;
 274      }
 275  
 276      /**
 277       * Return the minors of the matrix
 278       * The minor of a matrix A is the determinant of some smaller square matrix, cut down from A by removing one or
 279       *     more of its rows or columns.
 280       * Minors obtained by removing just one row and one column from square matrices (first minors) are required for
 281       *     calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of
 282       *     square matrices.
 283       *
 284       * @param Matrix $matrix The matrix whose minors we wish to calculate
 285       * @return Matrix
 286       * @throws Exception
 287       **/
 288      public static function minors(Matrix $matrix)
 289      {
 290          if (!$matrix->isSquare()) {
 291              throw new Exception('Minors can only be calculated for a square matrix');
 292          }
 293  
 294          return new Matrix(self::getMinors($matrix));
 295      }
 296  
 297      /**
 298       * Return the trace of this matrix
 299       * The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right)
 300       *     of the matrix
 301       *
 302       * @param Matrix $matrix The matrix whose trace we wish to calculate
 303       * @return float
 304       * @throws Exception
 305       **/
 306      public static function trace(Matrix $matrix)
 307      {
 308          if (!$matrix->isSquare()) {
 309              throw new Exception('Trace can only be extracted from a square matrix');
 310          }
 311  
 312          $dimensions = $matrix->rows;
 313          $result = 0;
 314          for ($i = 1; $i <= $dimensions; ++$i) {
 315              $result += $matrix->getValue($i, $i);
 316          }
 317  
 318          return $result;
 319      }
 320  
 321      /**
 322       * Return the transpose of this matrix
 323       *
 324       * @param Matrix $matrix The matrix whose transpose we wish to calculate
 325       * @return Matrix
 326       **/
 327      public static function transpose(Matrix $matrix)
 328      {
 329          $array = array_values(array_merge([null], $matrix->toArray()));
 330          $grid = call_user_func_array(
 331              'array_map',
 332              $array
 333          );
 334  
 335          return new Matrix($grid);
 336      }
 337  }