pyVolutionary 2.6.6__tar.gz → 2.7.1__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 (266) hide show
  1. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/PKG-INFO +4 -1
  2. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/README.md +3 -0
  3. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyproject.toml +1 -1
  4. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/abstract.py +72 -23
  5. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/african_vulture/african_vulture_optimization.py +7 -3
  6. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_colony/ant_colony_optimization.py +6 -3
  7. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_lion/ant_lion_optimization.py +9 -9
  8. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/aquila/aquila_optimization.py +1 -1
  9. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/archimede/archimede_optimization.py +1 -1
  10. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bacterial_foraging/bacterial_foraging_optimization.py +21 -15
  11. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bat/bat_optimization.py +1 -1
  12. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/battle_royale/battle_royale_optimization.py +1 -1
  13. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bee_colony/bee_colony_optimization.py +12 -5
  14. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/biogeography_based/biogeography_based_optimization.py +3 -3
  15. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brain_storm/brain_storm_optimization.py +19 -18
  16. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brain_storm/improved_brain_storm_optimization.py +15 -15
  17. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brown_bear/brown_bear_optimization.py +2 -2
  18. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/camel_caravan/camel_caravan_optimization.py +1 -1
  19. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cat_swarm/cat_swarm_optimization.py +5 -5
  20. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chaos_game/chaos_game_optimization.py +4 -4
  21. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chernobyl_disaster/chernobyl_disaster_optimization.py +5 -1
  22. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coati/coati_optimization.py +4 -4
  23. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coral_reef/coral_reef_optimization.py +10 -4
  24. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coronavirus_herd_immunity/coronavirus_herd_immunity_optimization.py +9 -9
  25. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coyotes/coyotes_optimization.py +5 -1
  26. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cuckoo_search/cuckoo_search_optimization.py +3 -2
  27. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dragonfly/dragonfly_optimization.py +10 -15
  28. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dwarf_mongoose/dwarf_mongoose_optimization.py +5 -1
  29. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/earthworms/earthworms_optimization.py +36 -13
  30. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/egret_swarm/egret_swarm_optimization.py +1 -1
  31. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/electromagnetic_field/electromagnetic_field_optimization.py +16 -5
  32. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/elephant_herd/elephant_herd_optimization.py +9 -11
  33. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/energy_valley/energy_valley_optimization.py +9 -5
  34. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ficks_law/ficks_law_optimization.py +10 -6
  35. pyvolutionary-2.7.1/pyvolutionary/firefly_swarm/firefly_swarm_optimization.py +106 -0
  36. pyvolutionary-2.7.1/pyvolutionary/firefly_swarm/models.py +61 -0
  37. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/firehawk/firehawk_optimization.py +4 -3
  38. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fireworks/fireworks_optimization.py +13 -9
  39. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fish_school_search/fish_school_search_optimization.py +29 -23
  40. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/flower_pollination_algorithm/flower_pollination_algorithm_optimization.py +5 -1
  41. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forensic_based_investigation/forensic_based_investigation_optimization.py +8 -4
  42. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forest_algorithm/forest_optimization_algorithm.py +6 -6
  43. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fox/fox_optimization.py +6 -1
  44. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/gaining_sharing_knowledge_algorithm/gaining_sharing_knowledge_optimization.py +21 -7
  45. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/genetic_algorithm/classes.py +6 -5
  46. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/genetic_algorithm/genetic_algorithm_optimization.py +9 -6
  47. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/germinal_center/germinal_center_optimization.py +10 -5
  48. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giant_trevally/giant_trevally_optimization.py +3 -3
  49. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giza_pyramid_construction/giza_pyramid_construction_optimization.py +1 -1
  50. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/golden_jackal/golden_jackal_optimization.py +3 -3
  51. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grasshopper/grasshopper_optimization_algorithm.py +9 -7
  52. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grey_wolf/grey_wolf_optimization.py +5 -1
  53. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/harmony_search/harmony_search_optimization.py +3 -2
  54. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/helpers.py +104 -24
  55. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/henry_gas_solubility/henry_gas_solubility_optimization.py +2 -2
  56. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/hunger_games_search/hunger_games_search_optimization.py +1 -1
  57. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/hypertuner.py +26 -13
  58. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/imperialist_competitive/classes.py +3 -1
  59. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/imperialist_competitive/imperialist_competitive_optimization.py +41 -24
  60. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/invasive_weed/invasive_weed_optimization.py +1 -1
  61. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/krill_herd/krill_herd_optimization.py +35 -23
  62. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/levi_jaya_swarm/levy_flight_jaya_swarm_optimization.py +1 -1
  63. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/marine_predators/marine_predators_optimization.py +2 -2
  64. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/models.py +155 -36
  65. pyvolutionary-2.7.1/pyvolutionary/monarch_butterfly/monarch_butterfly_optimization.py +83 -0
  66. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/moth_flame/moth_flame_optimization.py +1 -1
  67. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/mountain_gazelle/mountain_gazelle_optimization.py +1 -1
  68. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/multi_verse/multi_verse_optimization.py +1 -1
  69. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/multitask.py +23 -15
  70. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/nuclear_reaction/nuclear_reaction_optimization.py +7 -3
  71. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/osprey/osprey_optimization.py +2 -2
  72. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/particle_swarm/particle_swarm_optimization.py +3 -4
  73. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pathfinder_algorithm/pathfinder_algorithm_optimization.py +1 -1
  74. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pelican/pelican_optimization.py +2 -2
  75. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/runge_kutta/runge_kutta_optimization.py +5 -1
  76. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/salp_swarm/salp_swarm_optimization.py +1 -1
  77. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/seagull/seagull_optimization.py +1 -1
  78. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/serval/serval_optimization.py +2 -2
  79. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/siberian_tiger/siberian_tiger_optimization.py +2 -2
  80. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/sine_cosine_algorithm/qle_sine_cosine_algorithm_optimization.py +2 -2
  81. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/sine_cosine_algorithm/sine_cosine_algorithm_optimization.py +1 -1
  82. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/spotted_hyena/spotted_hyena_optimization.py +6 -5
  83. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/success_history_intelligent/success_history_intelligent_optimization.py +9 -1
  84. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/swarm_hill_climbing/swarm_hill_climbing_optimization.py +1 -1
  85. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tasmanian_devil/tasmanian_devil_optimization.py +2 -2
  86. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tuna_swarm/tuna_swarm_optimization.py +1 -1
  87. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/virus_colony_search/virus_colony_search_optimization.py +7 -3
  88. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/walrus/walrus_optimization.py +2 -2
  89. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/war_strategy/war_strategy_optimization.py +1 -1
  90. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/water_cycle/water_cycle_optimization.py +21 -5
  91. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/whales/whales_optimization.py +1 -1
  92. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wildebeest_herd/wildebeest_herd_optimization.py +6 -6
  93. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wind_driven/wind_driven_optimization.py +1 -1
  94. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/zebra/zebra_optimization.py +2 -2
  95. pyvolutionary-2.6.6/pyvolutionary/firefly_swarm/firefly_swarm_optimization.py +0 -76
  96. pyvolutionary-2.6.6/pyvolutionary/firefly_swarm/models.py +0 -37
  97. pyvolutionary-2.6.6/pyvolutionary/monarch_butterfly/monarch_butterfly_optimization.py +0 -74
  98. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/LICENSE +0 -0
  99. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/__init__.py +0 -0
  100. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/african_vulture/__init__.py +0 -0
  101. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/african_vulture/models.py +0 -0
  102. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_colony/__init__.py +0 -0
  103. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_colony/models.py +0 -0
  104. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_lion/__init__.py +0 -0
  105. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ant_lion/models.py +0 -0
  106. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/aquila/__init__.py +0 -0
  107. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/aquila/models.py +0 -0
  108. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/archimede/__init__.py +0 -0
  109. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/archimede/models.py +0 -0
  110. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bacterial_foraging/__init__.py +0 -0
  111. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bacterial_foraging/models.py +0 -0
  112. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bat/__init__.py +0 -0
  113. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bat/models.py +0 -0
  114. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/battle_royale/__init__.py +0 -0
  115. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/battle_royale/models.py +0 -0
  116. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bee_colony/__init__.py +0 -0
  117. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/bee_colony/models.py +0 -0
  118. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/biogeography_based/__init__.py +0 -0
  119. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/biogeography_based/models.py +0 -0
  120. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brain_storm/__init__.py +0 -0
  121. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brain_storm/models.py +0 -0
  122. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brown_bear/__init__.py +0 -0
  123. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/brown_bear/models.py +0 -0
  124. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/camel_caravan/__init__.py +0 -0
  125. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/camel_caravan/models.py +0 -0
  126. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cat_swarm/__init__.py +0 -0
  127. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cat_swarm/models.py +0 -0
  128. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chaos_game/__init__.py +0 -0
  129. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chaos_game/models.py +0 -0
  130. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chernobyl_disaster/__init__.py +0 -0
  131. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/chernobyl_disaster/models.py +0 -0
  132. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coati/__init__.py +0 -0
  133. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coati/models.py +0 -0
  134. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coral_reef/__init__.py +0 -0
  135. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coral_reef/models.py +0 -0
  136. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coronavirus_herd_immunity/__init__.py +0 -0
  137. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coronavirus_herd_immunity/models.py +0 -0
  138. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coyotes/__init__.py +0 -0
  139. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/coyotes/models.py +0 -0
  140. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cuckoo_search/__init__.py +0 -0
  141. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/cuckoo_search/models.py +0 -0
  142. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dragonfly/__init__.py +0 -0
  143. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dragonfly/models.py +0 -0
  144. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dwarf_mongoose/__init__.py +0 -0
  145. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/dwarf_mongoose/models.py +0 -0
  146. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/earthworms/__init__.py +0 -0
  147. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/earthworms/models.py +0 -0
  148. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/egret_swarm/__init__.py +0 -0
  149. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/egret_swarm/models.py +0 -0
  150. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/electromagnetic_field/__init__.py +0 -0
  151. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/electromagnetic_field/models.py +0 -0
  152. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/elephant_herd/__init__.py +0 -0
  153. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/elephant_herd/models.py +0 -0
  154. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/energy_valley/__init__.py +0 -0
  155. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/energy_valley/models.py +0 -0
  156. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/enums.py +0 -0
  157. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ficks_law/__init__.py +0 -0
  158. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/ficks_law/models.py +0 -0
  159. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/firefly_swarm/__init__.py +0 -0
  160. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/firehawk/__init__.py +0 -0
  161. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/firehawk/models.py +0 -0
  162. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fireworks/__init__.py +0 -0
  163. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fireworks/models.py +0 -0
  164. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fish_school_search/__init__.py +0 -0
  165. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fish_school_search/models.py +0 -0
  166. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/flower_pollination_algorithm/__init__.py +0 -0
  167. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/flower_pollination_algorithm/models.py +0 -0
  168. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forensic_based_investigation/__init__.py +0 -0
  169. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forensic_based_investigation/models.py +0 -0
  170. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forest_algorithm/__init__.py +0 -0
  171. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/forest_algorithm/models.py +0 -0
  172. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fox/__init__.py +0 -0
  173. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/fox/models.py +0 -0
  174. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/gaining_sharing_knowledge_algorithm/__init__.py +0 -0
  175. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/gaining_sharing_knowledge_algorithm/models.py +0 -0
  176. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/genetic_algorithm/__init__.py +0 -0
  177. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/genetic_algorithm/models.py +0 -0
  178. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/germinal_center/__init__.py +0 -0
  179. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/germinal_center/models.py +0 -0
  180. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giant_trevally/__init__.py +0 -0
  181. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giant_trevally/models.py +0 -0
  182. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giza_pyramid_construction/__init__.py +0 -0
  183. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/giza_pyramid_construction/models.py +0 -0
  184. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/golden_jackal/__init__.py +0 -0
  185. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/golden_jackal/models.py +0 -0
  186. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grasshopper/__init__.py +0 -0
  187. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grasshopper/models.py +0 -0
  188. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grey_wolf/__init__.py +0 -0
  189. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/grey_wolf/models.py +0 -0
  190. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/harmony_search/__init__.py +0 -0
  191. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/harmony_search/models.py +0 -0
  192. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/heap_based/__init__.py +0 -0
  193. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/heap_based/heap_based_optimization.py +0 -0
  194. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/heap_based/models.py +0 -0
  195. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/henry_gas_solubility/__init__.py +0 -0
  196. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/henry_gas_solubility/models.py +0 -0
  197. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/hunger_games_search/__init__.py +0 -0
  198. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/hunger_games_search/models.py +0 -0
  199. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/imperialist_competitive/__init__.py +0 -0
  200. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/imperialist_competitive/models.py +0 -0
  201. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/invasive_weed/__init__.py +0 -0
  202. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/invasive_weed/models.py +0 -0
  203. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/krill_herd/__init__.py +0 -0
  204. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/krill_herd/models.py +0 -0
  205. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/levi_jaya_swarm/__init__.py +0 -0
  206. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/levi_jaya_swarm/models.py +0 -0
  207. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/marine_predators/__init__.py +0 -0
  208. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/marine_predators/models.py +0 -0
  209. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/monarch_butterfly/__init__.py +0 -0
  210. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/monarch_butterfly/models.py +0 -0
  211. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/moth_flame/__init__.py +0 -0
  212. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/moth_flame/models.py +0 -0
  213. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/mountain_gazelle/__init__.py +0 -0
  214. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/mountain_gazelle/models.py +0 -0
  215. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/multi_verse/__init__.py +0 -0
  216. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/multi_verse/models.py +0 -0
  217. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/nuclear_reaction/__init__.py +0 -0
  218. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/nuclear_reaction/models.py +0 -0
  219. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/osprey/__init__.py +0 -0
  220. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/osprey/models.py +0 -0
  221. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/particle_swarm/__init__.py +0 -0
  222. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/particle_swarm/models.py +0 -0
  223. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pathfinder_algorithm/__init__.py +0 -0
  224. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pathfinder_algorithm/models.py +0 -0
  225. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pelican/__init__.py +0 -0
  226. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/pelican/models.py +0 -0
  227. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/runge_kutta/__init__.py +0 -0
  228. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/runge_kutta/models.py +0 -0
  229. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/salp_swarm/__init__.py +0 -0
  230. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/salp_swarm/models.py +0 -0
  231. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/seagull/__init__.py +0 -0
  232. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/seagull/models.py +0 -0
  233. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/serval/__init__.py +0 -0
  234. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/serval/models.py +0 -0
  235. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/siberian_tiger/__init__.py +0 -0
  236. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/siberian_tiger/models.py +0 -0
  237. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/sine_cosine_algorithm/__init__.py +0 -0
  238. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/sine_cosine_algorithm/classes.py +0 -0
  239. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/sine_cosine_algorithm/models.py +0 -0
  240. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/spotted_hyena/__init__.py +0 -0
  241. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/spotted_hyena/models.py +0 -0
  242. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/success_history_intelligent/__init__.py +0 -0
  243. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/success_history_intelligent/models.py +0 -0
  244. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/swarm_hill_climbing/__init__.py +0 -0
  245. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/swarm_hill_climbing/models.py +0 -0
  246. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tasmanian_devil/__init__.py +0 -0
  247. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tasmanian_devil/models.py +0 -0
  248. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tuna_swarm/__init__.py +0 -0
  249. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/tuna_swarm/models.py +0 -0
  250. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/utils.py +0 -0
  251. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/virus_colony_search/__init__.py +0 -0
  252. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/virus_colony_search/models.py +0 -0
  253. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/walrus/__init__.py +0 -0
  254. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/walrus/models.py +0 -0
  255. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/war_strategy/__init__.py +0 -0
  256. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/war_strategy/models.py +0 -0
  257. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/water_cycle/__init__.py +0 -0
  258. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/water_cycle/models.py +0 -0
  259. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/whales/__init__.py +0 -0
  260. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/whales/models.py +0 -0
  261. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wildebeest_herd/__init__.py +0 -0
  262. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wildebeest_herd/models.py +0 -0
  263. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wind_driven/__init__.py +0 -0
  264. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/wind_driven/models.py +0 -0
  265. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/zebra/__init__.py +0 -0
  266. {pyvolutionary-2.6.6 → pyvolutionary-2.7.1}/pyvolutionary/zebra/models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyVolutionary
