9
$\begingroup$

Here is a very nice example from The Student's Introduction to MATHEMATICA.

Manipulate[Module[{x, y},
  ContourPlot[Exp[-x^2 - y^2] + x y, {x, -1, 1}, {y, -1, 1},
   Contours -> 20,
   Epilog -> 
    Dynamic[{Arrow[{pt, 
        pt + {y - 2 E^(-x^2 - y^2) x, x - 2 E^(-x^2 - y^2) y} /. {x ->
            pt[[1]], y -> pt[[2]]}}]}]
   ]],
 {{pt, {.5, .5}}, Locator, 
  Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}]

Which produces a wonderful demonstration.

enter image description here

My next step was to try the following:

Clear[x, y, f];
f = E^(-x^2 - y^2) + x y;
Grad[f, {x, y}];

Then in the next cell, I tried:

Manipulate[Module[{x, y},
  ContourPlot[f, {x, -1, 1}, {y, -1, 1},
   Contours -> 20,
   Epilog -> 
    Dynamic[{Arrow[{pt, 
        pt + Grad[f, {x, y}] /. {x -> pt[[1]], y -> pt[[2]]}}]}]
   ]],
 {{pt, {.5, .5}}, Locator, 
  Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}]

Which gave only this:

enter image description here

I gave Evaluate a try but that didn't work. So I tried removing the Module.

Manipulate[
 ContourPlot[f, {x, -1, 1}, {y, -1, 1},
  Contours -> 20,
  Epilog -> 
   Dynamic[{Arrow[{pt, 
       pt + Grad[f, {x, y}] /. {x -> pt[[1]], y -> pt[[2]]}}]}]
  ],
 {{pt, {.5, .5}}, Locator, 
  Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}]

And that worked.

enter image description here

But try typing

x=12

in the next cell and watch what happens to the arrow.

Finally I tried wrapping everything with a DynamicModule to see if it would prevent the x=12 issue in the notebook. First, this cell.

Clear[x, y, f];
f = E^(-x^2 - y^2) + x y;
Grad[f, {x, y}];

Then:

DynamicModule[{x, y},
 Manipulate[
  ContourPlot[f, {x, -1, 1}, {y, -1, 1},
   Contours -> 20,
   Epilog -> 
    Dynamic[{Arrow[{pt, 
        pt + Grad[f, {x, y}] /. {x -> pt[[1]], y -> pt[[2]]}}]}]
   ],
  {{pt, {.5, .5}}, Locator, 
   Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}]]

This only produced:

enter image description here

As folks know, there has been a lot of discussion on not using Module inside of Manipulate and this is probably a good example of why not, but there is a lot of stuff happening here that I don't understand and could use some discussion explaining some of the issues:

  1. Why does Dynamic in the first code just update the arrow and not the contour plot.

  2. Why doesn't f = E^(-x^2 - y^2) + x y; and Grad[f, {x, y}]; work in the second piece of code?

  3. Why doesn't DynamicModule work in the last piece of code?

  4. And what is the best way to protect the arrow if a student type x=12 in their notebook?

Answers to Questions #3 and #4:

I should have defined the function f in the body of my dynamic module.

DynamicModule[{x, y, f},
 f = E^(-x^2 - y^2) + x y;
 Manipulate[
  ContourPlot[f, {x, -1, 1}, {y, -1, 1}, Contours -> 20, 
   Epilog -> 
    Dynamic[{Arrow[{pt, 
        pt + Grad[f, {x, y}] /. {x -> pt[[1]], 
          y -> pt[[2]]}}]}]], {{pt, {.5, .5}}, Locator, 
   Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}]]

This works and x is protected.

$\endgroup$
2
  • 1
    $\begingroup$ You can define f outside the module if you use f[x_,y_]:=Exp[...], and then use 'f[x,y]' inside the ContourPlot, and the Grad. $\endgroup$
    – N.J.Evans
    Commented Jul 30, 2015 at 15:21
  • 2
    $\begingroup$ @N.J.Evans. Sorry, that won't work in this situation. We're talking about having several Manipulate activities in a single notebook, not just one. And we need to protect the Manipulate activities from a host of static things inside the notebook. $\endgroup$
    – David
    Commented Jul 30, 2015 at 18:56

2 Answers 2

8
$\begingroup$

Both Module and DynamicModule are shadowing the global variables x and y in the example in which you use them. The demonstration is best written without using either Module or DynamicModule.

Manipulate[
  ContourPlot[f, {x, -1, 1}, {y, -1, 1}, Contours -> 20, 
    Epilog -> Dynamic[Arrow[{pt, pt + grad /. {x -> pt[[1]], y -> pt[[2]]}}]]],
  {f, None}, 
  {grad, None},
  {{pt, {.5, .5}}, Locator, Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]},
  TrackedSymbols :> {pt},
  Initialization :> (
    f = E^(-x^2 - y^2) + x y;
    grad = Grad[f, {x, y}])]

