-
Notifications
You must be signed in to change notification settings - Fork 0
/
spike.go
157 lines (141 loc) · 5.51 KB
/
spike.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (c) 2019, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package spike
import (
"github.com/chewxy/math32"
"github.com/dhairyyas/leabra-sleep/leabra"
)
// ActParams is full set of activation params including those from base
// leabra and the additional Spiking-specific ones.
type ActParams struct {
leabra.ActParams
Spike SpikeParams `view:"inline" desc:"spiking parameters"`
}
func (sk *ActParams) Defaults() {
sk.ActParams.Defaults()
sk.Spike.Defaults()
}
func (sk *ActParams) Update() {
sk.ActParams.Update()
sk.Spike.Update()
}
func (sk *ActParams) SpikeVmFmG(nrn *leabra.Neuron) {
updtVm := true
if sk.Spike.Tr > 0 && nrn.ISI >= 0 && nrn.ISI < float32(sk.Spike.Tr) {
updtVm = false // don't update the spiking vm during refract
}
nwVm := nrn.Vm
if updtVm {
ge := nrn.Ge * sk.Gbar.E
gi := nrn.Gi * sk.Gbar.I
gk := sk.Gbar.K * (nrn.GknaFast + nrn.GknaMed + nrn.GknaSlow)
nrn.Gk = gk
vmEff := nrn.Vm
// midpoint method: take a half-step in vmEff
inet1 := sk.InetFmG(vmEff, ge, gi, gk)
vmEff += .5 * sk.Dt.VmDt * inet1 // go half way
inet2 := sk.InetFmG(vmEff, ge, gi, gk)
// add spike current if relevant
if sk.Spike.Exp {
inet2 += sk.Gbar.L * sk.Spike.ExpSlope *
math32.Exp((vmEff-sk.XX1.Thr)/sk.Spike.ExpSlope)
}
nwVm += sk.Dt.VmDt * inet2
nrn.Inet = inet2
}
if sk.Noise.Type == leabra.VmNoise {
nwVm += nrn.Noise
}
nrn.Vm = sk.VmRange.ClipVal(nwVm)
}
// SpikeActFmVm computes the discrete spiking activation
// from membrane potential Vm
func (sk *ActParams) SpikeActFmVm(nrn *leabra.Neuron) {
var thr float32
if sk.Spike.Exp {
thr = sk.Spike.ExpThr
} else {
thr = sk.XX1.Thr
}
if nrn.Vm > thr {
nrn.Spike = 1
nrn.Vm = sk.Spike.VmR
nrn.Inet = 0
if nrn.ISIAvg == -1 {
nrn.ISIAvg = -2
} else if nrn.ISI > 0 { // must have spiked to update
sk.Spike.AvgFmISI(&nrn.ISIAvg, nrn.ISI+1)
}
nrn.ISI = 0
} else {
nrn.Spike = 0
if nrn.ISI >= 0 {
nrn.ISI += 1
}
if nrn.ISIAvg >= 0 && nrn.ISI > 0 && nrn.ISI > 1.2*nrn.ISIAvg {
sk.Spike.AvgFmISI(&nrn.ISIAvg, nrn.ISI)
}
}
nwAct := sk.Spike.ActFmISI(nrn.ISIAvg, .001, 1) // todo: use real #'s
if nwAct > 1 {
nwAct = 1
}
nwAct = nrn.Act + sk.Dt.VmDt*(nwAct-nrn.Act)
nrn.ActDel = nwAct - nrn.Act
nrn.Act = nwAct
if sk.KNa.On {
sk.KNa.GcFmSpike(&nrn.GknaFast, &nrn.GknaMed, &nrn.GknaSlow, nrn.Spike > .5)
}
}
// SpikeParams contains spiking activation function params.
// Implements the AdEx adaptive exponential function
type SpikeParams struct {
Exp bool `def:"false" desc:"if true, turn on exponential excitatory current that drives Vm rapidly upward for spiking as it gets past its nominal firing threshold (Thr) -- nicely captures the Hodgkin Huxley dynamics of Na and K channels -- uses Brette & Gurstner 2005 AdEx formulation -- this mechanism has an unfortunate interaction with the continuous inhibitory currents generated by the standard FFFB inhibitory function, which cause this mechanism to desensitize and fail to spike"`
ExpSlope float32 `viewif:"Exp" def:"0.02" desc:"slope in Vm (2 mV = .02 in normalized units) for extra exponential excitatory current that drives Vm rapidly upward for spiking as it gets past its nominal firing threshold (Thr) -- nicely captures the Hodgkin Huxley dynamics of Na and K channels -- uses Brette & Gurstner 2005 AdEx formulation -- a value of 0 disables this mechanism"`
ExpThr float32 `viewif:"Exp" def:"1.2" desc:"membrane potential threshold for actually triggering a spike when using the exponential mechanism"`
VmR float32 `def:"0.3,0,0.15" desc:"post-spiking membrane potential to reset to, produces refractory effect if lower than VmInit -- 0.30 is apropriate biologically-based value for AdEx (Brette & Gurstner, 2005) parameters"`
Tr int `def:"3" desc:"post-spiking explicit refractory period, in cycles -- prevents Vm updating for this number of cycles post firing"`
MaxHz float32 `def:"180" min:"1" desc:"for translating spiking interval (rate) into rate-code activation equivalent (and vice-versa, for clamped layers), what is the maximum firing rate associated with a maximum activation value (max act is typically 1.0 -- depends on act_range)"`
RateTau float32 `def:"5" min:"1" desc:"constant for integrating the spiking interval in estimating spiking rate"`
RateDt float32 `view:"-" desc:"rate = 1 / tau"`
}
func (sk *SpikeParams) Defaults() {
sk.Exp = false
sk.ExpSlope = 0.02
sk.ExpThr = 1.2
sk.VmR = 0.3
sk.Tr = 3
sk.MaxHz = 180
sk.RateTau = 5
sk.Update()
}
func (sk *SpikeParams) Update() {
sk.RateDt = 1 / sk.RateTau
}
// ActToISI compute spiking interval from a given rate-coded activation,
// based on time increment (.001 = 1msec default), Act.Dt.Integ
func (sk *SpikeParams) ActToISI(act, timeInc, integ float32) float32 {
if act == 0 {
return 0
}
return (1 / (timeInc * integ * act * sk.MaxHz))
}
// ActFmISI computes rate-code activation from estimated spiking interval
func (sk *SpikeParams) ActFmISI(isi, timeInc, integ float32) float32 {
if isi <= 0 {
return 0
}
maxInt := 1.0 / (timeInc * integ * sk.MaxHz) // interval at max hz..
return maxInt / isi // normalized
}
// AvgFmISI updates spiking ISI from current isi interval value
func (sk *SpikeParams) AvgFmISI(avg *float32, isi float32) {
if *avg <= 0 {
*avg = isi
} else if isi < 0.8**avg {
*avg = isi // if significantly less than we take that
} else { // integrate on slower
*avg += sk.RateDt * (isi - *avg) // running avg updt
}
}