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/moretry.py CHANGED
@@ -6,30 +6,33 @@
6
6
  # parallel optimization retry of a multi-objective problem.
7
7
 
8
8
  import numpy as np
9
- import math, sys, time, warnings
9
+ import math, sys, time, warnings, threadpoolctl
10
10
  import multiprocessing as mp
11
11
  from multiprocessing import Process
12
12
  from scipy.optimize import Bounds
13
- from numpy.random import Generator, MT19937, SeedSequence
14
- from fcmaes.optimizer import de_cma, logger, dtime
13
+ from numpy.random import Generator, PCG64DXSM, SeedSequence
14
+ from fcmaes.optimizer import de_cma, dtime, Optimizer
15
15
  from fcmaes import retry, advretry
16
+ from loguru import logger
16
17
 
17
- def minimize(fun,
18
- bounds,
19
- weight_bounds,
20
- ncon = 0,
21
- value_exp = 2.0,
22
- value_limits = None,
23
- num_retries = 1024,
24
- logger = None,
25
- workers = mp.cpu_count(),
26
- popsize = 31,
27
- max_evaluations = 50000,
28
- capacity = None,
29
- optimizer = None,
30
- statistic_num = 0,
31
- plot_name = None
32
- ):
18
+ from typing import Optional, Callable, Tuple
19
+ from numpy.typing import ArrayLike
20
+
21
+ def minimize(fun: Callable[[ArrayLike], float],
22
+ bounds: Bounds,
23
+ weight_bounds: Bounds,
24
+ ncon: Optional[int] = 0,
25
+ value_exp: Optional[float] = 2.0,
26
+ value_limits: Optional[ArrayLike] = None,
27
+ num_retries: Optional[int] = 1024,
28
+ workers: Optional[int] = mp.cpu_count(),
29
+ popsize: Optional[int] = 31,
30
+ max_evaluations: Optional[int] = 50000,
31
+ capacity: Optional[int] = None,
32
+ optimizer: Optional[Optimizer] = None,
33
+ statistic_num: Optional[int] = 0,
34
+ plot_name: Optional[str] = None
35
+ ) -> Tuple[np.ndarray, np.ndarray]:
33
36
  """Minimization of a multi objective function of one or more variables using parallel
34
37
  optimization retry.
35
38
 
@@ -37,10 +40,8 @@ def minimize(fun,
37
40
  ----------
38
41
  fun : callable
39
42
  The objective function to be minimized.
40
- ``fun(x, *args) -> float``
41
- where ``x`` is an 1-D array with shape (n,) and ``args``
42
- is a tuple of the fixed parameters needed to completely
43
- specify the function.
43
+ ``fun(x) -> float``
44
+ where ``x`` is an 1-D array with shape (n,)
44
45
  bounds : sequence or `Bounds`, optional
45
46
  Bounds on variables. There are two ways to specify the bounds:
46
47
  1. Instance of the `scipy.Bounds` class.
@@ -56,10 +57,6 @@ def minimize(fun,
56
57
  Upper limit for optimized objective values to be stored.
57
58
  num_retries : int, optional
58
59
  Number of optimization retries.
59
- logger : logger, optional
60
- logger for log output of the retry mechanism. If None, logging
61
- is switched off. Default is a logger which logs both to stdout and
62
- appends to a file ``optimizer.log``.
63
60
  workers : int, optional
64
61
  number of parallel processes used. Default is mp.cpu_count()
65
62
  popsize = int, optional
@@ -85,17 +82,26 @@ def minimize(fun,
85
82
  optimizer = de_cma(max_evaluations, popsize)
86
83
  if capacity is None:
87
84
  capacity = num_retries
88
- store = retry.Store(fun, bounds, capacity = capacity, logger = logger,
89
- statistic_num = statistic_num, plot_name = plot_name)
85
+ store = retry.Store(fun, bounds, capacity = capacity,
86
+ statistic_num = statistic_num)
87
+ store.plot_name = plot_name
90
88
  xs = np.array(mo_retry(fun, weight_bounds, ncon, value_exp,
91
89
  store, optimizer.minimize, num_retries, value_limits, workers))
92
90
  ys = np.array([fun(x) for x in xs])
93
91
  return xs, ys
94
92
 
95
- def mo_retry(fun, weight_bounds, ncon, y_exp, store, optimize, num_retries, value_limits,
96
- workers=mp.cpu_count()):
93
+ def mo_retry(fun: Callable[[ArrayLike], float],
94
+ weight_bounds: Bounds,
95
+ ncon: int,
96
+ y_exp: float,
97
+ store,
98
+ optimize: Callable,
99
+ num_retries: int,
100
+ value_limits: ArrayLike,
101
+ workers: Optional[int] = mp.cpu_count()):
102
+
97
103
  sg = SeedSequence()
98
- rgs = [Generator(MT19937(s)) for s in sg.spawn(workers)]
104
+ rgs = [Generator(PCG64DXSM(s)) for s in sg.spawn(workers)]
99
105
  proc=[Process(target=_retry_loop,
100
106
  args=(pid, rgs, fun, weight_bounds, ncon, y_exp,
101
107
  store, optimize, num_retries, value_limits)) for pid in range(workers)]
@@ -103,38 +109,38 @@ def mo_retry(fun, weight_bounds, ncon, y_exp, store, optimize, num_retries, valu
103
109
  [p.join() for p in proc]
104
110
  store.sort()
105
111
  store.dump()
106
- return store.get_xs()
112
+ return store.xs_view
107
113
 
108
114
  def _retry_loop(pid, rgs, fun, weight_bounds, ncon, y_exp,
109
115
  store, optimize, num_retries, value_limits):
110
116
 
111
- if 'win' in sys.platform and not store.logger is None:
112
- store.logger = logger()
117
+ store.create_xs_view()
113
118
  lower = store.lower
114
119
  wlb = np.array(weight_bounds.lb)
115
120
  wub = np.array(weight_bounds.ub)
116
- while store.get_runs_compare_incr(num_retries):
117
- try:
118
- rg = rgs[pid]
119
- w = rg.uniform(size=len(wub))
120
- w /= _avg_exp(w, y_exp) # correct scaling
121
- w = wlb + w * (wub - wlb)
122
- wrapper = mo_wrapper(fun, w, ncon, y_exp)
123
- x, y, evals = optimize(wrapper.eval, Bounds(store.lower, store.upper), None,
124
- [rg.uniform(0.05, 0.1)]*len(lower), rg, store)
125
- objs = wrapper.mo_eval(x) # retrieve the objective values
126
- if value_limits is None or all([objs[i] < value_limits[i] for i in range(len(w))]):
127
- store.add_result(y, x, evals, math.inf)
128
- if not store.plot_name is None:
129
- name = store.plot_name + "_moretry_" + str(store.get_count_evals())
130
- xs = np.array(store.get_xs())
131
- ys = np.array([fun(x) for x in xs])
132
- np.savez_compressed(name, xs=xs, ys=ys)
133
- plot(name, ncon, xs, ys)
134
- except Exception as ex:
135
- print(str(ex))
121
+ with threadpoolctl.threadpool_limits(limits=1, user_api="blas"):
122
+ while store.get_runs_compare_incr(num_retries):
123
+ try:
124
+ rg = rgs[pid]
125
+ w = rg.uniform(size=len(wub))
126
+ w /= _avg_exp(w, y_exp) # correct scaling
127
+ w = wlb + w * (wub - wlb)
128
+ wrapper = mo_wrapper(fun, w, ncon, y_exp)
129
+ x, y, evals = optimize(wrapper.eval, Bounds(store.lower, store.upper), None,
130
+ [rg.uniform(0.05, 0.1)]*len(lower), rg, store)
131
+ objs = wrapper.mo_eval(x) # retrieve the objective values
132
+ if value_limits is None or all([objs[i] < value_limits[i] for i in range(len(w))]):
133
+ store.add_result(y, x, evals, np.inf)
134
+ if not store.plot_name is None:
135
+ name = store.plot_name + "_moretry_" + str(store.get_count_evals())
136
+ xs = np.array(store.get_xs())
137
+ ys = np.array([fun(x) for x in xs])
138
+ np.savez_compressed(name, xs=xs, ys=ys)
139
+ plot(name, ncon, xs, ys)
140
+ except Exception as ex:
141
+ print(str(ex))
136
142
 
137
- def pareto(xs, ys):
143
+ def pareto(xs: np.ndarray, ys: np.ndarray):
138
144
  """pareto front for argument vectors and corresponding function value vectors."""
139
145
  par = _pareto(ys)
140
146
  xp = xs[par]
@@ -157,7 +163,7 @@ class mo_wrapper(object):
157
163
  y = self.fun(np.array(x))
158
164
  weighted = _avg_exp(self.weights*y, self.y_exp)
159
165
  if self.ncon > 0: # check constraint violations
160
- violations = np.array([i for i in range(self.nobj, self.ny) if y[i] > 0])
166
+ violations = np.fromiter((i for i in range(self.nobj, self.ny) if y[i] > 0), dtype=int)
161
167
  if len(violations) > 0:
162
168
  weighted += sum(self.weights[violations])
163
169
  return weighted
@@ -165,10 +171,18 @@ class mo_wrapper(object):
165
171
  def mo_eval(self, x):
166
172
  return self.fun(np.array(x))
167
173
 
168
- def minimize_plot(name, optimizer, fun, bounds, weight_bounds, ncon = 0,
169
- value_limits = None, num_retries = 1024,
170
- exp = 2.0, workers = mp.cpu_count(),
171
- logger=logger(), statistic_num = 0, plot_name = None):
174
+ def minimize_plot(name: str,
175
+ optimizer: Optimizer,
176
+ fun: Callable[[ArrayLike], float],
177
+ bounds: Bounds,
178
+ weight_bounds,
179
+ ncon: Optional[int] = 0,
180
+ value_limits: Optional[ArrayLike] = None,
181
+ num_retries: Optional[int] = 1024,
182
+ exp: Optional[float] = 2.0,
183
+ workers: Optional[int] = mp.cpu_count(),
184
+ statistic_num = 0, plot_name = None):
185
+
172
186
  time0 = time.perf_counter() # optimization start time
173
187
  name += '_' + optimizer.name
174
188
  logger.info('optimize ' + name)
@@ -178,43 +192,48 @@ def minimize_plot(name, optimizer, fun, bounds, weight_bounds, ncon = 0,
178
192
  num_retries = num_retries,
179
193
  optimizer = optimizer,
180
194
  workers = workers,
181
- logger=logger, statistic_num = statistic_num, plot_name = plot_name)
195
+ statistic_num = statistic_num, plot_name = plot_name)
182
196
  logger.info(name + ' time ' + str(dtime(time0)))
183
197
  np.savez_compressed(name, xs=xs, ys=ys)
184
198
  plot(name, ncon, xs, ys)
185
199
 
186
- def plot(name, ncon, xs, ys, eps = 1E-2):
200
+ def plot(name, ncon, xs, ys, eps = 1E-2, all=True, interp=False, plot3d=False):
187
201
  try:
188
202
  if ncon > 0: # select feasible
189
203
  ycon = np.array([np.maximum(y[-ncon:], 0) for y in ys])
190
204
  con = np.sum(ycon, axis=1)
191
205
  nobj = len(ys[0]) - ncon
192
- feasible = np.array([i for i in range(len(ys)) if con[i] < eps])
206
+ feasible = np.fromiter((i for i in range(len(ys)) if con[i] < eps), dtype=int)
193
207
  if len(feasible) > 0:
194
- #yc = [y[nobj:] for y in ys[feasible]]
195
- xs, ys = xs[feasible], np.array([ y[:nobj] for y in ys[feasible]])
208
+ xs, ys = xs[feasible], np.array([y[:nobj] for y in ys[feasible]])
196
209
  else:
210
+ print("no feasible")
197
211
  return
198
- retry.plot(ys, 'all_' + name + '.png', interp=False)
199
- xs, front = pareto(xs, ys)
200
- retry.plot(front, 'front_' + name + '.png', interp=False)
201
- if ncon > 0:
202
- for x, y, feas, in zip(xs, front, con[feasible]):
203
- print(str(list(y)) + ' ' + #str(feas) + ' ' +
204
- str([int(xi) for xi in x]))
212
+ if all:
213
+ retry.plot(ys, 'all_' + name + '.png', interp=False)
214
+ xs, ys = pareto(xs, ys)
215
+ for x, y in zip(xs, ys):
216
+ print(str(list(y)) + ' ' + str([round(xi,5) for xi in x]))
217
+ retry.plot(ys, 'front_' + name + '.png', interp=interp, plot3d=plot3d)
205
218
  except Exception as ex:
206
219
  print(str(ex))
207
220
 
208
- def adv_minimize_plot(name, optimizer, fun, bounds,
209
- value_limit = math.inf, num_retries = 1024, logger=logger(), statistic_num = 0):
221
+ def adv_minimize_plot(name: str,
222
+ optimizer: Optimizer,
223
+ fun: Callable[[ArrayLike], float],
224
+ bounds: Optional[Bounds],
225
+ value_limit: Optional[float] = np.inf,
226
+ num_retries: Optional[int] = 1024,
227
+ statistic_num: Optional[int] = 0):
228
+
210
229
  time0 = time.perf_counter() # optimization start time
211
230
  name += '_smart_' + optimizer.name
212
231
  logger.info('smart optimize ' + name)
213
- store = advretry.Store(lambda x:fun(x)[0], bounds, capacity=5000, logger=logger,
232
+ store = advretry.Store(lambda x:fun(x)[0], bounds, capacity=5000,
214
233
  num_retries=num_retries, statistic_num = statistic_num)
215
234
  advretry.retry(store, optimizer.minimize, value_limit)
216
235
  xs = np.array(store.get_xs())
217
- ys = np.array([fun(x) for x in xs])
236
+ ys = np.fromiter((fun(x) for x in xs), dtype=float)
218
237
  retry.plot(ys, '_all_' + name + '.png', interp=False)
219
238
  np.savez_compressed(name , xs=xs, ys=ys)
220
239
  xs, front = pareto(xs, ys)
@@ -227,6 +246,17 @@ def _avg_exp(y, y_exp):
227
246
  weighted = sum([y[i]**y_exp for i in range(len(y))])**(1.0/y_exp)
228
247
  return weighted
229
248
 
249
+ def _pareto_values(ys):
250
+ ys = ys[ys.sum(1).argsort()[::-1]]
251
+ undominated = np.ones(ys.shape[0], dtype=bool)
252
+ for i in range(ys.shape[0]):
253
+ n = ys.shape[0]
254
+ if i >= n:
255
+ break
256
+ undominated[i+1:n] = (ys[i+1:] >= ys[i]).any(1)
257
+ ys = ys[undominated[:n]]
258
+ return ys
259
+
230
260
  def _pareto(ys):
231
261
  pareto = np.arange(ys.shape[0])
232
262
  index = 0 # Next index to search for
fcmaes/multiretry.py CHANGED
@@ -9,12 +9,22 @@ import numpy as np
9
9
  import _pickle as cPickle
10
10
  import bz2
11
11
  import multiprocessing as mp
12
- from scipy.optimize import OptimizeResult
13
- from fcmaes.optimizer import logger, de_cma, eprint
12
+ from scipy.optimize import OptimizeResult, Bounds
13
+ from fcmaes.optimizer import de_cma, eprint, Optimizer
14
14
  from fcmaes import advretry
15
15
 
16
- def minimize(problems, ids=None, num_retries = min(256, 8*mp.cpu_count()),
17
- keep = 0.7, optimizer = de_cma(1500), logger = None, datafile = None):
16
+ from fcmaes.evaluator import is_debug_active
17
+ from loguru import logger
18
+ from typing import Optional, Callable, Tuple, List
19
+ from numpy.typing import ArrayLike
20
+
21
+ def minimize(problems: ArrayLike,
22
+ ids: Optional[ArrayLike] = None,
23
+ retries_inc: Optional[int] = min(256, 8*mp.cpu_count()),
24
+ num_retries: Optional[int] = 10000,
25
+ keep: Optional[float] = 0.7,
26
+ optimizer: Optional[Optimizer] = de_cma(1500),
27
+ datafile = None) -> List:
18
28
 
19
29
  """Minimization of a list of optimization problems by first applying parallel retry