3
- Version: 2.6.6
3
+ Version: 2.7.1
4
4
  Summary: pyVolutionary is a package collecting metaheuristic algorithms, available for free usage and able to solve a wide range of optimization problems.
5
5
  License: GPL-3.0-only
6
6
  License-File: LICENSE
@@ -331,6 +331,9 @@ As an illustration, consider a TSP scenario involving 5 cities denoted as A, B,
331
331
  be denoted by the permutation [A, B, D, E, C], illustrating the order in which the cities are visited. This interpretation
332
332
  indicates that the tour initiates at city A, proceeds to city B, then D, E, and ultimately C before looping back to city A.
333
333
 
334
+ A `PermutationVariable` spans one dimension per item: the position of an agent holds a key for each item, and the
335
+ permutation is the order of the items by increasing key. Use `transform_solution` to get the permutation of the items.
336
+
334
337
  The following code snippet illustrates how to solve the TSP with the Virus Colony Search Optimization algorithm.
335
338
 
336
339
  ```python
@@ -304,6 +304,9 @@ As an illustration, consider a TSP scenario involving 5 cities denoted as A, B,
304
304
  be denoted by the permutation [A, B, D, E, C], illustrating the order in which the cities are visited. This interpretation
305
305
  indicates that the tour initiates at city A, proceeds to city B, then D, E, and ultimately C before looping back to city A.
306
306
 
307
+ A `PermutationVariable` spans one dimension per item: the position of an agent holds a key for each item, and the
308
+ permutation is the order of the items by increasing key. Use `transform_solution` to get the permutation of the items.
309
+
307
310
  The following code snippet illustrates how to solve the TSP with the Virus Colony Search Optimization algorithm.
308
311
 
309
312
  ```python
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pyVolutionary"
3
- version = "2.6.6"
3
+ version = "2.7.1"
4
4
  description = "pyVolutionary is a package collecting metaheuristic algorithms, available for free usage and able to solve a wide range of optimization problems."
