-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnode.go
159 lines (137 loc) · 3.57 KB
/
node.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
package gwc
import (
"math"
"math/rand"
)
// Builds a Node from the provided state function and neighbours.
func NewNode(id NodeID, fn NodeStateFn, neighbours ...NodeID) Node {
return &BaseNode{id, neighbours, fn}
}
// Builds a Node from the provided superposition and neighbours.
func NewSuperpositionNode(id NodeID, super NodeSuperposition, neighbours ...NodeID) Node {
return NewNode(id, SuperpositionStateFn(super), neighbours...)
}
type (
NodesMap map[NodeID]Node
Nodes []Node
Node interface {
ID() NodeID
Neighbours() NodeIDs
Collapse(*rand.Rand, NodeEnvironment) NodeState
}
NodeIDs []NodeID
NodeID = string
NodeProbability = float64
NodeState = interface{}
NodeStateFn = func(*rand.Rand, NodeEnvironment) NodeState
)
// BaseNode can be used as base for a more concrete struct, which implements a concrete Collapse() method.
type BaseNode struct {
id NodeID
neighbours NodeIDs
fn NodeStateFn
}
func (n *BaseNode) ID() NodeID {
return n.id
}
func (n *BaseNode) Neighbours() NodeIDs {
return n.neighbours
}
func (n *BaseNode) Collapse(rnd *rand.Rand, env NodeEnvironment) NodeState {
if n.fn != nil {
return n.fn(rnd, env)
}
return nil
}
// Applies a logical AND to the two index lists and returns the product.
func (ids NodeIDs) And(other NodeIDs) NodeIDs {
xs := NodeIDs{}
for _, a := range ids {
for _, b := range other {
if a == b {
xs = append(xs, a)
}
}
}
return xs
}
// Applies a logical OR to the two index lists and returns the product.
func (ids NodeIDs) Or(other NodeIDs) NodeIDs {
xs := NodeIDs{}
xs = append(xs, ids...)
Outer:
for _, b := range other {
for _, x := range xs {
if b == x {
continue Outer
}
}
xs = append(xs, b)
}
return xs
}
// Applies a logical XOR to the two index lists and returns the product.
func (ids NodeIDs) Xor(other NodeIDs) NodeIDs {
xs := NodeIDs{}
Outer:
for _, a := range ids {
for _, b := range other {
if a == b {
continue Outer
}
}
xs = append(xs, a)
}
Outer2:
for _, b := range other {
for _, a := range ids {
if a == b {
continue Outer2
}
}
xs = append(xs, b)
}
return xs
}
type (
NodeSuperpositionFn = func(*rand.Rand, NodeEnvironment) (NodeProbability, NodeState)
NodeSuperposition = []NodeSuperpositionFn
)
func SuperpositionStateFn(super NodeSuperposition) NodeStateFn {
return func(rnd *rand.Rand, env NodeEnvironment) NodeState {
// Stop early when the Node's superposition is empty.
num := len(super)
if num == 0 {
return nil
}
// We'll later compare the generated probabilities against this float, in this order.
// Both are generated now, so that collapsing the superposition below won't interfere with these values.
compare := rnd.Float64()
order := rnd.Perm(num)
// Call all functions in the superposition and collect their probabilities and states.
sum := float64(0.0)
probabilities := make([]NodeProbability, num)
states := make([]NodeState, num)
for _, i := range order {
ip, is := super[i](rnd, env)
sum += ip
probabilities[i] = ip
states[i] = is
}
// Scale compare float according to the relative probability sum.
compare *= math.Max(1, sum)
// Collapse into the first state that had a high enough Nodeprobability to reach the compare float.
for i, p := range probabilities {
compare -= p
if compare <= 0 {
return states[i]
}
}
// If no state was probable enough but there are states available, return a random one.
if len(states) > 0 {
return states[rnd.Intn(len(states))]
}
// Fallback to nil when there were no states.
return nil
}
}