@@ -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,24 @@ 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.
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)
88
89
89
90
$M1->value is (1, 2, 3)
90
91
$M2->value is ([1, 2], [3, 4])
91
92
$M3->value is ([[1, 2], [3, 4]], [[5, 6], [7, 8]])
92
93
93
94
$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 .
95
96
96
97
=head3 Information
97
98
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
99
100
the n dimensions.
100
101
102
+ $matrix->degree returns the degree of the Matrix (the depth of the nested array).
103
+
101
104
=head3 Access values
102
105
103
106
row(i) : MathObjectMatrix
@@ -305,7 +308,7 @@ returns C<(2,2,2)>
305
308
306
309
#
307
310
# 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 .
309
312
#
310
313
sub dimensions {
311
314
my $self = shift ;
@@ -315,6 +318,13 @@ sub dimensions {
315
318
return ($r , $v -> [0]-> dimensions);
316
319
}
317
320
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
+
318
328
#
319
329
# Return the proper type for the matrix
320
330
#
@@ -327,24 +337,31 @@ sub typeRef {
327
337
328
338
=head3 C<isSquare >
329
339
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
331
341
332
342
Usage:
333
343
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 ] ] ]);
336
350
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
339
357
340
358
=cut
341
359
342
360
sub isSquare {
343
361
my $self = shift ;
344
362
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];
348
365
}
349
366
350
367
=head3 C<isRow >
@@ -369,7 +386,7 @@ sub isRow {
369
386
370
387
=head3 C<isOne >
371
388
372
- Check for identity matrix.
389
+ Check for identity Matrix (for degree > 2, this means the last two dimensions hold identity matrices)
373
390
374
391
Usage:
375
392
@@ -379,19 +396,33 @@ Usage:
379
396
$B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]);
380
397
$B->isOne; # is true;
381
398
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
+
382
405
=cut
383
406
384
407
sub isOne {
385
408
my $self = shift ;
386
409
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
+ }
393
425
}
394
- $i ++;
395
426
}
396
427
return 1;
397
428
}
@@ -597,36 +628,73 @@ sub mult {
597
628
return $self -> make(@coords );
598
629
}
599
630
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 );
602
637
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 ) }
607
639
my @dl = $l -> dimensions;
608
640
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
+ }
613
653
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 ;
615
676
616
- my @l = $l -> value;
617
- my @r = $r -> value;
677
+ # Perform matrix/tensor multiplication.
618
678
my @M = ();
619
679
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 ]);
625
690
}
626
- push (@M , $self -> make(@row ));
627
691
}
628
692
$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 ));
630
698
}
631
699
632
700
sub div {
@@ -653,10 +721,23 @@ sub power {
653
721
$self -> Error(" Matrix is not invertible" ) unless defined ($l );
654
722
}
655
723
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 ;
660
741
}
661
742
662
743
# Do lexicographic comparison (row by row)
@@ -718,31 +799,51 @@ sub transpose {
718
799
719
800
=head3 C<I >
720
801
721
- Get an identity matrix of the requested size
802
+ Get an identity Matrix of the requested size
722
803
723
804
Value::Matrix->I(n)
724
805
725
806
Usage:
726
807
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
729
812
730
813
=cut
731
814
732
815
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 *$ / ;
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
+ }
740
835
741
- my @M = () ;
836
+ my @M ;
742
837
my $REAL = $context -> Package(' Real' );
743
838
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
+ }
746
847
}
747
848
return $self -> make($context , @M );
748
849
}
@@ -1108,10 +1209,19 @@ sub det {
1108
1209
1109
1210
sub inverse {
1110
1211
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 ));
1115
1225
}
1116
1226
1117
1227
sub decompose_LR {
0 commit comments