Skip to content

Commit 0c7b952

Browse files
authored
Merge pull request #1217 from Alex-Jordan/matrices-3
transpose degree n matrices
2 parents dea814e + b62cf82 commit 0c7b952

File tree

2 files changed

+277
-109
lines changed

2 files changed

+277
-109
lines changed

lib/Value/Matrix.pm

Lines changed: 137 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -837,29 +837,155 @@ sub twiddle {
837837
return $self->make(@coords);
838838
}
839839

840-
=head3 C<transpose>
840+
=head3 C<slice>
841841
842-
Take the transpose of a matrix.
842+
Apply this to a degree n Matrix, passing (m, k), and produce the degree (n-1) Matrix defined by
843+
taking all entries whose position has mth index with value k. For example if C<$M> is a 4x5x6
844+
Matrix, then m can be 1, 2, or 3. If m is 2, then k can be 1, 2, 3, 4, or 5. C<$M-<gt>slice(2,3)>
845+
is the 4x6 Matrix such that the entry at position (i,j) is the entry of C<$M> at position (i,3,j).
843846
844847
Usage:
845848
846849
$A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
847-
$A->transpose;
850+
$A->slice(1, 2)
851+
# Index 1 identifies the 1st, 2nd, or 3rd row above, and with value 2 we get the second one:
852+
# Matrix([5, 6, 7, 8])
853+
854+
$B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
855+
$B->slice(1, 2)
856+
# Index 1 identifies the two arrays at depth 1, and with value 2 we get the second one:
857+
# Matrix([ [ 5, 6 ], [ 7, 8 ] ])
858+
$B->slice(2, 1)
859+
# Here we take all entries from $B where the 2nd index is 1: the 1 at position (1,1,1),
860+
# the 2 at position (1,1,2), the 5 at position (2,1,1), and the 6 at position (2,1,2):
861+
# Matrix([ [ 1, 2 ], [ 5, 6 ] ])
862+
$B->slice(3, 1)
863+
# Here we take all entries from $B where the 3rd index is 1: the 1 at position (1,1,1),
864+
# the 3 at position (1,2,1), the 5 at position (2,1,1), and the 7 at position (2,2,1):
865+
# Matrix([ [ 1, 3 ], [ 5, 7 ] ])
848866
849867
=cut
850868

