Skip to content

Commit f450f33

Browse files
committed
transpose degree n matrices
1 parent 2c51a87 commit f450f33

File tree

1 file changed

+100
-10
lines changed

1 file changed

+100
-10
lines changed

lib/Value/Matrix.pm

+100-10
Original file line numberDiff line numberDiff line change
@@ -848,29 +848,119 @@ sub twiddle {
848848
return $self->make(@coords);
849849
}
850850

851+
=head3 C<slice>
852+
853+
Produce the degree (n-1) Matrix defined by a given index and value for that index. If n is 1,
854+
this produces a Real/Complex/Fraction.
855+
856+
Usage:
857+
858+
$A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
859+
$A->slice(1, 2) # will be same as Matrix([5, 6, 7, 8])
860+
$A->slice(2, 3) # will be same as Matrix([3, 7, 11])
861+
862+
$B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
863+
$B->slice(1, 2) # will be same as Matrix([ [ 5, 6 ], [ 7, 8 ] ])
864+
$B->slice(2, 1) # will be same as Matrix([ [ 1, 2 ], [ 5, 6 ] ])
865+
$B->slice(3, 1) # will be same as Matrix([ [ 1, 3 ], [ 5, 7 ] ])
866+
867+
=cut
868+
869+
sub slice {
870+
my $self = shift;
871+
my ($index, $value) = @_;
872+
my @d = $self->dimensions;
873+
my $d = scalar(@d);
874+
my $w = $d[0];
875+
Value::Error("index must be an integer from 1 to $d") unless ($index == int($index) && $index >= 1 && $index <= $d);
876+
my $M = $self->data;
877+
if ($index == 1) {
878+
Value::Error("value must be an integer from 1 to $w")
879+
unless ($value == int($value) && $value >= 1 && $value <= $w);
880+
return $M->[ $value - 1 ];
881+
return $self->make($M->[ $value - 1 ]);
882+
} else {
883+
my @rows;
884+
for (1 .. $w) {
885+
push @rows, $M->[ $_ - 1 ]->slice($index - 1, $value);
886+
}
887+
return $self->make(@rows);
888+
}
889+
}
890+
851891
=head3 C<transpose>
852892
853-
Take the transpose of a matrix.
893+
Take the transpose of a matrix. For a degree 1 Matrix, first promote to a degree 2 Matrix.
894+
For a degree n Matrix, apply a permutation of the indices. The default permutation transposes the
895+
last two indices. To specify a permutation, provide an array reference representing a cycle
896+
or an array of array references that represents a product of cycles. If a permutation is not
897+
specified, the default is the usual transposition of the last two indices.
854898
855899
Usage:
856900
857901
$A = Matrix([ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]);
858-
$A->transpose;
902+
$A->transpose # will be [ [ 1, 5, 9 ], [ 2, 6, 10 ], [ 3, 7, 11 ], [ 4, 8, 12 ] ]
859903
904+
$B = Matrix([ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ]);
905+
$B->transpose([1, 2, 3]) # will be [ [ [ 1, 3 ], [ 5, 7 ] ], [ [2 , 4 ], [ 6, 8 ] ] ]
906+
907+
$C = Matrix([ [ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 5, 6 ], [ 7, 8 ] ] ], [ [ [ 9, A ], [ B, C ] ], [ [ D, E ], [ F, 0 ] ] ] ]);
908+
$C->transpose([ [ 1, 2], [3, 4] ]) # will be [ [ [ [ 1, 3 ], [ 2, 4 ] ], [ [ 9, B ], [ A, C ] ] ], [ [ [ 5, 7 ], [ 6, 8 ] ], [ [ D, F ], [ E, 0 ] ] ]
860909
=cut
861910

862911
sub transpose {
863-
my $self = promote(@_);
912+
my $self = shift;
913+
my $p = shift;
864914
my @d = $self->dimensions;
865-
if (scalar(@d) == 1) { @d = (1, @d); $self = $self->make($self) }
866-
Value::Error("Can't transpose %d-dimensional matrices", scalar(@d)) unless scalar(@d) == 2;
915+
my $N = scalar(@d);
916+
917+
# elevate a degree 1 Matrix to degree 2
918+
if ($N == 1) { @d = (1, @d); $N = 2; $self = $self->make($self) }
919+
920+
# default to transpose last two indices
921+
$p = [ [ $N - 1, $N ] ] unless $p;
922+
923+
# build the permutation hash from cycles
924+
my %p;
925+
if (ref $p eq 'HASH') {
926+
%p = %{$p};
927+
} else {
928+
$p = [$p] unless ref($p->[0]);
929+
my @p = (1 .. $N);
930+
for my $cycle (@{$p}) {
931+
next unless defined $cycle->[0];
932+
my $tmp = $p[ $cycle->[0] - 1 ];
933+
for my $i (0 .. $#{$cycle} - 1) {
934+
$p[ $cycle->[$i] - 1 ] = $p[ $cycle->[ $i + 1 ] - 1 ];
935+
}
936+
$p[ $cycle->[ $#{$cycle} ] - 1 ] = $tmp;
937+
}
938+
%p = map { $_ => $p[ $_ - 1 ] } (1 .. $N);
939+
}
940+
%p = reverse %p;
867941

868942
my @M = ();
869-
my $M = $self->data;
870-
for my $j (0 .. $d[1] - 1) {
871-
my @row = ();
872-
for my $i (0 .. $d[0] - 1) { push(@row, $M->[$i]->data->[$j]) }
873-
push(@M, $self->make(@row));
943+
if ($N == 2) {
944+
return $self if ($p{1} == 1);
945+
my $M = $self->data;
946+
for my $j (0 .. $d[1] - 1) {
947+
my @row = ();
948+
for my $i (0 .. $d[0] - 1) { push(@row, $M->[$i]->data->[$j]) }
949+
push(@M, $self->make(@row));
950+
}
951+
} else {
952+
# reduce the permutation hash
953+
my @q = map { $p{$_} } (1 .. $N);
954+
my $p1 = shift @q;
955+
for (@q) {
956+
$_-- if ($_ >= $p1);
957+
}
958+
my %q = map { $_ => $q[ $_ - 1 ] } (1 .. $N - 1);
959+
960+
for my $j (1 .. $d[ $p1 - 1 ]) {
961+
my $slice = $self->slice($p1, $j);
962+
push(@M, $slice->class eq 'Matrix' ? $slice->transpose(\%q) : $slice);
963+
}
874964
}
875965
return $self->make(@M);
876966
}

0 commit comments

Comments
 (0)