Skip to content

Commit 7326a88

Browse files
authored
Merge pull request #35 from gugarosa/feat/slime_mould_algorithm
chore(optimizers): Adds Slime Mould Algorithm.
2 parents 4c6d5ce + 4616306 commit 7326a88

File tree

4 files changed

+223
-0
lines changed

4 files changed

+223
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from opytimizer.optimizers.science import SMA
2+
3+
# One should declare a hyperparameters object based
4+
# on the desired algorithm that will be used
5+
params = {"z": 0.03}
6+
7+
# Creates a WCA optimizer
8+
o = SMA(params=params)

opytimizer/optimizers/science/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from opytimizer.optimizers.science.moa import MOA
1515
from opytimizer.optimizers.science.mvo import MVO
1616
from opytimizer.optimizers.science.sa import SA
17+
from opytimizer.optimizers.science.sma import SMA
1718
from opytimizer.optimizers.science.teo import TEO
1819
from opytimizer.optimizers.science.two import TWO
1920
from opytimizer.optimizers.science.wca import WCA
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""Slime Mould Algorithm.
2+
"""
3+
4+
from typing import Any, Dict, List, Optional
5+
6+
import numpy as np
7+
8+
import opytimizer.math.random as r
9+
import opytimizer.utils.exception as e
10+
import opytimizer.utils.constant as c
11+
from opytimizer.core import Optimizer
12+
from opytimizer.core.agent import Agent
13+
from opytimizer.core.function import Function
14+
from opytimizer.core.space import Space
15+
from opytimizer.utils import logging
16+
17+
logger = logging.get_logger(__name__)
18+
19+
20+
class SMA(Optimizer):
21+
"""A SMA class, inherited from Optimizer.
22+
23+
This is the designed class to define SMA-related
24+
variables and methods.
25+
26+
References:
27+
S. Li, H. Chen, M. Wang, A. A. Heidari, S. Mirjalili
28+
Slime mould algorithm: A new method for stochastic optimization.
29+
Future Generation Computer Systems (2020).
30+
31+
"""
32+
33+
def __init__(self, params: Optional[Dict[str, Any]] = None) -> None:
34+
"""Initialization method.
35+
36+
Args:
37+
params: Contains key-value parameters to the meta-heuristics.
38+
39+
"""
40+
41+
logger.info("Overriding class: Optimizer -> SMA.")
42+
43+
super(SMA, self).__init__()
44+
45+
self.z = 0.03
46+
47+
self.build(params)
48+
49+
logger.info("Class overrided.")
50+
51+
@property
52+
def z(self) -> float:
53+
"""Probability threshold."""
54+
55+
return self._z
56+
57+
@z.setter
58+
def z(self, z: float) -> None:
59+
if not isinstance(z, (float, int)):
60+
raise e.TypeError("`z` should be a float or integer")
61+
if z < 0:
62+
raise e.ValueError("`z` should be >= 0")
63+
64+
self._z = z
65+
66+
@property
67+
def weight(self) -> np.ndarray:
68+
"""Array of weights."""
69+
70+
return self._weight
71+
72+
@weight.setter
73+
def weight(self, weight: np.ndarray) -> None:
74+
if not isinstance(weight, np.ndarray):
75+
raise e.TypeError("`weight` should be a numpy array")
76+
77+
self._weight = weight
78+
79+
def compile(self, space: Space) -> None:
80+
"""Compiles additional information that is used by this optimizer.
81+
Args:
82+
space: A Space object containing meta-information.
83+
"""
84+
85+
self.weight = np.zeros(
86+
(space.n_agents, space.n_variables, space.n_dimensions)
87+
)
88+
89+
def _update_weight(self, agents: List[Agent]):
90+
"""Updates the weight of slime mould (eq. 2.5).
91+
92+
Args:
93+
agents: List of agents.
94+
95+
"""
96+
97+
best, worst = agents[0].fit, agents[-1].fit
98+
99+
n_agents = len(agents)
100+
101+
for i in range(n_agents):
102+
103+
r1 = r.generate_uniform_random_number(0, 1,
104+
(agents[i].n_variables, agents[i].n_dimensions))
105+
106+
if i <= int(n_agents/2):
107+
self.weight[i] = 1 + r1 * \
108+
np.log10((best - agents[i].fit)/((best - worst)+c.EPSILON) + 1)
109+
else:
110+
self.weight[i] = 1 - r1 * \
111+
np.log10((best - agents[i].fit)/((best - worst)+c.EPSILON) + 1)
112+
113+
def update(self, space: Space, iteration: int, n_iterations: int) -> None:
114+
"""Wraps Slime Mould Algorithm over all agents and variables.
115+
116+
Args:
117+
space: Space containing agents and update-related information.
118+
function: A function object.
119+
120+
"""
121+
122+
space.agents.sort(key=lambda x: x.fit)
123+
124+
self._update_weight(space.agents)
125+
126+
a = np.arctanh(-((iteration+1)/(n_iterations+1)) + 1)
127+
b = 1 - (iteration+1)/(n_iterations+1)
128+
129+
for i, agent in enumerate(space.agents):
130+
131+
r2 = r.generate_uniform_random_number()
132+
133+
if r2 < self.z:
134+
agent.fill_with_uniform()
135+
else:
136+
p = np.tanh(np.abs(agent.fit - space.agents[0].fit))
137+
vb = r.generate_uniform_random_number(-a, a)
138+
vc = r.generate_uniform_random_number(-b, b)
139+
140+
r3 = r.generate_uniform_random_number()
141+
142+
if r3 < p:
143+
k = r.generate_integer_random_number(0, len(space.agents))
144+
l = r.generate_integer_random_number(
145+
0, len(space.agents), exclude_value=k
146+
)
147+
agent.position = space.agents[0].position + vb * \
148+
(self.weight[i] * (space.agents[k].position - space.agents[l].position))
149+
else:
150+
agent.position *= vc
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import numpy as np
2+
3+
from opytimizer.optimizers.science import sma
4+
from opytimizer.spaces import search
5+
6+
def test_sma_params():
7+
params = {"z": 0.03}
8+
9+
new_sma = sma.SMA(params=params)
10+
11+
assert new_sma.z == 0.03
12+
13+
def test_sma_params_setter():
14+
new_sma = sma.SMA()
15+
16+
try:
17+
new_sma.z = "a"
18+
except:
19+
new_sma.z = 0.05
20+
21+
try:
22+
new_sma.z = -1
23+
except:
24+
new_sma.z = 0.05
25+
26+
assert new_sma.z == 0.05
27+
28+
def test_sma_compile():
29+
search_space = search.SearchSpace(
30+
n_agents=2, n_variables=2, lower_bound=[0, 0], upper_bound=[10, 10]
31+
)
32+
33+
new_sma = sma.SMA()
34+
new_sma.compile(search_space)
35+
36+
try:
37+
new_sma.weight = 1
38+
except:
39+
new_sma.weight = np.array([1])
40+
41+
assert new_sma.weight == np.array([1])
42+
43+
def test_sma_update_weight():
44+
def square(x):
45+
return np.sum(x**2)
46+
47+
search_space = search.SearchSpace(
48+
n_agents=10, n_variables=2, lower_bound=[0, 0], upper_bound=[10, 10]
49+
)
50+
51+
new_sma = sma.SMA()
52+
new_sma.compile(search_space)
53+
54+
new_sma._update_weight(search_space.agents)
55+
56+
def test_sma_update():
57+
search_space = search.SearchSpace(
58+
n_agents=10, n_variables=2, lower_bound=[0, 0], upper_bound=[10, 10]
59+
)
60+
61+
new_sma = sma.SMA()
62+
new_sma.compile(search_space)
63+
64+
new_sma.update(search_space, 1, 10)

0 commit comments

Comments
 (0)