This is a quick programming exercise to correct the following problem in Maple:
for n from 8 to 12 do A := Matrix(2^n, 2^n, storage='sparse'): # zero matrix print( 2^n, time(LinearAlgebra:-Transpose(A)) ); end do:
The problem is that the LinearAlgebra:-Transpose command is not sparse. That is, the time it takes is proportional to the overall size of the matrix and not to the number of non-zero entries - even when your matrix uses sparse storage. In this post we will look at what is required to program a new Transpose command which can handle much larger matrices.
The first thing we need is to get information out of the matrix. Run the following commands:
A := Matrix([[1,0,3,0],[2,0,0,1],[0,0,0,1]],storage=sparse); op(A);
and you should see a sequence: 3, 4, {(1, 1) = 1, (2, 1) = 2, (1, 3) = 3, (2, 4) = 1, (3, 4) = 1}, etc. This is misleading: op(1,A) gives the sequence of dimensions (3,4) and op(2,A) gives you the set of elements {(1,1)=1, ...}. You can read all about it on the help page ?Matrix.
We are not going to worry too much about the shape and indexing functions; our function will work only for sparse rectangular matrices. To transpose the matrix we will need to go through the list of elements (a,b)=c and return (b,a)=c. We start by writing a small procedure:
reverselhs := proc(eqn) local a,b,c; a,b := lhs(eqn); c := rhs(eqn); (b,a) = c; end proc:
The problem with this is that it is slow. We could eliminate a line and the local variable c by using rhs(eqn) in the last line, but we can get an even better improvement if we can use an inline procedure. Given an equation eqn := (a,b)=c, op(eqn) returns the sequence a,b,c. Thus we can write an inline procedure which takes a,b, and c as input and outputs (b,a)=c, and compose it with the op command when mapping it onto the set. This is what I mean:
reverse := proc(a,b,c) option inline; (b,a)=c end proc: map(reverse@op, op(2,A)); op(2,A); # compare
The only thing left is to quickly build the sparse matrix. We can use the table initialization option of the Matrix constructor to make this quick:
S := map(reverse@op, op(2,A)); Matrix(4,3,table(S));
Here is the completed command. Don't forget the 'storage'='sparse' option.
reverse := proc(a,b,c) option inline; (b,a)=c end proc: transpose := proc(A::Matrix) local n, m, S; n, m := op(1,A); S := map(reverse@op, op(2,A)); Matrix(m, n ,table(S), 'storage'='sparse'); end proc:
And now a little something for the experts out there:
reverse := proc(a,b,c) option inline; (b,a)=c end proc: transpose := proc(A) option inline: Matrix(op(1,A)[2], op(1,A)[1], map(reverse@op, op(2,A)), 'storage'='sparse') end proc:
You can make it work for Vectors too (keeping it inline). Have fun :)
Comments
nice write up
Nice write up. A few minor points. The @ command is not builtin, which slows this down a bit. I think you'll find the following slightly faster:
transpose2 := proc(A) Matrix(op([1,2],A), op([1,1],A) , map(eq -> (op([1,2],eq),op([1,1],eq)) = op(2,eq) , op(2,A)) , 'storage'='sparse') end proc:If you want to inline that (not sure why) you can rewrite reverse so the @ is not required.
reverse2 := proc(eq) option inline; (op([1,2],eq),op([1,1],eq)) = op(2,eq) end proc: transpose3 := proc(A) option inline; Matrix(op([1,2],A), op([1,1],A) , map(reverse2, op(2,A)) , 'storage'='sparse') end proc:It may be marginally faster than transpose2.
There is a subtle bug in the original assignment of transpose. It doesn't work if the Matrix contains sequences (unlikely, but possible). Consider
M := Matrix(1,2, {(1,1)=(a,b), (1,2)=(x,y)}); transpose(M); [a] [ ] [x] transpose2(M); [a b] [ ] [x y]nice
nice to hear that
Tomas Hellix
mp3 blog
Tomashellix@yahoo.com