plot

Update

Sorry that I was careless about the testing of my code. The issue that you raise in your comment can be fixed by using pure functions. I do need to introduce Module in the fix.

My reworking of your example still keeps everything localized, Specifying controls that are non-functioning and invisible, like func and grad, is a useful trick for creating localized variables in Manipulate expressions,

x = 12; y = 42; func = 1; grad = 0;
Manipulate[
  ContourPlot[func[x, y], {x, -1, 1}, {y, -1, 1},
    Contours -> 20,
    Epilog -> Dynamic[Arrow[{pt, pt + grad[pt[[1]], pt[[2]]]}]]],
  {func, None},
  {grad, None},
  {{pt, {.5, .5}}, Locator, Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}, 
  TrackedSymbols :> {pt},
  Initialization :> (
    func = (E^(-#1^2 - #2^2) + #1 #2 &);
    grad = 
     With[{
       g = Module[{x, y}, 
             Grad[func[x, y], {x, y}] /. {x -> #1, y -> #2}]}, 
       Function[g]])]

The code taken from The Student's Introduction to MATHEMATICA works because it doesn't define functions, but uses expressions for both the function and the gradient. I don't like that approach because it is too rigidly coupled to a particular function. With my fixed code you only need to redefine func to introduce a new function. For example

Manipulate[
  ContourPlot[func[x, y], {x, -1, 1}, {y, -1, 1},
    Contours -> 20,
    Epilog -> Dynamic[Arrow[{pt, pt + grad[pt[[1]], pt[[2]]]}]]],
  {func, None},
  {grad, None},
  {{pt, {0, .1}}, Locator, Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}, 
  TrackedSymbols :> {pt},
  Initialization :> (
    func = (E^(#1^2 - #2^2) &);
    grad = 
    With[{
      g = Module[{x, y}, 
             Grad[func[x, y], {x, y}] /. {x -> #1, y -> #2}]}, 
      Function[g]])]

new-func

$\endgroup$
2
  • 2
    $\begingroup$ Sorry, it does not work. Put your code in a new notebook, quit the kernel, run your code, then in the next cell, type x=12 and run the cell. Watch what happens to the arrow. The Manipulate no longer runs properly. $\endgroup$
    – David
    Commented Jul 30, 2015 at 3:00
  • $\begingroup$ @David. I believe I have fixed the code so that no global variables can interfere with it. $\endgroup$
    – m_goldberg
    Commented Jul 30, 2015 at 15:53
4
$\begingroup$

This is taking your first modification of the original code and just changing the way f is defined, then using that function inside the module. It seems to work fine for me.

Clear[x, y, f];
x = 10;(*Global values have no effect on Module...*)
y = 12;(*Global values have no effect on Module...*)
f[x_, y_] := E^(-x^2 - y^2) + x y;
Manipulate[
 Module[
  {x, y},
  ContourPlot[f[x, y], {x, -1, 1}, {y, -1, 1}
   , Contours -> 20
   , Epilog -> Dynamic[{
      Arrow[
       {pt, pt + Grad[f[x, y], {x, y}] /. {x -> pt[[1]], y -> pt[[2]]}}
       ]
      }]
   ]
  ]
 , {{pt, {.5, .5}}
  , Locator
  , Appearance -> Graphics[{Red, Disk[]}, ImageSize -> 5]}
 ]
  1. Your first piece of code does not update the contour plot because you set f in terms of the global variables x and y. Inside the module x and y are scoped uniquely so x becomes x$1234, or something, and similar for y. Inside the module f is still a symbolic expression. This is why you should define f as a pure function, using set delayed.

  2. Setting f as a pure function will allow you to write Grad[f[x,y],{x,y}], which will work, unless you've set a value for x. That value will be applied before grad evaluates. You have a few options there. You can use Unique[x] to generate a symbol with no definitions, and then use that in Grad, or just clear x explicitly before calling Grad. Or you can use a module to scope x and y for this particular calculation. Which is basically the same thing as using Unique, but MMA handles the scoping for you.

  3. See 1. The symbols x and y are again localized to unique values within the dynamic module.

  4. If you write the function as I have here global definitions of x have no effect on the code inside the module. And you don't have to worry about students defining values for x or y.

$\endgroup$
2
  • $\begingroup$ Sorry, this does not work. Perform the following: (1) create a new notebook and quite the kernel. Copy your code into a cell and evaluate the cell. Then go to the next cell and enter the function f[x_, y_] := E^(x^2 - y^2) + x y; and evaluate this second cell. You will see that the image in your manipulate changes. $\endgroup$
    – David
    Commented Jul 30, 2015 at 23:21
  • $\begingroup$ If you keep moving the goal posts no one will ever answer your question. You listed four questions, I answered them. You should write another question about scoping that specifically addresses how to keep all symbols local, because that can be done. It's just not what you asked. $\endgroup$
    – N.J.Evans
    Commented Jul 31, 2015 at 2:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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