Implementing a custom moduleΒΆ
This example demonstrates how custom SPA modules can be created that can take advantage of all the features of the SPA syntax. We will adapt the InputGatedMemory
from nengo
.
[1]:
import nengo
import nengo_spa as spa
Implementing a SPA module requires a few steps:
Implement a class inheriting from
spa.Network
.Use
VocabularyOrDimParam
to declare class variables for storing vocublary parameters. This will allow the usage of integer dimensions instead of vocabularies without any further additions.Declare inputs and outputs with their respective vocabularies.
Not that parameters in SPA modules should usually be defined as readonly because changing them will usually not update the network accordingly.
[2]:
class GatedMemory(spa.Network):
# The vocabulary parameter.
vocab = spa.vocabulary.VocabularyOrDimParam("vocab", default=None, readonly=True)
# The number of neurons per dimensions.
neurons_per_dimension = nengo.params.IntParam(
"neurons_per_dimension", default=200, low=1, readonly=True
)
# Arguments assigned to parameters should be assigned
# nengo.params.Default as default value. This makes sure they work
# properly with nengo.Config. It is a good idea to pass on the keyword
# arguments **kwargs to the spa.Network constructor to allow the user to
# set the network label etc.
def __init__(
self,
vocab=nengo.params.Default,
neurons_per_dimension=nengo.params.Default,
**kwargs
):
super(GatedMemory, self).__init__(**kwargs)
# Assign parameter values
# If vocab is an integer dimension, the appropriate Vocabulary
# instance will assigned to self.vocab.
self.vocab = vocab
self.neurons_per_dimension = neurons_per_dimension
# Construct the network
with self:
self.mem = nengo.networks.InputGatedMemory(
self.neurons_per_dimension, self.vocab.dimensions
)
# Assign inputs to root object for easier referencing
self.input = self.mem.input
self.input_gate = self.mem.gate
self.input_reset = self.mem.reset
self.output = self.mem.output
# Declare inputs and outputs
# Use None as vocabulary for scalar inputs/outputs
self.declare_input(self.input, self.vocab)
self.declare_input(self.input_gate, None)
self.declare_input(self.input_reset, None)
self.declare_output(self.output, self.vocab)
We can then use our new module as we would any other module.
[3]:
dimensions = 32
with spa.Network() as model:
# The module can be configured
model.config[GatedMemory].neurons_per_dimension = 150
spa_in = spa.Transcode("OKAY", output_vocab=dimensions)
gate_in = nengo.Node(lambda t: 1 if t < 0.1 else 0)
g_mem = GatedMemory(dimensions)
# It can be in routing rules
spa_in >> g_mem
gate_in >> g_mem.input_gate