6
6
7
7
=head1 Value::Matrix class
8
8
9
- References:
9
+ This is the Math Object code for a Matrix.
10
+
11
+ =head2 References:
10
12
11
13
=over
12
14
@@ -18,11 +20,7 @@ References:
18
20
19
21
=back
20
22
21
- For allowing Matrices in Fractions, see L<http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2978>
22
-
23
- Context()->parens->set("[" => {formMatrix => 1});
24
-
25
- =head2 Files interacting with Matrices:
23
+ =head2 Matrix-Related libraries and macros:
26
24
27
25
=over
28
26
@@ -46,7 +44,7 @@ For allowing Matrices in Fractions, see L<http://webwork.maa.org/moodle/mod/foru
46
44
47
45
=item L<tableau.pl>
48
46
49
- =item quickMatrixEntry.pl
47
+ =item L< quickMatrixEntry.pl>
50
48
51
49
=item L<LinearProgramming.pl>
52
50
@@ -55,12 +53,18 @@ For allowing Matrices in Fractions, see L<http://webwork.maa.org/moodle/mod/foru
55
53
=head2 Contexts
56
54
57
55
=over
58
- =item C<Matrix > -- allows students to enter [[3,4],[3,6]]
59
- -- formMatrix =>1 also allows this?
60
- =item C<Complex-Matrix > -- allows complex entries
56
+
57
+ =item C<Matrix >
58
+
59
+ Allows students to enter C<[[3,4],[3,6]] >
60
+
61
+ =item C<Complex-Matrix >
62
+
63
+ Allows complex entries
61
64
62
65
=back
63
66
67
+
64
68
=head2 Creation of Matrices
65
69
66
70
Using the C<Matrix > , C<Vector > or C<ColumnVector > methods
@@ -69,16 +73,16 @@ Examples:
69
73
70
74
$M1 = Matrix([1,2],[3,4]);
71
75
$M2 = Matrix([5,6],[7,8]);
72
- $v = Vector(9,10);
73
- $w = ColumnVector(9,10); # differs in how it is printed
74
76
75
77
Commands added in Value::matrix
76
78
77
79
Conversion:
80
+
78
81
$matrix->value produces [[3,4,5],[1,3,4]] recursive array references of numbers (not MathObjects)
79
82
$matrix->wwMatrix produces CPAN MatrixReal1 matrix, used for computation subroutines
80
83
81
84
Information
85
+
82
86
$matrix->dimension: ARRAY
83
87
84
88
Access values
@@ -88,7 +92,8 @@ Access values
88
92
element : Real or Complex value
89
93
90
94
Update values
91
- setElement.
95
+
96
+ setElement
92
97
93
98
See C<change_matrix_entry() > in MatrixReduce and L<http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2970>
94
99
@@ -128,6 +133,20 @@ The commands below are Value::Matrix B<methods> unless otherwise noted.
128
133
solve_SSM
129
134
solve_RM
130
135
136
+ =head2 Fractions in Matrices
137
+
138
+ One can use fractions in Matrices by including C<Context("Fraction") > . For example
139
+
140
+ Context("Fraction");
141
+ $A = Matrix([
142
+ [Fraction(1,1), Fraction(1,2), Fraction(1,3)],
143
+ [Fraction(1,2), Fraction(1,3), Fraction(1,4)],
144
+ [Fraction(1,3), Fraction(1,4), Fraction(1,5)]]);
145
+
146
+ and operations will be done using rational arithmetic. Also helpful is the method
147
+ C<apply_fraction_to_matrix_entries > in the L<MatrixReduce.pl> macro. Some additional information can be
148
+ found in L<https://webwork.maa.org/moodle/mod/forum/discuss.php?d=2978> .
149
+
131
150
=head2 methods
132
151
133
152
=cut
@@ -548,20 +567,17 @@ sub mult {
548
567
}
549
568
550
569
# Make points and vectors into columns if they are on the right.
551
-
552
- if (!$flag && Value::classMatch($r , ' Point' , ' Vector' )) { $r = ($self -> promote($r ))-> transpose }
553
- else { $r = $self -> promote($r ) }
570
+ $r = !$flag && Value::classMatch($r , ' Point' , ' Vector' ) ? ($self -> promote($r ))-> transpose : $self -> promote($r );
554
571
555
572
if ($flag ) { my $tmp = $l ; $l = $r ; $r = $tmp }
556
573
my @dl = $l -> dimensions;
557
574
my @dr = $r -> dimensions;
558
575
if (scalar (@dl ) == 1) { @dl = (1, @dl ); $l = $self -> make($l ) }
559
576
if (scalar (@dr ) == 1) { @dr = (@dr , 1); $r = $self -> make($r )-> transpose }
560
577
Value::Error(" Can only multiply 2-dimensional matrices" ) if scalar (@dl ) > 2 || scalar (@dr ) > 2;
561
- Value::Error(" Matrices of dimensions %dx%d and %dx%d can't be multiplied" , @dl , @dr )
562
- unless ($dl [1] == $dr [0]);
578
+ Value::Error(" Matrices of dimensions %dx%d and %dx%d can't be multiplied" , @dl , @dr ) unless ($dl [1] == $dr [0]);
563
579
564
- # Perform atrix multiplication.
580
+ # Perform matrix multiplication.
565
581
566
582
my @l = $l -> value;
567
583
my @r = $r -> value;
@@ -635,7 +651,7 @@ sub conj { shift->twiddle(@_) }
635
651
sub twiddle {
636
652
my $self = promote(@_ );
637
653
my @coords = ();
638
- foreach my $x (@{ $self -> data }) { push (@coords , ($x -> can(" conj" ) ? $x -> conj : $x )) }
654
+ for my $x (@{ $self -> data }) { push (@coords , ($x -> can(" conj" ) ? $x -> conj : $x )); }
639
655
return $self -> make(@coords );
640
656
}
641
657
@@ -655,6 +671,7 @@ sub transpose {
655
671
my @d = $self -> dimensions;
656
672
if (scalar (@d ) == 1) { @d = (1, @d ); $self = $self -> make($self ) }
657
673
Value::Error(" Can't transpose %d -dimensional matrices" , scalar (@d )) unless scalar (@d ) == 2;
674
+
658
675
my @M = ();
659
676
my $M = $self -> data;
660
677
for my $j (0 .. $d [1] - 1) {
@@ -683,8 +700,10 @@ sub I {
683
700
my $d = shift ;
684
701
my $context = shift || $self -> context;
685
702
$d = ($self -> dimensions)[0] if !defined $d && ref ($self );
703
+
686
704
Value::Error(" You must provide a dimension for the Identity matrix" ) unless defined $d ;
687
705
Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
706
+
688
707
my @M = ();
689
708
my $REAL = $context -> Package(' Real' );
690
709
@@ -772,14 +791,16 @@ sub E {
772
791
($rows , $k , $context ) = ($d , $rows , $k );
773
792
$d = ($self -> dimensions)[0] if ref ($self );
774
793
}
775
- $context = $self -> context unless $context ;
776
- Value::Error(" You must provide a dimension for an Elementary matrix" ) unless defined $d ;
777
- Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
794
+ $context = $self -> context unless $context ;
778
795
my @ij = @{$rows };
796
+
797
+ Value::Error(" You must provide a dimension for an Elementary matrix" ) unless defined $d ;
798
+ Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
779
799
Value::Error(" Either one or two rows must be specified for an Elementary matrix" ) unless (@ij == 1 || @ij == 2);
780
800
Value::Error(
781
801
" If only one row is specified for an Elementary matrix, then a number to scale by must also be specified" )
782
802
if (@ij == 1 && !defined $k );
803
+
783
804
for (@ij ) {
784
805
Value::Error(" Row indices must be integers between 1 and $d " )
785
806
unless ($_ =~ m / ^[1-9] \d *$ / && $_ >= 1 && $_ <= $d );
@@ -845,6 +866,7 @@ sub P {
845
866
}
846
867
my $context = $self -> context;
847
868
$d = ($self -> dimensions)[0] if !defined $d && ref ($self ) && $self -> isSquare;
869
+
848
870
Value::Error(" You must provide a dimension for a Permutation matrix" ) unless defined $d ;
849
871
Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
850
872
for my $c (@cycles ) {
@@ -895,6 +917,7 @@ sub Zero {
895
917
$n = $m if !defined $n && defined $m ;
896
918
$m = ($self -> dimensions)[0] if !defined $m && ref ($self );
897
919
$n = ($self -> dimensions)[1] if !defined $n && ref ($self );
920
+
898
921
Value::Error(" You must provide dimensions for the Zero matrix" ) unless defined $m && defined $n ;
899
922
Value::Error(" Dimension must be a positive integer" ) unless $m =~ m / ^[1-9] \d *$ / && $n =~ m / ^[1-9] \d *$ / ;
900
923
my @M = ();
@@ -933,7 +956,7 @@ Extract a given column from the matrix.
933
956
934
957
Usage:
935
958
936
- my $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
959
+ $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
937
960
$A1->column(2); # returns the column Matrix [[2],[6],[10]]
938
961
939
962
=cut
@@ -962,13 +985,13 @@ Extract an element from the given row/col.
962
985
963
986
Usage:
964
987
965
- my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
988
+ $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
966
989
$A->element(2,3); # returns 7
967
990
968
- my $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
991
+ $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
969
992
$B->element(1,2,1); # returns 3;
970
993
971
- my $row = Matrix([4,3,2,1]);
994
+ $row = Matrix([4,3,2,1]);
972
995
$row->element(2); # returns 3;
973
996
=cut
974
997
@@ -988,30 +1011,61 @@ Note: this mutates the matrix itself.
988
1011
989
1012
Usage:
990
1013
991
- my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
1014
+ $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
992
1015
$A->setElement([2,3],-5);
993
1016
994
-
995
1017
=cut
996
1018
997
1019
sub setElement {
998
1020
my ($self , $ind , $value ) = @_ ;
999
1021
1022
+ Value::Error(" The index $ind ->[0] does not exist in the matrix" ) unless defined $self -> {data }[ $ind -> [0] - 1 ];
1023
+
1000
1024
# Drill down into the matrix
1001
- my $el = $self -> {data }[ $ind -> [0] - 1 ];
1002
- for my $i (1 .. scalar (@$ind ) - 1) { $el = $el -> {data }[ $ind -> [$i ] - 1 ]; }
1025
+ my $el = \($self -> {data }[ $ind -> [0] - 1 ]);
1026
+ for my $i (1 .. scalar (@$ind ) - 1) {
1027
+ Value::Error(" The index $ind ->[$i ] does not exist in the matrix" ) unless defined $$el -> {data }[ $ind -> [$i ] - 1 ];
1028
+ $el = \($$el -> {data }[ $ind -> [$i ] - 1 ]);
1029
+ }
1003
1030
1004
1031
# update the value of $el
1005
- $el = Value::makeValue($value );
1032
+ $$el = Value::makeValue($value );
1033
+ }
1034
+
1035
+ # The subroutine extractElements is used in the subMatrix routine. This called recursively to handle
1036
+ # any dimension of a Matrix. initially $indices needs to be [] and $elements an arrayref of the
1037
+ # elements to be extracted.
1038
+ #
1039
+ # Through subsequent passes through the subroutine, the indices in the $elements arguments are passed to the $indices.
1040
+
1041
+ sub extractElements {
1042
+ my ($self , $indices , $elements ) = @_ ;
1043
+
1044
+ # These need to be copies of the array arguments.
1045
+ my @ind_copy = @$indices ;
1046
+ my @elements_copy = @$elements ;
1047
+
1048
+ my $ind = shift @elements_copy ;
1049
+ push (@ind_copy , [ 1 .. scalar (@$ind ) ]);
1050
+
1051
+ my @M ;
1052
+ for my $i (@$ind ) {
1053
+ push (@M ,
1054
+ ref $self -> element($i ) eq ' Value::Matrix'
1055
+ ? $self -> element($i )-> extractElements(\@ind_copy , \@elements_copy )
1056
+ : $self -> element($i ));
1057
+ }
1058
+
1059
+ return $self -> make($self -> context, @M );
1006
1060
}
1007
1061
1008
1062
=head3 C<subMatrix >
1009
1063
1010
1064
1011
- Return a submatrix of the matrix. If the rows and columns are array refs, the given rows and
1012
- columns of the matrix are returns as a Matrix object.
1065
+ Return a submatrix of the matrix. If the indices are array refs, the given rows and
1066
+ columns (or more) of the matrix are returns as a Matrix object.
1013
1067
1014
- If the input are integers, then the submatrix with that row and column removed.
1068
+ If the input are integers, then the submatrix with those indices removed.
1015
1069
1016
1070
Usage:
1017
1071
@@ -1022,37 +1076,51 @@ Usage:
1022
1076
$A->subMatrix(2,3); # returns Matrix([ [ 1, 2, 4 ], [ 9, 10, 12 ] ]);
1023
1077
1024
1078
$A->subMatrix([3,1,2],[1,4,2]); # returns Matrix([9,12,10],[1,4,2],[5,8,6]);
1079
+
1080
+ This subroutine can be used on non 2D matrices. For example,
1081
+
1082
+ $B = Matrix([2, 4, 6, 8]);
1083
+ $B->subMatrix([1, 3]); # returns Matrix([2, 6]);
1084
+ $B->subMatrix(2); # returns Matrix([2, 6, 8]);
1085
+
1086
+ And for 3D matrices:
1087
+
1088
+ $C = Matrix([ [ [ 1, 2, 3 ], [ 4, 5, 6 ] ], [ [ 7, 8, 9 ], [ 10, 11, 12 ] ] ]);
1089
+ $C->subMatrix([1, 2], [1, 2], [1, 3]); # returns Matrix([ [ [ 1, 3 ], [ 4, 6 ] ], [ [ 7, 9 ], [ 10, 12 ] ] ]);
1090
+
1091
+ $C->subMatrix(1,2,3); # returns Matrix([ [ [ 7, 8 ] ] ]);
1092
+
1025
1093
=cut
1026
1094
1027
1095
sub subMatrix {
1028
- my ($self , $r , $c ) = @_ ;
1029
- my $context = $self -> context;
1030
- my ($rows , $cols );
1031
- my ($nrow , $ncol ) = $self -> dimensions;
1032
-
1033
- # check if the inputs are integers.
1034
- if (ref $r eq ' ' && ref $c eq ' ' ) {
1035
- Value::Error(" The input $r is not a valid row." ) unless $r >= 1 && $r <= $nrow && int ($r ) == $r ;
1036
- Value::Error(" The input $c is not a valid column." ) unless $c >= 1 && $c <= $ncol && int ($c ) == $c ;
1037
- $rows = [ grep { $_ != $r } (1 .. $nrow ) ];
1038
- $cols = [ grep { $_ != $c } (1 .. $ncol ) ];
1039
- } elsif (ref $r eq ' ARRAY' && ref $c eq ' ARRAY' ) {
1040
- $rows = $r ;
1041
- $cols = $c ;
1042
- for my $i (@$rows ) {
1043
- Value::Error(" The input $i is not a valid row." ) unless int ($i ) == $i && $i >= 1 && $i <= $nrow ;
1044
- }
1045
- for my $i (@$cols ) {
1046
- Value::Error(" The input $i is not a valid column." ) unless int ($i ) == $i && $i >= 1 && $i <= $ncol ;
1096
+ my ($self , @ind ) = @_ ;
1097
+ my @dim = $self -> dimensions;
1098
+ my @indices ; # Indices to keep for submatrix.
1099
+
1100
+ # check that the input is appropriate for the size of the matrix.
1101
+ Value::Error(" The indices must be array refs the same size as the dimension of the matrix." ) unless $#dim == $#ind ;
1102
+
1103
+ # check that inputs are either all integers or all array refs
1104
+ my @index_types = keys %{ { map { ref $_ , 1 } @ind } };
1105
+
1106
+ Value::Error(' The inputs must both be integers or array refs.' )
1107
+ unless scalar (@index_types ) == 1 && ($index_types [0] eq ' ' || $index_types [0] eq ' ARRAY' );
1108
+
1109
+ for my $i (0 .. $#ind ) {
1110
+ if ($index_types [0] eq ' ' ) { # input is a scalar (integer)
1111
+ Value::Error(" The input $ind [$i ] is not a valid index" )
1112
+ unless $ind [$i ] >= 1 && $ind [$i ] <= $dim [$i ] && int ($ind [$i ]) == $ind [$i ];
1113
+ push (@indices , [ grep { $_ != $ind [$i ] } (1 .. $dim [$i ]) ]);
1114
+
1115
+ } elsif ($index_types [0] eq ' ARRAY' ) { # input are array refs
1116
+ for my $j (@{ $ind [$i ] }) {
1117
+ Value::Error(" The input $j is not a valid index" ) unless int ($j ) == $j && $j >= 1 && $j <= $dim [$i ];
1118
+ }
1119
+ push (@indices , $ind [$i ]);
1047
1120
}
1048
- } else {
1049
- Value::Error(' The inputs must both be integers or array refs.' );
1050
- }
1051
- my @M = ();
1052
- for my $r (@$rows ) {
1053
- push (@M , $self -> make($context , map { $self -> element($r , $_ ) } @$cols ));
1054
1121
}
1055
- return $self -> make($context , @M );
1122
+
1123
+ return $self -> extractElements([], \@indices );
1056
1124
}
1057
1125
1058
1126
=head3 C<removeRow >
@@ -1072,13 +1140,13 @@ sub removeRow {
1072
1140
my ($self , $row ) = @_ ;
1073
1141
my $context = $self -> context;
1074
1142
my @d = $self -> dimensions;
1075
- Value::Error(" The method removeRow is only valid for 2D matrices." ) unless scalar (@d ) eq 2;
1143
+ Value::Error(" The method removeRow is only valid for 2D matrices." ) unless scalar (@d ) == 2;
1076
1144
my ($nrow , $ncol ) = @d ;
1077
1145
Value::Error(" The input $row is not a valid row." )
1078
1146
unless ref ($row ) eq ' ' && $row >= 1 && $row <= $nrow && int ($row ) == $row ;
1079
1147
1080
1148
my @M = ();
1081
- for my $r (1 .. $nrow ) { push (@M , $self -> make( $context , $r )) unless $r eq $row ; }
1149
+ for my $r (1 .. $nrow ) { push (@M , $self -> row( $r )) unless $r eq $row ; }
1082
1150
return $self -> make($context , @M );
1083
1151
}
1084
1152
@@ -1113,9 +1181,6 @@ sub removeColumn {
1113
1181
return $self -> make($context , @M );
1114
1182
}
1115
1183
1116
- # @@@ removeRow, removeColumn @@@
1117
- # @@@ Minor @@@
1118
-
1119
1184
# Convert MathObject Matrix to old-style Matrix
1120
1185
sub wwMatrix {
1121
1186
my $self = (ref ($_ [0]) ? $_ [0] : shift );
@@ -1373,4 +1438,3 @@ sub TeX {
1373
1438
}
1374
1439
1375
1440
1;
1376
-
0 commit comments