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