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/decpp.py CHANGED
@@ -8,32 +8,47 @@
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
10
  b) reinitialization of individuals based on their age.
11
- c) oscillating CR/F parameters."""
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."""
12
18
 
13
19
  import sys
14
20
  import os
15
- import math
16
21
  import ctypes as ct
17
22
  import numpy as np
18
- from numpy.random import MT19937, Generator
19
- from scipy.optimize import OptimizeResult
20
- from fcmaes import de
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
21
30
 
22
31
  os.environ['MKL_DEBUG_CPU_TYPE'] = '5'
23
32
 
24
- def minimize(fun,
25
- dim = None,
26
- bounds = None,
27
- popsize = None,
28
- max_evaluations = 100000,
29
- stop_fitness = None,
30
- keep = 200,
31
- f = 0.5,
32
- cr = 0.9,
33
- rg = Generator(MT19937()),
34
- workers = 1,
35
- is_terminate = None,
36
- 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:
37
52
 
38
53
  """Minimization of a scalar function of one or more variables using a
39
54
  C++ Differential Evolution implementation called via ctypes.
@@ -42,10 +57,8 @@ def minimize(fun,
42
57
  ----------
43
58
  fun : callable
44
59
  The objective function to be minimized.
45
- ``fun(x, *args) -> float``
46
- where ``x`` is an 1-D array with shape (dim,) and ``args``
47
- is a tuple of the fixed parameters needed to completely
48
- specify the function.
60
+ ``fun(x) -> float``
61
+ where ``x`` is an 1-D array with shape (dim,)
49
62
  dim : int
50
63
  dimension of the argument of the objective function
51
64
  bounds : sequence or `Bounds`, optional
@@ -70,11 +83,25 @@ def minimize(fun,
70
83
  In the literature this is also known as the crossover probability.
71
84
  rg = numpy.random.Generator, optional
72
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.
73
93
  workers : int or None, optional
74
94
  If not workers is None, function evaluation is performed in parallel for the whole population.
75
95
  Useful for costly objective functions but is deactivated for parallel retry.
76
96
  is_terminate : callable, optional
77
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).
78
105
  runid : int, optional
79
106
  id used to identify the run for debugging / logging.
80
107
 
@@ -88,26 +115,32 @@ def minimize(fun,
88
115
  ``nit`` the number of iterations,
89
116
  ``success`` a Boolean flag indicating if the optimizer exited successfully. """
90
117
 
91
- dim, lower, upper = de._check_bounds(bounds, dim)
118
+ dim, lower, upper = _check_bounds(bounds, dim)
92
119
  if popsize is None:
93
120
  popsize = 31
94
- if lower is None:
95
- lower = [0]*dim
96
- upper = [0]*dim
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
97
126
  if workers is None:
98
- workers = 0
99
- if stop_fitness is None:
100
- stop_fitness = math.inf
127
+ workers = 0
101
128
  array_type = ct.c_double * dim
102
- c_callback = mo_call_back_type(callback(fun, dim, is_terminate))
129
+ bool_array_type = ct.c_bool * dim
130
+ c_callback = mo_call_back_type(callback_so(fun, dim, is_terminate))
103
131
  seed = int(rg.uniform(0, 2**32 - 1))
104
132
  res = np.empty(dim+4)
105
133
  res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
106
134
  try:
107
135
  optimizeDE_C(runid, c_callback, dim, seed,
108
- array_type(*lower), array_type(*upper),
109
- max_evaluations, keep, stop_fitness,
110
- popsize, f, cr, workers, res_p)
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)
111
144
  x = res[:dim]
112
145
  val = res[dim]
113
146
  evals = int(res[dim+1])
