How to efficiently create nodes for each features in your dataset? Can we use size_in in nengo.Node?

Hello, I am working with Iris dataset. Iris dataset as 4 features: sepal length, sepal width, petal length and petal width. I have created four nodes for each features and hence four functions

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

data = np.genfromtxt('Iris.csv', delimiter=',', usecols=(1, 2, 3, 4))

# normalization to unity of each pattern in the data
features = np.apply_along_axis(lambda x: x / np.linalg.norm(x), 1, data[1:len(data), :])

dt = 0.001

def sepal_length(t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, 0]

def sepal_width(t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, 1]

def petal_length(self, t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, 2]

def petal_width(self, t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, 3]

with model:
    data_feeder = DataFeeder()
    feature_1 = nengo.Node(output=sepal_length, label="Features 1")
    feature_2 = nengo.Node(output=sepal_width, label="Features 2")
    feature_3 = nengo.Node(output=petal_length, label="Features 3")
    feature_4 = nengo.Node(output=petal_width, label="Features 4")

    feature_1_probe = nengo.Probe(feature_1)
    feature_2_probe = nengo.Probe(feature_2)
    feature_3_probe = nengo.Probe(feature_3)
    feature_4_probe = nengo.Probe(feature_4)

    ens = nengo.Ensemble(n_neurons=10, dimensions=1, label="Encoder")
    ens_probe = nengo.Probe(ens)
    ens_spikes = nengo.Probe(ens.neurons)

    nengo.Connection(feature_1, ens)
    nengo.Connection(feature_2, ens)
    nengo.Connection(feature_3, ens)
    nengo.Connection(feature_4, ens)

dt = 1e-3
with nengo.Simulator(model, dt=dt) as sim:
    sim.run(dt * 1000) #14900
    spike_count = np.sum(sim.data[ens_spikes] > 0, axis=0)

print(f'spike counts are {spike_count}')

Right now I have only four features, so it is alright to have 4 functions for each features (although it is inefficient). But what if I have a csv dataset with 50 features and I want to create a node for each feature?

Is there an efficient way to create nodes for each features in your dataset? In node.py (source code for nengo.Node), it is written

size_in : int
The number of dimensions for incoming connection.

Can we use size_in in nengo.Node?

How, when and why to use size_in in nengo.Node?

I did this:

import nengo
import matplotlib.pyplot as plt
from nengo.utils.ensemble import sorted_neurons
from nengo.utils.matplotlib import rasterplot
import numpy as np

# reading the iris dataset in the csv format

data = np.genfromtxt('Iris.csv', delimiter=',', usecols=(1, 2, 3, 4))
# normalization to unity of each pattern in the data

features = np.apply_along_axis(lambda x: x / np.linalg.norm(x), 1, data[1:len(data), :])

# loading the labels
target = np.genfromtxt('Iris.csv',
                       delimiter=',',
                       usecols=[5],
                       dtype=str)

target = target[1:len(target)]
labels = np.unique(target)
print(labels)


def feat(t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, :]


model = nengo.Network()

with model:
    feature_1 = nengo.Node(output=feat, size_in=4 ,size_out=4,label="Features 1")
    feature_1_probe = nengo.Probe(feature_1[0])
    feature_2_probe = nengo.Probe(feature_1[1])
    feature_3_probe = nengo.Probe(feature_1[2])
    feature_4_probe = nengo.Probe(feature_1[3])

    ens = nengo.Ensemble(n_neurons=10, dimensions=1, label="Encoder", neuron_type=nengo.LIF(amplitude=10))
    ens_probe = nengo.Probe(ens)
    ens_spikes = nengo.Probe(ens.neurons)

    nengo.Connection(feature_1[0], ens)
    nengo.Connection(feature_1[1], ens)
    nengo.Connection(feature_1[2], ens)
    nengo.Connection(feature_1[3], ens)

dt = 1e-3
with nengo.Simulator(model, dt=dt) as sim:
    sim.run(dt * 1000) #14900
    spike_count = np.sum(sim.data[ens_spikes] > 0, axis=0)

print(f'spike counts are {spike_count}')

I got the error message

nengo.exceptions.ValidationError: Node.output: output function ‘<function feat at 0x7fce166f7760>’ is expected to accept exactly 2 arguments (time, as a float and data, as a NumPy array)

I also tried

ens = nengo.Ensemble(n_neurons=10, dimensions=4, label=“Encoder”)

It didn’t work.

Yes. In Nengo, both nengo.Node and nengo.Ensemble objects have a notion of representational dimensionality. That is to say, the values represented by these objects can have more than one dimension. For example, you can write a Nengo node to output a four dimensional value, which each dimension representing one of the four features:

def features(t):
    ix = int(t / dt) % features.shape[0]
    return features[ix, :]

with model:
    feature_node = nengo.Node(features)  # This will output a 4-dimensional value

You can then use Numpy style array slicing / indexing if you need to connect to / probe subsets of these dimensions:

with model:
    feature_1_ens = nengo.Ensemble(50, 1)  # Ensemble representing feature 1
    nengo.Connection(feature_node[0], feature_1_ens)

    # Probe features 2 and 3 of feature_node
    nengo.Probe(feature_node[1:2])

The size_in parameter of a Node determines how many dimensions the Node is expecting as an input value. For nodes that just output data, this value should be left as default (iirc, it’s 0). This parameter is really only used for Nodes that take in an input signal (and does something to it). As an example, here’s a node that takes in a 2D signal, and writes it to file:

def write_func(t, x):
    file.write(x)

with model:
    ens_2D = nengo.Ensemble(50, 2)
    write_node = nengo.Node(write_func, size_in=2)
    nengo.Connection(ens_2D, write_node)

When you create a node with a non-zero size_in value, the function you pass to the node (to run) needs to accept 2 arguments: t, and x (or data). If you look at the write_func function I defined above, this is exactly the case. Additionally, the x argument will be a Numpy array that has a dimensionality of size size_in. So, if size_in=2, then the values of x are 2-dimensional.

The error you received:

nengo.exceptions.ValidationError: Node.output: output function ‘<function feat at 0x7fce166f7760>’ is expected to accept exactly 2 arguments (time, as a float and data, as a NumPy array)

is telling you that the function your provided the node (feat) does not accept both t and x, and thus Nengo is unable to properly build your model.

Thank you for your reply :slight_smile: