evonet 0.1.0.dev25__tar.gz → 0.1.0.dev27__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.dev25 → evonet-0.1.0.dev27}/PKG-INFO +1 -1
- evonet-0.1.0.dev27/evonet/connection.py +124 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/core.py +23 -3
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/mutation.py +3 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/serialization.py +10 -1
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/pyproject.toml +2 -2
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/tests/test_recurrent_dynamics.py +86 -0
- evonet-0.1.0.dev25/evonet/connection.py +0 -70
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/LICENSE +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/README.md +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/enums.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/io.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/utils.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/setup.cfg +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev25 → evonet-0.1.0.dev27}/tests/test_nnet_io_and_forward.py +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""
|
|
3
|
+
Connection between neurons in an evolvable neural network.
|
|
4
|
+
|
|
5
|
+
A connection links a source neuron to a target neuron and transmits a weighted signal.
|
|
6
|
+
Supports optional connection types for specialized behaviors (e.g. inhibitory,
|
|
7
|
+
recurrent).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from collections import deque
|
|
12
|
+
from typing import TYPE_CHECKING, Deque, Optional
|
|
13
|
+
|
|
14
|
+
from evonet.enums import ConnectionType
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from evonet.neuron import Neuron
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Connection:
|
|
21
|
+
"""
|
|
22
|
+
Represents a directed, weighted connection between two neurons.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
source (Neuron): The source neuron (presynaptic).
|
|
26
|
+
target (Neuron): The target neuron (postsynaptic).
|
|
27
|
+
weight (float): Multiplicative weight of the transmitted signal.
|
|
28
|
+
delay (int): Delay in discrete time steps (used for recurrent edges).
|
|
29
|
+
type (ConnectionType): Type of connection (e.g. standard, recurrent).
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
source: "Neuron",
|
|
35
|
+
target: "Neuron",
|
|
36
|
+
weight: float = 1.0,
|
|
37
|
+
delay: int = 0,
|
|
38
|
+
conn_type: ConnectionType = ConnectionType.STANDARD,
|
|
39
|
+
) -> None:
|
|
40
|
+
|
|
41
|
+
if delay < 0:
|
|
42
|
+
raise ValueError("delay must be >= 0")
|
|
43
|
+
|
|
44
|
+
# Normalize: recurrent implies delay >= 1
|
|
45
|
+
if conn_type is ConnectionType.RECURRENT and delay == 0:
|
|
46
|
+
delay = 1
|
|
47
|
+
|
|
48
|
+
self.source = source
|
|
49
|
+
self.target = target
|
|
50
|
+
self.weight = weight
|
|
51
|
+
self.delay = delay
|
|
52
|
+
self.type = conn_type
|
|
53
|
+
|
|
54
|
+
self._history: Optional[Deque[float]] = None
|
|
55
|
+
if self.type is ConnectionType.RECURRENT:
|
|
56
|
+
self._history = deque(maxlen=self.delay)
|
|
57
|
+
|
|
58
|
+
def set_delay(self, delay: int) -> None:
|
|
59
|
+
if self.type is not ConnectionType.RECURRENT:
|
|
60
|
+
raise ValueError("set_delay is only valid for recurrent connections")
|
|
61
|
+
if delay <= 0:
|
|
62
|
+
delay = 1
|
|
63
|
+
self.delay = int(delay)
|
|
64
|
+
self._history = deque(maxlen=self.delay)
|
|
65
|
+
|
|
66
|
+
def push_source_output(self, value: float) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Push the current source output into the delay buffer.
|
|
69
|
+
|
|
70
|
+
This should be called once per time step after the network computed outputs.
|
|
71
|
+
"""
|
|
72
|
+
if self._history is None:
|
|
73
|
+
return
|
|
74
|
+
self._history.append(float(value))
|
|
75
|
+
|
|
76
|
+
def delayed_source_output(self) -> float:
|
|
77
|
+
"""
|
|
78
|
+
Return the delayed source output.
|
|
79
|
+
|
|
80
|
+
Semantics:
|
|
81
|
+
- delay == 0 -> no delay buffer used (caller decides what to do)
|
|
82
|
+
- delay > 0 -> returns output[t-delay] if available else 0.0
|
|
83
|
+
"""
|
|
84
|
+
if self.delay == 0:
|
|
85
|
+
return 0.0
|
|
86
|
+
|
|
87
|
+
if self._history is None:
|
|
88
|
+
return 0.0
|
|
89
|
+
|
|
90
|
+
if len(self._history) < self.delay:
|
|
91
|
+
return 0.0
|
|
92
|
+
|
|
93
|
+
return float(self._history[0])
|
|
94
|
+
|
|
95
|
+
def reset_buffer(self) -> None:
|
|
96
|
+
"""Clear the internal delay history buffer."""
|
|
97
|
+
if self._history is not None:
|
|
98
|
+
self._history.clear()
|
|
99
|
+
|
|
100
|
+
def get_signal(self) -> float:
|
|
101
|
+
"""
|
|
102
|
+
Return the weighted signal from the source neuron.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
float: source.output × weight
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
return self.source.output * self.weight
|
|
109
|
+
|
|
110
|
+
def __repr__(self) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Return a concise string representation of the connection.
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
<Conn abc123 -> def456 w=0.85 d=3 type=standard>
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
type_str = self.type.name.lower()
|
|
119
|
+
return (
|
|
120
|
+
f"<Conn {self.source.id[:6]} "
|
|
121
|
+
f"-> {self.target.id[:6]} "
|
|
122
|
+
f"d={self.delay} "
|
|
123
|
+
f"w={self.weight:.2f} type={type_str}>"
|
|
124
|
+
)
|
|
@@ -318,7 +318,7 @@ class Nnet:
|
|
|
318
318
|
# --------------------------------------------------------------
|
|
319
319
|
# Create connections
|
|
320
320
|
# --------------------------------------------------------------
|
|
321
|
-
for src, dst, ctype in
|
|
321
|
+
for src, dst, ctype in sampled:
|
|
322
322
|
self.add_connection(src, dst, weight=weight, conn_type=ctype)
|
|
323
323
|
|
|
324
324
|
return new_neurons
|
|
@@ -329,6 +329,7 @@ class Nnet:
|
|
|
329
329
|
target: Neuron,
|
|
330
330
|
weight: float | None = None,
|
|
331
331
|
conn_type: ConnectionType = ConnectionType.STANDARD,
|
|
332
|
+
delay: int = 0,
|
|
332
333
|
) -> Connection | None:
|
|
333
334
|
"""
|
|
334
335
|
Create a directed connection between two neurons.
|
|
@@ -338,6 +339,7 @@ class Nnet:
|
|
|
338
339
|
target (Neuron): Target neuron.
|
|
339
340
|
weight (float | None): Initial weight. If None, 0.0 is used.
|
|
340
341
|
conn_type (ConnectionType): Type of connection (e.g. standard, recurrent).
|
|
342
|
+
delay (int): Delay in discrete time steps.
|
|
341
343
|
|
|
342
344
|
Returns:
|
|
343
345
|
Connection: Added Connection.
|
|
@@ -349,7 +351,15 @@ class Nnet:
|
|
|
349
351
|
if weight is None:
|
|
350
352
|
weight = 0.0
|
|
351
353
|
|
|
352
|
-
|
|
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 and delay < 0:
|
|
358
|
+
delay = 0
|
|
359
|
+
|
|
360
|
+
conn = Connection(
|
|
361
|
+
source, target, weight=weight, conn_type=conn_type, delay=delay
|
|
362
|
+
)
|
|
353
363
|
source.outgoing.append(conn)
|
|
354
364
|
target.incoming.append(conn)
|
|
355
365
|
|
|
@@ -361,6 +371,11 @@ class Nnet:
|
|
|
361
371
|
for neuron in layer.neurons:
|
|
362
372
|
neuron.reset(full=full)
|
|
363
373
|
|
|
374
|
+
if full:
|
|
375
|
+
# Clear delayed history buffers on connections.
|
|
376
|
+
for conn in self.get_all_connections():
|
|
377
|
+
conn.reset_buffer()
|
|
378
|
+
|
|
364
379
|
def calc(self, input_values: list[float]) -> list[float]:
|
|
365
380
|
"""
|
|
366
381
|
Perform a forward pass through the network.
|
|
@@ -393,7 +408,7 @@ class Nnet:
|
|
|
393
408
|
for n in layer.neurons:
|
|
394
409
|
for c in n.incoming:
|
|
395
410
|
if c.type is ConnectionType.RECURRENT:
|
|
396
|
-
|
|
411
|
+
n.input += c.weight * c.delayed_source_output()
|
|
397
412
|
|
|
398
413
|
# Feed-forward by layers: activate first, then propagate non-recurrent edges
|
|
399
414
|
for layer in self.layers:
|
|
@@ -426,6 +441,11 @@ class Nnet:
|
|
|
426
441
|
if c.type is not ConnectionType.RECURRENT:
|
|
427
442
|
c.target.input += c.weight * n.output
|
|
428
443
|
|
|
444
|
+
# Update recurrent delay buffers once per time step.
|
|
445
|
+
for c in self.get_all_connections():
|
|
446
|
+
if c.type is ConnectionType.RECURRENT:
|
|
447
|
+
c.push_source_output(c.source.output)
|
|
448
|
+
|
|
429
449
|
return [n.output for n in self.layers[-1].neurons]
|
|
430
450
|
|
|
431
451
|
def get_all_neurons(self) -> list[Neuron]:
|
|
@@ -300,6 +300,9 @@ def add_random_neuron(
|
|
|
300
300
|
if len(net.layers) == 2:
|
|
301
301
|
net.insert_layer(1)
|
|
302
302
|
|
|
303
|
+
if dynamics_params is None:
|
|
304
|
+
dynamics_params = {}
|
|
305
|
+
|
|
303
306
|
# Choose target layer (not input, not output)
|
|
304
307
|
candidate_layers = net.layers[1:-1]
|
|
305
308
|
if not candidate_layers:
|
|
@@ -49,12 +49,15 @@ def to_dict(net: Nnet) -> dict[str, Any]:
|
|
|
49
49
|
"bias": n.bias,
|
|
50
50
|
"role": n.role.name,
|
|
51
51
|
"label": n.label,
|
|
52
|
+
"dynamics_name": n.dynamics_name,
|
|
53
|
+
"dynamics_params": n.dynamics_params,
|
|
52
54
|
"incoming": [
|
|
53
55
|
{
|
|
54
56
|
"source": c.source.id,
|
|
55
57
|
"target": c.target.id,
|
|
56
58
|
"weight": c.weight,
|
|
57
59
|
"type": c.type.name, # store enum as string
|
|
60
|
+
"delay": c.delay,
|
|
58
61
|
}
|
|
59
62
|
for c in n.incoming
|
|
60
63
|
],
|
|
@@ -81,6 +84,8 @@ def from_dict(data: dict[str, Any]) -> Nnet:
|
|
|
81
84
|
bias=n_info["bias"],
|
|
82
85
|
role=NeuronRole[n_info["role"]],
|
|
83
86
|
label=n_info.get("label", ""),
|
|
87
|
+
dynamics_name=n_info.get("dynamics_name", "standard"),
|
|
88
|
+
dynamics_params=n_info.get("dynamics_params", {}),
|
|
84
89
|
connection_init="none",
|
|
85
90
|
)[0]
|
|
86
91
|
n.id = n_info["id"]
|
|
@@ -92,11 +97,15 @@ def from_dict(data: dict[str, Any]) -> Nnet:
|
|
|
92
97
|
for c_info in n_info["incoming"]:
|
|
93
98
|
src = neuron_map[c_info["source"]]
|
|
94
99
|
dst = neuron_map[c_info["target"]]
|
|
100
|
+
delay = int(c_info.get("delay", 0))
|
|
101
|
+
conn_type = ConnectionType[c_info["type"]]
|
|
102
|
+
|
|
95
103
|
net.add_connection(
|
|
96
104
|
src,
|
|
97
105
|
dst,
|
|
98
106
|
weight=c_info["weight"],
|
|
99
|
-
conn_type=
|
|
107
|
+
conn_type=conn_type,
|
|
108
|
+
delay=delay,
|
|
100
109
|
)
|
|
101
110
|
|
|
102
111
|
return net
|
|
@@ -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.dev27"
|
|
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"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
import pytest
|
|
2
3
|
|
|
3
4
|
from evonet.core import Nnet
|
|
4
5
|
from evonet.enums import ConnectionType, NeuronRole
|
|
@@ -104,3 +105,88 @@ def test_recurrent_back_edge_output_to_hidden_linear() -> None:
|
|
|
104
105
|
actual_outputs.append(y)
|
|
105
106
|
|
|
106
107
|
assert np.allclose(actual_outputs, expected_outputs, atol=1e-12)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_recurrent_delay_two_steps_linear_chain() -> None:
|
|
111
|
+
"""
|
|
112
|
+
A minimal sequence test for recurrent delay buffers.
|
|
113
|
+
|
|
114
|
+
Network:
|
|
115
|
+
input -> hidden -> output
|
|
116
|
+
hidden has a recurrent self-loop with delay=2
|
|
117
|
+
|
|
118
|
+
Expected behaviour (linear activations, weights=1):
|
|
119
|
+
t0 input=1 -> hidden=1, output=1
|
|
120
|
+
t1 input=0 -> hidden=0, output=0 (delay=2 not yet filled)
|
|
121
|
+
t2 input=0 -> hidden=1, output=1 (uses hidden output from t0)
|
|
122
|
+
"""
|
|
123
|
+
net = Nnet()
|
|
124
|
+
net.add_layer() # 0 input
|
|
125
|
+
net.add_layer() # 1 hidden
|
|
126
|
+
net.add_layer() # 2 output
|
|
127
|
+
|
|
128
|
+
n_in = net.add_neuron(
|
|
129
|
+
layer_idx=0, activation="linear", role=NeuronRole.INPUT, connection_init="none"
|
|
130
|
+
)[0]
|
|
131
|
+
n_h = net.add_neuron(
|
|
132
|
+
layer_idx=1, activation="linear", role=NeuronRole.HIDDEN, connection_init="none"
|
|
133
|
+
)[0]
|
|
134
|
+
n_out = net.add_neuron(
|
|
135
|
+
layer_idx=2, activation="linear", role=NeuronRole.OUTPUT, connection_init="none"
|
|
136
|
+
)[0]
|
|
137
|
+
|
|
138
|
+
net.add_connection(n_in, n_h, weight=1.0, conn_type=ConnectionType.STANDARD)
|
|
139
|
+
net.add_connection(n_h, n_out, weight=1.0, conn_type=ConnectionType.STANDARD)
|
|
140
|
+
|
|
141
|
+
# Recurrent self-loop with delay=2
|
|
142
|
+
net.add_connection(
|
|
143
|
+
n_h, n_h, weight=1.0, conn_type=ConnectionType.RECURRENT, delay=2
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
y0 = net.calc([1.0])[0]
|
|
147
|
+
y1 = net.calc([0.0])[0]
|
|
148
|
+
y2 = net.calc([0.0])[0]
|
|
149
|
+
|
|
150
|
+
assert y0 == pytest.approx(1.0)
|
|
151
|
+
assert y1 == pytest.approx(0.0)
|
|
152
|
+
assert y2 == pytest.approx(1.0)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_reset_full_clears_delay_history() -> None:
|
|
156
|
+
"""Full reset must clear delay buffers, otherwise episodes leak memory."""
|
|
157
|
+
net = Nnet()
|
|
158
|
+
net.add_layer()
|
|
159
|
+
net.add_layer()
|
|
160
|
+
net.add_layer()
|
|
161
|
+
|
|
162
|
+
n_in = net.add_neuron(
|
|
163
|
+
layer_idx=0, activation="linear", role=NeuronRole.INPUT, connection_init="none"
|
|
164
|
+
)[0]
|
|
165
|
+
n_h = net.add_neuron(
|
|
166
|
+
layer_idx=1, activation="linear", role=NeuronRole.HIDDEN, connection_init="none"
|
|
167
|
+
)[0]
|
|
168
|
+
n_out = net.add_neuron(
|
|
169
|
+
layer_idx=2, activation="linear", role=NeuronRole.OUTPUT, connection_init="none"
|
|
170
|
+
)[0]
|
|
171
|
+
|
|
172
|
+
net.add_connection(n_in, n_h, weight=1.0, conn_type=ConnectionType.STANDARD)
|
|
173
|
+
net.add_connection(n_h, n_out, weight=1.0, conn_type=ConnectionType.STANDARD)
|
|
174
|
+
net.add_connection(
|
|
175
|
+
n_h, n_h, weight=1.0, conn_type=ConnectionType.RECURRENT, delay=2
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
_ = net.calc([1.0])[0] # fills history partially
|
|
179
|
+
net.reset(full=True)
|
|
180
|
+
|
|
181
|
+
# After full reset, history is gone -> delay contribution must be 0.0
|
|
182
|
+
y = net.calc([0.0])[0]
|
|
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
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
"""
|
|
3
|
-
Connection between neurons in an evolvable neural network.
|
|
4
|
-
|
|
5
|
-
A connection links a source neuron to a target neuron and transmits a weighted signal.
|
|
6
|
-
Supports optional connection types for specialized behaviors (e.g. inhibitory,
|
|
7
|
-
recurrent).
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from typing import TYPE_CHECKING
|
|
12
|
-
|
|
13
|
-
from evonet.enums import ConnectionType
|
|
14
|
-
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from evonet.neuron import Neuron
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class Connection:
|
|
20
|
-
"""
|
|
21
|
-
Represents a directed, weighted connection between two neurons.
|
|
22
|
-
|
|
23
|
-
Attributes:
|
|
24
|
-
source (Neuron): The source neuron (presynaptic).
|
|
25
|
-
target (Neuron): The target neuron (postsynaptic).
|
|
26
|
-
weight (float): Multiplicative weight of the transmitted signal.
|
|
27
|
-
delay (int): Optional delay in time steps (not yet implemented).
|
|
28
|
-
type (ConnectionType): Type of connection (e.g. standard, recurrent,
|
|
29
|
-
inhibitory).
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
def __init__(
|
|
33
|
-
self,
|
|
34
|
-
source: "Neuron",
|
|
35
|
-
target: "Neuron",
|
|
36
|
-
weight: float = 1.0,
|
|
37
|
-
delay: int = 0,
|
|
38
|
-
conn_type: ConnectionType = ConnectionType.STANDARD,
|
|
39
|
-
) -> None:
|
|
40
|
-
|
|
41
|
-
self.source = source
|
|
42
|
-
self.target = target
|
|
43
|
-
self.weight = weight
|
|
44
|
-
self.delay = delay
|
|
45
|
-
self.type: ConnectionType = conn_type
|
|
46
|
-
|
|
47
|
-
def get_signal(self) -> float:
|
|
48
|
-
"""
|
|
49
|
-
Return the weighted signal from the source neuron.
|
|
50
|
-
|
|
51
|
-
Returns:
|
|
52
|
-
float: source.output × weight
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
return self.source.output * self.weight
|
|
56
|
-
|
|
57
|
-
def __repr__(self) -> str:
|
|
58
|
-
"""
|
|
59
|
-
Return a concise string representation of the connection.
|
|
60
|
-
|
|
61
|
-
Example:
|
|
62
|
-
<Conn abc123 -> def456 w=0.85 type=standard>
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
type_str = self.type.name.lower()
|
|
66
|
-
return (
|
|
67
|
-
f"<Conn {self.source.id[:6]} "
|
|
68
|
-
f"-> {self.target.id[:6]} "
|
|
69
|
-
f"w={self.weight:.2f} type={type_str}>"
|
|
70
|
-
)
|
|
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
|