If you checkout the pes-synapse branch of nengo from the git repo, and have nengolib installed, then you can implement eligibilty trace like functionality using the following:
#NOTE: To run this file you must be on the pes-synapse branch of nengo!! <-----
import numpy as np
import nengo
from nengolib.signal import z
dt = 0.001
model = nengo.Network()
model.config[nengo.Ensemble].neuron_type = nengo.LIFRate()
with model:
state = nengo.Node(np.cos)
target = nengo.Node(lambda t: -np.cos(t))
ensA = nengo.Ensemble(n_neurons=100, dimensions=1)
ensB = nengo.Ensemble(n_neurons=100, dimensions=1)
outA = nengo.Node(size_in=1)
outB = nengo.Node(size_in=1)
nengo.Connection(state, ensA)
nengo.Connection(state, ensB)
# need to use a relatively low learning rate, because the delay means
# that any learning effects won't be noticed for a while
learning_rate = 1e-5
num_timesteps_delayed = 1000 # default sim timestep is 0.001, so .1s delay
# set up learning without the pre_synaptic filtering
learn_connA = nengo.Connection(
ensA, outA,
learning_rule_type=nengo.PES(learning_rate=learning_rate))
# connect in training signal with num_timesteps_delay
nengo.Connection(outA, learn_connA.learning_rule, transform=1,
synapse=z**(-num_timesteps_delayed))
nengo.Connection(target, learn_connA.learning_rule, transform=-1,
synapse=z**(-num_timesteps_delayed))
# set up learning with the pre_synaptic filtering
learn_connB = nengo.Connection(
ensB, outB,
learning_rule_type=nengo.PES(pre_synapse=z**(-num_timesteps_delayed-1),
learning_rate=learning_rate))
# connect in training signal with num_timesteps_delay
nengo.Connection(outB, learn_connB.learning_rule, transform=1,
synapse=z**(-num_timesteps_delayed))
nengo.Connection(target, learn_connB.learning_rule, transform=-1,
synapse=z**(-num_timesteps_delayed))
compare = nengo.Node(size_in=5)
nengo.Connection(state, compare[0])
nengo.Connection(state, compare[1], synapse=z**(-num_timesteps_delayed))
nengo.Connection(outA, compare[1], synapse=z**(-num_timesteps_delayed))
nengo.Connection(target, compare[2])
nengo.Connection(outA, compare[3])
nengo.Connection(outB, compare[4])
probe_target = nengo.Probe(target)
probeA = nengo.Probe(outA)
probeB = nengo.Probe(outB)
if __name__ == '__main__':
num_seconds = 100
half = int(num_seconds/2)
sim = nengo.Simulator(model)
sim.run(num_seconds)
target = sim.data[probe_target]
outA = sim.data[probeA]
outB = sim.data[probeB]
print('RMSE during second half of trial')
print('RMSE A: ', np.sqrt(np.mean((target[half:] - outA[half:])**2)))
print('RMSE B: ', np.sqrt(np.mean((target[half:] - outB[half:])**2)))
import matplotlib.pyplot as plt
plt.subplot(2, 1, 1)
plt.title('Learning with a delayed error signal')
plt.plot(sim.trange(), sim.data[probe_target], 'r--', lw=3)
plt.plot(sim.trange(), sim.data[probeA], 'b')
plt.legend(['target', 'without delay learning'])
plt.subplot(2, 1, 2)
plt.plot(sim.trange(), sim.data[probe_target], 'r--', lw=3)
plt.plot(sim.trange(), sim.data[probeB], 'g')
plt.legend(['target', 'with delay learning'])
plt.show()
note: it runs for about 100s (which takes a little bit) so you can see the stability of the learning using the delayed neural activity vs learning on the non-delayed neural activity.