Probing an SPA network using a Node or State

Hi all,

Looking at various examples and documentations on nenge SPA, i see that output probing seems to be done exclusively on the State.output object, that being a Node.

I see why this happens when a network is contructed of only SPA objects, but what if you employ a more base nengo approach of bulding a network using only Ensembles, which you feed a signal using the spa.Input and spa.State objects. The first thing that happens is to connect this to an Ensemble, which is then further used as the de facto model input. Now, I wonder how I should probe the output Ensemble of such a model. I see that I could connect it to an spa.State object. But, given that my model does all its “magic” by means of Ensembles (and not spa.States) this feels a bit unnecessary.

Below I provide a small example model. I look at different ways to probe the ensemble such that I can use it to compare it to a vocabulary: a Node, and State,input, and State.output objects.

import nengo
import nengo.spa as spa
import numpy as np
import matplotlib.pyplot as plt

dim = 64

rng_vocabs = np.random.RandomState()
vocab = spa.Vocabulary(dim, rng=rng_vocabs)
vocab.parse('DOG')

def input_func(t): # input function visual stimuli
    if t > 0 and t < 1:
        return "DOG"
    elif t > 2 and t < 3: 
        return "DOG"
    else:
        return "0"

model = nengo.Network()
with model:
    model.stim = spa.State(dim, vocab = vocab)
    model.inp = spa.Input(stim = input_func)

    model.ens_in = nengo.Ensemble(200, dim)
    
    # connect semantic pointer input to input ensemble
    nengo.Connection(model.stim.output, model.ens_in)

    # This is the ensemble that I wish to probe, to see 
    # which semantic pointer its activity represents
    model.ens_out = nengo.Ensemble(200, dim)

    nengo.Connection(model.ens_in, model.ens_out)

    model.output_state = spa.State(dim, vocab=vocab)

    nengo.Connection(model.ens_out, model.output_state.input, synapse=0.01)

    model.output_node = nengo.Node(None, size_in=dim)

    nengo.Connection(model.ens_out, model.output_node, synapse=0.01)

    output_state_input_probe = nengo.Probe(model.output_state.input)
    output_state_output_probe = nengo.Probe(model.output_state.output)
    output_node_probe = nengo.Probe(model.output_node)

sim = nengo.Simulator(model)

sim.run(4)

state_input = sim.data[output_state_input_probe]
state_output = sim.data[output_state_output_probe]
node = sim.data[output_node_probe]

# plotting code
ymin, ymax = -3.5,3.5
plt.figure(figsize=(16, 10))

t = sim.trange()

plt.plot(t, spa.similarity(state_input, vocab)+2, label="state.input (shifted +2)")
plt.plot(t, spa.similarity(state_output, vocab), label="state.output")
plt.plot(t, spa.similarity(node, vocab)-2, label="node (shifted -2)")

plt.title("Similarity to the single vocab item at different probe locations")
plt.xlabel("Time")
plt.xlim(right=t[-1])
plt.ylim(ymin, ymax)
plt.grid(True)
plt.legend()
plt.tight_layout()

plt.show()

When the synapse parameter for the connections that run form the output ensemble are set to 0, we see the following:

data from the probes (seem to) contain the exact same signal. Not unexpected. state.input and node are both just Nodes to which the output ensemble connects. It also comes as no suprise that we see the same for state.input and state.ouput as spa.State is described in the documentation as being able to “capable of representing a single vector, with optional memory”.

Changing the synapse value to 0.01 gives the following:

Again, node and state.input are exactly the same, the additional noise seen at state.ouput is not unexpected, given that State.input and State.ouput are connected through Ensembles.

My question thus is: If my neural model is constructed of only ensembles, and the SPA (and its nengo implementated objects) are used only to provide input to an input ensemble, which of the three described options is best for me if I wish to probe which semantic pointer (or a number of vocabulary items) the output ensemble best represents?

Thank you,

Chiel

Using a spa.State will store the Semantic Pointer in an additional network of neural ensembles. Thus, probing the output of such a state will have additional noise. Thus, I would use a node to probe the ensemble. If you are only probing a single ensemble (and not a multiple ensembles as used by spa.State), you can even probe the ensemble directly. Probing the input of a spa.State is equivalent to probing a with node (as the input is a node), but you don’t really need the rest of the spa.State (i.e. you’re are using computational resources to simulate neurons that are irrelevant).

Aside: maybe I overlooked it, but it seems that in the example above model.inp isn’t connected to anything. Also note that nengo.spa is deprecated in favor of the nengo_spa module. You find more details on why and how switch in the documentation.

1 Like

The model should indeed been have defined as model = spa.SPA() instead of model = nengo.Network() in this case to provide the proper input. The output looks slightly different, and actually as one would expect :sweat_smile: