evonet 0.1.0.dev18__tar.gz → 0.1.0.dev20__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.dev18 → evonet-0.1.0.dev20}/PKG-INFO +1 -1
  2. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/core.py +9 -1
  3. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/mutation.py +41 -18
  4. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet.egg-info/PKG-INFO +1 -1
  5. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/pyproject.toml +1 -1
  6. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/LICENSE +0 -0
  7. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/README.md +0 -0
  8. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/__init__.py +0 -0
  9. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/activation.py +0 -0
  10. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/connection.py +0 -0
  11. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/core_mit_plot_simple.py +0 -0
  12. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/enums.py +0 -0
  13. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/io.py +0 -0
  14. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/layer.py +0 -0
  15. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/neuron.py +0 -0
  16. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/serialization.py +0 -0
  17. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/utils.py +0 -0
  18. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet/visualize.py +0 -0
  19. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet.egg-info/SOURCES.txt +0 -0
  20. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet.egg-info/dependency_links.txt +0 -0
  21. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet.egg-info/requires.txt +0 -0
  22. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/evonet.egg-info/top_level.txt +0 -0
  23. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/setup.cfg +0 -0
  24. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/tests/test_activation.py +0 -0
  25. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/tests/test_core.py +0 -0
  26. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/tests/test_nnet_io_and_forward.py +0 -0
  27. {evonet-0.1.0.dev18 → evonet-0.1.0.dev20}/tests/test_recurrent_dynamics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evonet
3
- Version: 0.1.0.dev18
3
+ Version: 0.1.0.dev20
4
4
  Summary: Evolvable neural network core for integration with EvoLib
5
5
  Author-email: EvoLib <evolib@dismail.de>
6
6
  License: MIT License
@@ -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
- ) -> bool:
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
- bool: True if a connection was added, False otherwise.
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 False
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 False
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
- return True
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) -> None:
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,21 +296,23 @@ 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
- def remove_random_neuron(net: Nnet) -> None:
315
+ def remove_random_neuron(net: Nnet) -> bool:
303
316
  """
304
317
  Remove a randomly selected hidden neuron from the network.
305
318
 
@@ -307,7 +320,7 @@ def remove_random_neuron(net: Nnet) -> None:
307
320
  """
308
321
  hidden_neurons = [n for n in net.get_all_neurons() if n.role == NeuronRole.HIDDEN]
309
322
  if not hidden_neurons:
310
- return
323
+ return False
311
324
 
312
325
  neuron = random.choice(hidden_neurons)
313
326
 
@@ -323,8 +336,12 @@ def remove_random_neuron(net: Nnet) -> None:
323
336
  layer.neurons.remove(neuron)
324
337
  break
325
338
 
339
+ return True
340
+
326
341
 
327
- def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) -> None:
342
+ def split_connection(
343
+ net: Nnet, activation: str = "tanh", noise: float = 0.1
344
+ ) -> Neuron | None:
328
345
  """
329
346
  Insert a neuron in the middle of an existing connection.
330
347
 
@@ -336,10 +353,14 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
336
353
  net (Nnet): The target network.
337
354
  activation (str): Activation function for the new neuron.
338
355
  noise (float): Optional noise applied to new weights.
356
+
357
+ Returns:
358
+ Neuron: Added neuron, or None
339
359
  """
360
+
340
361
  all_connections = net.get_all_connections()
341
362
  if not all_connections:
342
- return
363
+ return None
343
364
 
344
365
  conn = random.choice(all_connections)
345
366
  src, dst = conn.source, conn.target
@@ -351,7 +372,7 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
351
372
  break
352
373
 
353
374
  if insert_idx is None or insert_idx >= len(net.layers):
354
- return
375
+ return None
355
376
 
356
377
  new_neuron = net.add_neuron(
357
378
  layer_idx=insert_idx,
@@ -367,3 +388,5 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
367
388
  weight = 1.0 + np.random.normal(0, noise)
368
389
  net.add_connection(src, new_neuron, weight=weight)
369
390
  net.add_connection(new_neuron, dst, weight=conn.weight)
391
+
392
+ return new_neuron
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evonet
3
- Version: 0.1.0.dev18
3
+ Version: 0.1.0.dev20
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.dev18"
7
+ version = "0.1.0.dev20"
8
8
  description = "Evolvable neural network core for integration with EvoLib"
9
9
  authors = [
10
10
  { name = "EvoLib", email = "evolib@dismail.de" }
File without changes
File without changes
File without changes
File without changes