evonet 0.1.0.dev15__tar.gz → 0.1.0.dev17__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.dev17}/PKG-INFO +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/core.py +55 -22
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/pyproject.toml +1 -1
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/LICENSE +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/README.md +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/connection.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/io.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/mutation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/serialization.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/setup.cfg +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/tests/test_nnet_io_and_forward.py +0 -0
- {evonet-0.1.0.dev15 → evonet-0.1.0.dev17}/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,51 @@ class Nnet:
|
|
|
145
156
|
target_layer.neurons.append(neuron)
|
|
146
157
|
new_neurons.append(neuron)
|
|
147
158
|
|
|
148
|
-
#
|
|
149
|
-
|
|
159
|
+
# Skip connection creation if requested
|
|
160
|
+
if connection_init == "none":
|
|
161
|
+
return new_neurons
|
|
150
162
|
|
|
151
|
-
|
|
163
|
+
weight = connection_init_value(connection_init)
|
|
152
164
|
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
# Build connections for each new neuron
|
|
166
|
+
for n in new_neurons:
|
|
167
|
+
|
|
168
|
+
possible_in: list[Neuron] = []
|
|
169
|
+
possible_out: list[Neuron] = []
|
|
170
|
+
|
|
171
|
+
# ADJACENT connections
|
|
172
|
+
if connection_scope == "adjacent":
|
|
173
|
+
if layer_idx > 0:
|
|
174
|
+
possible_in.extend(self.layers[layer_idx - 1].neurons)
|
|
175
|
+
if role == NeuronRole.HIDDEN and layer_idx < len(self.layers) - 1:
|
|
176
|
+
possible_out.extend(self.layers[layer_idx + 1].neurons)
|
|
177
|
+
|
|
178
|
+
# CROSSLAYER connections
|
|
179
|
+
elif connection_scope == "crosslayer":
|
|
180
|
+
# All earlier layers ---> new neuron
|
|
181
|
+
for i in range(0, layer_idx):
|
|
182
|
+
possible_in.extend(self.layers[i].neurons)
|
|
183
|
+
# New neuron ---> all later layers
|
|
184
|
+
for j in range(layer_idx + 1, len(self.layers)):
|
|
185
|
+
possible_out.extend(self.layers[j].neurons)
|
|
186
|
+
|
|
187
|
+
# Density control per neuron
|
|
188
|
+
if connection_density < 1.0:
|
|
189
|
+
if possible_in:
|
|
190
|
+
k_in = max(1, int(len(possible_in) * connection_density))
|
|
191
|
+
possible_in = random.sample(possible_in, k_in)
|
|
192
|
+
if possible_out:
|
|
193
|
+
k_out = max(1, int(len(possible_out) * connection_density))
|
|
194
|
+
possible_out = random.sample(possible_out, k_out)
|
|
195
|
+
|
|
196
|
+
# Create the actual connections
|
|
197
|
+
for src in possible_in:
|
|
198
|
+
self.add_connection(src, n, weight=weight)
|
|
199
|
+
for dst in possible_out:
|
|
200
|
+
self.add_connection(n, dst, weight=weight)
|
|
168
201
|
|
|
169
202
|
# Recurrent connections
|
|
170
|
-
if recurrent
|
|
203
|
+
if recurrent:
|
|
171
204
|
if RecurrentKind.DIRECT in recurrent:
|
|
172
205
|
for n in new_neurons:
|
|
173
206
|
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
|