Helipad 0.7 is here with two big features: stackplots, and parameter sweeps.
If you’ve wanted to plot things where the total matters, or maybe where the values add up to a constant total and you’re interested in the relative proportions, stackplots can improve the visualization.
The Deme Selection sample model introduced in Helipad 0.6 is a good example (and see that page for the “before” screenshot): the top plot depicts the proportion of altruistic versus selfish phenotypes. But the plots are mirror images of one another. A stackplot eliminates the redundancy and makes it easier to scan the model output.
To turn a plot into a stackplot, just add a
stack=True argument when calling
A parameter sweep is a common way to exploring the sensitivity of an agent-based model to its parameter values by running the model repeatedly and systematically varying one or more parameters. In NetLogo, for example, this is called BehaviorSpace.
This is now possible with Helipad’s new
paramSweep function. Because possible values of any parameter are nailed down when instantiated with the
addParameter function, even for numerical variables (because the min, the max, and the step are set for sliders), all paramSweep needs to know is the variable(s) to sweep and how long to run each instance. The former can sweep n-dimensional parameter spaces (i.e. every combination of possible values from n different parameters), and the latter can specify either a fixed time, or a function with arbitrary stop conditions, for example if you’re trying to see if the variable affects how long it takes to get to equilibrium.
Sweeping hundreds or thousands of parameter combinations is a time-consuming task, so to make it as fast as possible, the parameter sweep bypasses the GUI and returns the results of each run as a Pandas dataframe, whose columns can optionally be narrowed just to the data of interest.
Helipad 0.7 has a new
createNetwork function that automatically generates an undirected and unweighted network of a specific density among agents. Helipad’s support for network types means that multiple distinct networks can be created this way, even over the same agents.
More significantly, to support the new parameter sweep functionality and to simplify the internals, the way parameters are handled internally has changed. The upshot is two things:
- More consistency in how callbacks and updating parameters are handled. For example, updating a parameter value in code will always update the parameter value in the GUI, where previously this was not true for sliders due to a Tkinter inconsistency.
- A new pattern for identifying parameters to functions.
This second feature is backward-incompatible and entails a few function removals and signature changes. First, functions that need to identify a parameter (for example
Shocks.register) now all do so with a single tuple-argument pattern, rather than the ad hoc list of optional arguments like
good, and so on.
The new parameter identification pattern looks like this:
- A global parameter can be identified using a string or a one-element tuple.
model.param('pSmooth')retrieves the value of the global
- The parameter values for all items – for example a per-breed or a per-good parameter – can be retrieved using a two-element tuple.
model.param(('productivity', 'good'))retrieves a
productivityparameter values for all goods. Note that parameters cannot be set this way.
- The parameter value for a particular item of a per-breed or per-good tuple can be referred to using a three-element tuple.
model.param(('demand', 'breed', 'hobbit'), 50)sets the
demandparameter for the
hobbitbreed to 50.
- Finally, because breeds apply only to specific agent primitives, when there are multiple primitives, the relevant primitive is specified using a fourth element of the tuple (this is optional when there is only one primitive). The previous example could also be specified
model.param(('demand', 'breed', 'hobbit', 'agent), 50).
- The second case can also be achieved in the event of multiple primitives by putting
Noneas the third element.
model.param(('demand', 'breed', None, 'agent))returns a
demandbreed parameter values for all breeds of the
This simplifies the internals a good bit, and standardizes the parameter interface across several different functions. It also obviates the
breedParameter functions, which have been removed. Your code should replace them with the new and more powerful
The full list of API changes follows.
API Changes in Version 0.7
- ⚠️ TimeSeriesRemoved
Graph.graph(the list of Matplotlib
Graph.series(the list of Matplotlib
Lineobjects). These are now kept track of in the Plot objects stored in
- ⚠️ HelipadRemoved the
goodParammethods. The latter two are replaced by a more flexible
parammethod that can take parameter ID tuples now.
- ⚠️ CpanelRemoved the
slidersproperty. Parameter widgets are now stored in
- ⚠️ ShockReplaced the
primproperties, which stored attributes of the parameter, with the
paramproperty, which stores the parameter object itself.
- ⚠️ registerReplaced the
primarguments with the
paramargument, a standard parameter identification tuple or string.
- ⚠️ paramReplaced the
nameand several undocumented arguments with the
paramargument, to increase flexibility and accommodate the new parameter identification pattern.
- ⚠️ GUICloseRenamed
GUICloseto more accurately reflect the hook's place in the flow, and passed the model instead of the GUI object.
- PlotKeep track of the Matplotlib
AxesSubplotobject here rather than in a separate list in
- SeriesKeep track of the Matplotlib
Lineobject here rather than in a separate list in
Graph, and the output series here rather than in the
- addSeriesReturns a
- addPlotAdded the
stackargument, and returns a
- addGoodReturns a
- addBreedReturns a
- addItemReturns either a
- ParamAdded the
elementproperties, and the