Group Abstract Group Abstract

Message Boards Message Boards

What's wrong in my use of Manipulate

GROUPS:
This code contains control system functions, but I'm posting it under Dynamics because I suspect that's where my problem lies.
The code below defines a simple plant, and then a generic PID controller. I extract the poles of the closed loop transfer function and try to plot them while using manipulate to adjust the model parameters. When I manipulate only the list of points to be plotted, it works fine. But when I enclose the list in ListPlot, ListPlot does not plot them?
If someone can point out the flaw in my code I would appreciaate it greatly!

The code below has output deleted, but will execute:
 (* TF for plant *)
 plantTf = TransferFunctionModel[k/s, s]
 
 (* TF for PID controller *)
 pidTf = TransferFunctionModel[kp (1 + 1/(\[Tau]i s) + \[Tau]d s), s]
 
 (* TF for closed loop system *)
 closedLoopTf = SystemsModelFeedbackConnect[plantTf, pidTf]
 
(* poles of the closed loop system in symbolic form *)
closedLoopPoles = TransferFunctionPoles[closedLoopTf][[1, 1]]

(* convret complex number to a vector *)
complexPair[z_] := {Re[z], Im[z]}
(* Manipulate can create a list of the poles *)
Manipulate[
complexPair /@ closedLoopPoles // Evaluate,
{k, 1, 10}, {kp, 1, 10}, {\[Tau]i, 1, 10}, {\[Tau]d, 1, 10}]

This works fine. The image below is from one setting of parameters:
(* but when ListPlot plots them, they don't plot *)
Manipulate[
ListPlot[complexPair /@ closedLoopPoles // Evaluate],
{k, 1, 10}, {kp, 1, 10}, {\[Tau]i, 1, 10}, {\[Tau]d, 1, 10}]

But that did not work. No points get plotted:
POSTED BY: David Keith
Answer
9 months ago
Manipulate does not see the dependency, since you have all the dependent variables out of its scope. One way is to put the function inside Manipulate
 Manipulate[
 {kp, \[Tau]i, \[Tau]d, k};
 ListPlot[complexPair /@ closedLoopPoles], {k, 1, 10}, {kp, 1,10}, {\[Tau]i, 1, 10}, {\[Tau]d, 1, 10},
 Initialization :>
   (
    plantTf = TransferFunctionModel[k/s, s];
    pidTf = TransferFunctionModel[kp (1 + 1/(\[Tau]i s) + \[Tau]d s), s];
    closedLoopTf = SystemsModelFeedbackConnect[plantTf, pidTf];
    closedLoopPoles = TransferFunctionPoles[closedLoopTf][[1, 1]];
   complexPair[z_] := {Re[z], Im[z]}
   )
]

Another option is to use LocalizedVariables->False
Manipulate[
{kp, \[Tau]i, \[Tau]d, k};
ListPlot[complexPair /@ closedLoopPoles // Evaluate], {k, 1,10}, {kp, 1, 10}, {\[Tau]i, 1, 10}, {\[Tau]d, 1, 10},
LocalizeVariables -> False]
POSTED BY: Nasser
Answer
9 months ago
Thank you very much, Nasser. That worked great.
It does leave me a bit confused. I do not see how my implemention differs from the example:
Manipulate[Plot[Sin[x (1 + a x)], {x, 0, 6}], {a, 0, 2}]
Which of course works fine. Could you perhaps point out the difference for me?
Best regards,
David
POSTED BY: David Keith
Answer
9 months ago
David; in the example you show
Manipulate[Plot[Sin[x (1 + a x)], {x, 0, 6}], {a, 0, 2}]
The manipulate expression is anything between the start of Manipulate the first ",", i..e this

Manipulate[  expression,   controlVariables, Initialization :> ()]

In your case, it is:

Plot[Sin[x (1 + a x)], {x, 0, 6}]
Hence, the dependent variable, which is "a"  is visible to Manipulate. i.e. Manipulate "sees" that there is a dependent variable (symbol) in its expression which needs to be tracked by the change in the slider.

If you change this example to
foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}]
Manipulate[Evaluate@foo[], {a, 0, 2}]
Which is basically what you had, now Manipulate looks at its expression and does not "see" the "a" in there any more. So, it has nothing to track. It is important that symbols to be tracked to appear literally in the expression of Manipulate, or somewhere in the body of Manipulate. Otherwise, you have to tell Manipulate to track all symbols all over the place. This is what "LocalizeVariables -> False" does. However, this is not a good solution. It is better to put all code and data used by Manipulate inside Manipulate and not in the global context as you had.

Here is a summary of all the cases:

Case 1

move "foo[]" in the above example to inside Manipulate, in the initialization section, add literal symbol "a" so Manipulate will track it
Manipulate[
a;
foo[], {a, 0, 2},
Initialization :>
  (
   foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}];
   )
]
Notice the trick above. "a;" was added so that it appears in the Manipulate expression. Otherwise, it will not be tracked. You'll get an initial plot, but nothing will happen when moving the slider

