fcmaes 1.3.17__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/crfmnescpp.py ADDED
@@ -0,0 +1,273 @@
1
+ # Copyright (c) Dietmar Wolz.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory.
5
+
6
+ """ Eigen based implementation of Fast Moving Natural Evolution Strategy
7
+ for High-Dimensional Problems (CR-FM-NES), see https://arxiv.org/abs/2201.11422 .
8
+ Derived from https://github.com/nomuramasahir0/crfmnes .
9
+ """
10
+
11
+ import sys
12
+ import os
13
+ import math
14
+ import ctypes as ct
15
+ import numpy as np
16
+ from numpy.random import PCG64DXSM, Generator
17
+ from scipy.optimize import OptimizeResult, Bounds
18
+ from fcmaes.evaluator import _check_bounds, _get_bounds, callback_par, parallel, call_back_par, libcmalib
19
+
20
+ from typing import Optional, Callable, Union
21
+ from numpy.typing import ArrayLike
22
+
23
+ os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
24
+
25
+ def minimize(fun: Callable[[ArrayLike], float],
26
+ bounds: Optional[Bounds] = None,
27
+ x0: Optional[ArrayLike] = None,
28
+ input_sigma = 0.3,
29
+ popsize = 32,
30
+ max_evaluations = 100000,
31
+ workers = None,
32
+ stop_fitness = -np.inf,
33
+ rg = Generator(PCG64DXSM()),
34
+ runid=0,
35
+ normalize = False,
36
+ use_constraint_violation = True,
37
+ penalty_coef = 1E5
38
+ ) -> OptimizeResult:
39
+
40
+ """Minimization of a scalar function of one or more variables using a
41
+ C++ CR-FM-NES implementation called via ctypes.
42
+
43
+ Parameters
44
+ ----------
45
+ fun : callable
46
+ The objective function to be minimized.
47
+ ``fun(x) -> float``
48
+ where ``x`` is an 1-D array with shape (dim,)
49
+ bounds : sequence or `Bounds`, optional
50
+ Bounds on variables. There are two ways to specify the bounds:
51
+ 1. Instance of the `scipy.Bounds` class.
52
+ 2. Sequence of ``(min, max)`` pairs for each element in `x`. None
53
+ is used to specify no bound.
54
+ x0 : ndarray, shape (dim,)
55
+ Initial guess. Array of real elements of size (dim,),
56
+ where 'dim' is the number of independent variables.
57
+ input_sigma : float, optional
58
+ Initial step size.
59
+ popsize = int, optional
60
+ CMA-ES population size.
61
+ max_evaluations : int, optional
62
+ Forced termination after ``max_evaluations`` function evaluations.
63
+ workers : int or None, optional
64
+ If workers > 1, function evaluation is performed in parallel for the whole population.
65
+ Useful for costly objective functions but is deactivated for parallel retry.
66
+ stop_fitness : float, optional
67
+ Limit for fitness value. If reached minimize terminates.
68
+ rg = numpy.random.Generator, optional
69
+ Random generator for creating random guesses.
70
+ runid : int, optional
71
+ id used by the is_terminate callback to identify the CMA-ES run.
72
+ normalize : boolean, optional
73
+ if true pheno -> geno transformation maps arguments to interval [-1,1]
74
+
75
+ Returns
76
+ -------
77
+ res : scipy.OptimizeResult
78
+ The optimization result is represented as an ``OptimizeResult`` object.
79
+ Important attributes are: ``x`` the solution array,
80
+ ``fun`` the best function value,
81
+ ``nfev`` the number of function evaluations,
82
+ ``nit`` the number of CMA-ES iterations,
83
+ ``status`` the stopping critera and
84
+ ``success`` a Boolean flag indicating if the optimizer exited successfully. """
85
+
86
+ lower, upper, guess = _check_bounds(bounds, x0, rg)
87
+ dim = guess.size
88
+ if popsize is None:
89
+ popsize = 32
90
+ if popsize % 2 == 1: # requires even popsize
91
+ popsize += 1
92
+ if callable(input_sigma):
93
+ input_sigma=input_sigma()
94
+ if np.ndim(input_sigma) > 0:
95
+ input_sigma = np.mean(input_sigma)
96
+ array_type = ct.c_double * dim
97
+ parfun = None if (workers is None or workers <= 1) else parallel(fun, workers)
98
+ c_callback_par = call_back_par(callback_par(fun, parfun))
99
+ res = np.empty(dim+4)
100
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
101
+ try:
102
+ optimizeCRFMNES_C(runid, c_callback_par, dim, array_type(*guess),
103
+ None if lower is None else array_type(*lower),
104
+ None if upper is None else array_type(*upper),
105
+ input_sigma, max_evaluations, stop_fitness,
106
+ popsize, int(rg.uniform(0, 2**32 - 1)), penalty_coef,
107
+ use_constraint_violation, normalize, res_p)
108
+ x = res[:dim]
109
+ val = res[dim]
110
+ evals = int(res[dim+1])
111
+ iterations = int(res[dim+2])
112
+ stop = int(res[dim+3])
113
+ res = OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
114
+ except Exception as ex:
115
+ res = OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
116
+ if not parfun is None:
117
+ parfun.stop()
118
+ return res
119
+
120
+ class CRFMNES_C:
121
+
122
+ def __init__(self,
123
+ dim: int,
124
+ bounds: Optional[Bounds] = None,
125
+ x0: Optional[ArrayLike] = None,
126
+ input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
127
+ popsize: Optional[int] = 32,
128
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
129
+ runid: Optional[int] = 0,
130
+ normalize: Optional[bool] = False,
131
+ use_constraint_violation: Optional[bool] = True,
132
+ penalty_coef: Optional[float] = 1E5
133
+ ):
134
+
135
+ """Minimization of a scalar function of one or more variables using a
136
+ C++ CR-FM-NES implementation called via ctypes.
137
+
138
+ Parameters
139
+ ----------
140
+ dim : int
141
+ dimension of the argument of the objective function
142
+ bounds : sequence or `Bounds`, optional
143
+ Bounds on variables. There are two ways to specify the bounds:
144
+ 1. Instance of the `scipy.Bounds` class.
145
+ 2. Sequence of ``(min, max)`` pairs for each element in `x`. None
146
+ is used to specify no bound.
147
+ x0 : ndarray, shape (dim,)
148
+ Initial guess. Array of real elements of size (dim,),
149
+ where 'dim' is the number of independent variables.
150
+ input_sigma : float, optional
151
+ Initial step size.
152
+ popsize = int, optional
153
+ CMA-ES population size.
154
+ rg = numpy.random.Generator, optional
155
+ Random generator for creating random guesses.
156
+ runid : int, optional
157
+ id used by the is_terminate callback to identify the CMA-ES run.
158
+ normalize : boolean, optional
159
+ if true pheno -> geno transformation maps arguments to interval [-1,1]"""
160
+
161
+ lower, upper, guess = _get_bounds(dim, bounds, x0, rg)
162
+ if popsize is None:
163
+ popsize = 32
164
+ if popsize % 2 == 1: # requires even popsize
165
+ popsize += 1
166
+ if lower is None:
167
+ lower = [0]*dim
168
+ upper = [0]*dim
169
+ if callable(input_sigma):
170
+ input_sigma=input_sigma()
171
+ if np.ndim(input_sigma) > 0:
172
+ input_sigma = np.mean(input_sigma)
173
+ array_type = ct.c_double * dim
174
+ try:
175
+ self.ptr = initCRFMNES_C(runid, dim, array_type(*guess),
176
+ array_type(*lower), array_type(*upper),
177
+ input_sigma, popsize, int(rg.uniform(0, 2**32 - 1)), penalty_coef,
178
+ use_constraint_violation, normalize)
179
+ self.popsize = popsize
180
+ self.dim = dim
181
+ except Exception as ex:
182
+ print (ex)
183
+ pass
184
+
185
+ def __del__(self):
186
+ destroyCRFMNES_C(self.ptr)
187
+
188
+ def ask(self) -> np.ndarray:
189
+ try:
190
+ lamb = self.popsize
191
+ n = self.dim
192
+ res = np.empty(lamb*n)
193
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
194
+ askCRFMNES_C(self.ptr, res_p)
195
+ xs = np.empty((lamb, n))
196
+ for p in range(lamb):
197
+ xs[p,:] = res[p*n : (p+1)*n]
198
+ return xs
199
+ except Exception as ex:
200
+ print (ex)
201
+ return None
202
+
203
+ def tell(self, ys: np.ndarray):
204
+ try:
205
+ array_type_ys = ct.c_double * len(ys)
206
+ return tellCRFMNES_C(self.ptr, array_type_ys(*ys))
207
+ except Exception as ex:
208
+ print (ex)
209
+ return -1
210
+
211
+ def population(self) -> np.ndarray:
212
+ try:
213
+ lamb = self.popsize
214
+ n = self.dim
215
+ res = np.empty(lamb*n)
216
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
217
+ populationCRFMNES_C(self.ptr, res_p)
218
+ xs = np.array(lamb, n)
219
+ for p in range(lamb):
220
+ xs[p] = res[p*n : (p+1)*n]
221
+ return xs
222
+ except Exception as ex:
223
+ print (ex)
224
+ return None
225
+
226
+ def result(self) -> OptimizeResult:
227
+ res = np.empty(self.dim+4)
228
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
229
+ try:
230
+ resultCRFMNES_C(self.ptr, res_p)
231
+ x = res[:self.dim]
232
+ val = res[self.dim]
233
+ evals = int(res[self.dim+1])
234
+ iterations = int(res[self.dim+2])
235
+ stop = int(res[self.dim+3])
236
+ res = OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
237
+ except Exception as ex:
238
+ res = OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
239
+ return res
240
+
241
+ if not libcmalib is None:
242
+
243
+ optimizeCRFMNES_C = libcmalib.optimizeCRFMNES_C
244
+ optimizeCRFMNES_C.argtypes = [ct.c_long, call_back_par, ct.c_int, \
245
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
246
+ ct.c_double, ct.c_int, ct.c_double, ct.c_int,
247
+ ct.c_long, ct.c_double,
248
+ ct.c_bool, ct.c_bool, ct.POINTER(ct.c_double)]
249
+
250
+ initCRFMNES_C = libcmalib.initCRFMNES_C
251
+ initCRFMNES_C.argtypes = [ct.c_long, ct.c_int, \
252
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
253
+ ct.c_double, ct.c_int,
254
+ ct.c_long, ct.c_double,
255
+ ct.c_bool, ct.c_bool]
256
+
257
+ initCRFMNES_C.restype = ct.c_void_p
258
+
259
+ destroyCRFMNES_C = libcmalib.destroyCRFMNES_C
260
+ destroyCRFMNES_C.argtypes = [ct.c_void_p]
261
+
262
+ askCRFMNES_C = libcmalib.askCRFMNES_C
263
+ askCRFMNES_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
264
+
265
+ tellCRFMNES_C = libcmalib.tellCRFMNES_C
266
+ tellCRFMNES_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
267
+ tellCRFMNES_C.restype = ct.c_int
268
+
269
+ populationCRFMNES_C = libcmalib.populationCRFMNES_C
270
+ populationCRFMNES_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
271
+
272
+ resultCRFMNES_C = libcmalib.resultCRFMNES_C
273
+ resultCRFMNES_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
fcmaes/dacpp.py CHANGED
@@ -12,21 +12,22 @@ import sys
12
12
  import os
