EnsembleArray to Ensemble Neural Connection

Is there a way to have a direct neural connection between an EnsembleArray and an Ensemble? I can achieve this between two ensembles like this:

with nengo.Network() as net:
    ens1 = nengo.Ensemble(20, 2)
    ens2 = nengo.Ensemble(50, 2)
    nengo.Connection(ens1, ens2, solver=nengo.solvers.LstsqL2(weights=True))

but something like:

with nengo.Network() as net:
    ens_arr = nengo.networks.EnsembleArray(20, 6)
    ens = nengo.Ensemble(50, 2)
    nengo.Connection(ens_arr, ens, solver=nengo.solvers.LstsqL2(weights=True))

doesn’t work. I was hoping the neurons in the EnsembleArray could be connected to another ensemble as if the 6 ensembles of 20 neurons were a single 120 neuron ensemble.

Hi @Luciano,

If you want to set up a direct “neuron-to-neuron” connection between an EnsembleArray and an Ensemble, it can be done using the add_neuron_output() (or add_neuron_input()) method of the EnsembleArray class:

with nengo.Network() as model:
    ens_arr = nengo.networks.EnsembleArray(20, 6)
    ens_arr.add_neuron_output()  # Add the "neuron" output to the ensemble array
    ens = nengo.Ensemble(50, 2)

Then, you can create a direct neuron connection like so:

    nengo.Connection(
        ens_arr.neuron_output,
        ens.neurons,
        transform=np.random.random(
            (ens.n_neurons, ens_arr.n_ensemble * ens_arr.n_neurons_per_ensemble)
        ),
    )

There is one downside to this method though. Using the solver=nengo.solvers.LstsqL2(weights=True), the weights are solved for you to optimize whatever function you provide the nengo.Connection. With the .neuron connections however, you have to solve for the weight matrix yourself. Let me know if you need to have the weight matrix solved to perform a specific function (and I’ll post updated code to do this – once I figure out how to do it… it is somewhat complicated to do), or if my proposed solution above is adequate.

I’m a little confused about the Connections. Are you saying the solver argument only takes effect if a transform is specified? Now that I think about it I’m not sure what the solver would be solving for without a transform. What would a connection like this do?

nengo.Connection(ens1, ens2, solver=nengo.solvers.LstsqL2(weights=True))

For my particular task I have a network with the larger goal of moving an arm towards a target and this would just be the connection between the sensory and motor cortexes. Currently, the sensory cortex is an ensemble array which is connected to a node which scales things before outputting to the motor cortex, which is an ensemble. I want to remove that intermediate node but now I’m not sure how I should even be optimizing these weights. And in my other question I was trying to achieve the same thing between the pre-motor cortex, an Ensemble, and the motor cortex, which also had a scaling node between them.

No. The solver argument just allows you to override the default solver that is used when creating a nengo.Connection. By default, the solver used is nengo.solvers.LstsqL2(weights=False).

The purpose of the solver is to, given a transform and function for a Nengo connection, to solve for the weight matrix that performs this transform + function. By default, the function being solved for is the identity function, and the transform is 1. By doing this, the default connection makes a communication channel, where the input value is “pass” from one ensemble to the next unchanged.

The weights=True or weights=False parameter of the solver determines what part of the weight matrix is being solved for. When weights=False, what is being solved for are the ensemble decoders (see this NEF documentation) that will compute the desired function + transform.
When weights=True, the decoders are combined with the encoders of the post ensemble to form the full (neuron-to-neuron) weight matrix.

From my explanation above, this code would create a communication channel between ens1 and ens2, but, instead of using decoders and encoders to compute the connection, it would generate the entire weight matrix.

Working with ensemble arrays, particularly when you want to do specific neuron-to-neuron connections get really tricky, because an ensemble array is really a bunch of ensembles grouped together. What this means is that the values represented by each ensemble do not interact with the other ensembles in the ensemble array. If the combined number of neurons in the ensemble array is relatively small (<5000), I would try using a regular ensemble instead of an ensemble array.

If the node is simply applying a scalar to the transmitted values, then it can be rolled into one nengo.Connection. For example:

scale = nengo.Node(lambda t, x: x*2, size_in=1)
ens1 = nengo.Ensemble(100, 1)
ens2 = nengo.Ensemble(100, 1)
nengo.Connection(ens1, scale, synapse=None)
nengo.Connection(scale, ens2)

is equivalent to:

ens1 = nengo.Ensemble(100, 1)
ens2 = nengo.Ensemble(100, 1)
nengo.Connection(ens1, ens2, transform=2)

The beauty of Nengo is that it should be able to do all of the weights optimization for you. If you could provide some example code, and describe what exactly you are trying to achieve, perhaps I can advise you on the direction you should take.