String representations

This should be an exhaustive list of the objects that can be created with Nengo, and their string representations.

[1]:
import difflib

import numpy as np

import nengo

Core objects

[2]:
with nengo.Network() as net1:

    a = nengo.Ensemble(100, 2)
    with nengo.Network(label="net2") as net2:
        b = nengo.Ensemble(100, 2, label="b")

    ap = nengo.Probe(a)
    bp = nengo.Probe(b)

    c1 = nengo.Connection(a, b)
    c2 = nengo.Connection(a, b, function=np.square)

    n1 = nengo.Node(output=np.sin)
    n2 = nengo.Node(output=np.cos, label="n2")

print("  str(obj)")
print("============")
print("--- Network")
print("    %s" % net1)
print("    %s" % net2)
print("--- Ensemble")
print("    %s" % a)
print("    %s" % b)
print("--- Probe")
print("    %s" % ap)
print("    %s" % bp)
print("--- Connection")
print("    %s" % c1)
print("    %s" % c2)
print("--- Node")
print("    %s" % n1)
print("    %s" % n2)
print("--- Neurons")
print("    %s" % a.neurons)
print("--- ObjView")
print("    %s" % b[:1])
print("")

print("  repr(obj)  ")
print("=============")
print("--- Network")
print("    %r" % net1)
print("    %r" % net2)
print("--- Ensemble")
print("    %r" % a)
print("    %r" % b)
print("--- Probe")
print("    %r" % ap)
print("    %r" % bp)
print("--- Connection")
print("    %r" % c1)
print("    %r" % c2)
print("--- Node")
print("    %r" % n1)
print("    %r" % n2)
print("--- Neurons")
print("    %r" % a.neurons)
print("--- ObjView")
print("    %r" % b[:1])
  str(obj)
============
--- Network
    <Network (unlabeled) at 0x7f1b1d307e80>
    <Network "net2">
--- Ensemble
    <Ensemble (unlabeled) at 0x7f1b1d307f28>
    <Ensemble "b">
--- Probe
    <Probe of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>
    <Probe of 'decoded_output' of <Ensemble "b">>
--- Connection
    <Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b">>
    <Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b"> computing 'square'>
--- Node
    <Node (unlabeled) at 0x7f1b4ec48278>
    <Node "n2">
--- Neurons
    <Neurons of <Ensemble (unlabeled) at 0x7f1b1d307f28>>
--- ObjView
    <Ensemble "b">[:1]

  repr(obj)
=============
--- Network
    <Network (unlabeled) at 0x7f1b1d307e80>
    <Network "net2" at 0x7f1b1d30f0b8>
--- Ensemble
    <Ensemble (unlabeled) at 0x7f1b1d307f28>
    <Ensemble "b" at 0x7f1b1d30f128>
--- Probe
    <Probe at 0x7f1b1d30f240 of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>
    <Probe at 0x7f1b1d30f278 of 'decoded_output' of <Ensemble "b">>
--- Connection
    <Connection at 0x7f1b1d30f2b0 from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b">>
    <Connection at 0x7f1b1db651d0 from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b"> computing 'square'>
--- Node
    <Node (unlabeled) at 0x7f1b4ec48278>
    <Node "n2" at 0x7f1b4ec543c8>
--- Neurons
    <Neurons at 0x7f1b1d81b0b8 of <Ensemble (unlabeled) at 0x7f1b1d307f28>>
--- ObjView
    <Ensemble "b" at 0x7f1b1d30f128>[:1]

Neuron types

[3]:
print(nengo.Direct())
print(nengo.RectifiedLinear())
print(nengo.Sigmoid())
print(nengo.Sigmoid(tau_ref=0.001))
print(nengo.LIFRate())
print(nengo.LIFRate(tau_rc=0.01, tau_ref=0))
print(nengo.LIF())
print(nengo.LIF(tau_rc=0.01, tau_ref=0))
print(nengo.AdaptiveLIFRate())
print(nengo.AdaptiveLIFRate(tau_rc=0.01, tau_n=0.5, inc_n=0.02))
print(nengo.AdaptiveLIF())
print(nengo.AdaptiveLIF(tau_rc=0.01, tau_n=0.5, inc_n=0.02))
print(nengo.Izhikevich())
print(nengo.Izhikevich(
    tau_recovery=0.01, coupling=0.5, reset_voltage=-60, reset_recovery=6))
