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/cmaescpp.py CHANGED
@@ -13,24 +13,32 @@ import os
13
13
  import math
14
14
  import ctypes as ct
15
15
  import numpy as np
16
- from numpy.random import MT19937, Generator
17
- from scipy.optimize import OptimizeResult
18
- from fcmaes.cmaes import _check_bounds
16
+ from numpy.random import PCG64DXSM, Generator
17
+ from scipy.optimize import OptimizeResult, Bounds
18
+ from fcmaes.evaluator import _check_bounds, _get_bounds, mo_call_back_type, callback_so, callback_par, call_back_par, parallel, libcmalib
19
+
20
+ from typing import Optional, Callable, Union
21
+ from numpy.typing import ArrayLike
19
22
 
20
23
  os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
21
24
 
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
- accuracy = 1.0,
30
- stop_fittness = None,
31
- is_terminate = None,
32
- rg = Generator(MT19937()),
33
- runid=0):
25
+ def minimize(fun: Callable[[ArrayLike], float],
26
+ bounds: Optional[Bounds] = None,
27
+ x0: Optional[ArrayLike] = None,
28
+ input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
29
+ popsize: Optional[int] = 31,
30
+ max_evaluations: Optional[int] = 100000,
31
+ accuracy: Optional[float] = 1.0,
32
+ stop_fitness: Optional[float] = -np.inf,
33
+ stop_hist: Optional[float] = -1,
34
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
35
+ runid: Optional[int] = 0,
36
+ workers: Optional[int] = 1,
37
+ normalize: Optional[bool] = True,
38
+ delayed_update: Optional[bool] = True,
39
+ update_gap: Optional[int] = None
40
+ ) -> OptimizeResult:
41
+
34
42
  """Minimization of a scalar function of one or more variables using a
35
43
  C++ CMA-ES implementation called via ctypes.
36
44
 
@@ -38,38 +46,43 @@ def minimize(fun,
38
46
  ----------
39
47
  fun : callable
40
48
  The objective function to be minimized.
41
- ``fun(x, *args) -> float``
42
- where ``x`` is an 1-D array with shape (n,) and ``args``
43
- is a tuple of the fixed parameters needed to completely
44
- specify the function.
49
+ ``fun(x) -> float``
50
+ where ``x`` is an 1-D array with shape (dim,)
45
51
  bounds : sequence or `Bounds`, optional
46
52
  Bounds on variables. There are two ways to specify the bounds:
47
53
  1. Instance of the `scipy.Bounds` class.
48
54
  2. Sequence of ``(min, max)`` pairs for each element in `x`. None
49
55
  is used to specify no bound.
50
- x0 : ndarray, shape (n,)
51
- Initial guess. Array of real elements of size (n,),
52
- where 'n' is the number of independent variables.
53
- input_sigma : ndarray, shape (n,) or scalar
56
+ x0 : ndarray, shape (dim,)
57
+ Initial guess. Array of real elements of size (dim,),
58
+ where 'dim' is the number of independent variables.
59
+ input_sigma : ndarray, shape (dim,) or scalar
54
60
  Initial step size for each dimension.
55
61
  popsize = int, optional
56
62
  CMA-ES population size.
57
63
  max_evaluations : int, optional
58
64
  Forced termination after ``max_evaluations`` function evaluations.
59
- max_iterations : int, optional
60
- Forced termination after ``max_iterations`` iterations.
61
65
  accuracy : float, optional
62
66
  values > 1.0 reduce the accuracy.
63
- stop_fittness : float, optional
67
+ stop_fitness : float, optional
64
68
  Limit for fitness value. If reached minimize terminates.
65
- is_terminate : callable, optional
66
- Callback to be used if the caller of minimize wants to
67
- decide when to terminate.
69
+ stop_hist : float, optional
70
+ Set to 0 if you want to prevent premature termination because
71
+ there is no progress
68
72
  rg = numpy.random.Generator, optional
69
73
  Random generator for creating random guesses.
70
74
  runid : int, optional
71
75
  id used by the is_terminate callback to identify the CMA-ES run.
72
-
76
+ workers : int or None, optional
77
+ If not workers is None, function evaluation is performed in parallel for the whole population.
78
+ Useful for costly objective functions but is deactivated for parallel retry.
79
+ normalize : boolean, optional
80
+ if true pheno -> geno transformation maps arguments to interval [-1,1]
81
+ delayed_update : boolean, optional
82
+ if true uses delayed update / C++ parallelism, i false uses Python multithreading
83
+ update_gap : int, optional
84
+ number of iterations without distribution update
85
+
73
86
  Returns
74
87
  -------
75
88
  res : scipy.OptimizeResult
@@ -82,72 +95,225 @@ def minimize(fun,
82
95
  ``success`` a Boolean flag indicating if the optimizer exited successfully. """
