Skip to main content

Mathematica Manipulate with macros AND variable number of controls (persistence desired)


This post about avoiding code duplication in Manipulate helped a lot getting as far as I did. The method of "macros" outlined there works excellent, but when the number of controls is again controlled by yet another manipulate control, things get difficult.


I got two closely related problems in that area:



Problem 1: The following works like a charm:


With[{rows = 3, cols = 3, fieldSize = 5},
Manipulate[
Column[{
TableForm@mat,
Row[{"det = ", Det[mat]}]
}]
, {mat, None}, {initialized, None}
, Evaluate@With[{makeRow = Function[{rowIndex},
Map[Function[{colIndex}

,InputField[Dynamic[mat[[rowIndex, colIndex]]], Number, FieldSize -> fieldSize]]
, Range[cols]]
, HoldAll]}
, Grid[Map[makeRow, Range[rows]]]]
, Initialization :> (
If[Not[initialized === True], mat = Table[0, {rows}, {cols}]];
initialized = True
)
, SaveDefinitions -> True
]]


Mathematica graphics


I can enter a matrix, and Manipulate will calculate the determinant. If I start a new session, the values from the last session will still be there (important). But now suppose, I want to specify the matrix size with a setter bar. This can be done by wrapping a manipulate around it.


Manipulate[
With[{rows = size, cols = size, fieldSize = 5},
Manipulate[
Column[{TableForm@mat, Row[{"det = ", Det[mat]}]}]
, {mat, None}, {initialized, None}
, Evaluate@With[{makeRow = Function[{rowIndex},
Map[Function[{colIndex}, InputField[Dynamic[mat[[rowIndex, colIndex]]], Number, FieldSize -> fieldSize]], Range[cols]]

, HoldAll]}
, Grid[Map[makeRow, Range[rows]]]]
, Initialization :> (
If[Not[initialized === True], mat = Table[0, {rows}, {cols}]];
initialized = True
), SaveDefinitions -> True]]
, {{size, 3}, Range[7], ControlType -> SetterBar}
]

But this breaks the persistence over sessions. I tried to put the variable mat into the outer Manipulate, but that broke the whole thing.



Mathematica graphics


Problem 2: Kind of the same thing, with sliders instead of input fields.
I want to use a combination of setter bars and sliders to input a discrete probability istribution, and then do stuff with it within a manipulate.
IMPORTANT: The values should be persistent over sessions.


Mathematica graphics


Via the setter bar, I indicate that I want, for example, a prob dist for the integers from 2 through 5. That should then bring up 4 sliders that I can use to define the prob dist, one for each number. So the number of sliders should be variable. As you can see, I solved the problem of writing a macro for the slider generation, but I failed to make the number of sliders dependent on the inputs from the setter bars. It's clear from the code how I tried to solve the problem: The code, as it is, does everything I want, except that the number of sliders is always the same; and if you change one of the "lifeIsEasy" variables, it will seize to work.


With[{slidersPerRow = 3, lifeIsEasy1 = False, lifeIsEasy2 = False, 
length = 8
, normalize = Function[{pd, from, to}
, With[{total = Total[pd[[Range[from, to]]]], len = to - from + 1}

, If[total == 0, Table[1/len, {len}],
pd[[Range[from, to]]]/total]]]},
Manipulate[
If[max < min, max = min];
Column[{
Row[{"min ", min, " max ", max}]
, Row[{"=== slider values ==="}]
, TableForm[{sliderVals}, TableHeadings -> {None, Range[length]}]
, Row[{"=== prob dist ==="}]
, TableForm[{normalize[sliderVals, min, max]},

TableHeadings -> {None, Range[min, max]}]
, Row[{"expectation: ",
normalize[sliderVals, min, max] . Range[min, max]}]
}]
, {initialized, None}
, {sliderVals, None}
, Row[{
Control[{{min, 1}, Range[4], ControlType -> SetterBar}]
, Spacer[25]
, Control[{{max, min + 3}, Range[min, min + 4],

ControlType -> SetterBar}]
}]
, Row[{"I want ", Dynamic[max - min + 1], " sliders in ",
Dynamic@Ceiling[(max - min + 1)/slidersPerRow], " rows, "
, slidersPerRow, " per Row, and ",
Dynamic@Mod[max - min + 1, slidersPerRow, 1], " in the last" }]
, Evaluate@With[{makeSl = Function[{index}
, Dynamic@
Row[{ToString[index] <> " ",
Manipulator[Dynamic[sliderVals[[index]]]]}]

, HoldAll]}
, With[{
makeSliderRow = Function[{startIndex, endIndex}
, Row[Map[makeSl, Range[startIndex, endIndex]], Spacer[10]]]
}
, Sequence @@ Table[
makeSliderRow[
min + slidersPerRow*sliderRow(*=startIndex*)
, Min[Join[{min + slidersPerRow - 1 + slidersPerRow*sliderRow}, If[lifeIsEasy1, max, {}]]]](*=endIndex*)
, {sliderRow, 0, If[lifeIsEasy2, Ceiling[(max - min + 1)/slidersPerRow] - 1, 1]}

](*seqtab*)
](*with*)
](*evalwith*)
, Initialization :> (
If[Not[initialized === True], sliderVals = Table[0, {length}]];
initialized = True
)
, SaveDefinitions -> True
](*manip*)
](*with*)


