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.
- pyvolutionary/__init__.py +44 -0
- pyvolutionary/abstract.py +325 -0
- pyvolutionary/african_vulture/__init__.py +2 -0
- pyvolutionary/african_vulture/african_vulture_optimization.py +87 -0
- pyvolutionary/african_vulture/models.py +41 -0
- pyvolutionary/ant_colony/__init__.py +2 -0
- pyvolutionary/ant_colony/ant_colony_optimization.py +80 -0
- pyvolutionary/ant_colony/models.py +25 -0
- pyvolutionary/aquila/__init__.py +2 -0
- pyvolutionary/aquila/aquila_optimization.py +75 -0
- pyvolutionary/aquila/models.py +9 -0
- pyvolutionary/bacterial_foraging/__init__.py +2 -0
- pyvolutionary/bacterial_foraging/bacterial_foraging_optimization.py +152 -0
- pyvolutionary/bacterial_foraging/models.py +63 -0
- pyvolutionary/bat/__init__.py +2 -0
- pyvolutionary/bat/bat_optimization.py +66 -0
- pyvolutionary/bat/models.py +66 -0
- pyvolutionary/bee_colony/__init__.py +2 -0
- pyvolutionary/bee_colony/bee_colony_optimization.py +116 -0
- pyvolutionary/bee_colony/models.py +22 -0
- pyvolutionary/camel_caravan/__init__.py +2 -0
- pyvolutionary/camel_caravan/camel_caravan_optimization.py +96 -0
- pyvolutionary/camel_caravan/models.py +61 -0
- pyvolutionary/coral_reef/__init__.py +2 -0
- pyvolutionary/coral_reef/coral_reef_optimization.py +133 -0
- pyvolutionary/coral_reef/models.py +81 -0
- pyvolutionary/coyotes/__init__.py +2 -0
- pyvolutionary/coyotes/coyotes_optimization.py +96 -0
- pyvolutionary/coyotes/models.py +22 -0
- pyvolutionary/earthworms/__init__.py +2 -0
- pyvolutionary/earthworms/earthworms_optimization.py +124 -0
- pyvolutionary/earthworms/models.py +62 -0
- pyvolutionary/electromagnetic_field/__init__.py +2 -0
- pyvolutionary/electromagnetic_field/electromagnetic_field_optimization.py +58 -0
- pyvolutionary/electromagnetic_field/models.py +45 -0
- pyvolutionary/elephant_herd/__init__.py +2 -0
- pyvolutionary/elephant_herd/elephant_herd_optimization.py +56 -0
- pyvolutionary/elephant_herd/models.py +38 -0
- pyvolutionary/enums.py +39 -0
- pyvolutionary/firefly_swarm/__init__.py +2 -0
- pyvolutionary/firefly_swarm/firefly_swarm_optimization.py +76 -0
- pyvolutionary/firefly_swarm/models.py +37 -0
- pyvolutionary/fireworks/__init__.py +2 -0
- pyvolutionary/fireworks/fireworks_optimization.py +96 -0
- pyvolutionary/fireworks/models.py +41 -0
- pyvolutionary/fish_school_search/__init__.py +2 -0
- pyvolutionary/fish_school_search/fish_school_search_optimization.py +142 -0
- pyvolutionary/fish_school_search/models.py +27 -0
- pyvolutionary/flower_pollination_algorithm/__init__.py +2 -0
- pyvolutionary/flower_pollination_algorithm/flower_pollination_algorithm_optimization.py +46 -0
- pyvolutionary/flower_pollination_algorithm/models.py +29 -0
- pyvolutionary/forest_algorithm/__init__.py +2 -0
- pyvolutionary/forest_algorithm/forest_optimization_algorithm.py +121 -0
- pyvolutionary/forest_algorithm/models.py +53 -0
- pyvolutionary/fox/__init__.py +2 -0
- pyvolutionary/fox/fox_optimization.py +43 -0
- pyvolutionary/fox/models.py +29 -0
- pyvolutionary/genetic_algorithm/__init__.py +2 -0
- pyvolutionary/genetic_algorithm/genetic_algorithm_optimization.py +167 -0
- pyvolutionary/genetic_algorithm/models.py +21 -0
- pyvolutionary/grasshopper/__init__.py +2 -0
- pyvolutionary/grasshopper/grasshopper_optimization_algorithm.py +73 -0
- pyvolutionary/grasshopper/models.py +29 -0
- pyvolutionary/grey_wolf/__init__.py +2 -0
- pyvolutionary/grey_wolf/grey_wolf_optimization.py +62 -0
- pyvolutionary/grey_wolf/models.py +9 -0
- pyvolutionary/harmony_search/__init__.py +2 -0
- pyvolutionary/harmony_search/harmony_search_optimization.py +64 -0
- pyvolutionary/harmony_search/models.py +29 -0
- pyvolutionary/helpers.py +290 -0
- pyvolutionary/imperialist_competitive/__init__.py +2 -0
- pyvolutionary/imperialist_competitive/classes.py +119 -0
- pyvolutionary/imperialist_competitive/imperialist_competitive_optimization.py +198 -0
- pyvolutionary/imperialist_competitive/models.py +47 -0
- pyvolutionary/invasive_weed/__init__.py +2 -0
- pyvolutionary/invasive_weed/invasive_weed_optimization.py +52 -0
- pyvolutionary/invasive_weed/models.py +51 -0
- pyvolutionary/krill_herd/__init__.py +2 -0
- pyvolutionary/krill_herd/krill_herd_optimization.py +179 -0
- pyvolutionary/krill_herd/models.py +62 -0
- pyvolutionary/levi_jaya_swarm/__init__.py +2 -0
- pyvolutionary/levi_jaya_swarm/levy_flight_jaya_swarm_optimization.py +40 -0
- pyvolutionary/levi_jaya_swarm/models.py +9 -0
- pyvolutionary/models.py +130 -0
- pyvolutionary/monarch_butterfly/__init__.py +2 -0
- pyvolutionary/monarch_butterfly/models.py +25 -0
- pyvolutionary/monarch_butterfly/monarch_butterfly_optimization.py +104 -0
- pyvolutionary/mountain_gazelle/__init__.py +2 -0
- pyvolutionary/mountain_gazelle/models.py +9 -0
- pyvolutionary/mountain_gazelle/mountain_gazelle_optimization.py +118 -0
- pyvolutionary/osprey/__init__.py +2 -0
- pyvolutionary/osprey/models.py +9 -0
- pyvolutionary/osprey/osprey_optimization.py +54 -0
- pyvolutionary/particle_swarm/__init__.py +2 -0
- pyvolutionary/particle_swarm/models.py +40 -0
- pyvolutionary/particle_swarm/particle_swarm_optimization.py +78 -0
- pyvolutionary/pathfinder_algorithm/__init__.py +2 -0
- pyvolutionary/pathfinder_algorithm/models.py +12 -0
- pyvolutionary/pathfinder_algorithm/pathfinder_algorithm_optimization.py +56 -0
- pyvolutionary/pelican/__init__.py +2 -0
- pyvolutionary/pelican/models.py +9 -0
- pyvolutionary/pelican/pelican_optimization.py +46 -0
- pyvolutionary/seagull/__init__.py +2 -0
- pyvolutionary/seagull/models.py +21 -0
- pyvolutionary/seagull/seagull_optimization.py +43 -0
- pyvolutionary/siberian_tiger/__init__.py +2 -0
- pyvolutionary/siberian_tiger/models.py +9 -0
- pyvolutionary/siberian_tiger/siberian_tiger_optimization.py +53 -0
- pyvolutionary/tasmanian_devil/__init__.py +2 -0
- pyvolutionary/tasmanian_devil/models.py +9 -0
- pyvolutionary/tasmanian_devil/tasmanian_devil_optimization.py +69 -0
- pyvolutionary/tests/fixtures.py +21 -0
- pyvolutionary/tests/test_african_vulture_optimization.py +34 -0
- pyvolutionary/tests/test_ant_colony_optimization.py +34 -0
- pyvolutionary/tests/test_aquila_optimization.py +27 -0
- pyvolutionary/tests/test_bacterial_foraging_optimization.py +37 -0
- pyvolutionary/tests/test_bat_optimization.py +34 -0
- pyvolutionary/tests/test_bee_colony_optimization.py +32 -0
- pyvolutionary/tests/test_camel_caravan_optimization.py +37 -0
- pyvolutionary/tests/test_coral_reef_optimization.py +39 -0
- pyvolutionary/tests/test_coyotes_optimization.py +32 -0
- pyvolutionary/tests/test_earthworms_optimization.py +37 -0
- pyvolutionary/tests/test_electromagnetic_field_optimization.py +35 -0
- pyvolutionary/tests/test_elephant_herd_optimization.py +34 -0
- pyvolutionary/tests/test_firefly_swarm_optimization.py +34 -0
- pyvolutionary/tests/test_fireworks_algorithm_optimization.py +36 -0
- pyvolutionary/tests/test_fish_school_search_optimization.py +37 -0
- pyvolutionary/tests/test_flower_pollination_algorithm_optimization.py +33 -0
- pyvolutionary/tests/test_forest_algorithm_optimization.py +36 -0
- pyvolutionary/tests/test_foxes_optimization.py +33 -0
- pyvolutionary/tests/test_genetic_algorithm_optimization.py +32 -0
- pyvolutionary/tests/test_grasshopper_optimization_algorithm.py +33 -0
- pyvolutionary/tests/test_grey_wolf_optimization.py +27 -0
- pyvolutionary/tests/test_harmony_search_optimization.py +33 -0
- pyvolutionary/tests/test_imperialist_competitive_optimization.py +40 -0
- pyvolutionary/tests/test_invasive_weed_optimization.py +34 -0
- pyvolutionary/tests/test_krill_herd_optimization.py +40 -0
- pyvolutionary/tests/test_levy_flight_jaya_swarm_optimization.py +33 -0
- pyvolutionary/tests/test_monarch_butterfly_optimization.py +33 -0
- pyvolutionary/tests/test_mountain_gazelle_optimization.py +27 -0
- pyvolutionary/tests/test_osprey_optimization.py +26 -0
- pyvolutionary/tests/test_particle_swarm_optimization.py +34 -0
- pyvolutionary/tests/test_pathfinder_algorithm_optimization.py +27 -0
- pyvolutionary/tests/test_pelican_optimization.py +27 -0
- pyvolutionary/tests/test_seagull_optimization.py +32 -0
- pyvolutionary/tests/test_siberian_tiger_optimization.py +27 -0
- pyvolutionary/tests/test_task_bounds.py +9 -0
- pyvolutionary/tests/test_tasmanian_devil_optimization.py +27 -0
- pyvolutionary/tests/test_utils.py +43 -0
- pyvolutionary/tests/test_virus_colony_search_optimization.py +33 -0
- pyvolutionary/tests/test_walrus_optimization.py +27 -0
- pyvolutionary/tests/test_whales_optimization.py +27 -0
- pyvolutionary/tests/test_wildebeest_herd_optimization.py +41 -0
- pyvolutionary/tests/test_zebra_optimization.py +27 -0
- pyvolutionary/utils.py +94 -0
- pyvolutionary/virus_colony_search/__init__.py +2 -0
- pyvolutionary/virus_colony_search/models.py +29 -0
- pyvolutionary/virus_colony_search/virus_colony_search_optimization.py +99 -0
- pyvolutionary/walrus/__init__.py +2 -0
- pyvolutionary/walrus/models.py +9 -0
- pyvolutionary/walrus/walrus_optimization.py +43 -0
- pyvolutionary/whales/__init__.py +2 -0
- pyvolutionary/whales/models.py +12 -0
- pyvolutionary/whales/whales_optimization.py +56 -0
- pyvolutionary/wildebeest_herd/__init__.py +2 -0
- pyvolutionary/wildebeest_herd/models.py +94 -0
- pyvolutionary/wildebeest_herd/wildebeest_herd_optimization.py +143 -0
- pyvolutionary/zebra/__init__.py +2 -0
- pyvolutionary/zebra/models.py +9 -0
- pyvolutionary/zebra/zebra_optimization.py +53 -0
- pyvolutionary-1.0.1.dist-info/METADATA +350 -0
- pyvolutionary-1.0.1.dist-info/RECORD +173 -0
- 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,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)
|
pyvolutionary/models.py
ADDED
|
@@ -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,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,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,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,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
|