pyVolutionary 1.0.1__py3-none-any.whl

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 (173) hide show
  1. pyvolutionary/__init__.py +44 -0
  2. pyvolutionary/abstract.py +325 -0
  3. pyvolutionary/african_vulture/__init__.py +2 -0
  4. pyvolutionary/african_vulture/african_vulture_optimization.py +87 -0
  5. pyvolutionary/african_vulture/models.py +41 -0
  6. pyvolutionary/ant_colony/__init__.py +2 -0
  7. pyvolutionary/ant_colony/ant_colony_optimization.py +80 -0
  8. pyvolutionary/ant_colony/models.py +25 -0
  9. pyvolutionary/aquila/__init__.py +2 -0
  10. pyvolutionary/aquila/aquila_optimization.py +75 -0
  11. pyvolutionary/aquila/models.py +9 -0
  12. pyvolutionary/bacterial_foraging/__init__.py +2 -0
  13. pyvolutionary/bacterial_foraging/bacterial_foraging_optimization.py +152 -0
  14. pyvolutionary/bacterial_foraging/models.py +63 -0
  15. pyvolutionary/bat/__init__.py +2 -0
  16. pyvolutionary/bat/bat_optimization.py +66 -0
  17. pyvolutionary/bat/models.py +66 -0
  18. pyvolutionary/bee_colony/__init__.py +2 -0
  19. pyvolutionary/bee_colony/bee_colony_optimization.py +116 -0
  20. pyvolutionary/bee_colony/models.py +22 -0
  21. pyvolutionary/camel_caravan/__init__.py +2 -0
  22. pyvolutionary/camel_caravan/camel_caravan_optimization.py +96 -0
  23. pyvolutionary/camel_caravan/models.py +61 -0
  24. pyvolutionary/coral_reef/__init__.py +2 -0
  25. pyvolutionary/coral_reef/coral_reef_optimization.py +133 -0
  26. pyvolutionary/coral_reef/models.py +81 -0
  27. pyvolutionary/coyotes/__init__.py +2 -0
  28. pyvolutionary/coyotes/coyotes_optimization.py +96 -0
  29. pyvolutionary/coyotes/models.py +22 -0
  30. pyvolutionary/earthworms/__init__.py +2 -0
  31. pyvolutionary/earthworms/earthworms_optimization.py +124 -0
  32. pyvolutionary/earthworms/models.py +62 -0
  33. pyvolutionary/electromagnetic_field/__init__.py +2 -0
  34. pyvolutionary/electromagnetic_field/electromagnetic_field_optimization.py +58 -0
  35. pyvolutionary/electromagnetic_field/models.py +45 -0
  36. pyvolutionary/elephant_herd/__init__.py +2 -0
  37. pyvolutionary/elephant_herd/elephant_herd_optimization.py +56 -0
  38. pyvolutionary/elephant_herd/models.py +38 -0
  39. pyvolutionary/enums.py +39 -0
  40. pyvolutionary/firefly_swarm/__init__.py +2 -0
  41. pyvolutionary/firefly_swarm/firefly_swarm_optimization.py +76 -0
  42. pyvolutionary/firefly_swarm/models.py +37 -0
  43. pyvolutionary/fireworks/__init__.py +2 -0
  44. pyvolutionary/fireworks/fireworks_optimization.py +96 -0
  45. pyvolutionary/fireworks/models.py +41 -0
  46. pyvolutionary/fish_school_search/__init__.py +2 -0
  47. pyvolutionary/fish_school_search/fish_school_search_optimization.py +142 -0
  48. pyvolutionary/fish_school_search/models.py +27 -0
  49. pyvolutionary/flower_pollination_algorithm/__init__.py +2 -0
  50. pyvolutionary/flower_pollination_algorithm/flower_pollination_algorithm_optimization.py +46 -0
  51. pyvolutionary/flower_pollination_algorithm/models.py +29 -0
  52. pyvolutionary/forest_algorithm/__init__.py +2 -0
  53. pyvolutionary/forest_algorithm/forest_optimization_algorithm.py +121 -0
  54. pyvolutionary/forest_algorithm/models.py +53 -0
  55. pyvolutionary/fox/__init__.py +2 -0
  56. pyvolutionary/fox/fox_optimization.py +43 -0
  57. pyvolutionary/fox/models.py +29 -0
  58. pyvolutionary/genetic_algorithm/__init__.py +2 -0
  59. pyvolutionary/genetic_algorithm/genetic_algorithm_optimization.py +167 -0
  60. pyvolutionary/genetic_algorithm/models.py +21 -0
  61. pyvolutionary/grasshopper/__init__.py +2 -0
  62. pyvolutionary/grasshopper/grasshopper_optimization_algorithm.py +73 -0
  63. pyvolutionary/grasshopper/models.py +29 -0
  64. pyvolutionary/grey_wolf/__init__.py +2 -0
  65. pyvolutionary/grey_wolf/grey_wolf_optimization.py +62 -0
  66. pyvolutionary/grey_wolf/models.py +9 -0
  67. pyvolutionary/harmony_search/__init__.py +2 -0
  68. pyvolutionary/harmony_search/harmony_search_optimization.py +64 -0
  69. pyvolutionary/harmony_search/models.py +29 -0
  70. pyvolutionary/helpers.py +290 -0
  71. pyvolutionary/imperialist_competitive/__init__.py +2 -0
  72. pyvolutionary/imperialist_competitive/classes.py +119 -0
  73. pyvolutionary/imperialist_competitive/imperialist_competitive_optimization.py +198 -0
  74. pyvolutionary/imperialist_competitive/models.py +47 -0
  75. pyvolutionary/invasive_weed/__init__.py +2 -0
  76. pyvolutionary/invasive_weed/invasive_weed_optimization.py +52 -0
  77. pyvolutionary/invasive_weed/models.py +51 -0
  78. pyvolutionary/krill_herd/__init__.py +2 -0
  79. pyvolutionary/krill_herd/krill_herd_optimization.py +179 -0
  80. pyvolutionary/krill_herd/models.py +62 -0
  81. pyvolutionary/levi_jaya_swarm/__init__.py +2 -0
  82. pyvolutionary/levi_jaya_swarm/levy_flight_jaya_swarm_optimization.py +40 -0
  83. pyvolutionary/levi_jaya_swarm/models.py +9 -0
  84. pyvolutionary/models.py +130 -0
  85. pyvolutionary/monarch_butterfly/__init__.py +2 -0
  86. pyvolutionary/monarch_butterfly/models.py +25 -0
  87. pyvolutionary/monarch_butterfly/monarch_butterfly_optimization.py +104 -0
  88. pyvolutionary/mountain_gazelle/__init__.py +2 -0
  89. pyvolutionary/mountain_gazelle/models.py +9 -0
  90. pyvolutionary/mountain_gazelle/mountain_gazelle_optimization.py +118 -0
  91. pyvolutionary/osprey/__init__.py +2 -0
  92. pyvolutionary/osprey/models.py +9 -0
  93. pyvolutionary/osprey/osprey_optimization.py +54 -0
  94. pyvolutionary/particle_swarm/__init__.py +2 -0
  95. pyvolutionary/particle_swarm/models.py +40 -0
  96. pyvolutionary/particle_swarm/particle_swarm_optimization.py +78 -0
  97. pyvolutionary/pathfinder_algorithm/__init__.py +2 -0
  98. pyvolutionary/pathfinder_algorithm/models.py +12 -0
  99. pyvolutionary/pathfinder_algorithm/pathfinder_algorithm_optimization.py +56 -0
  100. pyvolutionary/pelican/__init__.py +2 -0
  101. pyvolutionary/pelican/models.py +9 -0
  102. pyvolutionary/pelican/pelican_optimization.py +46 -0
  103. pyvolutionary/seagull/__init__.py +2 -0
  104. pyvolutionary/seagull/models.py +21 -0
  105. pyvolutionary/seagull/seagull_optimization.py +43 -0
  106. pyvolutionary/siberian_tiger/__init__.py +2 -0
  107. pyvolutionary/siberian_tiger/models.py +9 -0
  108. pyvolutionary/siberian_tiger/siberian_tiger_optimization.py +53 -0
  109. pyvolutionary/tasmanian_devil/__init__.py +2 -0
  110. pyvolutionary/tasmanian_devil/models.py +9 -0
  111. pyvolutionary/tasmanian_devil/tasmanian_devil_optimization.py +69 -0
  112. pyvolutionary/tests/fixtures.py +21 -0
  113. pyvolutionary/tests/test_african_vulture_optimization.py +34 -0
  114. pyvolutionary/tests/test_ant_colony_optimization.py +34 -0
  115. pyvolutionary/tests/test_aquila_optimization.py +27 -0
  116. pyvolutionary/tests/test_bacterial_foraging_optimization.py +37 -0
  117. pyvolutionary/tests/test_bat_optimization.py +34 -0
  118. pyvolutionary/tests/test_bee_colony_optimization.py +32 -0
  119. pyvolutionary/tests/test_camel_caravan_optimization.py +37 -0
  120. pyvolutionary/tests/test_coral_reef_optimization.py +39 -0
  121. pyvolutionary/tests/test_coyotes_optimization.py +32 -0
  122. pyvolutionary/tests/test_earthworms_optimization.py +37 -0
  123. pyvolutionary/tests/test_electromagnetic_field_optimization.py +35 -0
  124. pyvolutionary/tests/test_elephant_herd_optimization.py +34 -0
  125. pyvolutionary/tests/test_firefly_swarm_optimization.py +34 -0
  126. pyvolutionary/tests/test_fireworks_algorithm_optimization.py +36 -0
  127. pyvolutionary/tests/test_fish_school_search_optimization.py +37 -0
  128. pyvolutionary/tests/test_flower_pollination_algorithm_optimization.py +33 -0
  129. pyvolutionary/tests/test_forest_algorithm_optimization.py +36 -0
  130. pyvolutionary/tests/test_foxes_optimization.py +33 -0
  131. pyvolutionary/tests/test_genetic_algorithm_optimization.py +32 -0
  132. pyvolutionary/tests/test_grasshopper_optimization_algorithm.py +33 -0
  133. pyvolutionary/tests/test_grey_wolf_optimization.py +27 -0
  134. pyvolutionary/tests/test_harmony_search_optimization.py +33 -0
  135. pyvolutionary/tests/test_imperialist_competitive_optimization.py +40 -0
  136. pyvolutionary/tests/test_invasive_weed_optimization.py +34 -0
  137. pyvolutionary/tests/test_krill_herd_optimization.py +40 -0
  138. pyvolutionary/tests/test_levy_flight_jaya_swarm_optimization.py +33 -0
  139. pyvolutionary/tests/test_monarch_butterfly_optimization.py +33 -0
  140. pyvolutionary/tests/test_mountain_gazelle_optimization.py +27 -0
  141. pyvolutionary/tests/test_osprey_optimization.py +26 -0
  142. pyvolutionary/tests/test_particle_swarm_optimization.py +34 -0
  143. pyvolutionary/tests/test_pathfinder_algorithm_optimization.py +27 -0
  144. pyvolutionary/tests/test_pelican_optimization.py +27 -0
  145. pyvolutionary/tests/test_seagull_optimization.py +32 -0
  146. pyvolutionary/tests/test_siberian_tiger_optimization.py +27 -0
  147. pyvolutionary/tests/test_task_bounds.py +9 -0
  148. pyvolutionary/tests/test_tasmanian_devil_optimization.py +27 -0
  149. pyvolutionary/tests/test_utils.py +43 -0
  150. pyvolutionary/tests/test_virus_colony_search_optimization.py +33 -0
  151. pyvolutionary/tests/test_walrus_optimization.py +27 -0
  152. pyvolutionary/tests/test_whales_optimization.py +27 -0
  153. pyvolutionary/tests/test_wildebeest_herd_optimization.py +41 -0
  154. pyvolutionary/tests/test_zebra_optimization.py +27 -0
  155. pyvolutionary/utils.py +94 -0
  156. pyvolutionary/virus_colony_search/__init__.py +2 -0
  157. pyvolutionary/virus_colony_search/models.py +29 -0
  158. pyvolutionary/virus_colony_search/virus_colony_search_optimization.py +99 -0
  159. pyvolutionary/walrus/__init__.py +2 -0
  160. pyvolutionary/walrus/models.py +9 -0
  161. pyvolutionary/walrus/walrus_optimization.py +43 -0
  162. pyvolutionary/whales/__init__.py +2 -0
  163. pyvolutionary/whales/models.py +12 -0
  164. pyvolutionary/whales/whales_optimization.py +56 -0
  165. pyvolutionary/wildebeest_herd/__init__.py +2 -0
  166. pyvolutionary/wildebeest_herd/models.py +94 -0
  167. pyvolutionary/wildebeest_herd/wildebeest_herd_optimization.py +143 -0
  168. pyvolutionary/zebra/__init__.py +2 -0
  169. pyvolutionary/zebra/models.py +9 -0
  170. pyvolutionary/zebra/zebra_optimization.py +53 -0
  171. pyvolutionary-1.0.1.dist-info/METADATA +350 -0
  172. pyvolutionary-1.0.1.dist-info/RECORD +173 -0
  173. pyvolutionary-1.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,96 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ best_agent,
