CatSwarmOptimization.runIteration()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 31
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 9
dl 0
loc 31
rs 9.95
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# encoding=utf8
2
import logging
3
import math
4
5
import numpy as np
6
from NiaPy.algorithms.algorithm import Algorithm
7
logging.basicConfig()
8
logger = logging.getLogger('NiaPy.algorithms.basic')
9
logger.setLevel('INFO')
10
11
__all__ = ['CatSwarmOptimization']
12
13
class CatSwarmOptimization(Algorithm):
14
    r"""Implementation of Cat swarm optimiization algorithm.
15
16
    **Algorithm:** Cat swarm optimization
17
18
    **Date:** 2019
19
20
    **Author:** Mihael Baketarić
21
22
    **License:** MIT
23
24
    **Reference paper:** Chu, S. C., Tsai, P. W., & Pan, J. S. (2006). Cat swarm optimization. In Pacific Rim international conference on artificial intelligence (pp. 854-858). Springer, Berlin, Heidelberg..
25
    """
26
    Name = ['CatSwarmOptimization', 'CSO']
27
28
    @staticmethod
29
    def algorithmInfo():
30
    	r"""Get algorithm information.
31
32
    	Returns:
33
    		str: Algorithm information.
34
35
    	See Also:
36
    		* :func:`NiaPy.algorithms.Algorithm.algorithmInfo`
37
    	"""
38
    	return r"""Chu, S. C., Tsai, P. W., & Pan, J. S. (2006). Cat swarm optimization. In Pacific Rim international conference on artificial intelligence (pp. 854-858). Springer, Berlin, Heidelberg."""
39
40
    @staticmethod
41
    def typeParameters(): return {
42
        'NP': lambda x: isinstance(x, int) and x > 0,
43
        'MR': lambda x: isinstance(x, (int, float)) and 0 <= x <= 1,
44
        'C1': lambda x: isinstance(x, (int, float)) and x >= 0,
45
        'SMP': lambda x: isinstance(x, int) and x > 0,
46
        'SPC': lambda x: isinstance(x, bool),
47
        'CDC': lambda x: isinstance(x, (int, float)) and 0 <= x <= 1,
48
        'SRD': lambda x: isinstance(x, (int, float)) and 0 <= x <= 1,
49
        'vMax': lambda x: isinstance(x, (int, float)) and x > 0
50
    }
51
52
    def setParameters(self, NP=30, MR=0.1, C1=2.05, SMP=3, SPC=True, CDC=0.85, SRD=0.2, vMax=1.9, **ukwargs):
53
        r"""Set the algorithm parameters.
54
55
        Arguments:
56
            NP (int): Number of individuals in population.
57
            MR (float): Mixture ratio.
58
            C1 (float): Constant in tracing mode.
59
            SMP (int): Seeking memory pool.
60
            SPC (bool): Self-position considering.
61
            CDC (float): Decides how many dimensions will be varied.
62
            SRD (float): Seeking range of the selected dimension.
63
            vMax (float): Maximal velocity.
64
65
            See Also:
66
                * :func:`NiaPy.algorithms.Algorithm.setParameters`
67
        """
68
        Algorithm.setParameters(self, NP=NP, **ukwargs)
69
        self.MR, self.C1, self.SMP, self.SPC, self.CDC, self.SRD, self.vMax = MR, C1, SMP, SPC, CDC, SRD, vMax
70
71
    def initPopulation(self, task):
72
        r"""Initialize population.
73
74
        Args:
75
            task (Task): Optimization task.
76
77
        Returns:
78
            Tuple[numpy.ndarray, numpy.ndarray[float], Dict[str, Any]]:
79
                1. Initialized population.
80
                2. Initialized populations fitness/function values.
81
                3. Additional arguments:
82
                    * Dictionary of modes (seek or trace) and velocities for each cat
83
        See Also:
84
            * :func:`NiaPy.algorithms.Algorithm.initPopulation`
85
        """
86
        pop, fpop, d = Algorithm.initPopulation(self, task)
87
        d['modes'] = self.randomSeekTrace()
88
        d['velocities'] = self.uniform(-self.vMax, self.vMax, [len(pop), task.D])
89
        return pop, fpop, d
90
91
    def repair(self, x, l, u):
92
        r"""Repair array to range.
93
94
        Args:
95
            x (numpy.ndarray): Array to repair.
96
            l (numpy.ndarray): Lower limit of allowed range.
97
            u (numpy.ndarray): Upper limit of allowed range.
98
99
        Returns:
100
            numpy.ndarray: Repaired array.
101
        """
102
        ir = np.where(x < l)
103
        x[ir] = l[ir]
104
        ir = np.where(x > u)
105
        x[ir] = u[ir]
106
        return x
107
108
    def randomSeekTrace(self):
109
        r"""Set cats into seeking/tracing mode.
110
111
        Returns:
112
            numpy.ndarray: One or zero. One means tracing mode. Zero means seeking mode. Length of list is equal to NP.
113
        """
114
        lista = np.zeros((self.NP,), dtype=int)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable int does not seem to be defined.
Loading history...
115
        indexes = np.arange(self.NP)
116
        self.Rand.shuffle(indexes)
117
        lista[indexes[:int(self.NP * self.MR)]] = 1
118
        return lista
119
120
    def weightedSelection(self, weights):
121
        r"""Random selection considering the weights.
122
123
        Args:
124
            weights (numpy.ndarray): weight for each potential position.
125
126
        Returns:
127
            int: index of selected next position.
128
        """
