evonet 0.1.0.dev17__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.dev17 → evonet-0.1.0.dev19}/PKG-INFO +1 -1
  2. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/core.py +9 -1
  3. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/mutation.py +41 -16
  4. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet.egg-info/PKG-INFO +1 -1
  5. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/pyproject.toml +1 -1
  6. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/LICENSE +0 -0
  7. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/README.md +0 -0
  8. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/__init__.py +0 -0
  9. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/activation.py +0 -0
  10. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/connection.py +0 -0
  11. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/core_mit_plot_simple.py +0 -0
  12. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/enums.py +0 -0
  13. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/io.py +0 -0
  14. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/layer.py +0 -0
  15. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/neuron.py +0 -0
  16. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/serialization.py +0 -0
  17. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/utils.py +0 -0
  18. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet/visualize.py +0 -0
  19. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet.egg-info/SOURCES.txt +0 -0
  20. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet.egg-info/dependency_links.txt +0 -0
  21. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet.egg-info/requires.txt +0 -0
  22. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/evonet.egg-info/top_level.txt +0 -0
  23. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/setup.cfg +0 -0
  24. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/tests/test_activation.py +0 -0
  25. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/tests/test_core.py +0 -0
  26. {evonet-0.1.0.dev17 → evonet-0.1.0.dev19}/tests/test_nnet_io_and_forward.py +0 -0
  27. {evonet-0.1.0.dev17 → 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.dev17
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,30 +240,39 @@ 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,
264
271
  activations: list[str] | None = None,
265
272
  connection_init: Literal["zero", "random", "near_zero", "none"] = "zero",
266
- ) -> None:
273
+ connection_scope: Literal["adjacent", "crosslayer"] = "adjacent",
274
+ connection_density: float = 1.0,
275
+ ) -> Neuron | None:
267
276
  """
268
277
  Insert a new hidden neuron into a random layer.
269
278
 
@@ -273,9 +282,13 @@ def add_random_neuron(
273
282
  net (Nnet): The target network.
274
283
  activations (list[str] | None): Optional list of allowed activation functions.
275
284
  If None, all registered activations are used.
285
+
286
+ Returns:
287
+ Neuron: Added neurons, or None
276
288
  """
289
+
277
290
  if len(net.layers) < 2:
278
- return
291
+ return None
279
292
 
280
293
  if len(net.layers) == 2:
281
294
  net.insert_layer(1)
@@ -283,16 +296,20 @@ def add_random_neuron(
283
296
  # Choose target layer (not input, not output)
284
297
  candidate_layers = net.layers[1:-1]
285
298
  if not candidate_layers:
286
- return
299
+ return None
287
300
 
288
301
  layer = random.choice(candidate_layers)
289
302
 
290
- net.add_neuron(
303
+ new_neuron = net.add_neuron(
291
304
  layer_idx=net.layers.index(layer),
292
305
  activation=random_function_name(activations),
293
306
  role=NeuronRole.HIDDEN,
294
307
  connection_init=connection_init,
295
- )
308
+ connection_scope=connection_scope,
309
+ connection_density=connection_density,
310
+ )[0]
311
+
312
+ return new_neuron
296
313
 
297
314
 
298
315
  def remove_random_neuron(net: Nnet) -> None:
@@ -320,7 +337,9 @@ def remove_random_neuron(net: Nnet) -> None:
320
337
  break
321
338
 
322
339
 
323
- 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:
324
343
  """
325
344
  Insert a neuron in the middle of an existing connection.
326
345
 
@@ -332,10 +351,14 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
332
351
  net (Nnet): The target network.
333
352
  activation (str): Activation function for the new neuron.
334
353
  noise (float): Optional noise applied to new weights.
354
+
355
+ Returns:
356
+ Neuron: Added neuron, or None
335
357
  """
358
+
336
359
  all_connections = net.get_all_connections()
337
360
  if not all_connections:
338
- return
361
+ return None
339
362
 
340
363
  conn = random.choice(all_connections)
341
364
  src, dst = conn.source, conn.target
@@ -347,7 +370,7 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
347
370
  break
348
371
 
349
372
  if insert_idx is None or insert_idx >= len(net.layers):
350
- return
373
+ return None
351
374
 
352
375
  new_neuron = net.add_neuron(
353
376
  layer_idx=insert_idx,
@@ -363,3 +386,5 @@ def split_connection(net: Nnet, activation: str = "tanh", noise: float = 0.1) ->
363
386
  weight = 1.0 + np.random.normal(0, noise)
364
387
  net.add_connection(src, new_neuron, weight=weight)
365
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.dev17
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.dev17"
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