3
$\begingroup$

Suppose I have two sets of parameters. One set of parameters is defined in terms of the other, according to some rules. I'm interested in displaying an object, which may by manipulated by either set of parameters. The expression is a plot based on the latter set of parameters.

It's important for me to keep variables and rules aside, as I'll be dealing with a larger list of them, and each would additionally have their own initial values and min./max. range. Also, this information would be used elsewhere in the code - I don't want to go through my list of ~30 parameters (16 inner, 14 outer) everytime I want to display a different Manipulate control.

Intuitively, I'd use a nested Manipulate expression to achieve this:

(* Two sets of parameters *)
vars1 = {a, b, c};
vars2 = {p, q, r};

(* One set is defined in terms of the other *)
rules = {
   p -> a^2 + c,
   q -> a*b,
   r -> a + b + c
   };

(* An object I'm interested in displaying *)
expr = Plot[p*x^2 + q*x + r, {x, 0, 1}];

(* Nested Manipulate *)
Apply[Manipulate, Join[
  {Apply[Manipulate, Join[
     {expr},
     Map[{{#1[[1]], #1[[2]]}, 0, 4, Appearance -> "Open"} &, rules]
     ]]},
  Map[{#1, 0, 1} &, vars1]
  ]]

This works as expected for the parameter values: when setting a, b or c, the other set of variables resets to get their default values. However, the plot isn't displayed correctly:

Output from code above

I assume the cause for the empty plot, is that the Plot gets evaluated out-of-context from the Manipulate. Being somewhat new to Mathematica and its use of symbols and dynamic variables, I'm not quite sure how to proceed.

I want to keep my variables and rules aside, as I'll be dealing with a larger list of them, and each would additionally have their own initial values and min./max. range.

There's more than one question about using Manipulate with a variable parameter list, and most answers point towards using Dynamic (e.g. Manipulate with a variable number of sliders). I also thought Hold and variants and/or Unevaluated might help, but not sure how.

I tried using Dynamic approach, and I thought Refresh might be useful in setting the variables. Taking from the example in the documentation, I have this working:

DynamicModule[{a, p},
 Column[{
   Slider[Dynamic[a]],
   Slider[Dynamic[p]],

   (* Reset p according to a, when a is set *)
   Dynamic[Refresh[p = a^2, TrackedSymbols -> {a}]],

   (* Show values and plot some expression *)
   Dynamic[{a, p}],
   Dynamic[Plot[a*z^2 + p, {z, 0, 1}]]
   }]
 ]

Here, p is set according to a (but not vice versa) and the plot is updated.

Output for code above

However, I don't know how to expand this to work on a predefined list of variables, such as is defined in vars1, vars2 and rules above.

The closest I got is the following code, but it produces errors and doesn't run after the first, initial run:

DynamicModule[Evaluate[Join[vars1, vars2]],
 Column[
  {
   (* Create simple manipulators for each of the variables *)
   Grid[
    Join[
     Map[{ToString[#1, TraditionalForm], Slider[Dynamic[#1], {0, 4}], 
        InputField[Dynamic[#1], FieldSize -> Tiny]} &, vars2],
     Map[{ToString[#1, TraditionalForm], Slider[Dynamic[#1], {0, 1}], 
        InputField[Dynamic[#1], FieldSize -> Tiny]} &, vars1]
     ]
    ],

   (* My honest attempt at reproducing the example with two *)
   (* parameters to work with a list of variables. *)
   Refresh[Map[#1[[1]] -> Dynamic[#1[[2]]] &, rules] /. Rule -> Set, 
    TrackedSymbols -> Map[Dynamic[#1] &, vars1]]
   }
  ]
 ]

Output for above code

I'd appreciate any help getting this code to work in the manner I want. Thank you!

$\endgroup$

2 Answers 2

2
$\begingroup$

You may use the second parameter of Dynamic to keep the variables in sync.

(*Two sets of parameters*)
vars1 = {a, b, c};
vars2 = {p, q, r};

(*One set is defined in terms of the other*)
rules = {p -> a^2 + c, q -> a*b, r -> a + b + c};

(*An object I'm interested in displaying*)
expr = Hold@Plot[p*x^2 + q*x + r, {x, 0, 1}];

Hold is needed on the Plot so that the variables will resolve at the correct time with ReleaseHold.

The following is need to be able to clear the variables while setting up the DynamicModule and its components.

hvars1 = Hold /@ vars1;
hvars2 = Hold /@ vars2;
srules = Association @@ MapAt[SymbolName, {All, 1}]@rules;

The following DynamicModule as three parts. The first sets up the master variables. The second the dependent variables. Finally, the third the plot.

Hold[Clear] /@ hvars1 // ReleaseHold;
Hold[Clear] /@ hvars2 // ReleaseHold;
DynamicModule[{},
 {
   With[{s = #, sn = SymbolName@#},
       {
        sn,
        Slider[
         Dynamic[s,
          (
            s = #;
            Clear[sn];
            Evaluate@Symbol[sn] = #;
            KeyValueMap[(Clear[#1]; Evaluate@Symbol[#1] = #2;) &]@srules
            ) &],
         {0, 1}],
        Dynamic@Symbol[sn]
        }
       ] & /@ vars1 // Apply[Sequence]
   ,
   Hold[Clear] /@ hvars2 // ReleaseHold;
   With[{s = #, sn = SymbolName@#},
       {
        sn,
        Slider[
         Dynamic[s,
          (
            s = #;
            Clear[sn];
            Evaluate@Symbol[sn] = #;
            ) &],
         {0, 1}],
        Dynamic@Symbol[sn]
        }
       ] & /@ vars2 // Apply[Sequence]
   ,
   Hold[Clear] /@ hvars1 // ReleaseHold;
   Hold[Clear] /@ hvars2 // ReleaseHold;
   {Dynamic[ReleaseHold@expr], SpanFromLeft, SpanFromLeft}
   } // Grid
 ]

enter image description here

Futher explainations to follow.

Hope this helps.

$\endgroup$
1
$\begingroup$

Below is a manual brute force way that works.

Manipulate[
 Manipulate[
  Plot[p*x^2 + q*x + r, {x, 0, 1}],
  {{p, a^2 + c }, 0, 4, Appearance -> "Open"},
  {{q, a*b}, 0, 4, Appearance -> "Open"},
  {{r, a + b + c}, 0, 4, Appearance -> "Open"}
  ],
 {a, 0, 1},
 {b, 0, 1},
 {c, 0, 1}
 ]

Mathematica graphics

It doesn't use the nice Map methods for creating the sliders nor the symbol expr for the plot. On the other hand, the plot is visible.

Possibly you can use this as the starting point and see if you can incorporate the enhancements.

Update

In order to handle a variable number of controls with the inner set dependent upon the outer set try:

vars1 = {a, b, c};
vars2 = {p, q, r};

Manipulate[
 With[
  {
   assocRules = Association[rules],
   fun = p*x^2 + q*x + r
   },

  Manipulate[
   {a, b, c};
   Plot[fun, {x, 0, 1}],

   (* Inner controls based on vars2 *)
   Evaluate[
    Sequence @@ 
     Table[{{symbol, assocRules[symbol]}, 0, 1}, {symbol, vars2}]]
   ]
  ],

 (* Outer controls based on vars1 *)
 Evaluate[Sequence @@ Table[{{symbol, 0}, 0, 1}, {symbol, vars1}]],

 (* Set rules in Initialization *)
 Initialization :> {
   rules := {p -> a^2 + c, q -> a*b, r -> a + b + c}
   }
 ]

Mathematica graphics

For reasons that escape me I had to place a statement

{a, b, c};

within the inner Manipulate. If one removes that statement the plot remains unchanged. I don't understand why but stumbled upon it during experimentation.

Other links may prove helpful

variable sliders

variable controls

$\endgroup$
3
  • $\begingroup$ Thank you Jack. However, this does not answer my question. This is just the regular use of Manipulate. My question is focused on using a variable list of variables. I pushed the sentence up about how I want the variables to be separate, to make this more apparent in my phrasing of the question. $\endgroup$
    – Yuval
    Commented Nov 1, 2016 at 6:55
  • $\begingroup$ @Yuval I updated to handle vars1, vars2 and rules. $\endgroup$ Commented Nov 4, 2016 at 17:52
  • $\begingroup$ Cool. I'm guessing the need to put {a,b,c} inside the inner Manipulated might have to do with the TrackedSymbols option which defaults to True, which means tracking only of symbols that appear in Manipulate, etc. controls. $\endgroup$
    – Yuval
    Commented Nov 5, 2016 at 10:17

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.