evonet 0.1.0.dev7__tar.gz → 0.1.0.dev8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/PKG-INFO +1 -1
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/core.py +45 -27
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/mutation.py +2 -2
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/pyproject.toml +1 -1
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/tests/test_core.py +4 -4
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/tests/test_nnet_io_and_forward.py +5 -5
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/tests/test_recurrent_dynamics.py +3 -3
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/LICENSE +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/README.md +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/connection.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/io.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/setup.cfg +0 -0
- {evonet-0.1.0.dev7 → evonet-0.1.0.dev8}/tests/test_activation.py +0 -0
|
@@ -9,7 +9,7 @@ export interfaces.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from typing import Optional
|
|
12
|
+
from typing import Literal, Optional
|
|
13
13
|
|
|
14
14
|
import graphviz
|
|
15
15
|
import numpy as np
|
|
@@ -100,31 +100,33 @@ class Nnet:
|
|
|
100
100
|
label: str = "",
|
|
101
101
|
role: NeuronRole = NeuronRole.HIDDEN,
|
|
102
102
|
count: int = 1,
|
|
103
|
-
|
|
103
|
+
connection_init: Literal["random", "zero", "none"] = "random",
|
|
104
104
|
recurrent: Optional[set[RecurrentKind]] = None,
|
|
105
105
|
) -> list[Neuron]:
|
|
106
106
|
"""
|
|
107
107
|
Add one or more neurons to the network.
|
|
108
108
|
|
|
109
109
|
Args:
|
|
110
|
-
layer_idx
|
|
111
|
-
activation
|
|
112
|
-
bias
|
|
113
|
-
label
|
|
114
|
-
role
|
|
115
|
-
count
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
layer_idx: Target layer index. Defaults to last layer.
|
|
111
|
+
activation: Activation function name.
|
|
112
|
+
bias: Initial bias value.
|
|
113
|
+
label: Optional label.
|
|
114
|
+
role: Role of the neuron (INPUT, HIDDEN, OUTPUT).
|
|
115
|
+
count: Number of neurons to add (default: 1).
|
|
116
|
+
connection_init:
|
|
117
|
+
"random" – connect with random weights (feedforward + recurrent)
|
|
118
|
+
"zero" – connect with weight 0.0 (feedforward + recurrent)
|
|
119
|
+
"none" – do not create connections (feedforward + recurrent)
|
|
120
|
+
recurrent: Optional recurrent connection types.
|
|
118
121
|
|
|
119
122
|
Returns:
|
|
120
123
|
list[Neuron]: List of added neurons.
|
|
121
124
|
"""
|
|
122
|
-
|
|
123
125
|
if layer_idx is None:
|
|
124
126
|
layer_idx = len(self.layers) - 1 # Add neuron to last layer
|
|
125
127
|
|
|
126
128
|
if layer_idx < 0:
|
|
127
|
-
raise ValueError(f"
|
|
129
|
+
raise ValueError(f"Layer index must be >= 0 (got {layer_idx})")
|
|
128
130
|
if layer_idx >= len(self.layers):
|
|
129
131
|
raise ValueError(f"Layer index out of bounds: {layer_idx}")
|
|
130
132
|
|
|
@@ -132,46 +134,56 @@ class Nnet:
|
|
|
132
134
|
new_neurons: list[Neuron] = []
|
|
133
135
|
|
|
134
136
|
# Create neurons without connections
|
|
135
|
-
for
|
|
137
|
+
for _ in range(count):
|
|
136
138
|
neuron = Neuron(activation=activation, bias=bias)
|
|
137
139
|
neuron.role = role
|
|
138
140
|
neuron.label = label
|
|
139
141
|
target_layer.neurons.append(neuron)
|
|
140
142
|
new_neurons.append(neuron)
|
|
141
143
|
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
+
# Weights based on init mode
|
|
145
|
+
weight_map = {"random": None, "zero": 0.0, "none": None}
|
|
146
|
+
if connection_init not in weight_map:
|
|
147
|
+
raise ValueError(f"Invalid connection_init: {connection_init}")
|
|
148
|
+
weight = weight_map[connection_init]
|
|
149
|
+
|
|
150
|
+
skip_connections = connection_init == "none"
|
|
151
|
+
|
|
152
|
+
# Connect to previous layer
|
|
153
|
+
if not skip_connections and layer_idx > 0:
|
|
144
154
|
for prev_neuron in self.layers[layer_idx - 1].neurons:
|
|
145
155
|
for n in new_neurons:
|
|
146
|
-
self.add_connection(prev_neuron, n)
|
|
156
|
+
self.add_connection(prev_neuron, n, weight=weight)
|
|
147
157
|
|
|
148
|
-
# Connect to next layer (
|
|
158
|
+
# Connect to next layer (hidden only)
|
|
149
159
|
if (
|
|
150
|
-
|
|
160
|
+
not skip_connections
|
|
151
161
|
and role == NeuronRole.HIDDEN
|
|
152
162
|
and layer_idx < len(self.layers) - 1
|
|
153
163
|
):
|
|
154
|
-
|
|
155
164
|
for next_neuron in self.layers[layer_idx + 1].neurons:
|
|
156
165
|
for n in new_neurons:
|
|
157
|
-
self.add_connection(n, next_neuron)
|
|
166
|
+
self.add_connection(n, next_neuron, weight=weight)
|
|
158
167
|
|
|
159
168
|
# Recurrent connections
|
|
160
|
-
if recurrent:
|
|
169
|
+
if recurrent and not skip_connections:
|
|
161
170
|
if RecurrentKind.DIRECT in recurrent:
|
|
162
171
|
for n in new_neurons:
|
|
163
172
|
if n.role == NeuronRole.HIDDEN:
|
|
164
|
-
self.add_connection(
|
|
173
|
+
self.add_connection(
|
|
174
|
+
n, n, weight=weight, conn_type=ConnectionType.RECURRENT
|
|
175
|
+
)
|
|
165
176
|
|
|
166
177
|
if RecurrentKind.LATERAL in recurrent:
|
|
167
|
-
# All neurons in this layer (old + new)
|
|
168
178
|
full_layer = list(self.layers[layer_idx].neurons)
|
|
169
|
-
|
|
170
179
|
for src in full_layer:
|
|
171
180
|
for dst in new_neurons:
|
|
172
181
|
if src is not dst:
|
|
173
182
|
self.add_connection(
|
|
174
|
-
src,
|
|
183
|
+
src,
|
|
184
|
+
dst,
|
|
185
|
+
weight=weight,
|
|
186
|
+
conn_type=ConnectionType.RECURRENT,
|
|
175
187
|
)
|
|
176
188
|
|
|
177
189
|
if RecurrentKind.INDIRECT in recurrent:
|
|
@@ -179,13 +191,19 @@ class Nnet:
|
|
|
179
191
|
for lower_layer in self.layers[1:layer_idx]:
|
|
180
192
|
for dst in lower_layer.neurons:
|
|
181
193
|
self.add_connection(
|
|
182
|
-
src,
|
|
194
|
+
src,
|
|
195
|
+
dst,
|
|
196
|
+
weight=weight,
|
|
197
|
+
conn_type=ConnectionType.RECURRENT,
|
|
183
198
|
)
|
|
184
199
|
for higher_layer in self.layers[layer_idx + 1 :]:
|
|
185
200
|
for src in higher_layer.neurons:
|
|
186
201
|
for dst in new_neurons:
|
|
187
202
|
self.add_connection(
|
|
188
|
-
src,
|
|
203
|
+
src,
|
|
204
|
+
dst,
|
|
205
|
+
weight=weight,
|
|
206
|
+
conn_type=ConnectionType.RECURRENT,
|
|
189
207
|
)
|
|
190
208
|
|
|
191
209
|
return new_neurons
|
|
@@ -270,7 +270,7 @@ def add_random_neuron(net: Nnet, activations: list[str] | None = None) -> None:
|
|
|
270
270
|
layer_idx=net.layers.index(layer),
|
|
271
271
|
activation=random_function_name(activations),
|
|
272
272
|
role=NeuronRole.HIDDEN,
|
|
273
|
-
|
|
273
|
+
connection_init="random",
|
|
274
274
|
)
|
|
275
275
|
|
|
276
276
|
|
|
@@ -332,7 +332,7 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
|
|
|
332
332
|
layer_idx=insert_idx,
|
|
333
333
|
role=NeuronRole.HIDDEN,
|
|
334
334
|
activation=activation,
|
|
335
|
-
|
|
335
|
+
connection_init="none",
|
|
336
336
|
)[0]
|
|
337
337
|
|
|
338
338
|
# Set new connections
|
|
@@ -13,14 +13,14 @@ def test_forward_pass_identity() -> None:
|
|
|
13
13
|
activation="linear",
|
|
14
14
|
role=NeuronRole.INPUT,
|
|
15
15
|
label="in",
|
|
16
|
-
|
|
16
|
+
connection_init="none",
|
|
17
17
|
)
|
|
18
18
|
net.add_neuron(
|
|
19
19
|
layer_idx=1,
|
|
20
20
|
activation="linear",
|
|
21
21
|
role=NeuronRole.OUTPUT,
|
|
22
22
|
label="out",
|
|
23
|
-
|
|
23
|
+
connection_init="none",
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
src = net.layers[0].neurons[0]
|
|
@@ -49,7 +49,7 @@ def test_forward_pass_with_bias() -> None:
|
|
|
49
49
|
activation="linear",
|
|
50
50
|
role=NeuronRole.INPUT,
|
|
51
51
|
label="in",
|
|
52
|
-
|
|
52
|
+
connection_init="none",
|
|
53
53
|
)
|
|
54
54
|
net.add_neuron(
|
|
55
55
|
layer_idx=1,
|
|
@@ -57,7 +57,7 @@ def test_forward_pass_with_bias() -> None:
|
|
|
57
57
|
role=NeuronRole.OUTPUT,
|
|
58
58
|
bias=0.5,
|
|
59
59
|
label="out",
|
|
60
|
-
|
|
60
|
+
connection_init="none",
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
src = net.layers[0].neurons[0]
|
|
@@ -29,21 +29,21 @@ def build_simple_ff_net() -> Nnet:
|
|
|
29
29
|
role=NeuronRole.INPUT,
|
|
30
30
|
activation="linear",
|
|
31
31
|
count=2,
|
|
32
|
-
|
|
32
|
+
connection_init="none",
|
|
33
33
|
)
|
|
34
34
|
net.add_neuron(
|
|
35
35
|
layer_idx=1,
|
|
36
36
|
role=NeuronRole.HIDDEN,
|
|
37
37
|
activation="linear",
|
|
38
38
|
count=3,
|
|
39
|
-
|
|
39
|
+
connection_init="random",
|
|
40
40
|
)
|
|
41
41
|
net.add_neuron(
|
|
42
42
|
layer_idx=2,
|
|
43
43
|
role=NeuronRole.OUTPUT,
|
|
44
44
|
activation="linear",
|
|
45
45
|
count=1,
|
|
46
|
-
|
|
46
|
+
connection_init="random",
|
|
47
47
|
)
|
|
48
48
|
return net
|
|
49
49
|
|
|
@@ -117,14 +117,14 @@ def test_forward_sanity_linear_1x1() -> None:
|
|
|
117
117
|
role=NeuronRole.INPUT,
|
|
118
118
|
activation="linear",
|
|
119
119
|
count=1,
|
|
120
|
-
|
|
120
|
+
connection_init="none",
|
|
121
121
|
)
|
|
122
122
|
net.add_neuron(
|
|
123
123
|
layer_idx=1,
|
|
124
124
|
role=NeuronRole.OUTPUT,
|
|
125
125
|
activation="linear",
|
|
126
126
|
count=1,
|
|
127
|
-
|
|
127
|
+
connection_init="random",
|
|
128
128
|
)
|
|
129
129
|
|
|
130
130
|
# There should be exactly one connection L0(0) -> L1(0). Force known params:
|
|
@@ -15,17 +15,17 @@ def _mk_linear_ih_h_o() -> tuple[Nnet, Neuron, Neuron, Neuron]:
|
|
|
15
15
|
net.add_layer(3) # input, hidden, output
|
|
16
16
|
|
|
17
17
|
neurons: list[Neuron] = net.add_neuron(
|
|
18
|
-
layer_idx=0, role=NeuronRole.INPUT, activation="linear",
|
|
18
|
+
layer_idx=0, role=NeuronRole.INPUT, activation="linear", connection_init="none"
|
|
19
19
|
)
|
|
20
20
|
n_in = neurons[0]
|
|
21
21
|
|
|
22
22
|
neurons = net.add_neuron(
|
|
23
|
-
layer_idx=1, role=NeuronRole.HIDDEN, activation="linear",
|
|
23
|
+
layer_idx=1, role=NeuronRole.HIDDEN, activation="linear", connection_init="none"
|
|
24
24
|
)
|
|
25
25
|
n_hid = neurons[0]
|
|
26
26
|
|
|
27
27
|
neurons = net.add_neuron(
|
|
28
|
-
layer_idx=2, role=NeuronRole.OUTPUT, activation="linear",
|
|
28
|
+
layer_idx=2, role=NeuronRole.OUTPUT, activation="linear", connection_init="none"
|
|
29
29
|
)
|
|
30
30
|
n_out = neurons[0]
|
|
31
31
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|