Passed
Pull Request — master (#280)
by
unknown
01:14
created

HarrisHawksOptimization.levy_function()   A

Complexity

Conditions 1

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nop 4
dl 0
loc 16
rs 10
c 0
b 0
f 0
1
# encoding=utf8
2
import logging
3
4
from numpy import random as rand, sin, pi, argmin, abs, mean
5
from scipy.special import gamma
6
7
from NiaPy.algorithms.algorithm import Algorithm
8
9
logging.basicConfig()
10
logger = logging.getLogger('NiaPy.algorithms.basic')
11
logger.setLevel('INFO')
12
13
__all__ = ['HarrisHawksOptimization']
14
15
16
17
class HarrisHawksOptimization(Algorithm):
18
    r"""Implementation of Harris Hawks Optimization algorithm.
19
20
    Algorithm:
21
            Harris Hawks Optimization
22
23
    Date:
24
            2020
25
26
    Authors:
27
            Francisco Jose Solis-Munoz
28
29
    License:
30
            MIT
31
32
    Reference paper:
33
            Heidari et al. "Harris hawks optimization: Algorithm and applications". Future Generation Computer Systems. 2019. Vol. 97. 849-872.
34
35
    Attributes:
36
            Name (List[str]): List of strings representing algorithm name.
37
            levy (float): Levy factor.
38
39
    See Also:
40
            * :class:`NiaPy.algorithms.Algorithm`
41
    """
42
    Name = ['HarrisHawksOptimization', 'HHO']
43
44
    def __init__(self, **kwargs):
45
        super(HarrisHawksOptimization, self).__init__(**kwargs)
46
47
    @staticmethod
48
    def algorithmInfo():
49
        r"""Get algorithms information.
50
51
        Returns:
52
                str: Algorithm information.
53
54
        See Also:
55
                * :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
56
        """
57
        return r"""Heidari et al. "Harris hawks optimization: Algorithm and applications". Future Generation Computer Systems. 2019. Vol. 97. 849-872."""
58
59
    @staticmethod
60
    def typeParameters():
61
        r"""Return dict with where key of dict represents parameter name and values represent checking functions for selected parameter.
62
63
        Returns:
64
                Dict[str, Callable]:
65
                        * levy (Callable[[Union[float, int]], bool]): Levy factor.
66
67
        See Also:
68
                * :func:`NiaPy.algorithms.Algorithm.typeParameters`
69
        """
70
        d = Algorithm.typeParameters()
71
        d.update({
72
            'levy': lambda x: isinstance(x, (float, int)) and x > 0,
73
        })
74
        return d
75
76
    def setParameters(self, NP=40, levy=0.01, **ukwargs):
77
        r"""Set the parameters of the algorithm.
78
79
        Args:
80
                levy (Optional[float]): Levy factor.
81
82
        See Also:
83
                * :func:`NiaPy.algorithms.Algorithm.setParameters`
84
        """
85
        Algorithm.setParameters(self, NP=NP, **ukwargs)
86
        self.levy = levy
87
88
    def getParameters(self):
89
        r"""Get parameters of the algorithm.
90
91
        Returns:
92
                Dict[str, Any]
93
        """
94
        d = Algorithm.getParameters(self)
95
        d.update({
96
            'levy': self.levy
97
        })
98
        return d
99
100
    def initPopulation(self, task, rnd=rand):
101
        r"""Initialize the starting population.
102
103
        Parameters:
104
                task (Task): Optimization task
105
106
        Returns:
107
                Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
108
                        1. New population.
109
                        2. New population fitness/function values.
110
111
        See Also:
112
                * :func:`NiaPy.algorithms.Algorithm.initPopulation`
113
        """
114
        Sol, Fitness, d = Algorithm.initPopulation(self, task)
115
        return Sol, Fitness, d
116
117
    def levy_function(self, dims, step=0.01, rnd=rand):
118
        r"""Calculate levy function.
119
    
120
        Parameters:
121
                dim (int): Number of dimensions
122
                step (float): Step of the Levy function
123
    
124
        Returns:
125
                float: The Levy function evaluation
126
        """
127
        beta = 1.5
128
        sigma = (gamma(1 + beta) * sin(pi * beta / 2) / (gamma((1 + beta / 2) * beta * 2.0 ** ((beta - 1) / 2)))) ** (1 / beta)
129
        normal_1 = rnd.normal(0, sigma, size=dims)
130
        normal_2 = rnd.normal(0, 1, size=dims)
131
        result = step * normal_1 / (abs(normal_2) ** (1 / beta))
132
        return result
133
134
    def runIteration(self, task, Sol, Fitness, xb, fxb, **dparams):
135
        r"""Core function of Harris Hawks Optimization.
136
137
        Parameters:
138
                task (Task): Optimization task.
139
                Sol (numpy.ndarray): Current population
140
                Fitness (numpy.ndarray[float]): Current population fitness/funciton values
141
                xb (numpy.ndarray): Current best individual
142
                fxb (float): Current best individual function/fitness value
143
                dparams (Dict[str, Any]): Additional algorithm arguments
144
145
        Returns:
146
                Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]:
147
                        1. New population
148
                        2. New population fitness/function vlues
149
                        3. New global best solution
150
                        4. New global best fitness/objective value
151
        """
152
        # Decreasing energy factor
153
        decreasing_energy_factor = 2 * (1 - task.iters() / task.nGEN)
154
        mean_sol = mean(Sol)
155
        # Update population
156
        for i in range(self.NP):
157
            jumping_energy = self.Rand.uniform(0, 2)
158
            decreasing_energy_random = self.Rand.uniform(-1, 1)
159
            escaping_energy = decreasing_energy_factor * decreasing_energy_random
160
            escaping_energy_abs = abs(escaping_energy)
161
            random_number = self.Rand.rand()
162
            if escaping_energy >= 1 and random_number >= 0.5:
163
                # 0. Exploration: Random tall tree
164
                rhi = self.Rand.randint(0, self.NP)
165
                random_agent = Sol[rhi]
166
                Sol[i] = random_agent - self.Rand.rand() * abs(random_agent - 2 * self.Rand.rand() * Sol[i])
167
            elif escaping_energy_abs >= 1 and random_number < 0.5:
168
                # 1. Exploration: Family members mean
169
                Sol[i] = (xb - mean_sol) - self.Rand.rand() * self.Rand.uniform(task.Lower, task.Upper)
170
            elif escaping_energy_abs >= 0.5 and random_number >= 0.5:
171
                # 2. Exploitation: Soft besiege
172
                Sol[i] = \
173
                    (xb - Sol[i]) - \
174
                    escaping_energy * \
175
                    abs(jumping_energy * xb - Sol[i])
176
            elif escaping_energy_abs < 0.5 and random_number >= 0.5:
177
                # 3. Exploitation: Hard besiege
178
                Sol[i] = \
179
                    xb - \
180
                    escaping_energy * \
181
                    abs(xb - Sol[i])
182
            elif escaping_energy_abs >= 0.5 and random_number < 0.5:
183
                # 4. Exploitation: Soft besiege with pprogressive rapid dives
184
                cand1 = task.repair(xb - escaping_energy * abs(jumping_energy * xb - Sol[i]), rnd=self.Rand)
185
                random_vector = self.Rand.rand(task.D)
186
                cand2 = task.repair(cand1 + random_vector * self.levy_function(task.D, self.levy, rnd=self.Rand), rnd=self.Rand)
187
                if task.eval(cand1) < Fitness[i]:
188
                    Sol[i] = cand1
189
                elif task.eval(cand2) < Fitness[i]:
190
                    Sol[i] = cand2
191
            elif escaping_energy_abs < 0.5 and random_number < 0.5:
192
                # 5. Exploitation: Hard besiege with progressive rapid dives
193
                cand1 = task.repair(xb - escaping_energy * abs(jumping_energy * xb - mean_sol), rnd=self.Rand)
194
                random_vector = self.Rand.rand(task.D)
195
                cand2 = task.repair(cand1 + random_vector * self.levy_function(task.D, self.levy, rnd=self.Rand), rnd=self.Rand)
196
                if task.eval(cand1) < Fitness[i]:
197
                    Sol[i] = cand1
198
                elif task.eval(cand2) < Fitness[i]:
199
                    Sol[i] = cand2
200
            # Repair agent (from population) values
201
            Sol[i] = task.repair(Sol[i], rnd=self.Rand)
202
            # Eval population
203
            Fitness[i] = task.eval(Sol[i])
204
        # Get best of population
205
        best_index = argmin(Fitness)
206
        xb_cand = Sol[best_index].copy()
207
        fxb_cand = Fitness[best_index].copy()
208
        if fxb_cand < fxb:
209
            fxb = fxb_cand
210
            xb = xb_cand.copy()
211
        return Sol, Fitness, xb, fxb, {}
212
213
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
214