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,96 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..helpers import (
|
|
4
|
+
best_agent,
|
|
5
|
+
parse_obj_doc, # type: ignore
|
|
6
|
+
)
|
|
7
|
+
from ..abstract import OptimizationAbstract
|
|
8
|
+
from .models import Camel, CamelCaravanOptimizationConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CamelCaravanOptimization(OptimizationAbstract):
|
|
12
|
+
"""
|
|
13
|
+
Implementation of the Camels Optimization algorithm.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
config (CamelCaravanOptimizationConfig): an instance of CamelCaravanOptimizationConfig class.
|
|
17
|
+
{parse_obj_doc(CamelCaravanOptimizationConfig)}
|
|
18
|
+
|
|
19
|
+
Bibliography
|
|
20
|
+
----------
|
|
21
|
+
[1] Ali, Ramzy. (2016). Novel Optimization Algorithm Inspired by Camel Traveling Behavior.
|
|
22
|
+
Iraq J. Electrical and Electronic Engineering. 12. 167-177.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, config: CamelCaravanOptimizationConfig, debug: bool | None = False):
|
|
25
|
+
super().__init__(config, debug)
|
|
26
|
+
|
|
27
|
+
def _init_agent(
|
|
28
|
+
self,
|
|
29
|
+
position: list[float] | np.ndarray | None = None,
|
|
30
|
+
endurance: float | None = None,
|
|
31
|
+
supply: float | None = None,
|
|
32
|
+
) -> Camel:
|
|
33
|
+
agent = super()._init_agent(position=position)
|
|
34
|
+
|
|
35
|
+
return Camel(
|
|
36
|
+
**agent.model_dump(),
|
|
37
|
+
endurance=endurance if endurance is not None else self._config.endurance,
|
|
38
|
+
supply=supply if supply is not None else self._config.supply,
|
|
39
|
+
temperature=np.random.uniform(*self._config.temperatures),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def __walk__(self, camel: Camel) -> Camel:
|
|
43
|
+
"""
|
|
44
|
+
Move the camel in search space. This function is used to move Camel.
|
|
45
|
+
:param camel: the camel to consider.
|
|
46
|
+
:return: the moved camel.
|
|
47
|
+
:rtype: Camel
|
|
48
|
+
"""
|
|
49
|
+
bc_position = np.array(best_agent(self._population).position)
|
|
50
|
+
|
|
51
|
+
min_temperature, max_temperature = self._config.temperatures
|
|
52
|
+
|
|
53
|
+
temperature = np.random.uniform(min_temperature, max_temperature)
|
|
54
|
+
supply = camel.supply * (1 - self._config.burden_factor * camel.steps / self._config.max_cycles)
|
|
55
|
+
endurance = camel.endurance * (1 - temperature / max_temperature) * (1 - camel.steps / self._config.max_cycles)
|
|
56
|
+
|
|
57
|
+
new_position = camel.position + np.random.uniform(-1, 1) * (
|
|
58
|
+
1 - (endurance / self._config.endurance)
|
|
59
|
+
) * np.exp(1 - supply / self._config.supply) * (bc_position - np.array(camel.position))
|
|
60
|
+
if self._is_valid_position(new_position):
|
|
61
|
+
return self._init_agent(position=new_position, endurance=endurance, supply=supply)
|
|
62
|
+
|
|
63
|
+
return self._init_agent(position=camel.position, endurance=endurance, supply=supply)
|
|
64
|
+
|
|
65
|
+
def __oasis__(self, camel: Camel, past_cost: float) -> Camel:
|
|
66
|
+
"""
|
|
67
|
+
Apply oasis function to Camel. This function is used to refill supply and endurance of Camel.
|
|
68
|
+
:param camel: the camel to consider.
|
|
69
|
+
:param past_cost: the past cost of Camel.
|
|
70
|
+
:return: the camel after oasis function.
|
|
71
|
+
"""
|
|
72
|
+
if np.random.random() <= (1 - self._config.visibility) and camel.cost <= past_cost:
|
|
73
|
+
camel.supply = self._config.supply
|
|
74
|
+
camel.endurance = self._config.endurance
|
|
75
|
+
return camel
|
|
76
|
+
|
|
77
|
+
def __life_cycle__(self, camel: Camel, past_cost: float) -> Camel:
|
|
78
|
+
"""
|
|
79
|
+
Apply life cycle to Camel. This function is used to kill Camel.
|
|
80
|
+
:param camel: the camel to consider.
|
|
81
|
+
:param past_cost: the past cost of Camel.
|
|
82
|
+
:return: the camel after life cycle.
|
|
83
|
+
"""
|
|
84
|
+
if past_cost <= self._config.death_rate * camel.cost:
|
|
85
|
+
return self._init_agent(endurance=self._config.endurance, supply=self._config.supply)
|
|
86
|
+
|
|
87
|
+
camel.steps += 1
|
|
88
|
+
return camel
|
|
89
|
+
|
|
90
|
+
def optimization_step(self):
|
|
91
|
+
for idx in range(0, self._config.population_size):
|
|
92
|
+
camel = self._population[idx].model_copy()
|
|
93
|
+
past_cost = camel.cost
|
|
94
|
+
|
|
95
|
+
camel = self.__life_cycle__(self.__walk__(camel), past_cost)
|
|
96
|
+
self._population[idx] = self.__oasis__(camel, past_cost)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pydantic import field_validator
|
|
3
|
+
|
|
4
|
+
from ..models import Agent, BaseOptimizationConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Camel(Agent):
|
|
8
|
+
endurance: float
|
|
9
|
+
supply: float
|
|
10
|
+
temperature: float | None = None
|
|
11
|
+
steps: int = 1
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CamelCaravanOptimizationConfig(BaseOptimizationConfig):
|
|
15
|
+
"""
|
|
16
|
+
Configuration class of the Camel Caravan algorithm.
|
|
17
|
+
burden_factor (float): [0, 1], burden factor.\n
|
|
18
|
+
death_rate (float): [0, 1], dying rate.\n
|
|
19
|
+
visibility (float): view range of camel.\n
|
|
20
|
+
supply (float): (0, Inf), initial supply.\n
|
|
21
|
+
endurance (float): (0, Inf), initial endurance.\n
|
|
22
|
+
temperatures (list[float]): minimum and maximum temperatures.\n
|
|
23
|
+
"""
|
|
24
|
+
burden_factor: float
|
|
25
|
+
death_rate: float
|
|
26
|
+
visibility: float
|
|
27
|
+
supply: float
|
|
28
|
+
endurance: float
|
|
29
|
+
temperatures: list[float]
|
|
30
|
+
|
|
31
|
+
@field_validator('burden_factor')
|
|
32
|
+
def check_burden_factor(cls, v):
|
|
33
|
+
if not 0 <= v <= 1:
|
|
34
|
+
raise ValueError('burden_factor must be in range [0, 1]')
|
|
35
|
+
return v
|
|
36
|
+
|
|
37
|
+
@field_validator('death_rate')
|
|
38
|
+
def check_death_rate(cls, v):
|
|
39
|
+
if not 0 <= v <= 1:
|
|
40
|
+
raise ValueError('death_rate must be in range [0, 1]')
|
|
41
|
+
return v
|
|
42
|
+
|
|
43
|
+
@field_validator('supply')
|
|
44
|
+
def check_supply(cls, v):
|
|
45
|
+
if not 0 < v:
|
|
46
|
+
raise ValueError('supply must be greater than 0')
|
|
47
|
+
return v
|
|
48
|
+
|
|
49
|
+
@field_validator('endurance')
|
|
50
|
+
def check_endurance(cls, v):
|
|
51
|
+
if not 0 < v:
|
|
52
|
+
raise ValueError('endurance must be greater than 0')
|
|
53
|
+
return v
|
|
54
|
+
|
|
55
|
+
@field_validator('temperatures')
|
|
56
|
+
def check_temperatures(cls, v):
|
|
57
|
+
if not len(v) == 2:
|
|
58
|
+
raise ValueError('temperatures must be a list of length 2')
|
|
59
|
+
if not v[0] < v[1]:
|
|
60
|
+
raise ValueError('temperatures[0] must be less than temperatures[1]')
|
|
61
|
+
return v
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..helpers import (
|
|
4
|
+
sort_and_trim,
|
|
5
|
+
parse_obj_doc # type: ignore
|
|
6
|
+
)
|
|
7
|
+
from ..abstract import OptimizationAbstract
|
|
8
|
+
from .models import Coral, CoralReefOptimizationConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CoralReefOptimization(OptimizationAbstract):
|
|
12
|
+
"""
|
|
13
|
+
Implementation of the Coral Reef Optimization algorithm.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
config (CoralReefOptimizationConfig): an instance of CoralReefOptimizationConfig class.
|
|
17
|
+
{parse_obj_doc(CoralReefOptimizationConfig)}
|
|
18
|
+
|
|
19
|
+
Bibliography
|
|
20
|
+
----------
|
|
21
|
+
[1] Salcedo-Sanz, S., Del Ser, J., Landa-Torres, I., Gil-López, S. and Portilla-Figueras, J.A., 2014.
|
|
22
|
+
The coral reefs optimization algorithm: a novel metaheuristic for efficiently solving optimization problems.
|
|
23
|
+
The Scientific World Journal, 2014.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, config: CoralReefOptimizationConfig, debug: bool | None = False):
|
|
26
|
+
super().__init__(config, debug)
|
|
27
|
+
_, self.__G1 = self._config.gamma
|
|
28
|
+
self.__alpha = 10 * self._config.Pd / self._config.max_cycles
|
|
29
|
+
self.__gamma = 10 * (self._config.gamma[1] - self._config.gamma[0]) / self._config.max_cycles
|
|
30
|
+
self.__num_occupied = int(self._config.population_size / (1 + self._config.po))
|
|
31
|
+
self.__dyn_Pd = 0
|
|
32
|
+
self.__occupied_list = np.zeros(self._config.population_size, dtype=int)
|
|
33
|
+
self.__occupied_idx_list = np.random.choice(
|
|
34
|
+
list(range(self._config.population_size)), self.__num_occupied, replace=False
|
|
35
|
+
)
|
|
36
|
+
self.__occupied_list[self.__occupied_idx_list] = 1
|
|
37
|
+
self.__reset_count = 0
|
|
38
|
+
|
|
39
|
+
def __broadcast_spawning_brooding__(self) -> list[Coral]:
|
|
40
|
+
"""
|
|
41
|
+
Broadcast spawning and brooding process. This process is done in two steps: 1a and 1b. In step 1a, the corals
|
|
42
|
+
that are going to broadcast spawning are selected. In step 1b, the corals that are going to brooding are
|
|
43
|
+
selected. The corals that are going to broadcast spawning are selected randomly from the occupied corals. The
|
|
44
|
+
corals that are going to brooding are selected randomly from the occupied corals that are not selected for
|
|
45
|
+
broadcast spawning.
|
|
46
|
+
:return: the list of larvae.
|
|
47
|
+
:rtype: list[Coral]
|
|
48
|
+
"""
|
|
49
|
+
def gaussian_mutation(position):
|
|
50
|
+
return self._correct_position(np.where(
|
|
51
|
+
np.random.random(self._task.space_dimension) < self._config.GCR,
|
|
52
|
+
position + self.__G1 * self._bandwidth() * np.random.normal(0, 1, self._task.space_dimension),
|
|
53
|
+
position
|
|
54
|
+
))
|
|
55
|
+
|
|
56
|
+
def multi_point_cross(pos1, pos2):
|
|
57
|
+
p1, p2 = np.random.choice(list(range(0, len(pos1))), 2, replace=False)
|
|
58
|
+
start, end = min(p1, p2), max(p1, p2)
|
|
59
|
+
return self._correct_position(
|
|
60
|
+
np.concatenate((pos1[:start], pos2[start:end], pos1[end:]), axis=0)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Step 1a
|
|
64
|
+
selected_corals = np.random.choice(
|
|
65
|
+
self.__occupied_idx_list, int(len(self.__occupied_idx_list) * self._config.Fb), replace=False
|
|
66
|
+
)
|
|
67
|
+
larvae = [Coral(**self._init_agent(gaussian_mutation(self._population[idx].position)).model_dump())
|
|
68
|
+
for idx in self.__occupied_idx_list if idx not in selected_corals]
|
|
69
|
+
|
|
70
|
+
# Step 1b
|
|
71
|
+
while len(selected_corals) >= 2:
|
|
72
|
+
id1, id2 = np.random.choice(range(len(selected_corals)), 2, replace=False)
|
|
73
|
+
agent = Coral(**self._init_agent(multi_point_cross(
|
|
74
|
+
self._population[selected_corals[id1]].position,
|
|
75
|
+
self._population[selected_corals[id2]].position
|
|
76
|
+
)).model_dump())
|
|
77
|
+
larvae.append(agent)
|
|
78
|
+
selected_corals = np.delete(selected_corals, [id1, id2])
|
|
79
|
+
return larvae
|
|
80
|
+
|
|
81
|
+
def __larvae_setting__(self, larvae: list[Coral]):
|
|
82
|
+
"""
|
|
83
|
+
Larvae setting process. This process is done in two steps: 2a and 2b. In step 2a, the larvae are evaluated and
|
|
84
|
+
the larvae that are going to be settled are selected. In step 2b, the larvae that are going to be settled are
|
|
85
|
+
placed in the occupied corals.
|
|
86
|
+
:param larvae: the list of larvae.
|
|
87
|
+
"""
|
|
88
|
+
def find_available_slot(lc: Coral) -> tuple[int | None, bool]:
|
|
89
|
+
for i in range(self._config.n_trials):
|
|
90
|
+
p = np.random.randint(0, self._config.population_size - 1)
|
|
91
|
+
if self.__occupied_list[p] == 0:
|
|
92
|
+
return p, True
|
|
93
|
+
if lc.cost < self._population[p].cost:
|
|
94
|
+
return p, False
|
|
95
|
+
return None, False
|
|
96
|
+
|
|
97
|
+
def update(p: int, upd: bool, lc: Coral):
|
|
98
|
+
self._population[p] = lc # update population
|
|
99
|
+
if upd:
|
|
100
|
+
self.__occupied_idx_list = np.append(self.__occupied_idx_list, p) # update occupied id
|
|
101
|
+
self.__occupied_list[p] = 1 # update occupied list
|
|
102
|
+
|
|
103
|
+
# trial to land on a square of reefs
|
|
104
|
+
res = list(map(find_available_slot, larvae))
|
|
105
|
+
for idx, (pdx, upd_occupied) in enumerate(res):
|
|
106
|
+
if pdx is not None:
|
|
107
|
+
update(pdx, upd_occupied, larvae[idx])
|
|
108
|
+
|
|
109
|
+
def __reef_cost__(self, idx):
|
|
110
|
+
return self._population[idx].cost
|
|
111
|
+
|
|
112
|
+
def optimization_step(self):
|
|
113
|
+
# broadcast spawning brooding
|
|
114
|
+
larvae = self.__broadcast_spawning_brooding__()
|
|
115
|
+
self.__larvae_setting__(larvae)
|
|
116
|
+
|
|
117
|
+
# asexual reproduction
|
|
118
|
+
pop_best = sort_and_trim(
|
|
119
|
+
[self._population[idx] for idx in self.__occupied_idx_list],
|
|
120
|
+
int(len(self.__occupied_idx_list) * self._config.Fa)
|
|
121
|
+
)
|
|
122
|
+
self.__larvae_setting__(pop_best)
|
|
123
|
+
|
|
124
|
+
# depredation
|
|
125
|
+
if np.random.random() < self.__dyn_Pd:
|
|
126
|
+
num_depredation = int(len(self.__occupied_idx_list) * self._config.Fd)
|
|
127
|
+
idx_list_sorted = sorted(self.__occupied_idx_list, key=self.__reef_cost__)
|
|
128
|
+
for idx in idx_list_sorted[-num_depredation:]:
|
|
129
|
+
self.__occupied_list[idx] = 0
|
|
130
|
+
if self.__dyn_Pd <= self._config.Pd:
|
|
131
|
+
self.__dyn_Pd += self.__alpha
|
|
132
|
+
if self.__G1 >= self._config.gamma[0]:
|
|
133
|
+
self.__G1 -= self.__gamma
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from pydantic import field_validator
|
|
2
|
+
|
|
3
|
+
from ..models import Agent, BaseOptimizationConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Coral(Agent):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CoralReefOptimizationConfig(BaseOptimizationConfig):
|
|
11
|
+
"""
|
|
12
|
+
Configuration class of the Coral Reef algorithm.
|
|
13
|
+
po (float): [0.2, 0.5], the rate between free/occupied at the beginning.\n
|
|
14
|
+
Fb (float): [0.6, 0.9], BroadcastSpawner/ExistingCorals rate.\n
|
|
15
|
+
Fa (float): [0.05, 0.3], fraction of corals duplicates its self and tries to settle in a different part of the
|
|
16
|
+
reef.\n
|
|
17
|
+
Fd (float): [0.05, 0.5], fraction of the worse health corals in reef will be applied depredation.\n
|
|
18
|
+
Pd (float): [0.1, 0.7], Probability of depredation.\n
|
|
19
|
+
GCR (float): [0.05, 0.2], probability for mutation process.\n
|
|
20
|
+
gamma (list[float]): [0.01, 0.1], [0.1, 0.5], min and max factors for mutation process.\n
|
|
21
|
+
n_trials (int): [2, 10], number of attempts for a larvae to set in the reef.
|
|
22
|
+
"""
|
|
23
|
+
po: float
|
|
24
|
+
Fb: float
|
|
25
|
+
Fa: float
|
|
26
|
+
Fd: float
|
|
27
|
+
Pd: float
|
|
28
|
+
GCR: float
|
|
29
|
+
gamma: list[float]
|
|
30
|
+
n_trials: int
|
|
31
|
+
|
|
32
|
+
@field_validator('po')
|
|
33
|
+
def check_po(cls, v):
|
|
34
|
+
if not 0.2 <= v <= 0.5:
|
|
35
|
+
raise ValueError('po must be in range [0.2, 0.5]')
|
|
36
|
+
return v
|
|
37
|
+
|
|
38
|
+
@field_validator('Fb')
|
|
39
|
+
def check_Fb(cls, v):
|
|
40
|
+
if not 0.6 <= v <= 0.9:
|
|
41
|
+
raise ValueError('Fb must be in range [0.6, 0.9]')
|
|
42
|
+
return v
|
|
43
|
+
|
|
44
|
+
@field_validator('Fa')
|
|
45
|
+
def check_Fa(cls, v):
|
|
46
|
+
if not 0.05 <= v <= 0.3:
|
|
47
|
+
raise ValueError('Fa must be in range [0.05, 0.3]')
|
|
48
|
+
return v
|
|
49
|
+
|
|
50
|
+
@field_validator('Fd')
|
|
51
|
+
def check_Fd(cls, v):
|
|
52
|
+
if not 0.05 <= v <= 0.5:
|
|
53
|
+
raise ValueError('Fd must be in range [0.05, 0.5]')
|
|
54
|
+
return v
|
|
55
|
+
|
|
56
|
+
@field_validator('Pd')
|
|
57
|
+
def check_Pd(cls, v):
|
|
58
|
+
if not 0.1 <= v <= 0.7:
|
|
59
|
+
raise ValueError('Pd must be in range [0.1, 0.7]')
|
|
60
|
+
return v
|
|
61
|
+
|
|
62
|
+
@field_validator('GCR')
|
|
63
|
+
def check_GCR(cls, v):
|
|
64
|
+
if not 0.05 <= v <= 0.2:
|
|
65
|
+
raise ValueError('GCR must be in range [0.05, 0.2]')
|
|
66
|
+
return v
|
|
67
|
+
|
|
68
|
+
@field_validator('gamma')
|
|
69
|
+
def check_gamma(cls, v):
|
|
70
|
+
gamma_min, gamma_max = v
|
|
71
|
+
if not 0.01 <= gamma_min <= 0.1:
|
|
72
|
+
raise ValueError('gamma_min must be in range [0.01, 0.1]')
|
|
73
|
+
if not 0.1 <= gamma_max <= 0.5:
|
|
74
|
+
raise ValueError('gamma_max must be in range [0.1, 0.5]')
|
|
75
|
+
return v
|
|
76
|
+
|
|
77
|
+
@field_validator('n_trials')
|
|
78
|
+
def check_n_trials(cls, v):
|
|
79
|
+
if not 2 <= v <= 10:
|
|
80
|
+
raise ValueError('n_trials must be in range [2, 10]')
|
|
81
|
+
return v
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..helpers import (
|
|
4
|
+
generate_group_population,
|
|
5
|
+
sort_by_cost,
|
|
6
|
+
parse_obj_doc, # type: ignore
|
|
7
|
+
)
|
|
8
|
+
from ..abstract import OptimizationAbstract
|
|
9
|
+
from .models import Coyote, CoyotesOptimizationConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CoyotesOptimization(OptimizationAbstract):
|
|
13
|
+
"""
|
|
14
|
+
Implementation of the Coyotes Optimization algorithm.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
config (CoyotesOptimizationConfig): an instance of CoyotesOptimizationConfig class.
|
|
18
|
+
{parse_obj_doc(CoyotesOptimizationConfig)}
|
|
19
|
+
|
|
20
|
+
Bibliography
|
|
21
|
+
----------
|
|
22
|
+
[1] Pierezan, J. and Coelho, L.D.S., 2018, July. Coyote optimization algorithm: a new metaheuristic
|
|
23
|
+
for global optimization problems. In 2018 IEEE congress on evolutionary computation (CEC) (pp. 1-8). IEEE.
|
|
24
|
+
"""
|
|
25
|
+
def __init__(self, config: CoyotesOptimizationConfig, debug: bool | None = False):
|
|
26
|
+
super().__init__(config, debug)
|
|
27
|
+
self.__n_packs: int = 0
|
|
28
|
+
self.__ps: float = 0.
|
|
29
|
+
self.__p_leave: float = 0.
|
|
30
|
+
self.__packs: list[list[Coyote]] = []
|
|
31
|
+
|
|
32
|
+
def before_initialization(self):
|
|
33
|
+
self.__n_packs = int(self._config.population_size / self._config.num_coyotes)
|
|
34
|
+
self.__ps = 1. / self._task.space_dimension # Probability of selecting a dimension
|
|
35
|
+
self.__p_leave = 0.005 * (self._config.num_coyotes ** 2) # Probability of leaving a pack
|
|
36
|
+
|
|
37
|
+
def _init_agent(self, position: list[float] | np.ndarray | None = None, age: int | None = None) -> Coyote:
|
|
38
|
+
agent = super()._init_agent(position)
|
|
39
|
+
return Coyote(**agent.model_dump(), age=age if age is not None else 0)
|
|
40
|
+
|
|
41
|
+
def optimization_step(self):
|
|
42
|
+
self.__packs = generate_group_population(self._population, self.__n_packs, self._config.num_coyotes)
|
|
43
|
+
|
|
44
|
+
for p in range(self.__n_packs):
|
|
45
|
+
pack = self.__packs[p]
|
|
46
|
+
|
|
47
|
+
# get the coyotes that belong to each pack and compute the social tendency of the pack (Eq. 6)
|
|
48
|
+
sort_by_cost(pack)
|
|
49
|
+
tendency = np.mean(np.array([coyote.position for coyote in pack]), axis=0)
|
|
50
|
+
|
|
51
|
+
# update social condition of coyotes (Eq. 8)
|
|
52
|
+
for i, coyote in enumerate(pack):
|
|
53
|
+
rc1, rc2 = np.random.choice(list(set(range(0, self._config.num_coyotes)) - {i}), 2, replace=False)
|
|
54
|
+
|
|
55
|
+
# try to update the social condition according to the alpha and the pack tendency (Eq. 12)
|
|
56
|
+
pos_new = np.array(coyote.position) + np.random.random() * (
|
|
57
|
+
np.array(pack[0].position) - np.array(pack[rc1].position)
|
|
58
|
+
) + np.random.random() * (tendency - np.array(pack[rc2].position))
|
|
59
|
+
|
|
60
|
+
# keep the coyotes in the search space (optimization problem constraint) and evaluate the new position
|
|
61
|
+
# it means to evaluate the new social condition (Eq. 13) and apply the adaptation (Eq. 14)
|
|
62
|
+
pack[i] = self._greedy_select_agent(
|
|
63
|
+
coyote, self._init_agent(self._correct_position(pos_new), coyote.age)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# birth of a new coyote from random parents (Eq. 7 and Alg. 1)
|
|
67
|
+
id_parent1, id_parent2 = np.random.choice(list(range(0, self._config.num_coyotes)), 2, replace=False)
|
|
68
|
+
# generate the coyote considering intrinsic and extrinsic influence, with eventual noise
|
|
69
|
+
pup_pos = np.array(np.where(
|
|
70
|
+
np.random.random(self._task.space_dimension) < (1. - self.__ps) / 2.,
|
|
71
|
+
pack[id_parent1].position,
|
|
72
|
+
pack[id_parent2].position
|
|
73
|
+
)) * np.random.normal(0, 1)
|
|
74
|
+
pup = self._init_agent(self._correct_position(pup_pos.tolist()))
|
|
75
|
+
|
|
76
|
+
# Verify if the pup will survive
|
|
77
|
+
sort_by_cost(pack)
|
|
78
|
+
# find index of element has cost larger than new child: if existing, new child is good
|
|
79
|
+
if pup.cost < pack[-1].cost:
|
|
80
|
+
# replace the worst element by new child, New born child with age = 0
|
|
81
|
+
pack = sorted(pack, key=lambda x: x.age)
|
|
82
|
+
pack[-1] = pup
|
|
83
|
+
self.__packs[p] = [coyote.model_copy() for coyote in pack]
|
|
84
|
+
|
|
85
|
+
# a coyote can leave a pack and enter to another pack (Eq. 4)
|
|
86
|
+
if self.__n_packs > 1 and np.random.random() < self.__p_leave:
|
|
87
|
+
id_pack1, id_pack2 = np.random.choice(list(range(0, self.__n_packs)), 2, replace=False)
|
|
88
|
+
id1, id2 = np.random.choice(list(range(0, self._config.num_coyotes)), 2, replace=False)
|
|
89
|
+
self.__packs[id_pack1][id1], self.__packs[id_pack2][id2] = (
|
|
90
|
+
self.__packs[id_pack2][id2], self.__packs[id_pack1][id1]
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Update coyotes ages
|
|
94
|
+
self._population = [
|
|
95
|
+
self._init_agent(position=coyote.position, age=coyote.age + 1) for pack in self.__packs for coyote in pack
|
|
96
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pydantic import model_validator
|
|
2
|
+
|
|
3
|
+
from ..models import Agent, BaseOptimizationConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Coyote(Agent):
|
|
7
|
+
age: int
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CoyotesOptimizationConfig(BaseOptimizationConfig):
|
|
11
|
+
"""
|
|
12
|
+
Configuration class of the Coyotes Optimization algorithm.
|
|
13
|
+
num_coyotes (int): [2, int(population_size/2)], the number of coyotes per group.
|
|
14
|
+
"""
|
|
15
|
+
num_coyotes: int
|
|
16
|
+
|
|
17
|
+
@model_validator(mode="after")
|
|
18
|
+
def validate_keep(self) -> "CoyotesOptimizationConfig":
|
|
19
|
+
if not 2 <= self.num_coyotes <= int(self.population_size / 2):
|
|
20
|
+
raise ValueError(f"num_coyotes must be greater than 2 and lower than population_size / 2. "
|
|
21
|
+
f"Got {self.num_coyotes}")
|
|
22
|
+
return self
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from ..helpers import (
|
|
4
|
+
best_agents,
|
|
5
|
+
sort_by_cost,
|
|
6
|
+
parse_obj_doc, # type: ignore
|
|
7
|
+
)
|
|
8
|
+
from ..abstract import OptimizationAbstract
|
|
9
|
+
from .models import Earthworm, EarthwormsOptimizationConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EarthwormsOptimization(OptimizationAbstract):
|
|
13
|
+
"""
|
|
14
|
+
Implementation of the Earthworms Optimization algorithm.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
config (EarthwormsOptimizationConfig): an instance of EarthwormsOptimizationConfig class.
|
|
18
|
+
{parse_obj_doc(EarthwormsOptimizationConfig)}
|
|
19
|
+
|
|
20
|
+
Bibliography
|
|
21
|
+
----------
|
|
22
|
+
[1] Wang, Gai-Ge & Deb, Suash & Coelho, Leandro. (2015). Earthworm optimization algorithm: a bio-inspired
|
|
23
|
+
metaheuristic algorithm for global optimization problems. International Journal of Bio-Inspired Computation.
|
|
24
|
+
DOI: 10.1504/IJBIC.2015.10004283.
|
|
25
|
+
"""
|
|
26
|
+
def __init__(self, config: EarthwormsOptimizationConfig, debug: bool | None = False):
|
|
27
|
+
super().__init__(config, debug)
|
|
28
|
+
self.__chrome_keep: list[Earthworm] = []
|
|
29
|
+
self.__dyn_beta = self._config.beta
|
|
30
|
+
|
|
31
|
+
def __evolve__(self):
|
|
32
|
+
"""
|
|
33
|
+
The earthworm optimization algorithm process. It is composed of two reproduction operators and a mutation
|
|
34
|
+
operator. The reproduction operators are used to generate new individuals, and the mutation operator is used
|
|
35
|
+
to improve the individuals. The reproduction operators are: (1) the first way of reproducing, and (2) the
|
|
36
|
+
second way of reproducing. The mutation operator is: Cauchy's mutation (CM). The reproduction operators and
|
|
37
|
+
the mutation operator are applied to all individuals in the population.
|
|
38
|
+
:return:
|
|
39
|
+
"""
|
|
40
|
+
alpha = self._config.alpha
|
|
41
|
+
beta = self.__dyn_beta
|
|
42
|
+
|
|
43
|
+
n_chromes = self._config.population_size
|
|
44
|
+
keep = self._config.keep
|
|
45
|
+
for idx in range(0, self._config.population_size):
|
|
46
|
+
position = np.array(self._population[idx].position)
|
|
47
|
+
|
|
48
|
+
# reproduction 1: the first way of reproducing
|
|
49
|
+
x_t1 = self._sum_bounds() - alpha * np.array(position)
|
|
50
|
+
|
|
51
|
+
# reproduction 2: the second way of reproducing
|
|
52
|
+
x_child = self._population[np.random.randint(0, n_chromes - 1)].position
|
|
53
|
+
if idx >= keep: # select two parents to mate and create two children
|
|
54
|
+
# 80% parents selected from best population
|
|
55
|
+
rng = range(0, int(n_chromes * 0.2)) if np.random.uniform(0, 1) < 0.5 else range(idx, n_chromes)
|
|
56
|
+
idx1, idx2 = np.random.choice(rng, 2, replace=len(rng) < 2)
|
|
57
|
+
r = np.random.rand()
|
|
58
|
+
x_child = (
|
|
59
|
+
r * np.array(self._population[idx2].position) + (1 - r) * np.array(self._population[idx1].position)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
self._population[idx] = self._init_agent(beta * np.array(x_t1) + (1 - beta) * np.array(x_child))
|
|
63
|
+
self.__dyn_beta *= self._config.gamma
|
|
64
|
+
|
|
65
|
+
def __cauchy_mutation__(self):
|
|
66
|
+
"""
|
|
67
|
+
Cauchy's mutation (CM): make sure each individual is legal
|
|
68
|
+
"""
|
|
69
|
+
keep = self._config.keep
|
|
70
|
+
|
|
71
|
+
pos_list = np.array([agent.position for agent in self._population])
|
|
72
|
+
x_mean = np.mean(pos_list, axis=0)
|
|
73
|
+
cauchy_w = self._best_agent.position
|
|
74
|
+
for idx in range(keep, self._config.population_size):
|
|
75
|
+
# Cauchy's mutation (CM)
|
|
76
|
+
cauchy_w = np.where(np.random.rand() < self._config.prob_mutate, x_mean, cauchy_w)
|
|
77
|
+
x_t1 = (cauchy_w + self._best_agent.position) / 2
|
|
78
|
+
# make sure each individual is legal
|
|
79
|
+
self._population[idx] = self._init_agent(x_t1)
|
|
80
|
+
|
|
81
|
+
def __clear_duplicates__(self):
|
|
82
|
+
"""
|
|
83
|
+
Clear duplicates in the population
|
|
84
|
+
"""
|
|
85
|
+
n_chromes = self._config.population_size
|
|
86
|
+
for idx in range(0, n_chromes):
|
|
87
|
+
pos_idx = self._population[idx].position
|
|
88
|
+
|
|
89
|
+
jdx_list = [jdx for jdx, chrome in enumerate(self._population[(idx + 1):], idx + 1) if np.array_equal(
|
|
90
|
+
pos_idx, chrome.position
|
|
91
|
+
)]
|
|
92
|
+
for jdx in jdx_list:
|
|
93
|
+
dimension_to_change = np.random.randint(0, self._task.space_dimension - 1)
|
|
94
|
+
position_jdx = self._population[jdx].position
|
|
95
|
+
position_jdx[dimension_to_change] = self._uniform_coordinates(dimension_to_change)
|
|
96
|
+
self._population[jdx] = self._init_agent(position_jdx)
|
|
97
|
+
|
|
98
|
+
def __elitism__(self, agents: list[Earthworm], keep: int):
|
|
99
|
+
"""
|
|
100
|
+
Elitism operator. Replace the worst individuals with the best ones from the previous cycle.
|
|
101
|
+
:param agents: the agents
|
|
102
|
+
:param keep: the number of best individuals to keep
|
|
103
|
+
"""
|
|
104
|
+
# sorted from best, i.e., minor cost, to worst
|
|
105
|
+
sort_by_cost(self._population)
|
|
106
|
+
population_size = self._config.population_size
|
|
107
|
+
for idx in range(0, keep):
|
|
108
|
+
self._population[population_size - idx - 1] = agents[idx].model_copy()
|
|
109
|
+
|
|
110
|
+
def optimization_step(self):
|
|
111
|
+
# elitism strategy: save the best "keep" earthworms into the self.__chrome_keep
|
|
112
|
+
self.__chrome_keep = best_agents(self._population, self._config.keep)
|
|
113
|
+
|
|
114
|
+
# the earthworm optimization algorithm process
|
|
115
|
+
self.__evolve__()
|
|
116
|
+
|
|
117
|
+
# Cauchy's mutation (CM): make sure each individual is legal and doesn't have duplicates
|
|
118
|
+
self.__cauchy_mutation__()
|
|
119
|
+
|
|
120
|
+
# elitism strategy: save the best earthworms in a temporary array
|
|
121
|
+
self.__elitism__(self.__chrome_keep, self._config.keep)
|
|
122
|
+
|
|
123
|
+
# clear duplicates in the population
|
|
124
|
+
self.__clear_duplicates__()
|