5
5
  authors = ["Matteo Cacciola <matteo.cacciola@gmail.com>"]
6
6
  license = "GPL-3.0-only"
@@ -1,3 +1,4 @@
1
+ import random
1
2
  from abc import ABC, abstractmethod
2
3
  from typing import Generic, Final, Any
3
4
  import numpy as np
@@ -50,6 +51,15 @@ class OptimizationAbstract(ABC, Generic[T]):
50
51
  def before_initialization(self):
51
52
  pass
52
53
 
54
+ def _minimum_population_size(self) -> int:
55
+ """
56
+ The minimum size of the population the algorithm can work with (e.g. an algorithm guided by the three best
57
+ agents needs at least three agents). The algorithms needing more than two agents override it.
58
+ :return: the minimum population size
59
+ :rtype: int
60
+ """
61
+ return 2
62
+
53
63
  def after_initialization(self):
54
64
  pass
55
65
 
@@ -66,14 +76,24 @@ class OptimizationAbstract(ABC, Generic[T]):
66
76
  def name(self):
67
77
  return self.__class__.__name__
68
78
 
69
- def _fcn(self, x: list[float] | np.ndarray) -> float | list[float]:
79
+ def _fcn(self, x: list[float] | np.ndarray, corrected: bool = False) -> float | list[float]:
70
80
  """
71
81
  This method evaluates the objective function.
72
82
  :param x: the position to evaluate
83
+ :param corrected: whether the position has already been corrected by the task
73
84
  :return the cost of the position, or the list of costs if the objective function is multi-objective
74
85
  :rtype: float | list[float]
75
86
  """
