evonet 0.1.0.dev26__tar.gz → 0.1.0.dev28__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.dev26 → evonet-0.1.0.dev28}/PKG-INFO +1 -1
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/connection.py +19 -5
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/core.py +21 -8
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/pyproject.toml +2 -2
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_recurrent_dynamics.py +9 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/LICENSE +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/README.md +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/io.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/mutation.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/serialization.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/setup.cfg +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_nnet_io_and_forward.py +0 -0
|
@@ -7,7 +7,6 @@ Supports optional connection types for specialized behaviors (e.g. inhibitory,
|
|
|
7
7
|
recurrent).
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
from collections import deque
|
|
12
11
|
from typing import TYPE_CHECKING, Deque, Optional
|
|
13
12
|
|
|
@@ -38,18 +37,33 @@ class Connection:
|
|
|
38
37
|
conn_type: ConnectionType = ConnectionType.STANDARD,
|
|
39
38
|
) -> None:
|
|
40
39
|
|
|
40
|
+
if delay < 0:
|
|
41
|
+
raise ValueError("delay must be >= 0")
|
|
42
|
+
|
|
43
|
+
if conn_type is not ConnectionType.RECURRENT:
|
|
44
|
+
delay = 0
|
|
45
|
+
|
|
46
|
+
# Normalize: recurrent implies delay >= 1
|
|
47
|
+
if conn_type is ConnectionType.RECURRENT and delay == 0:
|
|
48
|
+
delay = 1
|
|
49
|
+
|
|
41
50
|
self.source = source
|
|
42
51
|
self.target = target
|
|
43
52
|
self.weight = weight
|
|
44
53
|
self.delay = delay
|
|
45
|
-
self.type
|
|
54
|
+
self.type = conn_type
|
|
46
55
|
|
|
47
56
|
self._history: Optional[Deque[float]] = None
|
|
48
|
-
if self.
|
|
57
|
+
if self.type is ConnectionType.RECURRENT:
|
|
49
58
|
self._history = deque(maxlen=self.delay)
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
def set_delay(self, delay: int) -> None:
|
|
61
|
+
if self.type is not ConnectionType.RECURRENT:
|
|
62
|
+
raise ValueError("set_delay is only valid for recurrent connections")
|
|
63
|
+
if delay <= 0:
|
|
64
|
+
delay = 1
|
|
65
|
+
self.delay = int(delay)
|
|
66
|
+
self._history = deque(maxlen=self.delay)
|
|
53
67
|
|
|
54
68
|
def push_source_output(self, value: float) -> None:
|
|
55
69
|
"""
|
|
@@ -351,6 +351,12 @@ class Nnet:
|
|
|
351
351
|
if weight is None:
|
|
352
352
|
weight = 0.0
|
|
353
353
|
|
|
354
|
+
# Normalize recurrent delays
|
|
355
|
+
if conn_type is ConnectionType.RECURRENT and delay <= 0:
|
|
356
|
+
delay = 1
|
|
357
|
+
if conn_type is not ConnectionType.RECURRENT:
|
|
358
|
+
delay = 0
|
|
359
|
+
|
|
354
360
|
conn = Connection(
|
|
355
361
|
source, target, weight=weight, conn_type=conn_type, delay=delay
|
|
356
362
|
)
|
|
@@ -397,17 +403,12 @@ class Nnet:
|
|
|
397
403
|
for i, n in enumerate(input_layer.neurons):
|
|
398
404
|
n.input = float(input_values[i])
|
|
399
405
|
|
|
400
|
-
# Preload recurrent contributions from
|
|
406
|
+
# Preload recurrent contributions from delay buffers.
|
|
401
407
|
for layer in self.layers:
|
|
402
408
|
for n in layer.neurons:
|
|
403
409
|
for c in n.incoming:
|
|
404
410
|
if c.type is ConnectionType.RECURRENT:
|
|
405
|
-
|
|
406
|
-
# Use delayed history if configured
|
|
407
|
-
n.input += c.weight * c.delayed_source_output()
|
|
408
|
-
else:
|
|
409
|
-
# default: 1-step recurrence via last_output
|
|
410
|
-
n.input += c.weight * c.source.last_output
|
|
411
|
+
n.input += c.weight * c.delayed_source_output()
|
|
411
412
|
|
|
412
413
|
# Feed-forward by layers: activate first, then propagate non-recurrent edges
|
|
413
414
|
for layer in self.layers:
|
|
@@ -442,7 +443,7 @@ class Nnet:
|
|
|
442
443
|
|
|
443
444
|
# Update recurrent delay buffers once per time step.
|
|
444
445
|
for c in self.get_all_connections():
|
|
445
|
-
if c.type is ConnectionType.RECURRENT
|
|
446
|
+
if c.type is ConnectionType.RECURRENT:
|
|
446
447
|
c.push_source_output(c.source.output)
|
|
447
448
|
|
|
448
449
|
return [n.output for n in self.layers[-1].neurons]
|
|
@@ -478,6 +479,7 @@ class Nnet:
|
|
|
478
479
|
colors_on: bool = True,
|
|
479
480
|
thickness_on: bool = False,
|
|
480
481
|
fillcolors_on: bool = False,
|
|
482
|
+
delay_on: bool = True,
|
|
481
483
|
) -> None:
|
|
482
484
|
"""
|
|
483
485
|
Render a visual representation of the network using Graphviz.
|
|
@@ -489,6 +491,7 @@ class Nnet:
|
|
|
489
491
|
colors_on (bool): Whether to color edges by sign.
|
|
490
492
|
thickness_on (bool): Whether to scale edge thickness by weight.
|
|
491
493
|
fillcolors_on (bool): Whether to color neurons by role.
|
|
494
|
+
delay_on (bool): Whether to show delay values on recurrent connections.
|
|
492
495
|
"""
|
|
493
496
|
|
|
494
497
|
if not self.layers:
|
|
@@ -541,14 +544,24 @@ class Nnet:
|
|
|
541
544
|
# Add edges
|
|
542
545
|
for conn in self.get_all_connections():
|
|
543
546
|
label = f"{conn.weight:.2f}" if labels_on else ""
|
|
547
|
+
|
|
548
|
+
# Optional delay annotation (only meaningful for recurrent connections)
|
|
549
|
+
if delay_on and labels_on and conn.type.name == "RECURRENT":
|
|
550
|
+
if label:
|
|
551
|
+
label = f"{label}\\nd={conn.delay}"
|
|
552
|
+
else:
|
|
553
|
+
label = f"d={conn.delay}"
|
|
554
|
+
|
|
544
555
|
color = (
|
|
545
556
|
"green"
|
|
546
557
|
if colors_on and conn.weight >= 0
|
|
547
558
|
else "red" if colors_on else "black"
|
|
548
559
|
)
|
|
560
|
+
|
|
549
561
|
penwidth = (
|
|
550
562
|
str(max(1, min(5, abs(conn.weight * 5)))) if thickness_on else "1"
|
|
551
563
|
)
|
|
564
|
+
|
|
552
565
|
style = "dashed" if conn.type.name == "RECURRENT" else "solid"
|
|
553
566
|
|
|
554
567
|
dot.edge(
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "evonet"
|
|
7
|
-
version = "0.1.0.
|
|
7
|
+
version = "0.1.0.dev28"
|
|
8
8
|
description = "Evolvable neural network core for integration with EvoLib"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "EvoLib", email = "evolib@dismail.de" }
|
|
@@ -48,7 +48,7 @@ include = ["evonet*"]
|
|
|
48
48
|
# Tool-Konfigurationen
|
|
49
49
|
[tool.black]
|
|
50
50
|
line-length = 88
|
|
51
|
-
target-version = ["
|
|
51
|
+
target-version = ["py312"]
|
|
52
52
|
|
|
53
53
|
[tool.isort]
|
|
54
54
|
profile = "black"
|
|
@@ -181,3 +181,12 @@ def test_reset_full_clears_delay_history() -> None:
|
|
|
181
181
|
# After full reset, history is gone -> delay contribution must be 0.0
|
|
182
182
|
y = net.calc([0.0])[0]
|
|
183
183
|
assert y == pytest.approx(0.0)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_recurrent_delay_zero_normalizes_to_one() -> None:
|
|
187
|
+
net, n_in, n_hid, _ = _mk_linear_ih_h_o()
|
|
188
|
+
c = net.add_connection(
|
|
189
|
+
n_hid, n_hid, weight=1.0, conn_type=ConnectionType.RECURRENT, delay=0
|
|
190
|
+
)
|
|
191
|
+
assert c is not None
|
|
192
|
+
assert c.delay == 1
|
|
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
|