@@ -116,162 +149,138 @@ def minimize(fun,
116
149
  return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
117
150
  except Exception as ex:
118
151
  return OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
152
+
153
+ class DE_C:
119
154
 
120
- def minimize(fun,
121
- dim = None,
122
- bounds = None,
123
- popsize = None,
124
- max_evaluations = 100000,
125
- stop_fitness = None,
126
- keep = 200,
127
- f = 0.5,
128
- cr = 0.9,
129
- rg = Generator(MT19937()),
130
- workers = 1,
131
- runid=0):
132
-
133
- """Minimization of a scalar function of one or more variables using a
134
- C++ Differential Evolution implementation called via ctypes.
135
-
136
- Parameters
137
- ----------
138
- fun : callable
139
- The objective function to be minimized.
140
- ``fun(x, *args) -> float``
141
- where ``x`` is an 1-D array with shape (dim,) and ``args``
142
- is a tuple of the fixed parameters needed to completely
143
- specify the function.
144
- dim : int
145
- dimension of the argument of the objective function
146
- bounds : sequence or `Bounds`, optional
147
- Bounds on variables. There are two ways to specify the bounds:
148
- 1. Instance of the `scipy.Bounds` class.
149
- 2. Sequence of ``(min, max)`` pairs for each element in `x`. None
150
- is used to specify no bound.
151
- popsize : int, optional
152
- Population size.
153
- max_evaluations : int, optional
154
- Forced termination after ``max_evaluations`` function evaluations.
155
- stop_fitness : float, optional
156
- Limit for fitness value. If reached minimize terminates.
157
- keep = float, optional
158
- changes the reinitialization probability of individuals based on their age. Higher value
159
- means lower probablity of reinitialization.
160
- f = float, optional
161
- The mutation constant. In the literature this is also known as differential weight,
162
- being denoted by F. Should be in the range [0, 2].
163
- cr = float, optional
164
- The recombination constant. Should be in the range [0, 1].
165
- In the literature this is also known as the crossover probability.
166
- rg = numpy.random.Generator, optional
167
- Random generator for creating random guesses.
168
- workers : int or None, optional
169
- If not workers is None, function evaluation is performed in parallel for the whole population.
170
- Useful for costly objective functions but is deactivated for parallel retry.
171
- runid : int, optional
172
- id used to identify the run for debugging / logging.
173
-
174
- Returns
175
- -------
176
- res : scipy.OptimizeResult
177
- The optimization result is represented as an ``OptimizeResult`` object.
178
- Important attributes are: ``x`` the solution array,
179
- ``fun`` the best function value,
180
- ``nfev`` the number of function evaluations,
181
- ``nit`` the number of iterations,
182
- ``success`` a Boolean flag indicating if the optimizer exited successfully. """
183
-
184
- dim, lower, upper = de._check_bounds(bounds, dim)
185
- if popsize is None:
186
- popsize = 31
187
- if lower is None:
188
- lower = [0]*dim
189
- upper = [0]*dim
190
- if workers is None:
191
- workers = 0
192
- if stop_fitness is None:
193
- stop_fitness = math.inf
194
- array_type = ct.c_double * dim
195
- c_callback = mo_call_back_type(callback(fun, dim))
196
- seed = int(rg.uniform(0, 2**32 - 1))
197
- res = np.empty(dim+4)
198
- res_p = res.ctypes.data_as(ct.POINTER(ct.c_double))
199
- try:
200
- optimizeDE_C(runid, c_callback, dim, seed,
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,
201
184
  array_type(*lower), array_type(*upper),
202
- max_evaluations, keep, stop_fitness,
203
- popsize, f, cr, workers, res_p)
204
- x = res[:dim]
205
- val = res[dim]
206
- evals = int(res[dim+1])
207
- iterations = int(res[dim+2])
208
- stop = int(res[dim+3])
209
- return OptimizeResult(x=x, fun=val, nfev=evals, nit=iterations, status=stop, success=True)
210
- except Exception as ex:
211
- return OptimizeResult(x=None, fun=sys.float_info.max, nfev=0, nit=0, status=-1, success=False)
212
-
213
- class callback(object):
214
-
215
- def __init__(self, fun, dim, is_terminate = None):
216
- self.fun = fun
217
- self.dim = dim
218
- self.nobj = 1
219
- self.is_terminate = is_terminate
220
-
221
- def __call__(self, dim, x, y):
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:
222
198
  try:
223
- arrTypeX = ct.c_double*(self.dim)
224
- xaddr = ct.addressof(x.contents)
225
- xbuf = np.frombuffer(arrTypeX.from_address(xaddr))
226
- arrTypeY = ct.c_double*(self.nobj)
227
- yaddr = ct.addressof(y.contents)
228
- ybuf = np.frombuffer(arrTypeY.from_address(yaddr))
229
- fit = self.fun(xbuf)
230
- ybuf[0] = fit if math.isfinite(fit) else sys.float_info.max
231
- return False if self.is_terminate is None else self.is_terminate(xbuf, ybuf)
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
232
208
  except Exception as ex:
233
209
  print (ex)
234
- return False
210
+ return None
235
211
 
236
-
237
- class callback_mo(object):
238
-
239
- def __init__(self, fun, dim, nobj, is_terminate = None):
240
- self.fun = fun
241
- self.dim = dim
242
- self.nobj = nobj
243
- self.is_terminate = is_terminate
244
-
245
- def __call__(self, dim, x, y):
212
+ def tell(self, ys: np.ndarray):
246
213
  try:
247
- arrTypeX = ct.c_double*(dim)
248
- xaddr = ct.addressof(x.contents)
249
- xbuf = np.frombuffer(arrTypeX.from_address(xaddr))
250
- arrTypeY = ct.c_double*(self.nobj)
251
- yaddr = ct.addressof(y.contents)
252
- ybuf = np.frombuffer(arrTypeY.from_address(yaddr))
253
- ybuf[:] = self.fun(xbuf)[:]
254
- return False if self.is_terminate is None else self.is_terminate(xbuf, ybuf)
214
+ array_type_ys = ct.c_double * len(ys)
215
+ return tellDE_C(self.ptr, array_type_ys(*ys))
255
216
  except Exception as ex:
256
217
  print (ex)
257
- return False
218
+ return -1
258
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
259
234
 
260
- mo_call_back_type = ct.CFUNCTYPE(ct.c_bool, ct.c_int, ct.POINTER(ct.c_double), ct.POINTER(ct.c_double))
261
-
262
- basepath = os.path.dirname(os.path.abspath(__file__))
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
263
249
 
264
- if sys.platform.startswith('linux'):
265
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.so')
266
- elif 'mac' in sys.platform:
267
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dylib')
268
- else:
269
- os.environ['PATH'] = (basepath + '/lib') + os.pathsep + os.environ['PATH']
270
- libcmalib = ct.cdll.LoadLibrary(basepath + '/lib/libacmalib.dll')
271
-
272
- optimizeDE_C = libcmalib.optimizeDE_C
273
- optimizeDE_C.argtypes = [ct.c_long, mo_call_back_type, ct.c_int, ct.c_int, \
274
- ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), \
275
- ct.c_int, ct.c_double, ct.c_double, ct.c_int, \
276
- ct.c_double, ct.c_double, ct.c_int, ct.POINTER(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
277
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)]
286
+