I am looking for expertise that helps to improve speed of the code below. First, a little bit of background:
There is some system of differential equations $\dot{\vec{m}}(t)=S(t)\vec{m}(t)$. In order to solve this without using NDSolve/NDSolveValue
for this one can just proceed as $$\vec{m}(T)=\left(\prod\limits_{j=N}^1 e^{S(j\,dt)}\right) \cdot\vec{m}(0)$$
where $dt$ is length of one timestep and $N=T/dt$ the number thereof. Obviously, this method involves the matrix exponential of $S$ which can be time consuming, especially since $N$ should be on the order of $10^3$ for my purposes to achieve reasonable accuracy.
Let me define the system in Mathematica code (the important variable is mat
which corresponds to $S(t)$ - the tiny rest is needed to construct a sample matrix that is almost of same dimension and "sparsity" as the ones I am actually dealing with)
ClearAll[init, listDiag, listOffDiag, valOffDiag, tmp, mat, col, squ];
col[mat_?MatrixQ] := Flatten[Transpose[mat]]; (* stack columns of a matrix *)
squ[list_?VectorQ] := Transpose[ArrayReshape[list, {Sqrt[Length@list], Sqrt[Length@list]}]]; (* transform stacked column form into square matrix again *)
(* preliminary definitions of initial condition and `tmp` that is needed to construct `mat` *)
init = ConstantArray[0, {36, 36}];
init[[8, 8]] = 1;
listOffDiag = {{13, 1}, {13, 7}, {14, 2}, {14, 8}, {15, 3}, {15, 9}, {16, 4}, {16, 10}, {17, 5}, {17, 11}, {18, 6}, {18, 12}, {19, 1}, {19, 7}, {20, 2}, {20, 8}, {21, 3}, {21, 9}, {22, 4}, {22,10}, {23, 5}, {23, 11}, {24, 6}, {24, 12}, {25, 1}, {25, 7}, {26,2}, {26, 8}, {27, 3}, {27, 9}, {28, 4}, {28, 10}, {29, 5}, {29, 11}, {30, 6}, {30, 12}};
listDiag = {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {6, 6}, {7, 7}, {9, 9}, {10, 10}, {11, 11}, {13, 13}, {14, 14}, {15, 15}, {16,16}, {17, 17}, {18, 18}, {19, 19}, {20, 20}, {21, 21}, {22, 22}, {23, 23}, {24, 24}, {25, 25}, {26, 26}, {27, 27}, {28, 28}, {29, 29}, {30, 30}, {31, 31}, {33, 33}, {34, 34}, {35, 35}};
valOffDiag = t*RandomReal[{-5, 5}, Length@listOffDiag];
tmp = SparseArray[Join[Thread[Rule[listDiag, RandomReal[{-100, 100},Length@listDiag]]],Thread[Rule[Table[{30 + i, 30 + i}, {i, 1, 6}],ConstantArray[0, 6]]]]] + SparseArray[Join[Thread[Rule[listOffDiag,valOffDiag]], {{36, 36} -> 0}]] + Transpose@SparseArray[Join[Thread[Rule[listOffDiag, valOffDiag]],{{36, 36}->0}]];
mat = KroneckerProduct[tmp, IdentityMatrix[36]] + KroneckerProduct[IdentityMatrix[36], Transpose@tmp];
Now here are my two approaches. Observing AbsoluteTiming
of
MatrixExp[-I*SparseArray[ArrayRules[mat] /. t -> 3,Dimensions[mat]]].col[init]; // AbsoluteTiming
MatrixExp[-I*SparseArray[ArrayRules[mat] /. t -> 3, Dimensions[mat]],col[init]]; // AbsoluteTiming
yields an order of magnitude improvement of the latter over the former (0.254 vs 0.027) on my machine. So instead of first computing the matrix product surrounded by the parentheses in the system of ODEs above, it should be faster to use MatrixExp[matrix,vector]
sequentially.
evol1[mat_, initial_, ti_, tf_] := Module[
{dt = (tf - ti)/10, res, d = Dimensions[mat][[1]]},
res = Prepend[Table[MatrixExp[-I*SparseArray[ArrayRules[mat]/.t->i, Dimensions[mat]]], {i,ti, tf, dt}], col[initial]];
Return[squ[Apply[Dot, Reverse[res]]]]];
evol2[mat_, initial_, ti_, tf_] := Module[
{dt = (tf - ti)/10, res = col[initial]},
Do[res = MatrixExp[-I*SparseArray[ArrayRules[mat]/.t->i,Dimensions[mat]],res], {i, ti, tf, dt}];
Return[squ[res]]];
evol1
is the straightforward method that computes the matrix product of all matrix exponentials and then applies it to the initial vector. evol2
makes use of MatrixExp[matrix,vector]
. Comparing speed and results:
res1 = evol1[mat, init, 0.01, 10]; // AbsoluteTiming
(* {2.634993, Null} *)
res2 = evol2[mat, init, 0.01, 10]; // AbsoluteTiming
(* {0.302688, Null} *)
Chop[res1 - res2] == ConstantArray[0, {36, 36}]
(* True *)
I feel like the Do
in evol2
is all but efficient but I do not get any idea about how to replace it. Is there any possibility to increase speed - not caring about memory usage?
Edit I am sorry for the initial confusion arising from a copy-paste error that affected the results.
t->3
does inevol1
andevol2
? $\endgroup$1/0.
and(0.+0.I)ComplexInfinity
errors when calculatingres2
... $\endgroup$res2
is nonsense. Please check you code again. $\endgroup$