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/decpp.py CHANGED
@@ -3,34 +3,52 @@
3
3
  # This source code is licensed under the MIT license found in the
4
4
  # LICENSE file in the root directory.
5
5
 
6
- """Eigen based implementation of differential evolution using onl the DE/best/1 strategy.
7
- Uses two deviations from the standard DE algorithm:
6
+ """Eigen based implementation of differential evolution using the DE/best/1 strategy.
7
+ Uses three deviations from the standard DE algorithm:
8
8
  a) temporal locality introduced in
9
9
  https://www.researchgate.net/publication/309179699_Differential_evolution_for_protein_folding_optimization_based_on_a_three-dimensional_AB_off-lattice_model
10
- b) reinitialization of individuals based on their age."""
11
-
10
+ b) reinitialization of individuals based on their age.
11
+ c) oscillating CR/F parameters.
12
+
13
+ The ints parameter is a boolean array indicating which parameters are discrete integer values. This
14
+ parameter was introduced after observing non optimal results for the ESP2 benchmark problem:
15
+ https://github.com/AlgTUDelft/ExpensiveOptimBenchmark/blob/master/expensiveoptimbenchmark/problems/DockerCFDBenchmark.py
16
+ If defined it causes a "special treatment" for discrete variables: They are rounded to the next integer value and
17
+ there is an additional mutation to avoid getting stuck to local minima."""
18
+
12
19
  import sys
13
20
  import os
14
- import math
15
21
  import ctypes as ct
16
22
  import numpy as np
17
- from numpy.random import MT19937, Generator
18
- from scipy.optimize import OptimizeResult
19
- from fcmaes.cmaescpp import callback
23
+ from numpy.random import PCG64DXSM, Generator
24
+ from scipy.optimize import OptimizeResult, Bounds
25
+ from fcmaes.evaluator import mo_call_back_type, callback_so, libcmalib
26
+ from fcmaes.de import _check_bounds
27
+
28
+ from typing import Optional, Callable, Tuple, Union
29
+ from numpy.typing import ArrayLike
20
30
 
21
31
  os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
22
32
 
23
- def minimize(fun,
24
- dim,
25
- bounds = None,
26
- popsize = None,
27
- max_evaluations = 100000,
28
- stop_fittness = None,
29
- keep = 200.0,
30
- f = 0.5,
31
- cr = 0.9,
32
- rg = Generator(MT19937()),
33
- runid=0):
33
+ def minimize(fun: Callable[[ArrayLike], float],
34
+ dim: Optional[int] = None,
35
+ bounds: Optional[Bounds] = None,
36
+ popsize: Optional[int] = 31,
37
+ max_evaluations: Optional[int] = 100000,
38
+ stop_fitness: Optional[float] = -np.inf,
39
+ keep: Optional[int] = 200,
40
+ f: Optional[float] = 0.5,
41
+ cr: Optional[float] = 0.9,
42
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
43
+ ints: Optional[ArrayLike] = None,
44
+ min_mutate: Optional[float] = 0.1,
45
+ max_mutate: Optional[float] = 0.5,
46
+ workers: Optional[int] = 1,
47
+ is_terminate: Optional[Callable[[ArrayLike, float], bool]] = None,
48
+ x0: Optional[ArrayLike] = None,
49
+ input_sigma: Optional[Union[float, ArrayLike, Callable]] = None,
50
+ min_sigma: Optional[float] = 0,
51
+ runid: Optional[int] = 0) -> OptimizeResult:
34
52
 
35
53
  """Minimization of a scalar function of one or more variables using a
36
54
  C++ Differential Evolution implementation called via ctypes.
@@ -39,10 +57,8 @@ def minimize(fun,
39
57
  ----------
40
58
  fun : callable
41
59
  The objective function to be minimized.
42
- ``fun(x, *args) -> float``
43
- where ``x`` is an 1-D array with shape (n,) and ``args``
44
- is a tuple of the fixed parameters needed to completely
45
- specify the function.
60
+ ``fun(x) -> float``
61
+ where ``x`` is an 1-D array with shape (dim,)
46
62
  dim : int
47
63
  dimension of the argument of the objective function
48
64
  bounds : sequence or `Bounds`, optional
@@ -50,9 +66,11 @@ def minimize(fun,
50
66
  1. Instance of the `scipy.Bounds` class.
51
67
  2. Sequence of ``(min, max)`` pairs for each element in `x`. None
52
68
  is used to specify no bound.
69
+ popsize : int, optional
70
+ Population size.
53
71
  max_evaluations : int, optional
54
72
  Forced termination after ``max_evaluations`` function evaluations.
55
- stop_fittness : float, optional
73
+ stop_fitness : float, optional
56
74
  Limit for fitness value. If reached minimize terminates.
57
75
  keep = float, optional
58
76
  changes the reinitialization probability of individuals based on their age. Higher value
@@ -65,8 +83,27 @@ def minimize(fun,
65
83
  In the literature this is also known as the crossover probability.
66
84
  rg = numpy.random.Generator, optional
67
85
  Random generator for creating random guesses.
86
+ ints = list or array of bool, optional
87
+ indicating which parameters are discrete integer values. If defined these parameters will be
88
+ rounded to the next integer and some additional mutation of discrete parameters are performed.
89
+ min_mutate = float, optional
90
+ Determines the minimal mutation rate for discrete integer parameters.
91
+ max_mutate = float, optional
92
+ Determines the maximal mutation rate for discrete integer parameters.
93
+ workers : int or None, optional
94
+ If not workers is None, function evaluation is performed in parallel for the whole population.
95
+ Useful for costly objective functions but is deactivated for parallel retry.
96
+ is_terminate : callable, optional
97
+ Callback to be used if the caller of minimize wants to decide when to terminate.
98
+ x0 : ndarray, shape (dim,)
99
+ Initial guess. Array of real elements of size (dim,),
100
+ where 'dim' is the number of independent variables.
101
+ input_sigma : ndarray, shape (dim,) or scalar
102
+ Initial sigma for each dimension.
103
+ min_sigma = float, optional
104
+ minimal sigma limit. If 0, uniform random distribution is used (requires bounds).
68
105
  runid : int, optional
69
- id used to identify the optimization run.
106
+ id used to identify the run for debugging / logging.
70
107
 
71
108
  Returns
72
109
  -------
@@ -77,52 +114,173 @@ def minimize(fun,
77
114
  ``nfev`` the number of function evaluations,
78
115
  ``nit`` the number of iterations,
79
116
  ``success`` a Boolean flag indicating if the optimizer exited successfully. """
80
-
81
- lower = np.asarray(bounds.lb)
82
- upper = np.asarray(bounds.ub)
83
- n = dim
117
+
118
+ dim, lower, upper = _check_bounds(bounds, dim)
84
119
  if popsize is None:
85
- popsize = n*15
86
- if lower is None:
87
- lower = [0]*n
88
- upper = [0]*n
89
- if stop_fittness is None:
90
- stop_fittness = math.inf
91
- array_type = ct.c_double * n
92
- c_callback = call_back_type(callback(fun))
120
+ popsize = 31
121
+ if not input_sigma is None:
122
+ if callable(input_sigma):
123
+ input_sigma=input_sigma()
124
+ if np.ndim(input_sigma) == 0:
125
+ input_sigma = [input_sigma] * dim
126
+ if workers is None:
127
+ workers = 0
128
+ array_type = ct.c_double * dim
129
+ bool_array_type = ct.c_bool * dim
130
+ c_callback = mo_call_back_type(callback_so(fun, dim, is_terminate))
93
131
  seed = int(rg.uniform(0, 2**32 - 1))
132
+ res = np.empty(dim+4)
133
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
94
134
  try:
95
- res = optimizeDE_C(runid, c_callback, n, seed,
96
- array_type(*lower), array_type(*upper),
97
- max_evaluations, keep, stop_fittness,
98
- popsize, f, cr)
99
- x = np.array(np.fromiter(res, dtype=np.float64, count=n))
100
- val = res[n]
101
- evals = int(res[n+1])
102
- iterations = int(res[n+2])
103
- stop = int(res[n+3])
104
- freemem(res)
135
+ optimizeDE_C(runid, c_callback, dim, seed,
136
+ None if lower is None else array_type(*lower),
137
+ None if upper is None else array_type(*upper),
138
+ None if x0 is None else array_type(*x0),
139
+ None if input_sigma is None else array_type(*input_sigma),
140
+ min_sigma,
141
+ None if ints is None else bool_array_type(*ints),
142
+ max_evaluations, keep, stop_fitness,
143
+ popsize, f, cr, min_mutate, max_mutate, workers, res_p)
144
+ x = res[:dim]
145
+ val = res[dim]
146
+ evals = int(res[dim+1])
147
+ iterations = int(res[dim+2])
148
+ stop = int(res[dim+3])
105
149
  return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
106
150
  except Exception as ex:
107
151
  return OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
108
152
 
