How to control parameters of functions used for neural connections of ensembles

Dear all

I have to control a complex neural connection between a syllable oscillator, i.e.:

    SYLL01_oscillator = nengo.Ensemble(n_neurons=m, dimensions=2, intercepts=nengo.dists.Uniform(0.3,1)) 

and a ramp function integrator, i.e.:

    BAP_syll01_conn1 = nengo.Ensemble(n_neurons=Nc, dimensions=1, intercepts=nengo.dists.Uniform(0.3, 1)) 

The function is defined as follows:

BAP_func_sa_lab_constr1 = piecewise({0:[0], 0.2:[1.0], 0.5:[0]}) # C initial: /b/

def ramp_t(x):
    theta = math.atan2(x[1], x[0])
    t = theta/ (2*np.pi) + 0.5
    return t 

def BAP_signal_1(x): 
    return BAP_func_sa_lab_constr1(ramp_t(x))

and the neural connection between oscillator and integrator is:

nengo.Connection(SYLL01_oscillator, BAP_syll01_conn1, function=BAP_signal_1)

My question is:

How can I modify my model for

piecewise({0:[0], 0.2:[1.0], 0.5:[0]}

because the. values t1 = 0.2 and ampl1 = 1.0, and t2 = 0.5 need to be a result of
decoding neuron ensembles like

val_t1 = nengo.Ensemble (n_neurons=Nc, dimensions=1)
val_t2 = Nengo Ensemble (n_neurons=Nc, dimensions=1)
ampl1 = Nengo Ensemble (n_neurons=Nc, dimensions=1)

and how can I now decode ensemble activity in values (without probing; probing is later!)
and how can I feed the stepwise function during run of the simulation ?

Kind regards
Bernd

Hi @bernd,

I’m not entirely sure what you are trying to accomplish. From your code, it seems like you have constructed a connection between an oscillator and an integrator with a specific function applied to the connection? And it looks like this function takes the 2D coordinate point of the oscillator, and turns it into a ramp signal, which is then used to determine what the output of the piecewise function should be?
So… in essence, what you are looking to do is to have something generate a customizable piecewise function that you can feed into the integrator? Are you looking to have this entirely in neurons?

If you are okay with using a nengo.Node to make a customizable piecewise function, then something like this should suffice:

def custom_piecewise(t, x):
    t1, t2, ampl = x
    if t < t1 or t >= t2:
        return 0
    else:
        return ampl

pw_node = nengo.Node(size_in=3, output=custom_piecewise)
nengo.Connection(val_t1, pw_node[0])
nengo.Connection(val_t2, pw_node[1])
nengo.Connection(ampl1, pw_node[2])
nengo.Connection(pw_node, BAP_syll01_conn1)

If, however, you want to implement it in neurons, there are a few ways to do it. I gave it some thought, and I think the most straightforward way is to leverage what you already have implemented in your network. Right now, you have the oscillator connected to the integrator with a connection function that turns the oscillator output into the piecewise signal:

oscillator --> connection function --> integrator

My thought is to introduce an intermediary ensemble to modify the output of the oscillator, like so:

val_t1 ------------+
val_t2 ----------+ |
ampl ----------+ | |
               V V V
oscillator --> modulator --> connection function --> integrator

The goal of this “modulator” ensemble is to adjust the output of the oscillator based off the values of val_t1, val_t2, and ampl. For example, let’s say the oscillator takes 1s to do one cycle. Let’s also say that in the connection function, you configure it so that the output is like this piecewise({0:[0], 0.5:[1.0], 1.0:[0]} (so, default values are val_t1=0.5, val_t2=1, ampl=1). Let’s also assume the oscillator starts at [0, 1] and cycles counter clockwise.

Now, let’s say you changed the output of the val and ampl ensembles to something different. Suppose you wanted val_t1=0.25, val_t2=0.5, ampl=1. What the “modulator” ensemble should be tasked to compute is the mapping between the input oscillator values, and the output oscillator values. So, if the modulator ensemble maps an input of [1, 0] to an output of [0, -1], and an input of [0, -1] to an output of [0, 1], we should observe that as the oscillator cycles, it will produce the modified piecewise function that we wanted. Do I make any sense?

An alternate approach to this is to connect several ensembles (some with thresholds, some that are integrators, etc.) to form the piecewise function. The dynamics of these ensembles can then be controlled by the outputs of val_t1, val_t2, and ampl. As an example, we could use the output of ampl to get the amplitude of the piecewise function. Then, use a fast-decaying integrator (where the decay time is controlled by val_t1, which should be doable if val_t1 determines the initial value in the integrator) to inhibit ampl. This sets up the initial “0” value for the time until val_t1. For val_t2, we could have a regular integrator integrate to a specific threshold. Once it reaches this threshold, it triggers an inhibition on ampl to then bring the function back to 0.

As to your question:

I’m not entirely sure what you are asking here… In Nengo, if you use “standard” connections (i.e., not connecting to the .neurons object), the values being transmitted between ensembles is already decoded.

Hi Xuan
thank you for your detailed answer.
May be you did not understand my problem because it was a kind of trivial.
Yes, you understood my question correctly.
The solution which is offered by you will help me:
the main point is: to reformulate the piecewise function in a way that I can input parameter values to that function.
I can do that now, by using your custom_piecewise function and by forwarding ensemble activities into the pw_node construct.

The new idea or take-away message for me is, that I can use nengo.Node not just as an artificial construct to prompt input in a neuron model (into a neuron ensemble at the perceptual input part of a model) but I as well can use nengo.Node “in the flow of transformations” appearing in a complex model.

In my model: the oscillator is already started by the action selection component (BG-thal) and now values are added from a memory component in order to activate the correct ramp signals. These values are the input to the piecewise function.

One last question:
Is it biologically realistic to include nengo.Node , i.e., to include “NEF-nodes” at any processing stage in a complex model and not just at the sensory input part of a model?
For me it is still unclear how the nengo.Node can be interpreted from a neurobiologically point of view. I understand the notion of neurons and connections building up all components of a brain model like neuron ensembles and neural connections but I am not sure how to interpret a Node in a neurobiologically way in terms of neurons and connections.

Best
Bernd

From a biological standpoint, the nengo.Node is quite artificial. It’s like dropping a computer chip inside the brain to do additional computation, so, no, a nengo.Node is not biologically realistic.
There is one instance where you can consider the nengo.Node biologically realistic though, and that’s when:

  • The nengo.Node has no output function assigned to it
  • And the output connections from the node only do a transform and not a function.

In the above instance, since transforms are just matrix multiplications, the matrix multiplication can be “folded” into the connection weights, essentially removing the nengo.Node, and embedding all of the information within the weights itself. Some of the Nengo backends (e.g., NengoLoihi) do this to optimize the network.

Hi Xuan
thank you for that response.
I so far thought that a connection (.A, B, transform= …, function=…) in both cases (transform and function) resolves the connection statement in a biologically realistic way by just adapting connection weights from ensemble A towards ensemble B?
Regards
Bernd

Yes, @bernd, but I should clarify that the statement you made (i.e., using both transform and function will result in a biologically realistic connection) is only applicable if A is a nengo.Ensemble.

If A is a nengo.Node (as is the case in my example custom_piecewise code, where the last connection is from the node), then this is no longer the case. Having a node compute a function in a connection makes the connection not biologically plausible.