76
- return self._task.solve(x) if self._task.minmax == TaskType.MIN else -1 * self._task.solve(x)
87
+ # correcting an already corrected solution is a no-op for the built-in variables: skip it, and pass a copy of
88
+ # the position, so that the objective function can not alter it
89
+ if corrected and self._task.idempotent_correction:
90
+ cost = self._task.objective_function(list(x))
91
+ else:
92
+ cost = self._task.solve(x)
93
+ if self._task.minmax == TaskType.MIN:
94
+ return cost
95
+ # multi-objective functions return a list of costs: negate each of them
96
+ return [-c for c in cost] if isinstance(cost, (list, tuple, np.ndarray)) else -cost
77
97
 
78
98
  def _init_agent(self, position: list[Any] | np.ndarray | None = None) -> Agent:
79
99
  """
@@ -81,7 +101,7 @@ class OptimizationAbstract(ABC, Generic[T]):
81
101
  not provided. The other properties of the agent.
82
102
  """
83
103
  position = self._task.initial_solution(position)
84
- cost = self._fcn(position)
104
+ cost = self._fcn(position, corrected=True)
85
105
  n_weights = len(self._task.objective_weights) if self._task.objective_weights is not None else 1
86
106
  n_objectives = len(cost) if isinstance(cost, list) else 1
87
107
  if n_weights != n_objectives:
@@ -100,10 +120,25 @@ class OptimizationAbstract(ABC, Generic[T]):
100
120
 
101
121
  # Parallel mode
102
122
  with get_pool_executor(self._mode, self._workers) as executor:
103
- executors = [executor.submit(self._init_agent) for _ in range(0, n_agents)]
123
+ if self._mode == ModeSolver.PROCESS:
124
+ # each worker process inherits a copy of the parent's random state: seed each job independently, or
125
+ # different workers would generate identical agents
126
+ seeds = np.random.randint(0, 2**31 - 1, size=n_agents)
127
+ executors = [executor.submit(self._init_agent_with_seed, int(seed)) for seed in seeds]
128
+ else:
129
+ executors = [executor.submit(self._init_agent) for _ in range(0, n_agents)]
104
130
  pop = get_pool_results(executors)
105
131
  return pop
106
132
 
133
+ def _init_agent_with_seed(self, seed: int) -> Agent:
134
+ """
135
+ This method initializes a random agent after seeding the random generators. It is used in process mode.
136
+ :param seed: the seed of the random generators
137
+ """
138
+ np.random.seed(seed)
139
+ random.seed(seed)
140
+ return self._init_agent()
141
+
107
142
  def _init_population(self):
108
143
  """
109
144
  This method initializes the population of the optimization algorithm.
@@ -121,19 +156,11 @@ class OptimizationAbstract(ABC, Generic[T]):
121
156
  self._population = sort_by_cost(self._population)
122
157
  new_population = sort_by_cost(new_population)
123
158
 
124
- # Serial mode
125
- if self._mode == ModeSolver.SERIAL:
126
- self._population = [
127
- self._greedy_select_agent(agent, new_population[idx]) for idx, agent in enumerate(self._population)
128
- ]
129
- return
130
-
131
- # Parallel mode
132
- with get_pool_executor(self._mode, self._workers) as executor:
133
- executors = [executor.submit(
134
- self._greedy_select_agent, agent, new_population[idx]
135
- ) for idx, agent in enumerate(self._population)]
136
- self._population = get_pool_results(executors)
159
+ # the selection is a mere comparison of costs: dispatching it to a pool of workers would be far more
160
+ # expensive than running it serially
161
+ self._population = [
162
+ self._greedy_select_agent(agent, new_population[idx]) for idx, agent in enumerate(self._population)
163
+ ]
137
164
 
138
165
  def _greedy_select_agent(self, agent: T, new_agent: T) -> T:
139
166
  """
@@ -206,9 +233,24 @@ class OptimizationAbstract(ABC, Generic[T]):
206
233
  if not self._config:
207
234
  raise ValueError("Invalid configuration")
208
235
 
236
+ minimum_population_size = self._minimum_population_size()
237
+ if self._config.population_size < minimum_population_size:
238
+ raise ValueError(
239
+ f"{self.name} needs a population of at least {minimum_population_size} agents. "
240
+ f"Got {self._config.population_size}"
241
+ )
242
+
209
243
  np.random.seed(task.seed)
244
+ random.seed(task.seed)
210
245
  evolution: list[Population] = []
211
246
 
247
+ # reset the state of any previous run, so that the same instance can be used to optimize several times
248
+ self._current_cycle = 1
249
+ self._errors = []
250
+ self._error_diffs = []
251
+ self._best_agent = None
252
+ self._worst_agent = None
253
+
212
254
  if workers is not None:
213
255
  if workers <= 0:
214
256
  raise ValueError("Invalid number of workers. It must be greater than 0")
@@ -235,6 +277,7 @@ class OptimizationAbstract(ABC, Generic[T]):
235
277
  self._init_population()
