DownhillSimplexOptimizer.evaluate()   D
last analyzed

Complexity

Conditions 13

Size

Total Lines 64
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 42
dl 0
loc 64
rs 4.2
c 0
b 0
f 0
cc 13
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like gradient_free_optimizers.optimizers.local_opt.downhill_simplex.DownhillSimplexOptimizer.evaluate() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# Author: Simon Blanke
2
# Email: [email protected]
3
# License: MIT License
4
5
6
import random
7
import numpy as np
8
9
from .hill_climbing_optimizer import HillClimbingOptimizer
10
11
12
def sort_list_idx(list_):
13
    list_np = np.array(list_)
14
    idx_sorted = list(list_np.argsort()[::-1])
15
    return idx_sorted
16
17
18 View Code Duplication
def centeroid(array_list):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
19
    centeroid = []
20
21
    for idx in range(array_list[0].shape[0]):
22
        center_dim_pos = []
23
        for array in array_list:
24
            center_dim_pos.append(array[idx])
25
26
        center_dim_mean = np.array(center_dim_pos).mean()
27
        centeroid.append(center_dim_mean)
28
29
    return centeroid
30
31
32
class DownhillSimplexOptimizer(HillClimbingOptimizer):
33
    name = "Downhill Simplex"
34
    _name_ = "downhill_simplex"
35
    __name__ = "DownhillSimplexOptimizer"
36
37
    optimizer_type = "local"
38
    computationally_expensive = False
39
40
    def __init__(
41
        self,
42
        search_space,
43
        initialize={"grid": 4, "random": 2, "vertices": 4},
44
        constraints=[],
45
        random_state=None,
46
        rand_rest_p=0,
47
        nth_process=None,
48
        alpha=1,
49
        gamma=2,
50
        beta=0.5,
51
        sigma=0.5,
52
    ):
53
        super().__init__(
54
            search_space=search_space,
55
            initialize=initialize,
56
            constraints=constraints,
57
            random_state=random_state,
58
            rand_rest_p=rand_rest_p,
59
            nth_process=nth_process,
60
        )
61
62
        self.alpha = alpha
63
        self.gamma = gamma
64
        self.beta = beta
65
        self.sigma = sigma
66
67
        self.n_simp_positions = len(self.conv.search_space) + 1
68
        self.simp_positions = []
69
70
        self.simplex_step = 0
71
72
        diff_init = self.n_simp_positions - self.init.n_inits
73
        if diff_init > 0:
74
            self.init.add_n_random_init_pos(diff_init)
75
76
    def finish_initialization(self):
77
        idx_sorted = sort_list_idx(self.scores_valid)
78
        self.simplex_pos = [self.positions_valid[idx] for idx in idx_sorted]
79
        self.simplex_scores = [self.scores_valid[idx] for idx in idx_sorted]
80
81
        self.simplex_step = 1
82
83
        self.i_x_0 = 0
84
        self.i_x_N_1 = -2
85
        self.i_x_N = -1
86
87
        self.search_state = "iter"
88
89
    @HillClimbingOptimizer.track_new_pos
90
    def iterate(self):
91
        simplex_stale = all(
92
            [
93
                np.array_equal(self.simplex_pos[0], array)
94
                for array in self.simplex_pos
95
            ]
96
        )
97
98
        if simplex_stale:
99
            idx_sorted = sort_list_idx(self.scores_valid)
100
            self.simplex_pos = [self.positions_valid[idx] for idx in idx_sorted]
101
            self.simplex_scores = [self.scores_valid[idx] for idx in idx_sorted]
102
103
            self.simplex_step = 1
104
105
        if self.simplex_step == 1:
106
            idx_sorted = sort_list_idx(self.simplex_scores)
107
            self.simplex_pos = [self.simplex_pos[idx] for idx in idx_sorted]
108
            self.simplex_scores = [
109
                self.simplex_scores[idx] for idx in idx_sorted
110
            ]
111
112
            self.center_array = centeroid(self.simplex_pos[:-1])
113
114
            r_pos = self.center_array + self.alpha * (
115
                self.center_array - self.simplex_pos[-1]
116
            )
117
            self.r_pos = self.conv2pos(r_pos)
118
            pos_new = self.r_pos
119
120
        elif self.simplex_step == 2:
121
            e_pos = self.center_array + self.gamma * (
122
                self.center_array - self.simplex_pos[-1]
123
            )
124
            self.e_pos = self.conv2pos(e_pos)
125
            self.simplex_step = 1
126
127
            pos_new = self.e_pos
128
129
        elif self.simplex_step == 3:
130
            # iter Contraction
131
            c_pos = self.h_pos + self.beta * (self.center_array - self.h_pos)
132
            c_pos = self.conv2pos(c_pos)
133
134
            pos_new = c_pos
135
136
        elif self.simplex_step == 4:
137
            # iter Shrink
138
            pos = self.simplex_pos[self.compress_idx]
139
            pos = pos + self.sigma * (self.simplex_pos[0] - pos)
140
141
            pos_new = self.conv2pos(pos)
142
143
        if self.conv.not_in_constraint(pos_new):
0 ignored issues
show
introduced by
The variable pos_new does not seem to be defined for all execution paths.
Loading history...
144
            return pos_new
145
146
        return self.move_climb(
147
            pos_new, epsilon=self.epsilon, distribution=self.distribution
148
        )
149
150
    @HillClimbingOptimizer.track_new_score
151
    def evaluate(self, score_new):
152
        if self.simplex_step != 0:
153
            self.prev_pos = self.positions_valid[-1]
154
155
        if self.simplex_step == 1:
156
            # self.r_pos = self.prev_pos
157
            self.r_score = score_new
158
159
            if self.r_score > self.simplex_scores[0]:
160
                self.simplex_step = 2
161
162
            elif self.r_score > self.simplex_scores[-2]:
163
                # if r is better than x N-1
164
                self.simplex_pos[-1] = self.r_pos
165
                self.simplex_scores[-1] = self.r_score
166
                self.simplex_step = 1
167
168
            if self.simplex_scores[-1] > self.r_score:
169
                self.h_pos = self.simplex_pos[-1]
170
                self.h_score = self.simplex_scores[-1]
171
            else:
172
                self.h_pos = self.r_pos
173
                self.h_score = self.r_score
174
175
            self.simplex_step = 3
176
177
        elif self.simplex_step == 2:
178
            self.e_score = score_new
179
180
            if self.e_score > self.r_score:
181
                self.simplex_scores[-1] = self.e_pos
182
            elif self.r_score > self.e_score:
183
                self.simplex_scores[-1] = self.r_pos
184
            else:
185
                self.simplex_scores[-1] = random.choice(
186
                    [self.e_pos, self.r_pos]
187
                )[0]
188
189
        elif self.simplex_step == 3:
190
            # eval Contraction
191
            self.c_pos = self.prev_pos
192
            self.c_score = score_new
193
194
            if self.c_score > self.simplex_scores[-1]:
195
                self.simplex_scores[-1] = self.c_score
196
                self.simplex_pos[-1] = self.c_pos
197
198
                self.simplex_step = 1
199
200
            else:
201
                # start Shrink
202
                self.simplex_step = 4
203
                self.compress_idx = 0
204
205
        elif self.simplex_step == 4:
206
            # eval Shrink
207
            self.simplex_scores[self.compress_idx] = score_new
208
            self.simplex_pos[self.compress_idx] = self.prev_pos
209
210
            self.compress_idx += 1
211
212
            if self.compress_idx == self.n_simp_positions:
213
                self.simplex_step = 1
214