# How can I pass arguments to Module without being reinterpreted each time they appear inside it?

I'm trying to understand how to save time when calling functions in Mathematica. I wrote this example,

mat1 = RandomInteger[{-100, +100}, {200, 200}];
mat2 = RandomInteger[{-100, +100}, {200, 200}];
Timing[Inverse[mat1]][[1]]
matrixOp1[mat_?MatrixQ] := Module[{lm = mat}, lm + mat2]
Timing[matrixOp1[Inverse[mat1]]][[1]]
matrixOp2[mat_?MatrixQ] := Module[{lm = mat}, lm + lm]
Timing[matrixOp2[Inverse[mat1]]][[1]]


I thought that since module uses local vars, matrixOp2 will work at the same speed as matrixOp1. But this is what I found - the output is as such

Out[134]= 0.0312002
Out[136]= 0.0780005
Out[138]= 0.764405


It looks to me like the Inverse operation occurs many times inside matrixOp2 and I don't understand why. Can anyone please explain?

Thanks
Tamir

• Have you tried using TracePrint[] on your functions? Dec 12, 2015 at 2:18
• what do you mean Inverse operation happens many times? Result of Inverse goes in matrixOp2. I believe it has to do with implementation of Module. There could be a call to create a new context and copy information in there before proceeding and switching to old context after processing. This could be the reason for delay. Dec 12, 2015 at 4:39
• The slow down is due to + using the new allocated matrix lm in Module context. You can see this by changing Module[{lm = mat}, lm + lm] to Module[{lm = 2*mat}, lm ] which is the same thing, but now its speed is similar to the second case where the + with with the global mat. So the slow down happens to due to adding, element by element, lm to lm in the module content. It should not really be this much slower. This is strange. With compiled languages, the compiler will see this and will do this at compile time! Dec 12, 2015 at 5:04
• @Nasser : I don't think that correctly answers the question because in that case lm+mat2 and lm+lm should take same time, but it doesn't. In both cases, it is element by element addition. Dec 12, 2015 at 5:46
• @Rorschach first of all, I was not answering, just giving an observation. But there is a difference between lm+mat2 and lm+lm. In one case mat2 is global, while in the second case it is local. There could be difference there in how and/or where each is allocated, or some overhead to access local module level heap. But not knowing internals of Mathematica, this is all speculation. Dec 12, 2015 at 6:18

Fred's answer explains why Timing is a poor indicator of how much internal computation is taking place. This is an extended comment to point out that the real timing differences you see are just due to the different arithmetic problem in each case.

Note that the inverse of your integer matrix is a matrix of rationals with large numerators and denominators. Mathematica can add integer + rational faster than it can add rational + rational. Here's a simplified version of your code without any Modules:

lm = Inverse @ RandomInteger[{-100, +100}, {200, 200}];
mat2 = RandomInteger[{-100, +100}, {200, 200}];

AbsoluteTiming[lm + mat2][[1]]
(* 0.0290076 *)

AbsoluteTiming[lm + lm][[1]]
(* 1.12371 *)


So you can see that the difference between matrixOp1 and matrixOp2 is nothing to do with global vs local symbols, but simply that matrixOp1 is doing a faster operation (adding rationals and integers) than matrixOp2 (adding rationals and rationals)

It seems to me that your results have to do more with strange behaviour of Timing than with Module. See the answers on my question here for more information. In absolute timings, the results are as expected. I included also Nasser's modification.

Here are the definitions:

mat1=RandomInteger[{-100,+100},{200,200}];
mat2=RandomInteger[{-100,+100},{200,200}];
matrixOp1[mat_?MatrixQ]:=Module[{lm=mat},lm+mat2]
matrixOp2[mat_?MatrixQ]:=Module[{lm=mat},lm+lm]
matrixOp3[mat_?MatrixQ]:=Module[{lm=mat},2 lm]


And here my results:

Timing[Inverse[mat1]][[1]]// AbsoluteTiming
(* {5.65742,0.0312002} *)

Timing[matrixOp1[Inverse[mat1]]][[1]] // AbsoluteTiming
(* {5.71078,0.0624004} *)

Timing[matrixOp2[Inverse[mat1]]][[1]] // AbsoluteTiming
(* {7.002,1.37281} *)

Timing[matrixOp3[Inverse[mat1]]][[1]] // AbsoluteTiming
(* {5.83677,0.109201} *)


The big difference between Timing and AbsoluteTiming is at least remarkable.