5
+ parse_obj_doc, # type: ignore
6
+ )
7
+ from ..abstract import OptimizationAbstract
8
+ from .models import Camel, CamelCaravanOptimizationConfig
9
+
10
+
11
+ class CamelCaravanOptimization(OptimizationAbstract):
12
+ """
13
+ Implementation of the Camels Optimization algorithm.
14
+
15
+ Args:
16
+ config (CamelCaravanOptimizationConfig): an instance of CamelCaravanOptimizationConfig class.
17
+ {parse_obj_doc(CamelCaravanOptimizationConfig)}
18
+
19
+ Bibliography
20
+ ----------
21
+ [1] Ali, Ramzy. (2016). Novel Optimization Algorithm Inspired by Camel Traveling Behavior.
22
+ Iraq J. Electrical and Electronic Engineering. 12. 167-177.
23
+ """
24
+ def __init__(self, config: CamelCaravanOptimizationConfig, debug: bool | None = False):
25
+ super().__init__(config, debug)
26
+
27
+ def _init_agent(
28
+ self,
29
+ position: list[float] | np.ndarray | None = None,
30
+ endurance: float | None = None,
31
+ supply: float | None = None,
32
+ ) -> Camel:
33
+ agent = super()._init_agent(position=position)
34
+
35
+ return Camel(
36
+ **agent.model_dump(),
37
+ endurance=endurance if endurance is not None else self._config.endurance,
38
+ supply=supply if supply is not None else self._config.supply,
39
+ temperature=np.random.uniform(*self._config.temperatures),
40
+ )
41
+
42
+ def __walk__(self, camel: Camel) -> Camel:
43
+ """
44
+ Move the camel in search space. This function is used to move Camel.
45
+ :param camel: the camel to consider.
46
+ :return: the moved camel.
47
+ :rtype: Camel
48
+ """
49
+ bc_position = np.array(best_agent(self._population).position)
50
+
51
+ min_temperature, max_temperature = self._config.temperatures
52
+
53
+ temperature = np.random.uniform(min_temperature, max_temperature)
54
+ supply = camel.supply * (1 - self._config.burden_factor * camel.steps / self._config.max_cycles)
55
+ endurance = camel.endurance * (1 - temperature / max_temperature) * (1 - camel.steps / self._config.max_cycles)
56
+
57
+ new_position = camel.position + np.random.uniform(-1, 1) * (
58
+ 1 - (endurance / self._config.endurance)
59
+ ) * np.exp(1 - supply / self._config.supply) * (bc_position - np.array(camel.position))
60
+ if self._is_valid_position(new_position):
61
+ return self._init_agent(position=new_position, endurance=endurance, supply=supply)
62
+
63
+ return self._init_agent(position=camel.position, endurance=endurance, supply=supply)
64
+
65
+ def __oasis__(self, camel: Camel, past_cost: float) -> Camel:
66
+ """
67
+ Apply oasis function to Camel. This function is used to refill supply and endurance of Camel.
68
+ :param camel: the camel to consider.
69
+ :param past_cost: the past cost of Camel.
70
+ :return: the camel after oasis function.
71
+ """
72
+ if np.random.random() <= (1 - self._config.visibility) and camel.cost <= past_cost:
73
+ camel.supply = self._config.supply
74
+ camel.endurance = self._config.endurance
75
+ return camel
76
+
77
+ def __life_cycle__(self, camel: Camel, past_cost: float) -> Camel:
78
+ """
79
+ Apply life cycle to Camel. This function is used to kill Camel.
80
+ :param camel: the camel to consider.
81
+ :param past_cost: the past cost of Camel.
82
+ :return: the camel after life cycle.
83
+ """
84
+ if past_cost <= self._config.death_rate * camel.cost:
85
+ return self._init_agent(endurance=self._config.endurance, supply=self._config.supply)
86
+
87
+ camel.steps += 1
88
+ return camel
89
+
90
+ def optimization_step(self):
91
+ for idx in range(0, self._config.population_size):
92
+ camel = self._population[idx].model_copy()
93
+ past_cost = camel.cost
94
+
95
+ camel = self.__life_cycle__(self.__walk__(camel), past_cost)
96
+ self._population[idx] = self.__oasis__(camel, past_cost)
@@ -0,0 +1,61 @@
1
+ import numpy as np
2
+ from pydantic import field_validator
3
+
4
+ from ..models import Agent, BaseOptimizationConfig
5
+
6
+
7
+ class Camel(Agent):
8
+ endurance: float
9
+ supply: float
10
+ temperature: float | None = None
11
+ steps: int = 1
12
+
13
+
14
+ class CamelCaravanOptimizationConfig(BaseOptimizationConfig):
15
+ """
16
+ Configuration class of the Camel Caravan algorithm.
17
+ burden_factor (float): [0, 1], burden factor.\n
18
+ death_rate (float): [0, 1], dying rate.\n
19
+ visibility (float): view range of camel.\n
20
+ supply (float): (0, Inf), initial supply.\n
21
+ endurance (float): (0, Inf), initial endurance.\n
22
+ temperatures (list[float]): minimum and maximum temperatures.\n
23
+ """
24
+ burden_factor: float
25
+ death_rate: float
26
+ visibility: float
27
+ supply: float
28
+ endurance: float
29
+ temperatures: list[float]
30
+
31
+ @field_validator('burden_factor')
32
+ def check_burden_factor(cls, v):
33
+ if not 0 <= v <= 1:
34
+ raise ValueError('burden_factor must be in range [0, 1]')
35
+ return v
36
+
37
+ @field_validator('death_rate')
38
+ def check_death_rate(cls, v):
39
+ if not 0 <= v <= 1:
40
+ raise ValueError('death_rate must be in range [0, 1]')
41
+ return v
42
+
43
+ @field_validator('supply')
44
+ def check_supply(cls, v):
45
+ if not 0 < v:
46
+ raise ValueError('supply must be greater than 0')
47
+ return v
48
+
49
+ @field_validator('endurance')
50
+ def check_endurance(cls, v):
51
+ if not 0 < v:
52
+ raise ValueError('endurance must be greater than 0')
53
+ return v
54
+
55
+ @field_validator('temperatures')
56
+ def check_temperatures(cls, v):
57
+ if not len(v) == 2:
58
+ raise ValueError('temperatures must be a list of length 2')
59
+ if not v[0] < v[1]:
60
+ raise ValueError('temperatures[0] must be less than temperatures[1]')
61
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Coral, CoralReefOptimizationConfig
2
+ from .coral_reef_optimization import CoralReefOptimization
@@ -0,0 +1,133 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ sort_and_trim,
5
+ parse_obj_doc # type: ignore
6
+ )
7
+ from ..abstract import OptimizationAbstract
8
+ from .models import Coral, CoralReefOptimizationConfig
9
+
10
+
11
+ class CoralReefOptimization(OptimizationAbstract):
12
+ """
13
+ Implementation of the Coral Reef Optimization algorithm.
14
+
15
+ Args:
16
+ config (CoralReefOptimizationConfig): an instance of CoralReefOptimizationConfig class.
17
+ {parse_obj_doc(CoralReefOptimizationConfig)}
18
+
19
+ Bibliography
20
+ ----------
21
+ [1] Salcedo-Sanz, S., Del Ser, J., Landa-Torres, I., Gil-López, S. and Portilla-Figueras, J.A., 2014.
22
+ The coral reefs optimization algorithm: a novel metaheuristic for efficiently solving optimization problems.
23
+ The Scientific World Journal, 2014.
24
+ """
25
+ def __init__(self, config: CoralReefOptimizationConfig, debug: bool | None = False):
26
+ super().__init__(config, debug)
27
+ _, self.__G1 = self._config.gamma
28
+ self.__alpha = 10 * self._config.Pd / self._config.max_cycles
29
+ self.__gamma = 10 * (self._config.gamma[1] - self._config.gamma[0]) / self._config.max_cycles
30
+ self.__num_occupied = int(self._config.population_size / (1 + self._config.po))
31
+ self.__dyn_Pd = 0
32
+ self.__occupied_list = np.zeros(self._config.population_size, dtype=int)
33
+ self.__occupied_idx_list = np.random.choice(
34
+ list(range(self._config.population_size)), self.__num_occupied, replace=False
35
+ )
36
+ self.__occupied_list[self.__occupied_idx_list] = 1
37
+ self.__reset_count = 0
38
+
39
+ def __broadcast_spawning_brooding__(self) -> list[Coral]:
40
+ """
41
+ Broadcast spawning and brooding process. This process is done in two steps: 1a and 1b. In step 1a, the corals
42
+ that are going to broadcast spawning are selected. In step 1b, the corals that are going to brooding are
43
+ selected. The corals that are going to broadcast spawning are selected randomly from the occupied corals. The
44
+ corals that are going to brooding are selected randomly from the occupied corals that are not selected for
45
+ broadcast spawning.
46
+ :return: the list of larvae.
47
+ :rtype: list[Coral]
48
+ """
49
+ def gaussian_mutation(position):
50
+ return self._correct_position(np.where(
51
+ np.random.random(self._task.space_dimension) < self._config.GCR,
52
+ position + self.__G1 * self._bandwidth() * np.random.normal(0, 1, self._task.space_dimension),
53
+ position
54
+ ))
55
+
56
+ def multi_point_cross(pos1, pos2):
57
+ p1, p2 = np.random.choice(list(range(0, len(pos1))), 2, replace=False)
58
+ start, end = min(p1, p2), max(p1, p2)
59
+ return self._correct_position(
60
+ np.concatenate((pos1[:start], pos2[start:end], pos1[end:]), axis=0)
61
+ )
62
+
63
+ # Step 1a
64
+ selected_corals = np.random.choice(
65
+ self.__occupied_idx_list, int(len(self.__occupied_idx_list) * self._config.Fb), replace=False
66
+ )
67
+ larvae = [Coral(**self._init_agent(gaussian_mutation(self._population[idx].position)).model_dump())
68
+ for idx in self.__occupied_idx_list if idx not in selected_corals]
69
+
70
+ # Step 1b
71
+ while len(selected_corals) >= 2:
72
+ id1, id2 = np.random.choice(range(len(selected_corals)), 2, replace=False)
73
+ agent = Coral(**self._init_agent(multi_point_cross(
74
+ self._population[selected_corals[id1]].position,
75
+ self._population[selected_corals[id2]].position
76
+ )).model_dump())
77
+ larvae.append(agent)
78
+ selected_corals = np.delete(selected_corals, [id1, id2])
79
+ return larvae
80
+
81
+ def __larvae_setting__(self, larvae: list[Coral]):
82
+ """
83
+ Larvae setting process. This process is done in two steps: 2a and 2b. In step 2a, the larvae are evaluated and
84
+ the larvae that are going to be settled are selected. In step 2b, the larvae that are going to be settled are
85
+ placed in the occupied corals.
86
+ :param larvae: the list of larvae.
87
+ """
88
+ def find_available_slot(lc: Coral) -> tuple[int | None, bool]:
89
+ for i in range(self._config.n_trials):
90
+ p = np.random.randint(0, self._config.population_size - 1)
91
+ if self.__occupied_list[p] == 0:
92
+ return p, True
93
+ if lc.cost < self._population[p].cost:
94
+ return p, False
95
+ return None, False
96
+
97
+ def update(p: int, upd: bool, lc: Coral):
98
+ self._population[p] = lc # update population
99
+ if upd:
100
+ self.__occupied_idx_list = np.append(self.__occupied_idx_list, p) # update occupied id
101
+ self.__occupied_list[p] = 1 # update occupied list
102
+
103
+ # trial to land on a square of reefs
104
+ res = list(map(find_available_slot, larvae))
105
+ for idx, (pdx, upd_occupied) in enumerate(res):
106
+ if pdx is not None:
107
+ update(pdx, upd_occupied, larvae[idx])
108
+
109
+ def __reef_cost__(self, idx):
110
+ return self._population[idx].cost
111
+
112
+ def optimization_step(self):
113
+ # broadcast spawning brooding
114
+ larvae = self.__broadcast_spawning_brooding__()
115
+ self.__larvae_setting__(larvae)
116
+
117
+ # asexual reproduction
118
+ pop_best = sort_and_trim(
119
+ [self._population[idx] for idx in self.__occupied_idx_list],
120
+ int(len(self.__occupied_idx_list) * self._config.Fa)
121
+ )
122
+ self.__larvae_setting__(pop_best)
123
+
124
+ # depredation
125
+ if np.random.random() < self.__dyn_Pd:
126
+ num_depredation = int(len(self.__occupied_idx_list) * self._config.Fd)
127
+ idx_list_sorted = sorted(self.__occupied_idx_list, key=self.__reef_cost__)
128
+ for idx in idx_list_sorted[-num_depredation:]:
129
+ self.__occupied_list[idx] = 0
130
+ if self.__dyn_Pd <= self._config.Pd:
131
+ self.__dyn_Pd += self.__alpha
132
+ if self.__G1 >= self._config.gamma[0]:
133
+ self.__G1 -= self.__gamma
@@ -0,0 +1,81 @@
1
+ from pydantic import field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Coral(Agent):
7
+ pass
8
+
9
+
10
+ class CoralReefOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Coral Reef algorithm.
13
+ po (float): [0.2, 0.5], the rate between free/occupied at the beginning.\n
14
+ Fb (float): [0.6, 0.9], BroadcastSpawner/ExistingCorals rate.\n
15
+ Fa (float): [0.05, 0.3], fraction of corals duplicates its self and tries to settle in a different part of the
16
+ reef.\n
17
+ Fd (float): [0.05, 0.5], fraction of the worse health corals in reef will be applied depredation.\n
18
+ Pd (float): [0.1, 0.7], Probability of depredation.\n
19
+ GCR (float): [0.05, 0.2], probability for mutation process.\n
20
+ gamma (list[float]): [0.01, 0.1], [0.1, 0.5], min and max factors for mutation process.\n
21
+ n_trials (int): [2, 10], number of attempts for a larvae to set in the reef.
22
+ """
23
+ po: float
24
+ Fb: float
25
+ Fa: float
26
+ Fd: float
27
+ Pd: float
28
+ GCR: float
29
+ gamma: list[float]
30
+ n_trials: int
31
+
32
+ @field_validator('po')
33
+ def check_po(cls, v):
34
+ if not 0.2 <= v <= 0.5:
35
+ raise ValueError('po must be in range [0.2, 0.5]')
36
+ return v
37
+
38
+ @field_validator('Fb')
39
+ def check_Fb(cls, v):
40
+ if not 0.6 <= v <= 0.9:
41
+ raise ValueError('Fb must be in range [0.6, 0.9]')
42
+ return v
43
+
44
+ @field_validator('Fa')
45
+ def check_Fa(cls, v):
46
+ if not 0.05 <= v <= 0.3:
47
+ raise ValueError('Fa must be in range [0.05, 0.3]')
48
+ return v
49
+
50
+ @field_validator('Fd')
51
+ def check_Fd(cls, v):
52
+ if not 0.05 <= v <= 0.5:
53
+ raise ValueError('Fd must be in range [0.05, 0.5]')
54
+ return v
55
+
56
+ @field_validator('Pd')
57
+ def check_Pd(cls, v):
58
+ if not 0.1 <= v <= 0.7:
59
+ raise ValueError('Pd must be in range [0.1, 0.7]')
60
+ return v
61
+
62
+ @field_validator('GCR')
63
+ def check_GCR(cls, v):
64
+ if not 0.05 <= v <= 0.2:
65
+ raise ValueError('GCR must be in range [0.05, 0.2]')
66
+ return v
67
+
68
+ @field_validator('gamma')
69
+ def check_gamma(cls, v):
70
+ gamma_min, gamma_max = v
71
+ if not 0.01 <= gamma_min <= 0.1:
72
+ raise ValueError('gamma_min must be in range [0.01, 0.1]')
73
+ if not 0.1 <= gamma_max <= 0.5:
74
+ raise ValueError('gamma_max must be in range [0.1, 0.5]')
75
+ return v
76
+
77
+ @field_validator('n_trials')
78
+ def check_n_trials(cls, v):
79
+ if not 2 <= v <= 10:
80
+ raise ValueError('n_trials must be in range [2, 10]')
81
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Coyote, CoyotesOptimizationConfig
2
+ from .coyotes_optimization import CoyotesOptimization
@@ -0,0 +1,96 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ generate_group_population,
5
+ sort_by_cost,
6
+ parse_obj_doc, # type: ignore
7
+ )
8
+ from ..abstract import OptimizationAbstract
9
+ from .models import Coyote, CoyotesOptimizationConfig
10
+
11
+
12
+ class CoyotesOptimization(OptimizationAbstract):
13
+ """
14
+ Implementation of the Coyotes Optimization algorithm.
15
+
16
+ Args:
17
+ config (CoyotesOptimizationConfig): an instance of CoyotesOptimizationConfig class.
18
+ {parse_obj_doc(CoyotesOptimizationConfig)}
19
+
20
+ Bibliography
21
+ ----------
22
+ [1] Pierezan, J. and Coelho, L.D.S., 2018, July. Coyote optimization algorithm: a new metaheuristic
23
+ for global optimization problems. In 2018 IEEE congress on evolutionary computation (CEC) (pp. 1-8). IEEE.
24
+ """
25
+ def __init__(self, config: CoyotesOptimizationConfig, debug: bool | None = False):
26
+ super().__init__(config, debug)
27
+ self.__n_packs: int = 0
28
+ self.__ps: float = 0.
29
+ self.__p_leave: float = 0.
30
+ self.__packs: list[list[Coyote]] = []
31
+
32
+ def before_initialization(self):
33
+ self.__n_packs = int(self._config.population_size / self._config.num_coyotes)
34
+ self.__ps = 1. / self._task.space_dimension # Probability of selecting a dimension
35
+ self.__p_leave = 0.005 * (self._config.num_coyotes ** 2) # Probability of leaving a pack
36
+
37
+ def _init_agent(self, position: list[float] | np.ndarray | None = None, age: int | None = None) -> Coyote:
38
+ agent = super()._init_agent(position)
39
+ return Coyote(**agent.model_dump(), age=age if age is not None else 0)
40
+
41
+ def optimization_step(self):
42
+ self.__packs = generate_group_population(self._population, self.__n_packs, self._config.num_coyotes)
43
+
44
+ for p in range(self.__n_packs):
45
+ pack = self.__packs[p]
46
+
47
+ # get the coyotes that belong to each pack and compute the social tendency of the pack (Eq. 6)
48
+ sort_by_cost(pack)
49
+ tendency = np.mean(np.array([coyote.position for coyote in pack]), axis=0)
50
+
51
+ # update social condition of coyotes (Eq. 8)
52
+ for i, coyote in enumerate(pack):
53
+ rc1, rc2 = np.random.choice(list(set(range(0, self._config.num_coyotes)) - {i}), 2, replace=False)
54
+
55
+ # try to update the social condition according to the alpha and the pack tendency (Eq. 12)
56
+ pos_new = np.array(coyote.position) + np.random.random() * (
57
+ np.array(pack[0].position) - np.array(pack[rc1].position)
58
+ ) + np.random.random() * (tendency - np.array(pack[rc2].position))
59
+
60
+ # keep the coyotes in the search space (optimization problem constraint) and evaluate the new position
61
+ # it means to evaluate the new social condition (Eq. 13) and apply the adaptation (Eq. 14)
62
+ pack[i] = self._greedy_select_agent(
63
+ coyote, self._init_agent(self._correct_position(pos_new), coyote.age)
64
+ )
65
+
66
+ # birth of a new coyote from random parents (Eq. 7 and Alg. 1)
67
+ id_parent1, id_parent2 = np.random.choice(list(range(0, self._config.num_coyotes)), 2, replace=False)
68
+ # generate the coyote considering intrinsic and extrinsic influence, with eventual noise
69
+ pup_pos = np.array(np.where(
70
+ np.random.random(self._task.space_dimension) < (1. - self.__ps) / 2.,
71
+ pack[id_parent1].position,
72
+ pack[id_parent2].position
73
+ )) * np.random.normal(0, 1)
74
+ pup = self._init_agent(self._correct_position(pup_pos.tolist()))
75
+
76
+ # Verify if the pup will survive
77
+ sort_by_cost(pack)
78
+ # find index of element has cost larger than new child: if existing, new child is good
79
+ if pup.cost < pack[-1].cost:
80
+ # replace the worst element by new child, New born child with age = 0
81
+ pack = sorted(pack, key=lambda x: x.age)
82
+ pack[-1] = pup
83
+ self.__packs[p] = [coyote.model_copy() for coyote in pack]
84
+
85
+ # a coyote can leave a pack and enter to another pack (Eq. 4)
86
+ if self.__n_packs > 1 and np.random.random() < self.__p_leave:
87
+ id_pack1, id_pack2 = np.random.choice(list(range(0, self.__n_packs)), 2, replace=False)
88
+ id1, id2 = np.random.choice(list(range(0, self._config.num_coyotes)), 2, replace=False)
89
+ self.__packs[id_pack1][id1], self.__packs[id_pack2][id2] = (
90
+ self.__packs[id_pack2][id2], self.__packs[id_pack1][id1]
91
+ )
92
+
93
+ # Update coyotes ages
94
+ self._population = [
95
+ self._init_agent(position=coyote.position, age=coyote.age + 1) for pack in self.__packs for coyote in pack
96
+ ]
@@ -0,0 +1,22 @@
1
+ from pydantic import model_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Coyote(Agent):
7
+ age: int
8
+
9
+
10
+ class CoyotesOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Coyotes Optimization algorithm.
13
+ num_coyotes (int): [2, int(population_size/2)], the number of coyotes per group.
14
+ """
15
+ num_coyotes: int
16
+
17
+ @model_validator(mode="after")
18
+ def validate_keep(self) -> "CoyotesOptimizationConfig":
19
+ if not 2 <= self.num_coyotes <= int(self.population_size / 2):
20
+ raise ValueError(f"num_coyotes must be greater than 2 and lower than population_size / 2. "
21
+ f"Got {self.num_coyotes}")
22
+ return self
@@ -0,0 +1,2 @@
1
+ from .models import Earthworm, EarthwormsOptimizationConfig
2
+ from .earthworms_optimization import EarthwormsOptimization
@@ -0,0 +1,124 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ best_agents,
5
+ sort_by_cost,
6
+ parse_obj_doc, # type: ignore
7
+ )
8
+ from ..abstract import OptimizationAbstract
9
+ from .models import Earthworm, EarthwormsOptimizationConfig
10
+
11
+
12
+ class EarthwormsOptimization(OptimizationAbstract):
13
+ """
14
+ Implementation of the Earthworms Optimization algorithm.
15
+
16
+ Args:
17
+ config (EarthwormsOptimizationConfig): an instance of EarthwormsOptimizationConfig class.
18
+ {parse_obj_doc(EarthwormsOptimizationConfig)}
19
+
20
+ Bibliography
21
+ ----------
22
+ [1] Wang, Gai-Ge & Deb, Suash & Coelho, Leandro. (2015). Earthworm optimization algorithm: a bio-inspired
23
+ metaheuristic algorithm for global optimization problems. International Journal of Bio-Inspired Computation.
24
+ DOI: 10.1504/IJBIC.2015.10004283.
25
+ """
26
+ def __init__(self, config: EarthwormsOptimizationConfig, debug: bool | None = False):
27
+ super().__init__(config, debug)
28
+ self.__chrome_keep: list[Earthworm] = []
29
+ self.__dyn_beta = self._config.beta
30
+
31
+ def __evolve__(self):
32
+ """
33
+ The earthworm optimization algorithm process. It is composed of two reproduction operators and a mutation
34
+ operator. The reproduction operators are used to generate new individuals, and the mutation operator is used
35
+ to improve the individuals. The reproduction operators are: (1) the first way of reproducing, and (2) the
36
+ second way of reproducing. The mutation operator is: Cauchy's mutation (CM). The reproduction operators and
37
+ the mutation operator are applied to all individuals in the population.
38
+ :return:
39
+ """
40
+ alpha = self._config.alpha
41
+ beta = self.__dyn_beta
42
+
43
+ n_chromes = self._config.population_size
44
+ keep = self._config.keep
45
+ for idx in range(0, self._config.population_size):
46
+ position = np.array(self._population[idx].position)
47
+
48
+ # reproduction 1: the first way of reproducing
49
+ x_t1 = self._sum_bounds() - alpha * np.array(position)
50
+
51
+ # reproduction 2: the second way of reproducing
52
+ x_child = self._population[np.random.randint(0, n_chromes - 1)].position
53
+ if idx >= keep: # select two parents to mate and create two children
54
+ # 80% parents selected from best population
55
+ rng = range(0, int(n_chromes * 0.2)) if np.random.uniform(0, 1) < 0.5 else range(idx, n_chromes)
56
+ idx1, idx2 = np.random.choice(rng, 2, replace=len(rng) < 2)
57
+ r = np.random.rand()
58
+ x_child = (
59
+ r * np.array(self._population[idx2].position) + (1 - r) * np.array(self._population[idx1].position)
60
+ )
61
+
62
+ self._population[idx] = self._init_agent(beta * np.array(x_t1) + (1 - beta) * np.array(x_child))
63
+ self.__dyn_beta *= self._config.gamma
64
+
65
+ def __cauchy_mutation__(self):
66
+ """
67
+ Cauchy's mutation (CM): make sure each individual is legal
68
+ """
69
+ keep = self._config.keep
70
+
71
+ pos_list = np.array([agent.position for agent in self._population])
72
+ x_mean = np.mean(pos_list, axis=0)
73
+ cauchy_w = self._best_agent.position
74
+ for idx in range(keep, self._config.population_size):
75
+ # Cauchy's mutation (CM)
76
+ cauchy_w = np.where(np.random.rand() < self._config.prob_mutate, x_mean, cauchy_w)
77
+ x_t1 = (cauchy_w + self._best_agent.position) / 2
78
+ # make sure each individual is legal
79
+ self._population[idx] = self._init_agent(x_t1)
80
+
81
+ def __clear_duplicates__(self):
82
+ """
83
+ Clear duplicates in the population
84
+ """
85
+ n_chromes = self._config.population_size
86
+ for idx in range(0, n_chromes):
87
+ pos_idx = self._population[idx].position
88
+
89
+ jdx_list = [jdx for jdx, chrome in enumerate(self._population[(idx + 1):], idx + 1) if np.array_equal(
90
+ pos_idx, chrome.position
91
+ )]
92
+ for jdx in jdx_list:
93
+ dimension_to_change = np.random.randint(0, self._task.space_dimension - 1)
94
+ position_jdx = self._population[jdx].position
95
+ position_jdx[dimension_to_change] = self._uniform_coordinates(dimension_to_change)
96
+ self._population[jdx] = self._init_agent(position_jdx)
97
+
98
+ def __elitism__(self, agents: list[Earthworm], keep: int):
99
+ """
100
+ Elitism operator. Replace the worst individuals with the best ones from the previous cycle.
101
+ :param agents: the agents
102
+ :param keep: the number of best individuals to keep
103
+ """
104
+ # sorted from best, i.e., minor cost, to worst
105
+ sort_by_cost(self._population)
106
+ population_size = self._config.population_size
107
+ for idx in range(0, keep):
108
+ self._population[population_size - idx - 1] = agents[idx].model_copy()
109
+
110
+ def optimization_step(self):
111
+ # elitism strategy: save the best "keep" earthworms into the self.__chrome_keep
112
+ self.__chrome_keep = best_agents(self._population, self._config.keep)
113
+
114
+ # the earthworm optimization algorithm process
115
+ self.__evolve__()
116
+
117
+ # Cauchy's mutation (CM): make sure each individual is legal and doesn't have duplicates
118
+ self.__cauchy_mutation__()
119
+
120
+ # elitism strategy: save the best earthworms in a temporary array
121
+ self.__elitism__(self.__chrome_keep, self._config.keep)
122
+
123
+ # clear duplicates in the population
124
+ self.__clear_duplicates__()