13
13
  import ctypes as ct
14
14
  import numpy as np
15
- from numpy.random import MT19937, Generator
16
- from scipy.optimize import OptimizeResult
17
- from fcmaes.cmaes import _check_bounds
18
- from fcmaes.ldecpp import callback
19
- from fcmaes.decpp import libcmalib
15
+ from numpy.random import PCG64DXSM, Generator
16
+ from scipy.optimize import OptimizeResult, Bounds
17
+ from fcmaes.evaluator import _check_bounds, call_back_type, callback, libcmalib
18
+
19
+ from typing import Optional, Callable, Union
20
+ from numpy.typing import ArrayLike
20
21
 
21
22
  os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
22
23
 
23
- def minimize(fun,
24
- bounds=None,
25
- x0=None,
26
- max_evaluations = 100000,
27
- use_local_search = True,
28
- rg = Generator(MT19937()),
29
- runid=0):
24
+ def minimize(fun: Callable[[ArrayLike], float],
25
+ bounds: Optional[Bounds] = None,
26
+ x0: Optional[ArrayLike] = None,
27
+ max_evaluations: Optional[int] = 100000,
28
+ use_local_search: Optional[bool] = True,
29
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
30
+ runid: Optional[int] = 0) -> OptimizeResult:
30
31
 
