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.
Files changed (27) hide show
  1. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/PKG-INFO +1 -1
  2. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/connection.py +19 -5
  3. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/core.py +21 -8
  4. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/PKG-INFO +1 -1
  5. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/pyproject.toml +2 -2
  6. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_recurrent_dynamics.py +9 -0
  7. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/LICENSE +0 -0
  8. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/README.md +0 -0
  9. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/__init__.py +0 -0
  10. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/activation.py +0 -0
  11. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/core_mit_plot_simple.py +0 -0
  12. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/enums.py +0 -0
  13. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/io.py +0 -0
  14. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/layer.py +0 -0
  15. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/mutation.py +0 -0
  16. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/neuron.py +0 -0
  17. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/serialization.py +0 -0
  18. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/utils.py +0 -0
  19. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet/visualize.py +0 -0
  20. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/SOURCES.txt +0 -0
  21. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/dependency_links.txt +0 -0
  22. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/requires.txt +0 -0
  23. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/evonet.egg-info/top_level.txt +0 -0
  24. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/setup.cfg +0 -0
  25. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_activation.py +0 -0
  26. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_core.py +0 -0
  27. {evonet-0.1.0.dev26 → evonet-0.1.0.dev28}/tests/test_nnet_io_and_forward.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evonet
3
- Version: 0.1.0.dev26
3
+ Version: 0.1.0.dev28
4
4
  Summary: Evolvable neural network core for integration with EvoLib
5
5
  Author-email: EvoLib <evolib@dismail.de>
6
6
  License: MIT License
@@ -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: ConnectionType = conn_type
54
+ self.type = conn_type
46
55
 
47
56
  self._history: Optional[Deque[float]] = None
48
- if self.delay > 0:
57
+ if self.type is ConnectionType.RECURRENT:
49
58
  self._history = deque(maxlen=self.delay)
50
59
 
51
- if self.delay < 0:
52
- raise ValueError("delay must be >= 0")
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 previous time step (last_output)
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
- if c.delay > 0:
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 and c.delay > 0:
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(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evonet
3
- Version: 0.1.0.dev26
3
+ Version: 0.1.0.dev28
4
4
  Summary: Evolvable neural network core for integration with EvoLib
5
5
  Author-email: EvoLib <evolib@dismail.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "evonet"
7
- version = "0.1.0.dev26"
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 = ["py311"]
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