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
0 commit comments