Mathematica Stack Exchange is a question and answer site for users of Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Let's say I write

y = x^2;
x = 10;
z = y + y^2 + y^3

Does Mathematica internally evaluate y before or after substituting it into z? I.e., does Mathematica internally evaluate this as

y = x^2 = 100
z = 100 + 100^2 + 100^3 = 1010100

or as

z = (10^2)^2 + (10^2)^3 + (10^2)^4 = 1010100

? Obviously in this simple case it doesn't really matter, but if y is an expensive function to calculate, then the former order of evaluation could be much faster than the latter. In general, if I have intermediate variables that are expensive to calculate, will I get a speedup if I exchange the order of the first two lines of code?

share|improve this question
1  
Each symbol can have a value associated with it or not. When you write y=x^2, then later on, write x=10, then y now automatically becomes 100 since it was pointing to x before, but now x has a value which is 10. There is no explicit evaluation of y. It is done automatically. It is all linked lists. If a symbol x has value other than itself, then its value is used automatically when looking up y. – Nasser Jan 11 at 8:04
    
@Nasser this is exactly what OP is asking about. Are dependencies between rules automatically eliminated, or not? It seems that the answer is not, in most cases anyway. This reminds me of another question, but I can't put my finger on it at the moment. – Oleksandr R. Jan 11 at 8:42
1  
Aside: Mathematica is holding onto the symbolic definition of y = x^2. If you clear x the value of y will be x^2 not 100. If you had defined x=10 first then the value of y would be 100 before and after x is cleared. – Ymareth Jan 11 at 9:03
    
up vote 13 down vote accepted

Try Trace:

    Trace[y = x^2;
 x = 10;
 z = y + y^2 + y^3]

The result is enter image description here

From this result it looks like the second variant takes place.

Have fun!

share|improve this answer

Yes, x^2 is computed three times. One way to see it is this:

In[1]:= On[]                                                                                                                                     

On::trace: On[] --> Null.

In[2]:= y = x^2                                                                                                                                  

                 2      2
Set::trace: y = x  --> x .

         2
Out[2]= x

In[3]:= x = 10                                                                                                                                   

Set::trace: x = 10 --> 10.

Out[3]= 10

In[4]:= z = y + y^2 + y^3                                                                                                                        

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

               2        2
Power::trace: y  --> 100 .

                 2
Power::trace: 100  --> 10000.

                 2
y::trace: y --> x .

x::trace: x --> 10.

               2       2
Power::trace: x  --> 10 .

                2
Power::trace: 10  --> 100.

               3        3
Power::trace: y  --> 100 .

                 3
Power::trace: 100  --> 1000000.

                  2    3
Plus::trace: y + y  + y  --> 100 + 10000 + 1000000.

Plus::trace: 100 + 10000 + 1000000 --> 1010100.

                     2    3
Set::trace: z = y + y  + y  --> z = 1010100.

Set::trace: z = 1010100 --> 1010100.

Out[4]= 1010100

In[5]:= Off[]     

I find this easier to follow than Trace but it produces too much junk in recent versions when run in the front end, so I had to run it in a terminal.

This observation does not imply that the result of y -> x^2 -> 10^2 -> 100 is not cached! (I don't know if it is.)

You can re-assign y form x^2 to simply100as simply asy = y` in this case. Better, you could make it a function and use memoization to cache the result:

Clear[y]
y[x_] := y[x] = x^2
share|improve this answer

Yes, it seems the order can matter, at least when using SetDelayed (:=), which is common for functions. In that case, if you swapped the first two lines, it would indeed go faster.

One way to see this is with a very inefficient function, which calculates Fibonacci numbers the long way:

slowFib[n_] := If[n <= 2, 1, slowFib[n - 1] + slowFib[n - 2]];

On my machine, it takes about 3.2 seconds to calculate the 29th Fibonacci number this way:

slowFib[29] // AbsoluteTiming
(*  {3.23395, 514229}  *)

Here is a slow calculation, with the same ordering as your example code. It takes 9.7 seconds to run the last line, because it is running slowFib 3 times to calculate y1:

y1 = slowFib1[29];
slowFib1 = slowFib;
z1 = y1 + y1 + y1 // AbsoluteTiming
(*  {2.*10^-6, slowFib1[29]}
    {1.*10^-6, slowFib}
    {9.65774, 1542687}  *)

Compare this to a "fast" version with the first 2 lines swapped. It calculates the value of slowFib[29] just once, when it assigns the value to y2, so it only does so once, and takes 3.3 seconds total:

slowFib2 = slowFib ) // AbsoluteTiming
(y2 = slowFib2[29]) // AbsoluteTiming
(z2 = y2 + y2 + y2) // AbsoluteTiming
(*  {1.*10^-6, slowFib}
    {3.28709, 514229}
    {4.*10^-6, 1542687}  *)

What's going on here is that Mathematica only evaluates slowFib[29] when it is called. In the fast code (vesion 2), it gets called once when defining y2. In the slow code (version 1), it gets called thrice, when defining z1.

share|improve this answer
    
Indeed, I just tried swapping the order of the definitions in my actual application (much more complicated than the example in the OP) and got a significant speedup. – tparker Jan 11 at 8:58
1  
If you are only evaluating your functions at a few values, take a look at tip #5 on this list: write your expensive functions as f[x_]:=f[x]=…, so that values get saved as you go. – Seth Jan 11 at 9:03

I'm not sure that memoization applies to OPs question. In a direct test, the order doesn't seem to make much of a difference in execution time.

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.