@@ -64,17 +64,17 @@ Allows complex entries
6464
6565=back
6666
67- =head2 Creation of Matrices
67+ =head2 Creation of Matrix MathObjects
6868
6969Examples:
7070
71- $M1 = Matrix(1, 2, 3); # 1D (row vector)
71+ $M1 = Matrix(1, 2, 3); # degree 1 (row vector)
7272 $M1 = Matrix([1, 2, 3]);
7373
74- $M2 = Matrix([1, 2], [3, 4]); # 2D (matrix)
74+ $M2 = Matrix([1, 2], [3, 4]); # degree 2 (matrix)
7575 $M2 = Matrix([[1, 2], [3, 4]]);
7676
77- $M3 = Matrix([[1, 2], [3, 4]], [[5, 6], [7, 8]]); # 3D (tensor)
77+ $M3 = Matrix([[1, 2], [3, 4]], [[5, 6], [7, 8]]); # degree 3 (tensor)
7878 $M3 = Matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
7979
8080 $M4 = ...
@@ -83,21 +83,24 @@ Examples:
8383
8484=head3 Conversion
8585
86- $matrix->value produces an array of numbers (for a 1D tensor) or array refs representing the rows.
87- These are perl numbers and array refs, not MathObjects.
86+ $matrix->value produces an array of references to nested arrays, except at
87+ the deepest level, where there will be the more basic MathObjects that make
88+ up the Matrix (e.g. Real, Complex, Fraction, a mix of these, etc)
8889
8990 $M1->value is (1, 2, 3)
9091 $M2->value is ([1, 2], [3, 4])
9192 $M3->value is ([[1, 2], [3, 4]], [[5, 6], [7, 8]])
9293
9394 $matrix->wwMatrix produces CPAN MatrixReal1 matrix, used for computation subroutines and can only
94- be used on 1D tensors (row vector) or 2D tensors (matrix) .
95+ be used on a degree 1 or degree 2 Matrix .
9596
9697=head3 Information
9798
98- $matrix->dimensions produces an array. For an n-dimensional tensor , the array has n entries for
99+ $matrix->dimensions produces an array. For an n-degree Matrix , the array has n entries for
99100 the n dimensions.
100101
102+ $matrix->degree returns the degree of the Matrix (the depth of the nested array).
103+
101104=head3 Access values
102105
103106 row(i) : MathObjectMatrix
@@ -305,7 +308,7 @@ returns C<(2,2,2)>
305308
306309#
307310# Recursively get the dimensions of the matrix.
308- # Returns (d_1,d_2,...,d_n) for an nD matrix .
311+ # Returns (d_1,d_2,...,d_n) for a degree n Matrix .
309312#
310313sub dimensions {
311314 my $self = shift ;
@@ -315,6 +318,13 @@ sub dimensions {
315318 return ($r , $v -> [0]-> dimensions);
316319}
317320
321+ sub degree {
322+ my $self = shift ;
323+ my $v = $self -> data;
324+ return 1 unless Value::classMatch($v -> [0], ' Matrix' );
325+ return 1 + $v -> [0]-> degree;
326+ }
327+
318328#
319329# Return the proper type for the matrix
320330#
@@ -327,24 +337,31 @@ sub typeRef {
327337
328338=head3 C<isSquare >
329339
330- Return true is the matrix is 1D of length 1 or 2D and square , false otherwise
340+ Return true if the Matrix is degree 1 of length 1 or its last two dimensions are equal , false otherwise
331341
332342Usage:
333343
334- $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
335- $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]);
344+ $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]);
345+ $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ] ]);
346+ $C = Matrix(1);
347+ $D = Matrix(1, 2);
348+ $E = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]);
349+ $F = Matrix([ [ [ 1, 2 ] ], [ [ 3, 4 ] ] ]);
336350
337- $A->isSquare; # is '' (false)
338- $B->isSquare; # is 1 (true);
351+ $A->isSquare; # is 1 (true) because it is a 3x3 matrix
352+ $B->isSquare; # is '' (false) because it is a 2x3 matrix
353+ $C->isSquare; # is 1 (true) because it is a degree 1 matrix of length 1
354+ $D->isSquare; # is '' (false) because it is a degree 1 matrix of length 2
355+ $E->isSquare; # is 1 (true) because it is a 1x2x2 tensor
356+ $F->isSquare; # is '' (false) because it is a 2x1x2 tensor
339357
340358=cut
341359
342360sub isSquare {
343361 my $self = shift ;
344362 my @d = $self -> dimensions;
345- return 1 if scalar (@d ) == 1 && $d [0] == 1;
346- return 0 if scalar (@d ) != 2;
347- return $d [0] == $d [1];
363+ return $d [0] == 1 if scalar (@d ) == 1;
364+ return $d [-1] == $d [-2];
348365}
349366
350367=head3 C<isRow >
@@ -369,7 +386,7 @@ sub isRow {
369386
370387=head3 C<isOne >
371388
372- Check for identity matrix.
389+ Check for identity Matrix (for degree > 2, this means the last two dimensions hold identity matrices)
373390
374391Usage:
375392
@@ -379,19 +396,33 @@ Usage:
379396 $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]);
380397 $B->isOne; # is true;
381398
399+ $C = Matrix(1);
400+ $C->isOne; # is true
401+
402+ $D = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]);
403+ $D->isOne; # is true
404+
382405=cut
383406
384407sub isOne {
385408 my $self = shift ;
386409 return 0 unless $self -> isSquare;
387- my $i = 0;
388- for my $row (@{ $self -> {data } }) {
389- my $j = 0;
390- for my $k (@{ $row -> {data } }) {
391- return 0 unless $k eq (($i == $j ) ? " 1" : " 0" );
392- $j ++;
410+ if ($self -> degree <= 2) {
411+ my $i = 0;
412+ for my $row (@{ $self -> {data } }) {
413+ my $j = 0;
414+ for my $k (@{ $row -> {data } }) {
415+ return 0 unless $k eq (($i == $j ) ? " 1" : " 0" );
416+ $j ++;
417+ }
418+ $i ++;
419+ }
420+ } else {
421+ for my $row (@{ $self -> {data } }) {
422+ if (!$row -> isOne) {
423+ return 0;
424+ }
393425 }
394- $i ++;
395426 }
396427 return 1;
397428}
@@ -597,36 +628,73 @@ sub mult {
597628 return $self -> make(@coords );
598629 }
599630
600- # Make points and vectors into columns if they are on the right.
601- $r = !$flag && Value::classMatch($r , ' Point' , ' Vector' ) ? ($self -> promote($r ))-> transpose : $self -> promote($r );
631+ # Special case if $r is a Point or Vector and if $l is a degree 2 Matrix,
632+ # we promote $r to a degree 1 Matrix and later restore the product to be Point or Vector
633+ $r =
634+ !$flag
635+ && Value::classMatch($r , ' Point' , ' Vector' )
636+ && $l -> degree == 2 ? ($self -> promote($r ))-> transpose : $self -> promote($r );
602637
603- if (!$flag && Value::classMatch($r , ' Point' , ' Vector' )) { $r = ($self -> promote($r ))-> transpose }
604- else { $r = $self -> promote($r ) }
605- #
606- if ($flag ) { my $tmp = $l ; $l = $r ; $r = $tmp }
638+ if ($flag ) { ($l , $r ) = ($r , $l ) }
607639 my @dl = $l -> dimensions;
608640 my @dr = $r -> dimensions;
609- if (scalar (@dl ) == 1) { @dl = (1, @dl ); $l = $self -> make($l ) }
610- if (scalar (@dr ) == 1) { @dr = (@dr , 1); $r = $self -> make($r )-> transpose }
611- Value::Error(" Can only multiply 2-dimensional matrices" ) if scalar (@dl ) > 2 || scalar (@dr ) > 2;
612- Value::Error(" Matrices of dimensions %dx%d and %dx%d can't be multiplied" , @dl , @dr ) unless ($dl [1] == $dr [0]);
641+ my @l = $l -> value;
642+ my @r = $r -> value;
643+
644+ # Special case if $r and $l are both degree 1, perform dot product
645+ if (scalar (@dl ) == 1 && scalar (@dr ) == 1) {
646+ Value::Error(" Can't multiply degree 1 matrices of different lengths" ) unless ($dl [0] == $dr [0]);
647+ my $result = 0;
648+ for my $i (0 .. $dl [0] - 1) {
649+ $result += $l [$i ] * $r [$i ];
650+ }
651+ return $result ;
652+ }
613653
614- # Perform matrix multiplication.
654+ # Promote degree 1 Matrix to degree 2, as row or column depending on size
655+ # Later restore result to degree 1 if appropriate
656+ my $l1 = scalar (@dl ) == 1;
657+ my $r1 = scalar (@dr ) == 1;
658+ if ($l1 ) { @dl = (1, @dl ); $l = $self -> make($l ); @l = $l -> value }
659+ if ($r1 ) { @dr = (@dr , 1); $r = $self -> make($r )-> transpose; @r = $r -> value }
660+
661+ # Multiplication is only possible when dimensions are Zxmxn and Zxnxk
662+ my $bad_dims ;
663+ if (scalar (@dl ) != scalar (@dr )) {
664+ $bad_dims = 1;
665+ } elsif ($dl [-1] != $dr [-2]) {
666+ $bad_dims = 1;
667+ } else {
668+ for my $i (0 .. scalar (@dl ) - 3) {
669+ if ($dl [$i ] != $dr [$i ]) {
670+ $bad_dims = 1;
671+ last ;
672+ }
673+ }
674+ }
675+ Value::Error(" Matrices of dimensions %s and %s can't be multiplied" , join (' x' , @dl ), join (' x' , @dr )) if $bad_dims ;
615676
616- my @l = $l -> value;
617- my @r = $r -> value;
677+ # Perform matrix/tensor multiplication.
618678 my @M = ();
619679 for my $i (0 .. $dl [0] - 1) {
620- my @row = ();
621- for my $j (0 .. $dr [1] - 1) {
622- my $s = 0;
623- for my $k (0 .. $dl [1] - 1) { $s += $l [$i ]-> [$k ] * $r [$k ]-> [$j ] }
624- push (@row , $s );
680+ if (scalar (@dl ) == 2) {
681+ my @row = ();
682+ for my $j (0 .. $dr [1] - 1) {
683+ my $s = 0;
684+ for my $k (0 .. $dl [1] - 1) { $s += $l [$i ]-> [$k ] * $r [$k ]-> [$j ] }
685+ push (@row , $s );
686+ }
687+ push (@M , $self -> make(@row ));
688+ } else {
689+ push (@M , $l -> data-> [$i ] * $r -> data-> [$i ]);
625690 }
626- push (@M , $self -> make(@row ));
627691 }
628692 $self = $self -> inherit($other ) if Value::isValue($other );
629- return $self -> make(@M );
693+
694+ if ($l1 ) { return $self -> make(@M )-> row(1) }
695+ if ($r1 ) { return $self -> make(@M )-> transpose-> row(1) }
696+
697+ return $other -> new($self -> make(@M ));
630698}
631699
632700sub div {
@@ -653,10 +721,23 @@ sub power {
653721 $self -> Error(" Matrix is not invertible" ) unless defined ($l );
654722 }
655723 Value::Error(" Matrix powers must be non-negative integers" ) unless _isNumber($r ) && $r =~ m / ^\d +$ / ;
656- return $context -> Package(" Matrix" )-> I($l -> length , $context ) if $r == 0;
657- my $M = $l ;
658- for my $i (2 .. $r ) { $M = $M * $l }
659- return $M ;
724+ return $l if $r == 1;
725+ my @powers = ($l );
726+ my @d = $l -> dimensions;
727+ my $d = pop @d ;
728+ pop @d ;
729+ my $return = $context -> Package(" Matrix" )-> I(\@d , $d );
730+ my $p = $r ;
731+
732+ while ($p ) {
733+ if ($p % 2) {
734+ $p -= 1;
735+ $return *= $powers [-1];
736+ }
737+ push (@powers , $powers [-1] * $powers [-1]);
738+ $p /= 2;
739+ }
740+ return $return ;
660741}
661742
662743# Do lexicographic comparison (row by row)
@@ -718,31 +799,51 @@ sub transpose {
718799
719800=head3 C<I >
720801
721- Get an identity matrix of the requested size
802+ Get an identity Matrix of the requested size
722803
723804 Value::Matrix->I(n)
724805
725806Usage:
726807
727- Value::Matrix->I(3); # returns a 3 by 3 identity matrix.
728- $A->I; # return an n by n identity matrix, where n is the number of rows of A
808+ Value::Matrix->I(3); # returns a 3x3 identity matrix.
809+ Value::Matrix->I([2],3); # returns a 2x3x3 identity Matrix (tensor)
810+ Value::Matrix->I([2,4],2); # returns a 2x4x2x2 identity Matrix (tensor)
811+ $A->I; # return an identity matrix of the appropriate degree and dimensions such that I*A = A
729812
730813=cut
731814
732815sub I {
733- my $self = shift ;
734- my $d = shift ;
735- my $context = shift || $self -> context;
736- $d = ($self -> dimensions)[0] if !defined $d && ref ($self );
737-
738- Value::Error(" You must provide a dimension for the Identity matrix" ) unless defined $d ;
739- Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
816+ my $self = shift ;
817+ my @d ;
818+ my $d ;
819+ my $context ;
820+ if (ref ($self ) eq ' Value::Matrix' ) {
821+ @d = $self -> dimensions;
822+ pop @d unless scalar (@d ) == 1;
823+ $d = pop @d ;
824+ $context = $self -> context;
825+ } else {
826+ $d = shift ; # array ref, or number
827+ if (ref ($d ) eq ' ARRAY' ) {
828+ @d = @{$d };
829+ $d = shift ;
830+ }
831+ Value::Error(" You must provide a dimension for the Identity matrix" ) unless ($d || $d eq ' 0' );
832+ Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
833+ $context = shift || $self -> context;
834+ }
740835
741- my @M = () ;
836+ my @M ;
742837 my $REAL = $context -> Package(' Real' );
743838
744- for my $i (0 .. $d - 1) {
745- push (@M , $self -> make($context , map { $REAL -> new(($_ == $i ) ? 1 : 0) } 0 .. $d - 1));
839+ if (!@d ) {
840+ for my $i (0 .. $d - 1) {
841+ push (@M , $self -> make($context , map { $REAL -> new(($_ == $i ) ? 1 : 0) } 0 .. $d - 1));
842+ }
843+ } else {
844+ for my $i (1 .. $d [0]) {
845+ push (@M , Value::Matrix-> I([ @d [ 1 .. $#d ] ], $d ));
846+ }
746847 }
747848 return $self -> make($context , @M );
748849}
@@ -1108,10 +1209,19 @@ sub det {
11081209
11091210sub inverse {
11101211 my $self = shift ;
1111- $self -> wwMatrixLR;
1112- Value-> Error(" Can't take inverse of non-square matrix" ) unless $self -> isSquare;
1113- my $I = $self -> {lrM }-> invert_LR;
1114- return (defined ($I ) ? $self -> new($I ) : $I );
1212+ my @d = $self -> dimensions;
1213+ my @M ;
1214+ for my $i (0 .. $d [0] - 1) {
1215+ if (scalar (@d ) == 2) {
1216+ $self -> wwMatrixLR;
1217+ Value-> Error(" Can't take inverse of non-square matrix" ) unless $self -> isSquare;
1218+ my $I = $self -> {lrM }-> invert_LR;
1219+ return (defined ($I ) ? $self -> new($I ) : $I );
1220+ } else {
1221+ push (@M , $self -> data-> [$i ]-> inverse);
1222+ }
1223+ }
1224+ return $self -> new($self -> make(@M ));
11151225}
11161226
11171227sub decompose_LR {
0 commit comments