evolib 0.2.0b4.dev9__tar.gz → 0.2.0b4.dev10__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 (156) hide show
  1. {evolib-0.2.0b4.dev9/evolib.egg-info → evolib-0.2.0b4.dev10}/PKG-INFO +1 -1
  2. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/config_guide.md +14 -2
  3. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/config_parameter.md +26 -10
  4. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/base_component_config.py +41 -16
  5. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/evonet_component_config.py +67 -64
  6. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/initializers/evonet_initializers.py +2 -3
  7. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/evonet_structural_mutation.py +1 -2
  8. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/selection.py +12 -7
  9. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/evonet.py +5 -3
  10. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10/evolib.egg-info}/PKG-INFO +1 -1
  11. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib.egg-info/SOURCES.txt +0 -1
  12. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/03_delay_bitseq_echo.py +1 -1
  13. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/05_image_approximation.py +1 -2
  14. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/pyproject.toml +1 -1
  15. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_evonet_neuron_dynamics.py +9 -3
  16. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_initializer_evonet.py +4 -0
  17. evolib-0.2.0b4.dev9/evolib/interfaces/enum_helpers.py +0 -31
  18. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/LICENSE +0 -0
  19. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/MANIFEST.in +0 -0
  20. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/README.md +0 -0
  21. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_core_individual.rst +0 -0
  22. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_core_population.rst +0 -0
  23. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_crossover.rst +0 -0
  24. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_evonet_structural_mutation.rst +0 -0
  25. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_mutation.rst +0 -0
  26. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_replacement.rst +0 -0
  27. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_reproduction.rst +0 -0
  28. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_selection.rst +0 -0
  29. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_operators_strategy.rst +0 -0
  30. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_public_api.rst +0 -0
  31. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_representation_evonet.rst +0 -0
  32. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_representation_netvector.rst +0 -0
  33. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_representation_vector.rst +0 -0
  34. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_utils_benchmarks.rst +0 -0
  35. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_utils_history_logger.rst +0 -0
  36. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_utils_loss_functions.rst +0 -0
  37. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/api_utils_plotting.rst +0 -0
  38. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/conf.py +0 -0
  39. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/getting_started.md +0 -0
  40. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/docs/index.md +0 -0
  41. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/__init__.py +0 -0
  42. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/api.py +0 -0
  43. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/__init__.py +0 -0
  44. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/component_registry.py +0 -0
  45. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/schema.py +0 -0
  46. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/config/vector_component_config.py +0 -0
  47. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/core/README.md +0 -0
  48. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/core/__init__.py +0 -0
  49. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/core/individual.py +0 -0
  50. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/core/population.py +0 -0
  51. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/envs/gym_wrapper.py +0 -0
  52. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/globals/README.md +0 -0
  53. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/globals/__init__.py +0 -0
  54. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/globals/numeric.py +0 -0
  55. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/initializers/__init__.py +0 -0
  56. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/initializers/net_initializers.py +0 -0
  57. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/initializers/registry.py +0 -0
  58. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/initializers/vector_initializers.py +0 -0
  59. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/interfaces/__init__.py +0 -0
  60. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/interfaces/enums.py +0 -0
  61. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/interfaces/structs.py +0 -0
  62. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/interfaces/types.py +0 -0
  63. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/io/checkpoint.py +0 -0
  64. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/io/serialization.py +0 -0
  65. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/README.md +0 -0
  66. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/__init__.py +0 -0
  67. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/crossover.py +0 -0
  68. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/heli.py +0 -0
  69. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/mutation.py +0 -0
  70. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/replacement.py +0 -0
  71. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/reproduction.py +0 -0
  72. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/operators/strategy.py +0 -0
  73. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/registry/__init__.py +0 -0
  74. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/registry/replacement_registry.py +0 -0
  75. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/registry/selection_registry.py +0 -0
  76. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/registry/strategy_registry.py +0 -0
  77. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/__init__.py +0 -0
  78. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/_apply_config_mapping.py +0 -0
  79. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/base.py +0 -0
  80. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/composite.py +0 -0
  81. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/dummy.py +0 -0
  82. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/evo_params.py +0 -0
  83. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/netvector.py +0 -0
  84. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/representation/vector.py +0 -0
  85. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/README.md +0 -0
  86. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/__init__.py +0 -0
  87. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/benchmarks.py +0 -0
  88. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/config_loader.py +0 -0
  89. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/fitness.py +0 -0
  90. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/heli_experiment_logger.py +0 -0
  91. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/heli_utils.py +0 -0
  92. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/history_logger.py +0 -0
  93. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/lineage_logger.py +0 -0
  94. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/loss_functions.py +0 -0
  95. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/parallel.py +0 -0
  96. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/plotting.py +0 -0
  97. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/random.py +0 -0
  98. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib/utils/registry.py +0 -0
  99. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib.egg-info/dependency_links.txt +0 -0
  100. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib.egg-info/requires.txt +0 -0
  101. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/evolib.egg-info/top_level.txt +0 -0
  102. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/01_basic_usage/01_getting_started.py +0 -0
  103. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/01_basic_usage/02_mutation.py +0 -0
  104. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/01_basic_usage/03_population_mutation.py +0 -0
  105. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/01_basic_usage/04_fitness.py +0 -0
  106. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/02_strategies/01_step_by_step_evolution.py +0 -0
  107. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/02_strategies/02_mu_lambda_step.py +0 -0
  108. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/02_strategies/03_mu_lambda.py +0 -0
  109. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/02_strategies/04_flexible.py +0 -0
  110. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/01_history.py +0 -0
  111. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/02_plotting.py +0 -0
  112. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/03_compare_runs.py +0 -0
  113. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/04_exponential_decay_vs_static.py +0 -0
  114. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/05_adaptive_global_vs_static.py +0 -0
  115. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/06_adaptive_individual_vs_static.py +0 -0
  116. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/07_selection_comparison.py +0 -0
  117. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/08_selection_pressure.py +0 -0
  118. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/09_selection_vs_mutation_pressure.py +0 -0
  119. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/10_selection_stochastic_vs_deterministic.py +0 -0
  120. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/03_comparisons/11_crossover_comparison.py +0 -0
  121. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/04_function_approximation/01_polynomial_sine.py +0 -0
  122. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/04_function_approximation/02_sine_point_approximation.py +0 -0
  123. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/04_function_approximation/03_approximation_with_noise.py +0 -0
  124. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/01_fitness_landscape_exploration.py +0 -0
  125. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/02_rosenbrock_surface_path.py +0 -0
  126. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/03_vector_control.py +0 -0
  127. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/04_vector_control_with_obstacles.py +0 -0
  128. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/05_piecewise_linear_xsys.py +0 -0
  129. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/05_advanced_topics/06_evolving_mutation_strength_as_a_module.py +0 -0
  130. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/06_netvector/01_netvector_sine_approximation.py +0 -0
  131. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/06_netvector/02_netvector_modulated_output.py +0 -0
  132. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/06_netvector/03_netvector_gain_and_bias.py +0 -0
  133. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/01_sine_approximation.py +0 -0
  134. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/02_sine_delay.py +0 -0
  135. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/04_leaky_temporal_smoothing.py +0 -0
  136. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/06_structural_xor.py +0 -0
  137. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/07_recurrent_bit_prediction.py +0 -0
  138. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/08_recurrent_timeseries.py +0 -0
  139. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/07_evonet/09_recurrent_trading.py +0 -0
  140. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/08_gym/01_frozen_lake.py +0 -0
  141. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/08_gym/02_cliff_walking.py +0 -0
  142. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/08_gym/03_cartpole.py +0 -0
  143. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/08_gym/04_lunar_lander.py +0 -0
  144. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/08_gym/05_bipedal_walker.py +0 -0
  145. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/09_meta_learning/01_lamarck_linear_mapping.py +0 -0
  146. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/examples/09_meta_learning/02_meta_mapping_baldwin.py +0 -0
  147. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/setup.cfg +0 -0
  148. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_benchmarks.py +0 -0
  149. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_config_loader.py +0 -0
  150. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_elitism_preserves_best_fitness.py +0 -0
  151. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_evonet_deepcopy.py +0 -0
  152. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_evonet_vector_io.py +0 -0
  153. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_initializer_net.py +0 -0
  154. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_mutation.py +0 -0
  155. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_population.py +0 -0
  156. {evolib-0.2.0b4.dev9 → evolib-0.2.0b4.dev10}/tests/test_selection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evolib
