-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathcircle.go
150 lines (127 loc) · 4.52 KB
/
circle.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
// 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 paths
import (
"cogentcore.org/core/math32"
"cogentcore.org/core/math32/vecint"
"cogentcore.org/lab/tensor"
"github.com/emer/emergent/v2/edge"
"github.com/emer/emergent/v2/efuns"
)
// Circle implements a circular pattern of connectivity between two layers
// where the center moves in proportion to receiver position with offset
// and multiplier factors, and a given radius is used (with wrap-around
// optionally). A corresponding Gaussian bump of TopoWeights is available as well.
// Makes for a good center-surround connectivity pattern.
// 4D layers are automatically flattened to 2D for this connection.
type Circle struct {
// radius of the circle, in units from center in sending layer
Radius int
// starting offset in sending layer, for computing the corresponding sending center relative to given recv unit position
Start vecint.Vector2i
// scaling to apply to receiving unit position to compute sending center as function of recv unit position
Scale math32.Vector2
// auto-scale sending center positions as function of relative sizes of send and recv layers -- if Start is positive then assumes it is a border, subtracted from sending size
AutoScale bool
// if true, connectivity wraps around edges
Wrap bool
// if true, this path should set gaussian topographic weights, according to following parameters
TopoWeights bool
// gaussian sigma (width) as a proportion of the radius of the circle
Sigma float32
// maximum weight value for GaussWts function -- multiplies values
MaxWt float32
// if true, and connecting layer to itself (self pathway), then make a self-connection from unit to itself
SelfCon bool
}
func NewCircle() *Circle {
cr := &Circle{}
cr.Defaults()
return cr
}
func (cr *Circle) Defaults() {
cr.Wrap = true
cr.Radius = 8
cr.Scale.SetScalar(1)
cr.Sigma = 0.5
cr.MaxWt = 1
}
func (cr *Circle) Name() string {
return "Circle"
}
func (cr *Circle) Connect(send, recv *tensor.Shape, same bool) (sendn, recvn *tensor.Int32, cons *tensor.Bool) {
sendn, recvn, cons = NewTensors(send, recv)
sNy, sNx, _, _ := tensor.Projection2DShape(send, false)
rNy, rNx, _, _ := tensor.Projection2DShape(recv, false)
rnv := recvn.Values
snv := sendn.Values
sNtot := send.Len()
sc := cr.Scale
if cr.AutoScale {
ssz := math32.Vec2(float32(sNx), float32(sNy))
if cr.Start.X >= 0 && cr.Start.Y >= 0 {
ssz.X -= float32(2 * cr.Start.X)
ssz.Y -= float32(2 * cr.Start.Y)
}
rsz := math32.Vec2(float32(rNx), float32(rNy))
sc = ssz.Div(rsz)
}
for ry := 0; ry < rNy; ry++ {
for rx := 0; rx < rNx; rx++ {
sctr := math32.Vec2(float32(rx)*sc.X+float32(cr.Start.X), float32(ry)*sc.Y+float32(cr.Start.Y))
for sy := 0; sy < sNy; sy++ {
for sx := 0; sx < sNx; sx++ {
sp := math32.Vec2(float32(sx), float32(sy))
if cr.Wrap {
sp.X = edge.WrapMinDist(sp.X, float32(sNx), sctr.X)
sp.Y = edge.WrapMinDist(sp.Y, float32(sNy), sctr.Y)
}
d := int(math32.Round(sp.DistanceTo(sctr)))
if d <= cr.Radius {
ri := tensor.Projection2DIndex(recv, false, ry, rx)
si := tensor.Projection2DIndex(send, false, sy, sx)
off := ri*sNtot + si
if !cr.SelfCon && same && ri == si {
continue
}
cons.Values.Set(true, off)
rnv[ri]++
snv[si]++
}
}
}
}
}
return
}
// GaussWts returns gaussian weight value for given unit indexes in
// given send and recv layers according to Gaussian Sigma and MaxWt.
// Can be used for a Path.SetScalesFunc or SetWtsFunc
func (cr *Circle) GaussWts(si, ri int, send, recv *tensor.Shape) float32 {
sNy, sNx, _, _ := tensor.Projection2DShape(send, false)
rNy, rNx, _, _ := tensor.Projection2DShape(recv, false)
ry := ri / rNx // todo: this is not right for 4d!
rx := ri % rNx
sy := si / sNx
sx := si % sNx
fsig := cr.Sigma * float32(cr.Radius)
sc := cr.Scale
if cr.AutoScale {
ssz := math32.Vec2(float32(sNx), float32(sNy))
if cr.Start.X >= 0 && cr.Start.Y >= 0 {
ssz.X -= float32(2 * cr.Start.X)
ssz.Y -= float32(2 * cr.Start.Y)
}
rsz := math32.Vec2(float32(rNx), float32(rNy))
sc = ssz.Div(rsz)
}
sctr := math32.Vec2(float32(rx)*sc.X+float32(cr.Start.X), float32(ry)*sc.Y+float32(cr.Start.Y))
sp := math32.Vec2(float32(sx), float32(sy))
if cr.Wrap {
sp.X = edge.WrapMinDist(sp.X, float32(sNx), sctr.X)
sp.Y = edge.WrapMinDist(sp.Y, float32(sNy), sctr.Y)
}
wt := cr.MaxWt * efuns.GaussVecDistNoNorm(sp, sctr, fsig)
return wt
}