From a7e2649a2629e513e8f7975f8b6f84eaf62580af Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Sun, 23 Mar 2025 15:07:16 -0700 Subject: [PATCH 1/5] tensor multiplication, powers, idenity, inverses --- lib/Value/Matrix.pm | 239 ++++++++++++++++++++++++++++++++------------ 1 file changed, 174 insertions(+), 65 deletions(-) diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index 74e1b9f5a3..1a05ca3964 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -64,17 +64,17 @@ Allows complex entries =back -=head2 Creation of Matrices +=head2 Creation of Matrix MathObjects Examples: - $M1 = Matrix(1, 2, 3); # 1D (row vector) + $M1 = Matrix(1, 2, 3); # degree 1 (row vector) $M1 = Matrix([1, 2, 3]); - $M2 = Matrix([1, 2], [3, 4]); # 2D (matrix) + $M2 = Matrix([1, 2], [3, 4]); # degree 2 (matrix) $M2 = Matrix([[1, 2], [3, 4]]); - $M3 = Matrix([[1, 2], [3, 4]], [[5, 6], [7, 8]]); # 3D (tensor) + $M3 = Matrix([[1, 2], [3, 4]], [[5, 6], [7, 8]]); # degree 3 (tensor) $M3 = Matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]); $M4 = ... @@ -83,7 +83,7 @@ Examples: =head3 Conversion - $matrix->value produces an array of numbers (for a 1D tensor) or array refs representing the rows. + $matrix->value produces an array of numbers (for a degree 1 Matrix) or array refs representing the rows. These are perl numbers and array refs, not MathObjects. $M1->value is (1, 2, 3) @@ -91,13 +91,15 @@ Examples: $M3->value is ([[1, 2], [3, 4]], [[5, 6], [7, 8]]) $matrix->wwMatrix produces CPAN MatrixReal1 matrix, used for computation subroutines and can only - be used on 1D tensors (row vector) or 2D tensors (matrix). + be used on a degree 1 or degree 2 Matrix. =head3 Information - $matrix->dimensions produces an array. For an n-dimensional tensor, the array has n entries for + $matrix->dimensions produces an array. For an n-degree Matrix, the array has n entries for the n dimensions. + $matrix->degree returns the degree of the Matrix (the depth of the nested array). + =head3 Access values row(i) : MathObjectMatrix @@ -305,7 +307,7 @@ returns C<(2,2,2)> # # Recursively get the dimensions of the matrix. -# Returns (d_1,d_2,...,d_n) for an nD matrix. +# Returns (d_1,d_2,...,d_n) for an degree n Matrix. # sub dimensions { my $self = shift; @@ -315,6 +317,13 @@ sub dimensions { return ($r, $v->[0]->dimensions); } +sub degree { + my $self = shift; + my $v = $self->data; + return 1 unless Value::classMatch($v->[0], 'Matrix'); + return 1 + $v->[0]->degree; +} + # # Return the proper type for the matrix # @@ -327,24 +336,31 @@ sub typeRef { =head3 C -Return true is the matrix is 1D of length 1 or 2D and square, false otherwise +Return true if the Matrix is degree 1 of length 1 or its last two dimensions are equal, false otherwise Usage: - $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); - $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); + $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]); + $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ] ]); + $C = Matrix(1); + $D = Matrix(1, 2); + $E = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); + $F = Matrix([ [ [ 1, 2 ] ], [ [ 3, 4 ] ] ]); - $A->isSquare; # is '' (false) - $B->isSquare; # is 1 (true); + $A->isSquare; # is 1 (true) because it is a 3x3 matrix + $B->isSquare; # is '' (false) because it is a 2x3 matrix + $C->isSquare; # is 1 (true) because it is a degree 1 matrix of length 1 + $D->isSquare; # is '' (false) because it is a degree 1 matrix of length 2 + $E->isSquare; # is 1 (true) because it is a 1x2x2 tensor + $F->isSquare; # is '' (false) because it is a 2x1x2 tensor =cut sub isSquare { my $self = shift; my @d = $self->dimensions; - return 1 if scalar(@d) == 1 && $d[0] == 1; - return 0 if scalar(@d) != 2; - return $d[0] == $d[1]; + if (scalar(@d) == 1) { return $d[0] == 1 } + return ($d[-1] == $d[-2]); } =head3 C @@ -369,7 +385,7 @@ sub isRow { =head3 C -Check for identity matrix. +Check for identity Matrix (for degree > 2, this means the last two dimensions hold identity matrices) Usage: @@ -379,19 +395,33 @@ Usage: $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); $B->isOne; # is true; + $C = Matrix(1); + $C->isOne; # is true + + $D = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); + $D->isOne; # is true + =cut sub isOne { my $self = shift; return 0 unless $self->isSquare; - my $i = 0; - for my $row (@{ $self->{data} }) { - my $j = 0; - for my $k (@{ $row->{data} }) { - return 0 unless $k eq (($i == $j) ? "1" : "0"); - $j++; + if ($self->degree <= 2) { + my $i = 0; + for my $row (@{ $self->{data} }) { + my $j = 0; + for my $k (@{ $row->{data} }) { + return 0 unless $k eq (($i == $j) ? "1" : "0"); + $j++; + } + $i++; + } + } else { + for my $row (@{ $self->{data} }) { + if (!$row->isOne) { + return 0; + } } - $i++; } return 1; } @@ -597,36 +627,73 @@ sub mult { return $self->make(@coords); } - # Make points and vectors into columns if they are on the right. - $r = !$flag && Value::classMatch($r, 'Point', 'Vector') ? ($self->promote($r))->transpose : $self->promote($r); + # Special case if $r is a Point or Vector and if $l is a degree 2 Matrix, + # we promote $r to a degree 1 Matrix and later restore the product to be Point or Vector + $r = + !$flag + && Value::classMatch($r, 'Point', 'Vector') + && $l->degree == 2 ? ($self->promote($r))->transpose : $self->promote($r); - if (!$flag && Value::classMatch($r, 'Point', 'Vector')) { $r = ($self->promote($r))->transpose } - else { $r = $self->promote($r) } - # - if ($flag) { my $tmp = $l; $l = $r; $r = $tmp } + if ($flag) { ($l, $r) = ($r, $l) } my @dl = $l->dimensions; my @dr = $r->dimensions; - if (scalar(@dl) == 1) { @dl = (1, @dl); $l = $self->make($l) } - if (scalar(@dr) == 1) { @dr = (@dr, 1); $r = $self->make($r)->transpose } - Value::Error("Can only multiply 2-dimensional matrices") if scalar(@dl) > 2 || scalar(@dr) > 2; - Value::Error("Matrices of dimensions %dx%d and %dx%d can't be multiplied", @dl, @dr) unless ($dl[1] == $dr[0]); + my @l = $l->value; + my @r = $r->value; + + # Special case if $r and $l are both degree 1, perform dot product + if (scalar(@dl) == 1 && scalar(@dr) == 1) { + Value::Error("Can't multiply degree 1 matrices of different lengths") unless ($dl[0] == $dr[0]); + my $result = 0; + for my $i (0 .. $dl[0] - 1) { + $result += $l[$i] * $r[$i]; + } + return $result; + } - # Perform matrix multiplication. + # Promote degree 1 Matrix to degree 2, as row or column depending on side + # Later restore result to degree 1 if appropriate + my $l1 = scalar(@dl) == 1; + my $r1 = scalar(@dr) == 1; + if ($l1) { @dl = (1, @dl); $l = $self->make($l); @l = $l->value } + if ($r1) { @dr = (@dr, 1); $r = $self->make($r)->transpose; @r = $r->value } + + # Multiplication is only possible when dimensions are Zxmxn and Zxnxk + my $bad_dims; + if (scalar(@dl) != scalar(@dr)) { + $bad_dims = 1; + } elsif ($dl[-1] != $dr[-2]) { + $bad_dims = 1; + } else { + for my $i (0 .. scalar(@dl) - 3) { + if ($dl[$i] != $dr[$i]) { + $bad_dims = 1; + last; + } + } + } + Value::Error("Matrices of dimensions %s and %s can't be multiplied", join('x', @dl), join('x', @dr)) if $bad_dims; - my @l = $l->value; - my @r = $r->value; + # Perform matrix/tensor multiplication. my @M = (); for my $i (0 .. $dl[0] - 1) { - my @row = (); - for my $j (0 .. $dr[1] - 1) { - my $s = 0; - for my $k (0 .. $dl[1] - 1) { $s += $l[$i]->[$k] * $r[$k]->[$j] } - push(@row, $s); + if (scalar(@dl) == 2) { + my @row = (); + for my $j (0 .. $dr[1] - 1) { + my $s = 0; + for my $k (0 .. $dl[1] - 1) { $s += $l[$i]->[$k] * $r[$k]->[$j] } + push(@row, $s); + } + push(@M, $self->make(@row)); + } else { + push(@M, $l->data->[$i] * $r->data->[$i]); } - push(@M, $self->make(@row)); } $self = $self->inherit($other) if Value::isValue($other); - return $self->make(@M); + + if ($l1) { return $self->make(@M)->row(1) } + if ($r1) { return $self->make(@M)->transpose->row(1) } + + return $other->new($self->make(@M)); } sub div { @@ -653,10 +720,23 @@ sub power { $self->Error("Matrix is not invertible") unless defined($l); } Value::Error("Matrix powers must be non-negative integers") unless _isNumber($r) && $r =~ m/^\d+$/; - return $context->Package("Matrix")->I($l->length, $context) if $r == 0; - my $M = $l; - for my $i (2 .. $r) { $M = $M * $l } - return $M; + return $l if $r == 1; + my @powers = ($l); + my @d = $l->dimensions; + my $d = pop @d; + pop @d; + my $return = $context->Package("Matrix")->I(\@d, $d); + my $p = $r; + + while ($p) { + if ($p % 2) { + $p -= 1; + $return *= $powers[-1]; + } + push(@powers, $powers[-1] * $powers[-1]); + $p /= 2; + } + return $return; } # Do lexicographic comparison (row by row) @@ -718,31 +798,51 @@ sub transpose { =head3 C -Get an identity matrix of the requested size +Get a identity Matrix of the requested size Value::Matrix->I(n) Usage: - Value::Matrix->I(3); # returns a 3 by 3 identity matrix. - $A->I; # return an n by n identity matrix, where n is the number of rows of A + Value::Matrix->I(3); # returns a 3x3 identity matrix. + Value::Matrix->I([2],3); # returns a 2x3x3 identity Matrix (tensor) + Value::Matrix->I([2,4],2); # returns a 2x4x2x2 identity Matrix (tensor) + $A->I; # return an identity matrix of the appropriate degree and dimensions such that I*A = A =cut sub I { - my $self = shift; - my $d = shift; - my $context = shift || $self->context; - $d = ($self->dimensions)[0] if !defined $d && ref($self); - - Value::Error("You must provide a dimension for the Identity matrix") unless defined $d; - Value::Error("Dimension must be a positive integer") unless $d =~ m/^[1-9]\d*$/; + my $self = shift; + my @d; + my $d; + my $context; + if (ref($self) eq 'Value::Matrix') { + @d = $self->dimensions; + pop @d unless scalar(@d) == 1; + $d = pop @d; + $context = $self->context; + } else { + $d = shift; # array ref, or number + if (ref($d) eq 'ARRAY') { + @d = @{$d}; + $d = shift; + } + Value::Error("You must provide a dimension for the Identity matrix") unless ($d || $d eq '0'); + Value::Error("Dimension must be a positive integer") unless $d =~ m/^[1-9]\d*$/; + $context = shift || $self->context; + } - my @M = (); + my @M; my $REAL = $context->Package('Real'); - for my $i (0 .. $d - 1) { - push(@M, $self->make($context, map { $REAL->new(($_ == $i) ? 1 : 0) } 0 .. $d - 1)); + if (!@d) { + for my $i (0 .. $d - 1) { + push(@M, $self->make($context, map { $REAL->new(($_ == $i) ? 1 : 0) } 0 .. $d - 1)); + } + } else { + for my $i (1 .. $d[0]) { + push(@M, Value::Matrix->I([ @d[ 1 .. $#d ] ], $d)); + } } return $self->make($context, @M); } @@ -1108,10 +1208,19 @@ sub det { sub inverse { my $self = shift; - $self->wwMatrixLR; - Value->Error("Can't take inverse of non-square matrix") unless $self->isSquare; - my $I = $self->{lrM}->invert_LR; - return (defined($I) ? $self->new($I) : $I); + my @d = $self->dimensions; + my @M = (); + for my $i (0 .. $d[0] - 1) { + if (scalar(@d) == 2) { + $self->wwMatrixLR; + Value->Error("Can't take inverse of non-square matrix") unless $self->isSquare; + my $I = $self->{lrM}->invert_LR; + return (defined($I) ? $self->new($I) : $I); + } else { + push(@M, $self->data->[$i]->inverse); + } + } + return $self->new($self->make(@M)); } sub decompose_LR { From 2612e10fb21ebfe3c1277b082a52547d5d4ba08c Mon Sep 17 00:00:00 2001 From: Peter Staab Date: Thu, 27 Mar 2025 09:50:20 -0400 Subject: [PATCH 2/5] Fix typo in Matrix doc and add a test. --- lib/Value/Matrix.pm | 2 +- t/math_objects/matrix.t | 301 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 t/math_objects/matrix.t diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index 1a05ca3964..79d62e1f03 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -650,7 +650,7 @@ sub mult { return $result; } - # Promote degree 1 Matrix to degree 2, as row or column depending on side + # Promote degree 1 Matrix to degree 2, as row or column depending on size # Later restore result to degree 1 if appropriate my $l1 = scalar(@dl) == 1; my $r1 = scalar(@dr) == 1; diff --git a/t/math_objects/matrix.t b/t/math_objects/matrix.t new file mode 100644 index 0000000000..60ea420988 --- /dev/null +++ b/t/math_objects/matrix.t @@ -0,0 +1,301 @@ +#!/usr/bin/env perl + +=head1 MathObjects - Matrix + +Tests creation and manipulation of Matrix math objects. + +=cut + +use Test2::V0 '!E', { E => 'EXISTS' }; + +die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT}; +do "$ENV{PG_ROOT}/t/build_PG_envir.pl"; + +loadMacros('MathObjects.pl'); + +Context('Matrix'); +use Data::Dumper; +subtest 'Creating degree 1 matrices (row vector)' => sub { + ok my $M1 = Matrix(1, 2, 3), 'Create a row vector'; + is $M1->class, 'Matrix', 'M1 is a Matrix'; + my $M2 = Compute('[1,2,3]'); + is $M2->class, 'Matrix', 'Creation using Compute results in a Matrix.'; + is [ $M1->value ], [ 1, 2, 3 ], 'M1 is the row matrix [1,23]'; + is [ $M2->value ], [ 1, 2, 3 ], 'M2 is the row matrix [1,23]'; + is $M1->degree, 1, 'M1 is a degree 1 matrix.'; + is $M2->degree, 1, 'M2 is a degree 1 matrix.'; +}; + +subtest 'Creating Matrices' => sub { + my $values = [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]; + my $A = Matrix($values); + is $A->class, 'Matrix', 'Input as array ref is a Matrix.'; + is [ $A->value ], $values, 'The entry values is correct.'; + my $B = Matrix('[[1,2,3,4],[5,6,7,8], [9,10,11,12]]'); + is $B->class, 'Matrix', 'Input as a string is a Matrix.'; + my $C = Compute('[[1,2,3,4],[5,6,7,8], [9,10,11,12]]'); + is $C->class, 'Matrix', 'Input using Compute is a Matrix.'; + is $A->degree, 2, 'A is a degree 2 matrix.'; + is $C->degree, 2, 'C is a degree 2 matrix.'; +}; + +subtest 'Creating Tensors (degree 3)' => sub { + my $values = [ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]; + ok my $M3 = Matrix([ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ]), 'Creation of a tensor'; + is $M3->class, 'Matrix', 'Checking the result is a Matrix'; + # is $M3->value, $values, 'yay'; + # print Dumper ref($M3->value); + is $M3->degree, 3, 'M3 is a degree 3 matrix.'; +}; + +subtest 'Matrix Dimensions' => sub { + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); + my $C = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + my $row = Matrix([ 1, 2, 3, 4 ]); + my @dimsA = $A->dimensions; + my @dimsB = $B->dimensions; + my @dimsC = $C->dimensions; + my @dimsRow = $row->dimensions; + is \@dimsA, [ 3, 4 ], 'The dimensions of A are correct.'; + is \@dimsB, [ 3, 3 ], 'The dimensions of B are correct.'; + is \@dimsC, [ 2, 2, 2 ], 'The dimensions of C are correct.'; + is \@dimsRow, [4], 'The dimensions of a row vector are correct.'; +}; + +subtest 'isSquare and isOne' => sub { + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); + ok !$A->isSquare, 'The matrix A is not square.'; + ok $B->isSquare, 'The matrix B is square.'; + + my $row_vect = Matrix([ 1, 2, 3, 4 ]); + ok $row_vect->isRow, 'The matrix [[1,2,3,4]] is a row vector.'; + ok !$A->isRow, 'The matrix A is not a row vector.'; + + ok !$A->isOne, 'The matrix A is not an identity matrix.'; + ok $B->isOne, 'The matrix B is an identity matrix.'; + + # tensors (degree 3) + my $D = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); + my $E = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); + my $F = Matrix([ [ [ 1, 2 ] ], [ [ 3, 4 ] ] ]); + ok $D->isOne, "The tensor D's last two dimensions is an identity"; + ok !$E->isOne, "The tensor E's last two dimensions is not an identity"; + ok $E->isSquare, 'The tensor E is square.'; + ok !$F->isSquare, 'The tensor F is not square.'; +}; + +subtest 'Triangular Matrices' => sub { + my $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 0, 6, 7, 8 ], [ 0, 0, 11, 12 ], [ 0, 0, 0, 16 ] ]); + my $A2 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [ 13, 14, 15, 16 ] ]); + ok $A1->isUpperTriangular, 'test for upper triangular matrix'; + ok !$A2->isUpperTriangular, 'not an upper triangular matrix'; + my $B1 = Matrix([ [ 1, 0, 0, 0 ], [ 5, 6, 0, 0 ], [ 9, 10, 11, 0 ], [ 13, 14, 15, 16 ] ]); + ok $B1->isLowerTriangular, 'test for lower triangular matrix'; + my $B2 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + ok !$B2->isLowerTriangular, 'not a lower triangular matrix.'; + +}; + +subtest 'Transpose' => sub { + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + my $B = Matrix([ [ 1, 5, 9 ], [ 2, 6, 10 ], [ 3, 7, 11 ], [ 4, 8, 12 ] ]); + is $A->transpose->TeX, $B->TeX, 'Test the tranpose of a matrix.'; + + my $row = Matrix([ 1, 2, 3, 4 ]); + my $row_trans = Matrix([ [1], [2], [3], [4] ]); + is $row->transpose->TeX, $row_trans->TeX, 'Transpose of a Matrix with one row.'; + + my $C = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + like dies { + $C->transpose; + }, qr/Can't transpose \d+-dimensional matrices/, "Can't tranpose a three-d matrix."; +}; + +subtest 'Extract an element' => sub { + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + my $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + my $row = Matrix([ 1, 2, 3, 4 ]); + + is $A->element(1, 1), 1, 'extract an element from a 2D matrix.'; + is $A->element(3, 2), 10, 'extract an element from a 2D matrix.'; + is $B->element(1, 2, 1), 3, 'extract an element from a 3D matrix.'; + is $row->element(2), 2, 'extract an element from a row matrix'; +}; + +subtest 'Extract a column' => sub { + my $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + my $col = Matrix([ [2], [6], [10] ]); + is $A1->column(2)->TeX, $col->TeX, 'Extract a column from a matrix.'; + + like dies { + $A1->column(-1); + }, qr/Column must be a positive integer/, 'Test that an error is thrown for passing a non-positive integer.'; +}; + +subtest 'Identity matrix' => sub { + my $I = Value::Matrix->I(3); + my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + + is $I->TeX, $B->TeX, 'Create a 3 x 3 identity matrix.'; + is $A->I->TeX, $B->TeX, 'Create a 3 x 3 identity matrix by using an existing matrix.'; +}; + +subtest 'Permutation matrices' => sub { + my $P1 = Value::Matrix->P(3, [ 1, 2, 3 ]); + is $P1->TeX, Matrix([ [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ])->TeX, 'Create permuation matrix on cycle (123)'; + + my $P2 = Value::Matrix->P(6, [ 1, 3 ], [ 2, 4, 6 ]); + is $P2->TeX, + Matrix([ + [ 0, 0, 1, 0, 0, 0 ], + [ 0, 0, 0, 0, 0, 1 ], + [ 1, 0, 0, 0, 0, 0 ], + [ 0, 1, 0, 0, 0, 0 ], + [ 0, 0, 0, 0, 1, 0 ], + [ 0, 0, 0, 1, 0, 0 ] + ])->TeX, 'Create a permutation matrix on cycle product (13)(246)'; + + my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [ 13, 14, 15, 16 ] ]); + my $P3 = $A->P([ 1, 4 ]); + is $P3->TeX, + Matrix([ [ 0, 0, 0, 1 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 1, 0, 0, 0 ] ])->TeX, + 'Create a permutation matrix based on an existing matrix.'; +}; + +subtest 'Zero matrix' => sub { + my $Z1 = Matrix([ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]); + my $Z2 = Matrix([ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]); + is Value::Matrix->Zero(3, 4)->TeX, $Z1->TeX, 'Create a 3 by 4 zero matrix.'; + is Value::Matrix->Zero(4)->TeX, $Z2->TeX, 'Create a 4 by 4 zero matrix.'; + + my $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); + is $A1->Zero->TeX, $Z1->TeX, 'Create a zero matrix with same size as the given one.'; + + like dies { + Value::Matrix->Zero(4, 0); + }, qr/Dimension must be a positive integer/, 'Test that an error is thrown for passing a non-positive integer.'; +}; + +subtest 'Add Matrices' => sub { + my $row1 = Matrix(1, 2, 3); + my $row2 = Matrix(4, 5, 6); + my $sum1 = Matrix(5, 7, 9); + ok $row1+ $row2 == $sum1, 'Checking the sum of two row matrices.'; + + my $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]); + my $B = Matrix([ [ 0, 1, 0 ], [ -1, 2, -3 ], [ -2, -1, 0 ] ]); + my $sum2 = Matrix([ [ 1, 3, 3 ], [ 3, 7, 3 ], [ 5, 7, 9 ] ]); + ok $A+ $B == $sum2, 'Checking the sum of two 3 by 3 matrices.'; + + #tensors + my $M1 = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); + my $M2 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + my $M3 = Matrix([ [ [ 2, 2 ], [ 3, 5 ] ], [ [ 6, 6 ], [ 7, 9 ] ] ]); + ok $M1 + $M2 == $M3, 'Checking the sum of two tensors'; + + my $row3 = Matrix([ 1, 2, 3, 4 ]); + like dies { $row1 + $row3 }, qr/Can't add Matrices with different dimensions/, + 'Test that adding row matrices of different dimsensions throws an error.'; + + my $C = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]); + like dies { $A + $C }, qr/Can't add Matrices with different dimensions/, + 'Test that adding matrices of different dimsensions throws an error.'; + + my $M4 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); + like dies { $M3 + $M4 }, qr/Can't add Matrices with different dimensions/, + 'Test that adding tensors of different dimsensions throws an error.'; +}; + +subtest 'Subtract Matrices' => sub { + my $row1 = Matrix( 1, 2, 3); + my $row2 = Matrix( 4, 5, 6); + my $diff1 = Matrix(-3, -3, -3); + ok $row1 - $row2 == $diff1, 'Checking the difference of two row matrices.'; + + my $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]); + my $B = Matrix([ [ 0, 1, 0 ], [ -1, 2, -3 ], [ -2, -1, 0 ] ]); + my $diff2 = Matrix([ [ 1, 1, 3 ], [ 5, 3, 9 ], [ 9, 9, 9 ] ]); + ok $A - $B == $diff2, 'Checking the difference of two 3 by 3 matrices.'; + + #tensors + my $M1 = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); + my $M2 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + my $M3 = Matrix([ [ [ 0, -2 ], [ -3, -3 ] ], [ [ -4, -6 ], [ -7, -7 ] ] ]); + ok $M1 - $M2 == $M3, 'Checking the difference of two tensors'; + + my $row3 = Matrix([ 1, 2, 3, 4 ]); + like dies { $row1 - $row3 }, qr/Can't subtract Matrices with different dimensions/, + 'Test that subtracting row matrices of different dimsensions throws an error.'; + + my $C = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]); + like dies { $A - $C }, qr/Can't subtract Matrices with different dimensions/, + 'Test that subtracting matrices of different dimsensions throws an error.'; + + my $M4 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); + like dies { $M3 - $M4 }, qr/Can't subtract Matrices with different dimensions/, + 'Test that subtracting tensors of different dimsensions throws an error.'; +}; + +subtest 'Multiply Matrices' => sub { + + my $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]); + my $B = Matrix([ [ 0, 1, 0 ], [ -1, 2, -3 ], [ -2, -1, 0 ] ]); + my $prod1 = Matrix([ [ -8, 2, -6 ], [ -17, 8, -15 ], [ -26, 14, -24 ] ]); + my $C = Matrix([ [ 1, -5, -2, -5 ], [ 0, -5, 5, -4 ], [ 4, 1, -1, 1 ] ]); + my $prod2 = Matrix([ [ 13, -12, 5, -10 ], [ 28, -39, 11, -34 ], [ 43, -66, 17, -58 ] ]); + ok $A*$B == $prod1, 'Checking the product of two 3 by 3 matrices.'; + ok $A*$C == $prod2, 'Checking the product of a 3 by 3 and 3 by 4 matrix'; + + like dies { $C * $A }, qr/Matrices of dimensions \d+x\d+ and \d+x\d+ can't be multiplied/, + 'Test that multiplying row matrices of incompatible dimsensions throws an error.'; + + # multiply degree 2 and 1 matrices. + + my $row = Matrix(1, 2, 3); + my $prod3 = Matrix(14, 32, 50); + ok $A*$row == $prod3, 'Multiply a 3 by 3 matrix and a row matrix of length 3 (the row is promoted to a matrix)'; + + my $col = Matrix([ [1], [2], [3] ]); + my $prod4 = Matrix([ [14], [32], [50] ]); + ok $A*$col == $prod4, 'Multiply a 3 by 3 matrix and a column matrix of length 3'; + + my $v = Vector(1, 2, 3); + my $prod5 = Vector(14, 32, 50); + ok $A*$v == $prod5, 'Multiply a 3 by 3 matrix and a vector of length 3'; + + #tensors + # my $M1 = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); + # my $M2 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); + # my $M3 = Matrix([ [ [ 2, 2 ], [ 3, 5 ] ], [ [ 6, 6 ], [ 7, 9 ] ] ]); + # ok $M1 + $M2 == $M3, 'Checking the sum of two tensors'; + + # my $row3 = Matrix([ 1, 2, 3, 4 ]); + # like dies { $row1 + $row3 }, qr/Can't add Matrices with different dimensions/, + # 'Test that adding row matrices of different dimsensions throws an error.'; + + # my $C = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]); + # like dies { $A + $C }, qr/Can't add Matrices with different dimensions/, + # 'Test that adding matrices of different dimsensions throws an error.'; + + # my $M4 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); + # like dies { $M3 + $M4 }, qr/Can't add Matrices with different dimensions/, + # 'Test that adding tensors of different dimsensions throws an error.'; +}; + +subtest 'Elementary Matrices' => sub { + my $E1 = Value::Matrix->E(3, [ 1, 3 ]); + is $E1->TeX, Matrix([ [ 0, 0, 1 ], [ 0, 1, 0 ], [ 1, 0, 0 ] ])->TeX, 'Elementary Matrix with a row swap'; + + my $E2 = Value::Matrix->E(4, [2], 3); + is $E2->TeX, Matrix([ [ 1, 0, 0, 0 ], [ 0, 3, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ] ])->TeX, + 'Elementary Matrix with row multiple.'; + + my $E3 = Value::Matrix->E(4, [ 3, 2 ], -3); + is $E3->TeX, Matrix([ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, -3, 1, 0 ], [ 0, 0, 0, 1 ] ])->TeX, + 'Elementary Matrix with row multiple and add.'; +}; + +done_testing; From f547673e4d690f598c6a994591f67d52ce2dcb6f Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 27 Mar 2025 18:11:18 -0700 Subject: [PATCH 3/5] cleanup from PR#1215 review --- lib/Value/Matrix.pm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index 79d62e1f03..cad38fb015 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -307,7 +307,7 @@ returns C<(2,2,2)> # # Recursively get the dimensions of the matrix. -# Returns (d_1,d_2,...,d_n) for an degree n Matrix. +# Returns (d_1,d_2,...,d_n) for a degree n Matrix. # sub dimensions { my $self = shift; @@ -359,8 +359,8 @@ Usage: sub isSquare { my $self = shift; my @d = $self->dimensions; - if (scalar(@d) == 1) { return $d[0] == 1 } - return ($d[-1] == $d[-2]); + return $d[0] == 1 if scalar(@d) == 1; + return $d[-1] == $d[-2]; } =head3 C @@ -798,7 +798,7 @@ sub transpose { =head3 C -Get a identity Matrix of the requested size +Get an identity Matrix of the requested size Value::Matrix->I(n) @@ -1209,7 +1209,7 @@ sub det { sub inverse { my $self = shift; my @d = $self->dimensions; - my @M = (); + my @M; for my $i (0 .. $d[0] - 1) { if (scalar(@d) == 2) { $self->wwMatrixLR; From b97d8dd849a2afe5f34a51c36d59789e6fafebae Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Fri, 28 Mar 2025 22:07:34 -0700 Subject: [PATCH 4/5] correct POD for ->value --- lib/Value/Matrix.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index cad38fb015..4c9f457f30 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -83,8 +83,9 @@ Examples: =head3 Conversion - $matrix->value produces an array of numbers (for a degree 1 Matrix) or array refs representing the rows. - These are perl numbers and array refs, not MathObjects. + $matrix->value produces an array of references to nested arrays, except at + the deepest level, where there will be the more basic MathObjects that make + up the Matrix (e.g. Real, Complex, Fraction, a mix of these, etc) $M1->value is (1, 2, 3) $M2->value is ([1, 2], [3, 4]) From d7f62602438ce55dac2930c6a8886cc2caa94232 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Fri, 28 Mar 2025 22:09:57 -0700 Subject: [PATCH 5/5] some more tests for matrices --- t/math_objects/matrix.t | 118 ++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/t/math_objects/matrix.t b/t/math_objects/matrix.t index 60ea420988..1f27f70cf5 100644 --- a/t/math_objects/matrix.t +++ b/t/math_objects/matrix.t @@ -15,18 +15,18 @@ loadMacros('MathObjects.pl'); Context('Matrix'); use Data::Dumper; -subtest 'Creating degree 1 matrices (row vector)' => sub { +subtest 'Creating a degree 1 Matrix (row vector)' => sub { ok my $M1 = Matrix(1, 2, 3), 'Create a row vector'; is $M1->class, 'Matrix', 'M1 is a Matrix'; my $M2 = Compute('[1,2,3]'); is $M2->class, 'Matrix', 'Creation using Compute results in a Matrix.'; - is [ $M1->value ], [ 1, 2, 3 ], 'M1 is the row matrix [1,23]'; - is [ $M2->value ], [ 1, 2, 3 ], 'M2 is the row matrix [1,23]'; + is [ $M1->value ], [ 1, 2, 3 ], 'M1 is the row matrix [1,2,3]'; + is [ $M2->value ], [ 1, 2, 3 ], 'M2 is the row matrix [1,2,3]'; is $M1->degree, 1, 'M1 is a degree 1 matrix.'; is $M2->degree, 1, 'M2 is a degree 1 matrix.'; }; -subtest 'Creating Matrices' => sub { +subtest 'Creating a degree 2 Matrix' => sub { my $values = [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]; my $A = Matrix($values); is $A->class, 'Matrix', 'Input as array ref is a Matrix.'; @@ -39,7 +39,7 @@ subtest 'Creating Matrices' => sub { is $C->degree, 2, 'C is a degree 2 matrix.'; }; -subtest 'Creating Tensors (degree 3)' => sub { +subtest 'Creating a degree 3 Matrix (tensor)' => sub { my $values = [ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]; ok my $M3 = Matrix([ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ]), 'Creation of a tensor'; is $M3->class, 'Matrix', 'Checking the result is a Matrix'; @@ -48,7 +48,7 @@ subtest 'Creating Tensors (degree 3)' => sub { is $M3->degree, 3, 'M3 is a degree 3 matrix.'; }; -subtest 'Matrix Dimensions' => sub { +subtest 'Get dimensions' => sub { my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); my $C = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); @@ -63,30 +63,48 @@ subtest 'Matrix Dimensions' => sub { is \@dimsRow, [4], 'The dimensions of a row vector are correct.'; }; -subtest 'isSquare and isOne' => sub { - my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); - my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); - ok !$A->isSquare, 'The matrix A is not square.'; - ok $B->isSquare, 'The matrix B is square.'; - - my $row_vect = Matrix([ 1, 2, 3, 4 ]); - ok $row_vect->isRow, 'The matrix [[1,2,3,4]] is a row vector.'; - ok !$A->isRow, 'The matrix A is not a row vector.'; - - ok !$A->isOne, 'The matrix A is not an identity matrix.'; - ok $B->isOne, 'The matrix B is an identity matrix.'; - - # tensors (degree 3) - my $D = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); - my $E = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); - my $F = Matrix([ [ [ 1, 2 ] ], [ [ 3, 4 ] ] ]); - ok $D->isOne, "The tensor D's last two dimensions is an identity"; - ok !$E->isOne, "The tensor E's last two dimensions is not an identity"; - ok $E->isSquare, 'The tensor E is square.'; - ok !$F->isSquare, 'The tensor F is not square.'; +subtest 'Use isSquare, isOne, and isRow methods' => sub { + my $A1 = Matrix([ 1, 2, 3, 4 ]); + my $B1 = Matrix([1]); + my $C1 = Matrix([2]); + ok !$A1->isSquare, 'The matrix A1 is not square.'; + ok $B1->isSquare, 'The matrix B1 is square.'; + ok $C1->isSquare, 'The matrix C1 is square.'; + ok !$A1->isOne, 'The matrix A1 is not an identity.'; + ok $B1->isOne, 'The matrix B1 is an identity.'; + ok !$C1->isOne, 'The matrix C1 is not an identity.'; + ok $A1->isRow, 'The matrix A1 is a row.'; + ok $B1->isRow, 'The matrix B1 is a row.'; + ok $C1->isRow, 'The matrix C1 is a row.'; + + my $A2 = Matrix([ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ]); + my $B2 = Matrix([ 1, 0 ], [ 0, 1 ]); + my $C2 = Matrix([ 2, 0 ], [ 1, 2 ]); + ok !$A2->isSquare, 'The matrix A2 is not square.'; + ok $B2->isSquare, 'The matrix B2 is square.'; + ok $C2->isSquare, 'The matrix C2 is square.'; + ok !$A2->isOne, 'The matrix A2 is not an identity.'; + ok $B2->isOne, 'The matrix B2 is an identity.'; + ok !$C2->isOne, 'The matrix C2 is not an identity.'; + ok !$A2->isRow, 'The matrix A2 is not a row.'; + ok !$B2->isRow, 'The matrix B2 is not a row.'; + ok !$C2->isRow, 'The matrix C2 is not a row.'; + + my $A3 = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ], [ [ 7, 8, 9 ], [ 10, 11, 12 ] ]); + my $B3 = Matrix([ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ]); + my $C3 = Matrix([ [ 2, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ]); + ok !$A3->isSquare, 'The matrix A3 is not square.'; + ok $B3->isSquare, 'The matrix B3 is square.'; + ok $C3->isSquare, 'The matrix C3 is square.'; + ok !$A3->isOne, 'The matrix A3 is not an identity.'; + ok $B3->isOne, 'The matrix B3 is an identity.'; + ok !$C3->isOne, 'The matrix C3 is not an identity.'; + ok !$A3->isRow, 'The matrix A3 is not a row.'; + ok !$B3->isRow, 'The matrix B3 is not a row.'; + ok !$C3->isRow, 'The matrix C3 is not a row.'; }; -subtest 'Triangular Matrices' => sub { +subtest 'Use tests for triangular matrices' => sub { my $A1 = Matrix([ [ 1, 2, 3, 4 ], [ 0, 6, 7, 8 ], [ 0, 0, 11, 12 ], [ 0, 0, 0, 16 ] ]); my $A2 = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [ 13, 14, 15, 16 ] ]); ok $A1->isUpperTriangular, 'test for upper triangular matrix'; @@ -98,14 +116,14 @@ subtest 'Triangular Matrices' => sub { }; -subtest 'Transpose' => sub { +subtest 'Transpose a Matrix' => sub { my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); my $B = Matrix([ [ 1, 5, 9 ], [ 2, 6, 10 ], [ 3, 7, 11 ], [ 4, 8, 12 ] ]); is $A->transpose->TeX, $B->TeX, 'Test the tranpose of a matrix.'; my $row = Matrix([ 1, 2, 3, 4 ]); my $row_trans = Matrix([ [1], [2], [3], [4] ]); - is $row->transpose->TeX, $row_trans->TeX, 'Transpose of a Matrix with one row.'; + is $row->transpose->TeX, $row_trans->TeX, 'Transpose of a degree 1 Matrix.'; my $C = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); like dies { @@ -118,10 +136,10 @@ subtest 'Extract an element' => sub { my $B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); my $row = Matrix([ 1, 2, 3, 4 ]); - is $A->element(1, 1), 1, 'extract an element from a 2D matrix.'; - is $A->element(3, 2), 10, 'extract an element from a 2D matrix.'; - is $B->element(1, 2, 1), 3, 'extract an element from a 3D matrix.'; - is $row->element(2), 2, 'extract an element from a row matrix'; + is $A->element(1, 1), 1, 'extract an element from a degree 2 matrix.'; + is $A->element(3, 2), 10, 'extract an element from a degree 2 matrix.'; + is $B->element(1, 2, 1), 3, 'extract an element from a degree 3 matrix.'; + is $row->element(2), 2, 'extract an element from a degree 1 matrix.'; }; subtest 'Extract a column' => sub { @@ -134,7 +152,7 @@ subtest 'Extract a column' => sub { }, qr/Column must be a positive integer/, 'Test that an error is thrown for passing a non-positive integer.'; }; -subtest 'Identity matrix' => sub { +subtest 'Construct an identity matrix' => sub { my $I = Value::Matrix->I(3); my $B = Matrix([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]); my $A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]); @@ -143,7 +161,7 @@ subtest 'Identity matrix' => sub { is $A->I->TeX, $B->TeX, 'Create a 3 x 3 identity matrix by using an existing matrix.'; }; -subtest 'Permutation matrices' => sub { +subtest 'Construct a permutation matrix' => sub { my $P1 = Value::Matrix->P(3, [ 1, 2, 3 ]); is $P1->TeX, Matrix([ [ 0, 0, 1 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ])->TeX, 'Create permuation matrix on cycle (123)'; @@ -165,7 +183,7 @@ subtest 'Permutation matrices' => sub { 'Create a permutation matrix based on an existing matrix.'; }; -subtest 'Zero matrix' => sub { +subtest 'Construct a zero matrix' => sub { my $Z1 = Matrix([ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]); my $Z2 = Matrix([ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]); is Value::Matrix->Zero(3, 4)->TeX, $Z1->TeX, 'Create a 3 by 4 zero matrix.'; @@ -179,7 +197,7 @@ subtest 'Zero matrix' => sub { }, qr/Dimension must be a positive integer/, 'Test that an error is thrown for passing a non-positive integer.'; }; -subtest 'Add Matrices' => sub { +subtest 'Add matrices' => sub { my $row1 = Matrix(1, 2, 3); my $row2 = Matrix(4, 5, 6); my $sum1 = Matrix(5, 7, 9); @@ -209,7 +227,7 @@ subtest 'Add Matrices' => sub { 'Test that adding tensors of different dimsensions throws an error.'; }; -subtest 'Subtract Matrices' => sub { +subtest 'Subtract matrices' => sub { my $row1 = Matrix( 1, 2, 3); my $row2 = Matrix( 4, 5, 6); my $diff1 = Matrix(-3, -3, -3); @@ -239,7 +257,7 @@ subtest 'Subtract Matrices' => sub { 'Test that subtracting tensors of different dimsensions throws an error.'; }; -subtest 'Multiply Matrices' => sub { +subtest 'Multiply matrices' => sub { my $A = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]); my $B = Matrix([ [ 0, 1, 0 ], [ -1, 2, -3 ], [ -2, -1, 0 ] ]); @@ -265,27 +283,9 @@ subtest 'Multiply Matrices' => sub { my $v = Vector(1, 2, 3); my $prod5 = Vector(14, 32, 50); ok $A*$v == $prod5, 'Multiply a 3 by 3 matrix and a vector of length 3'; - - #tensors - # my $M1 = Matrix([ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 1, 0 ], [ 0, 1 ] ] ]); - # my $M2 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]); - # my $M3 = Matrix([ [ [ 2, 2 ], [ 3, 5 ] ], [ [ 6, 6 ], [ 7, 9 ] ] ]); - # ok $M1 + $M2 == $M3, 'Checking the sum of two tensors'; - - # my $row3 = Matrix([ 1, 2, 3, 4 ]); - # like dies { $row1 + $row3 }, qr/Can't add Matrices with different dimensions/, - # 'Test that adding row matrices of different dimsensions throws an error.'; - - # my $C = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]); - # like dies { $A + $C }, qr/Can't add Matrices with different dimensions/, - # 'Test that adding matrices of different dimsensions throws an error.'; - - # my $M4 = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ] ]); - # like dies { $M3 + $M4 }, qr/Can't add Matrices with different dimensions/, - # 'Test that adding tensors of different dimsensions throws an error.'; }; -subtest 'Elementary Matrices' => sub { +subtest 'Construct an elementary matrix' => sub { my $E1 = Value::Matrix->E(3, [ 1, 3 ]); is $E1->TeX, Matrix([ [ 0, 0, 1 ], [ 0, 1, 0 ], [ 1, 0, 0 ] ])->TeX, 'Elementary Matrix with a row swap';