236
278
  evolution.append(Population(agents=self._population, task_type=task.minmax))
237
279
  (self._best_agent, ), (self._worst_agent, ) = special_agents(self._population, n_best=1, n_worst=1)
280
+ self._best_agent = self._best_agent.model_copy()
238
281
 
239
282
  self.after_initialization()
240
283
 
@@ -249,7 +292,11 @@ class OptimizationAbstract(ABC, Generic[T]):
249
292
  # append the current population to the evolution, being sure that costs and fitness are updated
250
293
  evolution.append(Population(agents=self._population, task_type=task.minmax))
251
294
 
252
- (self._best_agent, ), (self._worst_agent, ) = special_agents(self._population, n_best=1, n_worst=1)
295
+ # the best agent is the best one found so far (the global best): an algorithm may lose it from the
296
+ # population, but it must neither be lost in the result nor stop guiding the algorithms using it
297
+ (current_best, ), (self._worst_agent, ) = special_agents(self._population, n_best=1, n_worst=1)
298
+ if current_best.cost < self._best_agent.cost:
299
+ self._best_agent = current_best.model_copy()
253
300
 
254
301
  # stop when the error is below the error criteria or when the maximum number of cycles is reached
255
302
  error, fitness, has_to_stop = self.__error_check__()
@@ -280,14 +327,13 @@ class OptimizationAbstract(ABC, Generic[T]):
280
327
  # Get the optimal population
281
328
  avg_fit = average_fitness(self._population)
282
329
  current_error = abs(1 - avg_fit)
283
- previous_error = self._errors[-1] if len(self._errors) > 0 else 0
330
+ # Append the difference between the current error and the previous one to the list of error differences
331
+ if len(self._errors) > 0:
332
+ self._error_diffs.append(current_error - self._errors[-1])
284
333
 
285
334
  # Append the current error to the list of errors
286
335
  self._errors.append(current_error)
287
336
 
288
- # Append the difference between the current error and the previous one to the list of error differences
289
- self._error_diffs.append(current_error - previous_error)
290
-
291
337
  return current_error, avg_fit, self.__should_stop__(current_error)
292
338
 
293
339
  def __should_stop__(self, current_error: float) -> bool:
@@ -301,9 +347,12 @@ class OptimizationAbstract(ABC, Generic[T]):
301
347
  has_to_stop = cycle >= max_cycles
302
348
 
303
349
  # Evaluate the early stopping criteria
350
+ # (stop when the error has not improved, i.e. decreased, by at least min_delta for patience cycles)
304
351
  if early_stopping is not None:
305
352
  min_delta, patience = early_stopping.min_delta, early_stopping.patience
306
- has_to_stop |= all([diff < 0 and abs(diff) < min_delta for diff in self._error_diffs[-patience:]])
353
+ has_to_stop |= len(self._error_diffs) >= patience and all(
354
+ -diff < min_delta for diff in self._error_diffs[-patience:]
355
+ )
307
356
 
308
357
  # Stop when the error is below the error criteria
309
358
  if fitness_error is not None:
@@ -35,13 +35,17 @@ class AfricanVultureOptimization(OptimizationAbstract):
35
35
  best_x1 = np.array(best_list[0].position)
36
36
  best_x2 = np.array(best_list[1].position)
37
37
  if np.random.random() < p2:
38
- A = best_x1 - ((best_x1 * position) / (best_x1 - position ** 2)) * F
39
- B = best_x2 - ((best_x2 * position) / (best_x2 - position ** 2)) * F
38
+ A = best_x1 - safe_ratio(best_x1 * position, best_x1 - position ** 2) * F
39
+ B = best_x2 - safe_ratio(best_x2 * position, best_x2 - position ** 2) * F
40
40
  return (A + B) / 2
41
41
  return rand_pos - np.abs(rand_pos - position) * F * get_levy_flight_step(
42
42
  beta=1.5, multiplier=1., size=dim, case=-1
43
43
  )
44
44
 
45
+ def safe_ratio(numerator: np.ndarray, denominator: np.ndarray) -> np.ndarray:
46
+ # the ratio is zero where the denominator is zero (it was NaN or infinite, e.g. with best_x = position = 0)
47
+ return np.divide(numerator, denominator, out=np.zeros_like(numerator, dtype=float), where=denominator != 0)
48
+
45
49
  def exploration_position_phase2(rand_pos: np.ndarray, position: np.ndarray) -> np.ndarray:
46
50
  if np.random.random() < p3:
47
51
  return (
@@ -66,7 +70,7 @@ class AfricanVultureOptimization(OptimizationAbstract):
66
70
  else: # Exploitation Phase 2
67
71
  pos_new = exploration_position_phase2(rand_pos, position)
68
72
  return self._greedy_select_agent(
69
- vulture, AfricanVulture(**self._init_agent(pos_new).model_dump())
73
+ vulture, AfricanVulture(**self._init_agent(pos_new).__dict__)
70
74
  )
71
75
 
72
76
  cycle_ratio = self._current_cycle / self._config.max_cycles
@@ -2,7 +2,7 @@ from typing import Any
2
2
  import numpy as np
3
3
 
4
4
  from ..helpers import (
5
- roulette_wheel_indexes,
5
+ roulette_wheel_sampler,
6
6
  parse_obj_doc, # type: ignore
7
7
  )
8
8
  from ..abstract import OptimizationAbstract
@@ -35,7 +35,7 @@ class AntColonyOptimization(OptimizationAbstract):
35
35
  return zeta * np.sum(np.abs(positions - M), axis=0) / (pop_size - 1)
36
36
 
37
37
  def generate_coordinate(j: int) -> float:
38
- rdx, = roulette_wheel_indexes(weights)
38
+ rdx = select_ant()
39
39
  return float(self._population[rdx].position[j] + np.random.normal() * sigmas[rdx, j])
40
40
 
41
41
  # compute the selection probability of each ant in the population
@@ -51,9 +51,12 @@ class AntColonyOptimization(OptimizationAbstract):
51
51
 
52
52
  sigmas = np.array([compute_sigma(ant) for ant in self._population])
53
53
 
54
+ # the weights do not change while generating the new ants: build the roulette wheel once
55
+ select_ant = roulette_wheel_sampler(weights)
56
+
54
57
  # Generate new ants
55
58
  new_ants = [Ant(**self._init_agent(
56
59
  list(map(generate_coordinate, range(0, self._task.space_dimension)))
57
- ).model_dump()) for _ in range(0, self._config.archive_size)]
60
+ ).__dict__) for _ in range(0, self._config.archive_size)]
58
61
 