3
- Version: 0.2.0b4.dev9
3
+ Version: 0.2.0b4.dev10
4
4
  Summary: A modular framework for evolutionary strategies and neuroevolution.
5
5
  Author-email: EvoLib <evolib@dismail.de>
6
6
  License: MIT License
@@ -120,6 +120,11 @@ This configuration demonstrates EvoNet evolution: weight mutation, bias-specific
120
120
 
121
121
  `dim: [2, 0, 0, 1]` starts with empty hidden layers, letting structural mutation grow nodes and edges.
122
122
 
123
+ Connectivity:
124
+ - `scope` controls which feedforward edges are allowed at initialization.
125
+ - `density` controls how many of those allowed edges are actually created at init.
126
+ - `recurrent` enables recurrent edge kinds (empty list means none).
127
+
123
128
  Topology constraints:
124
129
  - `max_neurons` and `max_connections` keep growth bounded (these are the current names; avoid older `max_nodes/max_edges`).
125
130
 
@@ -127,6 +132,8 @@ Delay:
127
132
  - `delay:` initializes delays of recurrent connections at build time.
128
133
  - `mutation.delay:` mutates delays during evolution (recurrent connections only).
129
134
 
135
+
136
+
130
137
  ```yaml
131
138
  parent_pool_size: 20
132
139
  offspring_pool_size: 40
@@ -145,17 +152,22 @@ modules:
145
152
  type: evonet
146
153
  dim: [2, 0, 0, 1] # hidden layers start empty
147
154
  activation: [linear, tanh, tanh, sigmoid]
155
+
156
+ connectivity:
157
+ scope: crosslayer # adjacent | crosslayer
158
+ density: 1.0 # (0, 1]
159
+ recurrent: [direct] # [], [direct], [lateral], [indirect]
160
+
148
161
  weights:
149
162
  initializer: normal
150
163
  std: 0.5
151
164
  bounds: [-5.0, 5.0]
165
+
152
166
  bias:
153
167
  initializer: normal
154
168
  std: 0.5
155
169
  bounds: [-1.0, 1.0]
156
170
 
157
- recurrent: direct # REQUIRED for delay to have any effect
158
-
159
171
  # NOTE: Delays only apply to recurrent connections.
160
172
  # If `recurrent` is not enabled, this block has no effect.
161
173
  delay:
@@ -289,12 +289,30 @@ modules:
289
289
  | `dim` | list[int] | — | Layer sizes, e.g. `[4, 0, 0, 2]`. Hidden layers can start empty (0) and grow through structural mutation. |
290
290
  | `activation` | str \| list[str] | — | If list: activation per layer. If str: used for non-input layers; input layer is treated as linear. |
291
291
  | `initializer` | str | default | Topology preset (e.g. `default`, `unconnected`, `identity`). Parameter initialization is configured via `weights`, `bias`, and `delay`. |
292
+ | `connectivity` | `dict` | — | **Required.** Defines feedforward scope/density and allowed recurrent kinds. |
292
293
  | `weights` | dict | — | Weight init and bounds configuration (initializer, bounds, optional params). |
293
294
  | `bias` | dict | — | Bias init and bounds configuration (initializer, bounds, optional params). |
294
295
  | `neuron_dynamics` | list[dict] \| null | null | Optional per-layer neuron dynamics specification. Must match `len(dim)`. |
295
296
  | `mutation` | dict \| null | null | Mutation settings for weights, biases, activations, delay, and structure. |
296
297
  | `crossover` | dict \| null | null | Optional crossover settings (weight/bias level). |
297
298
 
299
+ #### Connectivity (`connectivity`)
300
+
301
+ | Field | Type | Default | Notes |
302
+ |------------|------------------------------|---------|------|
303
+ | `scope` | `"adjacent" \| "crosslayer"` | — | **Required.** Allowed feedforward edge scope at initialization. |
304
+ | `density` | `float` | — | **Required.** Fraction of allowed feedforward edges created at init `(0,1]`. |
305
+ | `recurrent`| `list[str]` | `[]` | Allowed recurrent edge kinds. Empty list means none. |
306
+
307
+ Example:
308
+
309
+ ```yaml
310
+ connectivity:
311
+ scope: crosslayer
312
+ density: 0.5
313
+ recurrent: [direct]
314
+ ```
315
+
298
316
  ##### weights block
299
317
  |Parameter | Type | Default | Explanation |
300
318
  |------------|-------|-----------|-------------|
@@ -325,8 +343,8 @@ Allowed presets:
325
343
  | Initializer | Meaning (topology only) |
326
344
  |------------------------|-------------------------|
327
345
  | `default` | Standard EvoNet topology preset (uses `connection_scope`, `connection_density`, and `recurrent`). |
328
- | `unconnected_evonet` | Creates neurons/layers but starts with **no connections** (use structural mutation to grow). |
329
- | `identity_evonet` | Special preset intended for stable recurrent memory (may override parameters internally; see notes below). |
346
+ | `unconnected` | Creates neurons/layers but starts with **no connections** (use structural mutation to grow). |
347
+ | `identity` | Special preset intended for stable recurrent memory (may override parameters internally; see notes below). |
330
348
 
331
349
  ---
332
350
 
@@ -493,13 +511,12 @@ structural:
493
511
 
494
512
  ### Topology constraints
495
513
 
496
- | Field | Type | Default | Description |
497
- |---------------------|---------------|----------|-------------|
498
- | `recurrent` | str | `none` | Controls recurrence: `none`, `direct`, `local` or `all`. |
499
- | `connection_scope` | str | `adjacent` | Allowed layer connectivity: `adjacent` (neighbor layers only) or `crosslayer` (any-to-any). |
500
- | `connection_density` | float | 1.0 | Fraction of possible connections initialized at creation time. |
501
- | `max_neurons` | int \| null | null | Maximum number of non-input neurons (`null` = unlimited). |
502
- | `max_connections` | int \| null | null | Maximum number of edges (`null` = unlimited). |
514
+ | Field | Type | Default | Description |
515
+ |-------------------|--------------------------------|-------------|-------------|
516
+ | `recurrent` | `list[str]` | `[]` | Allowed recurrent kinds for `add_connection`. Empty list means none. |
517
+ | `connection_scope` | `"adjacent" \| "crosslayer" \| null` | `"adjacent"` | Constraint for structural add-neuron / add-connection edge placement. |
518
+ | `max_neurons` | `int \| null` | `null` | Upper bound on total neurons allowed (implementation-defined counting). |
519
+ | `max_connections` | `int \| null` | `null` | Upper bound on total connections allowed. |
503
520
 
504
521
  ---
505
522
 
@@ -544,7 +561,6 @@ modules:
544
561
  type: evonet
545
562
  dim: [4, 0, 0, 2]
546
563
  activation: [linear, tanh, tanh, tanh]
547
- initializer: normal_evonet
548
564
 
549
565
  delay:
550
566
  initializer: uniform
@@ -8,10 +8,11 @@ should be configured, not *how* it is executed. Any runtime behavior belongs
8
8
  into the respective Para* representations and operator modules.
9
9
  """
10
10
 
11
- from typing import Literal, Optional, Union
11
+ from typing import Any, Literal, Optional, Union
12
12
 
13
13
  from evonet.activation import ACTIVATIONS
14
- from pydantic import BaseModel, ConfigDict, Field, model_validator, validator
14
+ from evonet.enums import RecurrentKind
15
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
15
16
 
16
17
  from evolib.interfaces.enums import (
17
18
  CrossoverOperator,
@@ -221,15 +222,21 @@ class AddNeuron(BaseModel):
221
222
  "neurons in hidden layers.",
222
223
  )
223
224
 
224
- @validator("activations_allowed", each_item=True)
225
- def validate_activation_name(cls, act_name: str) -> str:
225
+ @field_validator("activations_allowed")
226
+ @classmethod
227
+ def validate_activations_allowed(
228
+ cls, acts: Optional[list[str]]
229
+ ) -> Optional[list[str]]:
226
230
  """Ensure only valid activation function names are allowed."""
227
- if act_name not in ACTIVATIONS:
228
- raise ValueError(
229
- f"Invalid activation function '{act_name}'. "
230
- f"Valid options are: {list(ACTIVATIONS.keys())}"
231
- )
232
- return act_name
231
+ if acts is None:
232
+ return None
233
+ for a in acts:
234
+ if a not in ACTIVATIONS:
235
+ raise ValueError(
236
+ f"Invalid activation function '{a}'. "
237
+ f"Valid options are: {sorted(ACTIVATIONS.keys())}"
238
+ )
239
+ return acts
233
240
 
234
241
 
235
242
  class RemoveNeuron(BaseModel):
@@ -267,7 +274,12 @@ class StructuralTopology(BaseModel):
267
274
  mutation operators.
268
275
  """
269
276
 
270
- recurrent: Optional[Literal["none", "direct", "local", "all"]] = "none"
277
+ recurrent: list[RecurrentKind] = Field(
278
+ default_factory=list,
279
+ description="Allowed recurrent edge kinds for structural "
280
+ "add_connection. Empty means none.",
281
+ )
282
+
271
283
  connection_scope: Optional[Literal["adjacent", "crosslayer"]] = "adjacent"
272
284
 
273
285
  max_neurons: Optional[int] = Field(
@@ -282,21 +294,34 @@ class StructuralTopology(BaseModel):
282
294
  ge=1,
283
295
  )
284
296
 
297
+ @field_validator("recurrent", mode="before")
298
+ @classmethod
299
+ def normalize_recurrent(cls, v: Any) -> list[RecurrentKind]:
300
+ """
301
+ Allow YAML convenience values like:
302
+ recurrent: none
303
+ recurrent: []
304
+ """
305
+ if v is None:
306
+ return []
307
+ if isinstance(v, str):
308
+ if v.lower() == "none":
309
+ return []
310
+ return [v]
311
+ return v
312
+
285
313
  @model_validator(mode="after")
286
314
  def _validate_topology(self) -> "StructuralTopology":
287
- if self.recurrent not in {None, "none", "direct", "local", "all"}:
288
- raise ValueError(f"Invalid recurrent value: {self.recurrent}")
289
-
290
315
  if self.connection_scope not in {None, "adjacent", "crosslayer"}:
291
316
  raise ValueError(f"Invalid connection_scope: {self.connection_scope}")
292
317
 
293
- # Only positive limits allowed
294
318
  if self.max_connections is not None and self.max_connections <= 0:
295
319
  raise ValueError("max_connections must be > 0")
296
-
297
320
  if self.max_neurons is not None and self.max_neurons <= 0:
298
321
  raise ValueError("max_neurons must be > 0")
299
322
 
323
+ # ensure deterministic unique list
324
+ self.recurrent = sorted(set(self.recurrent), key=lambda x: x.value)
300
325
  return self
301
326
 
302
327
 
@@ -11,16 +11,16 @@ After parsing, raw dicts are converted into this strongly typed Pydantic model d
11
11
  config resolution.
12
12
  """
13
13
 
14
- from typing import Literal, Optional, Tuple, Union
14
+ from typing import Any, Literal, Optional, Tuple, Union
15
15
 
16
16
  from evonet.activation import ACTIVATIONS
17
+ from evonet.enums import RecurrentKind
17
18
  from pydantic import (
18
19
  BaseModel,
19
20
  ConfigDict,
20
21
  Field,
21
22
  field_validator,
22
23
  model_validator,
23
- validator,
24
24
  )
25
25
  from pydantic_core import core_schema
26
26
 
@@ -35,6 +35,55 @@ from evolib.interfaces.enums import RepresentationType
35
35
  Bounds = Tuple[float, float]
36
36
 
37
37
 
38
+ class ConnectivityConfig(BaseModel):
39
+ """
40
+ EvoNet connectivity configuration.
41
+
42
+ recurrent:
43
+ Allowed recurrent edge kinds (DIRECT/LATERAL/INDIRECT).
44
+ If omitted, no recurrent edges are allowed.
45
+
46
+ scope:
47
+ adjacent -> only adjacent-layer feedforward connections during init
48
+ crosslayer -> cross-layer feedforward connections during init
49
+
50
+ density:
51
+ Fraction of allowed feedforward edges to create at init (0 < density <= 1).
52
+ """
53
+
54
+ model_config = ConfigDict(extra="forbid")
55
+
56
+ recurrent: list[RecurrentKind] = Field(default_factory=list)
57
+
58
+ # Required: must be explicitly set in YAML
59
+ scope: Literal["adjacent", "crosslayer"]
60
+
61
+ # Required: must be explicitly set in YAML
62
+ density: float = Field(..., gt=0.0, le=1.0)
63
+
64
+ @field_validator("recurrent", mode="before")
65
+ @classmethod
66
+ def normalize_recurrent(cls, v: Any) -> list[RecurrentKind]:
67
+ """
68
+ Allow YAML convenience values like:
69
+ recurrent: none
70
+ recurrent: []
71
+ """
72
+ if v is None:
73
+ return []
74
+ if isinstance(v, str):
75
+ if v.lower() == "none":
76
+ return []
77
+ return [v]
78
+ return v
79
+
80
+ @model_validator(mode="after")
81
+ def _normalize(self) -> "ConnectivityConfig":
82
+ # deterministic, unique
83
+ self.recurrent = sorted(set(self.recurrent), key=lambda x: x.value)
84
+ return self
85
+
86
+
38
87
  class WeightsConfig(BaseModel):
39
88
  # None means: no parameter-level initialization requested (preset may initialize).
40
89
  initializer: Optional[str] = None # normal | uniform | zero | None
@@ -227,33 +276,13 @@ class EvoNetComponentConfig(BaseModel):
227
276
  "neurons in hidden layers.",
228
277
  )
229
278
 
230
- # Recurrent connections
231
- recurrent: Optional[Literal["none", "direct", "local", "all"]] = "none"
232
-
233
279
  # Name of the initializer function (resolved via initializer registry)
234
280
  initializer: str = Field(
235
281
  default="default", description="Name of the initializer to use"
236
282
  )
237
283
 
238
- # Connection topology for initialization
239
- connection_scope: Literal["adjacent", "crosslayer"] = Field(
240
- default="adjacent",
241
- description=(
242
- "Defines how layers are connected during initialization. "
243
- "'adjacent' connects only consecutive layers, while 'crosslayer' "
244
- "connects all earlier layers to all later layers."
245
- ),
246
- )
247
-
248
- connection_density: float = Field(
249
- default=1.0,
250
- ge=0.0,
251
- le=1.0,
252
- description=(
253
- "Fraction of possible connections actually created during initialization. "
254
- "1.0 = fully connected, <1.0 = sparse."
255
- ),
256
- )
284
+ # Connectivity
285
+ connectivity: ConnectivityConfig
257
286
 
258
287
  # Numeric bounds for values; used by initialization and mutation
259
288
  weights: WeightsConfig = Field(default_factory=WeightsConfig)
@@ -335,45 +364,19 @@ class EvoNetComponentConfig(BaseModel):
335
364
  raise ValueError("Length of 'activation' list must match 'dim'")
336
365
  return act
337
366
 
338
- @validator("activations_allowed", each_item=True)
339
- def validate_activation_name(cls, act_name: str) -> str:
340
- """Ensure only valid activation function names are allowed."""
341
- if act_name not in ACTIVATIONS:
342
- raise ValueError(
343
- f"Invalid activation function '{act_name}'. "
344
- f"Valid options are: {list(ACTIVATIONS.keys())}"
345
- )
346
- return act_name
347
-
348
- @validator("recurrent")
349
- def validate_recurrent(cls, recurrent: Optional[str]) -> str:
350
- """Ensure recurrent preset is valid and normalized."""
351
- if recurrent is None:
352
- return "none"
353
- allowed = {"none", "direct", "local", "all"}
354
- if recurrent not in allowed:
355
- raise ValueError(
356
- f"Invalid recurrent preset '{recurrent}'. "
357
- f"Valid options are: {sorted(allowed)}"
358
- )
359
- return recurrent
360
-
361
- @field_validator("connection_scope")
367
+ @field_validator("activations_allowed")
362
368
  @classmethod
363
- def validate_connection_scope(cls, scope: str) -> str:
364
- """Ensure connection_scope is one of the supported options."""
365
- allowed = {"adjacent", "crosslayer"}
366
- if scope not in allowed:
367
- raise ValueError(
368
- f"Invalid connection_scope '{scope}'. "
369
- f"Valid options are: {sorted(allowed)}"
370
- )
371
- return scope
369
+ def validate_activations_allowed(
370
+ cls, acts: Optional[list[str]]
371
+ ) -> Optional[list[str]]:
372
+ """Validate that all activation names are known."""
373
+ if acts is None:
374
+ return None
372
375
 
373
- @field_validator("connection_density")
374
- @classmethod
375
- def validate_connection_density(cls, density: float) -> float:
376
- """Ensure connection_density is within [0, 1]."""
377
- if not (0.0 <= density <= 1.0):
378
- raise ValueError(f"connection_density must be in [0, 1], got {density}.")
379
- return density
376
+ for a in acts:
377
+ if a not in ACTIVATIONS:
378
+ raise ValueError(
379
+ f"Invalid activation function '{a}'. "
380
+ f"Valid options are: {sorted(ACTIVATIONS.keys())}"
381
+ )
382
+ return acts
@@ -14,7 +14,6 @@ from evonet.enums import ConnectionType, NeuronRole
14
14
 
15
15
  from evolib.config.evonet_component_config import DelayConfig, EvoNetComponentConfig
16
16
  from evolib.config.schema import FullConfig
17
- from evolib.interfaces.enum_helpers import resolve_recurrent_kinds
18
17
  from evolib.representation.evonet import EvoNet
19
18
 
20
19
 
@@ -158,14 +157,14 @@ def _build_architecture(
158
157
  dynamics_name = dynamics_cfg.name
159
158
  dynamics_params = dynamics_cfg.params or {}
160
159
 
161
- recurrent_kinds = resolve_recurrent_kinds(cfg.recurrent)
160
+ allowed_kinds = set(cfg.connectivity.recurrent)
162
161
  para.net.add_neuron(
163
162
  count=num_neurons,
164
163
  activation=activation_name,
165
164
  role=role,
166
165
  connection_init=connection_init,
167
166
  bias=0.0,
168
- recurrent=recurrent_kinds if role != NeuronRole.INPUT else None,
167
+ recurrent=allowed_kinds if role != NeuronRole.INPUT else None,
169
168
  connection_scope=para.connection_scope,
170
169
  connection_density=para.connection_density,
171
170
  dynamics_name=dynamics_name,
@@ -10,7 +10,6 @@ from evonet.mutation import (
10
10
  )
11
11
 
12
12
  from evolib.config.base_component_config import StructuralMutationConfig
13
- from evolib.interfaces.enum_helpers import resolve_recurrent_kinds
14
13
 
15
14
 
16
15
  def mutate_structure(net: Nnet, cfg: StructuralMutationConfig) -> bool:
@@ -67,7 +66,7 @@ def mutate_structure(net: Nnet, cfg: StructuralMutationConfig) -> bool:
67
66
  cfg.topology.max_connections is None
68
67
  or len(net.get_all_connections()) < cfg.topology.max_connections
69
68
  ):
70
- allowed_kinds = resolve_recurrent_kinds(cfg.topology.recurrent)
69
+ allowed_kinds = set(cfg.topology.recurrent)
71
70
  for _ in range(np.random.randint(1, add_cfg.max + 1)):
72
71
  if add_random_connection(
73
72
  net,
@@ -48,14 +48,19 @@ def _calculate_rank_probabilities(
48
48
  raise ValueError("Population size must be at least 1.")
49
49
 
50
50
  if mode == "linear":
51
- # Linear probability: p(i) = 2*(N - i) / (N*(N+1))
52
- probabilities = (2 * (population_size - ranks)) / (
53
- population_size * (population_size + 1)
54
- )
51
+ # Monotonic decreasing with rank: best gets highest probability
52
+ weights = population_size - ranks
53
+ probabilities = weights / np.sum(weights)
54
+
55
55
  elif mode == "exponential":
56
- # Exponential probability: p(i) = base^i / sum(base^j)
57
- probabilities = np.power(exp_base, ranks)
58
- probabilities = probabilities / np.sum(probabilities)
56
+ if exp_base <= 0:
57
+ raise ValueError("exp_base must be greater than 0")
58
+
59
+ # With exp_base > 1.0: best gets highest probability
60
+ # rank 0 -> exp_base^0 = 1, rank 1 -> exp_base^-1, ...
61
+ weights = np.power(exp_base, -ranks)
62
+ probabilities = weights / np.sum(weights)
63
+
59
64
  else:
60
65
  raise ValueError("Selection mode must be either 'linear' or 'exponential'.")
61
66
 
@@ -109,13 +109,15 @@ class EvoNet(ParaBase):
109
109
  # Define network architecture
110
110
  self.dim = cfg.dim
111
111
 
112
+ # Connectivity
113
+ self.connection_scope = cfg.connectivity.scope
114
+ self.connection_density = cfg.connectivity.density
115
+ self.recurrent_kinds = cfg.connectivity.recurrent
116
+
112
117
  # Bounds
113
118
  self.weight_bounds = cfg.weights.bounds or (-1.0, 1.0)
114
119
  self.bias_bounds = cfg.bias.bounds or (-0.5, 0.5)
115
120
 
116
- self.connection_scope = cfg.connection_scope
117
- self.connection_density = cfg.connection_density
118
-
119
121
  # Mutation
120
122
  if cfg.mutation is None:
121
123
  raise ValueError("Mutation config is required for EvoNet.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evolib
3
- Version: 0.2.0b4.dev9
3
+ Version: 0.2.0b4.dev10
4
4
  Summary: A modular framework for evolutionary strategies and neuroevolution.
5
5
  Author-email: EvoLib <evolib@dismail.de>
6
6
  License: MIT License
@@ -52,7 +52,6 @@ evolib/initializers/net_initializers.py
52
52
  evolib/initializers/registry.py
53
53
  evolib/initializers/vector_initializers.py
54
54
  evolib/interfaces/__init__.py
55
- evolib/interfaces/enum_helpers.py
56
55
  evolib/interfaces/enums.py
57
56
  evolib/interfaces/structs.py
58
57
  evolib/interfaces/types.py
@@ -79,7 +79,7 @@ def fitness_echo(indiv: Indiv) -> None:
79
79
  acc = correct / max(count, 1)
80
80
 
81
81
  indiv.extra_metrics = {"accuracy": acc, "mse": mse}
82
- indiv.fitness = mse + (1.0 - acc) / 4.0
82
+ indiv.fitness = mse + (1.0 - acc) / 2.0
83
83
 
84
84
 
85
85
  def save_plot(pop: Pop) -> None:
@@ -74,14 +74,13 @@ def my_fitness(indiv: Indiv) -> None:
74
74
 
75
75
  def on_improvement(pop: Pop) -> None:
76
76
  best = pop.best()
77
- assert best.fitness is not None
78
77
  pred_img = predict_image(best, coords, img_size)
79
78
  save_frame(
80
79
  path=(f"./05_frames/gen_{pop.generation_num:04d}.png"),
81
80
  target=target,
82
81
  pred=pred_img,
83
82
  gen=pop.generation_num,
84
- fitness=float(best.fitness),
83
+ fitness=float(best.fitness or 0.0),
85
84
  )
86
85
 
87
86
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "evolib"
7
- version = "0.2.0b4.dev9"
7
+ version = "0.2.0b4.dev10"
8
8
  description = "A modular framework for evolutionary strategies and neuroevolution."
9
9
  authors = [
10
10
  { name = "EvoLib", email = "evolib@dismail.de" }
@@ -16,6 +16,10 @@ def test_neuron_dynamics_config_length_matches_dim() -> None:
16
16
  type="evonet",
17
17
  dim=[1, 2, 1],
18
18
  activation=["linear", "tanh", "tanh"],
19
+ connectivity={
20
+ "scope": "adjacent",
21
+ "density": 1.0,
22
+ },
19
23
  weights={
20
24
  "initializer": "normal",
21
25
  "std": 0.5,
@@ -48,9 +52,11 @@ def test_neuron_dynamics_applied_to_neurons() -> None:
48
52
  "dim": [1, 3, 1],
49
53
  "activation": ["linear", "tanh", "tanh"],
50
54
  "initializer": "default",
51
- "recurrent": "local",
52
- "connection_scope": "adjacent",
53
- "connection_density": 1.0,
55
+ "connectivity": {
56
+ "recurrent": "lateral",
57
+ "scope": "adjacent",
58
+ "density": 1.0,
59
+ },
54
60
  "weights": {
55
61
  "initializer": "normal",
56
62
  "std": 0.5,
@@ -15,6 +15,10 @@ def test_default_initializer_evonet_builds_expected_structure() -> None:
15
15
  "type": "evonet",
16
16
  "dim": [2, 3, 1],
17
17
  "activation": "linear",
18
+ "connectivity": {
19
+ "scope": "adjacent",
20
+ "density": 1.0,
21
+ },
18
22
  "weights": {
19
23
  "initializer": "normal",
20
24
  "std": 0.5,
@@ -1,31 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
-
3
- from typing import Optional, Set
4
-
5
- from evonet.enums import RecurrentKind
6
-
7
-
8
- def resolve_recurrent_kinds(preset: Optional[str]) -> Set[RecurrentKind]:
9
- """
10
- Map a preset string to the corresponding set of recurrent connection types.
11
-
12
- Parameters
13
- ----------
14
- preset : Optional[str]
15
- One of: "none", "direct", "local", "all", or None.
16
-
17
- Returns
18
- -------
19
- Set[RecurrentKind]
20
- A set of recurrent kinds to use.
21
- """
22
- if preset is None or preset == "none":
23
- return set()
24
- elif preset == "direct":
25
- return {RecurrentKind.DIRECT}
26
- elif preset == "local":
27
- return {RecurrentKind.DIRECT, RecurrentKind.LATERAL}
28
- elif preset == "all":
29
- return {RecurrentKind.DIRECT, RecurrentKind.LATERAL, RecurrentKind.INDIRECT}
30
- else:
31
- raise ValueError(f"Unknown recurrent preset: {preset}")
File without changes
File without changes
File without changes