-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhappycat.py
228 lines (193 loc) · 6.88 KB
/
happycat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
"""
File: happycat.py
By Peter Caven, peter@sparseinference.com
Description:
HappyCat test function for the "Biased Random-Key Genetic Algorithm".
See: "HappyCat – A Simple Function Class Where Well-Known Direct Search Algorithms Do Fail",
by Hans-Georg Beyer and Steffen Finck, 2012
"""
import torch
from brkga import BRKGA,optAdam
def HappyCat(x, alpha=1/8):
X = x.dot(x)
N = len(x)
return (X - N).pow(2.0).pow(alpha) + (X.div(2.0) + x.sum())/N + 0.5
def box0(fun, lower, upper):
"""
1 dimension per population member.
Keyshape: (population size, parameter keys)
Parameter keys only.
"""
width = upper - lower
#----
def bounds(keys):
return lower,upper
#----
def decode(keys):
return (keys * width) + lower
#----
def evaluate(keys):
return fun(decode(keys))
#----
return bounds,decode,evaluate
def box1(fun, initialLower, initialUpper):
"""
Use for keyshape: (population_size, parameter_keys + two_bounds_keys)
where the second dimension has lower and upper bounds keys
prepended to the parameters keys.
"""
initialWidth = initialUpper - initialLower
#----
def bounds(keys):
lowerKey = keys[0]
upperKey = keys[1]
if lowerKey > upperKey:
lowerKey,upperKey = upperKey,lowerKey
lower = (lowerKey * initialWidth) + initialLower
upper = (upperKey * initialWidth) + initialLower
return lower,upper
#----
def decode(keys):
lower,upper = bounds(keys)
width = upper - lower
#---
return (keys[2:] * width) + lower
#----
def evaluate(keys):
return fun(decode(keys))
#----
return bounds,decode,evaluate
def box2(fun, initialLower, initialUpper):
"""
Use for keyshape: (population_size, 3, parameter_keys)
where the first parameter dimension is two rows of lower and upper bounds keys,
and one row of parameters keys.
The bounds are learned, two per parameter.
"""
#----
def bounds(keys):
lowers = initialLower + (3.0 * keys[0])
uppers = initialUpper - (3.0 * keys[1])
return lowers,uppers
#----
def decode(keys):
lowers,uppers = bounds(keys)
widths = uppers - lowers
return (keys[2] * widths) + lowers
#----
def evaluate(keys):
return fun(decode(keys))
#----
return bounds,decode,evaluate
def box3(fun, initialLower, initialUpper):
"""
Use for keyshape: (population_size, 3, parameter_keys)
where the first parameter dimension is two rows of lower and upper bounds keys,
and one row of parameters keys.
The bounds are learned, two per parameter.
"""
initialWidth = initialUpper - initialLower
#----
def bounds(keys):
v,_ = keys[:2].sort(-2)
lowerKeys = v[0]
upperKeys = v[1]
lowers = (lowerKeys * initialWidth) + initialLower
uppers = (upperKeys * initialWidth) + initialLower
return lowers,uppers
#----
def decode(keys):
lowers,uppers = bounds(keys)
widths = uppers - lowers
valueKeys = keys[2]
return (valueKeys * widths) + lowers
#----
def evaluate(keys):
return fun(decode(keys))
#----
return bounds,decode,evaluate
def Optimize(box, keyShape, elites=2, mutants=2):
"""
Minimize the HappyCat function in the interval (-2,+2).
"""
trial = 0
bounds,decode,f = box #box3(HappyCat, -2.0, 2.0)
pop = BRKGA(keyShape, elites=elites, mutants=mutants)
results = pop.map(f)
bestResult,best = pop.orderBy(results)
print(f"[{trial:6d}] {bestResult:.8f}")
print(decode(best.data))
try:
while bestResult > 1.0e-7:
trial += 1
pop.evolve()
results = pop.map(f)
bestResult,best = pop.orderBy(results)
if trial % 100 == 0:
print(f"[{trial:6d}] {bestResult:.8f}")
print(decode(best.data))
except KeyboardInterrupt:
pass
finally:
print(f"[{trial:6d}] {bestResult:.8f}")
print(f"BRKGA keys=\n{best.data}")
hcLower,hcUpper = bounds(best.data)
print(f"HappyCat Bounds: lower:{hcLower} upper:{hcUpper}")
print(f"HappyCat parameters=\n{decode(best.data)}")
return bestResult,best
def OptimizeGrad(box, keyShape, elites=2, mutants=2, lr=0.001):
"""
Minimize the HappyCat function in the interval (-2,+2).
This function uses gradient information to improve the evolved solutions.
"""
trial = 0
bounds,decode,f = box
pop = BRKGA(keyShape, elites=elites, mutants=mutants, optimizer=optAdam(lr=lr))
try:
results = pop.map(f)
bestResult,best = pop.orderBy(results)
while bestResult > 1.0e-7:
if trial % 100 == 0:
print(f"[{trial:6d}] {bestResult:.8f}")
print(decode(best.data))
trial += 1
pop.evolve()
results = pop.map(f)
results.mean().backward()
pop.optimize()
results = pop.map(f)
bestResult,best = pop.orderBy(results)
except KeyboardInterrupt:
pass
finally:
print(f"[{trial:6d}] {bestResult:.8f}")
print(f"BRKGA keys=\n{best.data}")
hcLower,hcUpper = bounds(best.data)
print(f"HappyCat Bounds: lower:{hcLower} upper:{hcUpper}")
print(f"HappyCat parameters=\n{decode(best.data)}")
return bestResult,best
##===================================================
if __name__ == '__main__':
## Optimize only the parameter keys:
# Optimize(box0(HappyCat, -2.0, 2.0), (50,10), elites=3, mutants=3)
# OptimizeGrad(box0(HappyCat, -2.0, 2.0), (20,10), elites=3, mutants=3, lr=1.0e-8)
## Optimize the bounding box by prepending lower and upper bounds to all parameter keys:
# Optimize(box1(HappyCat, -2.0, 2.0), (10,10+2), elites=3, mutants=3)
# OptimizeGrad(box1(HappyCat, -2.0, 2.0), (10,10+2), elites=3, mutants=3, lr=1.0e-10)
## Optimize lower and upper bounds for every parameter key:
Optimize(box2(HappyCat, -2.0, 2.0), (20,3,10), elites=3, mutants=3)
# OptimizeGrad(box2(HappyCat, -2.0, 2.0), (20,3,10), elites=3, mutants=3, lr=1.0e-8)
## Optimize lower and upper bounds for every parameter key:
# Optimize(box3(HappyCat, -2.0, 2.0), (20,3,10), elites=3, mutants=3)
# OptimizeGrad(box3(HappyCat, -2.0, 2.0), (20,3,10), elites=3, mutants=3, lr=1.0e-8)
"""
# OptimizeGrad(box1(HappyCat, -2.0, 2.0), (10,10+2), elites=3, mutants=3, lr=1.0e-10)
[ 20003] 0.03961472
BRKGA keys=
tensor([0.1348, 0.3538, 0.2620, 0.4432, 0.9986, 0.6629, 0.2453, 0.7208, 0.7745,
0.9454, 0.5650, 0.0409], dtype=torch.float64)
HappyCat Bounds: lower:-1.4609493686795931 upper:-0.5846058774871854
HappyCat parameters=
tensor([-1.2314, -1.0726, -0.5859, -0.8800, -1.2460, -0.8293, -0.7822, -0.6325,
-0.9658, -1.4251], dtype=torch.float64)
"""