evonet 0.1.0.dev15__tar.gz → 0.1.0.dev16__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.dev15 → evonet-0.1.0.dev16}/PKG-INFO +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/core.py +54 -23
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/pyproject.toml +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/LICENSE +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/README.md +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/connection.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/io.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/mutation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/serialization.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/setup.cfg +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/tests/test_nnet_io_and_forward.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev16}/tests/test_recurrent_dynamics.py +0 -0
|
@@ -9,6 +9,7 @@ export interfaces.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
import random
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Literal, Optional
|
|
14
15
|
|
|
@@ -105,6 +106,8 @@ class Nnet:
|
|
|
105
106
|
count: int = 1,
|
|
106
107
|
connection_init: Literal["random", "zero", "near_zero", "none"] = "zero",
|
|
107
108
|
recurrent: Optional[set[RecurrentKind]] = None,
|
|
109
|
+
connection_scope: Literal["adjacent", "crosslayer"] = "adjacent",
|
|
110
|
+
connection_density: float = 1.0,
|
|
108
111
|
) -> list[Neuron]:
|
|
109
112
|
"""
|
|
110
113
|
Add one or more neurons to the network.
|
|
@@ -122,6 +125,13 @@ class Nnet:
|
|
|
122
125
|
"near_zero" - connect with small random weights (-0.05, 0.05)
|
|
123
126
|
"none" - do not create connections (feedforward + recurrent)
|
|
124
127
|
recurrent: Optional recurrent connection types.
|
|
128
|
+
connection_scope:
|
|
129
|
+
- "adjacent": only connect to directly neighboring layers (default)
|
|
130
|
+
- "crosslayer": connect to all earlier and later
|
|
131
|
+
layers (feedforward only)
|
|
132
|
+
connection_density:
|
|
133
|
+
Fraction of possible connections that should actually be created.
|
|
134
|
+
Must be in (0, 1]. A value <1.0 randomly samples a subset.
|
|
125
135
|
|
|
126
136
|
Returns:
|
|
127
137
|
list[Neuron]: List of added neurons.
|
|
@@ -129,11 +139,12 @@ class Nnet:
|
|
|
129
139
|
if layer_idx is None:
|
|
130
140
|
layer_idx = len(self.layers) - 1 # Add neuron to last layer
|
|
131
141
|
|
|
132
|
-
if layer_idx < 0:
|
|
133
|
-
raise ValueError(f"Layer index must be >= 0 (got {layer_idx})")
|
|
134
|
-
if layer_idx >= len(self.layers):
|
|
142
|
+
if layer_idx < 0 or layer_idx >= len(self.layers):
|
|
135
143
|
raise ValueError(f"Layer index out of bounds: {layer_idx}")
|
|
136
144
|
|
|
145
|
+
if not (0.0 < connection_density <= 1.0):
|
|
146
|
+
raise ValueError("connection_density must be in (0, 1].")
|
|
147
|
+
|
|
137
148
|
target_layer = self.layers[layer_idx]
|
|
138
149
|
new_neurons: list[Neuron] = []
|
|
139
150
|
|
|
@@ -145,29 +156,49 @@ class Nnet:
|
|
|
145
156
|
target_layer.neurons.append(neuron)
|
|
146
157
|
new_neurons.append(neuron)
|
|
147
158
|
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
skip_connections = connection_init == "none"
|
|
159
|
+
# Skip connection creation if requested
|
|
160
|
+
if connection_init == "none":
|
|
161
|
+
return new_neurons
|
|
152
162
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
weight = connection_init_value(connection_init)
|
|
164
|
+
possible_pairs: list[tuple[Neuron, Neuron]] = []
|
|
165
|
+
|
|
166
|
+
# Connection logic
|
|
167
|
+
if connection_scope == "adjacent":
|
|
168
|
+
# Previous layer ---> new neurons
|
|
169
|
+
if layer_idx > 0:
|
|
170
|
+
for src in self.layers[layer_idx - 1].neurons:
|
|
171
|
+
for n in new_neurons:
|
|
172
|
+
possible_pairs.append((src, n))
|
|
173
|
+
# New neurons ---> next layer (only for hidden neurons)
|
|
174
|
+
if role == NeuronRole.HIDDEN and layer_idx < len(self.layers) - 1:
|
|
175
|
+
for dst in self.layers[layer_idx + 1].neurons:
|
|
176
|
+
for n in new_neurons:
|
|
177
|
+
possible_pairs.append((n, dst))
|
|
178
|
+
|
|
179
|
+
elif connection_scope == "crosslayer":
|
|
180
|
+
# All earlier layers ---> new neurons
|
|
181
|
+
for i in range(0, layer_idx):
|
|
182
|
+
for src in self.layers[i].neurons:
|
|
183
|
+
for n in new_neurons:
|
|
184
|
+
possible_pairs.append((src, n))
|
|
185
|
+
# New neurons ---> all later layers
|
|
186
|
+
for j in range(layer_idx + 1, len(self.layers)):
|
|
187
|
+
for dst in self.layers[j].neurons:
|
|
188
|
+
for n in new_neurons:
|
|
189
|
+
possible_pairs.append((n, dst))
|
|
190
|
+
|
|
191
|
+
# Density control: subsample of possible pairs
|
|
192
|
+
if connection_density < 1.0 and possible_pairs:
|
|
193
|
+
k = max(1, int(len(possible_pairs) * connection_density))
|
|
194
|
+
possible_pairs = random.sample(possible_pairs, k)
|
|
195
|
+
|
|
196
|
+
# Create connections
|
|
197
|
+
for src, dst in possible_pairs:
|
|
198
|
+
self.add_connection(src, dst, weight=weight)
|
|
168
199
|
|
|
169
200
|
# Recurrent connections
|
|
170
|
-
if recurrent
|
|
201
|
+
if recurrent:
|
|
171
202
|
if RecurrentKind.DIRECT in recurrent:
|
|
172
203
|
for n in new_neurons:
|
|
173
204
|
if n.role != NeuronRole.HIDDEN:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|