129
        cumulative_sum = np.cumsum(weights)
130
        return np.argmax(cumulative_sum >= (self.rand() * cumulative_sum[-1]))
131
132
    def seekingMode(self, task, cat, fcat, pop, fpop, fxb):
133
        r"""Seeking mode.
134
135
        Args:
136
            task (Task): Optimization task.
137
            cat (numpy.ndarray): Individual from population.
138
            fcat (float): Current individual's fitness/function value.
139
            pop (numpy.ndarray): Current population.
140
            fpop (numpy.ndarray): Current population fitness/function values.
141
            fxb (float): Current best cat fitness/function value.
142
143
        Returns:
144
            Tuple[numpy.ndarray, float, numpy.ndarray, float]:
145
                1. Updated individual's position
146
                2. Updated individual's fitness/function value
147
                3. Updated global best position
148
                4. Updated global best fitness/function value
149
        """
150
        cat_copies = []
151
        cat_copies_fs = []
152
        for j in range(self.SMP - 1 if self.SPC else self.SMP):
153
            cat_copies.append(cat.copy())
154
            indexes = np.arange(task.D)
155
            self.Rand.shuffle(indexes)
156
            to_vary_indexes = indexes[:int(task.D * self.CDC)]
157
            if self.randint(2) == 1:
158
                cat_copies[j][to_vary_indexes] += cat_copies[j][to_vary_indexes] * self.SRD
159
            else:
160
                cat_copies[j][to_vary_indexes] -= cat_copies[j][to_vary_indexes] * self.SRD
161
            cat_copies[j] = task.repair(cat_copies[j])
162
            cat_copies_fs.append(task.eval(cat_copies[j]))
163
        if self.SPC:
164
            cat_copies.append(cat.copy())
165
            cat_copies_fs.append(fcat)
166
167
        cat_copies_select_probs = np.ones(len(cat_copies))
168
        fmax = np.max(cat_copies_fs)
169
        fmin = np.min(cat_copies_fs)
170
        if any(x != cat_copies_fs[0] for x in cat_copies_fs):
171
            fb = fmax
172
            if math.isinf(fb):
173
                cat_copies_select_probs = np.full(len(cat_copies), fb)
174
            else:
175
                cat_copies_select_probs = np.abs(cat_copies_fs - fb) / (fmax - fmin)
176
        if fmin < fxb:
177
            fxb = fmin
178
            ind = self.randint(self.NP, 1, 0)
179
            pop[ind] = cat_copies[np.where(cat_copies_fs == fmin)[0][0]]
180
            fpop[ind] = fmin
181
        sel_index = self.weightedSelection(cat_copies_select_probs)
182
        return cat_copies[sel_index], cat_copies_fs[sel_index], pop, fpop
183
184
    def tracingMode(self, task, cat, velocity, xb):
185
        r"""Tracing mode.
186
187
        Args:
188
            task (Task): Optimization task.
189
            cat (numpy.ndarray): Individual from population.
190
            velocity (numpy.ndarray): Velocity of individual.
191
            xb (numpy.ndarray): Current best individual.
192
        Returns:
193
            Tuple[numpy.ndarray, float, numpy.ndarray]:
194
                1. Updated individual's position
195
                2. Updated individual's fitness/function value
196
                3. Updated individual's velocity vector
197
        """
198
        Vnew = self.repair(velocity + (self.uniform(0, 1, len(velocity)) * self.C1 * (xb - cat)), np.full(task.D, -self.vMax), np.full(task.D, self.vMax))
199
        cat_new = task.repair(cat + Vnew)
200
        return cat_new, task.eval(cat_new), Vnew
201
202
    def runIteration(self, task, pop, fpop, xb, fxb, velocities, modes, **dparams):
203
        r"""Core function of Cat Swarm Optimization algorithm.
204
205
        Args:
206
            task (Task): Optimization task.
207
            pop (numpy.ndarray): Current population.
208
            fpop (numpy.ndarray): Current population fitness/function values.
209
            xb (numpy.ndarray): Current best individual.
210
            fxb (float): Current best cat fitness/function value.
211
            velocities (numpy.ndarray): Velocities of individuals.
212
            modes (numpy.ndarray): Flag of each individual.
213
            **dparams (Dict[str, Any]): Additional function arguments.
214
215
        Returns:
216
            Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, float, Dict[str, Any]]:
217
                1. New population.
218
                2. New population fitness/function values.
219
                3. New global best solution.
220
                4. New global best solutions fitness/objective value.
221
                5. Additional arguments:
222
                    * Dictionary of modes (seek or trace) and velocities for each cat.
223
        """
224
        pop_copies = pop.copy()
225
        for k in range(len(pop_copies)):
226
            if modes[k] == 0:
227
                pop_copies[k], fpop[k], pop_copies[:], fpop[:] = self.seekingMode(task, pop_copies[k], fpop[k], pop_copies, fpop, fxb)
228
            else:  # if cat in tracing mode
229
                pop_copies[k], fpop[k], velocities[k] = self.tracingMode(task, pop_copies[k], velocities[k], xb)
230
        ib = np.argmin(fpop)
231
        if fpop[ib] < fxb: xb, fxb = pop_copies[ib].copy(), fpop[ib]
232
        return pop_copies, fpop, xb, fxb, {'velocities': velocities, 'modes': self.randomSeekTrace()}
233
234
# vim: tabstop=3 noexpandtab shiftwidth=3 softtabstop=3
235