evonet 0.1.0.dev18__tar.gz → 0.1.0.dev19__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.dev19}/PKG-INFO +1 -1
  2. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/core.py +9 -1
  3. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/mutation.py +37 -16
  4. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/PKG-INFO +1 -1
  5. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/pyproject.toml +1 -1
  6. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/LICENSE +0 -0
  7. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/README.md +0 -0
  8. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/__init__.py +0 -0
  9. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/activation.py +0 -0
  10. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/connection.py +0 -0
  11. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/core_mit_plot_simple.py +0 -0
  12. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/enums.py +0 -0
  13. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/io.py +0 -0
  14. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/layer.py +0 -0
  15. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/neuron.py +0 -0
  16. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/serialization.py +0 -0
  17. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/utils.py +0 -0
  18. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet/visualize.py +0 -0
  19. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/SOURCES.txt +0 -0
  20. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/dependency_links.txt +0 -0
  21. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/requires.txt +0 -0
  22. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/evonet.egg-info/top_level.txt +0 -0
  23. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/setup.cfg +0 -0
  24. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_activation.py +0 -0
  25. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_core.py +0 -0
  26. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/tests/test_nnet_io_and_forward.py +0 -0
  27. {evonet-0.1.0.dev18 → evonet-0.1.0.dev19}/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.dev19
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,18 +296,20 @@ 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
315
  def remove_random_neuron(net: Nnet) -> None:
@@ -324,7 +337,9 @@ def remove_random_neuron(net: Nnet) -> None:
324
337
  break
325
338
 
326
339
 
327
- def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) -> None:
340
+ def split_connection(
341
+ net: Nnet, activation: str = "tanh", noise: float = 0.1
342
+ ) -> Neuron | None:
328
343
  """
329
344
  Insert a neuron in the middle of an existing connection.
330
345
 
@@ -336,10 +351,14 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
336
351
  net (Nnet): The target network.
337
352
  activation (str): Activation function for the new neuron.
338
353
  noise (float): Optional noise applied to new weights.
354
+
355
+ Returns:
356
+ Neuron: Added neuron, or None
339
357
  """
358
+
340
359
  all_connections = net.get_all_connections()
341
360
  if not all_connections:
342
- return
361
+ return None
343
362
 
344
363
  conn = random.choice(all_connections)
345
364
  src, dst = conn.source, conn.target
@@ -351,7 +370,7 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
351
370
  break
352
371
 
353
372
  if insert_idx is None or insert_idx >= len(net.layers):
354
- return
373
+ return None
355
374
 
356
375
  new_neuron = net.add_neuron(
357
376
  layer_idx=insert_idx,
@@ -367,3 +386,5 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
367
386
  weight = 1.0 + np.random.normal(0, noise)
368
387
  net.add_connection(src, new_neuron, weight=weight)
369
388
  net.add_connection(new_neuron, dst, weight=conn.weight)
389
+
390
+ 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.dev19
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.dev19"
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