59
62
  self._extend_and_trim_population(new_ants)
@@ -42,25 +42,25 @@ class AntLionOptimization(OptimizationAbstract):
42
42
  return 1 + 1000000 * (cycle / max_cycles)
43
43
  return 1
44
44
 
45
- def random_walk_ant_lion(ant_lion_position: np.ndarray) -> np.ndarray:
46
- X = np.array([np.cumsum(2 * (np.random.random(pop_size) > 0.5) - 1) for _ in range(0, n_dims)])
45
+ def random_walk_ant_lion(ant_lion_position: np.ndarray, idx: int) -> np.ndarray:
46
+ # one row of random steps per dimension (drawn at once, in the same order as row by row)
47
+ X = np.cumsum(2 * (np.random.random((n_dims, pop_size)) > 0.5) - 1, axis=1)
47
48
  # Move the interval of [lb ub] around the ant lion [lb + ant_lion, ub + ant_lion]. Eq 2.8, 2.9
48
49
  lb = lower_bounds * (1 if np.random.random() < 0.5 else -1) + ant_lion_position
49
50
  ub = upper_bounds * (1 if np.random.random() < 0.5 else -1) + ant_lion_position
50
51
  a = np.min(X, axis=1)
51
52
  b = np.max(X, axis=1)
52
- return (X - np.reshape(a, (n_dims, 1))) * (
53
- np.reshape((ub - lb) / (b - a), (n_dims, 1))
54
- ) + np.reshape(lb, (n_dims, 1))
53
+ # only the idx-th step of the walk is used: normalize just that column
54
+ return (X[:, idx] - a) * ((ub - lb) / (b - a)) + lb
55
55
 
56
56
  def new_agent(idx: int) -> AntLion:
57
57
  roulette_index, = roulette_wheel_indexes(weights)
58
58
  # RA is the random walk around the selected ant lion by roulette wheel
59
- RA = random_walk_ant_lion(np.array(self._population[roulette_index].position))
59
+ RA = random_walk_ant_lion(np.array(self._population[roulette_index].position), idx)
60
60
  # RE is the random walk around the elite (the best ant lion so far)
61
- RE = random_walk_ant_lion(g_best_pos)
62
- pos_new = (RA[:, idx] + RE[:, idx]) / 2 # Equation(2.13) in the paper
63
- return AntLion(**self._init_agent(pos_new).model_dump())
61
+ RE = random_walk_ant_lion(g_best_pos, idx)
62
+ pos_new = (RA + RE) / 2 # Equation(2.13) in the paper
63
+ return AntLion(**self._init_agent(pos_new).__dict__)
64
64
 
65
65
  cycle = self._current_cycle
66
66
  max_cycles = self._config.max_cycles
@@ -45,7 +45,7 @@ class AquilaOptimization(OptimizationAbstract):
45
45
  ) if np.random.random() < 0.5 else QF * best_position - (
46
46
  g2 * pos * np.random.random()
47
47
  ) - g2 * levy_step + np.random.random() * g1 # Eq. 13, 14
48
- new_agent = Aquila(**self._init_agent(pos_new).model_dump())
48
+ new_agent = Aquila(**self._init_agent(pos_new).__dict__)
49
49
  return self._greedy_select_agent(new_agent, aquila)
50
50
 
51
51
  current_cycle = self._current_cycle
@@ -44,7 +44,7 @@ class ArchimedeOptimization(OptimizationAbstract):
44
44
  acceleration = self._task.empty_solution() if acceleration is None else (
45
45
  acceleration.tolist() if isinstance(acceleration, np.ndarray) else acceleration
46
46
  )
47
- return Object(**agent.model_dump(), density=density, volume=volume, acceleration=acceleration)
47
+ return Object(**agent.__dict__, density=density, volume=volume, acceleration=acceleration)
48
48
 
49
49
  def optimization_step(self):
50
50
  def calculate_new_properties(idx: int, obj: Object) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
@@ -36,19 +36,21 @@ class BacterialForagingOptimization(OptimizationAbstract):
36
36
 
37
37
  def _init_agent(self, position: list[Any] | np.ndarray | None = None) -> Cell:
38
38
  agent = super()._init_agent(position)
39
- return Cell(**agent.model_dump(), local_best=agent.position.copy(), local_cost=agent.cost)
39
+ return Cell(**agent.__dict__, local_best=agent.position.copy(), local_cost=agent.cost)
40
40
 
41
41
  def __clean_population__(self):
42
42
  """
43
43
  Remove duplicates from the population. This method is called after each optimization step.
44
44
  """
45
- new_set = set()
46
- pop = self._population.copy()
47
- for idx, obj in enumerate(pop):
48
- if pos := tuple(obj.position) in new_set:
49
- self._population.pop(idx)
50
- else:
51
- new_set.add(pos)
45
+ # keep the first cell of each position
46
+ seen = set()
47
+ unique = []
48
+ for cell in self._population:
49
+ position = tuple(cell.position)
50
+ if position not in seen:
51
+ seen.add(position)
52
+ unique.append(cell)
53
+ self._population = unique
52
54
 
53
55
  def __balance_population__(self):
