evonet 0.1.0.dev13__tar.gz → 0.1.0.dev15__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.dev13 → evonet-0.1.0.dev15}/PKG-INFO +1 -1
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/core.py +7 -8
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/enums.py +18 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/mutation.py +8 -10
- evonet-0.1.0.dev15/evonet/utils.py +28 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet.egg-info/PKG-INFO +1 -1
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/pyproject.toml +1 -1
- evonet-0.1.0.dev13/evonet/utils.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/LICENSE +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/README.md +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/__init__.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/activation.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/connection.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/core_mit_plot_simple.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/io.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/layer.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/neuron.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/serialization.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet/visualize.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet.egg-info/SOURCES.txt +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet.egg-info/dependency_links.txt +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet.egg-info/requires.txt +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/evonet.egg-info/top_level.txt +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/setup.cfg +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/tests/test_activation.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/tests/test_core.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/tests/test_nnet_io_and_forward.py +0 -0
- {evonet-0.1.0.dev13 → evonet-0.1.0.dev15}/tests/test_recurrent_dynamics.py +0 -0
|
@@ -20,6 +20,7 @@ from evonet.connection import Connection
|
|
|
20
20
|
from evonet.enums import ConnectionType, NeuronRole, RecurrentKind
|
|
21
21
|
from evonet.layer import Layer
|
|
22
22
|
from evonet.neuron import Neuron
|
|
23
|
+
from evonet.utils import connection_init_value
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class Nnet:
|
|
@@ -102,7 +103,7 @@ class Nnet:
|
|
|
102
103
|
label: str = "",
|
|
103
104
|
role: NeuronRole = NeuronRole.HIDDEN,
|
|
104
105
|
count: int = 1,
|
|
105
|
-
connection_init: Literal["random", "zero", "none"] = "
|
|
106
|
+
connection_init: Literal["random", "zero", "near_zero", "none"] = "zero",
|
|
106
107
|
recurrent: Optional[set[RecurrentKind]] = None,
|
|
107
108
|
) -> list[Neuron]:
|
|
108
109
|
"""
|
|
@@ -116,9 +117,10 @@ class Nnet:
|
|
|
116
117
|
role: Role of the neuron (INPUT, HIDDEN, OUTPUT).
|
|
117
118
|
count: Number of neurons to add (default: 1).
|
|
118
119
|
connection_init:
|
|
119
|
-
"random"
|
|
120
|
-
"zero"
|
|
121
|
-
"
|
|
120
|
+
"random" - connect with random weights (feedforward + recurrent)
|
|
121
|
+
"zero" - connect with weight 0.0 (feedforward + recurrent)
|
|
122
|
+
"near_zero" - connect with small random weights (-0.05, 0.05)
|
|
123
|
+
"none" - do not create connections (feedforward + recurrent)
|
|
122
124
|
recurrent: Optional recurrent connection types.
|
|
123
125
|
|
|
124
126
|
Returns:
|
|
@@ -144,10 +146,7 @@ class Nnet:
|
|
|
144
146
|
new_neurons.append(neuron)
|
|
145
147
|
|
|
146
148
|
# Weights based on init mode
|
|
147
|
-
|
|
148
|
-
if connection_init not in weight_map:
|
|
149
|
-
raise ValueError(f"Invalid connection_init: {connection_init}")
|
|
150
|
-
weight = weight_map[connection_init]
|
|
149
|
+
weight = connection_init_value(connection_init)
|
|
151
150
|
|
|
152
151
|
skip_connections = connection_init == "none"
|
|
153
152
|
|
|
@@ -48,3 +48,21 @@ class RecurrentKind(str, Enum):
|
|
|
48
48
|
DIRECT = "direct" # src == dst
|
|
49
49
|
LATERAL = "lateral" # same layer, src != dst
|
|
50
50
|
INDIRECT = "indirect" # src_layer > dst_layer (back edge)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Neutral activation functions
|
|
54
|
+
# These functions satisfy f(0) ≈ 0 and are safe for zero-weight initialization.
|
|
55
|
+
NEUTRAL_ACTIVATIONS: list[str] = [
|
|
56
|
+
"tanh",
|
|
57
|
+
"relu",
|
|
58
|
+
"relu_max1",
|
|
59
|
+
"leaky_relu",
|
|
60
|
+
"elu",
|
|
61
|
+
"selu",
|
|
62
|
+
"linear",
|
|
63
|
+
"linear_max1",
|
|
64
|
+
"invert",
|
|
65
|
+
"swish",
|
|
66
|
+
"mish",
|
|
67
|
+
"softsign",
|
|
68
|
+
]
|
|
@@ -19,6 +19,7 @@ from evonet.connection import Connection
|
|
|
19
19
|
from evonet.core import Nnet
|
|
20
20
|
from evonet.enums import ConnectionType, NeuronRole, RecurrentKind
|
|
21
21
|
from evonet.neuron import Neuron
|
|
22
|
+
from evonet.utils import connection_init_value
|
|
22
23
|
|
|
23
24
|
ALL_ACTIVATIONS = "all"
|
|
24
25
|
|
|
@@ -181,14 +182,7 @@ def add_random_connection(
|
|
|
181
182
|
"""
|
|
182
183
|
|
|
183
184
|
# Weight initialization
|
|
184
|
-
|
|
185
|
-
weight = 0.0
|
|
186
|
-
elif connection_init == "random":
|
|
187
|
-
weight = np.random.randn() * 0.5
|
|
188
|
-
elif connection_init == "near_zero":
|
|
189
|
-
weight = np.random.uniform(-0.05, 0.05)
|
|
190
|
-
else:
|
|
191
|
-
raise ValueError(f"Unknown init mode: {connection_init}")
|
|
185
|
+
weight = connection_init_value(connection_init)
|
|
192
186
|
|
|
193
187
|
# normalize to set[RecurrentKind]
|
|
194
188
|
kinds: set[RecurrentKind] = set()
|
|
@@ -265,7 +259,11 @@ def remove_random_connection(net: Nnet) -> None:
|
|
|
265
259
|
conn.target.incoming.remove(conn)
|
|
266
260
|
|
|
267
261
|
|
|
268
|
-
def add_random_neuron(
|
|
262
|
+
def add_random_neuron(
|
|
263
|
+
net: Nnet,
|
|
264
|
+
activations: list[str] | None = None,
|
|
265
|
+
connection_init: Literal["zero", "random", "near_zero", "none"] = "zero",
|
|
266
|
+
) -> None:
|
|
269
267
|
"""
|
|
270
268
|
Insert a new hidden neuron into a random layer.
|
|
271
269
|
|
|
@@ -293,7 +291,7 @@ def add_random_neuron(net: Nnet, activations: list[str] | None = None) -> None:
|
|
|
293
291
|
layer_idx=net.layers.index(layer),
|
|
294
292
|
activation=random_function_name(activations),
|
|
295
293
|
role=NeuronRole.HIDDEN,
|
|
296
|
-
connection_init=
|
|
294
|
+
connection_init=connection_init,
|
|
297
295
|
)
|
|
298
296
|
|
|
299
297
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def connection_init_value(mode: str) -> float | None:
|
|
7
|
+
"""
|
|
8
|
+
Return an initial weight value for a new connection.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
mode (str): Initialization mode. One of:
|
|
12
|
+
- "zero": weight = 0.0
|
|
13
|
+
- "near_zero": weight ~ U(-0.05, 0.05)
|
|
14
|
+
- "random": weight ~ N(0, 0.5)
|
|
15
|
+
- "none": no initialization (returns None)
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
float | None: Initial connection weight or None.
|
|
19
|
+
"""
|
|
20
|
+
if mode == "zero":
|
|
21
|
+
return 0.0
|
|
22
|
+
if mode == "near_zero":
|
|
23
|
+
return np.random.uniform(-0.05, 0.05)
|
|
24
|
+
if mode == "random":
|
|
25
|
+
return np.random.randn() * 0.5
|
|
26
|
+
if mode == "none":
|
|
27
|
+
return None
|
|
28
|
+
raise ValueError(f"Unknown connection_init mode: {mode}")
|
|
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
|