spa-question-answering-memory

A spiking SPA model that binds colour × shape into a bound pair, stores it in a recurrent working memory, and answers questions about the stored pair after the original inputs have gone quiet.

Description

This is example 4 of chapter 5 of How to Build a Brain (Eliasmith, 2013), distributed with nengo-gui as 4-spa_question-memory.py. It extends the spa-question-answering model by adding a recurrent spa.State(dim, feedback=1) named memory and rewiring the cortical actions so the bound pair persists after stimulus offset.

The cortical actions are:

D = A * B          # bind colour (A) to shape (B)
memory = D         # write the bound pair into recurrent memory
E = memory * ~C    # answer cue questions from memory

Stimulation is sequential rather than alternating:

  • t ∈ [0, 0.25)A=RED, B=CIRCLE
  • t ∈ [0.25, 0.5)A=BLUE, B=SQUARE
  • t ≥ 0.5 — inputs zero; cues start cycling and memory must answer

By the end of the input phase, memory holds (approximately) RED * CIRCLE + BLUE * SQUARE. After that, asking C = SQUARE returns something most similar to BLUE; asking C = BLUE returns SQUARE; and so on.

The model uses 32-dimensional semantic pointers — small enough for fast CI builds while still keeping vocabulary similarities clean.

A note on nengo.spa vs nengo_spa

This script uses the legacy nengo.spa module that still ships inside core Nengo. The newer, separately maintained nengo_spa package has a redesigned API. Both run today, but new submissions are generally encouraged to prefer nengo_spa; this submission preserves the original tutorial verbatim.

Run it

In NengoGUI (the intended way to view it):

pip install nengo-gui
nengo spa_question_answering_memory.py

Or just at the command line:

python spa_question_answering_memory.py

How it works

The trick is the feedback=1 argument to spa.State: the state gets a recurrent connection from its own output back to its input with unit gain, which approximates a perfect integrator. Inputs from the cortical action memory = D accumulate; once those inputs go to zero, the state holds the most recent value (up to neural drift). The cue is then used to project back out a single attribute via the inverse-binding action E = memory * ~C, just as in the no-memory version.

spa.Cortical compiles each action string into the equivalent set of always-on nengo.Connections — no action selection, no gating, the bindings and memory writes are continuous.

Citation

@book{eliasmith2013htb,
  author    = {Eliasmith, Chris},
  title     = {How to Build a Brain: A Neural Architecture for Biological Cognition},
  publisher = {Oxford University Press},
  year      = {2013}
}

License

GPLv2 (see LICENSE).

Figures

qa_memory_dynamics

2 seconds of the SPA question-answering-with-memory network, produced by running spa_question_answering_memory.py directly.

The first 0.5 s presents two colour×shape bindings to the network with no cue; the bindings accumulate in the memory. After 0.5 s the colour and shape inputs go silent and only the cue varies. The network must use memory to recover the answers.

A — colour input. RED (0 – 0.25 s) then BLUE (0.25 – 0.5 s), then ZERO for the rest of the run.

B — shape input. CIRCLE then SQUARE on the same schedule, then ZERO.

C — cue. Stays at ZERO for the first 0.5 s while the bindings are presented, then cycles through ZERO, CIRCLE, RED, ZERO, SQUARE, BLUE.

memory — bound-pair contents. RED * CIRCLE builds up during 0 – 0.25 s and persists; BLUE * SQUARE builds up during 0.25 – 0.5 s and persists. Both pairs remain in memory throughout the question phase thanks to the State's recurrent connection.

E — recovered answer. Computed as memory * ~cue. When the cue is CIRCLE the network recovers a mixture peaked on RED; when the cue is RED it recovers CIRCLE; and so on. The mixture is "fuzzier" than in the cortical-only variant because both bound pairs are in the memory at once.