54
56
  """
@@ -63,13 +65,15 @@ class BacterialForagingOptimization(OptimizationAbstract):
63
65
  if n_agents < 0:
64
66
  self._population += [self._init_agent() for _ in range(0, -n_agents)]
65
67
  else:
66
- list_idx_removed = np.random.choice(range(0, len(self._population)), n_agents, replace=False)
68
+ list_idx_removed = np.random.choice(len(self._population), n_agents, replace=False)
67
69
  for idx in sorted(list_idx_removed, reverse=True):
68
70
  self._population.pop(idx)
69
71
 
70
72
  def optimization_step(self):
71
73
  def update_step_size(c: Cell) -> float:
72
- step_sz = C_s - (C_s - C_e) * c.cost / total_costs
74
+ # the step size decreases with the share of the cost of the cell (all the costs may be zero)
75
+ share = c.cost / total_costs if total_costs != 0 else 0
76
+ step_sz = C_s - (C_s - C_e) * share
73
77
  return step_sz / c.nutrients if c.nutrients > 0 else step_sz
74
78
 
75
79
  def tumble_cell(c: Cell, step_sz: float) -> Cell:
@@ -89,11 +93,11 @@ class BacterialForagingOptimization(OptimizationAbstract):
89
93
  c.nutrients -= 1
90
94
  continue
91
95
  new_cell.nutrients += 1
96
+ # the personal best is the best position found by the cell so far
97
+ if c.local_cost < new_cell.cost:
98
+ new_cell.local_best = list(c.local_best)
99
+ new_cell.local_cost = c.local_cost
92
100
  c = new_cell
93
- # update personal best
94
- if new_cell.cost < c.local_cost:
95
- c.local_best = new_cell.position.copy()
96
- c.local_cost = new_cell.cost
97
101
  return c
98
102
 
99
103
  C_s, C_e = self.__C_s, self.__C_e
@@ -107,6 +111,7 @@ class BacterialForagingOptimization(OptimizationAbstract):
107
111
 
108
112
  step_size = update_step_size(cell)
109
113
  cell = swim(cell, step_size)
114
+ self._population[idx] = cell
110
115
 
111
116
  m = max(self._config.N_split, self._config.N_split + (
112
117
  len(self._population) - self._config.population_size
@@ -124,7 +129,8 @@ class BacterialForagingOptimization(OptimizationAbstract):
124
129
  len(self._population) - self._config.population_size
125
130
  ) / self._config.N_adapt)
126
131
 
127
- if cell.nutrients < nut_min and np.random.random() < self._config.Ped:
132
+ # eliminate the cell if it is starving, or with probability Ped (elimination-dispersal)
133
+ if cell.nutrients < nut_min or np.random.random() < self._config.Ped:
128
134
  self._population[idx] = self._init_agent()
129
135
 
130
136
  # make sure the population does not have duplicates
@@ -37,7 +37,7 @@ class BatOptimization(OptimizationAbstract):
37
37
  velocity = self._task.correct_solution(velocity if velocity is not None else self._task.empty_solution())
38
38
 
39
39
  return Bat(
40
- **agent.model_dump(),
40
+ **agent.__dict__,
41
41
  velocity=velocity,
42
42
  loudness=loudness if loudness is not None else np.random.uniform(*self._config.loudness),
43
43
  pulse_rate=pulse_rate if pulse_rate is not None else np.random.uniform(*self._config.pulse_rate),
@@ -31,7 +31,7 @@ class BattleRoyaleOptimization(OptimizationAbstract):
31
31
 
32
32
  def _init_agent(self, position: list[Any] | np.ndarray | None = None, damage: int | None = None) -> Soldier:
33
33
  agent = super()._init_agent(position=position)
34
- soldier = Soldier(**agent.model_dump())
34
+ soldier = Soldier(**agent.__dict__)
35
35
  if damage is not None:
36
36
  soldier.damage = damage
37
37
  return soldier
@@ -2,6 +2,7 @@ from typing import Any
2
2
  import numpy as np
3
3
 
4
4
  from ..helpers import (
5
+ normalize_costs,
5
6
  get_partner_index,
6
7
  roulette_wheel_indexes,
7
8
  parse_obj_doc, # type: ignore
@@ -33,12 +34,18 @@ class BeeColonyOptimization(OptimizationAbstract):
33
34
  def set_config_parameters(self, parameters: dict[str, Any]):
34
35
  self._config = BeeColonyOptimizationConfig(**parameters)
35
36
 
36
- def before_initialization(self):
37
- self._config.population_size = int(self._config.population_size / 2)
37
+ def _minimum_population_size(self) -> int:
38
+ # half of the colony are food sources, and a food source needs a partner
39
+ return 4
40
+
41
+ def _init_population(self):
42
+ # half of the colony are employed bees, each one bound to a food source: the population is made of the food
43
+ # sources (the configuration is not altered, so that it can be reused for further optimizations)
44
+ self._population = self._generate_agents(int(self._config.population_size / 2))
38
45
 
39
46
  def _init_agent(self, position: list[float] | np.ndarray | None = None) -> Bee:
40
47
  agent = super()._init_agent(position)
41
- return Bee(**agent.model_dump())
48
+ return Bee(**agent.__dict__)
42
49
 
43
50
  def _greedy_select_agent(self, agent: Bee, new_agent: Bee) -> Bee:
44
51
  """
@@ -77,7 +84,7 @@ class BeeColonyOptimization(OptimizationAbstract):
77
84
  selected_bee = self._population[jdx]
78
85
  return food_source_dance(idx, selected_bee)
79
86
 
80
- population_size = self._config.population_size
87
+ population_size = len(self._population)
81
88
  dims = self._task.space_dimension
82
89
  phi = np.random.uniform(low=-1, high=1, size=dims)
83
90
 
@@ -87,7 +94,7 @@ class BeeColonyOptimization(OptimizationAbstract):
87
94
  # based to probability, generate a neighbour point and evaluate again some food sources
88
95
  # same food source can be evaluated multiple times
89
96
  employed_costs = np.array([agent.cost for agent in self._population])
90
- probabilities = employed_costs / np.sum(employed_costs)
97
+ probabilities = normalize_costs(employed_costs)
91
98
  self._population = [send_onlooker_bees(idx) for idx in range(0, population_size)]
92
99
 
93
100
  # abandon the food sources which have not been improved after a predefined number of trials; it means to send
@@ -2,6 +2,7 @@ from typing import Any
2
2
  import numpy as np
3
3
 
