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,119 @@
1
+ import numpy as np
2
+
3
+ from .models import Empire as EmpireModel
4
+ from ..enums import TaskType
5
+ from ..helpers import calculate_fitness
6
+ from ..models import Agent
7
+
8
+
9
+ class Country:
10
+ def __init__(self, agent: Agent):
11
+ self.__colonies = []
12
+ self.__from_agent(agent)
13
+
14
+ def __from_agent(self, agent: Agent):
15
+ self.__representation = agent.position
16
+ self.__makespan = agent.cost
17
+
18
+ @property
19
+ def is_colony(self):
20
+ return False
21
+
22
+ @property
23
+ def is_imperialist(self):
24
+ return not self.is_colony
25
+
26
+ @property
27
+ def representation(self):
28
+ return self.__representation
29
+
30
+ @property
31
+ def position(self):
32
+ return self.representation
33
+
34
+ @property
35
+ def cost(self):
36
+ return self.__makespan
37
+
38
+ def set_representation(self, agent: Agent):
39
+ self.__from_agent(agent)
40
+
41
+
42
+ class Empire:
43
+ def __init__(self, emperor: Country):
44
+ self.__emperor = emperor
45
+ self.__colonies: list[Country] = []
46
+ self.__cost = emperor.cost
47
+
48
+ def __calculate_cost(self):
49
+ self.__cost = self.__emperor.cost + sum([x.cost for x in self.__colonies])
50
+
51
+ def replace_colony(self, index: int, colony: Country):
52
+ self.__colonies[index] = colony
53
+ self.__calculate_cost()
54
+
55
+ def replace_emperor(self, colony: Country):
56
+ self.__emperor = colony
57
+ self.__calculate_cost()
58
+
59
+ def delete_colony(self, index: int):
60
+ del self.__colonies[index]
61
+ self.__calculate_cost()
62
+
63
+ @property
64
+ def cost(self) -> float:
65
+ return self.__cost
66
+
67
+ def add_colony(self, colony: Country, index: int = 0):
68
+ self.__colonies.insert(len(self.__colonies) if index == 0 else index, colony)
69
+ self.__calculate_cost()
70
+
71
+ def remove_colony(self, index: int):
72
+ del self.__colonies[index]
73
+ self.__calculate_cost()
74
+
75
+ @property
76
+ def number_of_colonies(self) -> int:
77
+ return len(self.__colonies)
78
+
79
+ @property
80
+ def colonies(self) -> list[Country]:
81
+ return self.__colonies
82
+
83
+ def get_colony(self, index: int) -> Country:
84
+ return self.__colonies[index]
85
+
86
+ def get_strongest_colony(self) -> tuple[int, Country | None]:
87
+ """
88
+ Among all the colonies, find the one and its index with the lowest cost
89
+ :return: a tuple of the index and the colony with the lowest cost
90
+ :rtype: tuple[int, Country]
91
+ """
92
+ if self.number_of_colonies == 0:
93
+ return -1, None
94
+
95
+ strongest_colony_index = np.argmin(np.array([colony.cost for colony in self.__colonies]))
96
+ strongest_colony = self.get_colony(strongest_colony_index)
97
+ return strongest_colony_index, strongest_colony
98
+
99
+ @property
100
+ def emperor(self) -> Country:
101
+ return self.__emperor
102
+
103
+
104
+ class Transformer:
105
+ """
106
+ This class is used to transform the Empire class to EmpireModel class.
107
+ """
108
+ @staticmethod
109
+ def transform(empire: Empire, task_type: TaskType) -> EmpireModel:
110
+ """
111
+ :type empire: Empire
112
+ :param empire: the empire to be transformed
113
+ :param task_type: the type of the task, i.e. whether it is a minimization or maximization task
114
+ :return: an instance of EmpireModel class, i.e. the pydantic representation of the empire
115
+ """
116
+ cost = empire.cost
117
+ return EmpireModel(
118
+ position=empire.emperor.representation, cost=cost, fitness=calculate_fitness(cost, task_type)
119
+ )
@@ -0,0 +1,198 @@
1
+ import math
2
+ import numpy as np
3
+
4
+ from .classes import Country
5
+ from ..helpers import (
6
+ random_selection,
7
+ parse_obj_doc, # type: ignore
8
+ )
9
+ from ..abstract import OptimizationAbstract
10
+ from .classes import Empire as EmpireClass, Transformer
11
+ from .models import ImperialistCompetitiveOptimizationConfig
12
+
13
+
14
+ class ImperialistCompetitiveOptimization(OptimizationAbstract):
15
+ """
16
+ Implementation of the Imperialist Competitive Optimization algorithm.
17
+
18
+ Args:
19
+ config (ParticleSwarmOptimizationConfig): an instance of ParticleSwarmOptimizationConfig class.
20
+ {parse_obj_doc(ParticleSwarmOptimizationConfig)}
21
+
22
+ Bibliography
23
+ ----------
24
+ [1] Esmaeilzadeh, E., & Ghane, M. (2013). Imperialist competitive algorithm: A metaheuristic algorithm for
25
+ optimization inspired by imperialistic competition. 2013 3rd International Conference on Computer and Knowledge
26
+ Engineering (ICCKE), 1–6. https://doi.org/10.1109/ICCKE.2013.6687751
27
+ [2] Esmaeilzadeh, E., & Ghane, M. (2014). Imperialist competitive algorithm: An algorithm for optimization inspired
28
+ by imperialistic competition. Applied Soft Computing Journal, 14, 240–256.
29
+ https://doi.org/10.1016/j.asoc.2013.08.006
30
+ """
31
+ def __init__(self, config: ImperialistCompetitiveOptimizationConfig, debug: bool | None = False):
32
+ super().__init__(config, debug)
33
+ self.__countries: list[Country] = []
34
+ self.__empires: list[EmpireClass] = []
35
+
36
+ def _init_population(self):
37
+ # Create countries
38
+ k = self._config.number_of_countries
39
+ countries = []
40
+ i = 0
41
+ while i < k:
42
+ candidate = Country(self._init_agent())
43
+ if (True for elem in countries if np.array_equal(elem.representation, candidate.representation)):
44
+ countries.append(candidate)
45
+ i += 1
46
+
47
+ self.__countries = countries
48
+
49
+ # Create empires
50
+ costs = np.array([np.sum(countries[i].cost) for i in range(0, len(countries))])
51
+ indices = np.argsort(costs)
52
+ new_countries = np.array([countries[i] for i in indices])
53
+
54
+ candidate_empires = new_countries[:self._config.population_size]
55
+ candidate_colonies = new_countries[self._config.population_size:]
56
+
57
+ for ctr in candidate_empires:
58
+ self.__empires.append(EmpireClass(ctr))
59
+
60
+ empires_costs = np.array([np.sum(empire.cost) for empire in self.__empires])
61
+ p = np.exp(-np.multiply(self._config.alpha_rate, empires_costs) / np.max(empires_costs))
62
+ p = p / np.sum(p)
63
+ for country in candidate_colonies:
64
+ k = random_selection(p)
65
+ self.__empires[k].add_colony(country)
66
+
67
+ task_type = self._task.minmax
68
+ self._population = [Transformer.transform(empire, task_type) for empire in self.__empires]
69
+
70
+ def __assimilate(self):
71
+ """
72
+ Assimilate colonies into empires. The assimilation rate is defined by the user. The assimilation is done by
73
+ replacing the representation of the colony with the representation of the empire.
74
+ """
75
+ assimilation_rate = self._config.assimilation_rate
76
+ dim = self._task.space_dimension
77
+ for empire in self.__empires:
78
+ empire_representation = empire.emperor.representation
79
+ for colony in empire.colonies:
80
+ candidates = np.random.choice(
81
+ range(0, dim), int(np.round(dim * assimilation_rate, decimals=0)), replace=False,
82
+ )
83
+ colony.set_representation(self._init_agent(
84
+ [r if i in candidates else colony.representation[i] for i, r in enumerate(empire_representation)]
85
+ ))
86
+
87
+ def __revolution(self):
88
+ """
89
+ Revolution is a process that is applied to a colony. The revolution rate is defined by the user. The revolution
90
+ is done by exchanging the representation of the colony with the representation of another colony.
91
+ """
92
+ revolution_probability = self._config.revolution_probability
93
+ revolution_rate = self._config.revolution_rate
94
+ fcn = self._fcn
95
+ dim = self._task.space_dimension
96
+ for empire in self.__empires:
97
+ for i, colony in enumerate(empire.colonies):
98
+ if np.random.random() <= revolution_probability:
99
+ # select the colony to exchange with
100
+ colony_representation = colony.representation
101
+ old_cost = colony.cost
102
+ number_of_tasks = int(math.ceil(revolution_rate * dim))
103
+
104
+ candidates = np.random.choice(range(0, dim), number_of_tasks, replace=False)
105
+ exchange = list(range(0, dim))
106
+
107
+ # remove the candidates from the exchange list
108
+ for index in candidates:
109
+ del exchange[index]
110
+
111
+ # select the candidates to exchange with
112
+ exchange_candidates = np.random.choice(exchange, number_of_tasks)
113
+ new_colony_representation = colony_representation
114
+ # exchange the candidates
115
+ for (x, y) in zip(candidates, exchange_candidates):
116
+ new_colony_representation[x], new_colony_representation[y] = (
117
+ colony_representation[y], colony_representation[x]
118
+ )
119
+ new_colony = Country(self._init_agent(new_colony_representation))
120
+ if new_colony.cost < old_cost:
121
+ empire.replace_colony(i, new_colony)
122
+
123
+ def __inter_empire_war(self):
124
+ """
125
+ Inter-empire competition is a process that is applied to empires. The weakest empire is selected and a war is
126
+ held between the weakest empire and the other empires. The probability of winning the war is proportional to
127
+ the cost of the empire. The winning empire assimilates the weakest empire's colonies and the weakest colony
128
+ from the weakest empire. If the weakest empire has no colonies, then the weakest emperor is assimilated by the
129
+ winning empire.
130
+ """
131
+ if len(self.__empires) == 1:
132
+ return
133
+
134
+ total_cost = np.array([empire.cost for empire in self.__empires])
135
+
136
+ # the weakest empire is the one with the highest cost
137
+ weakest_empire_index = np.argmax(total_cost)
138
+ weakest_empire = self.__empires[weakest_empire_index]
139
+ p = np.exp(-np.multiply(self._config.alpha_rate, total_cost) / np.max(total_cost))
140
+
141
+ # the weakest empire has a probability of 0 to win the war
142
+ p[weakest_empire_index] = 0
143
+ p = p / np.sum(p)
144
+ # if all probabilities are 0, then the weakest empire wins the war
145
+ if np.any(np.isnan(p)):
146
+ p[np.isnan(p)] = 0
147
+ if all(p == 0):
148
+ p[:] = 1
149
+ p = p / sum(p)
150
+
151
+ # if the weakest empire has colonies, then select the weakest colony and add it to the winning empire
152
+ if weakest_empire.number_of_colonies > 0:
153
+ weakest_empire_colonies_cost = np.array([colony.cost for colony in weakest_empire.colonies])
154
+ weakest_colony_index = np.argmax(weakest_empire_colonies_cost)
155
+ weakest_colony = weakest_empire.get_colony(weakest_colony_index)
156
+
157
+ winning_empire_index = random_selection(p)
158
+ winning_empire = self.__empires[winning_empire_index]
159
+
160
+ winning_empire.add_colony(weakest_colony)
161
+ weakest_empire.delete_colony(weakest_colony_index)
162
+
163
+ # if the weakest empire has no colonies, then select the weakest emperor and add it to the winning empire
164
+ if weakest_empire.number_of_colonies == 0:
165
+ winning_empire_index = random_selection(p)
166
+ winning_empire = self.__empires[winning_empire_index]
167
+
168
+ winning_empire.add_colony(weakest_empire.emperor)
169
+ del self.__empires[self.__empires.index(weakest_empire)]
170
+
171
+ def __intra_empire_war(self):
172
+ """
173
+ Intra-empire competition is a process that is applied to empires. It replaces a weakest emperor with the
174
+ strongest colony in case of internal war. The probability of winning the war is proportional to the cost of the
175
+ colony. Total cost remains unchanged for the empire
176
+ """
177
+ for empire in self.__empires:
178
+ strongest_colony_index, strongest_colony = empire.get_strongest_colony()
179
+ # if there is a picked colony and its cost is lower than the emperor, then swap them
180
+ if strongest_colony and strongest_colony.cost < empire.emperor.cost:
181
+ empire.replace_colony(strongest_colony_index, empire.emperor)
182
+ empire.replace_emperor(strongest_colony)
183
+
184
+ def optimization_step(self):
185
+ # assimilation
186
+ self.__assimilate()
187
+
188
+ # revolution
189
+ self.__revolution()
190
+
191
+ # Intra - empire competition
192
+ self.__intra_empire_war()
193
+
194
+ # Inter - empire competition
195
+ self.__inter_empire_war()
196
+
197
+ task_type = self._task.minmax
198
+ self._population = [Transformer.transform(empire, task_type) for empire in self.__empires]
@@ -0,0 +1,47 @@
1
+ from pydantic import field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Empire(Agent):
7
+ pass
8
+
9
+
10
+ class ImperialistCompetitiveOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Imperialist Competitive Optimization algorithm.
13
+ assimilation_rate (float): [0, 1), the rate of assimilation of the colonies by the imperialist.\n
14
+ revolution_rate (float): [0, 1), the rate of revolution of the colonies.\n
15
+ alpha_rate (float): [0, 1), the rate of alpha.\n
16
+ revolution_probability (float): [0, 1), the probability of revolution.\n
17
+ number_of_countries (int): the number of countries per empire.\n
18
+ """
19
+ assimilation_rate: float
20
+ revolution_rate: float
21
+ alpha_rate: float
22
+ revolution_probability: float
23
+ number_of_countries: int
24
+
25
+ @field_validator("assimilation_rate")
26
+ def correct_assimilation_rate(cls, v):
27
+ if not 0 <= v < 1:
28
+ raise ValueError(f"\"assimilation_rate\" must be a positive float lower than 1. Got {v}")
29
+ return v
30
+
31
+ @field_validator("revolution_rate")
32
+ def correct_revolution_rate(cls, v):
33
+ if not 0 <= v < 1:
34
+ raise ValueError(f"\"revolution_rate\" must be a positive float lower than 1. Got {v}")
35
+ return v
36
+
37
+ @field_validator("alpha_rate")
38
+ def correct_alpha_rate(cls, v):
39
+ if not 0 <= v < 1:
40
+ raise ValueError(f"\"alpha_rate\" must be a positive float lower than 1. Got {v}")
41
+ return v
42
+
43
+ @field_validator("revolution_probability")
44
+ def correct_revolution_probability(cls, v):
45
+ if not 0 <= v < 1:
46
+ raise ValueError(f"\"revolution_probability\" must be a positive float lower than 1. Got {v}")
47
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import InvasiveWeed, InvasiveWeedOptimizationConfig
2
+ from .invasive_weed_optimization import InvasiveWeedOptimization
@@ -0,0 +1,52 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ special_agents,
5
+ parse_obj_doc, # type: ignore
6
+ )
7
+ from ..abstract import OptimizationAbstract
8
+ from .models import InvasiveWeedOptimizationConfig
9
+
10
+
11
+ class InvasiveWeedOptimization(OptimizationAbstract):
12
+ """
13
+ Implementation of the Invasive Weed Optimization algorithm.
14
+
15
+ Args:
16
+ config (InvasiveWeedOptimizationConfig): an instance of InvasiveWeedOptimizationConfig class.
17
+ {parse_obj_doc(InvasiveWeedOptimizationConfig)}
18
+
19
+ Bibliography
20
+ ----------
21
+ [1] Mehrabian, A.R. and Lucas, C., 2006. A novel numerical optimization algorithm inspired from weed colonization.
22
+ Ecological informatics, 1(4), pp.355-366.
23
+ """
24
+ def __init__(self, config: InvasiveWeedOptimizationConfig, debug: bool | None = False):
25
+ super().__init__(config, debug)
26
+
27
+ def optimization_step(self):
28
+ # update Standard Deviation
29
+ sigma_start, sigma_end = self._config.sigma
30
+ sigma = (
31
+ (self._config.max_cycles - self._cycles) / (self._config.max_cycles - 1)
32
+ ) ** self._config.exponent * (sigma_start - sigma_end) + sigma_end
33
+
34
+ # get best and worst Invasive Weeds
35
+ (best, ), (worst, ) = special_agents(self._population, n_best=1, n_worst=1)
36
+
37
+ seed_min, seed_max = self._config.seed
38
+ pop_size = self._config.population_size
39
+ n_dims = self._task.space_dimension
40
+
41
+ pop_new = []
42
+ for weed in self._population:
43
+ ratio = (
44
+ np.random.random() if best.cost == worst.cost else (weed.cost - worst.cost) / (best.cost - worst.cost)
45
+ )
46
+ s = min(int(np.ceil(seed_min + (seed_max - seed_min) * ratio)), int(np.sqrt(pop_size)))
47
+ for jdx in range(0, s):
48
+ # initialize offspring and generate random location
49
+ pop_new.append(self._init_agent(weed.position + sigma * np.random.normal(0, 1, n_dims)))
50
+
51
+ # update population
52
+ self._extend_and_trim_population(pop_new)
@@ -0,0 +1,51 @@
1
+ from pydantic import field_validator, model_validator, conlist
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class InvasiveWeed(Agent):
7
+ pass
8
+
9
+
10
+ class InvasiveWeedOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Invasive Weed Optimization algorithm.
13
+ seed (list[int]): [1, 3], [4, pop_size/2], minmax numbers of Seeds.\n
14
+ exponent (int): [2, 4], variance reduction exponent.\n
15
+ sigma (list[float]): [0.5, 5.0], (0, 0.5), the initial minmax values of Standard Deviation.
16
+ """
17
+ seed: conlist(int, min_length=2, max_length=2)
18
+ exponent: int
19
+ sigma: conlist(float, min_length=2, max_length=2)
20
+
21
+ @model_validator(mode="after")
22
+ def validate_seed(self) -> "InvasiveWeedOptimizationConfig":
23
+ seed_min, seed_max = self.seed
24
+ if seed_max > self.population_size / 2:
25
+ raise ValueError(f"\"seed_max\" must be less than the half of the population_size. Got {seed_max}")
26
+ return self
27
+
28
+ @field_validator("seed")
29
+ def correct_seed(cls, v):
30
+ seed_min, seed_max = v
31
+
32
+ if not 1 <= seed_min <= 3:
33
+ raise ValueError(f"\"seed[0]\" must be a positive int within the range [1, 3]. Got {seed_min}")
34
+ if seed_max < 4:
35
+ raise ValueError(f"\"seed[1]\" must be a positive int within the range [4, pop_size/2]. Got {seed_max}")
36
+ return v
37
+
38
+ @field_validator("exponent")
39
+ def correct_exponent(cls, v):
40
+ if not 2 <= v <= 4:
41
+ raise ValueError(f"\"exponent\" must be a positive int within the range [2, 4]. Got {v}")
42
+ return v
43
+
44
+ @field_validator("sigma")
45
+ def correct_sigma(cls, v):
46
+ start, end = v
47
+ if not 0.5 <= start <= 5:
48
+ raise ValueError(f"\"sigma[0]\" must be a positive float within the range [0.5, 5]. Got {start}")
49
+ if not 0 < end < 0.5:
50
+ raise ValueError(f"\"sigma[1]\" must be a positive float within the range (0, 0.5). Got {end}")
51
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Krill, KrillHerdOptimizationConfig
2
+ from .krill_herd_optimization import KrillHerdOptimization
@@ -0,0 +1,179 @@
1
+ from typing import Final
2
+ import numpy as np
3
+
4
+ from ..helpers import (
5
+ distance,
6
+ special_agents,
7
+ parse_obj_doc, # type: ignore
8
+ )
9
+ from ..abstract import OptimizationAbstract
10
+ from .models import Krill, KrillHerdOptimizationConfig
11
+
12
+
13
+ class KrillHerdOptimization(OptimizationAbstract):
14
+ """
15
+ Implementation of the Krill Herd Optimization algorithm.
16
+
17
+ Args:
18
+ config (KrillHerdOptimizationConfig): an instance of KrillHerdOptimizationConfig class.
19
+ {parse_obj_doc(KrillHerdOptimizationConfig)}
20
+
21
+ Bibliography
22
+ ----------
23
+ [1] Amir Hossein Gandomi, Amir Hossein Alavi, Krill herd: A new bio-inspired optimization algorithm, Communications
24
+ in Nonlinear Science and Numerical Simulation, Volume 17, Issue 12, 2012, Pages 4831-4845, ISSN 1007-5704,
25
+ https://doi.org/10.1016/j.cnsns.2012.05.010.
26
+ """
27
+ EPS: Final[float] = np.finfo(float).eps
28
+
29
+ def __init__(self, config: KrillHerdOptimizationConfig, debug: bool | None = False):
30
+ super().__init__(config, debug)
31
+ self.__w_neighbour: np.ndarray | None = None
32
+ self.__w_foraging: np.ndarray | None = None
33
+
34
+ def before_initialization(self):
35
+ self.__w_neighbour = np.full(self._task.space_dimension, self._config.w_neighbour)
36
+ self.__w_foraging = np.full(self._task.space_dimension, self._config.w_foraging)
37
+
38
+ def _init_agent(
39
+ self,
40
+ position: list[float] | np.ndarray | None = None,
41
+ induced_speed: list[float] | None = None,
42
+ foraging_speed: list[float] | None = None,
43
+ ) -> Krill:
44
+ agent = super()._init_agent(position)
45
+
46
+ zeros = np.zeros(self._task.space_dimension).tolist()
47
+
48
+ return Krill(
49
+ **agent.model_dump(),
50
+ induced_speed=induced_speed if induced_speed is not None else zeros,
51
+ foraging_speed=foraging_speed if foraging_speed is not None else zeros,
52
+ )
53
+
54
+ def __get_food_location__(self) -> list[float]:
55
+ """
56
+ Get food location for krill heard.
57
+ :return: food location.
58
+ :rtype: list[float]
59
+ """
60
+ costs = np.array([krill.cost for krill in self._population])
61
+ positions = np.array([krill.position for krill in self._population])
62
+
63
+ return self._correct_position(np.array(
64
+ [np.sum(positions[:, i] / costs) for i in range(0, self._task.space_dimension)]
65
+ ) / np.sum(1 / costs))
66
+
67
+ def __get_x__(self, x: Krill, y: Krill) -> np.ndarray:
68
+ """
69
+ Get x values.
70
+ :param x: First krill/individual.
71
+ :param y: Second krill/individual.
72
+ :return: X values.
73
+ :rtype: np.ndarray
74
+ """
75
+ return (
76
+ (np.array(y.position) - np.array(x.position)) + self.EPS
77
+ ) / (distance(y.position, x.position) + self.EPS)
78
+
79
+ def __get_k__(self, x: Krill, y: Krill, best: Krill, worst: Krill) -> float:
80
+ """
81
+ Get k values.
82
+ :param x: First krill/individual.
83
+ :param y: Second krill/individual.
84
+ :param best: Best krill/individual.
85
+ :param worst: Worst krill/individual.
86
+ :return: K values.
87
+ :rtype: float
88
+ """
89
+ return (x.cost - y.cost + self.EPS) / (worst.cost - best.cost + self.EPS)
90
+
91
+ def __induce_neighbours_motion__(self, idx: int, krill: Krill, best: Krill, worst: Krill) -> np.ndarray:
92
+ """
93
+ Induced neighbours motion operator.
94
+ :param idx: Index of current krill being operated.
95
+ :param krill: Krill being operated.
96
+ :param best: Best krill in heard.
97
+ :param worst: Worst krill in heard.
98
+ :return: Induced speed.
99
+ :rtype: np.ndarray
100
+ """
101
+ # calculate sense range for selected individual
102
+ population_size = self._config.population_size
103
+ sense_range = np.sum(
104
+ [distance(krill.position, self._population[i].position) for i in range(0, population_size)]
105
+ ) / (self._config.max_neighbours * population_size)
106
+
107
+ # get neighbours
108
+ neighbours = [agent for jdx, agent in enumerate(self._population) if jdx != idx and sense_range > distance(
109
+ krill.position, agent.position
110
+ )]
111
+ if not neighbours:
112
+ neighbours = [self._population[np.random.randint(self._config.population_size)]]
113
+
114
+ alpha_l = np.sum(
115
+ np.array([self.__get_k__(krill, neighbour, best, worst) for neighbour in neighbours]) * np.array(
116
+ [self.__get_x__(krill, neighbour) for neighbour in neighbours]
117
+ ).T
118
+ )
119
+
120
+ alpha_t = 2 * (1 + np.random.random() * (self._cycles + 1) / self._config.max_cycles)
121
+ return self._config.n_max * (alpha_l + alpha_t) + self.__w_neighbour * krill.induced_speed
122
+
123
+ def __induce_foraging_motion__(
124
+ self, krill: Krill, pos_food: list[float], g_best: Krill, g_worst: Krill
125
+ ) -> np.ndarray:
126
+ """
127
+ Induced foraging motion operator.
128
+ :param krill: Krill being operated.
129
+ :param pos_food: Food position.
130
+ :param g_best: Best krill/individual.
131
+ :param g_worst: Worst krill/individual.
132
+ :return: Foraging speed.
133
+ :rtype: np.ndarray
134
+ """
135
+ temp_krill = self._init_agent(position=pos_food)
136
+ beta_f = 2 * (1 - (self._cycles + 1) / self._config.max_cycles) * self.__get_k__(
137
+ krill, temp_krill, g_best, g_worst
138
+ ) * self.__get_x__(krill, temp_krill) if g_best.cost < krill.cost else 0
139
+ beta_b = self.__get_k__(krill, g_best, g_best, g_worst) * self.__get_x__(krill, g_best)
140
+ return self._config.foraging_speed * (beta_f + beta_b) + self.__w_foraging * krill.foraging_speed
141
+
142
+ def optimization_step(self):
143
+ (g_best, ), (g_worst, ) = special_agents(self._population, n_best=1, n_worst=1)
144
+ pos_food = self.__get_food_location__()
145
+
146
+ new_pop = []
147
+ for idx, krill in enumerate(self._population):
148
+ pos = krill.position
149
+
150
+ induced_speed = self.__induce_neighbours_motion__(idx, krill, g_best, g_worst)
151
+ foraging_speed = self.__induce_foraging_motion__(krill, pos_food, g_best, g_worst)
152
+
153
+ # induced physical diffusion operator
154
+ diffusion = self._config.diffusion_speed * (
155
+ 1 - (self._cycles + 1) / self._config.max_cycles
156
+ ) * np.random.uniform(-1, 1, self._task.space_dimension)
157
+
158
+ # get a new position by a new delta factor
159
+ new_pos = self._correct_position(krill.position + (
160
+ self._config.c_t * np.sum(self._bandwidth()) * (induced_speed + foraging_speed + diffusion)
161
+ ))
162
+
163
+ # crossover
164
+ crossover_rate = self._config.crossover_rate * self.__get_k__(krill, g_best, g_best, g_worst)
165
+ new_pos = self._correct_position(np.where(
166
+ [np.random.random() < crossover_rate for _ in range(0, self._task.space_dimension)], pos, new_pos
167
+ ))
168
+
169
+ # mutation
170
+ mutation_rate = self._config.mutation_rate / (self.__get_k__(krill, g_best, g_best, g_worst) + 1e-31)
171
+ new_pos = np.where(
172
+ [np.random.random() < mutation_rate for _ in range(0, self._task.space_dimension)],
173
+ new_pos,
174
+ g_best.position + np.random.random(self._task.space_dimension)
175
+ )
176
+
177
+ new_pop.append(self._init_agent(new_pos, induced_speed.tolist(), foraging_speed.tolist()))
178
+
179
+ self._greedy_select_population(new_pop)