83
96
 
84
97
  lower, upper, guess = _check_bounds(bounds, x0, rg)
85
- n = guess.size
86
- if lower is None:
87
- lower = [0]*n
88
- upper = [0]*n
98
+ dim = guess.size
99
+ if workers is None:
100
+ workers = 0
89
101
  mu = int(popsize/2)
102
+ if callable(input_sigma):
103
+ input_sigma=input_sigma()
90
104
  if np.ndim(input_sigma) == 0:
91
- input_sigma = [input_sigma] * n
92
- if stop_fittness is None:
93
- stop_fittness = math.inf
94
- if is_terminate is None:
95
- is_terminate=_is_terminate_false
96
- use_terminate = False
97
- else:
98
- use_terminate = True
99
- array_type = ct.c_double * n
100
- c_callback = call_back_type(callback(fun))
101
- c_is_terminate = is_terminate_type(is_terminate)
105
+ input_sigma = [input_sigma] * dim
106
+ if stop_hist is None:
107
+ stop_hist = -1;
108
+ array_type = ct.c_double * dim
109
+ c_callback = mo_call_back_type(callback_so(fun, dim))
110
+ parfun = None if delayed_update == True or workers is None or workers <= 1 else parallel(fun, workers)
111
+ c_callback_par = call_back_par(callback_par(fun, parfun))
112
+ res = np.empty(dim+4)
113
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
102
114
  try:
103
- res = optimizeACMA_C(runid, c_callback, n, array_type(*guess), array_type(*lower), array_type(*upper),
104
- array_type(*input_sigma), max_iterations, max_evaluations, stop_fittness, mu,
105
- popsize, accuracy, use_terminate, c_is_terminate, int(rg.uniform(0, 2**32 - 1)))
106
-
107
- x = np.array(np.fromiter(res, dtype=np.float64, count=n))
108
- val = res[n]
109
- evals = int(res[n+1])
110
- iterations = int(res[n+2])
111
- stop = int(res[n+3])
112
- freemem(res)
113
- return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
115
+ optimizeACMA_C(runid, c_callback, c_callback_par,
116
+ dim, array_type(*guess),
117
+ None if lower is None else array_type(*lower),
118
+ None if upper is None else array_type(*upper),
119
+ array_type(*input_sigma), max_evaluations, stop_fitness, stop_hist, mu,
120
+ popsize, accuracy, int(rg.uniform(0, 2**32 - 1)),
121
+ normalize, delayed_update, -1 if update_gap is None else update_gap,
122
+ workers, res_p)
123
+ x = res[:dim]
124
+ val = res[dim]
125
+ evals = int(res[dim+1])
126
+ iterations = int(res[dim+2])
127
+ stop = int(res[dim+3])
128
+ res = OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
114
129
  except Exception as ex:
115
- return OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
130
+ res = OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
131
+ if not parfun is None:
132
+ parfun.stop()
133
+ return res
116
134
 
