-
Notifications
You must be signed in to change notification settings - Fork 8
/
act_path.go
263 lines (218 loc) · 9.98 KB
/
act_path.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// 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 axon
import (
"log"
"cogentcore.org/core/base/randx"
"cogentcore.org/core/math32"
"cogentcore.org/core/vgpu/gosl/slbool"
)
//gosl:start act_path
// PathGTypes represents the conductance (G) effects of a given pathway,
// including excitatory, inhibitory, and modulatory.
type PathGTypes int32 //enums:enum
// The pathway conductance types
const (
// Excitatory pathways drive Ge conductance on receiving neurons,
// which send to GiRaw and GiSyn neuron variables.
ExcitatoryG PathGTypes = iota
// Inhibitory pathways drive Gi inhibitory conductance,
// which send to GiRaw and GiSyn neuron variables.
InhibitoryG
// Modulatory pathways have a multiplicative effect on other inputs,
// which send to GModRaw and GModSyn neuron variables.
ModulatoryG
// Maintenance pathways drive unique set of NMDA channels that support
// strong active maintenance abilities.
// Send to GMaintRaw and GMaintSyn neuron variables.
MaintG
// Context pathways are for inputs to CT layers, which update
// only at the end of the plus phase, and send to CtxtGe.
ContextG
)
//////////////////////////////////////////////////////////////////////////////////////
// SynComParams
// SynComParams are synaptic communication parameters:
// used in the Path parameters. Includes delay and
// probability of failure, and Inhib for inhibitory connections,
// and modulatory pathways that have multiplicative-like effects.
type SynComParams struct {
// type of conductance (G) communicated by this pathway
GType PathGTypes
// additional synaptic delay in msec for inputs arriving at this pathway. Must be <= MaxDelay which is set during network building based on MaxDelay of any existing Path in the network. Delay = 0 means a spike reaches receivers in the next Cycle, which is the minimum time (1 msec). Biologically, subtract 1 from biological synaptic delay values to set corresponding Delay value.
Delay uint32 `min:"0" default:"2"`
// maximum value of Delay -- based on MaxDelay values when the BuildGBuf function was called when the network was built -- cannot set it longer than this, except by calling BuildGBuf on network after changing MaxDelay to a larger value in any pathway in the network.
MaxDelay uint32 `edit:"-"`
// probability of synaptic transmission failure -- if > 0, then weights are turned off at random as a function of PFail (times 1-SWt if PFailSwt)
PFail float32
// if true, then probability of failure is inversely proportional to SWt structural / slow weight value (i.e., multiply PFail * (1-SWt)))
PFailSWt slbool.Bool
// delay length = actual length of the GBuf buffer per neuron = Delay+1 -- just for speed
DelLen uint32 `view:"-"`
pad, pad1 float32
}
func (sc *SynComParams) Defaults() {
sc.Delay = 2
sc.MaxDelay = 2
sc.PFail = 0 // 0.5 works?
sc.PFailSWt.SetBool(false)
sc.Update()
}
func (sc *SynComParams) Update() {
if sc.Delay > sc.MaxDelay {
sc.Delay = sc.MaxDelay
}
sc.DelLen = sc.Delay + 1
}
// RingIndex returns the wrap-around ring index for given raw index.
// For writing and reading spikes to GBuf buffer, based on
// Context.CyclesTotal counter.
// RN: 0 1 2 <- recv neuron indexes
// DI: 0 1 2 0 1 2 0 1 2 <- delay indexes
// C0: ^ v <- cycle 0, ring index: ^ = write, v = read
// C1: ^ v <- cycle 1, shift over by 1 -- overwrite last read
// C2: v ^ <- cycle 2: read out value stored on C0 -- index wraps around
func (sc *SynComParams) RingIndex(i uint32) uint32 {
if i >= sc.DelLen {
i -= sc.DelLen
}
return i
}
// WriteOff returns offset for writing new spikes into the GBuf buffer,
// based on Context CyclesTotal counter which increments each cycle.
// This is logically the last position in the ring buffer.
func (sc *SynComParams) WriteOff(cycTot int32) uint32 {
return sc.RingIndex(uint32(cycTot)%sc.DelLen + sc.DelLen)
}
// WriteIndex returns actual index for writing new spikes into the GBuf buffer,
// based on the layer-based recv neuron index, data parallel idx, and the
// WriteOff offset computed from the CyclesTotal.
func (sc *SynComParams) WriteIndex(rnIndex, di uint32, cycTot int32, nRecvNeurs, maxData uint32) uint32 {
return sc.WriteIndexOff(rnIndex, di, sc.WriteOff(cycTot), nRecvNeurs, maxData)
}
// WriteIndexOff returns actual index for writing new spikes into the GBuf buffer,
// based on the layer-based recv neuron index and the given WriteOff offset.
func (sc *SynComParams) WriteIndexOff(rnIndex, di, wrOff uint32, nRecvNeurs, maxData uint32) uint32 {
// return rnIndex*sc.DelLen + wrOff
return (wrOff*nRecvNeurs+rnIndex)*maxData + di
}
// ReadOff returns offset for reading existing spikes from the GBuf buffer,
// based on Context CyclesTotal counter which increments each cycle.
// This is logically the zero position in the ring buffer.
func (sc *SynComParams) ReadOff(cycTot int32) uint32 {
return sc.RingIndex(uint32(cycTot) % sc.DelLen)
}
// ReadIndex returns index for reading existing spikes from the GBuf buffer,
// based on the layer-based recv neuron index, data parallel idx, and the
// ReadOff offset from the CyclesTotal.
func (sc *SynComParams) ReadIndex(rnIndex, di uint32, cycTot int32, nRecvNeurs, maxData uint32) uint32 {
// return rnIndex*sc.DelLen + sc.ReadOff(cycTot)
// delay is outer, neurs are inner -- should be faster?
return (sc.ReadOff(cycTot)*nRecvNeurs+rnIndex)*maxData + di
}
// FloatToIntFactor returns the factor used for converting float32
// to int32 in GBuf encoding. Because total G is constrained via
// scaling factors to be around ~1, it is safe to use a factor that
// uses most of the available bits, leaving enough room to prevent
// overflow when adding together the different vals.
// For encoding, bake this into scale factor in SendSpike, and
// cast the result to int32.
func (sc *SynComParams) FloatToIntFactor() float32 {
return float32(1 << 24) // leaves 7 bits = 128 to cover any extreme cases
// this is sufficient to pass existing tests at std tolerances.
}
// FloatToGBuf converts the given floating point value
// to a large int32 for accumulating in GBuf.
// Note: more efficient to bake factor into scale factor per paths.
func (sc *SynComParams) FloatToGBuf(val float32) int32 {
return int32(val * sc.FloatToIntFactor())
}
// FloatFromGBuf converts the given int32 value produced
// via FloatToGBuf back into a float32 (divides by factor).
// If the value is negative, a panic is triggered indicating
// there was numerical overflow in the aggregation.
// If this occurs, the FloatToIntFactor needs to be decreased.
func (sc *SynComParams) FloatFromGBuf(ival int32) float32 {
//gosl:end act_path
if ival < 0 {
log.Printf("axon.SynComParams: FloatFromGBuf is negative, there was an overflow error\n")
return 1
}
//gosl:start act_path
return float32(ival) / sc.FloatToIntFactor()
}
// WtFailP returns probability of weight (synapse) failure given current SWt value
func (sc *SynComParams) WtFailP(swt float32) float32 {
if sc.PFailSWt.IsFalse() {
return sc.PFail
}
return sc.PFail * (1 - swt)
}
//gosl:end act_path
// WtFail returns true if synapse should fail, as function of SWt value (optionally)
func (sc *SynComParams) WtFail(ctx *Context, swt float32) bool {
fp := sc.WtFailP(swt)
if fp == 0 {
return false
}
return randx.BoolP32(fp)
// return slrand.BoolP(ctx.RandCtr, si) // todo: needs a synapse index
}
// Fail updates failure status of given weight, given SWt value
func (sc *SynComParams) Fail(ctx *Context, syni uint32, swt float32) {
if sc.PFail > 0 {
if sc.WtFail(ctx, swt) {
SetSynV(ctx, syni, Wt, 0)
}
}
}
//gosl:start act_path
//////////////////////////////////////////////////////////////////////////////////////
// PathScaleParams
// PathScaleParams are pathway scaling parameters: modulates overall strength of pathway,
// using both absolute and relative factors.
type PathScaleParams struct {
// relative scaling that shifts balance between different pathways -- this is subject to normalization across all other pathways into receiving neuron, and determines the GScale.Target for adapting scaling
Rel float32 `min:"0"`
// absolute multiplier adjustment factor for the path scaling -- can be used to adjust for idiosyncrasies not accommodated by the standard scaling based on initial target activation level and relative scaling factors -- any adaptation operates by directly adjusting scaling factor from the initially computed value
Abs float32 `default:"1" min:"0"`
pad, pad1 float32
}
func (ws *PathScaleParams) Defaults() {
ws.Rel = 1
ws.Abs = 1
}
func (ws *PathScaleParams) Update() {
}
// SLayActScale computes scaling factor based on sending layer activity level (savg), number of units
// in sending layer (snu), and number of recv connections (ncon).
// Uses a fixed sem_extra standard-error-of-the-mean (SEM) extra value of 2
// to add to the average expected number of active connections to receive,
// for purposes of computing scaling factors with partial connectivity
// For 25% layer activity, binomial SEM = sqrt(p(1-p)) = .43, so 3x = 1.3 so 2 is a reasonable default.
func (ws *PathScaleParams) SLayActScale(savg, snu, ncon float32) float32 {
if ncon < 1 { // path Avg can be < 1 in some cases
ncon = 1
}
semExtra := 2
slayActN := int(math32.Round(savg * snu)) // sending layer actual # active
slayActN = max(slayActN, 1)
var sc float32
if ncon == snu {
sc = 1 / float32(slayActN)
} else {
maxActN := int(math32.Min(ncon, float32(slayActN))) // max number we could get
avgActN := int(math32.Round(savg * ncon)) // recv average actual # active if uniform
avgActN = max(avgActN, 1)
expActN := avgActN + semExtra // expected
expActN = min(expActN, maxActN)
sc = 1 / float32(expActN)
}
return sc
}
// FullScale returns full scaling factor, which is product of Abs * Rel * SLayActScale
func (ws *PathScaleParams) FullScale(savg, snu, ncon float32) float32 {
return ws.Abs * ws.Rel * ws.SLayActScale(savg, snu, ncon)
}
//gosl:end act_path