Another approach I had for the problem was to use an inner Manipulate, but that didn't work either; I didn't get that even to the point where everything except persistence works, as in problem 1.


UPDATE


Although it is possible to do all this with Manipulate (see my 3 answers below), jVincent has convinced me that it is better and easier to do with DynamicModule instead.


I want to show how to make reusable components with this method.


Here is a slightly improved version of jVincent's solution.


makeInputGrid = Function[{mat},
Grid@Table[With[{x = x, y = y},
InputField[Dynamic[mat[[x, y]]], ImageSize -> 50]],
{x, 1, Dimensions[mat][[1]]},

{y, 1, Dimensions[mat][[2]]}],
HoldAll];
DynamicModule[{mymat = Table[0, {2}, {2}]},
Column[{
Slider[Dynamic[Dimensions[mymat][[1]], (mymat = Table[0, {#}, {#}]) &], {2, 8, 1}],
Dynamic@makeInputGrid[mymat],
Dynamic@MatrixForm@mymat,
Dynamic@Det@mymat
}]
]


Now, the function makeInputGrid can be used elsewhere. Note the HoldAll attribute, it won't work without that.


Here is how to do the slider problem with this approach. (I tried to make it look a little towards the way Manipulate makes things look.)


sliderGrid = Function[
{sliderValues, slidersPerRow, minVal, maxVal, which},
Panel@Grid[Partition[
Table[With[{index = which[[i]]},
Row[{index, Spacer[7], Manipulator[Dynamic[sliderValues[[index]]], {minVal, maxVal}]}]
], {i, Length[which]}],
slidersPerRow, slidersPerRow, 1, {}]],

HoldFirst
];
checkboxRow = Function[{flags},
Panel@Row[Table[With[{i = i},
Row[{i, Spacer[5], Checkbox[Dynamic[flags[[i]]]], Spacer[7]}]
], {i, Length[flags]}]],
HoldAll
];
With[{len = 10, min = -1, max = 1},
DynamicModule[{

flags = Table[True, {len}],
vals = RandomReal[{min, max}, len]
},
Panel[Column[{
Panel@Column[{
checkboxRow[flags],
Dynamic@sliderGrid[vals, 3, min, max, Select[Range[10], flags[[#]]&]]
}],
Dynamic@Grid[{Range[len], vals}]
}],

Background -> GrayLevel[0.9]
]
]]

variable number of sliders using dynamicmodule



Answer



When you start making much more complex interfaces using dynamic interactivity, I would suggest moving away from Manipulate, and instead using Dynamic along with controls, While it's nice that Manipulate makes appropriate controls for you, it can at times be harder to work with, especially when you want to generate new controls dynamically. Here is a simple implementation of your scaling matrix size determinant gizmo, which uses DynamicModule to save the variables and shows a Column of dynamic components:


DynamicModule[{mat = ConstantArray[0, {4, 4}]},
Column[{
Slider[

Dynamic[Dimensions[mat][[
1]], (mat = ConstantArray[0, {#, #}]) &], {2, 8, 1}],
Dynamic[Table[With[{x = x, y = y},
InputField[Dynamic[mat[[x, y]]], ImageSize -> 50]
], {x, 1, Dimensions[mat][[1]]}, {y, 1,
Dimensions[mat][[2]]}] // Grid]
,
Dynamic[MatrixForm[mat]],
Dynamic[Det[mat]]
}]

]

Image showing the interface resulting from the code


Building your interface this ways, in my opinion gives you much more freedom and less of a hassle.


Comments

Popular posts from this blog

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

mathematical optimization - Minimizing using indices, error: Part::pkspec1: The expression cannot be used as a part specification

I want to use Minimize where the variables to minimize are indices pointing into an array. Here a MWE that hopefully shows what my problem is. vars = u@# & /@ Range[3]; cons = Flatten@ { Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; Minimize[{Total@((vec1[[#]] - vec2[[u[#]]])^2 & /@ Range[1, 3]), cons}, vars, Integers] The error I get: Part::pkspec1: The expression u[1] cannot be used as a part specification. >> Answer Ok, it seems that one can get around Mathematica trying to evaluate vec2[[u[1]]] too early by using the function Indexed[vec2,u[1]] . The working MWE would then look like the following: vars = u@# & /@ Range[3]; cons = Flatten@{ Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; NMinimize[ {Total@((vec1[[#]] - Indexed[vec2, u[#]])^2 & /@ R...

How to remap graph properties?

Graph objects support both custom properties, which do not have special meanings, and standard properties, which may be used by some functions. When importing from formats such as GraphML, we usually get a result with custom properties. What is the simplest way to remap one property to another, e.g. to remap a custom property to a standard one so it can be used with various functions? Example: Let's get Zachary's karate club network with edge weights and vertex names from here: http://nexus.igraph.org/api/dataset_info?id=1&format=html g = Import[ "http://nexus.igraph.org/api/dataset?id=1&format=GraphML", {"ZIP", "karate.GraphML"}] I can remap "name" to VertexLabels and "weights" to EdgeWeight like this: sp[prop_][g_] := SetProperty[g, prop] g2 = g // sp[EdgeWeight -> (PropertyValue[{g, #}, "weight"] & /@ EdgeList[g])] // sp[VertexLabels -> (# -> PropertyValue[{g, #}, "name"]...