@@ -64,17 +64,17 @@ Allows complex entries
64
64
65
65
=back
66
66
67
- =head2 Creation of Matrices
67
+ =head2 Creation of Matrix MathObjects
68
68
69
69
Examples:
70
70
71
- $M1 = Matrix(1, 2, 3); # 1D (row vector)
71
+ $M1 = Matrix(1, 2, 3); # degree 1 (row vector)
72
72
$M1 = Matrix([1, 2, 3]);
73
73
74
- $M2 = Matrix([1, 2], [3, 4]); # 2D (matrix)
74
+ $M2 = Matrix([1, 2], [3, 4]); # degree 2 (matrix)
75
75
$M2 = Matrix([[1, 2], [3, 4]]);
76
76
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)
78
78
$M3 = Matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
79
79
80
80
$M4 = ...
@@ -83,21 +83,23 @@ Examples:
83
83
84
84
=head3 Conversion
85
85
86
- $matrix->value produces an array of numbers (for a 1D tensor ) or array refs representing the rows.
86
+ $matrix->value produces an array of numbers (for a degree 1 Matrix ) or array refs representing the rows.
87
87
These are perl numbers and array refs, not MathObjects.
88
88
89
89
$M1->value is (1, 2, 3)
90
90
$M2->value is ([1, 2], [3, 4])
91
91
$M3->value is ([[1, 2], [3, 4]], [[5, 6], [7, 8]])
92
92
93
93
$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) .
94
+ be used on a degree 1 or degree 2 Matrix .
95
95
96
96
=head3 Information
97
97
98
- $matrix->dimensions produces an array. For an n-dimensional tensor , the array has n entries for
98
+ $matrix->dimensions produces an array. For an n-degree Matrix , the array has n entries for
99
99
the n dimensions.
100
100
101
+ $matrix->degree returns the degree of the Matrix (the depth of the nested array).
102
+
101
103
=head3 Access values
102
104
103
105
row(i) : MathObjectMatrix
@@ -305,7 +307,7 @@ returns C<(2,2,2)>
305
307
306
308
#
307
309
# Recursively get the dimensions of the matrix.
308
- # Returns (d_1,d_2,...,d_n) for an nD matrix .
310
+ # Returns (d_1,d_2,...,d_n) for an degree n Matrix .
309
311
#
310
312
sub dimensions {
311
313
my $self = shift ;
@@ -315,6 +317,13 @@ sub dimensions {
315
317
return ($r , $v -> [0]-> dimensions);
316
318
}
317
319
320
+ sub degree {
321
+ my $self = shift ;
322
+ my $v = $self -> data;
323
+ return 1 unless Value::classMatch($v -> [0], ' Matrix' );
324
+ return 1 + $v -> [0]-> degree;
325
+ }
326
+
318
327
#
319
328
# Return the proper type for the matrix
320
329
#
@@ -327,24 +336,31 @@ sub typeRef {
327
336
328
337
=head3 C<isSquare >
329
338
330
- Return true is the matrix is 1D of length 1 or 2D and square , false otherwise
339
+ Return true if the Matrix is degree 1 of length 1 or its last two dimensions are equal , false otherwise
331
340
332
341
Usage:
333
342
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 ] ]);
343
+ $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]);
344
+ $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ] ]);
345
+ $C = Matrix(1);
346
+ $D = Matrix(1, 2);
347
+ $E = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]);
348
+ $F = Matrix([ [ [ 1, 2 ] ], [ [ 3, 4 ] ] ]);
336
349
337
- $A->isSquare; # is '' (false)
338
- $B->isSquare; # is 1 (true);
350
+ $A->isSquare; # is 1 (true) because it is a 3x3 matrix
351
+ $B->isSquare; # is '' (false) because it is a 2x3 matrix
352
+ $C->isSquare; # is 1 (true) because it is a degree 1 matrix of length 1
353
+ $D->isSquare; # is '' (false) because it is a degree 1 matrix of length 2
354
+ $E->isSquare; # is 1 (true) because it is a 1x2x2 tensor
355
+ $F->isSquare; # is '' (false) because it is a 2x1x2 tensor
339
356
340
357
=cut
341
358
342
359
sub isSquare {
343
360
my $self = shift ;
344
361
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];
362
+ if (scalar (@d ) == 1) { return $d [0] == 1 }
363
+ return ($d [-1] == $d [-2]);
348
364
}
349
365
350
366
=head3 C<isRow >
@@ -369,7 +385,7 @@ sub isRow {
369
385
370
386
=head3 C<isOne >
371
387
372
- Check for identity matrix.
388
+ Check for identity Matrix (for degree > 2, this means the last two dimensions hold identity matrices)
373
389
374
390
Usage:
375
391
@@ -379,19 +395,33 @@ Usage:
379
395
$B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]);
380
396
$B->isOne; # is true;
381
397
398
+ $C = Matrix(1);
399
+ $C->isOne; # is true
400
+
401
+ $D = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]);
402
+ $D->isOne; # is true
403
+
382
404
=cut
383
405
384
406
sub isOne {
385
407
my $self = shift ;
386
408
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 ++;
409
+ if ($self -> degree <= 2) {
410
+ my $i = 0;
411
+ for my $row (@{ $self -> {data } }) {
412
+ my $j = 0;
413
+ for my $k (@{ $row -> {data } }) {
414
+ return 0 unless $k eq (($i == $j ) ? " 1" : " 0" );
415
+ $j ++;
416
+ }
417
+ $i ++;
418
+ }
419
+ } else {
420
+ for my $row (@{ $self -> {data } }) {
421
+ if (!$row -> isOne) {
422
+ return 0;
423
+ }
393
424
}
394
- $i ++;
395
425
}
396
426
return 1;
397
427
}
@@ -597,36 +627,73 @@ sub mult {
597
627
return $self -> make(@coords );
598
628
}
599
629
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 );
630
+ # Special case if $r is a Point or Vector and if $l is a degree 2 Matrix,
631
+ # we promote $r to a degree 1 Matrix and later restore the product to be Point or Vector
632
+ $r =
633
+ !$flag
634
+ && Value::classMatch($r , ' Point' , ' Vector' )
635
+ && $l -> degree == 2 ? ($self -> promote($r ))-> transpose : $self -> promote($r );
602
636
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 }
637
+ if ($flag ) { ($l , $r ) = ($r , $l ) }
607
638
my @dl = $l -> dimensions;
608
639
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]);
640
+ my @l = $l -> value;
641
+ my @r = $r -> value;
642
+
643
+ # Special case if $r and $l are both degree 1, perform dot product
644
+ if (scalar (@dl ) == 1 && scalar (@dr ) == 1) {
645
+ Value::Error(" Can't multiply degree 1 matrices of different lengths" ) unless ($dl [0] == $dr [0]);
646
+ my $result = 0;
647
+ for my $i (0 .. $dl [0] - 1) {
648
+ $result += $l [$i ] * $r [$i ];
649
+ }
650
+ return $result ;
651
+ }
613
652
614
- # Perform matrix multiplication.
653
+ # Promote degree 1 Matrix to degree 2, as row or column depending on side
654
+ # Later restore result to degree 1 if appropriate
655
+ my $l1 = scalar (@dl ) == 1;
656
+ my $r1 = scalar (@dr ) == 1;
657
+ if ($l1 ) { @dl = (1, @dl ); $l = $self -> make($l ); @l = $l -> value }
658
+ if ($r1 ) { @dr = (@dr , 1); $r = $self -> make($r )-> transpose; @r = $r -> value }
659
+
660
+ # Multiplication is only possible when dimensions are Zxmxn and Zxnxk
661
+ my $bad_dims ;
662
+ if (scalar (@dl ) != scalar (@dr )) {
663
+ $bad_dims = 1;
664
+ } elsif ($dl [-1] != $dr [-2]) {
665
+ $bad_dims = 1;
666
+ } else {
667
+ for my $i (0 .. scalar (@dl ) - 3) {
668
+ if ($dl [$i ] != $dr [$i ]) {
669
+ $bad_dims = 1;
670
+ last ;
671
+ }
672
+ }
673
+ }
674
+ Value::Error(" Matrices of dimensions %s and %s can't be multiplied" , join (' x' , @dl ), join (' x' , @dr )) if $bad_dims ;
615
675
616
- my @l = $l -> value;
617
- my @r = $r -> value;
676
+ # Perform matrix/tensor multiplication.
618
677
my @M = ();
619
678
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 );
679
+ if (scalar (@dl ) == 2) {
680
+ my @row = ();
681
+ for my $j (0 .. $dr [1] - 1) {
682
+ my $s = 0;
683
+ for my $k (0 .. $dl [1] - 1) { $s += $l [$i ]-> [$k ] * $r [$k ]-> [$j ] }
684
+ push (@row , $s );
685
+ }
686
+ push (@M , $self -> make(@row ));
687
+ } else {
688
+ push (@M , $l -> data-> [$i ] * $r -> data-> [$i ]);
625
689
}
626
- push (@M , $self -> make(@row ));
627
690
}
628
691
$self = $self -> inherit($other ) if Value::isValue($other );
629
- return $self -> make(@M );
692
+
693
+ if ($l1 ) { return $self -> make(@M )-> row(1) }
694
+ if ($r1 ) { return $self -> make(@M )-> transpose-> row(1) }
695
+
696
+ return $other -> new($self -> make(@M ));
630
697
}
631
698
632
699
sub div {
@@ -653,10 +720,23 @@ sub power {
653
720
$self -> Error(" Matrix is not invertible" ) unless defined ($l );
654
721
}
655
722
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 ;
723
+ return $l if $r == 1;
724
+ my @powers = ($l );
725
+ my @d = $l -> dimensions;
726
+ my $d = pop @d ;
727
+ pop @d ;
728
+ my $return = $context -> Package(" Matrix" )-> I(\@d , $d );
729
+ my $p = $r ;
730
+
731
+ while ($p ) {
732
+ if ($p % 2) {
733
+ $p -= 1;
734
+ $return *= $powers [-1];
735
+ }
736
+ push (@powers , $powers [-1] * $powers [-1]);
737
+ $p /= 2;
738
+ }
739
+ return $return ;
660
740
}
661
741
662
742
# Do lexicographic comparison (row by row)
@@ -718,31 +798,51 @@ sub transpose {
718
798
719
799
=head3 C<I >
720
800
721
- Get an identity matrix of the requested size
801
+ Get a identity Matrix of the requested size
722
802
723
803
Value::Matrix->I(n)
724
804
725
805
Usage:
726
806
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
807
+ Value::Matrix->I(3); # returns a 3x3 identity matrix.
808
+ Value::Matrix->I([2],3); # returns a 2x3x3 identity Matrix (tensor)
809
+ Value::Matrix->I([2,4],2); # returns a 2x4x2x2 identity Matrix (tensor)
810
+ $A->I; # return an identity matrix of the appropriate degree and dimensions such that I*A = A
729
811
730
812
=cut
731
813
732
814
sub 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 *$ / ;
815
+ my $self = shift ;
816
+ my @d ;
817
+ my $d ;
818
+ my $context ;
819
+ if (ref ($self ) eq ' Value::Matrix' ) {
820
+ @d = $self -> dimensions;
821
+ pop @d unless scalar (@d ) == 1;
822
+ $d = pop @d ;
823
+ $context = $self -> context;
824
+ } else {
825
+ $d = shift ; # array ref, or number
826
+ if (ref ($d ) eq ' ARRAY' ) {
827
+ @d = @{$d };
828
+ $d = shift ;
829
+ }
830
+ Value::Error(" You must provide a dimension for the Identity matrix" ) unless ($d || $d eq ' 0' );
831
+ Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
832
+ $context = shift || $self -> context;
833
+ }
740
834
741
- my @M = () ;
835
+ my @M ;
742
836
my $REAL = $context -> Package(' Real' );
743
837
744
- for my $i (0 .. $d - 1) {
745
- push (@M , $self -> make($context , map { $REAL -> new(($_ == $i ) ? 1 : 0) } 0 .. $d - 1));
838
+ if (!@d ) {
839
+ for my $i (0 .. $d - 1) {
840
+ push (@M , $self -> make($context , map { $REAL -> new(($_ == $i ) ? 1 : 0) } 0 .. $d - 1));
841
+ }
842
+ } else {
843
+ for my $i (1 .. $d [0]) {
844
+ push (@M , Value::Matrix-> I([ @d [ 1 .. $#d ] ], $d ));
845
+ }
746
846
}
747
847
return $self -> make($context , @M );
748
848
}
@@ -1108,10 +1208,19 @@ sub det {
1108
1208
1109
1209
sub inverse {
1110
1210
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 );
1211
+ my @d = $self -> dimensions;
1212
+ my @M = ();
1213
+ for my $i (0 .. $d [0] - 1) {
1214
+ if (scalar (@d ) == 2) {
1215
+ $self -> wwMatrixLR;
1216
+ Value-> Error(" Can't take inverse of non-square matrix" ) unless $self -> isSquare;
1217
+ my $I = $self -> {lrM }-> invert_LR;
1218
+ return (defined ($I ) ? $self -> new($I ) : $I );
1219
+ } else {
1220
+ push (@M , $self -> data-> [$i ]-> inverse);
1221
+ }
1222
+ }
1223
+ return $self -> new($self -> make(@M ));
1115
1224
}
1116
1225
1117
1226
sub decompose_LR {
0 commit comments