Case 2

move "foo[]" to global context, but have to tell Manipulate that LocalizeVariable is false
foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}];
Manipulate[
  a;
  foo[],
  {a, 0, 2},
  LocalizeVariables -> False
]
But notice, we still need to put "a" somewhere in the expression for it to tracked.  Not enough just to say LocalizeVariables -> False

Case 3

It is not enough to use "TrackedSymbols :>a", if the symbol itself do not show up in the expression. Hence this does not work
foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}];
Manipulate[
foo[],
{a, 0, 2},
LocalizeVariables -> False,
TrackedSymbols :> a
]
Notice there is no "a" in the expression. Manipulate will not track "a" even though we told it to !

case 4

Same as case 3, even if we put the function inside the Initialization section, it will still not track "a" . One will get an initial plot, but that is all.
 Manipulate[
 foo[],
 {a, 0, 2},
 TrackedSymbols :> a,
 Initialization :>
   (
    foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}];
    )
 ]

Case 5

Putting the function definition of foo[] itself inside Manipulate expression, now Manipulate sees "a" there and will automatically track it. No need to do anything more:
Manipulate[
Module[{},
  foo[] := Plot[Sin[x (1 + a x)], {x, 0, 6}];
  foo[]
  ],
{a, 0, 2}
]

Or simply
Manipulate[
Plot[Sin[x (1 + a x)], {x, 0, 6}],
{a, 0, 2}
]

Case 6

This is the method I use myself. Put all the functions inside the initialization section, but pass the dependent variables by argument call.
Manipulate[
foo[a],
{a, 0, 2},
Initialization :>
  (
   foo[a_] := Module[{x}, Plot[Sin[x (1 + a x)], {x, 0, 6}]]
   )
]
I like this, becuase it achieves both the goal of having all the slider symbols inside the Manipulate expression, hence Manipulate will track them, and at the same time, it avoid the function be in global context, and it is the modular way, since one can see which parameter each function depends on by looking at the signature of the function.

Case 7

Similar to case 6, but the function itself is now in the global context. This is a fine solution as well, if this function needs to be called from somewhere else as well other than from the Manipulate. Otherwise, it is best not to have in the global context and use case 6.
foo[a_] := Module[{x}, Plot[Sin[x (1 + a x)], {x, 0, 6}]];
Manipulate[
foo[a],
{a, 0, 2}
]
POSTED BY: Nasser
Answer
9 months ago
Nasser did a very nice explanation. I just wanted to add something short. Look at this example:
x = 5; y = cat;
Manipulate[Sin[y x], {x, 0, 1}]


While y is global and leaks inside Manipulate, which is basically a DynamicModule, global x=5 is not the same as as dynamic local x inside manipulate. You can see it by selecting Manipulate cell and going Top menu >> Cell >> Show expression. It is x$$ which is a different variable.

