# MatrixPower performs slower than Do loop

I have two almost identical pieces of code: one with MatrixPower responsible for matrix multiplication and another with Do loop. Why the latter performs much faster? It looks counterintuitive...

refl1[λ_, d1_, d2_, n1_, n2_, Np_] :=
Module[{MN, M2}, MN = {{0.5, 0.5}, {0.5, -0.5}} + 0.0 I;
M2 = {{Cos[
2 Pi*n1*d1/λ], -((I Sin[2 Pi*n1*d1/λ])/
n1)}, {-I n1*Sin[2 Pi*n1*d1/λ],
Cos[2 Pi*n1*d1/λ]}}.{{Cos[
2 Pi*n2*d2/λ], -((I Sin[2 Pi*n2*d2/λ])/
n2)}, {-I n2*Sin[2 Pi*n2*d2/λ],
Cos[2 Pi*n2*d2/λ]}};
MN = MN.MatrixPower[M2, Np];
MN = MN.{{1.0, 1.0}, {1.0, -1.0}};
Abs[MN[][]/MN[][]]^2]
Timing[Do[refl1[100, 2, 3, 5, 6, 100], {100}]]

refl2[λ_, d1_, d2_, n1_, n2_, Np_] :=
Module[{MN, M2}, MN = {{0.5, 0.5}, {0.5, -0.5}} + 0.0 I;
M2 = {{Cos[
2 Pi*n1*d1/λ], -((I Sin[2 Pi*n1*d1/λ])/
n1)}, {-I n1*Sin[2 Pi*n1*d1/λ],
Cos[2 Pi*n1*d1/λ]}}.{{Cos[
2 Pi*n2*d2/λ], -((I Sin[2 Pi*n2*d2/λ])/
n2)}, {-I n2*Sin[2 Pi*n2*d2/λ],
Cos[2 Pi*n2*d2/λ]}};
Do[MN = MN.M2, {i, 1, Np}];
MN = MN.{{1.0, 1.0}, {1.0, -1.0}};
Abs[MN[][]/MN[][]]^2]
Timing[Do[refl2[100, 2, 3, 5, 6, 100], {100}]]


The reason for that is that MN is numerical, since you have 0.5 in it. So in the Do loop, every step is done just numerically. In refl1 the MatrixPower is analytically performed and converted to numerical just as last step. If you change the MatrixPower input also to a numerical input the execution times is much faster with the MatrixPower function.

refl3[λ_, d1_, d2_, n1_, n2_, Np_] :=
Module[{MN, M2}, MN = {{0.5, 0.5}, {0.5, -0.5}} + 0.0 I;
M2 = N[{{Cos[
2 Pi*n1*d1/λ], -((I Sin[2 Pi*n1*d1/λ])/
n1)}, {-I n1*Sin[2 Pi*n1*d1/λ],
Cos[2 Pi*n1*d1/λ]}}.{{Cos[
2 Pi*n2*d2/λ], -((I Sin[2 Pi*n2*d2/λ])/
n2)}, {-I n2*Sin[2 Pi*n2*d2/λ],
Cos[2 Pi*n2*d2/λ]}}];
MN = MN.MatrixPower[M2, Np];
MN = MN.{{1.0, 1.0}, {1.0, -1.0}};
Abs[MN[][]/MN[][]]^2]

Timing[Do[refl2[100, 2, 3, 5, 6, 100000], {100}]]


{6.30906, Null}

Timing[Do[refl3[100, 2, 3, 5, 6, 100000], {100}]]


{0.017559, Null}

It is because you are using exact numbers. Use floating number instead. Like

Timing[Do[refl1[100., 2., 3., 5., 6., 100.], {100}]]
Timing[Do[refl2[100., 2., 3., 5., 6., 100.], {100}]]


{0.012001, Null}

{0.016001, Null}

which is the counter example of your counter intuition :)