Direct()
RectifiedLinear()
Sigmoid()
Sigmoid(tau_ref=0.001)
LIFRate()
LIFRate(tau_rc=0.01, tau_ref=0)
LIF()
LIF(tau_rc=0.01, tau_ref=0)
AdaptiveLIFRate()
AdaptiveLIFRate(tau_n=0.5, inc_n=0.02, tau_rc=0.01)
AdaptiveLIF()
AdaptiveLIF(tau_n=0.5, inc_n=0.02, tau_rc=0.01)
Izhikevich()
Izhikevich(tau_recovery=0.01, coupling=0.5, reset_voltage=-60, reset_recovery=6)

Learning rules

[4]:
print(nengo.PES())
print(nengo.PES(learning_rate=1e-6, pre_synapse=0.01))
print(nengo.BCM())
print(nengo.BCM(learning_rate=1e-8,
                pre_synapse=0.01,
                post_synapse=0.005,
                theta_synapse=10.0))
print(nengo.Oja())
print(nengo.Oja(
    learning_rate=1e-5, pre_synapse=0.01, post_synapse=0.005, beta=0.5))
print(nengo.Voja())
print(nengo.Voja(learning_rate=1e-5, post_synapse=None))
PES()
PES(learning_rate=1e-06, pre_synapse=Lowpass(tau=0.01))
BCM()
BCM(learning_rate=1e-08, pre_synapse=Lowpass(tau=0.01), post_synapse=Lowpass(tau=0.005), theta_synapse=Lowpass(tau=10.0))
Oja()
Oja(learning_rate=1e-05, pre_synapse=Lowpass(tau=0.01), post_synapse=Lowpass(tau=0.005), beta=0.5)
Voja()
Voja(learning_rate=1e-05, post_synapse=None)

Distributions

[5]:
print(nengo.dists.PDF([1, 2], [0.4, 0.6]))
print(nengo.dists.Uniform(0, 1))
print(nengo.dists.Uniform(0, 5, integer=True))
print(nengo.dists.Gaussian(1, 0.1))
print(nengo.dists.UniformHypersphere())
print(nengo.dists.UniformHypersphere(surface=True))
print(nengo.dists.Choice([1, 2, 3]))
print(nengo.dists.Choice([1, 2, 3], weights=[0.1, 0.5, 0.4]))
print(nengo.dists.SqrtBeta(3))
print(nengo.dists.SqrtBeta(3, 2))
print(nengo.dists.SubvectorLength(3))
PDF(x=array([1., 2.]), p=array([0.4, 0.6]))
Uniform(low=0, high=1)
Uniform(low=0, high=5, integer=True)
Gaussian(mean=1, std=0.1)
UniformHypersphere()
UniformHypersphere(surface=True)
Choice(options=array([1., 2., 3.]))
Choice(options=array([1., 2., 3.]), weights=array([0.1, 0.5, 0.4]))
SqrtBeta(n=3)
SqrtBeta(n=3, m=2)
SubvectorLength(dimensions=3)

Synapses

[6]:
print(nengo.synapses.Lowpass(0.01))
print(nengo.synapses.Alpha(0.02))
print(nengo.synapses.Triangle(0.03))
print(nengo.synapses.LinearFilter([1], [0.03, 1]))
Lowpass(tau=0.01)
Alpha(tau=0.02)
Triangle(t=0.03)
LinearFilter(num=array([1.]), den=array([0.03, 1.  ]))

Processes

[7]:
gaussian = nengo.dists.Gaussian(1, 2)
print(nengo.processes.WhiteNoise(gaussian, scale=False))
print(nengo.processes.FilteredNoise(nengo.synapses.Alpha(0.2), gaussian))
print(nengo.processes.BrownNoise(gaussian))
print(nengo.processes.WhiteSignal(0.2, 10, rms=0.3))
WhiteNoise(Gaussian(mean=1, std=2), scale=False)
FilteredNoise(synapse=Alpha(tau=0.2), dist=Gaussian(mean=1, std=2), scale=True)
BrownNoise(Gaussian(mean=1, std=2))
WhiteSignal(period=0.2, high=10, rms=0.3)

Signals

[8]:
print(nengo.builder.signal.Signal(np.array([0.])))
print(nengo.builder.signal.Signal(np.array([1., 1.]), name="one"))
Signal(name=None, shape=(1,))
Signal(name=one, shape=(2,))

Operators

