Selection function

is it possible to create a multiplexer type selection network where outputs can be selected via the implemented network? I’m quite new to nengo…please help me out
For example I give 3 inputs to 3 sub-networks but I only want to take 2 of them and truncate the third. what sort of approach would be helpful…thanks In advance

:grinning:

1 Like

Hi @rahul, and welcome to the Nengo forums! :smiley:

To answer your question:

Yes, you can! You can use inhibitory gating to selectively inhibit the activities of neural ensembles that you do not want to affect the output. This type of inhibitory gating is used extensively in the Spaun2 model to control the flow of information within the network. You can find the code for a selection network (many inputs-to-one output) and routing network (one input-to-many outputs) here. Note that with inhibitory gating, the inhibitory signal is the inverse of the selection signal. By modifying the configuration of what is inhibited with a specific input, you can do things like select 2 of 3 inputs (compared to the many-to-1 or 1-to-many configurations of the Spaun network).

If you want a more textual and schematic-based description of how these networks work, check out section 3.3.8.3 in my Spaun2 thesis.

1 Like

i really am unable to completely understand the router.py, although the section 3.3.8.3 was clear please is it possible to give some explanation to it. and if it’s ok please point out where is the actual selections taking place…

on a basic level, here is what I wanted to do for one input (in is a fixed output node)
if in==1:
n.Connection(in1, A)
elif in==2:
n.Connection(in1, B)
the output I am getting is constant zero. for both ensembles A,B with both cases of in value
please help me sort it out

Both the router and selector networks work on two concepts: addition, and inhibition.

Consider the following Nengo network:

with nengo.Network() as model:
    ensA = nengo.Ensemble(50, 1)
    ensB = nengo.Ensemble(50, 1)
    ensC = nengo.Ensemble(50, 1)
    
    nengo.Connection(ensA, ensC)
    nengo.Connection(ensB, ensC)

On the surface, if you probe the output of ensC, you’ll see that it will be the result of adding the output of ensA and ensB (and this should be the case, since this is just the addition network).

What becomes interesting is when you add inhibition to the network:

with nengo.Network() as model:
    ensA = nengo.Ensemble(50, 1)
    ensB = nengo.Ensemble(50, 1)
    ensC = nengo.Ensemble(50, 1)
   
    nengo.Connection(ensA, ensC)
    nengo.Connection(ensB, ensC)

    inhibA = nengo.Node()
    inhibB = nengo.Node()
    nengo.Connection(inhibA, ensA.neurons, transform=[[-2]] * ensA.n_neurons)
    nengo.Connection(inhibB, ensB.neurons, transform=[[-2]] * ensB.n_neurons)

Now, let’s examine what happens if inhibA is 1. When the output of the inhibA node is 1, the ensA ensemble gets inhibited (i.e., the output of ensA becomes 0). Since the output of ensC just adds the value of ensA and ensB, the new result is 0 + ensB. We can do the same analysis when inhibA = 0 and inhibB = 1. In that case, the output of ensC would be ensA + 0. So, from just this simple addition, the addition network turns into a selection network, one that selects the output of ensA or ensB based on the value of inhibA or inhibB.

One difference between the simple selection network above and your desired implementation is that the network requires as many inhibitory inputs as there are selection inputs. In contrast, you would like to have the network select an output based on just one in value. This feature is also achievable in Nengo, but not in the way you think. Here’s where I’d recommend you shift your thinking from a purely scalar base (i.e., in==1 or in==2, etc.) to a vector based approach. If you play around with the inhibition network (the one in the Nengo examples), you’ll notice that the ensembles’ outputs are inhibited when the input is a positive value. This makes the network unable to distinguish between input values like “1” or “2”. Rather, inhibition works best if the inhibitory inputs are essentially binary (there, or not there). Thus, to have a singular input control the selection network, we should instead combine the individual inhibitory inputs into one vector input:

with nengo.Network() as model:
    ensA = nengo.Ensemble(50, 1)
    ensB = nengo.Ensemble(50, 1)
    ensC = nengo.Ensemble(50, 1)
   
    nengo.Connection(ensA, ensC)
    nengo.Connection(ensB, ensC)

    inhib = nengo.Node()  # Make this input 2D
    nengo.Connection(inhib[0], ensA.neurons, transform=[[-2]] * ensA.n_neurons)
    nengo.Connection(inhib[1], ensB.neurons, transform=[[-2]] * ensB.n_neurons)

Now, when inhib=[1,0], the output is ensB, and when inhib=[0,1] the output is ensA. You can do a little bit of connection magic (this is what the routing network does) to “invert” the inhibitory signal into a selection signal. This is done by connecting the inhibitory input to the undesired outputs rather than to the desired output (I’ve expanded the example to 3 inputs to demonstrate):

with nengo.Network() as model:
    ensA = nengo.Ensemble(50, 1)
    ensB = nengo.Ensemble(50, 1)
    ensC = nengo.Ensemble(50, 1)
    out = nengo.Ensemble(50, 1)
   
    nengo.Connection(ensA, out)
    nengo.Connection(ensB, out)
    nengo.Connection(ensC, out)

    inhib = nengo.Node()  # Make this input 3D
   
    # Select ensA
    nengo.Connection(inhib[0], ensB.neurons, transform=[[-2]] * ensB.n_neurons)
    nengo.Connection(inhib[0], ensC.neurons, transform=[[-2]] * ensC.n_neurons)

    # Select ensB
    nengo.Connection(inhib[1], ensA.neurons, transform=[[-2]] * ensA.n_neurons)
    nengo.Connection(inhib[1], ensC.neurons, transform=[[-2]] * ensC.n_neurons)

    # Select ensC
    nengo.Connection(inhib[2], ensA.neurons, transform=[[-2]] * ensA.n_neurons)
    nengo.Connection(inhib[2], ensB.neurons, transform=[[-2]] * ensB.n_neurons)

@xchoo thanks a lot for the reply.
the reason that a scalar value is not being detected is that the value gets approximated to the nearest decimal for example 1.999999456— for “2” something like that or are there any other reasons.

No… The reason why scalars are hard to differentiate using the inhibition method I mentioned above is not because of rounding. Rather, it is because of how neurons and inhibition interact with each other. When Nengo simulates the activity of neurons, it does this in a biologically-realistic way. That is to say, the rate at which a neuron spikes is dependent on the amount of input current being fed into the neuron. I sort of describe the process here.

In Nengo, one can inhibit the activity of a neuron by providing a negative current to the neuron. The negative current will “suppress” any positive current incoming to the neuron and without any net input current into the neuron, the neuron will not spike, and thus, it will not produce any output (i.e., the output is inhibited).

If you look at the Nengo code for an inhibitory connection:

nengo.Connection(inhib[0], ensB.neurons, transform=[[-2]] * ensB.n_neurons)

You will notice that a positive inhibitory signal of anything > 1 will cause negative current to be fed into the neuron, thus inhibiting it. It is for this reason that the selection network will not be able to differentiate between a “1” and “2” or any other positive input of sufficient value to inhibit the neurons.