POSTED BY: Vitaliy Kaurov
Answer
9 months ago
Thanks to both of you. It was very helpful, and I think I mostly understand.
Consider the following code:
 f[x] := x^a
 
 (* case 1 *)
 Manipulate[Plot[f[x], {x, 0, 3}], {a, 0, 3}]
 
 (* case 2 *)
 Manipulate[Plot[f[x] /. a -> pa, {x, 0, 3}], {pa, 0, 3}]
 
 (*case 3 *)
Manipulate[Plot[f[x] /. a -> pa // Evaluate, {x, 0, 3}], {pa, 0, 3}]

(* case 4 *)
Manipulate[Plot[f[x] // Evaluate, {x, 0, 3}], {a, 0, 3}]

In[53]:= f[x] // Evaluate

Out[53]= x^a
The first case does not work, for the reasons given, Manipulate can't see the a. The second puts pa in scope, but Manipulate doesn't see it as part of f  until the expression is evaluated. And the third case works, because once evaluated, pa is seen.

But the last case confuses me. If f is evaluated inside Plot to become x^a, then why wouldn't Manipulate see it, as in the third case.

The reason this is of interest to me is that in many cases I want the functions to have global scope,because looking at their output with Manipulate is just one step in their development, and they will be of further use in the notebook.

Best regards,
David
POSTED BY: David Keith
Answer
9 months ago
btw, you missed the underscore in your function. It should be "f[x_] := x^a" not "f := x^a
f[x_] := x^a
Manipulate[Plot[f[x] // Evaluate, {x, 0, 3}], {a, 0, 3}]
But that is why it does not work. Again, the slider symbol (the symbols that the expression depends on) has to appear literally in the expression (i.e. during the initial parsing phase of the Manipulate body). It does not matter if it when evaluates then "a" will appear, becuase the expression will not evaluate in the first place since Manipulate has no reason to evaluate it, since there is nothing to track and update in the first place !

You can, if you want, get that external input from inside Manipulate. There is no reason to have shared global variables all over the place. makes maintenance of code hard. In the above your "a" is global. The function "f[x_]" is not a true function either. A true function is one that takes all its input via parameters.  In your case, "f[x_]" get one of its input, the "x" via parameter, but the other variable "a" is somewhere else.

A function should be all self contained. It should take all its input via argument calls, and return its result by its evaluated value.  That is the right way to do things. You can still have your itself function be global, but simply pass it all its input like this:
f[x_, a_] := x^a
Manipulate[
Plot[f[x, a], {x, 0, 3}], {a, 0, 3}
]

Now the function is all self contained. The function itself is global, but it takes all its input via arguments. So it is modular and reusable in other places as well.
btw, This is the same pattern as putting the function in the Initialization section, which is case 6 I showed before. In this case, the function itself is global (no problem as long as its input is via parameters) while in case 6, the function itself was not global, but only known inside Manipulate. I added case 7 in my last reply to cover this case as well. Thank you for reminding me about it. I am sure one can come up with more variations, but I think these 7 cases now cover all the main ones.
POSTED BY: Nasser
Answer
9 months ago
Thank you, Nasser. I agree on functional programming -- I was just experimenting with what manipulate can see. If i understand correctly, manipulate looks before anything else is done. I will reread and think more about what you've written, but this is working for me. Thanks!
POSTED BY: David Keith
Answer
9 months ago
David;
I am not expert on the internals of Manipulate phases and working (since I do not work at WRI, I can't see inside). I know what I know about it from using it and lots of debugging. i.e. from looking from the outside.

But I got this useful email long time ago from one of the nice folks at WRI support on a Manipulate problem I once had. Here it is below. May be it sheds more light

Manipulate doesn't really evaluate until it gets to the Initialization
option, but it will check its input for correct form. Mathematica reads the
main body of the Manipulate before running the Initialization option.
POSTED BY: Nasser
Answer
9 months ago
Hello, Nasser, do you know the exact order of evaluation in a Manipulate of the type:
Manipulate[Module{variables},  expression],   controlVariables, Initialization :> (other variables)]
If one uses Block i.o. Module, does the Manipulate then recognize the global variable definitions?
Otherwise thanks for your excellent contribution. Erik
POSTED BY: Erik Mahieu
Answer
8 months ago