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,62 @@
1
+ from pydantic import field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Krill(Agent):
7
+ induced_speed: list[float]
8
+ foraging_speed: list[float]
9
+
10
+
11
+ class KrillHerdOptimizationConfig(BaseOptimizationConfig):
12
+ """
13
+ Krill Herd Optimization Configuration
14
+ n_max (float): maximum induced speed.\n
15
+ foraging_speed (float): Foraging speed.\n
16
+ diffusion_speed (float): maximum diffusion speed.\n
17
+ c_t (float): [0, 2] constant.\n
18
+ w_neighbour (float): [0, 1] inertia weights of the motion induced from neighbors.\n
19
+ w_foraging (float): [0, 1] inertia weights of the motion induced from foraging.\n
20
+ max_neighbours (int): maximum neighbors for neighbors effect.\n
21
+ crossover_rate (float): (0, 1) crossover probability.\n
22
+ mutation_rate (float): (0, 1) mutation probability.\n
23
+ """
24
+ n_max: float
25
+ foraging_speed: float
26
+ diffusion_speed: float
27
+ c_t: float
28
+ w_neighbour: float
29
+ w_foraging: float
30
+ max_neighbours: int
31
+ crossover_rate: float
32
+ mutation_rate: float
33
+
34
+ @field_validator("c_t")
35
+ def correct_c_t(cls, v):
36
+ if not 0 <= v <= 2:
37
+ raise ValueError(f"\"c_t\" must be a float in [0, 2]. Got {v}")
38
+ return v
39
+
40
+ @field_validator("w_neighbour")
41
+ def correct_w_neighbour(cls, v):
42
+ if not 0 <= v <= 1:
43
+ raise ValueError(f"\"w_neighbour\" must be a float in [0, 1]. Got {v}")
44
+ return v
45
+
46
+ @field_validator("w_foraging")
47
+ def correct_w_foraging(cls, v):
48
+ if not 0 <= v <= 1:
49
+ raise ValueError(f"\"w_foraging\" must be a float in [0, 1]. Got {v}")
50
+ return v
51
+
52
+ @field_validator("crossover_rate")
53
+ def correct_crossover_rate(cls, v):
54
+ if not 0 < v < 1:
55
+ raise ValueError(f"\"crossover_rate\" must be a float in (0, 1). Got {v}")
56
+ return v
57
+
58
+ @field_validator("mutation_rate")
59
+ def correct_mutation_rate(cls, v):
60
+ if not 0 < v < 1:
61
+ raise ValueError(f"\"mutation_rate\" must be a float in (0, 1). Got {v}")
62
+ return v
@@ -0,0 +1,2 @@
1
+ from .models import Jaya, LeviFlightJayaSwarmOptimizationConfig
2
+ from .levy_flight_jaya_swarm_optimization import LeviFlightJayaSwarmOptimization
@@ -0,0 +1,40 @@
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 LeviFlightJayaSwarmOptimizationConfig
9
+
10
+
11
+ class LeviFlightJayaSwarmOptimization(OptimizationAbstract):
12
+ """
13
+ Implementation of the Levy-Flight Jaya Swarm Optimization algorithm.
14
+
15
+ Args:
16
+ config (LeviFlightJayaSwarmOptimizationConfig): an instance of LeviFlightJayaSwarmOptimizationConfig class.
17
+ {parse_obj_doc(LeviFlightJayaSwarmOptimizationConfig)}
18
+
19
+ Bibliography
20
+ ----------
21
+ [1] Iacca, G., dos Santos Junior, V.C. and de Melo, V.V., 2021. An improved Jaya optimization
22
+ algorithm with Lévy flight. Expert Systems with Applications, 165, p.113902.
23
+ """
24
+ def __init__(self, config: LeviFlightJayaSwarmOptimizationConfig, debug: bool | None = False):
25
+ super().__init__(config, debug)
26
+
27
+ def optimization_step(self):
28
+ g_worst_pos = np.array(self._worst_agent.position)
29
+ for idx in range(0, self._config.population_size):
30
+ position = self._population[idx].position
31
+
32
+ L1 = get_levy_flight_step(multiplier=1.0, beta=1.8, case=-1)
33
+ L2 = get_levy_flight_step(multiplier=1.0, beta=1.8, case=-1)
34
+ pos_new = self._correct_position(
35
+ position + np.abs(L1) * (self._best_agent.position - np.abs(position)) - np.abs(L2) * (
36
+ g_worst_pos - np.abs(position)
37
+ )
38
+ )
39
+ agent = self._init_agent(pos_new)
40
+ self._population[idx] = self._greedy_select_agent(self._population[idx], agent)
@@ -0,0 +1,9 @@
1
+ from ..models import Agent, BaseOptimizationConfig
2
+
3
+
4
+ class Jaya(Agent):
5
+ pass
6
+
7
+
8
+ class LeviFlightJayaSwarmOptimizationConfig(BaseOptimizationConfig):
9
+ pass
@@ -0,0 +1,130 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import field
3
+ from typing import TypeVar, Any
4
+ import numpy as np
5
+ from pydantic import BaseModel, model_validator
6
+
7
+ from .enums import TaskType
8
+
9
+
10
+ class BaseOptimizationConfig(BaseModel):
11
+ population_size: int
12
+ fitness_error: float | None = None
13
+ max_cycles: int
14
+
15
+
16
+ class Agent(BaseModel):
17
+ position: list[float]
18
+ cost: float
19
+ fitness: float
20
+
21
+
22
+ class Population(BaseModel):
23
+ agents: list[Agent] = field(default_factory=list)
24
+
25
+ # initialize the agents by considering the task type: if it is a minimization task, each agent is accepted as is;
26
+ # otherwise, the position of each agent is multiplied by -1
27
+ def __init__(self, **data: Any):
28
+ def refine_agent(a: Agent, tt: TaskType) -> Agent:
29
+ if tt == TaskType.MIN:
30
+ return a
31
+ # return the agent with the position multiplied by -1
32
+ return a.model_copy(update={"cost": -a.cost})
33
+
34
+ task_type = data.get("task_type", TaskType.MIN)
35
+ agents = [refine_agent(a, task_type) for a in data.get("agents", [])]
36
+ data["agents"] = agents
37
+ super().__init__(**data)
38
+
39
+ def __iter__(self):
40
+ return iter(self.agents)
41
+
42
+ def __getitem__(self, item):
43
+ return self.agents[item]
44
+
45
+ def __setitem__(self, key, value):
46
+ self.agents[key] = value
47
+
48
+ def __len__(self):
49
+ return len(self.agents)
50
+
51
+ def __str__(self):
52
+ return str(self.agents)
53
+
54
+
55
+ class OptimizationResult(BaseModel):
56
+ evolution: list[Population] = field(default_factory=list)
57
+ rates: list[float] = field(default_factory=list)
58
+ best_solution: Agent | None = None
59
+
60
+ # initialize the best_solution by considering the task type: if it is a minimization task, it is accepted as is;
61
+ # otherwise, the position is multiplied by -1
62
+ def __init__(self, **data: Any):
63
+ def refine_best_solution(a: Agent, tt: TaskType) -> Agent:
64
+ if tt == TaskType.MIN:
65
+ return a
66
+ # return the agent with the position multiplied by -1
67
+ return a.model_copy(update={"cost": -a.cost})
68
+
69
+ task_type = data.get("task_type", TaskType.MIN)
70
+ best_solution = data.get("best_solution")
71
+ if best_solution is not None:
72
+ data["best_solution"] = refine_best_solution(best_solution, task_type)
73
+ super().__init__(**data)
74
+
75
+
76
+ class Variable(BaseModel, ABC):
77
+ name: str
78
+ value: Any | None = None
79
+
80
+ @abstractmethod
81
+ def randomize(self) -> None:
82
+ pass
83
+
84
+
85
+ class ContinuousVariable(Variable):
86
+ lower_bound: float
87
+ upper_bound: float
88
+
89
+ @model_validator(mode="after")
90
+ def validate_bounds(self) -> "ContinuousVariable":
91
+ if self.upper_bound <= self.lower_bound:
92
+ raise ValueError("Upper bound must be greater than lower bound")
93
+ return self
94
+
95
+ def randomize(self) -> float:
96
+ self.value = np.random.uniform(self.lower_bound, self.upper_bound)
97
+ return self.value
98
+
99
+
100
+ class DiscreteVariable(Variable):
101
+ choices: list[Any]
102
+
103
+ def randomize(self) -> int:
104
+ self.value = np.random.choice(range(0, len(self.choices)))
105
+ return self.value
106
+
107
+
108
+ class Task(BaseModel, ABC):
109
+ seed: float | None = None
110
+ variables: list[Variable] = field(default_factory=list)
111
+ space_dimension: int
112
+ minmax: TaskType = TaskType.MIN
113
+
114
+ def __init__(self, **data: Any):
115
+ variables = data.get("variables", [])
116
+ data["space_dimension"] = len(variables)
117
+ super().__init__(**data)
118
+
119
+ @abstractmethod
120
+ def objective_function(self, x: list[float | int]) -> float:
121
+ pass
122
+
123
+ def transform_position(self, x: list[float | int]) -> dict[str, Any]:
124
+ return {
125
+ v.name: x[i] if isinstance(v, ContinuousVariable) else v.choices[int(x[i])]
126
+ for i, v in enumerate(self.variables)
127
+ }
128
+
129
+
130
+ T = TypeVar("T", Agent, Agent)
@@ -0,0 +1,2 @@
1
+ from .models import MonarchButterfly, MonarchButterflyOptimizationConfig
2
+ from .monarch_butterfly_optimization import MonarchButterflyOptimization
@@ -0,0 +1,25 @@
1
+ from pydantic import model_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class MonarchButterfly(Agent):
7
+ pass
8
+
9
+
10
+ class MonarchButterflyOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Monarch Butterfly Optimization algorithm.
13
+ partition (float): the value of the partition.\n
14
+ period (float): the period as per the description in the original paper.\n
15
+ keep (int): elitism parameter: how many of the best habitats to keep from one generation to the next
16
+ """
17
+ partition: float
18
+ period: float
19
+ keep: int = 2
20
+
21
+ @model_validator(mode="after")
22
+ def validate_keep(self) -> "MonarchButterflyOptimizationConfig":
23
+ if not 2 <= self.keep < self.population_size / 2:
24
+ raise ValueError(f"keep must be between 2 and population_size / 2, got {self.keep}")
25
+ return self
@@ -0,0 +1,104 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import (
4
+ get_levy_flight_step,
5
+ sort_and_trim,
6
+ sort_by_cost,
7
+ parse_obj_doc, # type: ignore
8
+ )
9
+ from ..abstract import OptimizationAbstract
10
+ from .models import MonarchButterfly, MonarchButterflyOptimizationConfig
11
+
12
+
13
+ class MonarchButterflyOptimization(OptimizationAbstract):
14
+ """
15
+ Implementation of the Monarch Butterfly Optimization algorithm.
16
+
17
+ Args:
18
+ config (MonarchButterflyOptimizationConfig): an instance of MonarchButterflyOptimizationConfig class.
19
+ {parse_obj_doc(MonarchButterflyOptimizationConfig)}
20
+
21
+ Bibliography
22
+ ----------
23
+ [1] Wang, G. G., Deb, S., & Cui, Z. (2019). Monarch butterfly optimization. Neural computing and applications,
24
+ 31(7), 1995-2014.
25
+ """
26
+ def __init__(self, config: MonarchButterflyOptimizationConfig, debug: bool | None = False):
27
+ super().__init__(config, debug)
28
+ self.__bar = self._config.partition
29
+ self.__np1 = int(np.ceil(self._config.partition * self._config.population_size))
30
+ self.__np2 = self._config.population_size - self.__np1
31
+
32
+ def __get_elite__(self) -> list[MonarchButterfly]:
33
+ """
34
+ Get the elite agents of the current generation. The elite agents are the best habitats of the current generation.
35
+ The best habitats are selected according to the cost value of the agents. The number of elite agents is defined
36
+ by the keep parameter. The keep parameter is defined in the configuration class. The keep parameter must be
37
+ between 2 and population_size / 2. If the keep parameter is not defined, the default value is 2.
38
+ :return: a list of elite agents.
39
+ :rtype: list[MonarchButterfly]
40
+ """
41
+ sort_by_cost(self._population)
42
+ return self._population[:self._config.keep].copy()
43
+
44
+ def __migration_operator__(self) -> list[MonarchButterfly]:
45
+ """
46
+ Apply the migration operator. This operator is applied to the best habitats of the current generation. The best
47
+ habitats are selected according to the cost value of the agents. The result of this operator is a new population
48
+ of agents.
49
+ :return: a new population of agents.
50
+ :rtype: list[MonarchButterfly]
51
+ """
52
+ r1 = np.random.random(size=self.__np1) * self._config.period
53
+ partition_condition = r1 <= self._config.partition
54
+ n_values = np.where(partition_condition, self.__np1, self.__np2)
55
+
56
+ pop_indices = np.round(n_values * np.random.random(size=self.__np1) + 0.5).astype(int).tolist()
57
+
58
+ return [
59
+ MonarchButterfly(**self._init_agent(self._population[idx].position).model_dump()) for idx in pop_indices
60
+ ]
61
+
62
+ def __adjusting_operator__(self) -> list[MonarchButterfly]:
63
+ """
64
+ Apply the adjusting operator. This operator is applied to the worst habitats of the current generation. The
65
+ worst habitats are selected according to the cost value of the agents. The result of this operator is a new
66
+ population of agents.
67
+ :return: a new population of agents.
68
+ :rtype: list[MonarchButterfly]
69
+ """
70
+ scale = 1.0 / ((self._cycles + 1) ** 2)
71
+ step_size = np.ceil(np.random.exponential(2 * self._config.max_cycles))
72
+ delta_x = get_levy_flight_step(beta=1., multiplier=step_size, size=self._task.space_dimension, case=1)
73
+
74
+ mask = np.random.uniform(0.0, 1.0, size=self.__np2) <= self._config.partition
75
+ indices = np.where(
76
+ mask, -1, np.round(self.__np2 * np.random.random(size=self.__np2) + 0.5).astype(int).tolist()
77
+ )
78
+
79
+ positions = [
80
+ self._best_agent.position
81
+ if idx == -1
82
+ else self._population[idx].position + scale * (delta_x - 0.5) * int(
83
+ np.random.uniform(0.0, 1.0) > self.__bar
84
+ )
85
+ for idx in indices]
86
+ return [MonarchButterfly(**self._init_agent(position).model_dump()) for position in positions]
87
+
88
+ def __elitism__(self, elite: list[MonarchButterfly], pop1: list[MonarchButterfly], pop2: list[MonarchButterfly]):
89
+ """
90
+ Apply the elitism operator. This operator is applied to the best habitats of the current generation. The best
91
+ habitats are selected according to the cost value of the agents. The number of elite agents is defined by the
92
+ keep parameter. The keep parameter is defined in the configuration class. The keep parameter must be between 2
93
+ and population_size / 2. If the keep parameter is not defined, the default value is 2. The elite agents are
94
+ added to the next generation. The rest of the agents are discarded.
95
+ """
96
+ self._population = sort_and_trim(pop1 + pop2, self._config.population_size - self._config.keep)
97
+ self._population.extend(elite)
98
+
99
+ def optimization_step(self):
100
+ elite = self.__get_elite__()
101
+ pop1 = self.__migration_operator__()
102
+ pop2 = self.__adjusting_operator__()
103
+
104
+ self.__elitism__(elite, pop1, pop2)
@@ -0,0 +1,2 @@
1
+ from .models import MountainGazelle, MountainGazelleOptimizationConfig
2
+ from .mountain_gazelle_optimization import MountainGazelleOptimization
@@ -0,0 +1,9 @@
1
+ from ..models import Agent, BaseOptimizationConfig
2
+
3
+
4
+ class MountainGazelle(Agent):
5
+ pass
6
+
7
+
8
+ class MountainGazelleOptimizationConfig(BaseOptimizationConfig):
9
+ pass
@@ -0,0 +1,118 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import parse_obj_doc # type: ignore
4
+ from ..abstract import OptimizationAbstract
5
+ from .models import MountainGazelleOptimizationConfig
6
+
7
+
8
+ class MountainGazelleOptimization(OptimizationAbstract):
9
+ """
10
+ Implementation of the Mountain Gazelle Optimization algorithm.
11
+
12
+ Args:
13
+ config (MountainGazelleOptimizationConfig): an instance of MountainGazelleSwarmOptimizationConfig class.
14
+ {parse_obj_doc(MountainGazelleOptimizationConfig)}
15
+
16
+ Bibliography
17
+ ----------
18
+ [1] Abdollahzadeh, B., Gharehchopogh, F. S., Khodadadi, N., & Mirjalili, S. (2022). Mountain gazelle optimizer: a
19
+ new nature-inspired metaheuristic algorithm for global optimization problems. Advances in Engineering Software,
20
+ 174, 103282.
21
+ """
22
+ def __init__(self, config: MountainGazelleOptimizationConfig, debug: bool | None = False):
23
+ super().__init__(config, debug)
24
+
25
+ def __random_solution__(self) -> np.ndarray:
26
+ """
27
+ Random solution. It is used to generate a new solution. It is a random combination of the best solution and the
28
+ mean of the positions of the best solutions.
29
+ :return: random solution.
30
+ :rtype: np.ndarray
31
+ """
32
+ pop_size = self._config.population_size
33
+ mean_positions = np.average(
34
+ np.array([
35
+ self._population[mm].position for mm in np.random.permutation(pop_size)[:int(np.ceil(pop_size / 3))]
36
+ ]),
37
+ axis=0,
38
+ )
39
+
40
+ index = np.random.randint(int(np.ceil(pop_size / 3)), pop_size)
41
+ return (
42
+ np.array(self._population[index].position) * np.floor(np.random.normal()) +
43
+ mean_positions * np.ceil(np.random.normal())
44
+ )
45
+
46
+ def __coefficient_vectors__(self, epoch: int, position: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
47
+ """
48
+ Calculate the coefficient vectors. They are calculated according to [1].
49
+ :param epoch: the current epoch.
50
+ :param position: the position of the current agent.
51
+ :return: the coefficient vectors.
52
+ :rtype: tuple[np.ndarray, np.ndarray, np.ndarray]
53
+ """
54
+ n_dims = self._task.space_dimension
55
+ a2 = -1. + epoch * (-1. / self._config.max_cycles)
56
+ u = np.random.standard_normal(n_dims)
57
+ # Coefficient vector for the update of the position of the agents
58
+ cofi = np.zeros((4, n_dims))
59
+ cofi[0, :] = np.random.random(n_dims)
60
+ cofi[1, :] = (a2 + 1) + np.random.random()
61
+ cofi[2, :] = a2 * np.random.standard_normal(n_dims)
62
+ cofi[3, :] = u * np.random.standard_normal(n_dims) ** 2 * np.cos((np.random.random() * 2) * u)
63
+
64
+ A = np.random.standard_normal(n_dims) * np.exp(2 - (epoch + 1) * (2. / self._config.max_cycles))
65
+ D = (np.abs(position) + np.abs(self._best_agent.position)) * (2 * np.random.random() - 1)
66
+
67
+ return cofi, A, D
68
+
69
+ def __implement_candidate_positions__(
70
+ self, cofi: np.ndarray, A: np.ndarray, D: np.ndarray, M: np.ndarray, position: np.ndarray
71
+ ) -> list[np.ndarray]:
72
+ """
73
+ Implement the candidate positions. It is used to generate a new solution. It is a random combination of the
74
+ best solution and the mean of the positions of the best solutions.
75
+ :param cofi: the coefficient vector.
76
+ :param A: the coefficient vector.
77
+ :param D: the coefficient vector.
78
+ :param M: the random solution.
79
+ :param position: the position of the current agent.
80
+ :return: the candidate positions.
81
+ :rtype: list[np.ndarray]
82
+ """
83
+ best_position = np.array(self._best_agent.position)
84
+ random_position = np.array(self._population[np.random.randint(0, self._config.population_size)].position)
85
+
86
+ x1 = self._uniform_position()
87
+
88
+ x2 = best_position - np.abs(
89
+ (np.random.randint(1, 3) * M - np.random.randint(1, 3) * position) * A
90
+ ) * cofi[np.random.randint(0, 4)]
91
+
92
+ x3 = M + cofi[np.random.randint(0, 4)] + (
93
+ np.random.randint(1, 3) * best_position - np.random.randint(1, 3) * random_position
94
+ ) * cofi[np.random.randint(0, 4)]
95
+
96
+ x4 = position - D + (
97
+ np.random.randint(1, 3) * best_position - np.random.randint(1, 3) * M
98
+ ) * cofi[np.random.randint(0, 4)]
99
+
100
+ return [x1, x2, x3, x4]
101
+
102
+ def optimization_step(self):
103
+ pop_new = []
104
+ for index in range(0, self._config.population_size):
105
+ current_position = np.array(self._population[index].position)
106
+
107
+ # get a new random solution
108
+ M = self.__random_solution__()
109
+
110
+ # calculate the vector of coefficients
111
+ cofi, A, D = self.__coefficient_vectors__(self._cycles, current_position)
112
+
113
+ # update the new population for the next generation with the calculated candidate positions
114
+ pop_new += [
115
+ self._init_agent(x) for x in self.__implement_candidate_positions__(cofi, A, D, M, current_position)
116
+ ]
117
+
118
+ self._extend_and_trim_population(pop_new)
@@ -0,0 +1,2 @@
1
+ from .models import Osprey, OspreyOptimizationConfig
2
+ from .osprey_optimization import OspreyOptimization
@@ -0,0 +1,9 @@
1
+ from ..models import Agent, BaseOptimizationConfig
2
+
3
+
4
+ class Osprey(Agent):
5
+ pass
6
+
7
+
8
+ class OspreyOptimizationConfig(BaseOptimizationConfig):
9
+ pass
@@ -0,0 +1,54 @@
1
+ import numpy as np
2
+
3
+ from ..helpers import parse_obj_doc # type: ignore
4
+ from ..abstract import OptimizationAbstract
5
+ from .models import Osprey, OspreyOptimizationConfig
6
+
7
+
8
+ class OspreyOptimization(OptimizationAbstract):
9
+ """
10
+ Implementation of the Osprey Optimization algorithm.
11
+
12
+ Args:
13
+ config (OspreyOptimizationConfig): an instance of OspreyOptimizationConfig class.
14
+ {parse_obj_doc(OspreyOptimizationConfig)}
15
+
16
+ Bibliography
17
+ ----------
18
+ [1] Trojovský, P., & Dehghani, M. Osprey Optimization Algorithm: A new bio-inspired metaheuristic algorithm
19
+ for solving engineering optimization problems. Frontiers in Mechanical Engineering, 8, 136.
20
+ """
21
+ def __init__(self, config: OspreyOptimizationConfig, debug: bool | None = False):
22
+ super().__init__(config, debug)
23
+ self.__phi = (1 + np.sqrt(5)) / 2 # golden ratio
24
+
25
+ def __get_indexes_better__(self, osprey: Osprey) -> np.ndarray:
26
+ costs = np.array([agent.cost for agent in self._population])
27
+ idxs = np.where(costs < osprey.cost)
28
+ return idxs[0]
29
+
30
+ def __sf__(self, osprey: Osprey) -> np.ndarray:
31
+ idxs = self.__get_indexes_better__(osprey)
32
+ if len(idxs) == 0 or np.random.random() < 0.5:
33
+ return np.array(self._best_agent.position)
34
+
35
+ kk = np.random.permutation(idxs)[0]
36
+ return np.array(self._population[kk].position)
37
+
38
+ def optimization_step(self):
39
+ for idx, osprey in enumerate(self._population):
40
+ pos = np.array(osprey.position)
41
+
42
+ # phase 1: position identification and fish hunting (exploration)
43
+ sf = self.__sf__(osprey)
44
+ r1 = np.random.randint(1, 3)
45
+ pos_new = pos + np.random.normal(0, 1) * (sf - r1 * pos) # Eq. 5
46
+ agent = self._init_agent(self._correct_position(pos_new))
47
+
48
+ osprey = self._greedy_select_agent(osprey, agent)
49
+
50
+ # phase 2: carrying the fish to a suitable position (exploitation)
51
+ pos_new = self._increase_position(osprey.position) # Eq. 7
52
+ agent = self._init_agent(self._correct_position(pos_new))
53
+
54
+ self._population[idx] = self._greedy_select_agent(osprey, agent)
@@ -0,0 +1,2 @@
1
+ from .models import Particle, ParticleSwarmOptimizationConfig
2
+ from .particle_swarm_optimization import ParticleSwarmOptimization
@@ -0,0 +1,40 @@
1
+ from pydantic import model_validator, conlist, field_validator
2
+
3
+ from ..models import Agent, BaseOptimizationConfig
4
+
5
+
6
+ class Particle(Agent):
7
+ velocity: list[float]
8
+
9
+
10
+ class ParticleSwarmOptimizationConfig(BaseOptimizationConfig):
11
+ """
12
+ Configuration class of the Particle Swarm Optimization algorithm.
13
+ c1 (float): (0, 5), cognitive parameter.\n
14
+ c2 (float): (0, 5), social parameter.\n
15
+ w (list[float]): (0, 0.5), [0.5, 2.0], weights.
16
+ """
17
+ c1: float
18
+ c2: float
19
+ w: conlist(float, min_length=2, max_length=2)
20
+
21
+ @field_validator("c1")
22
+ def correct_c1(cls, v):
23
+ if not 0 < v < 5:
24
+ raise ValueError(f"\"c1\" must be a float in (0, 5.0). Got {v}")
25
+ return v
26
+
27
+ @field_validator("c2")
28
+ def correct_c2(cls, v):
29
+ if not 0 < v < 5:
30
+ raise ValueError(f"\"c2\" must be a float in (0, 5.0). Got {v}")
31
+ return v
32
+
33
+ @field_validator("w")
34
+ def correct_weights(cls, v):
35
+ w_min, w_max = v
36
+ if not 0 < w_min < 0.5:
37
+ raise ValueError(f"\"w[0]\" must be a float in (0, 0.5). Got {w_min}")
38
+ if not 0.5 < w_max < 2:
39
+ raise ValueError(f"\"w[1]\" must be a float in [0.5, 2.0]. Got {w_max}")
40
+ return v