20
30
  to filter the best ones and then applying coordinated retry to evaluate these further.
@@ -34,10 +44,13 @@ def minimize(problems, ids=None, num_retries = min(256, 8*mp.cpu_count()),
34
44
  list of objects corresponding to the list of problems used in logging to identify the
35
45
  problem variant currently logged. If None, the index of the problem
36
46
  variant is used instead.
37
-
38
- num_retries: int, optional
47
+
48
+ retries_inc: int, optional
39
49
  number of coordinated retries applied in the problem filter for each problem
40
50
  in each iteration.
51
+
52
+ num_retries: int, optional
53
+ number of coordinated retries applied in the problem filter for the winner problem.
41
54
 
42
55
  keep: float, optional
43
56
  rate of the problems kept after each iteration. 100*(1 - keep) % will be deleted.
@@ -45,11 +58,6 @@ def minimize(problems, ids=None, num_retries = min(256, 8*mp.cpu_count()),
45
58
  optimizer: optimizer.Optimizer, optional
46
59
  optimizer to use for the problem filter.
47
60
 
48
- logger, optional
49
- logger for log output. If None, logging
50
- is switched off. Default is a logger which logs both to stdout and
51
- appends to a file.
52
-
53
61
  datafile, optional
54
62
  file to persist / retrieve the internal state of the optimizations.
55
63
 
@@ -61,12 +69,12 @@ def minimize(problems, ids=None, num_retries = min(256, 8*mp.cpu_count()),
61
69
  ``fun`` the best function value, ``nfev`` the number of function evaluations,
62
70
  ``success`` a Boolean flag indicating if the optimizer exited successfully. """
63
71
 
64
- solver = multiretry(logger)
72
+ solver = multiretry()
65
73
  n = len(problems)
66
74
 
67
75
  for i in range(n):
68
76
  id = str(i+1) if ids is None else ids[i]
69
- solver.add(problem_stats(problems[i], id, i, num_retries, logger))
77
+ solver.add(problem_stats(problems[i], id, i, retries_inc, num_retries))
70
78
 
71
79
  if not datafile is None:
72
80
  solver.load(datafile)
@@ -86,29 +94,28 @@ def minimize(problems, ids=None, num_retries = min(256, 8*mp.cpu_count()),
86
94
 
87
95
  class problem_stats:
88
96
 
89
- def __init__(self, prob, id, index, num_retries = 64, logger = None):
90
- self.store = advretry.Store(prob.bounds, logger = logger, num_retries=num_retries)
97
+ def __init__(self, prob, id, index, retries_inc = 64, num_retries = 10000):
98
+ self.store = advretry.Store(prob.fun, prob.bounds, num_retries=num_retries)
91
99
  self.prob = prob
92
100
  self.name = prob.name
93
101
  self.fun = prob.fun
94
- self.num_retries = num_retries
95
- self.retries = 0
102
+ self.retries_inc = retries_inc
96
103
  self.value = 0
97
104
  self.id = id
98
105
  self.index = index
99
106
  self.ret = None
107
+ self.store.num_retries = self.retries_inc
100
108
 
101
109
  def retry(self, optimizer):
102
- self.retries += self.num_retries
103
- self.ret = advretry.retry(self.fun, self.store, optimizer.minimize)
110
+ self.store.num_retries += self.retries_inc
111
+ self.ret = advretry.retry(self.store, optimizer.minimize)
104
112
  self.value = self.store.get_y_best()
105
113
 
106
114
  class multiretry:
107
115
 
108
- def __init__(self, logger = None):
116
+ def __init__(self):
109
117
  self.problem_stats = []
110
118
  self.all_stats = []
111
- self.logger = logger
112
119
 
113
120
  def add(self, stats):
114
121
  self.problem_stats.append(stats)
@@ -116,12 +123,12 @@ class multiretry:
116
123
 
117
124
  def retry(self, optimizer):
118
125
  for ps in self.problem_stats:
119
- if not self.logger is None:
120
- self.logger.info("problem " + ps.prob.name + ' ' + str(ps.id))
126
+ if is_debug_active():
127
+ logger.debug("problem " + ps.prob.name + ' ' + str(ps.id))
121
128
  ps.retry(optimizer)
122
129
 
123
130
  def values(self):
124
- return np.array([ps.value for ps in self.problem_stats])
131
+ return np.fromiter((ps.value for ps in self.problem_stats), dtype=float)
125
132
 
126
133
  def remove_worst(self, n = 1):
127
134
  idx = self.values().argsort()
@@ -133,21 +140,21 @@ class multiretry:
133
140
  return len(self.problem_stats)
134
141
 
135
142
  def dump(self):
136
- if not self.logger is None:
143
+ if is_debug_active():
137
144
  for i in range(self.size()):
138
145
  ps = self.problem_stats[i]
139
- self.logger.info(str(ps.id) + ' ' + str(ps.value))
146
+ logger.debug(str(ps.id) + ' ' + str(ps.value))
140
147
 
141
148
  def dump_all(self):
142
- if not self.logger is None:
149
+ if is_debug_active():
143
150
  idx = self.values_all().argsort()
144
151
  self.all_stats = list(np.asarray(self.all_stats)[idx])
145
152
  for i in range(len(self.all_stats)):
146
153
  ps = self.all_stats[i]
147
- self.logger.info(str(ps.id) + ' ' + str(ps.value))
154
+ logger.debug(str(ps.id) + ' ' + str(ps.value))
148
155
 
149
156
  def values_all(self):
150
- return np.array([ps.value for ps in self.all_stats])
157
+ return np.fromiter((ps.value for ps in self.all_stats), dtype=float)
151
158
 
152
159
  def result(self):
153
160
  idx = self.values_all().argsort()
@@ -185,4 +192,4 @@ class multiretry:
185
192
  for i in range(len(data)):
186
193
  self.all_stats[i].store.set_data(data[i])
187
194
 
188
-
195
+