66
77=head1 Value::Matrix class
88
9- References:
9+ This is the Math Object code for a Matrix.
10+
11+ =head2 References:
1012
1113=over
1214
@@ -18,11 +20,7 @@ References:
1820
1921=back
2022
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:
2624
2725=over
2826
@@ -46,7 +44,7 @@ For allowing Matrices in Fractions, see L<http://webwork.maa.org/moodle/mod/foru
4644
4745=item L<tableau.pl>
4846
49- =item quickMatrixEntry.pl
47+ =item L< quickMatrixEntry.pl>
5048
5149=item L<LinearProgramming.pl>
5250
@@ -55,12 +53,18 @@ For allowing Matrices in Fractions, see L<http://webwork.maa.org/moodle/mod/foru
5553=head2 Contexts
5654
5755=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
6164
6265=back
6366
67+
6468=head2 Creation of Matrices
6569
6670Using the C<Matrix > , C<Vector > or C<ColumnVector > methods
@@ -69,16 +73,16 @@ Examples:
6973
7074 $M1 = Matrix([1,2],[3,4]);
7175 $M2 = Matrix([5,6],[7,8]);
72- $v = Vector(9,10);
73- $w = ColumnVector(9,10); # differs in how it is printed
7476
7577Commands added in Value::matrix
7678
7779Conversion:
80+
7881 $matrix->value produces [[3,4,5],[1,3,4]] recursive array references of numbers (not MathObjects)
7982 $matrix->wwMatrix produces CPAN MatrixReal1 matrix, used for computation subroutines
8083
8184Information
85+
8286 $matrix->dimension: ARRAY
8387
8488Access values
@@ -88,7 +92,8 @@ Access values
8892 element : Real or Complex value
8993
9094Update values
91- setElement.
95+
96+ setElement
9297
9398See C<change_matrix_entry() > in MatrixReduce and L<http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2970>
9499
@@ -128,6 +133,20 @@ The commands below are Value::Matrix B<methods> unless otherwise noted.
128133 solve_SSM
129134 solve_RM
130135
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+
131150=head2 methods
132151
133152=cut
@@ -548,20 +567,17 @@ sub mult {
548567 }
549568
550569 # 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 );
554571
555572 if ($flag ) { my $tmp = $l ; $l = $r ; $r = $tmp }
556573 my @dl = $l -> dimensions;
557574 my @dr = $r -> dimensions;
558575 if (scalar (@dl ) == 1) { @dl = (1, @dl ); $l = $self -> make($l ) }
559576 if (scalar (@dr ) == 1) { @dr = (@dr , 1); $r = $self -> make($r )-> transpose }
560577 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]);
563579
564- # Perform atrix multiplication.
580+ # Perform matrix multiplication.
565581
566582 my @l = $l -> value;
567583 my @r = $r -> value;
@@ -635,7 +651,7 @@ sub conj { shift->twiddle(@_) }
635651sub twiddle {
636652 my $self = promote(@_ );
637653 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 )); }
639655 return $self -> make(@coords );
640656}
641657
@@ -655,6 +671,7 @@ sub transpose {
655671 my @d = $self -> dimensions;
656672 if (scalar (@d ) == 1) { @d = (1, @d ); $self = $self -> make($self ) }
657673 Value::Error(" Can't transpose %d -dimensional matrices" , scalar (@d )) unless scalar (@d ) == 2;
674+
658675 my @M = ();
659676 my $M = $self -> data;
660677 for my $j (0 .. $d [1] - 1) {
@@ -683,8 +700,10 @@ sub I {
683700 my $d = shift ;
684701 my $context = shift || $self -> context;
685702 $d = ($self -> dimensions)[0] if !defined $d && ref ($self );
703+
686704 Value::Error(" You must provide a dimension for the Identity matrix" ) unless defined $d ;
687705 Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
706+
688707 my @M = ();
689708 my $REAL = $context -> Package(' Real' );
690709
@@ -772,14 +791,16 @@ sub E {
772791 ($rows , $k , $context ) = ($d , $rows , $k );
773792 $d = ($self -> dimensions)[0] if ref ($self );
774793 }
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 ;
778795 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 *$ / ;
779799 Value::Error(" Either one or two rows must be specified for an Elementary matrix" ) unless (@ij == 1 || @ij == 2);
780800 Value::Error(
781801 " If only one row is specified for an Elementary matrix, then a number to scale by must also be specified" )
782802 if (@ij == 1 && !defined $k );
803+
783804 for (@ij ) {
784805 Value::Error(" Row indices must be integers between 1 and $d " )
785806 unless ($_ =~ m / ^[1-9] \d *$ / && $_ >= 1 && $_ <= $d );
@@ -845,6 +866,7 @@ sub P {
845866 }
846867 my $context = $self -> context;
847868 $d = ($self -> dimensions)[0] if !defined $d && ref ($self ) && $self -> isSquare;
869+
848870 Value::Error(" You must provide a dimension for a Permutation matrix" ) unless defined $d ;
849871 Value::Error(" Dimension must be a positive integer" ) unless $d =~ m / ^[1-9] \d *$ / ;
850872 for my $c (@cycles ) {
@@ -895,6 +917,7 @@ sub Zero {
895917 $n = $m if !defined $n && defined $m ;
896918 $m = ($self -> dimensions)[0] if !defined $m && ref ($self );
897919 $n = ($self -> dimensions)[1] if !defined $n && ref ($self );
920+
898921 Value::Error(" You must provide dimensions for the Zero matrix" ) unless defined $m && defined $n ;
899922 Value::Error(" Dimension must be a positive integer" ) unless $m =~ m / ^[1-9] \d *$ / && $n =~ m / ^[1-9] \d *$ / ;
900923 my @M = ();
@@ -933,7 +956,7 @@ Extract a given column from the matrix.
933956
934957Usage:
935958
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 ] ]);
937960 $A1->column(2); # returns the column Matrix [[2],[6],[10]]
938961
939962=cut
@@ -962,13 +985,13 @@ Extract an element from the given row/col.
962985
963986Usage:
964987
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 ] ]);
966989 $A->element(2,3); # returns 7
967990
968- my $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
991+ $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
969992 $B->element(1,2,1); # returns 3;
970993
971- my $row = Matrix([4,3,2,1]);
994+ $row = Matrix([4,3,2,1]);
972995 $row->element(2); # returns 3;
973996=cut
974997
@@ -988,21 +1011,52 @@ Note: this mutates the matrix itself.
9881011
9891012Usage:
9901013
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 ] ]);
9921015 $A->setElement([2,3],-5);
9931016
994-
9951017=cut
9961018
9971019sub setElement {
9981020 my ($self , $ind , $value ) = @_ ;
9991021
1022+ Value::Error(" The index $ind ->[0] does not exist in the matrix" ) unless defined $self -> {data }[ $ind -> [0] - 1 ];
1023+
10001024 # 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+ }
10031030
10041031 # 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 );
10061060}
10071061
10081062=head3 C<subMatrix >
@@ -1022,37 +1076,51 @@ Usage:
10221076 $A->subMatrix(2,3); # returns Matrix([ [ 1, 2, 4 ], [ 9, 10, 12 ] ]);
10231077
10241078 $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+
10251093=cut
10261094
10271095sub 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 ]);
10471120 }
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 ));
10541121 }
1055- return $self -> make($context , @M );
1122+
1123+ return $self -> extractElements([], \@indices );
10561124}
10571125
10581126=head3 C<removeRow >
@@ -1072,13 +1140,13 @@ sub removeRow {
10721140 my ($self , $row ) = @_ ;
10731141 my $context = $self -> context;
10741142 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;
10761144 my ($nrow , $ncol ) = @d ;
10771145 Value::Error(" The input $row is not a valid row." )
10781146 unless ref ($row ) eq ' ' && $row >= 1 && $row <= $nrow && int ($row ) == $row ;
10791147
10801148 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 ; }
10821150 return $self -> make($context , @M );
10831151}
10841152
@@ -1113,9 +1181,6 @@ sub removeColumn {
11131181 return $self -> make($context , @M );
11141182}
11151183
1116- # @@@ removeRow, removeColumn @@@
1117- # @@@ Minor @@@
1118-
11191184# Convert MathObject Matrix to old-style Matrix
11201185sub wwMatrix {
11211186 my $self = (ref ($_ [0]) ? $_ [0] : shift );
@@ -1373,4 +1438,3 @@ sub TeX {
13731438}
13741439
137514401;
1376-
0 commit comments