Converting from Nengo 1.4 to Nengo 2.0¶
On this page, we’ll go over the changes between Nengo 1.4 and 2.0. They will first be reviewed heuristically in the section Big Changes, before being broken down practically in Changes to Common Functions
Big changes¶
Objects instead of strings¶
In the old API each object had to be assigned it’s own unique string.
In the new Nengo, you can use strings to identify objects called labels
,
but they are not unique. Instead, if you want to identify an object, you just
make sure to assign it a variable in your network
No Origins and Terminations¶
Previously, each object had a set of origins and terminations, which determined how the object produced output and accepted input, respectively. These two things have been collapsed into a single Connection object, which contains the logic of the origin and termination in one place.
Because the model is defined separately from when it’s built, the performance advantages of having origins and terminations can be accomplished during the build phase of the model instead.
Only Ensembles, Nodes, Networks and Probes¶
Many other objects have been removed, in order to start with a very minimal set of objects allowing a new user to get up and running without having to spend all the effort of memorizing a large API.
Basically:
Anything made with neurons is an Ensemble.
Anything not made with neurons (inputs, interfaces) are Nodes.
Probes are how you get data out of Nodes and Ensembles after simulating.
Networks are dumb containers for Ensembles, Nodes, Probes, and other Networks.
A power user can easily divide his code and stop from repeating themselves by encapsulating code that appears in multiple places in a Network.
Model and Simulator separation¶
There is now a clear separation between model definition and model creation/simulation. The motivation behind this is to allow for testing models as they are being created. For example, you can create a model, add a node and an ensemble, and the create a simulator based on that model and run it to make sure that your node and ensemble are doing what you think they’re doing. Then, you can continue adding new objects to your model—this will not be reflected in the simulator that you’ve already created, but you can create a new simulator with this updated model and run it without having to rerun your script from the top. Basically, it allows for a more iterative and interactive modelling process, and makes it more explicit which decisions are made manually and which are automatically determined when the simulator is created. Additionally, this means that the simulator timestep (dt) is not defined until the simulator is created, meaning that you can run the same model with different timesteps to see if there is a marked functional difference.
Changes to common functions¶
Many commonly used functions have been simplified or changed to be more explicit.
Making ensembles¶
Old API example:
nef.Network.make('A', 40, 1, mode='spike')
New API example:
A = nengo.Ensemble(40, 1, neuron_type=nengo.LIF(), label='A')
See nengo.Ensemble
for the full API specification.
Making ensemble arrays (i.e., network arrays)¶
Network arrays were very tightly coupled with the old API. In the new API, they have been decoupled and are just dumb containers, which you can easily import. The functionality should still be identical, though the syntax has changed.
Old API:
nef.Network.make_array(name, neurons, length, dimensions, **args)
New API:
nengo.networks.EnsembleArray(neurons, n_ensembles, dimensions_per_ensemble, **ens_args)
See nengo.networks.EnsembleArray
for more information.
Changes to common functions¶
Making nodes¶
Previously, there were several different ways
to provide input to a Nengo model:
SimpleNode
, FunctionInput
, and others.
All of these use cases should be covered
by nengo.Node
.
In the old API, you could create your own
SimpleNode
, or create a FunctionInput
with:
nef.Network.make_input(name, values, zero_after_time)
In the new API, you create a node with:
nengo.Node(output)
where output
is either a constant value
(float, list, NumPy array), a function, or
None
when passing through values unchanged.
See nengo.Node
for more information.
Making inputs¶
In the old API, inputs were defined as:
# Piecewise example
net.make_input("contextinput", {0.0:[0, 0.1], 0.5:[1, 0], 1.0:[0, 1]})
# Periodic white noise
net.make_fourier_input('fin1', base=0.1, high=10, power=0.5, seed=12)
Inputs are just nodes whose sole function are to output a function.
Many of the Examples use function output nodes.
Terminations and Origins¶
Practically, to convert from one to the other, consider this table
that uses an example ensemble called ens
who’s input needs to be
transformed by a two-dimensional identity function, [[1,0],[0,1]]
.
Nengo 1.4:
ens.addDecodedTermination("term_name", transform=MU.I(2))
Nengo 2.0:
# first create a simple pass-through node
term_name = nengo.Node(size_in=2, label="term_name")
# now connect the pass-through node to the ensemble
nengo.Connection(term_name, ens, transform=np.eye(2))
Same, thing but instead of a decoded origin, we want one that connects directly to the ensemble’s neurons.
Nengo 1.4:
ens.addTermination("term_name", transform=MU.I(2))
Nengo 2.0:
# first create a simple pass-through node
term_name = nengo.Node(size_in=2, label="term_name")
# now connect the pass-through node to the ensemble neurons
nengo.Connection(term_name, ens.neurons, transform=np.eye(2))
One more time, but with an output and no transform.
Nengo 1.4:
ens.addDecodedOrigin("origin_name")
Nengo 2.0:
# first create a simple pass-through node
origin_name = nengo.Node(size_in=2, label="origin_name")
# now connect the pass-through node to the ensemble
nengo.Connection(ens, origin_name, transform=np.eye(2))
Connecting things¶
A lot of the complexity of the old API has been pushed down to the constructors of the connection object. In general, old API calls of the form:
nef.Network.connect(pre, post)
are now:
nengo.Connection(pre, post)
However, there are some changes in the additional arguments.
The old API used weight
, index_pre
and index_post
as a shortcut to define transform
;
in the new API, only the transform
can be specified.
There are many NumPy functions that make transforms
easier to specify.
Additionally, we now utilize Python’s slice syntax
to route dimensions easily:
nengo.Connection(pre[0], post[1])
The keyword argument pstc
has been renamed to synapse
.