Matrix.php
21.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
<?php
/**
* matrix.class.php
*
* Will set up the defines for error checking as well as provide
* the Matrix class for include. As this is made to be modular,
* only the class (and possibly helper classes) along with their
* defines will be found in this file.
* @package Math
* @subpackage Matrix
*/
namespace jlawrence\eos;
/**
* Matrix Class
*
* This class will allow you to create and use Matrices
* as well as providing common Matrix operations. It
* uses PHP5 for OOP and Error Throwing and is commented
* for the PHPDoc Parser for documentation creation.
*
* @version $Id: matrix.class.php 10 2012-08-06 23:41:36Z jlawrence11 $
* @author Jon Lawrence <JLawrence11@gmail.com>
* @license http://opensource.org/licenses/LGPL-2.1 LGPL 2.1 License
* @copyright Copyright �2012, Jon Lawrence
* @package Math
* @subpackage Matrix
*/
class Matrix {
/**
* Invalid String input type
*/
const E_INVALID_INPUT = 5001;
/**
* Matrix needed to be a square matrix for the operation
*/
const E_NOT_SQUARE = 5002;
/**
* Matrix was undefined
*/
const E_NO_MATRIX = 5003;
/**
* Matrix had varying column lengths
*/
const E_INVALID_MATRIX = 5004;
/**
* Matrix operation required rows/cols to be even, they were not
*/
const E_NOT_EQUAL = 5005;
/**
* Determinate was '0' while preforming another operation
*/
const E_NO_INVERSE = 5006;
private $matrix;
/**
* Construct method
*
* For format of input string, see the see tag below
*
* @see Matrix::_assign()
* @param string $mText Matrix text input
*/
public function __construct($mText="")
{
if ($mText) $this->_assign($mText);
}
/**
* Create a matrix based on string input similar to the TI Calculators
* input string "[1,2,3;4,5,6;7,8,9]" is the equivalent of the matrix:
* <pre>
* | 1 2 3 |
* | 4 5 6 |
* | 7 8 9 |
* </pre>
*
* @param String $mText The matrix in string format to assign to the current object
* @return Boolean True if is passes verification after being converted
* @throws \Exception If the input text is not in a valid format
*/
public function _assign($mText)
{
if(trim($mText)=="")
return false;
$mText = preg_replace("/\s/", "", $mText);
if(!preg_match("/^\[(([\-]*[0-9\. ]+[,]{0,1})+[;]{0,1})*\]$/", $mText)) {
throw new \Exception("'{$mText}' is not a valid input", Matrix::E_INVALID_INPUT);
}
$mText = preg_replace("/(\[|\])/", "", $mText);
$rows = explode(";", $mText);
$i=0;$j=0;
foreach($rows as $row)
{
$cols = explode(",", $row);
foreach($cols as $value)
{
$this->matrix[$i][$j] = $value;
$j++;
}
$i++;
$j = 0;
}
return $this->_verify();
}
/**
* Private function that will verify all the columns have the same
* number of items, ensuring it is a valid matrix
*
* @access private
* @param array|bool $mArray
* @return bool True if it passes, false if not a valid matrix
*/
private function _verify($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
$nSet = false;
if(is_array($mArray))
{
foreach($mArray as $row)
{
$cols = count($row);
if($nSet===false) {
$nSet = $cols;
}
if($cols != $nSet) {
return false;
}
}
} else {
return false;
}
return true;
}
/**
* Is it a valid matrix?
*
* Public function to tell the class user whether or not the passed
* array is valid, if no array is passed, it will tell the user whether
* the matrix of the current instance is valid. Valid is denoted by all
* rows have the same number of columns.
*
* @param array|bool $mArray Array to be used, if not assigned will default to $this->matrix
* @return bool True/False depending on if array is a valid matrix
*/
public function isValid($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
return $this->_verify($mArray);
}
/**
* Is it a square Matrix?
*
* Will determine whether or not the matrix is valid, and if it
* is, will determine if the matrix is a square matrix (n by n).
*
* @param array|bool $mArray Matrix array, if not assigned will use $this->matrix
* @return bool True/False depending on whether or not the matrix is square
*/
public function isSquare($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
if(!$this->_verify($mArray)) {
return false;
}
$rows = count($mArray);
$cols = count($mArray[0]);
return ($rows == $cols);
}
/**
* Get 'n' from a square (n by n) Matrix
*
* Will check to see if a matrix is square, if so, will return 'n', which
* is the number of rows==columns in the matrix
*
* @param array|bool $mArray Matrix array, uses $this->matrix if not assigned
* @return int The 'n' of a square matrix, or false if not square
* @throws \Exception If not a square matrix, throws an exception
*/
public function _getN($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
if($this->isSquare($mArray)) {
return count($mArray);
} else {
$m = $this->toString($mArray);
throw new \Exception("'{$m}' is not a square matrix", Matrix::E_NOT_SQUARE);
}
}
/**
* Create an Identity Matrix
*
* Creates an Identity Matrix of size 'n'.
*
* @link http://en.wikipedia.org/wiki/Identity_matrix
* @param int $n The rows/cols of identity matrix
* @param bool $useInternal If true will set $this->matrix
* @return Matrix|bool Return an identity matrix if $useInternal is false, otherwise 'true'
*/
public function createIdentity($n, $useInternal = true)
{
$mArray = array();
for($rows=0;$rows<$n;$rows++) {
for($cols=0;$cols<$n;$cols++) {
if($rows==$cols) {
$mArray[$rows][$cols] = 1;
} else {
$mArray[$rows][$cols] = 0;
}
}
}
if($useInternal == true) {
$this->matrix = $mArray;
return true;
} else {
$nMatrix = new Matrix($this->toString($mArray));
return $nMatrix;
}
}
/**
* Convert current Matrix to string format
*
* Convert an array to the string format used by this class.
*
* @see Matrix::_assign()
* @param array|bool $mArray if not assigned will use this instance's matrix.
* @throws \Exception If matrix is not an array
* @return string The array broken down in to string format
*/
public function toString($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
$rows=array();
if(is_array($mArray))
{
foreach($mArray as $cols){
$rows[] = implode(",", $cols);
}
$retString = sprintf("[%s]", implode($rows, ";"));
return $retString;
} else {
throw new \Exception("No matrix to convert", Matrix::E_NO_MATRIX);
}
}
/**
* Overload PHP's class __toString() method
*
* PHP magic method for "echoing" this object without a specific method called
* Will use {@link Matrix::toString()} with no parameters for it's return.
*
* @return string Returns the $matrix value in string format
*/
public function __toString()
{
return $this->toString();
}
/**
* Get Matrix Array
*
* Will return the matrix array of the current instance.
*
* @return array The matrix array of the current instance
*/
public function getArray()
{
return $this->matrix;
}
/**
* Formatted Matrix output for use in console
*
* Will output the matrix in 'pretty' format, if used with 'echo' and
* HTML, surround it by the '<<pre>>' and '<</pre>>' tags to display properly
*
* @param int $width The width of printing space to use
* @param array|bool $mArray Matrix array, defaults to $this->matrix
* @return string "Pretty-Printed" matrix in ASCII format
*/
public function prettyPrint($width=80, $mArray=false)
{
if(!$mArray) $mArray = $this->matrix;
if(!$this->_verify($mArray)) return false;
$out = "";
$aCount = count($mArray[0]);
$space = floor(($width-4)/$aCount);
$space_2 = floor($space/2);
foreach($mArray as $row)
{
$out .= sprintf("| %{$space_2}.2f", $row[0]);
for($i=1;$i<$aCount;$i++)
{
$out .= sprintf("%{$space}.2f", $row[$i]);
}
$out .= sprintf("%{$space_2}s |\n", " ");
}
return $out;
}
/**
* Adds two matrices together
*
* Will add the inputted Matrix to the current instance, and return
* the result as Matrix class.
*
* @link http://en.wikipedia.org/wiki/Matrix_addition
* @param Matrix $nMatrix Matrix class to be added to current instance
* @return Matrix The result of the addition
* @throws \Exception $msg of exception explains problem
*/
public function addMatrix(Matrix $nMatrix)
{
if(!$this->_verify() || !$nMatrix->_verify())
throw new \Exception("Matrices have varying column sizes", Matrix::E_INVALID_MATRIX);
$matrix1 = $this->getArray();
$matrix2 = $nMatrix->getArray();
if((count($matrix1)!=count($matrix2)) || (count($matrix1[0])!=count($matrix2[0])))
{
$m1 = $this->toString($matrix1);
$m2 = $this->toString($matrix2);
throw new \Exception("The rows and/or columns '{$m1}' and '{$m2}' are not the same", Matrix::E_NOT_EQUAL);
}
$rArray = array();
for($row=0;$row<count($matrix1);$row++) {
for($col=0;$col<count($matrix1[0]);$col++) {
$rArray[$row][$col] = $matrix1[$row][$col] + $matrix2[$row][$col];
}
}
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
}
/**
* Subtract Matrices
*
* Will subtract the inputted Matrix from the current instance, and return
* the result as Matrix class.
*
* @link http://en.wikipedia.org/wiki/Matrix_subtraction
* @param Matrix $nMatrix Matrix class to be subtracted from current instance
* @return Matrix The result of the subtraction
* @throws \Exception $msg of exception explains problem
*/
public function subMatrix(Matrix $nMatrix)
{
if(!$this->_verify() || !$nMatrix->_verify())
throw new \Exception("Matrices have varying column sizes", Matrix::E_INVALID_MATRIX);
$matrix1 = $this->getArray();
$matrix2 = $nMatrix->getArray();
if((count($matrix1)!=count($matrix2)) || (count($matrix1[0])!=count($matrix2[0])))
{
$m1 = $this->toString($matrix1);
$m2 = $this->toString($matrix2);
throw new \Exception("The rows and/or columns '{$m1}' and '{$m2}' are not the same", Matrix::E_NOT_EQUAL);
}
$rArray = array();
for($row=0;$row<count($matrix1);$row++) {
for($col=0;$col<count($matrix1[0]);$col++) {
$rArray[$row][$col] = $matrix1[$row][$col] - $matrix2[$row][$col];
}
}
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
}
/**
* Multiply current matrix by a scalar value
*
* Multiplies a matrix by a scalar value (int/float/etc) (constant, ie '2')
*
* @link http://en.wikipedia.org/wiki/Scalar_multiplication
* @param float $k The value to multiply the matrix by
* @return Matrix Returns a new Matrix instance with the result
* @throws \Exception if the instance matrix is not valid
*/
public function mpScalar($k)
{
//we'll verify a true matrix to ... help the user
if(!$this->_verify())
throw new \Exception("Matrix '{$this}' has varying column sizes", Matrix::E_INVALID_MATRIX);
$cArray = $this->getArray();
$rArray = array();
$rows = count($cArray);
$cols = count($cArray[0]);
for($i=0;$i<$rows;$i++) {
for($j=0;$j<$cols;$j++) {
$rArray[$i][$j] = $cArray[$i][$j] * $k;
}
}
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
}
/**
* Get the Matrix Determinant
*
* Finds the determinant of the square matrix, user should not use
* the parameter, as that is meant to allow recursive calling
* of this function from within itself.
*
* @link http://en.wikipedia.org/wiki/Matrix_determinant
* @param array|bool $mArray The array to find a determinate of
* @return float The Determinate of the square matrix
* @throws \Exception If matrix is 1,1 or is not square
*/
public function getDeterminant($mArray = false)
{
if(!$mArray) $mArray = $this->matrix;
//print_r($mArray);
if(!$this->isSquare($mArray))
throw new \Exception("'{$this}' is not a square matrix", Matrix::E_NOT_SQUARE);
$n = $this->_getN($mArray);
if($n < 1){
// @codeCoverageIgnoreStart
// Should never get this far
throw new \Exception("No Matrix", Matrix::E_NO_MATRIX);
// @codeCoverageIgnoreEnd
} elseif ($n == 1) {
$det = $mArray[0][0];
} elseif ($n == 2) {
$det = $mArray[0][0]*$mArray[1][1] - $mArray[1][0]*$mArray[0][1];
} else {
$det = 0;
$nArray = array();
for($j1=0;$j1<$n;$j1++) {
for($i=1;$i<$n;$i++) {
$j2 = 0;
for($j=0;$j<$n;$j++) {
if($j==$j1) {
continue;
}
$nArray[$i-1][$j2] = $mArray[$i][$j];
$j2++;
}
}
$det += pow(-1,2+$j1)*$mArray[0][$j1]*$this->getDeterminant($nArray);
}
}
return $det;
}
/**
* coFactor Matrix
*
* Will return a Matrix of coFactors for the matrix provided, or an array
* of the matrix as is the default.
*
* @link http://en.wikipedia.org/wiki/Matrix_cofactors
* @param array|bool $cArray A matrix in array format (or $this->matrix by default)
* @param bool $asArray When set to true, will return an array, when false a Matrix Object
* @return Matrix|array A matrix of coFactors for the array provided (or current matrix)
* @throws \Exception if the matrix is not square
*/
public function coFactor($cArray=false,$asArray=true)
{
if(!$cArray) $cArray = $this->matrix;
if(!$this->isSquare($cArray))
throw new \Exception("'{$this}' is not a square matrix", Matrix::E_NOT_SQUARE);
$n = $this->_getN($cArray);
$minor = array();
$rArray = array();
for($j=0;$j<$n;$j++){
for($i=0;$i<$n;$i++) {
//Form the adjugate
$i1 = 0;
for($ii=0;$ii<$n;$ii++) {
if($ii==$i) {
continue;
}
$j1=0;
for($jj=0;$jj<$n;$jj++) {
if($jj==$j) {
continue;
}
$minor[$i1][$j1] = $cArray[$ii][$jj];
$j1++;
}
$i1++;
}
$det = $this->getDeterminant($minor);
$rArray[$i][$j] = pow(-1,$i+$j+2)*$det;
}
}
if($asArray==false){
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
} else {
return $rArray;
}
}
/**
* Will transpose the current matrix or array provided
*
* Transposes the current matrix, or the array provided
*
* @link http://en.wikipedia.org/wiki/Matrix_transpose
* @param array|bool $cArray the array to transpose (defaults to $this->matrix)
* @param bool $asArray whether to return an array or Matrix object
* @return array|Matrix Defaults to returning an array of the transposed matrix
* @throws \Exception if the matrix is not square
*/
public function transpose($cArray=false,$asArray=true)
{
if(!$cArray) $cArray = $this->matrix;
if(!$this->isSquare($cArray))
throw new \Exception("'{$this}' is not a square matrix", Matrix::E_NOT_SQUARE);
$n = $this->_getN();
$nArray = array();
for($i=0;$i<$n;$i++) {
for($j=0;$j<$n;$j++) {
$nArray[$j][$i] = $cArray[$i][$j];
}
}
if($asArray==true) {
return $nArray;
} else {
$nMatrix = new Matrix($this->toString($nArray));
return $nMatrix;
}
}
/**
* Adjugate Matrix
*
* Will return the Adjugate matrix of the array provided
* or the current matrix instance if not provided.
*
* @link http://en.wikipedia.org/wiki/Adjugate_matrix
* @param array|bool $cArray Defaults to $this->matrix if not provided
* @param bool $asArray Whether to return an array or Matrix object
* @return array|Matrix Defaults to return the array of the Adjugate matrix
*/
public function adjugate($cArray=false,$asArray=true)
{
if(!$cArray) $cArray = $this->matrix;
$rArray = $this->transpose($this->coFactor($cArray));
if($asArray==true)
return $rArray;
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
}
/**
* Inverse of current matrix
*
* Will give the inverse of the array provided or the current matrix
* Matrix returned denoted by A^-1
*
* @link http://en.wikipedia.org/wiki/Inverse_matrix
* @param array|bool $cArray Array to invert (defaults to $this->matrix)
* @return Matrix By default returns a new instance of Matrix
* @throws \Exception for any number of reasons that would make the inverse not available
*/
public function inverse($cArray = false)
{
if(!$cArray) $cArray = $this->matrix;
$det = $this->getDeterminant($cArray);
if($det == 0)
throw new \Exception("Determinant of {$this} is 0, No Inverse found", Matrix::E_NO_INVERSE);
$scalar = 1/$det;
$adj = $this->adjugate($cArray, false);
$iMatrix = $adj->mpScalar($scalar);
return $iMatrix;
}
/**
* Multiply Matrices
*
* This function will multiply the current matrix with the matrix provided.
* If current Matrix is denoted by 'A' and the inputted is denoted by 'B',
* When written, this will return AB.
*
* @link http://en.wikipedia.org/wiki/Matrix_multiplication
* @param Matrix $bMatrix The matrix to multiply with the current
* @return Matrix The result of multiplication.
* @throws \Exception $msg explains why operation failed
*/
public function mpMatrix(Matrix $bMatrix)
{
if(!$this->_verify() || !$bMatrix->_verify()) {
// @codeCoverageIgnoreStart
// Should never get this far
$eM1 = $this->toString();
$eM2 = $bMatrix->toString();
throw new \Exception("Either '{$eM1}' and/or '{$eM2}' is not a valid Matrix", Matrix::E_INVALID_MATRIX);
// @codeCoverageIgnoreEnd
}
$aArray = $this->matrix;
$bArray = $bMatrix->getArray();
//The number of columns in A must match the number of rows in B
if(count($aArray[0]) != count($bArray)) {
$mA = $this->toString();
$mB = $bMatrix->toString();
throw new \Exception("Columns in '{$mA}' don't match Rows of '{$mB}'", Matrix::E_NOT_EQUAL);
}
$rArray = array();
//Loop through rows of Matrix A
for($i=0;$i<count($aArray);$i++) {
//Loop through the columns of Matrix B
for($j=0;$j<count($bArray[0]);$j++) {
$value = 0;
//loop through the rows of Matrix B
for($k=0;$k<count($bArray);$k++) {
$value += $aArray[$i][$k] * $bArray[$k][$j];
}
$rArray[$i][$j] = $value;
}
}
$rMatrix = new Matrix($this->toString($rArray));
return $rMatrix;
}
}
?>