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,75 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ get_levy_flight_step,
5
+ parse_obj_doc, # type: ignore
6
+ )
7
+ from ..abstract import OptimizationAbstract
8
+ from .models import AquilaOptimizationConfig
9
+
10
+
11
+ class AquilaOptimization(OptimizationAbstract):
12
+ """
13
+ Implementation of the Aquila Optimization algorithm.
14
+
15
+ Args:
16
+ config (AquilaOptimizationConfig): an instance of AquilaOptimizationConfig class.
17
+ {parse_obj_doc(AquilaOptimizationConfig)}
18
+
19
+ Bibliography
20
+ ----------
21
+ [1] Abualigah, L., Yousri, D., Abd Elaziz, M., Ewees, A.A., Al-Qaness, M.A. and Gandomi, A.H., 2021.
22
+ Aquila optimizer: a novel meta-heuristic optimization algorithm. Computers & Industrial Engineering, 157,
23
+ p.107250.
24
+ """
25
+ def __init__(self, config: AquilaOptimizationConfig, debug: bool | None = False):
26
+ super().__init__(config, debug)
27
+
28
+ def __move__(self, idx: int, pos: list[float]) -> np.ndarray:
29
+ """
30
+ Move the agent to a new position. This method is called by the optimization step. It is not intended to be
31
+ called directly.
32
+ :param idx: the index of the agent in the population.
33
+ :param pos: the current position of the agent.
34
+ :return: the new position of the agent.
35
+ :rtype: np.ndarray
36
+ """
37
+ alpha = delta = 0.1
38
+ g1 = 2 * np.random.random() - 1 # Eq. 16
39
+ g2 = 2 * (1 - self._cycles / self._config.max_cycles) # Eq. 17
40
+ dim_list = np.array(list(range(1, self._task.space_dimension + 1)))
41
+ miu = 0.00565
42
+ r0 = 10
43
+ r = r0 + miu * dim_list
44
+ w = 0.005
45
+ phi0 = 3 * np.pi / 2
46
+ phi = -w * dim_list + phi0
47
+ x = r * np.sin(phi) # Eq.(9)
48
+ y = r * np.cos(phi) # Eq.(10)
49
+ QF = self._cycles ** ((2 * np.random.random() - 1) / (1 - self._config.max_cycles) ** 2) # Eq.(15)
50
+
51
+ best_position = np.array(self._best_agent.position)
52
+ pop_size = self._config.population_size
53
+
54
+ pos = np.array(pos)
55
+
56
+ x_mean = np.mean(np.array([agent.cost for agent in self._population]), axis=0)
57
+ levy_step = get_levy_flight_step(beta=1.5, multiplier=1.0, case=-1)
58
+ if self._cycles <= (2 / 3) * self._config.max_cycles: # Eq. 3, 4
59
+ jdx = np.random.choice(list(set(range(0, pop_size)) - {idx}))
60
+ return best_position * (1 - self._cycles / self._config.max_cycles) + np.random.random() * (
61
+ x_mean - best_position
62
+ ) if np.random.random() < 0.5 else best_position * levy_step + (
63
+ np.array(self._population[jdx].position) + np.random.random() * (y - x)
64
+ ) # Eq. 5
65
+
66
+ return alpha * (best_position - x_mean) - (
67
+ np.random.random() * self._random_position() * delta
68
+ ) if np.random.random() < 0.5 else QF * best_position - (
69
+ g2 * pos * np.random.random()
70
+ ) - g2 * levy_step + np.random.random() * g1 # Eq. 13, 14
71
+
72
+ def optimization_step(self):
73
+ for idx, aquila in enumerate(self._population):
74
+ agent = self._init_agent(self._correct_position(self.__move__(idx, aquila.position)))
75
+ self._population[idx] = self._greedy_select_agent(agent, aquila)
@@ -0,0 +1,9 @@
1
+ from ..models import Agent, BaseOptimizationConfig
2
+
3
+
4
+ class Aquila(Agent):
5
+ pass
6
+
7
+
8
+ class AquilaOptimizationConfig(BaseOptimizationConfig):
9
+ pass
@@ -0,0 +1,2 @@
1
+ from .models import Cell, BacterialForagingOptimizationConfig
2
+ from .bacterial_foraging_optimization import BacterialForagingOptimization
@@ -0,0 +1,152 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import parse_obj_doc # type: ignore
4
+ from ..abstract import OptimizationAbstract
5
+ from .models import Cell, BacterialForagingOptimizationConfig
6
+
7
+
8
+ class BacterialForagingOptimization(OptimizationAbstract):
9
+ """
10
+ Implementation of the Bacterial Foraging Optimization algorithm, in its Adaptive version.
11
+
12
+ Args:
13
+ config (BacterialForagingOptimizationConfig): an instance of BacterialForagingOptimization class.
14
+ {parse_obj_doc(AdaptiveBacterialForagingOptimizationConfig)}
15
+
16
+ Bibliography
17
+ ----------
18
+ [1] Passino, K.M., 2002. Biomimicry of bacterial foraging for distributed optimization and control.
19
+ IEEE control systems magazine, 22(3), pp.52-67.
20
+ [2] Nguyen, T., Nguyen, B.M. and Nguyen, G., 2019, April. Building resource auto-scaler with functional-link
21
+ neural network and adaptive bacterial foraging optimization. In International Conference on
22
+ Theory and Applications of Models of Computation (pp. 501-517). Springer, Cham.
23
+ """
24
+ def __init__(self, config: BacterialForagingOptimizationConfig, debug: bool | None = False):
25
+ super().__init__(config, debug)
26
+ self.__C_s: np.ndarray | None = None
27
+ self.__C_e: np.ndarray | None = None
28
+
29
+ def before_initialization(self):
30
+ self.__C_s = self._config.C_s * self._bandwidth()
31
+ self.__C_e = self._config.C_e * self._bandwidth()
32
+
33
+ def _init_agent(self, position: list[float] | np.ndarray | None = None) -> Cell:
34
+ agent = super()._init_agent(position)
35
+ return Cell(**agent.model_dump(), local_best=agent.position.copy(), local_cost=agent.cost)
36
+
37
+ def __update_step_size__(self, cell: Cell) -> float:
38
+ """
39
+ Update the step size of the cell. The step size is computed as follows:\n
40
+ s / cell.nutrients if cell.nutrients > 0 else s, where:\n
41
+ s = C_s - (C_s - C_e) * cell.cost / total_costs\n
42
+ :param cell: the cell to update the step size.
43
+ :return: the step size.
44
+ :rtype: float
45
+ """
46
+ total_costs = np.sum([agent.cost for agent in self._population])
47
+ step_size = self.__C_s - (self.__C_s - self.__C_e) * cell.cost / total_costs
48
+ return step_size / cell.nutrients if cell.nutrients > 0 else step_size
49
+
50
+ def __tumble_cell__(self, cell: Cell, step_size: float) -> Cell:
51
+ """
52
+ Tumble the cell. The new position is computed as follows:\n
53
+ new_position = cell.position + step_size * delta_i / delta, where:\n
54
+ delta_i = (best_agent.position - cell.position) + (cell.local_best - cell.position)\n
55
+ delta = sqrt(delta_i * delta_i.T)\n
56
+ :param cell: the cell to tumble.
57
+ :param step_size: the step size.
58
+ :return: the new cell.
59
+ :rtype: Cell
60
+ """
61
+ position = np.array(cell.position)
62
+ delta_i = (np.array(self._best_agent.position) - position) + (np.array(cell.local_best) - position)
63
+ delta = np.sqrt(np.dot(delta_i, delta_i.T))
64
+ if delta == 0:
65
+ delta_i = np.random.uniform(-1.0, 1.0, self._task.space_dimension)
66
+ delta = np.sqrt(np.dot(delta_i, delta_i.T))
67
+
68
+ return self._init_agent(self._correct_position(cell.position + step_size * delta_i / delta))
69
+
70
+ def __swim__(self, cell: Cell, step_size: float) -> Cell:
71
+ """
72
+ Swim the cell. The cell will swim to a new cell or tumble some every time interval.
73
+ :param cell: the cell to swim.
74
+ :param step_size: the step size.
75
+ :return: the cell after swimming.
76
+ :rtype: Cell
77
+ """
78
+ for m in range(self._config.Ns):
79
+ # move the bacterium to the location of the new cell and evaluate the moved position
80
+ new_cell = self.__tumble_cell__(cell, step_size)
81
+ if new_cell.cost >= cell.cost:
82
+ cell.nutrients -= 1
83
+ continue
84
+
85
+ new_cell.nutrients += 1
86
+ cell = new_cell
87
+ # update personal best
88
+ if new_cell.cost < cell.local_cost:
89
+ cell.local_best = new_cell.position.copy()
90
+ cell.local_cost = new_cell.cost
91
+
92
+ return cell
93
+
94
+ def __clean_population__(self):
95
+ """
96
+ Remove duplicates from the population. This method is called after each optimization step.
97
+ """
98
+ new_set = set()
99
+ for idx, obj in enumerate(self._population):
100
+ pos = tuple(obj.position)
101
+ if pos in new_set:
102
+ self._population.pop(idx)
103
+ else:
104
+ new_set.add(pos)
105
+
106
+ def __balance_population__(self):
107
+ """
108
+ Balance the population by adding more agents or remove some agents. This method is called after each
109
+ optimization step. The population size is fixed to the value of the configuration. If the population size
110
+ is greater than the value of the configuration, then some agents are removed. Otherwise, new agents are added.
111
+ """
112
+ n_agents = len(self._population) - self._config.population_size
113
+ if n_agents == 0:
114
+ return
115
+
116
+ if n_agents < 0:
117
+ self._population += [self._init_agent() for _ in range(0, -n_agents)]
118
+ else:
119
+ list_idx_removed = np.random.choice(range(0, len(self._population)), n_agents, replace=False)
120
+ for idx in sorted(list_idx_removed, reverse=True):
121
+ self._population.pop(idx)
122
+
123
+ def optimization_step(self):
124
+ for idx, cell in enumerate(self._population):
125
+ step_size = self.__update_step_size__(cell)
126
+ cell = self.__swim__(cell, step_size)
127
+
128
+ m = max(self._config.N_split, self._config.N_split + (
129
+ len(self._population) - self._config.population_size
130
+ ) / self._config.N_adapt)
131
+
132
+ pos = np.array(cell.position)
133
+
134
+ if cell.nutrients > m:
135
+ tt = np.random.normal(0, 1, self._task.space_dimension)
136
+ agent = self._init_agent(
137
+ self._correct_position(tt * pos + (1 - tt) * (np.array(self._best_agent.position) - pos))
138
+ )
139
+ self._population.append(agent)
140
+ nut_min = min(self._config.N_adapt, self._config.N_adapt + (
141
+ len(self._population) - self._config.population_size
142
+ ) / self._config.N_adapt)
143
+
144
+ self._population[idx] = cell
145
+ if cell.nutrients < nut_min and np.random.random() < self._config.Ped:
146
+ self._population[idx] = self._init_agent()
147
+
148
+ # make sure the population does not have duplicates
149
+ self.__clean_population__()
150
+
151
+ # balance the population by adding more agents or remove some agents
152
+ self.__balance_population__()
@@ -0,0 +1,63 @@
1
+ from pydantic import field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Cell(Agent):
7
+ local_cost: float
8
+ local_best: list[float]
9
+ nutrients: float = 0.0
10
+
11
+
12
+ class BacterialForagingOptimizationConfig(BaseOptimizationConfig):
13
+ """
14
+ Configuration class of the Adaptive Bacterial Foraging Optimization Algorithm.
15
+ C_s (float): (0, 2.0), step size start.\n
16
+ C_e (float): (0, 1.0), step size end.\n
17
+ Ped (float): (0, 1.0), probability eliminate.\n
18
+ Ns (int): [2, 100], swim_length.\n
19
+ N_adapt (int): [0, 4], dead threshold value.\n
20
+ N_split (int): [5, 50], split threshold value.
21
+ """
22
+ C_s: float
23
+ C_e: float
24
+ Ped: float
25
+ Ns: int
26
+ N_adapt: int
27
+ N_split: int
28
+
29
+ @field_validator("C_s")
30
+ def check_C_s(cls, v):
31
+ if not (0 < v < 2.0):
32
+ raise ValueError("C_s must be in (0, 2.0)")
33
+ return v
34
+
35
+ @field_validator("C_e")
36
+ def check_C_e(cls, v):
37
+ if not (0 < v < 1.0):
38
+ raise ValueError("C_e must be in (0, 1.0)")
39
+ return v
40
+
41
+ @field_validator("Ped")
42
+ def check_Ped(cls, v):
43
+ if not (0 < v < 1.0):
44
+ raise ValueError("Ped must be in (0, 1.0)")
45
+ return v
46
+
47
+ @field_validator("Ns")
48
+ def check_Ns(cls, v):
49
+ if not (2 <= v <= 100):
50
+ raise ValueError("Ns must be in [2, 100]")
51
+ return v
52
+
53
+ @field_validator("N_adapt")
54
+ def check_N_adapt(cls, v):
55
+ if not (0 <= v <= 4):
56
+ raise ValueError("N_adapt must be in [0, 4]")
57
+ return v
58
+
59
+ @field_validator("N_split")
60
+ def check_N_split(cls, v):
61
+ if not (5 <= v <= 50):
62
+ raise ValueError("N_split must be in [5, 50]")
63
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Bat, BatOptimizationConfig
2
+ from .bat_optimization import BatOptimization
@@ -0,0 +1,66 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import parse_obj_doc # type: ignore
4
+ from ..abstract import OptimizationAbstract
5
+ from .models import Bat, BatOptimizationConfig
6
+
7
+
8
+ class BatOptimization(OptimizationAbstract):
9
+ """
10
+ Implementation of the Bat Optimization algorithm.
11
+
12
+ Args:
13
+ config (BatOptimizationConfig): an instance of BatOptimizationConfig class.
14
+ {parse_obj_doc(BatsOptimizationConfig)}
15
+
16
+ Bibliography
17
+ ----------
18
+ [1] Yang, Xin-She. "A new metaheuristic bat-inspired algorithm." Nature inspired cooperative strategies for
19
+ optimization" (NICSO 2010). Springer, Berlin, Heidelberg, 2010. 65-74.
20
+ """
21
+
22
+ def __init__(self, config: BatOptimizationConfig, debug: bool | None = False):
23
+ super().__init__(config, debug)
24
+
25
+ def _init_agent(
26
+ self,
27
+ position: list[float] | np.ndarray | None = None,
28
+ velocity: list[float] | np.ndarray | None = None,
29
+ loudness: float | None = None,
30
+ pulse_rate: float | None = None,
31
+ ) -> Bat:
32
+ agent = super()._init_agent(position)
33
+ velocity = self._correct_position(velocity if velocity is not None else self._uniform_position().tolist())
34
+
35
+ return Bat(
36
+ **agent.model_dump(),
37
+ velocity=velocity,
38
+ loudness=loudness if loudness is not None else np.random.uniform(*self._config.loudness),
39
+ pulse_rate=pulse_rate if pulse_rate is not None else np.random.uniform(*self._config.pulse_rate),
40
+ )
41
+
42
+ def _greedy_select_agent(self, agent: Bat, new_agent: Bat) -> Bat:
43
+ """
44
+ Perform the greedy selection between the current agent and the new one. The greedy selection is performed by
45
+ comparing the costs of each agent. The one with the lowest cost is kept.
46
+ :param agent: the current agent
47
+ :param new_agent: the new agent
48
+ :return: the best agent
49
+ :rtype: Bat
50
+ """
51
+ return new_agent if new_agent.cost < agent.cost and np.random.random() < agent.loudness else agent
52
+
53
+ def optimization_step(self):
54
+ mean_a = np.mean([bat.loudness for bat in self._population])
55
+
56
+ pf_min, pf_max = self._config.pulse_frequency
57
+ best_position = np.array(self._best_agent.position)
58
+ for idx, bat in enumerate(self._population):
59
+ velocity = bat.velocity + np.random.uniform(pf_min, pf_max) * (np.array(bat.position) - best_position)
60
+
61
+ # Local Search around g_best position
62
+ position = best_position + mean_a * np.random.normal(-1, 1) \
63
+ if np.random.random() > bat.pulse_rate else bat.position + velocity
64
+ self._population[idx] = self._greedy_select_agent(
65
+ bat, self._init_agent(position, velocity, bat.loudness, bat.pulse_rate)
66
+ )
@@ -0,0 +1,66 @@
1
+ from pydantic import field_validator, conlist
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Bat(Agent):
7
+ velocity: list[float]
8
+ loudness: float
9
+ pulse_rate: float
10
+
11
+
12
+ class BatOptimizationConfig(BaseOptimizationConfig):
13
+ """
14
+ Configuration class for Bat Optimization algorithm.
15
+ loudness (list[float]): [0.5, 1.5], [1.5, 3.0], loudness range.\n
16
+ pulse_rate (list[float]): (0, 1.0), (0, 1.0), pulse rate range.\n
17
+ pulse_frequency (list[float]): [-10, 0], [0, 10], pulse frequency range (both cannot be 0).\n
18
+ alpha (float): [0, 1], loudness update parameter.\n
19
+ gamma (float): [0, 1], pulse rate update parameter.
20
+ """
21
+ loudness: conlist(float, min_length=2, max_length=2)
22
+ pulse_rate: conlist(float, min_length=2, max_length=2)
23
+ pulse_frequency: conlist(float, min_length=2, max_length=2)
24
+ alpha: float = 0.9
25
+ gamma: float = 0.9
26
+
27
+ @field_validator("loudness")
28
+ def correct_loudness(cls, v):
29
+ loudness_min, loudness_max = v
30
+ if not 0.5 <= loudness_min <= 1.5:
31
+ raise ValueError(f"\"loudness[0]\" must be a float in [0.5, 1.5]. Got {loudness_min}")
32
+ if not 1.5 <= loudness_max <= 3.0:
33
+ raise ValueError(f"\"loudness[1]\" must be a float in [1.5, 3.0]. Got {loudness_max}")
34
+ return v
35
+
36
+ @field_validator("pulse_rate")
37
+ def correct_pulse_rate(cls, v):
38
+ pulse_rate_min, pulse_rate_max = v
39
+ if not 0 < pulse_rate_min < 1:
40
+ raise ValueError(f"\"pulse_rate[0]\" must be a float in (0, 1.0). Got {pulse_rate_min}")
41
+ if not 0 < pulse_rate_max < 1:
42
+ raise ValueError(f"\"pulse_rate[1]\" must be a float in (0, 1.0). Got {pulse_rate_max}")
43
+ return v
44
+
45
+ @field_validator("pulse_frequency")
46
+ def correct_pulse_frequency(cls, v):
47
+ pulse_frequency_min, pulse_frequency_max = v
48
+ if not -10 <= pulse_frequency_min <= 0:
49
+ raise ValueError(f"\"pulse_frequency[0]\" must be a float in [-10, 0]. Got {pulse_frequency_min}")
50
+ if not 0 <= pulse_frequency_max <= 10:
51
+ raise ValueError(f"\"pulse_frequency[1]\" must be a float in [0, 10]. Got {pulse_frequency_max}")
52
+ if pulse_frequency_max == 0 or pulse_frequency_max == 0:
53
+ raise ValueError(f"\"pulse_frequency\" cannot have both 0 values. Got {v}")
54
+ return v
55
+
56
+ @field_validator("alpha")
57
+ def correct_alpha(cls, v):
58
+ if not 0 <= v <= 1:
59
+ raise ValueError(f"\"alpha\" must be a float in [0, 1]. Got {v}")
60
+ return v
61
+
62
+ @field_validator("gamma")
63
+ def correct_gamma(cls, v):
64
+ if not 0 <= v <= 1:
65
+ raise ValueError(f"\"gamma\" must be a float in [0, 1]. Got {v}")
66
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Bee, BeeColonyOptimizationConfig
2
+ from .bee_colony_optimization import BeeColonyOptimization
@@ -0,0 +1,116 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ get_partner_index,
5
+ roulette_wheel_index,
6
+ parse_obj_doc, # type: ignore
7
+ )
8
+ from ..abstract import OptimizationAbstract
9
+ from .models import Bee, BeeColonyOptimizationConfig
10
+
11
+
12
+ class BeeColonyOptimization(OptimizationAbstract):
13
+ """
14
+ Implementation of the Bee Colony Optimization algorithm.
15
+
16
+ Args:
17
+ config (BeeColonyOptimizationConfig): an instance of BeeColonyOptimizationConfig class.
18
+ {parse_obj_doc(AntColonyOptimizationConfig)}
19
+
20
+ Bibliography
21
+ ----------
22
+ [1] D. Karaboga, An idea based on honey bee swarm for numerical optimization, Technical Report TR06, Erciyes
23
+ University, Engineering Faculty, Computer Engineering Department, 2005.
24
+ [2] D. Karaboga, B. Basturk, On The Performance Of Artificial Bee Colony (ABC) Algorithm, Applied Soft Computing,
25
+ 8(1), 687-697, 2008.
26
+ [3] D. Karaboga, B. Basturk, A Powerful And Efficient Algorithm For Numerical Function Optimization: Artificial Bee
27
+ Colony (ABC) Algorithm, Journal of Global Optimization, 39(3), 459-471, 2007.
28
+ """
29
+ def __init__(self, config: BeeColonyOptimizationConfig, debug: bool | None = False):
30
+ super().__init__(config, debug)
31
+ self._config.population_size = int(self._config.population_size / 2)
32
+
33
+ def _init_agent(self, position: list[float] | np.ndarray | None = None) -> Bee:
34
+ agent = super()._init_agent(position)
35
+ return Bee(**agent.model_dump())
36
+
37
+ def _greedy_select_agent(self, agent: Bee, new_agent: Bee) -> Bee:
38
+ """
39
+ Perform the greedy selection between the current agent and the new one. The greedy selection is performed by
40
+ comparing the costs of each agent. The one with the lowest cost is kept.
41
+ :param agent: the current agent
42
+ :param new_agent: the new agent
43
+ :return: the best agent
44
+ """
45
+ # if the current agent can not be improved, increase its trial counter
46
+ return new_agent if new_agent.cost < agent.cost else agent.model_copy(update={"trials": agent.trials + 1})
47
+
48
+ def __send_employed_bees__(self) -> None:
49
+ """
50
+ Send employed bees to search for food sources. Each employed bee will dance on a food source.
51
+ :return:
52
+ """
53
+ for i, bee in enumerate(self._population):
54
+ self.__food_source_dance__(i, bee)
55
+
56
+ def __food_source_dance__(self, index: int, bee: Bee):
57
+ """
58
+ Perform a food source dance. The dance is performed by generating a mutant solution and evaluating it. If the
59
+ mutant solution is better than the current solution, the current solution is replaced with the mutant solution.
60
+ Otherwise, the trial counter of the current solution is increased by one.
61
+ :param index:
62
+ :param bee:
63
+ :return:
64
+ """
65
+ # a randomly chosen solution is used in producing a mutant solution of the i-th solution
66
+ # randomly selected solution must be different from the i-th solution
67
+ partner_index = get_partner_index(index, self._config.population_size)
68
+ partner = self._population[partner_index]
69
+
70
+ # generate a mutant solution by perturbing the current solution "index" with a random number
71
+ phi = np.random.uniform(low=-1, high=1, size=self._task.space_dimension)
72
+ pos_new = np.array(bee.position) + phi * (np.array(bee.position) - np.array(partner.position))
73
+ self._population[index] = self._greedy_select_agent(bee, self._init_agent(pos_new))
74
+
75
+ def __send_onlooker_bees__(self):
76
+ """
77
+ Send onlooker bees to search for food sources. Each onlooker bee will dance on a food source. The probability of
78
+ each onlooker bee to dance on a food source is proportional to the quality of the food source. The better the
79
+ food source, the higher the probability of being selected. The probability of each food source is calculated
80
+ using the following formula:
81
+ p_i = cost_i / sum(costs)
82
+ where p_i is the probability of the i-th food source, and cost_i is the cost of the i-th food source. The
83
+ probability of each food source is calculated using the costs of the employed bees. The onlooker bees will use a
84
+ roulette wheel selection to select a food source.
85
+ :return:
86
+ """
87
+ # Calculate the probabilities of each employed bee
88
+ employed_costs = np.array([agent.cost for agent in self._population])
89
+ probabilities = employed_costs / np.sum(employed_costs)
90
+ for idx in range(0, self._config.population_size):
91
+ # Select an employed bee using roulette wheel selection
92
+ selected_bee = self._population[roulette_wheel_index(probabilities)]
93
+ self.__food_source_dance__(idx, selected_bee)
94
+
95
+ def __send_scout_bees__(self):
96
+ """
97
+ Send scout bees to search for food sources. If the number of trials of a food source exceeds a predefined limit,
98
+ the food source is abandoned and a new food source is generated. The new food source is generated randomly.
99
+ """
100
+ trials = np.array([food.trials for food in self._population])
101
+
102
+ # Check the number of trials for each employed bee and abandon the food sources if the limit is exceeded
103
+ abandoned = np.where(trials >= self._config.scouting_limit)[0]
104
+ for idx in abandoned:
105
+ self._population[idx] = self._init_agent() # replace food source with a brand new one
106
+
107
+ def optimization_step(self):
108
+ # generate and evaluate a neighbour point to every food source
109
+ self.__send_employed_bees__()
110
+
111
+ # based to probability, generate a neighbour point and evaluate again some food sources
112
+ # same food source can be evaluated multiple times
113
+ self.__send_onlooker_bees__()
114
+
115
+ # abandon the food sources which have not been improved after a predefined number of trials
116
+ self.__send_scout_bees__()
@@ -0,0 +1,22 @@
1
+ from pydantic import field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Bee(Agent):
7
+ trials: int = 0
8
+
9
+
10
+ class BeeColonyOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class for Bee Colony Optimization algorithm.
13
+ scouting_limit (int): [1, +Inf), the number of times a bee can scout before it is considered exhausted and is
14
+ replaced.
15
+ """
16
+ scouting_limit: int
17
+
18
+ @field_validator("scouting_limit")
19
+ def correct_number_of_scouts(cls, v):
20
+ if v < 1:
21
+ raise ValueError(f"\"scouting_limit\" must be an integer greater than 1. Got {v}")
22
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Camel, CamelCaravanOptimizationConfig
2
+ from .camel_caravan_optimization import CamelCaravanOptimization