31
32
  """Minimization of a scalar function of one or more variables using a
32
33
  C++ Dual Annealing implementation called via ctypes.
@@ -35,10 +36,8 @@ def minimize(fun,
35
36
  ----------
36
37
  fun : callable
37
38
  The objective function to be minimized.
38
- ``fun(x, *args) -> float``
39
- where ``x`` is an 1-D array with shape (dim,) and ``args``
40
- is a tuple of the fixed parameters needed to completely
41
- specify the function.
39
+ ``fun(x) -> float``
40
+ where ``x`` is an 1-D array with shape (dim,)
42
41
  bounds : sequence or `Bounds`, optional
43
42
  Bounds on variables. There are two ways to specify the bounds:
44
43
  1. Instance of the `scipy.Bounds` class.
@@ -67,10 +66,7 @@ def minimize(fun,
67
66
  ``success`` a Boolean flag indicating if the optimizer exited successfully. """
68
67
 
69
68
  lower, upper, guess = _check_bounds(bounds, x0, rg)
70
- dim = guess.size
71
- if lower is None:
72
- lower = [0]*dim
73
- upper = [0]*dim
69
+ dim = guess.size
74
70
  array_type = ct.c_double * dim
75
71
  c_callback = call_back_type(callback(fun))
76
72
  seed = int(rg.uniform(0, 2**32 - 1))
