3 namespace PhpOffice\PhpSpreadsheet\Shared\JAMA
;
5 use PhpOffice\PhpSpreadsheet\Calculation\Exception
as CalculationException
;
6 use PhpOffice\PhpSpreadsheet\Calculation\Functions
;
7 use PhpOffice\PhpSpreadsheet\Shared\StringHelper
;
12 * @author Paul Meagher
13 * @author Michael Bommarito
14 * @author Lukasz Karapuda
15 * @author Bartek Matosiuk
19 * @see http://math.nist.gov/javanumerics/jama/
23 const POLYMORPHIC_ARGUMENT_EXCEPTION
= 'Invalid argument pattern for polymorphic function.';
24 const ARGUMENT_TYPE_EXCEPTION
= 'Invalid argument type.';
25 const ARGUMENT_BOUNDS_EXCEPTION
= 'Invalid argument range.';
26 const MATRIX_DIMENSION_EXCEPTION
= 'Matrix dimensions are not equal.';
27 const ARRAY_LENGTH_EXCEPTION
= 'Array length must be a multiple of m.';
28 const MATRIX_SPD_EXCEPTION
= 'Can only perform operation on symmetric positive definite matrix.';
38 * Matrix row dimension.
45 * Matrix column dimension.
52 * Polymorphic constructor.
54 * As PHP has no support for polymorphic constructors, we use tricks to make our own sort of polymorphism using func_num_args, func_get_arg, and gettype. In essence, we're just implementing a simple RTTI filter and calling the appropriate constructor.
56 public function __construct(...$args)
58 if (count($args) > 0) {
59 $match = implode(',', array_map('gettype', $args));
62 //Rectangular matrix - m x n initialized from 2D array
64 $this->m
= count($args[0]);
65 $this->n
= count($args[0][0]);
69 //Square matrix - n x n
73 $this->A
= array_fill(0, $this->m
, array_fill(0, $this->n
, 0));
76 //Rectangular matrix - m x n
77 case 'integer,integer':
80 $this->A
= array_fill(0, $this->m
, array_fill(0, $this->n
, 0));
83 //Rectangular matrix - m x n initialized from packed array
87 $this->n
= count($args[0]) / $this->m
;
91 if (($this->m
* $this->n
) == count($args[0])) {
92 for ($i = 0; $i < $this->m
; ++
$i) {
93 for ($j = 0; $j < $this->n
; ++
$j) {
94 $this->A
[$i][$j] = $args[0][$i +
$j * $this->m
];
98 throw new CalculationException(self
::ARRAY_LENGTH_EXCEPTION
);
103 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
108 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
115 * @return array Matrix array
117 public function getArray()
125 * @return int Row dimension
127 public function getRowDimension()
133 * getColumnDimension.
135 * @return int Column dimension
137 public function getColumnDimension()
145 * Get the i,j-th element of the matrix.
147 * @param int $i Row position
148 * @param int $j Column position
150 * @return mixed Element (int/float/double)
152 public function get($i = null, $j = null)
154 return $this->A
[$i][$j];
162 * @param int $i0 Initial row index
163 * @param int $iF Final row index
164 * @param int $j0 Initial column index
165 * @param int $jF Final column index
167 * @return Matrix Submatrix
169 public function getMatrix(...$args)
171 if (count($args) > 0) {
172 $match = implode(',', array_map('gettype', $args));
176 case 'integer,integer':
177 list($i0, $j0) = $args;
181 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
186 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
188 $R = new self($m, $n);
189 for ($i = $i0; $i < $this->m
; ++
$i) {
190 for ($j = $j0; $j < $this->n
; ++
$j) {
191 $R->set($i, $j, $this->A
[$i][$j]);
198 //A($i0...$iF; $j0...$jF)
199 case 'integer,integer,integer,integer':
200 list($i0, $iF, $j0, $jF) = $args;
201 if (($iF > $i0) && ($this->m
>= $iF) && ($i0 >= 0)) {
204 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
206 if (($jF > $j0) && ($this->n
>= $jF) && ($j0 >= 0)) {
209 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
211 $R = new self($m +
1, $n +
1);
212 for ($i = $i0; $i <= $iF; ++
$i) {
213 for ($j = $j0; $j <= $jF; ++
$j) {
214 $R->set($i - $i0, $j - $j0, $this->A
[$i][$j]);
221 //$R = array of row indices; $C = array of column indices
223 list($RL, $CL) = $args;
224 if (count($RL) > 0) {
227 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
229 if (count($CL) > 0) {
232 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
234 $R = new self($m, $n);
235 for ($i = 0; $i < $m; ++
$i) {
236 for ($j = 0; $j < $n; ++
$j) {
237 $R->set($i - $i0, $j - $j0, $this->A
[$RL[$i]][$CL[$j]]);
244 //A($i0...$iF); $CL = array of column indices
245 case 'integer,integer,array':
246 list($i0, $iF, $CL) = $args;
247 if (($iF > $i0) && ($this->m
>= $iF) && ($i0 >= 0)) {
250 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
252 if (count($CL) > 0) {
255 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
257 $R = new self($m, $n);
258 for ($i = $i0; $i < $iF; ++
$i) {
259 for ($j = 0; $j < $n; ++
$j) {
260 $R->set($i - $i0, $j, $this->A
[$RL[$i]][$j]);
267 //$RL = array of row indices
268 case 'array,integer,integer':
269 list($RL, $j0, $jF) = $args;
270 if (count($RL) > 0) {
273 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
275 if (($jF >= $j0) && ($this->n
>= $jF) && ($j0 >= 0)) {
278 throw new CalculationException(self
::ARGUMENT_BOUNDS_EXCEPTION
);
280 $R = new self($m, $n +
1);
281 for ($i = 0; $i < $m; ++
$i) {
282 for ($j = $j0; $j <= $jF; ++
$j) {
283 $R->set($i, $j - $j0, $this->A
[$RL[$i]][$j]);
291 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
296 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
301 * checkMatrixDimensions.
303 * Is matrix B the same size?
305 * @param Matrix $B Matrix B
309 public function checkMatrixDimensions($B = null)
311 if ($B instanceof self
) {
312 if (($this->m
== $B->getRowDimension()) && ($this->n
== $B->getColumnDimension())) {
316 throw new CalculationException(self
::MATRIX_DIMENSION_EXCEPTION
);
319 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
322 // function checkMatrixDimensions()
327 * Set the i,j-th element of the matrix.
329 * @param int $i Row position
330 * @param int $j Column position
331 * @param mixed $c Int/float/double value
333 * @return mixed Element (int/float/double)
335 public function set($i = null, $j = null, $c = null)
337 // Optimized set version just has this
338 $this->A
[$i][$j] = $c;
346 * Generate an identity matrix.
348 * @param int $m Row dimension
349 * @param int $n Column dimension
351 * @return Matrix Identity matrix
353 public function identity($m = null, $n = null)
355 return $this->diagonal($m, $n, 1);
361 * Generate a diagonal matrix
363 * @param int $m Row dimension
364 * @param int $n Column dimension
365 * @param mixed $c Diagonal value
367 * @return Matrix Diagonal matrix
369 public function diagonal($m = null, $n = null, $c = 1)
371 $R = new self($m, $n);
372 for ($i = 0; $i < $m; ++
$i) {
382 * Get a submatrix by row index/range
384 * @param int $i0 Initial row index
385 * @param int $iF Final row index
387 * @return Matrix Submatrix
389 public function getMatrixByRow($i0 = null, $iF = null)
393 return $this->getMatrix($i0, 0, $iF +
1, $this->n
);
396 return $this->getMatrix($i0, 0, $i0 +
1, $this->n
);
399 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
405 * Get a submatrix by column index/range
407 * @param int $j0 Initial column index
408 * @param int $jF Final column index
410 * @return Matrix Submatrix
412 public function getMatrixByCol($j0 = null, $jF = null)
416 return $this->getMatrix(0, $j0, $this->m
, $jF +
1);
419 return $this->getMatrix(0, $j0, $this->m
, $j0 +
1);
422 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
430 * @return Matrix Transposed matrix
432 public function transpose()
434 $R = new self($this->n
, $this->m
);
435 for ($i = 0; $i < $this->m
; ++
$i) {
436 for ($j = 0; $j < $this->n
; ++
$j) {
437 $R->set($j, $i, $this->A
[$i][$j]);
444 // function transpose()
449 * Sum of diagonal elements
451 * @return float Sum of diagonal elements
453 public function trace()
456 $n = min($this->m
, $this->n
);
457 for ($i = 0; $i < $n; ++
$i) {
458 $s +
= $this->A
[$i][$i];
467 * Unary minus matrix -A
469 * @return Matrix Unary minus matrix
471 public function uminus()
480 * @param mixed $B Matrix/Array
484 public function plus(...$args)
486 if (count($args) > 0) {
487 $match = implode(',', array_map('gettype', $args));
491 if ($args[0] instanceof self
) {
494 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
499 $M = new self($args[0]);
503 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
507 $this->checkMatrixDimensions($M);
508 for ($i = 0; $i < $this->m
; ++
$i) {
509 for ($j = 0; $j < $this->n
; ++
$j) {
510 $M->set($i, $j, $M->get($i, $j) +
$this->A
[$i][$j]);
517 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
525 * @param mixed $B Matrix/Array
529 public function plusEquals(...$args)
531 if (count($args) > 0) {
532 $match = implode(',', array_map('gettype', $args));
536 if ($args[0] instanceof self
) {
539 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
544 $M = new self($args[0]);
548 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
552 $this->checkMatrixDimensions($M);
553 for ($i = 0; $i < $this->m
; ++
$i) {
554 for ($j = 0; $j < $this->n
; ++
$j) {
556 $value = $M->get($i, $j);
557 if ((is_string($this->A
[$i][$j])) && (strlen($this->A
[$i][$j]) > 0) && (!is_numeric($this->A
[$i][$j]))) {
558 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"');
559 $validValues &= StringHelper
::convertToNumberIfFraction($this->A
[$i][$j]);
561 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
562 $value = trim($value, '"');
563 $validValues &= StringHelper
::convertToNumberIfFraction($value);
566 $this->A
[$i][$j] +
= $value;
568 $this->A
[$i][$j] = Functions
::NAN();
576 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
584 * @param mixed $B Matrix/Array
588 public function minus(...$args)
590 if (count($args) > 0) {
591 $match = implode(',', array_map('gettype', $args));
595 if ($args[0] instanceof self
) {
598 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
603 $M = new self($args[0]);
607 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
611 $this->checkMatrixDimensions($M);
612 for ($i = 0; $i < $this->m
; ++
$i) {
613 for ($j = 0; $j < $this->n
; ++
$j) {
614 $M->set($i, $j, $M->get($i, $j) - $this->A
[$i][$j]);
621 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
629 * @param mixed $B Matrix/Array
633 public function minusEquals(...$args)
635 if (count($args) > 0) {
636 $match = implode(',', array_map('gettype', $args));
640 if ($args[0] instanceof self
) {
643 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
648 $M = new self($args[0]);
652 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
656 $this->checkMatrixDimensions($M);
657 for ($i = 0; $i < $this->m
; ++
$i) {
658 for ($j = 0; $j < $this->n
; ++
$j) {
660 $value = $M->get($i, $j);
661 if ((is_string($this->A
[$i][$j])) && (strlen($this->A
[$i][$j]) > 0) && (!is_numeric($this->A
[$i][$j]))) {
662 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"');
663 $validValues &= StringHelper
::convertToNumberIfFraction($this->A
[$i][$j]);
665 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
666 $value = trim($value, '"');
667 $validValues &= StringHelper
::convertToNumberIfFraction($value);
670 $this->A
[$i][$j] -= $value;
672 $this->A
[$i][$j] = Functions
::NAN();
680 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
686 * Element-by-element multiplication
689 * @param mixed $B Matrix/Array
691 * @return Matrix Matrix Cij
693 public function arrayTimes(...$args)
695 if (count($args) > 0) {
696 $match = implode(',', array_map('gettype', $args));
700 if ($args[0] instanceof self
) {
703 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
708 $M = new self($args[0]);
712 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
716 $this->checkMatrixDimensions($M);
717 for ($i = 0; $i < $this->m
; ++
$i) {
718 for ($j = 0; $j < $this->n
; ++
$j) {
719 $M->set($i, $j, $M->get($i, $j) * $this->A
[$i][$j]);
726 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
732 * Element-by-element multiplication
735 * @param mixed $B Matrix/Array
737 * @return Matrix Matrix Aij
739 public function arrayTimesEquals(...$args)
741 if (count($args) > 0) {
742 $match = implode(',', array_map('gettype', $args));
746 if ($args[0] instanceof self
) {
749 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
754 $M = new self($args[0]);
758 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
762 $this->checkMatrixDimensions($M);
763 for ($i = 0; $i < $this->m
; ++
$i) {
764 for ($j = 0; $j < $this->n
; ++
$j) {
766 $value = $M->get($i, $j);
767 if ((is_string($this->A
[$i][$j])) && (strlen($this->A
[$i][$j]) > 0) && (!is_numeric($this->A
[$i][$j]))) {
768 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"');
769 $validValues &= StringHelper
::convertToNumberIfFraction($this->A
[$i][$j]);
771 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
772 $value = trim($value, '"');
773 $validValues &= StringHelper
::convertToNumberIfFraction($value);
776 $this->A
[$i][$j] *= $value;
778 $this->A
[$i][$j] = Functions
::NAN();
786 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
792 * Element-by-element right division
795 * @param Matrix $B Matrix B
797 * @return Matrix Division result
799 public function arrayRightDivide(...$args)
801 if (count($args) > 0) {
802 $match = implode(',', array_map('gettype', $args));
806 if ($args[0] instanceof self
) {
809 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
814 $M = new self($args[0]);
818 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
822 $this->checkMatrixDimensions($M);
823 for ($i = 0; $i < $this->m
; ++
$i) {
824 for ($j = 0; $j < $this->n
; ++
$j) {
826 $value = $M->get($i, $j);
827 if ((is_string($this->A
[$i][$j])) && (strlen($this->A
[$i][$j]) > 0) && (!is_numeric($this->A
[$i][$j]))) {
828 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"');
829 $validValues &= StringHelper
::convertToNumberIfFraction($this->A
[$i][$j]);
831 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
832 $value = trim($value, '"');
833 $validValues &= StringHelper
::convertToNumberIfFraction($value);
837 // Trap for Divide by Zero error
838 $M->set($i, $j, '#DIV/0!');
840 $M->set($i, $j, $this->A
[$i][$j] / $value);
843 $M->set($i, $j, Functions
::NAN());
851 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
855 * arrayRightDivideEquals.
857 * Element-by-element right division
860 * @param mixed $B Matrix/Array
862 * @return Matrix Matrix Aij
864 public function arrayRightDivideEquals(...$args)
866 if (count($args) > 0) {
867 $match = implode(',', array_map('gettype', $args));
871 if ($args[0] instanceof self
) {
874 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
879 $M = new self($args[0]);
883 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
887 $this->checkMatrixDimensions($M);
888 for ($i = 0; $i < $this->m
; ++
$i) {
889 for ($j = 0; $j < $this->n
; ++
$j) {
890 $this->A
[$i][$j] = $this->A
[$i][$j] / $M->get($i, $j);
897 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
903 * Element-by-element Left division
906 * @param Matrix $B Matrix B
908 * @return Matrix Division result
910 public function arrayLeftDivide(...$args)
912 if (count($args) > 0) {
913 $match = implode(',', array_map('gettype', $args));
917 if ($args[0] instanceof self
) {
920 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
925 $M = new self($args[0]);
929 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
933 $this->checkMatrixDimensions($M);
934 for ($i = 0; $i < $this->m
; ++
$i) {
935 for ($j = 0; $j < $this->n
; ++
$j) {
936 $M->set($i, $j, $M->get($i, $j) / $this->A
[$i][$j]);
943 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
947 * arrayLeftDivideEquals.
949 * Element-by-element Left division
952 * @param mixed $B Matrix/Array
954 * @return Matrix Matrix Aij
956 public function arrayLeftDivideEquals(...$args)
958 if (count($args) > 0) {
959 $match = implode(',', array_map('gettype', $args));
963 if ($args[0] instanceof self
) {
966 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
971 $M = new self($args[0]);
975 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
979 $this->checkMatrixDimensions($M);
980 for ($i = 0; $i < $this->m
; ++
$i) {
981 for ($j = 0; $j < $this->n
; ++
$j) {
982 $this->A
[$i][$j] = $M->get($i, $j) / $this->A
[$i][$j];
989 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
995 * Matrix multiplication
997 * @param mixed $n Matrix/Array/Scalar
999 * @return Matrix Product
1001 public function times(...$args)
1003 if (count($args) > 0) {
1004 $match = implode(',', array_map('gettype', $args));
1008 if ($args[0] instanceof self
) {
1011 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
1013 if ($this->n
== $B->m
) {
1014 $C = new self($this->m
, $B->n
);
1015 for ($j = 0; $j < $B->n
; ++
$j) {
1016 for ($k = 0; $k < $this->n
; ++
$k) {
1017 $Bcolj[$k] = $B->A
[$k][$j];
1019 for ($i = 0; $i < $this->m
; ++
$i) {
1020 $Arowi = $this->A
[$i];
1022 for ($k = 0; $k < $this->n
; ++
$k) {
1023 $s +
= $Arowi[$k] * $Bcolj[$k];
1032 throw new CalculationException(self
::MATRIX_DIMENSION_EXCEPTION
);
1034 $B = new self($args[0]);
1035 if ($this->n
== $B->m
) {
1036 $C = new self($this->m
, $B->n
);
1037 for ($i = 0; $i < $C->m
; ++
$i) {
1038 for ($j = 0; $j < $C->n
; ++
$j) {
1040 for ($k = 0; $k < $C->n
; ++
$k) {
1041 $s +
= $this->A
[$i][$k] * $B->A
[$k][$j];
1050 throw new CalculationException(self
::MATRIX_DIMENSION_EXCEPTION
);
1052 $C = new self($this->A
);
1053 for ($i = 0; $i < $C->m
; ++
$i) {
1054 for ($j = 0; $j < $C->n
; ++
$j) {
1055 $C->A
[$i][$j] *= $args[0];
1061 $C = new self($this->m
, $this->n
);
1062 for ($i = 0; $i < $C->m
; ++
$i) {
1063 for ($j = 0; $j < $C->n
; ++
$j) {
1064 $C->A
[$i][$j] = $args[0] * $this->A
[$i][$j];
1070 $C = new self($this->A
);
1071 for ($i = 0; $i < $C->m
; ++
$i) {
1072 for ($j = 0; $j < $C->n
; ++
$j) {
1073 $C->A
[$i][$j] *= $args[0];
1079 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1082 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1091 * @param mixed $B Matrix/Array
1093 * @return Matrix Sum
1095 public function power(...$args)
1097 if (count($args) > 0) {
1098 $match = implode(',', array_map('gettype', $args));
1102 if ($args[0] instanceof self
) {
1105 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
1110 $M = new self($args[0]);
1114 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1118 $this->checkMatrixDimensions($M);
1119 for ($i = 0; $i < $this->m
; ++
$i) {
1120 for ($j = 0; $j < $this->n
; ++
$j) {
1121 $validValues = true;
1122 $value = $M->get($i, $j);
1123 if ((is_string($this->A
[$i][$j])) && (strlen($this->A
[$i][$j]) > 0) && (!is_numeric($this->A
[$i][$j]))) {
1124 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"');
1125 $validValues &= StringHelper
::convertToNumberIfFraction($this->A
[$i][$j]);
1127 if ((is_string($value)) && (strlen($value) > 0) && (!is_numeric($value))) {
1128 $value = trim($value, '"');
1129 $validValues &= StringHelper
::convertToNumberIfFraction($value);
1132 $this->A
[$i][$j] = pow($this->A
[$i][$j], $value);
1134 $this->A
[$i][$j] = Functions
::NAN();
1142 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1150 * @param mixed $B Matrix/Array
1152 * @return Matrix Sum
1154 public function concat(...$args)
1156 if (count($args) > 0) {
1157 $match = implode(',', array_map('gettype', $args));
1161 if ($args[0] instanceof self
) {
1164 throw new CalculationException(self
::ARGUMENT_TYPE_EXCEPTION
);
1169 $M = new self($args[0]);
1173 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1177 $this->checkMatrixDimensions($M);
1178 for ($i = 0; $i < $this->m
; ++
$i) {
1179 for ($j = 0; $j < $this->n
; ++
$j) {
1180 $this->A
[$i][$j] = trim($this->A
[$i][$j], '"') . trim($M->get($i, $j), '"');
1187 throw new CalculationException(self
::POLYMORPHIC_ARGUMENT_EXCEPTION
);
1193 * @param Matrix $B Right hand side
1195 * @return Matrix ... Solution if A is square, least squares solution otherwise
1197 public function solve($B)
1199 if ($this->m
== $this->n
) {
1200 $LU = new LUDecomposition($this);
1202 return $LU->solve($B);
1204 $QR = new QRDecomposition($this);
1206 return $QR->solve($B);
1210 * Matrix inverse or pseudoinverse.
1212 * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
1214 public function inverse()
1216 return $this->solve($this->identity($this->m
, $this->m
));
1222 * Calculate determinant
1224 * @return float Determinant
1226 public function det()
1228 $L = new LUDecomposition($this);