117
- class callback(object):
118
-
119
- def __init__(self, fun):
120
- self.fun = fun
135
+ class ACMA_C:
136
+
137
+ def __init__(self,
138
+ dim,
139
+ bounds: Optional[Bounds] = None,
140
+ x0: Optional[ArrayLike] = None,
141
+ input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
142
+ popsize: Optional[int] = 31,
143
+ max_evaluations: Optional[int] = 100000,
144
+ accuracy: Optional[float] = 1.0,
145
+ stop_fitness: Optional[float] = -np.inf,
146
+ stop_hist: Optional[float] = -1,
147
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
148
+ runid: Optional[int] = 0,
149
+ normalize: Optional[bool] = True,
150
+ delayed_update: Optional[bool] = True,
151
+ update_gap: Optional[int] = None
152
+ ):
153
+
154
+ """Parameters
155
+ ----------
156
+ dim : int
157
+ dimension of the argument of the objective function
158
+ bounds : sequence or `Bounds`, optional
159
+ Bounds on variables. There are two ways to specify the bounds:
160
+ 1. Instance of the `scipy.Bounds` class.
161
+ 2. Sequence of ``(min, max)`` pairs for each element in `x`. None
162
+ is used to specify no bound.
163
+ x0 : ndarray, shape (dim,)
164
+ Initial guess. Array of real elements of size (dim,),
165
+ where 'dim' is the number of independent variables.
166
+ input_sigma : ndarray, shape (dim,) or scalar
167
+ Initial step size for each dimension.
168
+ popsize = int, optional
169
+ CMA-ES population size.
170
+ max_evaluations : int, optional
171
+ Forced termination after ``max_evaluations`` function evaluations.
172
+ accuracy : float, optional
173
+ values > 1.0 reduce the accuracy.
174
+ stop_fitness : float, optional
175
+ Limit for fitness value. If reached minimize terminates.
176
+ stop_hist : float, optional
177
+ Set to 0 if you want to prevent premature termination because
178
+ there is no progress
179
+ rg = numpy.random.Generator, optional
180
+ Random generator for creating random guesses.
181
+ runid : int, optional
182
+ id used by the is_terminate callback to identify the CMA-ES run.
183
+ normalize : boolean, optional
184
+ if true pheno -> geno transformation maps arguments to interval [-1,1]
185
+ delayed_update : boolean, optional
186
+ if true uses delayed update / C++ parallelism, i false uses Python multithreading
187
+ update_gap : int, optional
188
+ number of iterations without distribution update"""
189
+
190
+ lower, upper, guess = _get_bounds(dim, bounds, x0, rg)
191
+ if lower is None:
192
+ lower = [0]*dim
193
+ upper = [0]*dim
194
+ mu = int(popsize/2)
195
+ if callable(input_sigma):
196
+ input_sigma=input_sigma()
197
+ if np.ndim(input_sigma) == 0:
198
+ input_sigma = [input_sigma] * dim
199
+ if stop_hist is None:
200
+ stop_hist = -1;
201
+ array_type = ct.c_double * dim
202
+ try:
203
+ self.ptr = initACMA_C(runid,
204
+ dim, array_type(*guess), array_type(*lower), array_type(*upper),
205
+ array_type(*input_sigma), max_evaluations, stop_fitness, stop_hist, mu,
206
+ popsize, accuracy, int(rg.uniform(0, 2**32 - 1)),
207
+ normalize, delayed_update, -1 if update_gap is None else update_gap)
208
+ self.popsize = popsize
209
+ self.dim = dim
210
+ except Exception as ex:
211
+ print (ex)
212
+ pass
121
213
 
122
- def __call__(self, n, x):
214
+ def __del__(self):
215
+ destroyACMA_C(self.ptr)
216
+
217
+ def ask(self) -> np.array:
123
218
  try:
124
- fit = self.fun([x[i] for i in range(n)])
125
- return fit if math.isfinite(fit) else sys.float_info.max
126
- except Exception:
127
- return sys.float_info.max
128
-
129
- def _is_terminate_false(runid, iterations, val):
130
- return False
131
-
132
- basepath = os.path.dirname(os.path.abspath(__file__))
219
+ popsize = self.popsize
220
+ n = self.dim
221
+ res = np.empty(popsize*n)
222
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
223
+ askACMA_C(self.ptr, res_p)
224
+ xs = np.empty((popsize, n))
225
+ for p in range(popsize):
226
+ xs[p,:] = res[p*n : (p+1)*n]
227
+ return xs
228
+ except Exception as ex:
229
+ print (ex)
230
+ return None
133
231
 
134
- if sys.platform.startswith('linux'):
135
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.so')
136
- elif 'mac' in sys.platform:
137
- libgtoplib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dylib')
138
- else:
139
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dll')
232
+ def tell(self,
233
+ ys: np.ndarray,
234
+ xs: Optional[np.ndarray] = None) -> int:
235
+ if not xs is None:
236
+ return self.tell_x_(ys, xs)
237
+ try:
238
+ array_type_ys = ct.c_double * len(ys)
239
+ return tellACMA_C(self.ptr, array_type_ys(*ys))
240
+ except Exception as ex:
241
+ print (ex)
242
+ return -1
140
243
 