@@ -78,7 +74,9 @@ def minimize(fun,
78
74
  res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
79
75
  try:
80
76
  optimizeDA_C(runid, c_callback, dim, seed,
81
- array_type(*guess), array_type(*lower), array_type(*upper),
77
+ array_type(*guess),
78
+ None if lower is None else array_type(*lower),
79
+ None if upper is None else array_type(*upper),
82
80
  max_evaluations, use_local_search, res_p)
83
81
  x = res[:dim]
84
82
  val = res[dim]
@@ -88,10 +86,11 @@ def minimize(fun,
88
86
  return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
89
87
  except Exception as ex:
90
88
  return OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
91
-
92
- call_back_type = ct.CFUNCTYPE(ct.c_double, ct.c_int, ct.POINTER(ct.c_double))
93
- optimizeDA_C = libcmalib.optimizeDA_C
94
- optimizeDA_C.argtypes = [ct.c_long, call_back_type, ct.c_int, ct.c_int, \
95
- ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
96
- ct.c_int, ct.c_bool, ct.POINTER(ct.c_double)]
89
+
90
+ if not libcmalib is None:
91
+
92
+ optimizeDA_C = libcmalib.optimizeDA_C
93
+ optimizeDA_C.argtypes = [ct.c_long, call_back_type, ct.c_int, ct.c_int, \
94
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
95
+ ct.c_int, ct.c_bool, ct.POINTER(ct.c_double)]
97
96