Passed
Push — master ( 626f23...be3b1e )
by Simon
01:48
created

DownhillSimplexOptimizer.iterate()   B

Complexity

Conditions 6

Size

Total Lines 48
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 32
nop 1
dl 0
loc 48
rs 8.1786
c 0
b 0
f 0
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 ..base_optimizer import BaseOptimizer
10
from ...search import Search
11
12
13
def sort_list_idx(list_):
14
    list_np = np.array(list_)
15
    idx_sorted = list(list_np.argsort()[::-1])
16
    return idx_sorted
17
18
19
def centeroid(array_list):
20
    centeroid = []
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(BaseOptimizer, Search):
33
    name = "Downhill Simplex Optimizer"
34
35
    def __init__(
36
        self,
37
        *args,
38
        alpha=1,
39
        gamma=2,
40
        beta=0.5,
41
        sigma=0.5,
42
        **kwargs,
43
    ):
44
        super().__init__(*args, **kwargs)
45
46
        self.alpha = alpha
47
        self.gamma = gamma
48
        self.beta = beta
49
        self.sigma = sigma
50
51
        self.n_simp_positions = len(self.conv.search_space) + 1
52
        self.simp_positions = []
53
54
        self.simplex_step = 0
55
56
    def finish_initialization(self):
57
        idx_sorted = sort_list_idx(self.scores_valid)
58
        self.simplex_pos = [self.positions_valid[idx] for idx in idx_sorted]
59
        self.simplex_scores = [self.scores_valid[idx] for idx in idx_sorted]
60
61
        n_inits = len(self.positions_valid)
62
        if n_inits < self.n_simp_positions:
63
            print("\n Error: Not enough initial positions to form simplex")
64
            raise ValueError("Increase number of initial positions")
65
66
        self.simplex_step = 1
67
68
        self.i_x_0 = 0
69
        self.i_x_N_1 = -2
70
        self.i_x_N = -1
71
72
    @BaseOptimizer.track_nth_iter
73
    def iterate(self):
74
        simplex_stale = all(
75
            [np.array_equal(self.simplex_pos[0], array) for array in self.simplex_pos]
76
        )
77
78
        if simplex_stale:
79
            idx_sorted = sort_list_idx(self.scores_valid)
80
            self.simplex_pos = [self.positions_valid[idx] for idx in idx_sorted]
81
            self.simplex_scores = [self.scores_valid[idx] for idx in idx_sorted]
82
83
            self.simplex_step = 1
84
85
        if self.simplex_step == 1:
86
            idx_sorted = sort_list_idx(self.simplex_scores)
87
            self.simplex_pos = [self.simplex_pos[idx] for idx in idx_sorted]
88
            self.simplex_scores = [self.simplex_scores[idx] for idx in idx_sorted]
89
90
            self.center_array = centeroid(self.simplex_pos[:-1])
91
92
            r_pos = self.center_array + self.alpha * (
93
                self.center_array - self.simplex_pos[-1]
94
            )
95
            self.r_pos = self.conv2pos(r_pos)
96
            return self.r_pos
97
98
        elif self.simplex_step == 2:
99
            e_pos = self.center_array + self.gamma * (
100
                self.center_array - self.simplex_pos[-1]
101
            )
102
            self.e_pos = self.conv2pos(e_pos)
103
            self.simplex_step = 1
104
105
            return self.e_pos
106
107
        elif self.simplex_step == 3:
108
            # iter Contraction
109
            c_pos = self.h_pos + self.beta * (self.center_array - self.h_pos)
110
            c_pos = self.conv2pos(c_pos)
111
112
            return c_pos
113
114
        elif self.simplex_step == 4:
115
            # iter Shrink
116
            pos = self.simplex_pos[self.compress_idx]
117
            pos = pos + self.sigma * (self.simplex_pos[0] - pos)
118
119
            return self.conv2pos(pos)
120
121
    def evaluate(self, score_new):
122
        self.score_new = score_new
123
124
        if self.simplex_step != 0:
125
            self.prev_pos = self.positions_valid[-1]
126
127
        if self.simplex_step == 1:
128
            # self.r_pos = self.prev_pos
129
            self.r_score = score_new
130
131
            if self.r_score > self.simplex_scores[0]:
132
                self.simplex_step = 2
133
134
            elif self.r_score > self.simplex_scores[-2]:
135
                # if r is better than x N-1
136
                self.simplex_pos[-1] = self.r_pos
137
                self.simplex_scores[-1] = self.r_score
138
                self.simplex_step = 1
139
140
            if self.simplex_scores[-1] > self.r_score:
141
                self.h_pos = self.simplex_pos[-1]
142
                self.h_score = self.simplex_scores[-1]
143
            else:
144
                self.h_pos = self.r_pos
145
                self.h_score = self.r_score
146
147
            self.simplex_step = 3
148
149
        elif self.simplex_step == 2:
150
            self.e_score = score_new
151
152
            if self.e_score > self.r_score:
153
                self.simplex_scores[-1] = self.e_pos
154
            elif self.r_score > self.e_score:
155
                self.simplex_scores[-1] = self.r_pos
156
            else:
157
                self.simplex_scores[-1] = random.choice([self.e_pos, self.r_pos])[0]
158
159
        elif self.simplex_step == 3:
160
            # eval Contraction
161
            self.c_pos = self.prev_pos
162
            self.c_score = score_new
163
164
            if self.c_score > self.simplex_scores[-1]:
165
                self.simplex_scores[-1] = self.c_score
166
                self.simplex_pos[-1] = self.c_pos
167
168
                self.simplex_step = 1
169
170
            else:
171
                # start Shrink
172
                self.simplex_step = 4
173
                self.compress_idx = 0
174
175
        elif self.simplex_step == 4:
176
            # eval Shrink
177
            self.simplex_scores[self.compress_idx] = score_new
178
            self.simplex_pos[self.compress_idx] = self.prev_pos
179
180
            self.compress_idx += 1
181
182
            if self.compress_idx == self.n_simp_positions:
183
                self.simplex_step = 1
184