pymoo 0.6.1.5.dev0__cp310-cp310-macosx_11_0_arm64.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.
Potentially problematic release.
This version of pymoo might be problematic. Click here for more details.
- pymoo/__init__.py +3 -0
- pymoo/algorithms/__init__.py +0 -0
- pymoo/algorithms/base/__init__.py +0 -0
- pymoo/algorithms/base/bracket.py +38 -0
- pymoo/algorithms/base/genetic.py +109 -0
- pymoo/algorithms/base/line.py +62 -0
- pymoo/algorithms/base/local.py +39 -0
- pymoo/algorithms/base/meta.py +79 -0
- pymoo/algorithms/hyperparameters.py +89 -0
- pymoo/algorithms/moo/__init__.py +0 -0
- pymoo/algorithms/moo/age.py +310 -0
- pymoo/algorithms/moo/age2.py +194 -0
- pymoo/algorithms/moo/ctaea.py +298 -0
- pymoo/algorithms/moo/dnsga2.py +76 -0
- pymoo/algorithms/moo/kgb.py +446 -0
- pymoo/algorithms/moo/moead.py +183 -0
- pymoo/algorithms/moo/nsga2.py +113 -0
- pymoo/algorithms/moo/nsga3.py +358 -0
- pymoo/algorithms/moo/pinsga2.py +370 -0
- pymoo/algorithms/moo/rnsga2.py +188 -0
- pymoo/algorithms/moo/rnsga3.py +246 -0
- pymoo/algorithms/moo/rvea.py +214 -0
- pymoo/algorithms/moo/sms.py +195 -0
- pymoo/algorithms/moo/spea2.py +190 -0
- pymoo/algorithms/moo/unsga3.py +47 -0
- pymoo/algorithms/soo/__init__.py +0 -0
- pymoo/algorithms/soo/convex/__init__.py +0 -0
- pymoo/algorithms/soo/nonconvex/__init__.py +0 -0
- pymoo/algorithms/soo/nonconvex/brkga.py +161 -0
- pymoo/algorithms/soo/nonconvex/cmaes.py +554 -0
- pymoo/algorithms/soo/nonconvex/de.py +279 -0
- pymoo/algorithms/soo/nonconvex/direct.py +149 -0
- pymoo/algorithms/soo/nonconvex/es.py +203 -0
- pymoo/algorithms/soo/nonconvex/g3pcx.py +94 -0
- pymoo/algorithms/soo/nonconvex/ga.py +93 -0
- pymoo/algorithms/soo/nonconvex/ga_niching.py +223 -0
- pymoo/algorithms/soo/nonconvex/isres.py +74 -0
- pymoo/algorithms/soo/nonconvex/nelder.py +251 -0
- pymoo/algorithms/soo/nonconvex/optuna.py +80 -0
- pymoo/algorithms/soo/nonconvex/pattern.py +183 -0
- pymoo/algorithms/soo/nonconvex/pso.py +399 -0
- pymoo/algorithms/soo/nonconvex/pso_ep.py +297 -0
- pymoo/algorithms/soo/nonconvex/random_search.py +25 -0
- pymoo/algorithms/soo/nonconvex/sres.py +56 -0
- pymoo/algorithms/soo/univariate/__init__.py +0 -0
- pymoo/algorithms/soo/univariate/backtracking.py +59 -0
- pymoo/algorithms/soo/univariate/exp.py +46 -0
- pymoo/algorithms/soo/univariate/golden.py +65 -0
- pymoo/algorithms/soo/univariate/quadr_interp.py +81 -0
- pymoo/algorithms/soo/univariate/wolfe.py +163 -0
- pymoo/config.py +33 -0
- pymoo/constraints/__init__.py +3 -0
- pymoo/constraints/adaptive.py +62 -0
- pymoo/constraints/as_obj.py +56 -0
- pymoo/constraints/as_penalty.py +41 -0
- pymoo/constraints/eps.py +26 -0
- pymoo/constraints/from_bounds.py +36 -0
- pymoo/core/__init__.py +0 -0
- pymoo/core/algorithm.py +394 -0
- pymoo/core/callback.py +38 -0
- pymoo/core/crossover.py +77 -0
- pymoo/core/decision_making.py +102 -0
- pymoo/core/decomposition.py +76 -0
- pymoo/core/duplicate.py +163 -0
- pymoo/core/evaluator.py +116 -0
- pymoo/core/indicator.py +34 -0
- pymoo/core/individual.py +784 -0
- pymoo/core/infill.py +64 -0
- pymoo/core/initialization.py +42 -0
- pymoo/core/mating.py +39 -0
- pymoo/core/meta.py +21 -0
- pymoo/core/mixed.py +165 -0
- pymoo/core/mutation.py +44 -0
- pymoo/core/operator.py +40 -0
- pymoo/core/parameters.py +134 -0
- pymoo/core/plot.py +210 -0
- pymoo/core/population.py +180 -0
- pymoo/core/problem.py +460 -0
- pymoo/core/recorder.py +99 -0
- pymoo/core/repair.py +23 -0
- pymoo/core/replacement.py +96 -0
- pymoo/core/result.py +52 -0
- pymoo/core/sampling.py +43 -0
- pymoo/core/selection.py +61 -0
- pymoo/core/solution.py +10 -0
- pymoo/core/survival.py +103 -0
- pymoo/core/termination.py +70 -0
- pymoo/core/variable.py +399 -0
- pymoo/cython/__init__.py +0 -0
- pymoo/cython/calc_perpendicular_distance.cpython-310-darwin.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-310-darwin.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-310-darwin.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-310-darwin.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-310-darwin.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-310-darwin.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-310-darwin.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-310-darwin.so +0 -0
- pymoo/cython/stochastic_ranking.pyx +49 -0
- pymoo/cython/utils.pxd +129 -0
- pymoo/cython/vendor/__init__.py +0 -0
- pymoo/cython/vendor/hypervolume.cpp +1621 -0
- pymoo/cython/vendor/hypervolume.h +63 -0
- pymoo/decomposition/__init__.py +0 -0
- pymoo/decomposition/aasf.py +24 -0
- pymoo/decomposition/asf.py +10 -0
- pymoo/decomposition/pbi.py +13 -0
- pymoo/decomposition/perp_dist.py +13 -0
- pymoo/decomposition/tchebicheff.py +11 -0
- pymoo/decomposition/util.py +13 -0
- pymoo/decomposition/weighted_sum.py +8 -0
- pymoo/docs.py +187 -0
- pymoo/experimental/__init__.py +0 -0
- pymoo/experimental/algorithms/__init__.py +0 -0
- pymoo/experimental/algorithms/gde3.py +57 -0
- pymoo/gradient/__init__.py +21 -0
- pymoo/gradient/automatic.py +57 -0
- pymoo/gradient/grad_autograd.py +105 -0
- pymoo/gradient/grad_complex.py +35 -0
- pymoo/gradient/grad_jax.py +51 -0
- pymoo/gradient/toolbox/__init__.py +6 -0
- pymoo/indicators/__init__.py +0 -0
- pymoo/indicators/distance_indicator.py +55 -0
- pymoo/indicators/gd.py +7 -0
- pymoo/indicators/gd_plus.py +7 -0
- pymoo/indicators/hv/__init__.py +63 -0
- pymoo/indicators/hv/exact.py +71 -0
- pymoo/indicators/hv/exact_2d.py +102 -0
- pymoo/indicators/hv/monte_carlo.py +74 -0
- pymoo/indicators/igd.py +7 -0
- pymoo/indicators/igd_plus.py +7 -0
- pymoo/indicators/kktpm.py +151 -0
- pymoo/indicators/migd.py +55 -0
- pymoo/indicators/rmetric.py +203 -0
- pymoo/indicators/spacing.py +52 -0
- pymoo/mcdm/__init__.py +0 -0
- pymoo/mcdm/compromise_programming.py +19 -0
- pymoo/mcdm/high_tradeoff.py +40 -0
- pymoo/mcdm/pseudo_weights.py +32 -0
- pymoo/operators/__init__.py +0 -0
- pymoo/operators/control.py +187 -0
- pymoo/operators/crossover/__init__.py +0 -0
- pymoo/operators/crossover/binx.py +45 -0
- pymoo/operators/crossover/dex.py +122 -0
- pymoo/operators/crossover/erx.py +162 -0
- pymoo/operators/crossover/expx.py +51 -0
- pymoo/operators/crossover/hux.py +37 -0
- pymoo/operators/crossover/nox.py +13 -0
- pymoo/operators/crossover/ox.py +84 -0
- pymoo/operators/crossover/pcx.py +82 -0
- pymoo/operators/crossover/pntx.py +49 -0
- pymoo/operators/crossover/sbx.py +125 -0
- pymoo/operators/crossover/spx.py +5 -0
- pymoo/operators/crossover/ux.py +20 -0
- pymoo/operators/mutation/__init__.py +0 -0
- pymoo/operators/mutation/bitflip.py +17 -0
- pymoo/operators/mutation/gauss.py +58 -0
- pymoo/operators/mutation/inversion.py +42 -0
- pymoo/operators/mutation/nom.py +7 -0
- pymoo/operators/mutation/pm.py +94 -0
- pymoo/operators/mutation/rm.py +23 -0
- pymoo/operators/repair/__init__.py +0 -0
- pymoo/operators/repair/bounce_back.py +32 -0
- pymoo/operators/repair/bounds_repair.py +95 -0
- pymoo/operators/repair/inverse_penalty.py +89 -0
- pymoo/operators/repair/rounding.py +18 -0
- pymoo/operators/repair/to_bound.py +31 -0
- pymoo/operators/repair/vtype.py +11 -0
- pymoo/operators/sampling/__init__.py +0 -0
- pymoo/operators/sampling/lhs.py +73 -0
- pymoo/operators/sampling/rnd.py +50 -0
- pymoo/operators/selection/__init__.py +0 -0
- pymoo/operators/selection/rnd.py +72 -0
- pymoo/operators/selection/tournament.py +76 -0
- pymoo/operators/survival/__init__.py +0 -0
- pymoo/operators/survival/rank_and_crowding/__init__.py +1 -0
- pymoo/operators/survival/rank_and_crowding/classes.py +209 -0
- pymoo/operators/survival/rank_and_crowding/metrics.py +208 -0
- pymoo/optimize.py +72 -0
- pymoo/problems/__init__.py +157 -0
- pymoo/problems/dyn.py +47 -0
- pymoo/problems/dynamic/__init__.py +0 -0
- pymoo/problems/dynamic/cec2015.py +108 -0
- pymoo/problems/dynamic/df.py +452 -0
- pymoo/problems/dynamic/misc.py +167 -0
- pymoo/problems/functional.py +48 -0
- pymoo/problems/many/__init__.py +5 -0
- pymoo/problems/many/cdtlz.py +159 -0
- pymoo/problems/many/dcdtlz.py +88 -0
- pymoo/problems/many/dtlz.py +264 -0
- pymoo/problems/many/wfg.py +550 -0
- pymoo/problems/multi/__init__.py +14 -0
- pymoo/problems/multi/bnh.py +34 -0
- pymoo/problems/multi/carside.py +48 -0
- pymoo/problems/multi/clutch.py +104 -0
- pymoo/problems/multi/csi.py +55 -0
- pymoo/problems/multi/ctp.py +198 -0
- pymoo/problems/multi/dascmop.py +213 -0
- pymoo/problems/multi/kursawe.py +25 -0
- pymoo/problems/multi/modact.py +68 -0
- pymoo/problems/multi/mw.py +400 -0
- pymoo/problems/multi/omnitest.py +48 -0
- pymoo/problems/multi/osy.py +32 -0
- pymoo/problems/multi/srn.py +28 -0
- pymoo/problems/multi/sympart.py +94 -0
- pymoo/problems/multi/tnk.py +24 -0
- pymoo/problems/multi/truss2d.py +83 -0
- pymoo/problems/multi/welded_beam.py +41 -0
- pymoo/problems/multi/wrm.py +36 -0
- pymoo/problems/multi/zdt.py +151 -0
- pymoo/problems/multi_to_single.py +22 -0
- pymoo/problems/single/__init__.py +12 -0
- pymoo/problems/single/ackley.py +24 -0
- pymoo/problems/single/cantilevered_beam.py +34 -0
- pymoo/problems/single/flowshop_scheduling.py +112 -0
- pymoo/problems/single/g.py +874 -0
- pymoo/problems/single/griewank.py +18 -0
- pymoo/problems/single/himmelblau.py +15 -0
- pymoo/problems/single/knapsack.py +48 -0
- pymoo/problems/single/mopta08.py +26 -0
- pymoo/problems/single/multimodal.py +20 -0
- pymoo/problems/single/pressure_vessel.py +30 -0
- pymoo/problems/single/rastrigin.py +20 -0
- pymoo/problems/single/rosenbrock.py +22 -0
- pymoo/problems/single/schwefel.py +18 -0
- pymoo/problems/single/simple.py +13 -0
- pymoo/problems/single/sphere.py +19 -0
- pymoo/problems/single/traveling_salesman.py +79 -0
- pymoo/problems/single/zakharov.py +19 -0
- pymoo/problems/static.py +14 -0
- pymoo/problems/util.py +42 -0
- pymoo/problems/zero_to_one.py +27 -0
- pymoo/termination/__init__.py +23 -0
- pymoo/termination/collection.py +12 -0
- pymoo/termination/cv.py +48 -0
- pymoo/termination/default.py +45 -0
- pymoo/termination/delta.py +64 -0
- pymoo/termination/fmin.py +16 -0
- pymoo/termination/ftol.py +144 -0
- pymoo/termination/indicator.py +49 -0
- pymoo/termination/max_eval.py +14 -0
- pymoo/termination/max_gen.py +15 -0
- pymoo/termination/max_time.py +20 -0
- pymoo/termination/robust.py +34 -0
- pymoo/termination/xtol.py +33 -0
- pymoo/util/__init__.py +0 -0
- pymoo/util/archive.py +150 -0
- pymoo/util/cache.py +29 -0
- pymoo/util/clearing.py +82 -0
- pymoo/util/display/__init__.py +0 -0
- pymoo/util/display/column.py +52 -0
- pymoo/util/display/display.py +34 -0
- pymoo/util/display/multi.py +96 -0
- pymoo/util/display/output.py +53 -0
- pymoo/util/display/progress.py +54 -0
- pymoo/util/display/single.py +67 -0
- pymoo/util/dominator.py +67 -0
- pymoo/util/function_loader.py +129 -0
- pymoo/util/hv.py +23 -0
- pymoo/util/matlab_engine.py +39 -0
- pymoo/util/misc.py +460 -0
- pymoo/util/mnn.py +70 -0
- pymoo/util/nds/__init__.py +0 -0
- pymoo/util/nds/dominance_degree_non_dominated_sort.py +159 -0
- pymoo/util/nds/efficient_non_dominated_sort.py +152 -0
- pymoo/util/nds/fast_non_dominated_sort.py +70 -0
- pymoo/util/nds/naive_non_dominated_sort.py +36 -0
- pymoo/util/nds/non_dominated_sorting.py +67 -0
- pymoo/util/nds/tree_based_non_dominated_sort.py +133 -0
- pymoo/util/normalization.py +312 -0
- pymoo/util/optimum.py +42 -0
- pymoo/util/plotting.py +177 -0
- pymoo/util/pruning_cd.py +89 -0
- pymoo/util/randomized_argsort.py +60 -0
- pymoo/util/ref_dirs/__init__.py +24 -0
- pymoo/util/ref_dirs/construction.py +88 -0
- pymoo/util/ref_dirs/das_dennis.py +52 -0
- pymoo/util/ref_dirs/energy.py +319 -0
- pymoo/util/ref_dirs/energy_layer.py +119 -0
- pymoo/util/ref_dirs/genetic_algorithm.py +63 -0
- pymoo/util/ref_dirs/incremental.py +68 -0
- pymoo/util/ref_dirs/misc.py +128 -0
- pymoo/util/ref_dirs/optimizer.py +59 -0
- pymoo/util/ref_dirs/performance.py +162 -0
- pymoo/util/ref_dirs/reduction.py +85 -0
- pymoo/util/ref_dirs/sample_and_map.py +24 -0
- pymoo/util/reference_direction.py +260 -0
- pymoo/util/remote.py +55 -0
- pymoo/util/roulette.py +27 -0
- pymoo/util/running_metric.py +128 -0
- pymoo/util/sliding_window.py +25 -0
- pymoo/util/stochastic_ranking.py +32 -0
- pymoo/util/value_functions.py +719 -0
- pymoo/util/vectors.py +40 -0
- pymoo/util/vf_dominator.py +99 -0
- pymoo/vendor/__init__.py +0 -0
- pymoo/vendor/cec2018.py +398 -0
- pymoo/vendor/gta.py +617 -0
- pymoo/vendor/hv.py +267 -0
- pymoo/vendor/vendor_cmaes.py +412 -0
- pymoo/vendor/vendor_coco.py +81 -0
- pymoo/vendor/vendor_scipy.py +232 -0
- pymoo/version.py +1 -0
- pymoo/visualization/__init__.py +8 -0
- pymoo/visualization/fitness_landscape.py +127 -0
- pymoo/visualization/heatmap.py +123 -0
- pymoo/visualization/pcp.py +120 -0
- pymoo/visualization/petal.py +91 -0
- pymoo/visualization/radar.py +108 -0
- pymoo/visualization/radviz.py +68 -0
- pymoo/visualization/scatter.py +150 -0
- pymoo/visualization/star_coordinate.py +75 -0
- pymoo/visualization/util.py +123 -0
- pymoo/visualization/video/__init__.py +0 -0
- pymoo/visualization/video/callback_video.py +82 -0
- pymoo/visualization/video/one_var_one_obj.py +57 -0
- pymoo/visualization/video/two_var_one_obj.py +62 -0
- pymoo-0.6.1.5.dev0.dist-info/METADATA +187 -0
- pymoo-0.6.1.5.dev0.dist-info/RECORD +328 -0
- pymoo-0.6.1.5.dev0.dist-info/WHEEL +6 -0
- pymoo-0.6.1.5.dev0.dist-info/licenses/LICENSE +191 -0
- pymoo-0.6.1.5.dev0.dist-info/top_level.txt +1 -0
pymoo/vendor/hv.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Copyright (C) 2010 Simon Wessing
|
|
2
|
+
# TU Dortmund University
|
|
3
|
+
# The author (Simon Wessing) has explicitly given the permission for this source code
|
|
4
|
+
# to be included in pymoo. Thank you!
|
|
5
|
+
|
|
6
|
+
__author__ = "Simon Wessing"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HyperVolume:
|
|
10
|
+
"""
|
|
11
|
+
Hypervolume computation based on cross 3 of the algorithm in the paper:
|
|
12
|
+
C. M. Fonseca, L. Paquete, and M. Lopez-Ibanez. An improved dimension-sweep
|
|
13
|
+
algorithm for the hypervolume indicator. In IEEE Congress on Evolutionary
|
|
14
|
+
Computation, pages 1157-1163, Vancouver, Canada, July 2006.
|
|
15
|
+
|
|
16
|
+
Minimization is implicitly assumed here!
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, referencePoint):
|
|
21
|
+
"""Constructor."""
|
|
22
|
+
self.referencePoint = referencePoint
|
|
23
|
+
self.list = []
|
|
24
|
+
|
|
25
|
+
def compute(self, front):
|
|
26
|
+
"""Returns the hypervolume that is dominated by a non-dominated front.
|
|
27
|
+
|
|
28
|
+
Before the HV computation, front and reference point are translated, so
|
|
29
|
+
that the reference point is [0, ..., 0].
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def weaklyDominates(point, other):
|
|
34
|
+
for i in range(len(point)):
|
|
35
|
+
if point[i] > other[i]:
|
|
36
|
+
return False
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
relevantPoints = []
|
|
40
|
+
referencePoint = self.referencePoint
|
|
41
|
+
dimensions = len(referencePoint)
|
|
42
|
+
for point in front:
|
|
43
|
+
# only consider points that dominate the reference point
|
|
44
|
+
if weaklyDominates(point, referencePoint):
|
|
45
|
+
relevantPoints.append(point)
|
|
46
|
+
if any(referencePoint):
|
|
47
|
+
# shift points so that referencePoint == [0, ..., 0]
|
|
48
|
+
# this way the reference point doesn't have to be explicitly used
|
|
49
|
+
# in the HV computation
|
|
50
|
+
for j in range(len(relevantPoints)):
|
|
51
|
+
relevantPoints[j] = [relevantPoints[j][i] - referencePoint[i] for i in range(dimensions)]
|
|
52
|
+
self.preProcess(relevantPoints)
|
|
53
|
+
bounds = [-1.0e308] * dimensions
|
|
54
|
+
hyperVolume = self.hvRecursive(dimensions - 1, len(relevantPoints), bounds)
|
|
55
|
+
return hyperVolume
|
|
56
|
+
|
|
57
|
+
def hvRecursive(self, dimIndex, length, bounds):
|
|
58
|
+
"""Recursive call to hypervolume calculation.
|
|
59
|
+
|
|
60
|
+
In contrast to the paper, the code assumes that the reference point
|
|
61
|
+
is [0, ..., 0]. This allows the avoidance of a few operations.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
hvol = 0.0
|
|
65
|
+
sentinel = self.list.sentinel
|
|
66
|
+
if length == 0:
|
|
67
|
+
return hvol
|
|
68
|
+
elif dimIndex == 0:
|
|
69
|
+
# special case: only one dimension
|
|
70
|
+
# why using hypervolume at all?
|
|
71
|
+
return -sentinel.next[0].cargo[0]
|
|
72
|
+
elif dimIndex == 1:
|
|
73
|
+
# special case: two dimensions, end recursion
|
|
74
|
+
q = sentinel.next[1]
|
|
75
|
+
h = q.cargo[0]
|
|
76
|
+
p = q.next[1]
|
|
77
|
+
while p is not sentinel:
|
|
78
|
+
pCargo = p.cargo
|
|
79
|
+
hvol += h * (q.cargo[1] - pCargo[1])
|
|
80
|
+
if pCargo[0] < h:
|
|
81
|
+
h = pCargo[0]
|
|
82
|
+
q = p
|
|
83
|
+
p = q.next[1]
|
|
84
|
+
hvol += h * q.cargo[1]
|
|
85
|
+
return hvol
|
|
86
|
+
else:
|
|
87
|
+
remove = self.list.remove
|
|
88
|
+
reinsert = self.list.reinsert
|
|
89
|
+
hvRecursive = self.hvRecursive
|
|
90
|
+
p = sentinel
|
|
91
|
+
q = p.prev[dimIndex]
|
|
92
|
+
while q.cargo is not None:
|
|
93
|
+
if q.ignore < dimIndex:
|
|
94
|
+
q.ignore = 0
|
|
95
|
+
q = q.prev[dimIndex]
|
|
96
|
+
q = p.prev[dimIndex]
|
|
97
|
+
while length > 1 and (
|
|
98
|
+
q.cargo[dimIndex] > bounds[dimIndex] or q.prev[dimIndex].cargo[dimIndex] >= bounds[dimIndex]):
|
|
99
|
+
p = q
|
|
100
|
+
remove(p, dimIndex, bounds)
|
|
101
|
+
q = p.prev[dimIndex]
|
|
102
|
+
length -= 1
|
|
103
|
+
qArea = q.area
|
|
104
|
+
qCargo = q.cargo
|
|
105
|
+
qPrevDimIndex = q.prev[dimIndex]
|
|
106
|
+
if length > 1:
|
|
107
|
+
hvol = qPrevDimIndex.volume[dimIndex] + qPrevDimIndex.area[dimIndex] * (
|
|
108
|
+
qCargo[dimIndex] - qPrevDimIndex.cargo[dimIndex])
|
|
109
|
+
else:
|
|
110
|
+
qArea[0] = 1
|
|
111
|
+
qArea[1:dimIndex + 1] = [qArea[i] * -qCargo[i] for i in range(dimIndex)]
|
|
112
|
+
q.volume[dimIndex] = hvol
|
|
113
|
+
if q.ignore >= dimIndex:
|
|
114
|
+
qArea[dimIndex] = qPrevDimIndex.area[dimIndex]
|
|
115
|
+
else:
|
|
116
|
+
qArea[dimIndex] = hvRecursive(dimIndex - 1, length, bounds)
|
|
117
|
+
if qArea[dimIndex] <= qPrevDimIndex.area[dimIndex]:
|
|
118
|
+
q.ignore = dimIndex
|
|
119
|
+
while p is not sentinel:
|
|
120
|
+
pCargoDimIndex = p.cargo[dimIndex]
|
|
121
|
+
hvol += q.area[dimIndex] * (pCargoDimIndex - q.cargo[dimIndex])
|
|
122
|
+
bounds[dimIndex] = pCargoDimIndex
|
|
123
|
+
reinsert(p, dimIndex, bounds)
|
|
124
|
+
length += 1
|
|
125
|
+
q = p
|
|
126
|
+
p = p.next[dimIndex]
|
|
127
|
+
q.volume[dimIndex] = hvol
|
|
128
|
+
if q.ignore >= dimIndex:
|
|
129
|
+
q.area[dimIndex] = q.prev[dimIndex].area[dimIndex]
|
|
130
|
+
else:
|
|
131
|
+
q.area[dimIndex] = hvRecursive(dimIndex - 1, length, bounds)
|
|
132
|
+
if q.area[dimIndex] <= q.prev[dimIndex].area[dimIndex]:
|
|
133
|
+
q.ignore = dimIndex
|
|
134
|
+
hvol -= q.area[dimIndex] * q.cargo[dimIndex]
|
|
135
|
+
return hvol
|
|
136
|
+
|
|
137
|
+
def preProcess(self, front):
|
|
138
|
+
"""Sets up the list data structure needed for calculation."""
|
|
139
|
+
dimensions = len(self.referencePoint)
|
|
140
|
+
nodeList = MultiList(dimensions)
|
|
141
|
+
nodes = [MultiList.Node(dimensions, point) for point in front]
|
|
142
|
+
for i in range(dimensions):
|
|
143
|
+
self.sortByDimension(nodes, i)
|
|
144
|
+
nodeList.extend(nodes, i)
|
|
145
|
+
self.list = nodeList
|
|
146
|
+
|
|
147
|
+
def sortByDimension(self, nodes, i):
|
|
148
|
+
"""Sorts the list of nodes by the i-th value of the contained points."""
|
|
149
|
+
# build a list of tuples of (point[i], node)
|
|
150
|
+
decorated = [(node.cargo[i], index, node) for index, node in enumerate(nodes)]
|
|
151
|
+
# sort by this value
|
|
152
|
+
decorated.sort()
|
|
153
|
+
# write back to original list
|
|
154
|
+
nodes[:] = [node for (_, _, node) in decorated]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class MultiList:
|
|
158
|
+
"""A special data structure needed by FonsecaHyperVolume.
|
|
159
|
+
|
|
160
|
+
It consists of several doubly linked lists that share common nodes. So,
|
|
161
|
+
every node has multiple predecessors and successors, one in every list.
|
|
162
|
+
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
class Node:
|
|
166
|
+
|
|
167
|
+
def __init__(self, numberLists, cargo=None):
|
|
168
|
+
self.cargo = cargo
|
|
169
|
+
self.next = [None] * numberLists
|
|
170
|
+
self.prev = [None] * numberLists
|
|
171
|
+
self.ignore = 0
|
|
172
|
+
self.area = [0.0] * numberLists
|
|
173
|
+
self.volume = [0.0] * numberLists
|
|
174
|
+
|
|
175
|
+
def __str__(self):
|
|
176
|
+
return str(self.cargo)
|
|
177
|
+
|
|
178
|
+
def __init__(self, numberLists):
|
|
179
|
+
"""Constructor.
|
|
180
|
+
|
|
181
|
+
Builds 'numberLists' doubly linked lists.
|
|
182
|
+
|
|
183
|
+
"""
|
|
184
|
+
self.numberLists = numberLists
|
|
185
|
+
self.sentinel = MultiList.Node(numberLists)
|
|
186
|
+
self.sentinel.next = [self.sentinel] * numberLists
|
|
187
|
+
self.sentinel.prev = [self.sentinel] * numberLists
|
|
188
|
+
|
|
189
|
+
def __str__(self):
|
|
190
|
+
strings = []
|
|
191
|
+
for i in range(self.numberLists):
|
|
192
|
+
currentList = []
|
|
193
|
+
node = self.sentinel.next[i]
|
|
194
|
+
while node != self.sentinel:
|
|
195
|
+
currentList.append(str(node))
|
|
196
|
+
node = node.next[i]
|
|
197
|
+
strings.append(str(currentList))
|
|
198
|
+
stringRepr = ""
|
|
199
|
+
for string in strings:
|
|
200
|
+
stringRepr += string + "\n"
|
|
201
|
+
return stringRepr
|
|
202
|
+
|
|
203
|
+
def __len__(self):
|
|
204
|
+
"""Returns the number of lists that are included in this MultiList."""
|
|
205
|
+
return self.numberLists
|
|
206
|
+
|
|
207
|
+
def getLength(self, i):
|
|
208
|
+
"""Returns the length of the i-th list."""
|
|
209
|
+
length = 0
|
|
210
|
+
sentinel = self.sentinel
|
|
211
|
+
node = sentinel.next[i]
|
|
212
|
+
while node != sentinel:
|
|
213
|
+
length += 1
|
|
214
|
+
node = node.next[i]
|
|
215
|
+
return length
|
|
216
|
+
|
|
217
|
+
def append(self, node, index):
|
|
218
|
+
"""Appends a node to the end of the list at the given index."""
|
|
219
|
+
lastButOne = self.sentinel.prev[index]
|
|
220
|
+
node.next[index] = self.sentinel
|
|
221
|
+
node.prev[index] = lastButOne
|
|
222
|
+
# set the last element as the new one
|
|
223
|
+
self.sentinel.prev[index] = node
|
|
224
|
+
lastButOne.next[index] = node
|
|
225
|
+
|
|
226
|
+
def extend(self, nodes, index):
|
|
227
|
+
"""Extends the list at the given index with the nodes."""
|
|
228
|
+
sentinel = self.sentinel
|
|
229
|
+
for node in nodes:
|
|
230
|
+
lastButOne = sentinel.prev[index]
|
|
231
|
+
node.next[index] = sentinel
|
|
232
|
+
node.prev[index] = lastButOne
|
|
233
|
+
# set the last element as the new one
|
|
234
|
+
sentinel.prev[index] = node
|
|
235
|
+
lastButOne.next[index] = node
|
|
236
|
+
|
|
237
|
+
def remove(self, node, index, bounds):
|
|
238
|
+
"""Removes and returns 'node' from all lists in [0, 'index'[."""
|
|
239
|
+
for i in range(index):
|
|
240
|
+
predecessor = node.prev[i]
|
|
241
|
+
successor = node.next[i]
|
|
242
|
+
predecessor.next[i] = successor
|
|
243
|
+
successor.prev[i] = predecessor
|
|
244
|
+
if bounds[i] > node.cargo[i]:
|
|
245
|
+
bounds[i] = node.cargo[i]
|
|
246
|
+
return node
|
|
247
|
+
|
|
248
|
+
def reinsert(self, node, index, bounds):
|
|
249
|
+
"""
|
|
250
|
+
Inserts 'node' at the position it had in all lists in [0, 'index'[
|
|
251
|
+
before it was removed. This method assumes that the next and previous
|
|
252
|
+
nodes of the node that is reinserted are in the list.
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
for i in range(index):
|
|
256
|
+
node.prev[i].next[i] = node
|
|
257
|
+
node.next[i].prev[i] = node
|
|
258
|
+
if bounds[i] > node.cargo[i]:
|
|
259
|
+
bounds[i] = node.cargo[i]
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
# Example:
|
|
264
|
+
referencePoint = [2, 2, 2]
|
|
265
|
+
hv = HyperVolume(referencePoint)
|
|
266
|
+
front = [[1, 0, 1], [0, 1, 0]]
|
|
267
|
+
volume = hv.compute(front)
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from cma import CMAOptions
|
|
5
|
+
from cma import optimization_tools as ot
|
|
6
|
+
from cma.evolution_strategy import cma_default_options, CMAEvolutionStrategy
|
|
7
|
+
from cma.utilities import utils
|
|
8
|
+
from cma.utilities.math import Mh
|
|
9
|
+
|
|
10
|
+
all_stoppings = []
|
|
11
|
+
|
|
12
|
+
def void(_):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def my_fmin(x0,
|
|
16
|
+
sigma0,
|
|
17
|
+
objective_function=void,
|
|
18
|
+
options=None,
|
|
19
|
+
args=(),
|
|
20
|
+
gradf=None,
|
|
21
|
+
restarts=0,
|
|
22
|
+
restart_from_best='False',
|
|
23
|
+
incpopsize=2,
|
|
24
|
+
eval_initial_x=False,
|
|
25
|
+
parallel_objective=None,
|
|
26
|
+
noise_handler=None,
|
|
27
|
+
noise_change_sigma_exponent=1,
|
|
28
|
+
noise_kappa_exponent=0, # TODO: add max kappa value as parameter
|
|
29
|
+
bipop=False,
|
|
30
|
+
callback=None):
|
|
31
|
+
|
|
32
|
+
if 1 < 3: # try: # pass on KeyboardInterrupt
|
|
33
|
+
if not objective_function and not parallel_objective: # cma.fmin(0, 0, 0)
|
|
34
|
+
return CMAOptions() # these opts are by definition valid
|
|
35
|
+
|
|
36
|
+
fmin_options = locals().copy() # archive original options
|
|
37
|
+
del fmin_options['objective_function']
|
|
38
|
+
del fmin_options['x0']
|
|
39
|
+
del fmin_options['sigma0']
|
|
40
|
+
del fmin_options['options']
|
|
41
|
+
del fmin_options['args']
|
|
42
|
+
|
|
43
|
+
if options is None:
|
|
44
|
+
options = cma_default_options
|
|
45
|
+
CMAOptions().check_attributes(options) # might modify options
|
|
46
|
+
# checked that no options.ftarget =
|
|
47
|
+
opts = CMAOptions(options.copy()).complement()
|
|
48
|
+
|
|
49
|
+
if callback is None:
|
|
50
|
+
callback = []
|
|
51
|
+
elif callable(callback):
|
|
52
|
+
callback = [callback]
|
|
53
|
+
|
|
54
|
+
# BIPOP-related variables:
|
|
55
|
+
runs_with_small = 0
|
|
56
|
+
small_i = []
|
|
57
|
+
large_i = []
|
|
58
|
+
popsize0 = None # to be evaluated after the first iteration
|
|
59
|
+
maxiter0 = None # to be evaluated after the first iteration
|
|
60
|
+
base_evals = 0
|
|
61
|
+
|
|
62
|
+
irun = 0
|
|
63
|
+
best = ot.BestSolution()
|
|
64
|
+
all_stoppings = []
|
|
65
|
+
while True: # restart loop
|
|
66
|
+
sigma_factor = 1
|
|
67
|
+
|
|
68
|
+
# Adjust the population according to BIPOP after a restart.
|
|
69
|
+
if not bipop:
|
|
70
|
+
# BIPOP not in use, simply double the previous population
|
|
71
|
+
# on restart.
|
|
72
|
+
if irun > 0:
|
|
73
|
+
popsize_multiplier = fmin_options['incpopsize'] ** (irun - runs_with_small)
|
|
74
|
+
opts['popsize'] = popsize0 * popsize_multiplier
|
|
75
|
+
|
|
76
|
+
elif irun == 0:
|
|
77
|
+
# Initial run is with "normal" population size; it is
|
|
78
|
+
# the large population before first doubling, but its
|
|
79
|
+
# budget accounting is the same as in case of small
|
|
80
|
+
# population.
|
|
81
|
+
poptype = 'small'
|
|
82
|
+
|
|
83
|
+
elif sum(small_i) < sum(large_i):
|
|
84
|
+
# An interweaved run with small population size
|
|
85
|
+
poptype = 'small'
|
|
86
|
+
if 11 < 3: # not needed when compared to irun - runs_with_small
|
|
87
|
+
restarts += 1 # A small restart doesn't count in the total
|
|
88
|
+
runs_with_small += 1 # _Before_ it's used in popsize_lastlarge
|
|
89
|
+
|
|
90
|
+
sigma_factor = 0.01 ** np.random.uniform() # Local search
|
|
91
|
+
popsize_multiplier = fmin_options['incpopsize'] ** (irun - runs_with_small)
|
|
92
|
+
opts['popsize'] = np.floor(popsize0 * popsize_multiplier ** (np.random.uniform() ** 2))
|
|
93
|
+
opts['maxiter'] = min(maxiter0, 0.5 * sum(large_i) / opts['popsize'])
|
|
94
|
+
# print('small basemul %s --> %s; maxiter %s' % (popsize_multiplier, opts['popsize'], opts['maxiter']))
|
|
95
|
+
|
|
96
|
+
else:
|
|
97
|
+
# A run with large population size; the population
|
|
98
|
+
# doubling is implicit with incpopsize.
|
|
99
|
+
poptype = 'large'
|
|
100
|
+
|
|
101
|
+
popsize_multiplier = fmin_options['incpopsize'] ** (irun - runs_with_small)
|
|
102
|
+
opts['popsize'] = popsize0 * popsize_multiplier
|
|
103
|
+
opts['maxiter'] = maxiter0
|
|
104
|
+
# print('large basemul %s --> %s; maxiter %s' % (popsize_multiplier, opts['popsize'], opts['maxiter']))
|
|
105
|
+
|
|
106
|
+
if not callable(objective_function) and callable(parallel_objective):
|
|
107
|
+
def objective_function(x, *args):
|
|
108
|
+
"""created from `parallel_objective` argument"""
|
|
109
|
+
return parallel_objective([x], *args)[0]
|
|
110
|
+
|
|
111
|
+
# recover from a CMA object
|
|
112
|
+
if irun == 0 and isinstance(x0, MyCMAEvolutionStrategy):
|
|
113
|
+
es = x0
|
|
114
|
+
x0 = es.inputargs['x0'] # for the next restarts
|
|
115
|
+
if np.isscalar(sigma0) and np.isfinite(sigma0) and sigma0 > 0:
|
|
116
|
+
es.sigma = sigma0
|
|
117
|
+
# debatable whether this makes sense:
|
|
118
|
+
sigma0 = es.inputargs['sigma0'] # for the next restarts
|
|
119
|
+
if options is not None:
|
|
120
|
+
es.opts.set(options)
|
|
121
|
+
# ignore further input args and keep original options
|
|
122
|
+
else: # default case
|
|
123
|
+
if irun and eval(str(fmin_options['restart_from_best'])):
|
|
124
|
+
utils.print_warning('CAVE: restart_from_best is often not useful',
|
|
125
|
+
verbose=opts['verbose'])
|
|
126
|
+
es = MyCMAEvolutionStrategy(best.x, sigma_factor * sigma0, opts)
|
|
127
|
+
else:
|
|
128
|
+
es = MyCMAEvolutionStrategy(x0, sigma_factor * sigma0, opts)
|
|
129
|
+
# return opts, es
|
|
130
|
+
if callable(objective_function) and (
|
|
131
|
+
eval_initial_x
|
|
132
|
+
or es.opts['CMA_elitist'] == 'initial'
|
|
133
|
+
or (es.opts['CMA_elitist'] and
|
|
134
|
+
eval_initial_x is None)):
|
|
135
|
+
x = es.gp.pheno(es.mean,
|
|
136
|
+
into_bounds=es.boundary_handler.repair,
|
|
137
|
+
archive=es.sent_solutions)
|
|
138
|
+
es.f0 = yield x
|
|
139
|
+
es.best.update([x], es.sent_solutions,
|
|
140
|
+
[es.f0], 1)
|
|
141
|
+
es.countevals += 1
|
|
142
|
+
es.objective_function = objective_function # only for the record
|
|
143
|
+
|
|
144
|
+
opts = es.opts # processed options, unambiguous
|
|
145
|
+
# a hack:
|
|
146
|
+
fmin_opts = CMAOptions("unchecked", **fmin_options.copy())
|
|
147
|
+
for k in fmin_opts:
|
|
148
|
+
# locals() cannot be modified directly, exec won't work
|
|
149
|
+
# in 3.x, therefore
|
|
150
|
+
fmin_opts.eval(k, loc={'N': es.N,
|
|
151
|
+
'popsize': opts['popsize']},
|
|
152
|
+
correct_key=False)
|
|
153
|
+
|
|
154
|
+
es.logger.append = opts['verb_append'] or es.countiter > 0 or irun > 0
|
|
155
|
+
# es.logger is "the same" logger, because the "identity"
|
|
156
|
+
# is only determined by the `verb_filenameprefix` option
|
|
157
|
+
logger = es.logger # shortcut
|
|
158
|
+
try:
|
|
159
|
+
logger.persistent_communication_dict.update(
|
|
160
|
+
{'variable_annotations':
|
|
161
|
+
objective_function.variable_annotations})
|
|
162
|
+
except AttributeError:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
if 11 < 3:
|
|
166
|
+
if es.countiter == 0 and es.opts['verb_log'] > 0 and \
|
|
167
|
+
not es.opts['verb_append']:
|
|
168
|
+
logger = CMADataLogger(es.opts['verb_filenameprefix']
|
|
169
|
+
).register(es)
|
|
170
|
+
logger.add()
|
|
171
|
+
es.writeOutput() # initial values for sigma etc
|
|
172
|
+
|
|
173
|
+
if noise_handler:
|
|
174
|
+
if isinstance(noise_handler, type):
|
|
175
|
+
noisehandler = noise_handler(es.N)
|
|
176
|
+
else:
|
|
177
|
+
noisehandler = noise_handler
|
|
178
|
+
noise_handling = True
|
|
179
|
+
if fmin_opts['noise_change_sigma_exponent'] > 0:
|
|
180
|
+
es.opts['tolfacupx'] = inf
|
|
181
|
+
else:
|
|
182
|
+
noisehandler = ot.NoiseHandler(es.N, 0) # switched off
|
|
183
|
+
noise_handling = False
|
|
184
|
+
es.noise_handler = noisehandler
|
|
185
|
+
|
|
186
|
+
# the problem: this assumes that good solutions cannot take longer than bad ones:
|
|
187
|
+
# with EvalInParallel(objective_function, 2, is_feasible=opts['is_feasible']) as eval_in_parallel:
|
|
188
|
+
if 1 < 3:
|
|
189
|
+
while not es.stop(): # iteration loop
|
|
190
|
+
# X, fit = eval_in_parallel(lambda: es.ask(1)[0], es.popsize, args, repetitions=noisehandler.evaluations-1)
|
|
191
|
+
X, fit = yield from es.ask_and_eval(parallel_objective or objective_function,
|
|
192
|
+
args, gradf=gradf,
|
|
193
|
+
evaluations=noisehandler.evaluations,
|
|
194
|
+
aggregation=np.median,
|
|
195
|
+
parallel_mode=parallel_objective) # treats NaN with resampling if not parallel_mode
|
|
196
|
+
# TODO: check args and in case use args=(noisehandler.evaluations, )
|
|
197
|
+
|
|
198
|
+
if 11 < 3 and opts['vv']: # inject a solution
|
|
199
|
+
# use option check_point = [0]
|
|
200
|
+
if 0 * np.random.randn() >= 0:
|
|
201
|
+
X[0] = 0 + opts['vv'] * es.sigma ** 0 * np.random.randn(es.N)
|
|
202
|
+
fit[0] = yield X[0]
|
|
203
|
+
# print fit[0]
|
|
204
|
+
if es.opts['verbose'] > 4: # may be undesirable with dynamic fitness (e.g. Augmented Lagrangian)
|
|
205
|
+
if es.countiter < 2 or min(fit) <= es.best.last.f:
|
|
206
|
+
degrading_iterations_count = 0 # comes first to avoid code check complaint
|
|
207
|
+
else: # min(fit) > es.best.last.f:
|
|
208
|
+
degrading_iterations_count += 1
|
|
209
|
+
if degrading_iterations_count > 4:
|
|
210
|
+
utils.print_message('%d f-degrading iterations (set verbose<=4 to suppress)'
|
|
211
|
+
% degrading_iterations_count,
|
|
212
|
+
iteration=es.countiter)
|
|
213
|
+
es.tell(X, fit) # prepare for next iteration
|
|
214
|
+
if noise_handling: # it would be better to also use these f-evaluations in tell
|
|
215
|
+
es.sigma *= noisehandler(X, fit, objective_function, es.ask,
|
|
216
|
+
args=args) ** fmin_opts['noise_change_sigma_exponent']
|
|
217
|
+
|
|
218
|
+
es.countevals += noisehandler.evaluations_just_done # TODO: this is a hack, not important though
|
|
219
|
+
# es.more_to_write.append(noisehandler.evaluations_just_done)
|
|
220
|
+
if noisehandler.maxevals > noisehandler.minevals:
|
|
221
|
+
es.more_to_write.append(noisehandler.evaluations)
|
|
222
|
+
if 1 < 3:
|
|
223
|
+
# If sigma was above multiplied by the same
|
|
224
|
+
# factor cmean is divided by here, this is
|
|
225
|
+
# like only multiplying kappa instead of
|
|
226
|
+
# changing cmean and sigma.
|
|
227
|
+
es.sp.cmean *= np.exp(-noise_kappa_exponent * np.tanh(noisehandler.noiseS))
|
|
228
|
+
es.sp.cmean[es.sp.cmean > 1] = 1.0 # also works with "scalar arrays" like np.array(1.2)
|
|
229
|
+
for f in callback:
|
|
230
|
+
f is None or f(es)
|
|
231
|
+
es.disp()
|
|
232
|
+
logger.add(
|
|
233
|
+
# more_data=[noisehandler.evaluations, 10**noisehandler.noiseS] if noise_handling else [],
|
|
234
|
+
modulo=1 if es.stop() and logger.modulo else None)
|
|
235
|
+
if (opts['verb_log'] and opts['verb_plot'] and
|
|
236
|
+
(es.countiter % max(opts['verb_plot'], opts['verb_log']) == 0 or es.stop())):
|
|
237
|
+
logger.plot(324)
|
|
238
|
+
|
|
239
|
+
# end while not es.stop
|
|
240
|
+
if opts['eval_final_mean'] and callable(objective_function):
|
|
241
|
+
mean_pheno = es.gp.pheno(es.mean,
|
|
242
|
+
into_bounds=es.boundary_handler.repair,
|
|
243
|
+
archive=es.sent_solutions)
|
|
244
|
+
fmean = yield mean_pheno
|
|
245
|
+
es.countevals += 1
|
|
246
|
+
es.best.update([mean_pheno], es.sent_solutions, [fmean], es.countevals)
|
|
247
|
+
|
|
248
|
+
best.update(es.best, es.sent_solutions) # in restarted case
|
|
249
|
+
# es.best.update(best)
|
|
250
|
+
|
|
251
|
+
this_evals = es.countevals - base_evals
|
|
252
|
+
base_evals = es.countevals
|
|
253
|
+
|
|
254
|
+
# BIPOP stats update
|
|
255
|
+
|
|
256
|
+
if irun == 0:
|
|
257
|
+
popsize0 = opts['popsize']
|
|
258
|
+
maxiter0 = opts['maxiter']
|
|
259
|
+
# XXX: This might be a bug? Reproduced from Matlab
|
|
260
|
+
# small_i.append(this_evals)
|
|
261
|
+
|
|
262
|
+
if bipop:
|
|
263
|
+
if poptype == 'small':
|
|
264
|
+
small_i.append(this_evals)
|
|
265
|
+
else: # poptype == 'large'
|
|
266
|
+
large_i.append(this_evals)
|
|
267
|
+
|
|
268
|
+
# final message
|
|
269
|
+
if opts['verb_disp']:
|
|
270
|
+
es.result_pretty(irun, time.asctime(time.localtime()),
|
|
271
|
+
best.f)
|
|
272
|
+
|
|
273
|
+
irun += 1
|
|
274
|
+
# if irun > fmin_opts['restarts'] or 'ftarget' in es.stop() \
|
|
275
|
+
# if irun > restarts or 'ftarget' in es.stop() \
|
|
276
|
+
all_stoppings.append(dict(es.stop(check=False))) # keeping the order
|
|
277
|
+
if irun - runs_with_small > fmin_opts['restarts'] or 'ftarget' in es.stop() \
|
|
278
|
+
or 'maxfevals' in es.stop(check=False) or 'callback' in es.stop(check=False):
|
|
279
|
+
break
|
|
280
|
+
opts['verb_append'] = es.countevals
|
|
281
|
+
opts['popsize'] = fmin_opts['incpopsize'] * es.sp.popsize # TODO: use rather options?
|
|
282
|
+
try:
|
|
283
|
+
opts['seed'] += 1
|
|
284
|
+
except TypeError:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
# while irun
|
|
288
|
+
|
|
289
|
+
# es.out['best'] = best # TODO: this is a rather suboptimal type for inspection in the shell
|
|
290
|
+
if irun:
|
|
291
|
+
es.best.update(best)
|
|
292
|
+
# TODO: there should be a better way to communicate the overall best
|
|
293
|
+
return es.result + (es.stop(), es, logger)
|
|
294
|
+
### 4560
|
|
295
|
+
# TODO refine output, can #args be flexible?
|
|
296
|
+
# is this well usable as it is now?
|
|
297
|
+
else: # except KeyboardInterrupt: # Exception as e:
|
|
298
|
+
if eval(safe_str(options['verb_disp'])) > 0:
|
|
299
|
+
print(' in/outcomment ``raise`` in last line of cma.fmin to prevent/restore KeyboardInterrupt exception')
|
|
300
|
+
raise KeyboardInterrupt # cave: swallowing this exception can silently mess up experiments, if ctrl-C is hit
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class MyCMAEvolutionStrategy(CMAEvolutionStrategy):
|
|
304
|
+
|
|
305
|
+
def ask_and_eval(self, func, args=(), gradf=None, number=None, xmean=None, sigma_fac=1,
|
|
306
|
+
evaluations=1, aggregation=np.median, kappa=1, parallel_mode=False):
|
|
307
|
+
|
|
308
|
+
# initialize
|
|
309
|
+
popsize = self.sp.popsize
|
|
310
|
+
if number is not None:
|
|
311
|
+
popsize = int(number)
|
|
312
|
+
|
|
313
|
+
if self.opts['CMA_mirrormethod'] == 1: # direct selective mirrors
|
|
314
|
+
nmirrors = Mh.sround(self.sp.lam_mirr * popsize / self.sp.popsize)
|
|
315
|
+
self._mirrormethod1_done = self.countiter
|
|
316
|
+
else:
|
|
317
|
+
# method==0 unconditional mirrors are done in ask_geno
|
|
318
|
+
# method==2 delayed selective mirrors are done via injection
|
|
319
|
+
nmirrors = 0
|
|
320
|
+
assert nmirrors <= popsize // 2
|
|
321
|
+
self.mirrors_idx = np.arange(nmirrors) # might never be used
|
|
322
|
+
is_feasible = self.opts['is_feasible']
|
|
323
|
+
|
|
324
|
+
# do the work
|
|
325
|
+
fit = [] # or np.NaN * np.empty(number)
|
|
326
|
+
X_first = self.ask(popsize, xmean=xmean, gradf=gradf, args=args)
|
|
327
|
+
if xmean is None:
|
|
328
|
+
xmean = self.mean # might have changed in self.ask
|
|
329
|
+
X = []
|
|
330
|
+
if parallel_mode:
|
|
331
|
+
if hasattr(func, 'evaluations'):
|
|
332
|
+
evals0 = func.evaluations
|
|
333
|
+
fit_first = yield X_first
|
|
334
|
+
# the rest is only book keeping and warnings spitting
|
|
335
|
+
if hasattr(func, 'evaluations'):
|
|
336
|
+
self.countevals += func.evaluations - evals0 - self.popsize # why not .sp.popsize ?
|
|
337
|
+
if nmirrors and self.opts['CMA_mirrormethod'] > 0 and self.countiter < 2:
|
|
338
|
+
utils.print_warning(
|
|
339
|
+
"selective mirrors will not work in parallel mode",
|
|
340
|
+
"ask_and_eval", "CMAEvolutionStrategy")
|
|
341
|
+
if evaluations > 1 and self.countiter < 2:
|
|
342
|
+
utils.print_warning(
|
|
343
|
+
"aggregating evaluations will not work in parallel mode",
|
|
344
|
+
"ask_and_eval", "CMAEvolutionStrategy")
|
|
345
|
+
else:
|
|
346
|
+
fit_first = len(X_first) * [None]
|
|
347
|
+
for k in range(popsize):
|
|
348
|
+
x, f = X_first.pop(0), fit_first.pop(0)
|
|
349
|
+
rejected = -1
|
|
350
|
+
while f is None or not is_feasible(x, f): # rejection sampling
|
|
351
|
+
if parallel_mode:
|
|
352
|
+
utils.print_warning(
|
|
353
|
+
"rejection sampling will not work in parallel mode"
|
|
354
|
+
" unless the parallel_objective makes a distinction\n"
|
|
355
|
+
"between called with a numpy array vs a list (of"
|
|
356
|
+
" numpy arrays) as first argument.",
|
|
357
|
+
"ask_and_eval", "CMAEvolutionStrategy")
|
|
358
|
+
rejected += 1
|
|
359
|
+
if rejected: # resample
|
|
360
|
+
x = self.ask(1, xmean, sigma_fac)[0]
|
|
361
|
+
elif k >= popsize - nmirrors: # selective mirrors
|
|
362
|
+
if k == popsize - nmirrors:
|
|
363
|
+
self.mirrors_idx = np.argsort(fit)[-1:-1 - nmirrors:-1]
|
|
364
|
+
x = self.get_mirror(X[self.mirrors_idx[popsize - 1 - k]])
|
|
365
|
+
|
|
366
|
+
# constraints handling test hardwired ccccccccccc
|
|
367
|
+
|
|
368
|
+
length_normalizer = 1
|
|
369
|
+
# zzzzzzzzzzzzzzzzzzzzzzzzz
|
|
370
|
+
if 11 < 3:
|
|
371
|
+
# for some unclear reason, this normalization does not work as expected: the step-size
|
|
372
|
+
# becomes sometimes too large and overall the mean might diverge. Is the reason that
|
|
373
|
+
# we observe random fluctuations, because the length is not selection relevant?
|
|
374
|
+
# However sigma-adaptation should mainly work on the correlation, not the length?
|
|
375
|
+
# Or is the reason the deviation of the direction introduced by using the original
|
|
376
|
+
# length, which also can effect the measured correlation?
|
|
377
|
+
# Update: if the length of z in CSA is clipped at chiN+1, it works, but only sometimes?
|
|
378
|
+
length_normalizer = self.N**0.5 / self.mahalanobis_norm(x - xmean) # self.const.chiN < N**0.5, the constant here is irrelevant (absorbed by kappa)
|
|
379
|
+
# print(self.N**0.5 / self.mahalanobis_norm(x - xmean))
|
|
380
|
+
# self.more_to_write += [length_normalizer * 1e-3, length_normalizer * self.mahalanobis_norm(x - xmean) * 1e2]
|
|
381
|
+
if kappa == 1:
|
|
382
|
+
f = yield x
|
|
383
|
+
else:
|
|
384
|
+
f = yield xmean + kappa * length_normalizer * (x - xmean)
|
|
385
|
+
|
|
386
|
+
if is_feasible(x, f) and evaluations > 1:
|
|
387
|
+
if kappa == 1:
|
|
388
|
+
_f = yield x
|
|
389
|
+
f = aggregation([f] + [_f])
|
|
390
|
+
else:
|
|
391
|
+
_f = []
|
|
392
|
+
for _i in range(int(evaluations - 1)):
|
|
393
|
+
v = yield xmean + kappa * length_normalizer * (x - xmean)
|
|
394
|
+
_f.append(v)
|
|
395
|
+
f = aggregation([f] + _f)
|
|
396
|
+
|
|
397
|
+
if (rejected + 1) % 1000 == 0:
|
|
398
|
+
utils.print_warning(' %d solutions rejected (f-value NaN or None) at iteration %d' %
|
|
399
|
+
(rejected, self.countiter))
|
|
400
|
+
fit.append(f)
|
|
401
|
+
X.append(x)
|
|
402
|
+
self.evaluations_per_f_value = int(evaluations)
|
|
403
|
+
if any(f is None or utils.is_nan(f) for f in fit):
|
|
404
|
+
idxs = [i for i in range(len(fit))
|
|
405
|
+
if fit[i] is None or utils.is_nan(fit[i])]
|
|
406
|
+
utils.print_warning("f-values %s contain None or NaN at indices %s"
|
|
407
|
+
% (str(fit[:30]) + ('...' if len(fit) > 30 else ''),
|
|
408
|
+
str(idxs)),
|
|
409
|
+
'ask_and_tell',
|
|
410
|
+
'CMAEvolutionStrategy',
|
|
411
|
+
self.countiter)
|
|
412
|
+
return X, fit
|