4
4
  from ..helpers import (
5
+ normalize_costs,
5
6
  best_agents,
6
7
  roulette_wheel_indexes,
7
8
  parse_obj_doc, # type: ignore
@@ -45,7 +46,7 @@ class BiogeographyBasedOptimization(OptimizationAbstract):
45
46
  pos_new = np.where(condition, self._population[idx_selected].position, population.position)
46
47
  # Mutation
47
48
  pos_new = np.where(np.random.random(n_dims) < p_m, self._task.empty_solution(), pos_new)
48
- agent_new = Population(**self._init_agent(pos_new).model_dump())
49
+ agent_new = Population(**self._init_agent(pos_new).__dict__)
49
50
  return self._greedy_select_agent(population, agent_new)
50
51
 
51
52
  n_dims = self._task.space_dimension
@@ -55,8 +56,7 @@ class BiogeographyBasedOptimization(OptimizationAbstract):
55
56
  pop_elites = best_agents(self._population, n_best=self._config.n_elites)
56
57
 
57
58
  # pick a position from which to emigrate (roulette wheel selection)
58
- costs = np.array([agent.cost for agent in self._population])
59
- costs /= np.sum(costs)
59
+ costs = normalize_costs(np.array([agent.cost for agent in self._population]))
60
60
 
61
61
  self._population = [evolve(idx, agent) for idx, agent in enumerate(self._population)]
62
62
 
@@ -4,6 +4,7 @@ import numpy as np
4
4
 
5
5
  from ..helpers import (
6
6
  find_centers,
7
+ split_in_clusters,
7
8
  parse_obj_doc, # type: ignore
8
9
  )
9
10
  from ..abstract import OptimizationAbstract
@@ -25,40 +26,39 @@ class BrainStormOptimization(OptimizationAbstract):
25
26
  """
26
27
  def __init__(self, config: BrainStormOptimizationConfig | None = None, debug: bool | None = False):
27
28
  super().__init__(config, debug)
28
- self.__m_solution: int | None = None
29
29
  self.__clusters: list[list[Person]] = []
30
30
  self.__centers: list[Person] = []
31
31
 
32
32
  def set_config_parameters(self, parameters: dict[str, Any]):
33
33
  self._config = BrainStormOptimizationConfig(**parameters)
34
34
 
35
- def before_initialization(self):
36
- self.__m_solution = int(self._config.population_size / self._config.m_clusters)
37
-
38
35
  def after_initialization(self):
39
- self.__clusters = self._generate_group_population(self._config.m_clusters, self.__m_solution, False)
36
+ # every agent belongs to a cluster, also when the population size is not a multiple of m_clusters
37
+ self.__clusters = split_in_clusters(self._population, self._config.m_clusters)
40
38
  self.__centers: list[Person] = find_centers(self.__clusters)
41
39
 
42
40
  def optimization_step(self):
43
41
  def evolve(idx: int) -> tuple[Person, int, int]:
44
- cluster_id = int(idx / m_solution)
45
- location_id = int(idx % m_solution)
42
+ cluster_id, location_id = locations[idx]
46
43
  if np.random.uniform() < p2: # p_6b
47
- rand_idx = np.random.randint(0, m_solution)
44
+ rand_idx = np.random.randint(0, len(self.__clusters[cluster_id]))
48
45
  pos_new = np.array(self.__clusters[cluster_id][rand_idx].position) + np.random.normal(0, 1, n_dims)
49
46
  if np.random.uniform() < p3: # p_6i
50
- cluster_id = np.random.randint(0, m_clusters)
51
- pos_new = np.array(self.__centers[cluster_id].position) + epsilon * np.random.normal(0, 1, n_dims)
47
+ # the new idea comes from the center of a random cluster, and competes with the agent idx
48
+ center_id = np.random.randint(0, m_clusters)
49
+ pos_new = np.array(self.__centers[center_id].position) + epsilon * np.random.normal(0, 1, n_dims)
52
50
  else:
53
- id1, id2 = np.random.choice(range(0, m_clusters), 2, replace=False)
51
+ id1, id2 = np.random.choice(m_clusters, 2, replace=False)
54
52
  pos_new = 0.5 * (
55
53
  np.array(self.__centers[id1].position) + np.array(self.__centers[id2].position)
56
54
  ) + epsilon * np.random.normal(0, 1, n_dims)
57
55
  if np.random.uniform() >= p4:
58
- pos_rand_id1 = np.array(self.__clusters[id1][np.random.randint(0, m_solution)].position)
59
- pos_rand_id2 = np.array(self.__clusters[id2][np.random.randint(0, m_solution)].position)
56
+ cluster1 = self.__clusters[id1]
57
+ pos_rand_id1 = np.array(cluster1[np.random.randint(0, len(cluster1))].position)
58
+ cluster2 = self.__clusters[id2]
59
+ pos_rand_id2 = np.array(cluster2[np.random.randint(0, len(cluster2))].position)
60
60
  pos_new = 0.5 * (pos_rand_id1 + pos_rand_id2) + epsilon * np.random.normal(0, 1, n_dims)
61
- agent = Person(**self._init_agent(pos_new).model_dump())
61
+ agent = Person(**self._init_agent(pos_new).__dict__)
62
62
  return agent, cluster_id, location_id
63
63
 
64
64
  epoch = self._current_cycle
@@ -68,16 +68,17 @@ class BrainStormOptimization(OptimizationAbstract):
68
68
  epsilon = np.random.uniform() * (1 / (1 + np.exp(-x)))
69
69
 
70
70
  p1, p2, p3, p4 = self._config.p1, self._config.p2, self._config.p3, self._config.p4
71
- m_solution = self.__m_solution
71
+ # the (cluster, location) of each agent, cluster by cluster
72
+ locations = [(c_id, l_id) for c_id, cluster in enumerate(self.__clusters) for l_id in range(0, len(cluster))]
72
73
 
73
74
  n_dims = self._task.space_dimension
74
- m_clusters = self._config.m_clusters
75
+ m_clusters = len(self.__clusters)
75
76
 
76
77
  epsilon = 1. - 1. * epoch / epochs
77
78
  if np.random.uniform() < p1:
78
- self.__centers[np.random.randint(0, m_clusters)] = Person(**self._init_agent().model_dump())
79
+ self.__centers[np.random.randint(0, m_clusters)] = Person(**self._init_agent().__dict__)
79
80
 
80
- new_agents, cluster_ids, location_ids = zip(*[evolve(idx) for idx in range(0, self._config.population_size)])
81
+ new_agents, cluster_ids, location_ids = zip(*[evolve(idx) for idx in range(0, len(locations))])
81
82
  for cl_id, loc_id, new_agent in zip(cluster_ids, location_ids, new_agents):
82
83
  self.__clusters[cl_id][loc_id] = self._greedy_select_agent(self.__clusters[cl_id][loc_id], new_agent)
83
84