869+
sub slice {
870+
my ($self, $index, $value) = @_;
871+
my @d = $self->dimensions;
872+
my $d = scalar(@d);
873+
my $w = $d[0];
874+
Value::Error("index must be an integer from 1 to $d") unless ($index == int($index) && $index >= 1 && $index <= $d);
875+
my $M = $self->data;
876+
if ($index == 1) {
877+
Value::Error("value must be an integer from 1 to $w")
878+
unless ($value == int($value) && $value >= 1 && $value <= $w);
879+
return $M->[ $value - 1 ];
880+
} else {
881+
my @rows;
882+
for (1 .. $w) {
883+
push @rows, $M->[ $_ - 1 ]->slice($index - 1, $value);
884+
}
885+
return $self->make(@rows);
886+
}
887+
}
888+
889+
=head3 C<transpose>
890+
891+
Take the transpose of a matrix. For a degree 1 Matrix, first promote to a degree 2 Matrix.
892+
For a degree n Matrix, apply a permutation of the indices. The default permutation transposes the
893+
last two indices. To specify a permutation, provide an array reference representing a cycle
894+
or an array of array references that represents a product of cycles. If a permutation is not
895+
specified, the default is the usual transposition of the last two indices.
896+
897+
Usage:
898+
899+
$A = Matrix([
900+
[ 1, 2, 3, 4 ],
901+
[ 5, 6, 7, 8 ],
902+
[ 9, 10, 11, 12 ]
903+
]);
904+
$A->transpose
905+
# will be
906+
# [
907+
# [ 1, 5, 9 ],
908+
# [ 2, 6, 10 ],
909+
# [ 3, 7, 11 ],
910+
# [ 4, 8, 12 ]
911+
# ]
912+
913+
$B = Matrix([
914+
[ [ 1, 2 ], [ 3, 4 ] ],
915+
[ [ 5, 6 ], [ 7, 8 ] ]
916+
]);
917+
$B->transpose([1, 2, 3])
918+
# will be
919+
# [
920+
# [ [ 1, 3 ], [ 5, 7 ] ],
921+
# [ [ 2, 4 ], [ 6, 8 ] ]
922+
# ]
923+
924+
$C = Matrix([
925+
[ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ],
926+
[ [ [ 9, 10 ], [ 11, 12 ] ], [ [ 13, 14 ], [ 15, 16 ] ] ]
927+
]);
928+
$C->transpose([ [ 1, 2], [3, 4] ])
929+
# will be
930+
# [
931+
# [ [ [ 1, 3 ], [ 2, 4 ] ], [ [ 9, 11 ], [ 10, 12 ] ] ],
932+
# [ [ [ 5, 7 ], [ 6, 8 ] ], [ [ 13, 15 ], [ 14, 16 ] ] ]
933+
# ]
934+
=cut
935+
851936
sub transpose {
852-
my $self = promote(@_);
937+
my $self = shift;
938+
my $p = shift;
853939
my @d = $self->dimensions;
854-
if (scalar(@d) == 1) { @d = (1, @d); $self = $self->make($self) }
855-
Value::Error("Can't transpose %d-dimensional matrices", scalar(@d)) unless scalar(@d) == 2;
940+
my $N = scalar(@d);
941+
942+
# elevate a degree 1 Matrix to degree 2
943+
if ($N == 1) { @d = (1, @d); $N = 2; $self = $self->make($self) }
944+
945+
# default to transpose last two indices
946+
$p = [ [ $N - 1, $N ] ] unless $p;
947+
948+
# build the permutation hash from cycles
949+
my %p;
950+
if (ref $p eq 'HASH') {
951+
%p = %{$p};
952+
} else {
953+
$p = [$p] unless ref($p->[0]);
954+
my @p = (1 .. $N);
955+
for my $cycle (@{$p}) {
956+
next unless defined $cycle->[0];
957+
my $tmp = $p[ $cycle->[0] - 1 ];
958+
for my $i (0 .. $#{$cycle} - 1) {
959+
$p[ $cycle->[$i] - 1 ] = $p[ $cycle->[ $i + 1 ] - 1 ];
960+
}
961+
$p[ $cycle->[-1] - 1 ] = $tmp;
962+
}
963+
%p = map { $_ => $p[ $_ - 1 ] } (1 .. $N);
964+
}
965+
%p = reverse %p;
856966

857967
my @M = ();
858-
my $M = $self->data;
859-
for my $j (0 .. $d[1] - 1) {
860-
my @row = ();
861-
for my $i (0 .. $d[0] - 1) { push(@row, $M->[$i]->data->[$j]) }
862-
push(@M, $self->make(@row));
968+
if ($N == 2) {
969+
return $self if ($p{1} == 1);
970+
my $M = $self->data;
971+
for my $j (0 .. $d[1] - 1) {
972+
my @row = ();
973+
for my $i (0 .. $d[0] - 1) { push(@row, $M->[$i]->data->[$j]) }
974+
push(@M, $self->make(@row));
975+
}
976+
} else {
977+
# reduce the permutation hash
978+
my @q = map { $p{$_} } (1 .. $N);
979+
my $p1 = shift @q;
980+
for (@q) {
981+
$_-- if ($_ >= $p1);
982+
}
983+
my %q = map { $_ => $q[ $_ - 1 ] } (1 .. $N - 1);
984+
985+
for my $j (1 .. $d[ $p1 - 1 ]) {
986+
my $slice = $self->slice($p1, $j);
987+
push(@M, $slice->class eq 'Matrix' ? $slice->transpose(\%q) : $slice);
988+
}
863989
}
864990
return $self->make(@M);
865991
}

0 commit comments

Comments
 (0)