[9]:
sig = nengo.builder.signal.Signal(np.array([0.]), name="sig")
print(nengo.builder.operator.TimeUpdate(sig, sig))
print(nengo.builder.operator.TimeUpdate(sig, sig, tag="tag"))
print(nengo.builder.operator.Reset(sig))
print(nengo.builder.operator.Reset(sig, tag="tag"))
print(nengo.builder.operator.Copy(sig, sig))
print(nengo.builder.operator.Copy(sig, sig, tag="tag"))
print(nengo.builder.operator.Copy(sig, sig, [0], slice(0, 1)))
print(nengo.builder.operator.Copy(sig, sig, [0], slice(0, 1), tag="tag"))
print(nengo.builder.operator.ElementwiseInc(sig, sig, sig))
print(nengo.builder.operator.ElementwiseInc(sig, sig, sig, tag="tag"))
print(nengo.builder.operator.DotInc(sig, sig, sig))
print(nengo.builder.operator.DotInc(sig, sig, sig, tag="tag"))
print(nengo.builder.operator.SimPyFunc(sig, lambda x: 0.0, True, sig))
print(nengo.builder.operator.SimPyFunc(
    sig, lambda x: 0.0, True, sig, tag="tag"))
print(nengo.builder.learning_rules.SimPES(sig, sig, sig, sig, 0.1))
print(nengo.builder.learning_rules.SimPES(sig, sig, sig, sig, 0.1, tag="tag"))
print(nengo.builder.learning_rules.SimBCM(sig, sig, sig, sig, 0.1))
print(nengo.builder.learning_rules.SimBCM(sig, sig, sig, sig, 0.1, tag="tag"))
print(nengo.builder.learning_rules.SimOja(sig, sig, sig, sig, 0.1, 1.0))
print(nengo.builder.learning_rules.SimOja(
    sig, sig, sig, sig, 0.1, 1.0, tag="tag"))
print(nengo.builder.neurons.SimNeurons(nengo.LIF(), sig, sig, [sig]))
print(nengo.builder.neurons.SimNeurons(
    nengo.LIF(), sig, sig, [sig], tag="tag"))
print(nengo.builder.processes.SimProcess(
    nengo.processes.WhiteNoise(), sig, sig, sig))
print(nengo.builder.processes.SimProcess(
    nengo.processes.WhiteNoise(), sig, sig, sig, tag="tag"))
TimeUpdate{}
TimeUpdate{ "tag"}
Reset{Signal(name=sig, shape=(1,))}
Reset{Signal(name=sig, shape=(1,))  "tag"}
Copy{Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,)), inc=False}
Copy{Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,)), inc=False  "tag"}
Copy{Signal(name=sig, shape=(1,))[[0]] -> Signal(name=sig[(slice(0, 1, None),)], shape=(1,)), inc=False}
Copy{Signal(name=sig, shape=(1,))[[0]] -> Signal(name=sig[(slice(0, 1, None),)], shape=(1,)), inc=False  "tag"}
ElementwiseInc{Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
ElementwiseInc{Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}
DotInc{Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
DotInc{Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}
SimPyFunc{Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,)), fn='<lambda>'}
SimPyFunc{Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,)), fn='<lambda>'  "tag"}
SimPES{pre=Signal(name=sig, shape=(1,)), error=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
SimPES{pre=Signal(name=sig, shape=(1,)), error=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}
SimBCM{pre=Signal(name=sig, shape=(1,)), post=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
SimBCM{pre=Signal(name=sig, shape=(1,)), post=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}
SimOja{pre=Signal(name=sig, shape=(1,)), post=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
SimOja{pre=Signal(name=sig, shape=(1,)), post=Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}
SimNeurons{LIF(), Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,))}
SimNeurons{LIF(), Signal(name=sig, shape=(1,)), Signal(name=sig, shape=(1,))  "tag"}
SimProcess{WhiteNoise(Gaussian(mean=0, std=1), scale=True), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))}
SimProcess{WhiteNoise(Gaussian(mean=0, std=1), scale=True), Signal(name=sig, shape=(1,)) -> Signal(name=sig, shape=(1,))  "tag"}

Simulator

The representation of the Simulator is not particularly illuminating, but you can get a detailed account of the Simulator by printing its sorted list of Operators.

