@@ -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
844847Usage:
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+
851936sub 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