141
- call_back_type = ct.CFUNCTYPE(ct.c_double, ct.c_int, ct.POINTER(ct.c_double))
142
- is_terminate_type = ct.CFUNCTYPE(ct.c_bool, ct.c_long, ct.c_int, ct.c_double)
244
+ def tell_x_(self, ys: np.ndarray, xs: np.ndarray):
245
+ try:
246
+ flat_xs = xs.flatten()
247
+ array_type_xs = ct.c_double * len(flat_xs)
248
+ array_type_ys = ct.c_double * len(ys)
249
+ return tellXACMA_C(self.ptr, array_type_ys(*ys), array_type_xs(*flat_xs))
250
+ except Exception as ex:
251
+ print (ex)
252
+ return -1
253
+
254
+ def population(self) -> np.array:
255
+ try:
256
+ popsize = self.popsize
257
+ n = self.dim
258
+ res = np.empty(popsize*n)
259
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
260
+ populationACMA_C(self.ptr, res_p)
261
+ xs = np.array(popsize, n)
262
+ for p in range(popsize):
263
+ xs[p] = res[p*n : (p+1)*n]
264
+ return xs
265
+ except Exception as ex:
266
+ print (ex)
267
+ return None
143
268
 
144
- optimizeACMA_C = libcmalib.optimizeACMA_C
145
- optimizeACMA_C.argtypes = [ct.c_long, call_back_type, ct.c_int, \
146
- ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
147
- ct.POINTER(ct.c_double), ct.c_int, ct.c_int, ct.c_double, ct.c_int, ct.c_int, \
148
- ct.c_double, ct.c_bool, is_terminate_type, ct.c_long]
269
+ def result(self) -> OptimizeResult:
270
+ res = np.empty(self.dim+4)
271
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
272
+ try:
273
+ resultACMA_C(self.ptr, res_p)
274
+ x = res[:self.dim]
275
+ val = res[self.dim]
276
+ evals = int(res[self.dim+1])
277
+ iterations = int(res[self.dim+2])
278
+ stop = int(res[self.dim+3])
279
+ res = OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
280
+ except Exception as ex:
281
+ res = OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
282
+ return res
149
283
 
150
- optimizeACMA_C.restype = ct.POINTER(ct.c_double)
284
+ if not libcmalib is None:
151
285
 
152
- freemem = libcmalib.free_mem
153
- freemem.argtypes = [ct.POINTER(ct.c_double)]
286
+ optimizeACMA_C = libcmalib.optimizeACMA_C
287
+ optimizeACMA_C.argtypes = [ct.c_long, mo_call_back_type, call_back_par, ct.c_int, \
288
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
289
+ ct.POINTER(ct.c_double), ct.c_int, ct.c_double, ct.c_double, ct.c_int, ct.c_int, \
290
+ ct.c_double, ct.c_long, ct.c_bool, ct.c_bool, ct.c_int,
291
+ ct.c_int, ct.POINTER(ct.c_double)]
292
+
293
+ initACMA_C = libcmalib.initACMA_C
294
+ initACMA_C.argtypes = [ct.c_long, ct.c_int, \
295
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
296
+ ct.POINTER(ct.c_double), ct.c_int, ct.c_double, ct.c_double, ct.c_int,
297
+ ct.c_int, ct.c_double, ct.c_long, ct.c_bool, ct.c_bool, ct.c_int]
298
+
299
+ initACMA_C.restype = ct.c_void_p
300
+
301
+ destroyACMA_C = libcmalib.destroyACMA_C
302
+ destroyACMA_C.argtypes = [ct.c_void_p]
303
+
304
+ askACMA_C = libcmalib.askACMA_C
305
+ askACMA_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
306
+
307
+ tellACMA_C = libcmalib.tellACMA_C
308
+ tellACMA_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
309
+ tellACMA_C.restype = ct.c_int
310
+
311
+ tellXACMA_C = libcmalib.tellXACMA_C
312
+ tellXACMA_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double), ct.POINTER(ct.c_double)]
313
+ tellXACMA_C.restype = ct.c_int
314
+
315
+ populationACMA_C = libcmalib.populationACMA_C
316
+ populationACMA_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
317
+
318
+ resultACMA_C = libcmalib.resultACMA_C
319
+ resultACMA_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]