109
- basepath = os.path.dirname(os.path.abspath(__file__))
153
+ class DE_C:
154
+
155
+ def __init__(self,
156
+ dim: Optional[int] = None,
157
+ bounds: Optional[Bounds] = None,
158
+ popsize: Optional[int] = 31,
159
+ keep: Optional[int] = 200,
160
+ f: Optional[float] = 0.5,
161
+ cr: Optional[float] = 0.9,
162
+ rg: Optional[Generator] = Generator(PCG64DXSM()),
163
+ ints: Optional[ArrayLike] = None,
164
+ min_mutate: Optional[float] = 0.1,
165
+ max_mutate: Optional[float] = 0.5,
166
+ x0: Optional[ArrayLike] = None,
167
+ input_sigma: Optional[Union[float, ArrayLike, Callable]] = 0.3,
168
+ min_sigma: Optional[float] = 0,
169
+ ):
170
+ dim, lower, upper = _check_bounds(bounds, dim)
171
+ if popsize is None:
172
+ popsize = 31
173
+ if callable(input_sigma):
174
+ input_sigma=input_sigma()
175
+ if np.ndim(input_sigma) == 0:
176
+ input_sigma = [input_sigma] * dim
177
+ if ints is None:
178
+ ints = [False]*dim
179
+ array_type = ct.c_double * dim
180
+ bool_array_type = ct.c_bool * dim
181
+ seed = int(rg.uniform(0, 2**32 - 1))
182
+ try:
183
+ self.ptr = initDE_C(0, dim, seed,
184
+ array_type(*lower), array_type(*upper),
185
+ array_type(*x0), array_type(*input_sigma), min_sigma,
186
+ bool_array_type(*ints),
187
+ keep, popsize, f, cr, min_mutate, max_mutate)
188
+ self.popsize = popsize
189
+ self.dim = dim
190
+ except Exception as ex:
191
+ print (ex)
192
+ pass
193
+
194
+ def __del__(self):
195
+ destroyDE_C(self.ptr)
196
+
197
+ def ask(self) -> np.array:
198
+ try:
199
+ popsize = self.popsize
200
+ n = self.dim
201
+ res = np.empty(popsize*n)
202
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
203
+ askDE_C(self.ptr, res_p)
204
+ xs = np.empty((popsize, n))
205
+ for p in range(popsize):
206
+ xs[p,:] = res[p*n : (p+1)*n]
207
+ return xs
208
+ except Exception as ex:
209
+ print (ex)
210
+ return None
211
+
212
+ def tell(self, ys: np.ndarray):
213
+ try:
214
+ array_type_ys = ct.c_double * len(ys)
215
+ return tellDE_C(self.ptr, array_type_ys(*ys))
216
+ except Exception as ex:
217
+ print (ex)
218
+ return -1
219
+
220
+ def population(self) -> np.array:
221
+ try:
222
+ popsize = self.popsize
223
+ n = self.dim
224
+ res = np.empty(popsize*n)
225
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
226
+ populationDE_C(self.ptr, res_p)
227
+ xs = np.array(popsize, n)
228
+ for p in range(popsize):
229
+ xs[p] = res[p*n : (p+1)*n]
230
+ return xs
231
+ except Exception as ex:
232
+ print (ex)
233
+ return None
110
234
 
111
- if sys.platform.startswith('linux'):
112
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.so')
113
- elif 'mac' in sys.platform:
114
- libgtoplib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dylib')
115
- else:
116
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dll')
235
+ def result(self) -> OptimizeResult:
236
+ res = np.empty(self.dim+4)
237
+ res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
238
+ try:
239
+ resultDE_C(self.ptr, res_p)
240
+ x = res[:self.dim]
241
+ val = res[self.dim]
242
+ evals = int(res[self.dim+1])
243
+ iterations = int(res[self.dim+2])
244
+ stop = int(res[self.dim+3])
245
+ res = OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
246
+ except Exception as ex:
247
+ res = OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
248
+ return res
117
249
 
118
- call_back_type = ct.CFUNCTYPE(ct.c_double, ct.c_int, ct.POINTER(ct.c_double))
119
- optimizeDE_C = libcmalib.optimizeDE_C
120
- optimizeDE_C.argtypes = [ct.c_long, call_back_type, ct.c_int, ct.c_int, \
121
- ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
122
- ct.c_int, ct.c_double, ct.c_double, ct.c_int, \
123
- ct.c_double, ct.c_double]
250
+ if not libcmalib is None:
251
+
252
+ optimizeDE_C = libcmalib.optimizeDE_C
253
+ optimizeDE_C.argtypes = [ct.c_long, mo_call_back_type, ct.c_int, ct.c_int, \
254
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
255
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.c_double, \
256
+ ct.POINTER(ct.c_bool), \
257
+ ct.c_int, ct.c_double, ct.c_double, ct.c_int, \
258
+ ct.c_double, ct.c_double, ct.c_double, ct.c_double,
259
+ ct.c_int, ct.POINTER(ct.c_double)]
260
+
261
+ initDE_C = libcmalib.initDE_C
262
+ initDE_C.argtypes = [ct.c_long, ct.c_int, ct.c_int, \
263
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
264
+ ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.c_double, \
265
+ ct.POINTER(ct.c_bool), \
266
+ ct.c_double, ct.c_int, \
267
+ ct.c_double, ct.c_double, ct.c_double, ct.c_double]
268
+
269
+ initDE_C.restype = ct.c_void_p
270
+
271
+ destroyDE_C = libcmalib.destroyDE_C
272
+ destroyDE_C.argtypes = [ct.c_void_p]
273
+
274
+ askDE_C = libcmalib.askDE_C
275
+ askDE_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
276
+
277
+ tellDE_C = libcmalib.tellDE_C
278
+ tellDE_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
279
+ tellDE_C.restype = ct.c_int
280
+
281
+ populationDE_C = libcmalib.populationDE_C
282
+ populationDE_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
283
+
284
+ resultDE_C = libcmalib.resultDE_C
285
+ resultDE_C.argtypes = [ct.c_void_p, ct.POINTER(ct.c_double)]
124
286
 
125
- optimizeDE_C.restype = ct.POINTER(ct.c_double)
126
- freemem = libcmalib.free_mem
127
- freemem.argtypes = [ct.POINTER(ct.c_double)]
128
-