evonet 0.1.0.dev18__tar.gz → 0.1.0.dev19__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.dev18 → evonet-0.1.0.dev19}/PKG-INFO +1 -1
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/core.py +9 -1
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/mutation.py +37 -16
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/pyproject.toml +1 -1
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/LICENSE +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/README.md +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/connection.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/io.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/serialization.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/setup.cfg +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_nnet_io_and_forward.py +0 -0
- {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_recurrent_dynamics.py +0 -0
|
@@ -255,7 +255,7 @@ class Nnet:
|
|
|
255
255
|
target: Neuron,
|
|
256
256
|
weight: float | None = None,
|
|
257
257
|
conn_type: ConnectionType = ConnectionType.STANDARD,
|
|
258
|
-
) -> None:
|
|
258
|
+
) -> Connection | None:
|
|
259
259
|
"""
|
|
260
260
|
Create a directed connection between two neurons.
|
|
261
261
|
|
|
@@ -264,8 +264,14 @@ class Nnet:
|
|
|
264
264
|
target (Neuron): Target neuron.
|
|
265
265
|
weight (float | None): Initial weight. If None, 0.0 is used.
|
|
266
266
|
conn_type (ConnectionType): Type of connection (e.g. standard, recurrent).
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Connection: Added Connection.
|
|
267
270
|
"""
|
|
268
271
|
|
|
272
|
+
if any(c.target == target for c in source.outgoing):
|
|
273
|
+
return None # duplicate
|
|
274
|
+
|
|
269
275
|
if weight is None:
|
|
270
276
|
weight = 0.0
|
|
271
277
|
|
|
@@ -273,6 +279,8 @@ class Nnet:
|
|
|
273
279
|
source.outgoing.append(conn)
|
|
274
280
|
target.incoming.append(conn)
|
|
275
281
|
|
|
282
|
+
return conn
|
|
283
|
+
|
|
276
284
|
def reset(self, full: bool = False) -> None:
|
|
277
285
|
"""Reset all neurons (clears input, output, and caches)."""
|
|
278
286
|
for layer in self.layers:
|
|
@@ -154,7 +154,7 @@ def add_random_connection(
|
|
|
154
154
|
net: Nnet,
|
|
155
155
|
allowed_recurrent: Optional[Collection[RecurrentKind | str]] = None,
|
|
156
156
|
connection_init: Literal["zero", "random", "near_zero"] = "zero",
|
|
157
|
-
) ->
|
|
157
|
+
) -> Connection | None:
|
|
158
158
|
"""
|
|
159
159
|
Add a valid connection between two randomly chosen neurons.
|
|
160
160
|
|
|
@@ -178,7 +178,7 @@ def add_random_connection(
|
|
|
178
178
|
- "near_zero": Weight ~ U(-0.05, 0.05)
|
|
179
179
|
|
|
180
180
|
Returns:
|
|
181
|
-
|
|
181
|
+
Connection: New connection, None otherwise.
|
|
182
182
|
"""
|
|
183
183
|
|
|
184
184
|
# Weight initialization
|
|
@@ -192,7 +192,7 @@ def add_random_connection(
|
|
|
192
192
|
|
|
193
193
|
all_neurons = net.get_all_neurons()
|
|
194
194
|
if len(all_neurons) < 2:
|
|
195
|
-
return
|
|
195
|
+
return None
|
|
196
196
|
|
|
197
197
|
# Build a dict of existing connections
|
|
198
198
|
existing: set[tuple[Neuron, Neuron]] = {
|
|
@@ -230,7 +230,7 @@ def add_random_connection(
|
|
|
230
230
|
candidates.append((src, dst)) # recurrent allowed by policy
|
|
231
231
|
|
|
232
232
|
if not candidates:
|
|
233
|
-
return
|
|
233
|
+
return None
|
|
234
234
|
|
|
235
235
|
src, dst = random.choice(candidates)
|
|
236
236
|
src_layer_idx, dst_layer_idx = layer_of[src], layer_of[dst]
|
|
@@ -240,24 +240,31 @@ def add_random_connection(
|
|
|
240
240
|
else ConnectionType.RECURRENT
|
|
241
241
|
)
|
|
242
242
|
|
|
243
|
-
net.add_connection(src, dst, weight=weight, conn_type=conn_type)
|
|
244
|
-
|
|
243
|
+
conn = net.add_connection(src, dst, weight=weight, conn_type=conn_type)
|
|
244
|
+
|
|
245
|
+
return conn
|
|
245
246
|
|
|
246
247
|
|
|
247
|
-
def remove_random_connection(net: Nnet) ->
|
|
248
|
+
def remove_random_connection(net: Nnet) -> bool:
|
|
248
249
|
"""
|
|
249
250
|
Remove a randomly selected connection from the network.
|
|
250
251
|
|
|
251
252
|
Does nothing if no connections are present.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
bool: True if a connection was removed, False otherwise.
|
|
252
256
|
"""
|
|
257
|
+
|
|
253
258
|
all_connections = net.get_all_connections()
|
|
254
259
|
if not all_connections:
|
|
255
|
-
return
|
|
260
|
+
return False
|
|
256
261
|
|
|
257
262
|
conn = random.choice(all_connections)
|
|
258
263
|
conn.source.outgoing.remove(conn)
|
|
259
264
|
conn.target.incoming.remove(conn)
|
|
260
265
|
|
|
266
|
+
return True
|
|
267
|
+
|
|
261
268
|
|
|
262
269
|
def add_random_neuron(
|
|
263
270
|
net: Nnet,
|
|
@@ -265,7 +272,7 @@ def add_random_neuron(
|
|
|
265
272
|
connection_init: Literal["zero", "random", "near_zero", "none"] = "zero",
|
|
266
273
|
connection_scope: Literal["adjacent", "crosslayer"] = "adjacent",
|
|
267
274
|
connection_density: float = 1.0,
|
|
268
|
-
) -> None:
|
|
275
|
+
) -> Neuron | None:
|
|
269
276
|
"""
|
|
270
277
|
Insert a new hidden neuron into a random layer.
|
|
271
278
|
|
|
@@ -275,9 +282,13 @@ def add_random_neuron(
|
|
|
275
282
|
net (Nnet): The target network.
|
|
276
283
|
activations (list[str] | None): Optional list of allowed activation functions.
|
|
277
284
|
If None, all registered activations are used.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Neuron: Added neurons, or None
|
|
278
288
|
"""
|
|
289
|
+
|
|
279
290
|
if len(net.layers) < 2:
|
|
280
|
-
return
|
|
291
|
+
return None
|
|
281
292
|
|
|
282
293
|
if len(net.layers) == 2:
|
|
283
294
|
net.insert_layer(1)
|
|
@@ -285,18 +296,20 @@ def add_random_neuron(
|
|
|
285
296
|
# Choose target layer (not input, not output)
|
|
286
297
|
candidate_layers = net.layers[1:-1]
|
|
287
298
|
if not candidate_layers:
|
|
288
|
-
return
|
|
299
|
+
return None
|
|
289
300
|
|
|
290
301
|
layer = random.choice(candidate_layers)
|
|
291
302
|
|
|
292
|
-
net.add_neuron(
|
|
303
|
+
new_neuron = net.add_neuron(
|
|
293
304
|
layer_idx=net.layers.index(layer),
|
|
294
305
|
activation=random_function_name(activations),
|
|
295
306
|
role=NeuronRole.HIDDEN,
|
|
296
307
|
connection_init=connection_init,
|
|
297
308
|
connection_scope=connection_scope,
|
|
298
309
|
connection_density=connection_density,
|
|
299
|
-
)
|
|
310
|
+
)[0]
|
|
311
|
+
|
|
312
|
+
return new_neuron
|
|
300
313
|
|
|
301
314
|
|
|
302
315
|
def remove_random_neuron(net: Nnet) -> None:
|
|
@@ -324,7 +337,9 @@ def remove_random_neuron(net: Nnet) -> None:
|
|
|
324
337
|
break
|
|
325
338
|
|
|
326
339
|
|
|
327
|
-
def split_connection(
|
|
340
|
+
def split_connection(
|
|
341
|
+
net: Nnet, activation: str = "tanh", noise: float = 0.1
|
|
342
|
+
) -> Neuron | None:
|
|
328
343
|
"""
|
|
329
344
|
Insert a neuron in the middle of an existing connection.
|
|
330
345
|
|
|
@@ -336,10 +351,14 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
|
|
|
336
351
|
net (Nnet): The target network.
|
|
337
352
|
activation (str): Activation function for the new neuron.
|
|
338
353
|
noise (float): Optional noise applied to new weights.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Neuron: Added neuron, or None
|
|
339
357
|
"""
|
|
358
|
+
|
|
340
359
|
all_connections = net.get_all_connections()
|
|
341
360
|
if not all_connections:
|
|
342
|
-
return
|
|
361
|
+
return None
|
|
343
362
|
|
|
344
363
|
conn = random.choice(all_connections)
|
|
345
364
|
src, dst = conn.source, conn.target
|
|
@@ -351,7 +370,7 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
|
|
|
351
370
|
break
|
|
352
371
|
|
|
353
372
|
if insert_idx is None or insert_idx >= len(net.layers):
|
|
354
|
-
return
|
|
373
|
+
return None
|
|
355
374
|
|
|
356
375
|
new_neuron = net.add_neuron(
|
|
357
376
|
layer_idx=insert_idx,
|
|
@@ -367,3 +386,5 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
|
|
|
367
386
|
weight = 1.0 + np.random.normal(0, noise)
|
|
368
387
|
net.add_connection(src, new_neuron, weight=weight)
|
|
369
388
|
net.add_connection(new_neuron, dst, weight=conn.weight)
|
|
389
|
+
|
|
390
|
+
return new_neuron
|
|
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
|