Readings: Stewart et al.
Lots of contemporary neural models are quite simple
What happens when our models get more complex?
The brain needs a way to determine how to best use the finite resources it has.
Think about what happens when:
In all these cases, your brain needs to control the flow of information through it to solve the task.
One group of neurons for each action's utility $Q(s, a_i)$
What should the output be?
In [1]:
%pylab inline
import nengo
model = nengo.Network('Selection')
with model:
stim = nengo.Node(lambda t: [np.sin(t), np.cos(t)])
s = nengo.Ensemble(200, dimensions=2)
Q_A = nengo.Ensemble(50, dimensions=1)
Q_B = nengo.Ensemble(50, dimensions=1)
Q_C = nengo.Ensemble(50, dimensions=1)
Q_D = nengo.Ensemble(50, dimensions=1)
nengo.Connection(s, Q_A, transform=[[1,0]])
nengo.Connection(s, Q_B, transform=[[-1,0]])
nengo.Connection(s, Q_C, transform=[[0,1]])
nengo.Connection(s, Q_D, transform=[[0,-1]])
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qa_p = nengo.Probe(Q_A)
qb_p = nengo.Probe(Q_B)
qc_p = nengo.Probe(Q_C)
qd_p = nengo.Probe(Q_D)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(3.)
In [2]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend()
figure(figsize=(8,8))
plot(t, sim.data[qa_p], label='Q_A')
plot(t, sim.data[qb_p], label='Q_B')
plot(t, sim.data[qc_p], label='Q_C')
plot(t, sim.data[qd_p], label='Q_D')
legend(loc='best');
That behavior makes a lot of sense
It's annoying to have all those separate $Q$ neurons
In [5]:
import nengo
model = nengo.Network('Selection')
with model:
stim = nengo.Node(lambda t: [np.sin(t), np.cos(t)])
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(3.)
In [6]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend()
figure(figsize=(8,8))
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best');
Yay, Network Arrays make shorter code!
Back to the model: How do we implement the $max$ function?
In [7]:
import nengo
def maximum(x):
result = [0,0,0,0]
result[np.argmax(x)] = 1
return result
model = nengo.Network('Selection')
with model:
stim = nengo.Node(lambda t: [np.sin(t), np.cos(t)])
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
Qall = nengo.Ensemble(400, dimensions=4)
Action = nengo.Ensemble(200, dimensions=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(Qs.output, Qall)
nengo.Connection(Qall, Action, function=maximum)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
action_p = nengo.Probe(Action)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(3.)
In [8]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend()
figure()
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best')
figure()
plot(t, sim.data[action_p], label='Action')
legend(loc='best');
In [9]:
import nengo
model = nengo.Network('Selection')
with model:
stim = nengo.Node(lambda t: [.5,.4] if t <1. else [0,0] )
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
e = 0.1
i = -1
recur = [[e, i, i, i], [i, e, i, i], [i, i, e, i], [i, i, i, e]]
nengo.Connection(Qs.output, Qs.input, transform=recur)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(1.)
In [10]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend()
figure()
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best');
In [17]:
import nengo
model = nengo.Network('Selection')
with model:
stim = nengo.Node(lambda t: [.5,.4] if t <1. else [0,0] )
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
Action = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(Qs.output, Action.input)
e = 0.3
i = -1
recur = [[e, i, i, i], [i, e, i, i], [i, i, e, i], [i, i, i, e]]
# Let's force the feedback connection to only consider positive values
def positive(x):
if x[0]<0: return [0]
else: return x
pos = Action.add_output('positive', positive)
nengo.Connection(pos, Action.input, transform=recur)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
action_p = nengo.Probe(Action.output)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(1.)
In [18]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend(loc='best')
figure()
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best')
figure()
plot(t, sim.data[action_p], label='Action')
legend(loc='best');
e
?
In [25]:
%pylab inline
import nengo
def stimulus(t):
if t<.3:
return [.5,.4]
elif .3<t<.5:
return [.4,.5]
else:
return [0,0]
model = nengo.Network('Selection')
with model:
stim = nengo.Node(stimulus)
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
Action = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(Qs.output, Action.input)
e = .5
i = -1
recur = [[e, i, i, i], [i, e, i, i], [i, i, e, i], [i, i, i, e]]
# Let's force the feedback connection to only consider positive values
def positive(x):
if x[0]<0: return [0]
else: return x
pos = Action.add_output('positive', positive)
nengo.Connection(pos, Action.input, transform=recur)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
action_p = nengo.Probe(Action.output)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(1.)
'''import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(s)
viz.value(Qs.output)
viz.value(Action.output)
viz.start()'''
Out[25]:
In [26]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend(loc='best')
figure()
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best')
figure()
plot(t, sim.data[action_p], label='Action')
legend(loc='best');
e
too much?
In [ ]:
%pylab inline
import nengo
def stimulus(t):
if t<.3:
return [.5,.4]
elif .3<t<.5:
return [.3,.5]
else:
return [0,0]
model = nengo.Network('Selection')
with model:
stim = nengo.Node(stimulus)
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
Action = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(Qs.output, Action.input)
e = 0.2
i = -1
recur = [[e, i, i, i], [i, e, i, i], [i, i, e, i], [i, i, i, e]]
def positive(x):
if x[0]<0: return [0]
else: return x
pos = Action.add_output('positive', positive)
nengo.Connection(pos, Action.input, transform=recur)
def select(x):
if x[0]>=0: return [1]
else: return [0]
sel = Action.add_output('select', select)
aValues = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(sel, aValues.input)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
action_p = nengo.Probe(Action.output)
aValues_p = nengo.Probe(aValues.output)
s_p = nengo.Probe(s)
sim = nengo.Simulator(model)
sim.run(1.)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(s)
viz.value(Qs.output)
viz.value(aValues.output)
viz.start()
In [32]:
t = sim.trange()
plot(t, sim.data[s_p], label="state")
legend(loc='best')
figure()
plot(t, sim.data[qs_p], label='Qs')
legend(loc='best')
figure()
plot(t, sim.data[action_p], label='Action')
legend(loc='best')
figure()
plot(t, sim.data[aValues_p], label='Action Values')
legend(loc='best');
e
In [ ]:
%pylab inline
import nengo
def stimulus(t):
if t<.3:
return [.5,.4]
elif .3<t<.5:
return [.3,.5]
else:
return [0,0]
model = nengo.Network('Selection')
with model:
stim = nengo.Node(stimulus)
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
Action = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
nengo.Connection(Qs.output, Action.input)
e = 0.1
i = -1
recur = [[e, i, i, i], [i, e, i, i], [i, i, e, i], [i, i, i, e]]
def positive(x):
if x[0]<0: return [0]
else: return x
pos = Action.add_output('positive', positive)
nengo.Connection(pos, Action.input, transform=recur)
def select(x):
if x[0]>=0: return [1]
else: return [0]
sel = Action.add_output('select', select)
aValues = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(sel, aValues.input)
nengo.Connection(stim, s)
model.config[nengo.Probe].synapse = nengo.Lowpass(0.01)
qs_p = nengo.Probe(Qs.output)
action_p = nengo.Probe(Action.output)
aValues_p = nengo.Probe(aValues.output)
s_p = nengo.Probe(s)
#sim = nengo.Simulator(model)
#sim.run(1.)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(s)
viz.value(Qs.output)
viz.value(aValues.output)
viz.start()
And this gets harder to balance as the number of actions increases
But this is still a pretty standard approach
They tend to use a "kWTA" (k-Winners Take All) approach in their models
Any other options?
Then they found:
Activity in the GPi (output)
Leabra approach
Needs to do so quickly, and without the memory effects
Let's start with a very simple version
Note that both A and B have surround inhibition and local excitation that is the 'flipped' (in slightly different ways) on the way to the output
Now let's map that onto the basal ganglia
They showed that it qualitatively matches pretty well
So what happens if we convert this into realistic spiking neurons?
In [ ]:
%pylab inline
mm=1
mp=1
me=1
mg=1
ws=1
wt=1
wm=1
wg=1
wp=0.9
we=0.3
e=0.2
ep=-0.25
ee=-0.2
eg=-0.2
le=0.2
lg=0.2
D = 10
tau_ampa=0.002
tau_gaba=0.008
N = 50
radius = 1.5
import nengo
from nengo.dists import Uniform
model = nengo.Network('Basal Ganglia')
with model:
stim = nengo.Node([0]*D)
StrD1 = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(e,1),
encoders=Uniform(1,1), radius=radius)
StrD2 = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(e,1),
encoders=Uniform(1,1), radius=radius)
STN = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(ep,1),
encoders=Uniform(1,1), radius=radius)
GPi = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(eg,1),
encoders=Uniform(1,1), radius=radius)
GPe = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(ee,1),
encoders=Uniform(1,1), radius=radius)
nengo.Connection(stim, StrD1.input, transform=ws*(1+lg), synapse=tau_ampa)
nengo.Connection(stim, StrD2.input, transform=ws*(1-le), synapse=tau_ampa)
nengo.Connection(stim, STN.input, transform=wt, synapse=tau_ampa)
def func_str(x):
if x[0]<e: return 0
return mm*(x[0]-e)
strd1_out = StrD1.add_output('func_str', func_str)
strd2_out = StrD2.add_output('func_str', func_str)
nengo.Connection(strd1_out, GPi.input, transform=-wm, synapse=tau_gaba)
nengo.Connection(strd2_out, GPe.input, transform=-wm, synapse=tau_gaba)
def func_stn(x):
if x[0]<ep: return 0
return mp*(x[0]-ep)
stn_out = STN.add_output('func_stn', func_stn)
tr=[[wp]*D for i in range(D)]
nengo.Connection(stn_out, GPi.input, transform=tr, synapse=tau_ampa)
nengo.Connection(stn_out, GPe.input, transform=tr, synapse=tau_ampa)
def func_gpe(x):
if x[0]<ee: return 0
return me*(x[0]-ee)
gpe_out = GPe.add_output('func_gpe', func_gpe)
nengo.Connection(gpe_out, GPi.input, transform=-we, synapse=tau_gaba)
nengo.Connection(gpe_out, STN.input, transform=-wg, synapse=tau_gaba)
Action = nengo.networks.EnsembleArray(N, n_ensembles=D, intercepts=Uniform(0.2,1),
encoders=Uniform(1,1))
bias = nengo.Node([1]*D)
nengo.Connection(bias, Action.input)
nengo.Connection(Action.input, Action.output, transform=(np.eye(D)-1), synapse=tau_gaba)
def func_gpi(x):
if x[0]<eg: return 0
return mg*(x[0]-eg)
gpi_out = GPi.add_output('func_gpi', func_gpi)
nengo.Connection(gpi_out, Action.input, transform=-3, synapse=tau_gaba)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(StrD1.output)
viz.value(GPi.output)
viz.value(Action.output)
viz.start()
Notice that we are also flipping the output from [1, 1, 0, 1] to [0, 0, 1, 0]
Works pretty well
Dynamic Behaviour of a Spiking Model of Action Selection in the Basal Ganglia
Let's make sure this works with our original system
In [ ]:
%pylab inline
import nengo
from nengo.dists import Uniform
model = nengo.Network(label='Selection')
D=4
with model:
stim = nengo.Node([0,0])
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(stim, s)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
Action = nengo.networks.EnsembleArray(50, n_ensembles=D, intercepts=Uniform(0.2,1),
encoders=Uniform(1,1))
bias = nengo.Node([1]*D)
nengo.Connection(bias, Action.input)
nengo.Connection(Action.input, Action.output, transform=(np.eye(D)-1), synapse=0.008)
basal_ganglia = nengo.networks.BasalGanglia(dimensions=D)
nengo.Connection(Qs.output, basal_ganglia.input, synapse=None)
nengo.Connection(basal_ganglia.output, Action.input)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(s)
viz.value(basal_ganglia.output)
viz.value(Action.output)
viz.start()
In [ ]:
%pylab inline
import nengo
from nengo.dists import Uniform
model = nengo.Network(label='Selection')
D=4
with model:
stim = nengo.Node([0,0])
s = nengo.Ensemble(200, dimensions=2)
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(stim, s)
nengo.Connection(s, Qs.input, transform=[[1,0],[-1,0],[0,1],[0,-1]])
Action = nengo.networks.EnsembleArray(50, n_ensembles=D, intercepts=Uniform(0.2,1),
encoders=Uniform(1,1))
bias = nengo.Node([1]*D)
nengo.Connection(bias, Action.input)
nengo.Connection(Action.input, Action.output, transform=(np.eye(D)-1), synapse=0.008)
basal_ganglia = nengo.networks.BasalGanglia(dimensions=D)
nengo.Connection(Qs.output, basal_ganglia.input, synapse=None)
nengo.Connection(basal_ganglia.output, Action.input)
motor = nengo.Ensemble(100, dimensions=2)
nengo.Connection(Action.output[0], motor, transform=[[1],[0]])
nengo.Connection(Action.output[1], motor, transform=[[-1],[0]])
nengo.Connection(Action.output[2], motor, transform=[[0],[1]])
nengo.Connection(Action.output[3], motor, transform=[[0],[-1]])
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.value(s)
viz.value(basal_ganglia.output)
viz.value(Action.output)
viz.value(motor)
viz.start()
In [ ]:
%pylab inline
import nengo
from nengo.dists import Uniform
model = nengo.Network('Creature')
with model:
stim = nengo.Node([0,0], label='stim')
command = nengo.Ensemble(100, dimensions=2, label='command')
motor = nengo.Ensemble(100, dimensions=2, label='motor')
position = nengo.Ensemble(1000, dimensions=2, label='position')
scared_direction = nengo.Ensemble(100, dimensions=2, label='scared direction')
def negative(x):
return -x[0], -x[1]
nengo.Connection(position, scared_direction, function=negative)
nengo.Connection(position, position)
def rescale(x):
return x[0]*0.01, x[1]*0.01
nengo.Connection(motor, position, function=rescale)
nengo.Connection(stim, command)
D=4
Q_input = nengo.Node([0,0,0,0], label='select')
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(Q_input, Qs.input)
Action = nengo.networks.EnsembleArray(50, n_ensembles=D, intercepts=Uniform(0.2,1),
encoders=Uniform(1,1))
bias = nengo.Node([1]*D)
nengo.Connection(bias, Action.input)
nengo.Connection(Action.input, Action.output, transform=(np.eye(D)-1), synapse=0.008)
basal_ganglia = nengo.networks.BasalGanglia(dimensions=D)
nengo.Connection(Qs.output, basal_ganglia.input, synapse=None)
nengo.Connection(basal_ganglia.output, Action.input)
do_command = nengo.Ensemble(300, dimensions=3, label='do command')
nengo.Connection(command, do_command[0:2])
nengo.Connection(Action.output[0], do_command[2])
def apply_command(x):
return x[2]*x[0], x[2]*x[1]
nengo.Connection(do_command, motor, function=apply_command)
do_scared = nengo.Ensemble(300, dimensions=3, label='do scared')
nengo.Connection(command, do_scared[0:2])
nengo.Connection(Action.output[1], do_scared[2])
nengo.Connection(do_scared, motor, function=apply_command)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.slider(Q_input)
viz.value(command)
viz.value(Action.output)
viz.value(do_command)
viz.value(motor)
viz.value(position)
viz.start()
In [ ]:
%pylab inline
import nengo
from nengo.dists import Uniform
model = nengo.Network('Creature')
with model:
stim = nengo.Node([0,0], label='stim')
command = nengo.Ensemble(100, dimensions=2, label='command')
motor = nengo.Ensemble(100, dimensions=2, label='motor')
position = nengo.Ensemble(1000, dimensions=2, label='position')
scared_direction = nengo.Ensemble(100, dimensions=2, label='scared direction')
def negative(x):
return -x[0], -x[1]
nengo.Connection(position, scared_direction, function=negative)
nengo.Connection(position, position)
def rescale(x):
return x[0]*0.01, x[1]*0.01
nengo.Connection(motor, position, function=rescale)
nengo.Connection(stim, command)
D=4
Q_input = nengo.Node([0,0,0,0], label='select')
Qs = nengo.networks.EnsembleArray(50, n_ensembles=4)
nengo.Connection(Q_input, Qs.input)
Action = nengo.networks.EnsembleArray(50, n_ensembles=D, intercepts=Uniform(0.2,1),
encoders=Uniform(1,1))
bias = nengo.Node([1]*D)
nengo.Connection(bias, Action.input)
nengo.Connection(Action.input, Action.output, transform=(np.eye(D)-1), synapse=0.008)
basal_ganglia = nengo.networks.BasalGanglia(dimensions=D)
nengo.Connection(Qs.output, basal_ganglia.input, synapse=None)
nengo.Connection(basal_ganglia.output, Action.input)
do_command = nengo.Ensemble(300, dimensions=2, label='do command')
nengo.Connection(command, do_command)
nengo.Connection(Action.output[0], do_command.neurons, transform=-np.ones([300,1]))
nengo.Connection(do_command, motor)
do_scared = nengo.Ensemble(300, dimensions=2, label='do scared')
nengo.Connection(command, do_scared)
nengo.Connection(Action.output[1], do_scared.neurons, transform=-np.ones([300,1]))
nengo.Connection(do_scared, motor)
import nengo_viz
viz = nengo_viz.Viz(model)
viz.slider(stim)
viz.slider(Q_input)
viz.value(command)
viz.value(Action.output)
viz.value(do_command)
viz.value(do_scared)
viz.value(motor)
viz.value(position)
viz.start()
We build systems in cortex that give some input-output functionality
Example
In [ ]:
%pylab inline
import nengo
from nengo import spa
D = 16
def start(t):
if t < 0.05:
return 'A'
else:
return '0'
model = spa.SPA(label='Sequence_Module')
with model:
model.cortex = spa.Buffer(dimensions=D, label='cortex')
model.input = spa.Input(cortex=start, label='input')
actions = spa.Actions(
'dot(cortex, A) --> cortex = B',
'dot(cortex, B) --> cortex = C',
'dot(cortex, C) --> cortex = D',
'dot(cortex, D) --> cortex = E',
'dot(cortex, E) --> cortex = A'
)
model.bg = spa.BasalGanglia(actions=actions)
model.thal = spa.Thalamus(model.bg)
cortex = nengo.Probe(model.cortex.state.output, synapse=0.01)
actions = nengo.Probe(model.thal.actions.output, synapse=0.01)
utility = nengo.Probe(model.bg.input, synapse=0.01)
sim = nengo.Simulator(model)
sim.run(0.5)
'''
import nengo_viz
viz = nengo_viz.Viz(model)
viz.pointer(model.cortex)
#viz.pointer(model.input)
viz.start()'''
In [2]:
fig = figure(figsize=(12,8))
p1 = fig.add_subplot(3,1,1)
p1.plot(sim.trange(), model.similarity(sim.data, cortex))
p1.legend(model.get_output_vocab('cortex').keys, fontsize='x-small')
p1.set_ylabel('State')
p2 = fig.add_subplot(3,1,2)
p2.plot(sim.trange(), sim.data[actions])
p2_legend_txt = [a.effect for a in model.bg.actions.actions]
p2.legend(p2_legend_txt, fontsize='x-small')
p2.set_ylabel('Action')
p3 = fig.add_subplot(3,1,3)
p3.plot(sim.trange(), sim.data[utility])
p3_legend_txt = [a.condition for a in model.bg.actions.actions]
p3.legend(p3_legend_txt, fontsize='x-small')
p3.set_ylabel('Utility')
fig.subplots_adjust(hspace=0.2)
In [5]:
%pylab inline
import nengo
from nengo import spa
D = 16
def start(t):
if t < 0.05:
return '0.8*START+A'
else:
return '0'
model = spa.SPA(label='Sequence_Module', seed=1)
with model:
model.vision = spa.Buffer(dimensions=D, label='vision')
model.cortex = spa.Buffer(dimensions=D, label='cortex')
model.input = spa.Input(vision=start, label='input')
actions = spa.Actions(
'dot(vision, START) --> cortex = vision',
'dot(cortex, A) --> cortex = B',
'dot(cortex, B) --> cortex = C',
'dot(cortex, C) --> cortex = D',
'dot(cortex, D) --> cortex = E',
'dot(cortex, E) --> cortex = A'
)
model.bg = spa.BasalGanglia(actions=actions)
model.thal = spa.Thalamus(model.bg)
cortex = nengo.Probe(model.cortex.state.output, synapse=0.01)
vision = nengo.Probe(model.vision.state.output, synapse=0.01)
actions = nengo.Probe(model.thal.actions.output, synapse=0.01)
utility = nengo.Probe(model.bg.input, synapse=0.01)
sim = nengo.Simulator(model)
sim.run(0.5)
In [6]:
fig = figure(figsize=(12,8))
p1 = fig.add_subplot(4,1,1)
p1.plot(sim.trange(), model.similarity(sim.data, vision))
p1.legend(model.get_output_vocab('vision').keys, fontsize='x-small')
p1.set_ylabel('Vision')
p2 = fig.add_subplot(4,1,2)
p2.plot(sim.trange(), model.similarity(sim.data, cortex))
p2.legend(model.get_output_vocab('cortex').keys, fontsize='x-small')
p2.set_ylabel('Cortex')
p3 = fig.add_subplot(4,1,3)
p3.plot(sim.trange(), sim.data[actions])
p3.set_ylabel('Action')
p4 = fig.add_subplot(4,1,4)
p4.plot(sim.trange(), sim.data[utility])
p4.set_ylabel('Utility')
fig.subplots_adjust(hspace=0.2)
What about behavioural evidence?
A few sources of support
In [7]:
from IPython.display import YouTubeVideo
YouTubeVideo('sUvHCs5y0o8', width=640, height=390, loop=1, autoplay=0)
Out[7]:
State:
goal
: what disk am I trying to move (D0, D1, D2)focus
: what disk am I looking at (D0, D1, D2)goal_peg
: where is the disk I am trying to move (A, B, C)focus_peg
: where is the disk I am looking at (A, B, C)target_peg
: where am I trying to move a disk to (A, B, C)goal_final
: what is the overall final desired location of the disk I'm trying to move (A, B, C)Note: we're not yet modelling all the sensory and memory stuff (e.g. loading) here, so we manually set things like goal_final
.
Action effects: when an action is selected, it could do the following
focus
goal
goal_peg
move
and move_peg
Is this sufficient to implement the algorithm described above?
focus
=NONE then focus
=D2, goal
=D2, goal_peg
=goal_final
focus
$\cdot$ NONE focus
=D2 and goal
=D2 and goal_peg
!=target_peg
then focus
=D1focus
$\cdot$ D2 + goal
$\cdot$ D2 - goal_peg
$\cdot$ target_peg
focus
=D2 and goal
=D2 and goal_peg
==target_peg
then focus
=D1, goal
=D1, goal_peg
=goal_final
focus
=D1 and goal
=D1 and goal_peg
!=target_peg
then focus
=D0focus
=D1 and goal
=D1 and goal_peg
==target_peg
then focus
=D0, goal
=D0, goal_peg
=goal_final
focus
=D0 and goal_peg
==target_peg
then focus
=NONEfocus
=D0 and goal
=D0 and goal_peg
!=target_peg
then focus
=NONE, move
=D0, move_peg
=target_peg
focus
!=goal
and focus_peg
==goal_peg
and target_peg!=focus_peg
then goal
=focus
, goal_peg
=A+B+C-target_peg
-focus_peg
focus
!=goal
and focus_peg
!=goal_peg
and target_peg==focus_peg
then goal
=focus
, goal_peg
=A+B+C-target_peg
-goal_peg
focus
=D0 and goal
!=D0 and target_peg
!=focus_peg
and target_peg
!=goal_peg
and focus_peg
!=goal_peg
then move
=goal
, move_peg
=target_peg
focus
=D1 and goal
!=D1 and target_peg
!=focus_peg
and target_peg
!=goal_peg
and focus_peg
!=goal_peg
then focus
=D0Do science
Timing:
In [ ]: