fcmaes 1.1.3__py3-none-any.whl → 1.6.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fcmaes/__init__.py +12 -2
- fcmaes/advretry.py +217 -159
- fcmaes/astro.py +143 -27
- fcmaes/bitecpp.py +107 -0
- fcmaes/cmaes.py +204 -173
- fcmaes/cmaescpp.py +253 -87
- fcmaes/crfmnes.py +339 -0
- fcmaes/crfmnescpp.py +273 -0
- fcmaes/dacpp.py +39 -51
- fcmaes/de.py +472 -0
- fcmaes/decpp.py +222 -64
- fcmaes/diversifier.py +357 -0
- fcmaes/evaluator.py +297 -14
- fcmaes/lib/libacmalib.dll +0 -0
- fcmaes/lib/libacmalib.dylib +0 -0
- fcmaes/lib/libacmalib.so +0 -0
- fcmaes/lib/libhbv.so +0 -0
- fcmaes/lib/liblrgv.so +0 -0
- fcmaes/lib/librw_top_trumps.dll +0 -0
- fcmaes/lib/librw_top_trumps.so +0 -0
- fcmaes/mapelites.py +737 -0
- fcmaes/mode.py +719 -0
- fcmaes/modecpp.py +470 -0
- fcmaes/moretry.py +270 -0
- fcmaes/multiretry.py +195 -0
- fcmaes/optimizer.py +883 -112
- fcmaes/pgpecpp.py +340 -0
- fcmaes/pygmoretry.py +10 -19
- fcmaes/retry.py +248 -121
- fcmaes/test_cma.py +207 -30
- fcmaes/testfun.py +38 -1
- {fcmaes-1.1.3.dist-info → fcmaes-1.6.9.dist-info}/METADATA +22 -12
- fcmaes-1.6.9.dist-info/RECORD +36 -0
- {fcmaes-1.1.3.dist-info → fcmaes-1.6.9.dist-info}/WHEEL +1 -1
- fcmaes/hhcpp.py +0 -114
- fcmaes/lib/libgtoplib.dll +0 -0
- fcmaes/lib/libgtoplib.so +0 -0
- fcmaes-1.1.3.dist-info/RECORD +0 -23
- {fcmaes-1.1.3.dist-info → fcmaes-1.6.9.dist-info}/LICENSE +0 -0
- {fcmaes-1.1.3.dist-info → fcmaes-1.6.9.dist-info}/top_level.txt +0 -0
fcmaes/cmaes.py
CHANGED
|
@@ -12,36 +12,43 @@ import sys
|
|
|
12
12
|
import os
|
|
13
13
|
import math
|
|
14
14
|
import numpy as np
|
|
15
|
+
from time import time
|
|
16
|
+
import ctypes as ct
|
|
17
|
+
import multiprocessing as mp
|
|
15
18
|
from scipy import linalg
|
|
16
|
-
from scipy.optimize import OptimizeResult
|
|
17
|
-
from numpy.random import
|
|
18
|
-
from fcmaes.evaluator import Evaluator,
|
|
19
|
+
from scipy.optimize import OptimizeResult, Bounds
|
|
20
|
+
from numpy.random import PCG64DXSM, Generator
|
|
21
|
+
from fcmaes.evaluator import Evaluator, serial, _check_bounds, _fitness, is_debug_active
|
|
22
|
+
|
|
23
|
+
from loguru import logger
|
|
24
|
+
from typing import Optional, Callable, Union
|
|
25
|
+
from numpy.typing import ArrayLike
|
|
19
26
|
|
|
20
27
|
os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
|
|
21
28
|
|
|
22
|
-
def minimize(fun,
|
|
23
|
-
bounds=None,
|
|
24
|
-
x0=None,
|
|
25
|
-
input_sigma = 0.3,
|
|
26
|
-
popsize = 31,
|
|
27
|
-
max_evaluations = 100000,
|
|
28
|
-
max_iterations = 100000,
|
|
29
|
-
workers =
|
|
30
|
-
accuracy = 1.0,
|
|
31
|
-
|
|
32
|
-
is_terminate = None,
|
|
33
|
-
rg = Generator(
|
|
34
|
-
runid=0
|
|
29
|
+
def minimize(fun: Callable[[ArrayLike], float],
|
|
30
|
+
bounds: Optional[Bounds] = None,
|
|
31
|
+
x0: Optional[ArrayLike] = None,
|
|
32
|
+
input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
|
|
33
|
+
popsize: Optional[int] = 31,
|
|
34
|
+
max_evaluations: Optional[int] = 100000,
|
|
35
|
+
max_iterations: Optional[int] = 100000,
|
|
36
|
+
workers: Optional[int] = 1,
|
|
37
|
+
accuracy: Optional[float] = 1.0,
|
|
38
|
+
stop_fitness: Optional[float] = -np.inf,
|
|
39
|
+
is_terminate: Optional[Callable[[ArrayLike, float], bool]] = None,
|
|
40
|
+
rg: Optional[Generator] = Generator(PCG64DXSM()),
|
|
41
|
+
runid: Optional[int] = 0,
|
|
42
|
+
normalize: Optional[bool] = True,
|
|
43
|
+
update_gap: Optional[int] = None) -> OptimizeResult:
|
|
35
44
|
"""Minimization of a scalar function of one or more variables using CMA-ES.
|
|
36
45
|
|
|
37
46
|
Parameters
|
|
38
47
|
----------
|
|
39
48
|
fun : callable
|
|
40
49
|
The objective function to be minimized.
|
|
41
|
-
``fun(x
|
|
42
|
-
where ``x`` is an 1-D array with shape (n,)
|
|
43
|
-
is a tuple of the fixed parameters needed to completely
|
|
44
|
-
specify the function.
|
|
50
|
+
``fun(x) -> float``
|
|
51
|
+
where ``x`` is an 1-D array with shape (n,)
|
|
45
52
|
bounds : sequence or `Bounds`, optional
|
|
46
53
|
Bounds on variables. There are two ways to specify the bounds:
|
|
47
54
|
1. Instance of the `scipy.Bounds` class.
|
|
@@ -59,11 +66,11 @@ def minimize(fun,
|
|
|
59
66
|
max_iterations : int, optional
|
|
60
67
|
Forced termination after ``max_iterations`` iterations.
|
|
61
68
|
workers : int or None, optional
|
|
62
|
-
If
|
|
69
|
+
If workers > 1, function evaluation is performed in parallel for the whole population.
|
|
63
70
|
Useful for costly objective functions but is deactivated for parallel retry.
|
|
64
71
|
accuracy : float, optional
|
|
65
72
|
values > 1.0 reduce the accuracy.
|
|
66
|
-
|
|
73
|
+
stop_fitness : float, optional
|
|
67
74
|
Limit for fitness value. If reached minimize terminates.
|
|
68
75
|
is_terminate : callable, optional
|
|
69
76
|
Callback to be used if the caller of minimize wants to
|
|
@@ -72,7 +79,11 @@ def minimize(fun,
|
|
|
72
79
|
Random generator for creating random guesses.
|
|
73
80
|
runid : int, optional
|
|
74
81
|
id used by the is_terminate callback to identify the CMA-ES run.
|
|
75
|
-
|
|
82
|
+
normalize : boolean, optional
|
|
83
|
+
pheno -> if true geno transformation maps arguments to interval [-1,1]
|
|
84
|
+
update_gap : int, optional
|
|
85
|
+
number of iterations without distribution update
|
|
86
|
+
|
|
76
87
|
Returns
|
|
77
88
|
-------
|
|
78
89
|
res : scipy.OptimizeResult
|
|
@@ -81,42 +92,47 @@ def minimize(fun,
|
|
|
81
92
|
``fun`` the best function value, ``nfev`` the number of function evaluations,
|
|
82
93
|
``nit`` the number of CMA-ES iterations, ``status`` the stopping critera and
|
|
83
94
|
``success`` a Boolean flag indicating if the optimizer exited successfully. """
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
|
|
96
|
+
if workers is None or workers <= 1:
|
|
97
|
+
fun = serial(fun)
|
|
86
98
|
cmaes = Cmaes(bounds, x0,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if
|
|
93
|
-
|
|
99
|
+
input_sigma, popsize,
|
|
100
|
+
max_evaluations, max_iterations,
|
|
101
|
+
accuracy, stop_fitness,
|
|
102
|
+
is_terminate, rg, np.random.randn, runid, normalize,
|
|
103
|
+
update_gap, fun)
|
|
104
|
+
if workers and workers > 1:
|
|
105
|
+
x, val, evals, iterations, stop = cmaes.do_optimize_delayed_update(fun, workers=workers)
|
|
106
|
+
else:
|
|
107
|
+
x, val, evals, iterations, stop = cmaes.doOptimize()
|
|
94
108
|
return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop,
|
|
95
109
|
success=True)
|
|
96
110
|
|
|
97
111
|
class Cmaes(object):
|
|
98
112
|
"""Implements the cma-es ask/tell interactive interface."""
|
|
99
113
|
|
|
100
|
-
def __init__(self, bounds=None,
|
|
101
|
-
x0=None,
|
|
102
|
-
input_sigma = 0.3,
|
|
103
|
-
popsize = 31,
|
|
104
|
-
max_evaluations = 100000,
|
|
105
|
-
max_iterations = 100000,
|
|
106
|
-
accuracy = 1.0,
|
|
107
|
-
|
|
108
|
-
is_terminate = None,
|
|
109
|
-
rg = Generator(
|
|
110
|
-
randn = np.random.randn, # used for random offspring
|
|
111
|
-
runid=0,
|
|
112
|
-
|
|
114
|
+
def __init__(self, bounds: Optional[Bounds] = None,
|
|
115
|
+
x0: Optional[ArrayLike] = None,
|
|
116
|
+
input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
|
|
117
|
+
popsize: Optional[int] = 31,
|
|
118
|
+
max_evaluations: Optional[int] = 100000,
|
|
119
|
+
max_iterations: Optional[int] = 100000,
|
|
120
|
+
accuracy: Optional[int] = 1.0,
|
|
121
|
+
stop_fitness: Optional[float] = -np.inf,
|
|
122
|
+
is_terminate: Optional[bool] = None,
|
|
123
|
+
rg: Optional[Generator] = Generator(PCG64DXSM()), # used if x0 is undefined
|
|
124
|
+
randn: Optional[Callable] = np.random.randn, # used for random offspring
|
|
125
|
+
runid: Optional[int] = 0,
|
|
126
|
+
normalize: Optional[bool] = True,
|
|
127
|
+
update_gap: Optional[int] = None,
|
|
128
|
+
fun: Optional[Callable[[ArrayLike], float]] = None
|
|
113
129
|
):
|
|
114
130
|
|
|
115
131
|
# runid used in is_terminate callback to identify a specific run at different iteration
|
|
116
132
|
self.runid = runid
|
|
117
133
|
# bounds and guess
|
|
118
134
|
lower, upper, guess = _check_bounds(bounds, x0, rg)
|
|
119
|
-
self.fitfun =
|
|
135
|
+
self.fitfun = _fitness(fun, lower, upper, normalize)
|
|
120
136
|
# initial guess for the arguments of the fitness function
|
|
121
137
|
self.guess = self.fitfun.encode(guess)
|
|
122
138
|
# random generators
|
|
@@ -140,6 +156,8 @@ class Cmaes(object):
|
|
|
140
156
|
# Individual sigma values - initial search volume. input_sigma determines
|
|
141
157
|
# the initial coordinate wise standard deviations for the search. Setting
|
|
142
158
|
# SIGMA one third of the initial search region is appropriate.
|
|
159
|
+
if callable(input_sigma):
|
|
160
|
+
input_sigma=input_sigma()
|
|
143
161
|
if isinstance(input_sigma, list):
|
|
144
162
|
self.insigma = np.asarray(input_sigma)
|
|
145
163
|
elif np.isscalar(input_sigma):
|
|
@@ -153,7 +171,7 @@ class Cmaes(object):
|
|
|
153
171
|
self.max_evaluations = max_evaluations
|
|
154
172
|
self.max_iterations = max_iterations
|
|
155
173
|
# Limit for fitness value.
|
|
156
|
-
self.stop_fitness =
|
|
174
|
+
self.stop_fitness = stop_fitness
|
|
157
175
|
# Stop if x-changes larger stopTolUpX.
|
|
158
176
|
self.stopTolUpX = 1e3 * self.sigma
|
|
159
177
|
# Stop if x-change smaller stopTolX.
|
|
@@ -165,6 +183,12 @@ class Cmaes(object):
|
|
|
165
183
|
# selection strategy parameters
|
|
166
184
|
# Number of parents/points for recombination.
|
|
167
185
|
self.mu = int(self.popsize/2)
|
|
186
|
+
# timing / global best value
|
|
187
|
+
if is_debug_active():
|
|
188
|
+
self.best_y = mp.RawValue(ct.c_double, 1E99)
|
|
189
|
+
self.n_evals = mp.RawValue(ct.c_long, 0)
|
|
190
|
+
self.time_0 = time()
|
|
191
|
+
|
|
168
192
|
# Array for weighted recombination.
|
|
169
193
|
self.weights = (np.log(np.arange(1, self.mu+1, 1)) * -1) + math.log(self.mu + 0.5)
|
|
170
194
|
sumw = np.sum(self.weights)
|
|
@@ -190,6 +214,9 @@ class Cmaes(object):
|
|
|
190
214
|
self.chiN = math.sqrt(self.dim) * (1. - 1. / (4. * self.dim) + 1 / (21. * self.dim * self.dim))
|
|
191
215
|
self.ccov1Sep = min(1., self.ccov1 * (self.dim + 1.5) / 3.)
|
|
192
216
|
self.ccovmuSep = min(1. - self.ccov1, self.ccovmu * (self.dim + 1.5) / 3.)
|
|
217
|
+
# lazy covariance update gap
|
|
218
|
+
self.lazy_update_gap = 1. / (self.ccov1 + self.ccovmu + 1e-23) / self.dim / 10 \
|
|
219
|
+
if update_gap is None else update_gap
|
|
193
220
|
|
|
194
221
|
# CMA internal values - updated each generation
|
|
195
222
|
# Objective variables.
|
|
@@ -215,6 +242,7 @@ class Cmaes(object):
|
|
|
215
242
|
self.historySize = 10 + int(3. * 10. * self.dim / popsize)
|
|
216
243
|
|
|
217
244
|
self.iterations = 0
|
|
245
|
+
self.last_update = 0
|
|
218
246
|
self.stop = 0
|
|
219
247
|
self.best_value = sys.float_info.max
|
|
220
248
|
self.best_x = None
|
|
@@ -222,9 +250,9 @@ class Cmaes(object):
|
|
|
222
250
|
self.fitness_history = np.full(self.historySize, sys.float_info.max)
|
|
223
251
|
self.fitness_history[0] = self.best_value
|
|
224
252
|
self.arz = None
|
|
253
|
+
self.fitness = None
|
|
225
254
|
|
|
226
|
-
|
|
227
|
-
def ask(self):
|
|
255
|
+
def ask(self) -> np.array:
|
|
228
256
|
"""ask for popsize new argument vectors.
|
|
229
257
|
|
|
230
258
|
Returns
|
|
@@ -232,9 +260,11 @@ class Cmaes(object):
|
|
|
232
260
|
xs : popsize sized list of dim sized argument lists."""
|
|
233
261
|
|
|
234
262
|
self.newArgs()
|
|
235
|
-
return [self.fitfun.decode(x) for x in self.arx]
|
|
263
|
+
return np.array([self.fitfun.decode(x) for x in self.arx])
|
|
236
264
|
|
|
237
|
-
def tell(self,
|
|
265
|
+
def tell(self,
|
|
266
|
+
ys: np.ndarray,
|
|
267
|
+
xs: Optional[np.ndarray] = None) -> int:
|
|
238
268
|
"""tell function values for the argument lists retrieved by ask().
|
|
239
269
|
|
|
240
270
|
Parameters
|
|
@@ -264,15 +294,106 @@ class Cmaes(object):
|
|
|
264
294
|
self.updateCMA()
|
|
265
295
|
self.arz = None
|
|
266
296
|
return self.stop
|
|
267
|
-
|
|
297
|
+
|
|
298
|
+
def population(self) -> np.array:
|
|
299
|
+
return self.fitfun.decode(self.arx)
|
|
300
|
+
|
|
301
|
+
def result(self) -> OptimizeResult:
|
|
302
|
+
return OptimizeResult(x=self.best_x, fun=self.best_value,
|
|
303
|
+
nfev=self.fitfun.evaluation_counter,
|
|
304
|
+
nit=self.iterations, status=self.stop, success=True)
|
|
305
|
+
|
|
306
|
+
def ask_one(self) -> np.array:
|
|
307
|
+
"""ask for one new argument vector.
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
x : dim sized argument ."""
|
|
312
|
+
arz = self.randn(self.dim)
|
|
313
|
+
delta = (self.BD @ arz.transpose()) * self.sigma
|
|
314
|
+
arx = self.fitfun.closestFeasible(self.xmean + delta.transpose())
|
|
315
|
+
return self.fitfun.decode(arx)
|
|
316
|
+
|
|
317
|
+
def tell_one(self,
|
|
318
|
+
y: float,
|
|
319
|
+
x: np.array) -> int:
|
|
320
|
+
"""tell function value for a argument list retrieved by ask_one().
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
y : function value
|
|
325
|
+
x : dim sized argument list
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
stop : int termination criteria, if != 0 loop should stop."""
|
|
330
|
+
|
|
331
|
+
if self.fitness is None or not type(self.fitness) is list:
|
|
332
|
+
self.arx = []
|
|
333
|
+
self.fitness = []
|
|
334
|
+
self.fitness.append(y)
|
|
335
|
+
self.arx.append(x)
|
|
336
|
+
if len(self.fitness) >= self.popsize:
|
|
337
|
+
self.fitness = np.asarray(self.fitness)
|
|
338
|
+
self.arx = np.array([self.fitfun.encode(x) for x in self.arx])
|
|
339
|
+
try:
|
|
340
|
+
self.arz = (linalg.inv(self.BD) @ \
|
|
341
|
+
((self.arx - self.xmean).transpose() / self.sigma)).transpose()
|
|
342
|
+
except Exception:
|
|
343
|
+
if self.arz is None:
|
|
344
|
+
self.arz = self.randn(self.popsize, self.dim)
|
|
345
|
+
self.iterations += 1
|
|
346
|
+
self.updateCMA()
|
|
347
|
+
self.arz = None
|
|
348
|
+
self.arx = []
|
|
349
|
+
self.fitness = []
|
|
350
|
+
|
|
351
|
+
if is_debug_active():
|
|
352
|
+
self.n_evals.value += 1
|
|
353
|
+
if y < self.best_y.value or self.n_evals.value % 1000 == 999:
|
|
354
|
+
if y < self.best_y.value: self.best_y.value = y
|
|
355
|
+
t = time() - self.time_0
|
|
356
|
+
c = self.n_evals.value
|
|
357
|
+
message = '"c/t={0:.2f} c={1:d} t={2:.2f} y={3:.5f} yb={4:.5f} x={5!s}'.format(
|
|
358
|
+
c/t, c, t, y, self.best_y.value, x)
|
|
359
|
+
logger.debug(message)
|
|
360
|
+
return self.stop
|
|
361
|
+
|
|
268
362
|
def newArgs(self):
|
|
269
|
-
self.xmean = self.fitfun.closestFeasible(self.xmean)
|
|
270
|
-
self.fitness = np.full(self.popsize, math.inf)
|
|
271
363
|
# generate random offspring
|
|
272
364
|
self.arz = self.randn(self.popsize, self.dim)
|
|
273
365
|
delta = (self.BD @ self.arz.transpose()) * self.sigma
|
|
274
366
|
self.arx = self.fitfun.closestFeasible(self.xmean + delta.transpose())
|
|
275
|
-
|
|
367
|
+
|
|
368
|
+
def do_optimize_delayed_update(self, fun, max_evals=None, workers=mp.cpu_count()):
|
|
369
|
+
if not max_evals is None:
|
|
370
|
+
self.max_evaluations = max_evals
|
|
371
|
+
evaluator = Evaluator(fun)
|
|
372
|
+
evaluator.start(workers)
|
|
373
|
+
evals_x = {}
|
|
374
|
+
self.evals = 0;
|
|
375
|
+
for _ in range(workers): # fill queue
|
|
376
|
+
x = self.ask_one()
|
|
377
|
+
evaluator.pipe[0].send((self.evals, x))
|
|
378
|
+
evals_x[self.evals] = x # store x
|
|
379
|
+
self.evals += 1
|
|
380
|
+
|
|
381
|
+
while True: # read from pipe, tell es and create new x
|
|
382
|
+
evals, y = evaluator.pipe[0].recv()
|
|
383
|
+
|
|
384
|
+
x = evals_x[evals] # retrieve evaluated x
|
|
385
|
+
del evals_x[evals]
|
|
386
|
+
stop = self.tell_one(y, x) # tell evaluated x
|
|
387
|
+
if stop != 0 or self.evals >= self.max_evaluations:
|
|
388
|
+
break # shutdown worker if stop criteria met
|
|
389
|
+
|
|
390
|
+
x = self.ask_one() # create new x
|
|
391
|
+
evaluator.pipe[0].send((self.evals, x))
|
|
392
|
+
evals_x[self.evals] = x # store x
|
|
393
|
+
self.evals += 1
|
|
394
|
+
evaluator.stop()
|
|
395
|
+
return self.best_x, self.best_value, evals, self.iterations, self.stop
|
|
396
|
+
|
|
276
397
|
def doOptimize(self):
|
|
277
398
|
# -------------------- Generation Loop --------------------------------
|
|
278
399
|
while True:
|
|
@@ -281,10 +402,9 @@ class Cmaes(object):
|
|
|
281
402
|
self.iterations += 1
|
|
282
403
|
if self.fitfun.evaluation_counter > self.max_evaluations:
|
|
283
404
|
break
|
|
284
|
-
|
|
285
|
-
self.
|
|
286
|
-
self.
|
|
287
|
-
self.updateCMA()
|
|
405
|
+
xs = self.ask()
|
|
406
|
+
ys = self.fitfun.values(xs)
|
|
407
|
+
self.tell(ys, xs)
|
|
288
408
|
if self.stop != 0:
|
|
289
409
|
break
|
|
290
410
|
return self.best_x, self.best_value, self.fitfun.evaluation_counter, self.iterations, self.stop
|
|
@@ -300,6 +420,9 @@ class Cmaes(object):
|
|
|
300
420
|
if self.best_value > best_fitness:
|
|
301
421
|
self.best_value = best_fitness
|
|
302
422
|
self.best_x = self.fitfun.decode(self.arx[arindex[0]])
|
|
423
|
+
if best_fitness < self.stop_fitness:
|
|
424
|
+
self.stop = 1
|
|
425
|
+
return
|
|
303
426
|
|
|
304
427
|
# Calculate new xmean, this is selection and recombination
|
|
305
428
|
xold = self.xmean # for speed up of Eq. (2) and (3)
|
|
@@ -309,30 +432,29 @@ class Cmaes(object):
|
|
|
309
432
|
bestArz = self.arz[bestIndex]
|
|
310
433
|
zmean = np.transpose(bestArz) @ self.weights
|
|
311
434
|
hsig = self.updateEvolutionPaths(zmean, xold)
|
|
312
|
-
negccov = self.updateCovariance(hsig, bestArx, self.arz, arindex, xold)
|
|
313
|
-
self.updateBD(negccov)
|
|
314
435
|
# Adapt step size sigma - Eq. (5)
|
|
315
436
|
self.sigma *= math.exp(min(1.0, (self.normps / self.chiN - 1.) * self.cs / self.damps))
|
|
316
|
-
|
|
317
|
-
if self.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
self.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
self.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
437
|
+
|
|
438
|
+
if self.iterations >= self.last_update + self.lazy_update_gap:
|
|
439
|
+
self.last_update = self.iterations
|
|
440
|
+
negccov = self.updateCovariance(hsig, bestArx, self.arz, arindex, xold)
|
|
441
|
+
self.updateBD(negccov)
|
|
442
|
+
# handle termination criteria
|
|
443
|
+
sqrtDiagC = np.sqrt(np.abs(self.diagC))
|
|
444
|
+
pcCol = self.pc
|
|
445
|
+
for i in range(self.dim):
|
|
446
|
+
if self.sigma * max(abs(pcCol[i]), sqrtDiagC[i]) > self.stopTolX:
|
|
447
|
+
break
|
|
448
|
+
if i == self.dim - 1:
|
|
449
|
+
self.stop = 2
|
|
450
|
+
if self.stop != 0:
|
|
451
|
+
return
|
|
452
|
+
for i in range(self.dim):
|
|
453
|
+
if self.sigma * sqrtDiagC[i] > self.stopTolUpX:
|
|
454
|
+
self.stop = 3
|
|
455
|
+
break
|
|
456
|
+
if self.stop != 0:
|
|
457
|
+
return
|
|
336
458
|
history_best = min(self.fitness_history)
|
|
337
459
|
history_worst = max(self.fitness_history)
|
|
338
460
|
if self.iterations > 2 and max(history_worst, worstFitness) - min(history_best, best_fitness) < self.stopTolFun:
|
|
@@ -472,95 +594,4 @@ class Cmaes(object):
|
|
|
472
594
|
self.diagD = np.sqrt(self.diagD) # diagD contains standard deviations now
|
|
473
595
|
|
|
474
596
|
self.BD = self.B * self.diagD # O(n^2)
|
|
475
|
-
|
|
476
|
-
def serial(fun):
|
|
477
|
-
"""Convert an objective function for serial execution for cmaes.minimize.
|
|
478
|
-
|
|
479
|
-
Parameters
|
|
480
|
-
----------
|
|
481
|
-
fun : objective function mapping a list of float arguments to a float value
|
|
482
|
-
|
|
483
|
-
Returns
|
|
484
|
-
-------
|
|
485
|
-
out : function
|
|
486
|
-
A function mapping a list of lists of float arguments to a list of float values
|
|
487
|
-
by applying the input function in a loop."""
|
|
488
|
-
|
|
489
|
-
return lambda xs : [_tryfun(fun, x) for x in xs]
|
|
490
|
-
|
|
491
|
-
class parallel(object):
|
|
492
|
-
"""Convert an objective function for parallel execution for cmaes.minimize.
|
|
493
|
-
|
|
494
|
-
Parameters
|
|
495
|
-
----------
|
|
496
|
-
fun : objective function mapping a list of float arguments to a float value.
|
|
497
|
-
|
|
498
|
-
represents a function mapping a list of lists of float arguments to a list of float values
|
|
499
|
-
by applying the input function using parallel processes. stop needs to be called to avoid
|
|
500
|
-
a resource leak"""
|
|
501
|
-
|
|
502
|
-
def __init__(self, fun, workers):
|
|
503
|
-
self.evaluator = Evaluator(fun)
|
|
504
|
-
self.evaluator.start(workers)
|
|
505
|
-
|
|
506
|
-
def __call__(self, xs):
|
|
507
|
-
return eval_parallel(xs, self.evaluator)
|
|
508
|
-
|
|
509
|
-
def stop(self):
|
|
510
|
-
self.evaluator.stop()
|
|
511
|
-
|
|
512
|
-
def _func_serial(fun, num, pid, xs, ys):
|
|
513
|
-
for i in range(pid, len(xs), num):
|
|
514
|
-
ys[i] = _tryfun(fun, xs[i])
|
|
515
|
-
|
|
516
|
-
def _tryfun(fun, x):
|
|
517
|
-
try:
|
|
518
|
-
fit = fun(x)
|
|
519
|
-
return fit if math.isfinite(fit) else sys.float_info.max
|
|
520
|
-
except Exception:
|
|
521
|
-
return sys.float_info.max
|
|
522
|
-
|
|
523
|
-
def _check_bounds(bounds, guess, rg):
|
|
524
|
-
if bounds is None and guess is None:
|
|
525
|
-
raise ValueError('either guess or bounds need to be defined')
|
|
526
|
-
if bounds is None:
|
|
527
|
-
return None, None, np.asarray(guess)
|
|
528
|
-
if guess is None:
|
|
529
|
-
guess = rg.uniform(bounds.lb, bounds.ub)
|
|
530
|
-
return np.asarray(bounds.lb), np.asarray(bounds.ub), np.asarray(guess)
|
|
531
|
-
|
|
532
|
-
class _Fittness(object):
|
|
533
|
-
"""wrapper around the objective function, scales relative to boundaries."""
|
|
534
|
-
|
|
535
|
-
def __init__(self, fun, lower, upper):
|
|
536
|
-
self.fun = fun
|
|
537
|
-
self.evaluation_counter = 0
|
|
538
|
-
self.lower = lower
|
|
539
|
-
if not lower is None:
|
|
540
|
-
self.upper = upper
|
|
541
|
-
self.scale = 0.5 * (upper - lower)
|
|
542
|
-
self.typx = 0.5 * (upper + lower)
|
|
543
|
-
|
|
544
|
-
def values(self, Xs): #enables parallel evaluation
|
|
545
|
-
values = self.fun([self.decode(X) for X in Xs])
|
|
546
|
-
self.evaluation_counter += len(Xs)
|
|
547
|
-
return np.array(values)
|
|
548
|
-
|
|
549
|
-
def closestFeasible(self, X):
|
|
550
|
-
if self.lower is None:
|
|
551
|
-
return X
|
|
552
|
-
else:
|
|
553
|
-
return np.maximum(np.minimum(X, 1.0), -1.0)
|
|
554
|
-
|
|
555
|
-
def encode(self, X):
|
|
556
|
-
if self.lower is None:
|
|
557
|
-
return X
|
|
558
|
-
else:
|
|
559
|
-
return (X - self.typx) / self.scale
|
|
560
|
-
|
|
561
|
-
def decode(self, X):
|
|
562
|
-
if self.lower is None:
|
|
563
|
-
return X
|
|
564
|
-
else:
|
|
565
|
-
return (X * self.scale) + self.typx
|
|
566
|
-
|
|
597
|
+
|