[10]:
with nengo.Simulator(net1) as sim:
    print('\n'.join("  * %s" % op for op in sim._step_order))
  * Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,))}
  * Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(8,))}
  * Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b"> computing 'square'>"}
  * Reset{Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>>, shape=(4,))}
  * TimeUpdate{}
  * Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b">>"}
  * Copy{Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.bias, ..., <Ensemble "b">.bias>, shape=(200,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>, shape=(200,)), inc=False}
  * SimPyFunc{None -> Signal(name=<Node (unlabeled) at 0x7f1b4ec48278>.out, shape=(1,)), fn='sin'}
  * DotInc{Signal(name=<Ensemble (unlabeled) at 0x7f1b1d307f28>.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>[(slice(0, 100, None),)], shape=(100,))  "<Ensemble (unlabeled) at 0x7f1b1d307f28> encoding"}
  * SimPyFunc{None -> Signal(name=<Node "n2">.out, shape=(1,)), fn='cos'}
  * DotInc{Signal(name=<Ensemble "b">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>[(slice(100, 200, None),)], shape=(100,))  "<Ensemble "b"> encoding"}
  * SimNeurons{LIF(), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>, shape=(200,)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>, shape=(200,))}
  * DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
  * DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(6, 8, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
  * DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
  * Copy{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,)) -> Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>>, shape=(4,)), inc=True}
  * DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
  * SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(6, 8, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
  * SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
/mnt/d/Documents/nengo-repos/nengo/nengo/builder/optimizer.py:639: UserWarning: Skipping some optimization steps because SciPy is not installed. Installing SciPy may result in faster simulations.
  "Skipping some optimization steps because SciPy is "

The diff of two simulators’ sorted ops tells us how two built models differ. We can use the difflib library (included with Python) to do a diff directly in Python.

[11]:
# Setting labels on all of the Nengo objects ensures the strings
# stay the same across simulator instances.
a.label = 'a'
n1.label = 'n1'

with nengo.Simulator(net1) as sim1:
    sim1_str = sorted(str(op) for op in sim1._step_order)

diff = difflib.unified_diff(
    a=sorted([str(op) for op in sim._step_order]), b=sim1_str)
print('\n'.join(diff))
# Several differences here because labels weren't set on sim
---

+++

@@ -1,19 +1,19 @@

-Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b"> computing 'square'>"}
-Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble (unlabeled) at 0x7f1b1d307f28> to <Ensemble "b">>"}
-Copy{Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.bias, ..., <Ensemble "b">.bias>, shape=(200,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>, shape=(200,)), inc=False}
-Copy{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,)) -> Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>>, shape=(4,)), inc=True}
-DotInc{Signal(name=<Ensemble "b">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>[(slice(100, 200, None),)], shape=(100,))  "<Ensemble "b"> encoding"}
-DotInc{Signal(name=<Ensemble (unlabeled) at 0x7f1b1d307f28>.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>[(slice(0, 100, None),)], shape=(100,))  "<Ensemble (unlabeled) at 0x7f1b1d307f28> encoding"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(6, 8, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-Reset{Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble (unlabeled) at 0x7f1b1d307f28>>>, shape=(4,))}
+Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "a"> to <Ensemble "b"> computing 'square'>"}
+Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "a"> to <Ensemble "b">>"}
+Copy{Signal(name=merged<<Ensemble "b">.bias, ..., <Ensemble "a">.bias>, shape=(200,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>, shape=(200,)), inc=False}
+Copy{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,)) -> Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,)), inc=True}
+DotInc{Signal(name=<Ensemble "a">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(4, 6, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(100, 200, None),)], shape=(100,))  "<Ensemble "a"> encoding"}
+DotInc{Signal(name=<Ensemble "b">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(0, 100, None),)], shape=(100,))  "<Ensemble "b"> encoding"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+Reset{Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,))}
+Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>, shape=(8,))}
 Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,))}
-Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(8,))}
-SimNeurons{LIF(), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_in, ..., <Ensemble "b">.neuron_in>, shape=(200,)), Signal(name=merged<<Ensemble (unlabeled) at 0x7f1b1d307f28>.neuron_out, ..., <Ensemble "b">.neuron_out>, shape=(200,))}
-SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
-SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(6, 8, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
+SimNeurons{LIF(), Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>, shape=(200,)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>, shape=(200,))}
+SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
+SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(2, 4, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
+SimPyFunc{None -> Signal(name=<Node "n1">.out, shape=(1,)), fn='sin'}
 SimPyFunc{None -> Signal(name=<Node "n2">.out, shape=(1,)), fn='cos'}
-SimPyFunc{None -> Signal(name=<Node (unlabeled) at 0x7f1b4ec48278>.out, shape=(1,)), fn='sin'}
 TimeUpdate{}
[12]:
with nengo.Simulator(net1) as sim2:
    sim2_str = sorted(str(op) for op in sim2._step_order)
diff = difflib.unified_diff(a=sim1_str, b=sim2_str)
print('\n'.join(diff))
# No more differences because labels keep representations stable
---

+++

@@ -1,19 +1,20 @@

-Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "a"> to <Ensemble "b"> computing 'square'>"}
-Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "a"> to <Ensemble "b">>"}
+Copy{Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,)) -> Signal(name=merged<<Ensemble "b">.signal, ..., <Probe of 'decoded_output' of <Ensemble "a">>>[(slice(0, 2, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "a"> to <Ensemble "b"> computing 'square'>"}
+Copy{Signal(name=merged<<Ensemble "a">.signal, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "a">.signal, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,)), inc=True  "<Connection from <Ensemble "b"> to <Probe of 'decoded_output' of <Ensemble "b">>>"}
 Copy{Signal(name=merged<<Ensemble "b">.bias, ..., <Ensemble "a">.bias>, shape=(200,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>, shape=(200,)), inc=False}
-Copy{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,)) -> Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,)), inc=True}
-DotInc{Signal(name=<Ensemble "a">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(4, 6, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(100, 200, None),)], shape=(100,))  "<Ensemble "a"> encoding"}
-DotInc{Signal(name=<Ensemble "b">.scaled_encoders, shape=(100, 2)), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(6, 8, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(0, 100, None),)], shape=(100,))  "<Ensemble "b"> encoding"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(0, 100, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(0, 2, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>[(slice(100, 200, None),)], shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
-Reset{Signal(name=merged<<Probe of 'decoded_output' of <Ensemble "b">>, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,))}
-Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>, shape=(8,))}
+Copy{Signal(name=merged<Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), ..., Dense(shape=(2, 2)).weighted>, shape=(4,)) -> Signal(name=merged<<Ensemble "b">.signal, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,)), inc=True}
+DotInc{Signal(name=<Ensemble "a">.scaled_encoders, shape=(100, 2)), Signal(name=merged<<Ensemble "a">.signal, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(100, 200, None),)], shape=(100,))  "<Ensemble "a"> encoding"}
+DotInc{Signal(name=<Ensemble "b">.scaled_encoders, shape=(100, 2)), Signal(name=merged<<Ensemble "b">.signal, ..., <Probe of 'decoded_output' of <Ensemble "a">>>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(0, 100, None),)], shape=(100,))  "<Ensemble "b"> encoding"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=<Ensemble "a">.neuron_out, shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+DotInc{Signal(name=Dense(shape=(2, 2)).weights, shape=(2, 100)), Signal(name=<Ensemble "b">.neuron_out, shape=(100,)) -> Signal(name=merged<<Ensemble "a">.signal, ..., Dense(shape=(2, 2)).weighted>[(slice(4, 6, None),)], shape=(2,))  "Dense(shape=(2, 2)).apply_weights"}
+DotInc{Signal(name=merged<Dense(shape=(2, 2)).weights, ..., Dense(shape=(2, 2)).weights>, shape=(4, 100)), Signal(name=<Ensemble "a">.neuron_out, shape=(100,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,))}
+Reset{Signal(name=merged<<Ensemble "a">.signal, ..., Dense(shape=(2, 2)).weighted>, shape=(6,))}
+Reset{Signal(name=merged<<Ensemble "b">.signal, ..., <Probe of 'decoded_output' of <Ensemble "a">>>, shape=(4,))}
 Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>, shape=(4,))}
-SimNeurons{LIF(), Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>, shape=(200,)), Signal(name=merged<<Ensemble "b">.neuron_out, ..., <Ensemble "a">.neuron_out>, shape=(200,))}
-SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
-SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., <Ensemble "b">.signal>[(slice(2, 4, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
+Reset{Signal(name=merged<Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,))}
+SimNeurons{LIF(), Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(0, 100, None),)], shape=(100,)), Signal(name=<Ensemble "b">.neuron_out, shape=(100,))}
+SimNeurons{LIF(), Signal(name=merged<<Ensemble "b">.neuron_in, ..., <Ensemble "a">.neuron_in>[(slice(100, 200, None),)], shape=(100,)), Signal(name=<Ensemble "a">.neuron_out, shape=(100,))}
+SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,)) -> Signal(name=Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), shape=(2,))}
+SimProcess{Lowpass(tau=0.005), Signal(name=merged<Dense(shape=(2, 2)).weighted, ..., Dense(shape=(2, 2)).weighted>[(slice(2, 4, None),)], shape=(2,)) -> Signal(name=merged<Dense(shape=(2, 2)).weighted.Lowpass(tau=0.005), ..., Dense(shape=(2, 2)).weighted>[(slice(0, 2, None),)], shape=(2,))}
 SimPyFunc{None -> Signal(name=<Node "n1">.out, shape=(1,)), fn='sin'}
 SimPyFunc{None -> Signal(name=<Node "n2">.out, shape=(1,)), fn='cos'}
 TimeUpdate{}