pymoo 0.6.1.6__cp312-cp312-macosx_10_13_universal2.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.
- 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 +110 -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 +91 -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/cmopso.py +239 -0
- pymoo/algorithms/moo/ctaea.py +305 -0
- pymoo/algorithms/moo/dnsga2.py +80 -0
- pymoo/algorithms/moo/kgb.py +450 -0
- pymoo/algorithms/moo/moead.py +183 -0
- pymoo/algorithms/moo/mopso_cd.py +309 -0
- pymoo/algorithms/moo/nsga2.py +113 -0
- pymoo/algorithms/moo/nsga3.py +361 -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 +196 -0
- pymoo/algorithms/moo/spea2.py +191 -0
- pymoo/algorithms/moo/unsga3.py +49 -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 +162 -0
- pymoo/algorithms/soo/nonconvex/cmaes.py +556 -0
- pymoo/algorithms/soo/nonconvex/de.py +283 -0
- pymoo/algorithms/soo/nonconvex/direct.py +148 -0
- pymoo/algorithms/soo/nonconvex/es.py +213 -0
- pymoo/algorithms/soo/nonconvex/g3pcx.py +94 -0
- pymoo/algorithms/soo/nonconvex/ga.py +95 -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/nrbo.py +191 -0
- pymoo/algorithms/soo/nonconvex/optuna.py +80 -0
- pymoo/algorithms/soo/nonconvex/pattern.py +185 -0
- pymoo/algorithms/soo/nonconvex/pso.py +337 -0
- pymoo/algorithms/soo/nonconvex/pso_ep.py +307 -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/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 +66 -0
- pymoo/constraints/as_obj.py +56 -0
- pymoo/constraints/as_penalty.py +41 -0
- pymoo/constraints/eps.py +34 -0
- pymoo/constraints/from_bounds.py +36 -0
- pymoo/core/__init__.py +0 -0
- pymoo/core/algorithm.py +408 -0
- pymoo/core/callback.py +38 -0
- pymoo/core/crossover.py +79 -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 +65 -0
- pymoo/core/initialization.py +44 -0
- pymoo/core/mating.py +39 -0
- pymoo/core/meta.py +21 -0
- pymoo/core/mixed.py +164 -0
- pymoo/core/mutation.py +44 -0
- pymoo/core/operator.py +46 -0
- pymoo/core/parameters.py +134 -0
- pymoo/core/plot.py +208 -0
- pymoo/core/population.py +180 -0
- pymoo/core/problem.py +373 -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 +45 -0
- pymoo/core/selection.py +61 -0
- pymoo/core/solution.py +10 -0
- pymoo/core/survival.py +107 -0
- pymoo/core/termination.py +70 -0
- pymoo/core/variable.py +415 -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/functions/__init__.py +135 -0
- pymoo/functions/compiled/__init__.py +0 -0
- pymoo/functions/compiled/calc_perpendicular_distance.cpp +27464 -0
- pymoo/functions/compiled/calc_perpendicular_distance.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/decomposition.cpp +28853 -0
- pymoo/functions/compiled/decomposition.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/info.cpp +7058 -0
- pymoo/functions/compiled/info.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/mnn.cpp +30095 -0
- pymoo/functions/compiled/mnn.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/non_dominated_sorting.cpp +35692 -0
- pymoo/functions/compiled/non_dominated_sorting.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/pruning_cd.cpp +29248 -0
- pymoo/functions/compiled/pruning_cd.cpython-312-darwin.so +0 -0
- pymoo/functions/compiled/stochastic_ranking.cpp +28042 -0
- pymoo/functions/compiled/stochastic_ranking.cpython-312-darwin.so +0 -0
- pymoo/functions/standard/__init__.py +1 -0
- pymoo/functions/standard/calc_perpendicular_distance.py +20 -0
- pymoo/functions/standard/decomposition.py +18 -0
- pymoo/functions/standard/hv.py +5 -0
- pymoo/functions/standard/mnn.py +78 -0
- pymoo/functions/standard/non_dominated_sorting.py +474 -0
- pymoo/functions/standard/pruning_cd.py +93 -0
- pymoo/functions/standard/stochastic_ranking.py +42 -0
- pymoo/gradient/__init__.py +24 -0
- pymoo/gradient/automatic.py +85 -0
- pymoo/gradient/grad_autograd.py +105 -0
- pymoo/gradient/grad_complex.py +35 -0
- pymoo/gradient/grad_jax.py +51 -0
- pymoo/gradient/numpy.py +22 -0
- pymoo/gradient/toolbox/__init__.py +19 -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 +59 -0
- pymoo/indicators/hv/approximate.py +105 -0
- pymoo/indicators/hv/exact.py +68 -0
- pymoo/indicators/hv/exact_2d.py +102 -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 +190 -0
- pymoo/operators/crossover/__init__.py +0 -0
- pymoo/operators/crossover/binx.py +47 -0
- pymoo/operators/crossover/dex.py +125 -0
- pymoo/operators/crossover/erx.py +164 -0
- pymoo/operators/crossover/expx.py +53 -0
- pymoo/operators/crossover/hux.py +37 -0
- pymoo/operators/crossover/nox.py +25 -0
- pymoo/operators/crossover/ox.py +88 -0
- pymoo/operators/crossover/pcx.py +84 -0
- pymoo/operators/crossover/pntx.py +49 -0
- pymoo/operators/crossover/sbx.py +137 -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 +60 -0
- pymoo/operators/mutation/inversion.py +42 -0
- pymoo/operators/mutation/nom.py +7 -0
- pymoo/operators/mutation/pm.py +96 -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 +97 -0
- pymoo/operators/repair/inverse_penalty.py +91 -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 +76 -0
- pymoo/operators/sampling/rnd.py +52 -0
- pymoo/operators/selection/__init__.py +0 -0
- pymoo/operators/selection/rnd.py +75 -0
- pymoo/operators/selection/tournament.py +78 -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 +212 -0
- pymoo/operators/survival/rank_and_crowding/metrics.py +208 -0
- pymoo/optimize.py +72 -0
- pymoo/parallelization/__init__.py +15 -0
- pymoo/parallelization/dask.py +25 -0
- pymoo/parallelization/joblib.py +28 -0
- pymoo/parallelization/ray.py +31 -0
- pymoo/parallelization/starmap.py +24 -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 +451 -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 +553 -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 +113 -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 +49 -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 +33 -0
- pymoo/util/archive.py +152 -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 +100 -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/hv.py +21 -0
- pymoo/util/matlab_engine.py +39 -0
- pymoo/util/misc.py +447 -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/find_non_dominated.py +54 -0
- pymoo/util/nds/naive_non_dominated_sort.py +36 -0
- pymoo/util/nds/non_dominated_sorting.py +94 -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/randomized_argsort.py +63 -0
- pymoo/util/ref_dirs/__init__.py +24 -0
- pymoo/util/ref_dirs/construction.py +89 -0
- pymoo/util/ref_dirs/das_dennis.py +52 -0
- pymoo/util/ref_dirs/energy.py +317 -0
- pymoo/util/ref_dirs/energy_layer.py +119 -0
- pymoo/util/ref_dirs/genetic_algorithm.py +64 -0
- pymoo/util/ref_dirs/incremental.py +69 -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 +258 -0
- pymoo/util/remote.py +55 -0
- pymoo/util/roulette.py +29 -0
- pymoo/util/running_metric.py +128 -0
- pymoo/util/sliding_window.py +25 -0
- pymoo/util/value_functions.py +720 -0
- pymoo/util/vectors.py +40 -0
- pymoo/util/vf_dominator.py +102 -0
- pymoo/vendor/__init__.py +0 -0
- pymoo/vendor/cec2018.py +398 -0
- pymoo/vendor/gta.py +617 -0
- pymoo/vendor/vendor_cmaes.py +421 -0
- pymoo/vendor/vendor_coco.py +81 -0
- pymoo/vendor/vendor_scipy.py +232 -0
- pymoo/version.py +1 -0
- pymoo/visualization/__init__.py +21 -0
- pymoo/visualization/app/__init__.py +0 -0
- pymoo/visualization/app/pso.py +61 -0
- pymoo/visualization/fitness_landscape.py +128 -0
- pymoo/visualization/heatmap.py +123 -0
- pymoo/visualization/matplotlib.py +61 -0
- pymoo/visualization/pcp.py +121 -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 +296 -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.6.dist-info/METADATA +209 -0
- pymoo-0.6.1.6.dist-info/RECORD +337 -0
- pymoo-0.6.1.6.dist-info/WHEEL +6 -0
- pymoo-0.6.1.6.dist-info/licenses/LICENSE +191 -0
- pymoo-0.6.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
7
|
+
# Object Oriented Interface
|
|
8
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
# ---- Abstract Class
|
|
11
|
+
from numpy.linalg import LinAlgError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Normalization:
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def forward(self, X):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def backward(self, X):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ---- Useful if normalization is optional - can be simply disabled by using this object
|
|
29
|
+
class NoNormalization(Normalization):
|
|
30
|
+
|
|
31
|
+
def forward(self, X):
|
|
32
|
+
return X
|
|
33
|
+
|
|
34
|
+
def backward(self, X):
|
|
35
|
+
return X
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ---- Normalizes between zero and one given bounds or estimating them
|
|
39
|
+
class ZeroToOneNormalization(Normalization):
|
|
40
|
+
|
|
41
|
+
def __init__(self, xl=None, xu=None) -> None:
|
|
42
|
+
super().__init__()
|
|
43
|
+
|
|
44
|
+
# if both are None we are basically done because normalization is disabled
|
|
45
|
+
if xl is None and xu is None:
|
|
46
|
+
self.xl, self.xu = None, None
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
# if not set simply fall back no nan values
|
|
50
|
+
if xl is None:
|
|
51
|
+
xl = np.full_like(xu, np.nan)
|
|
52
|
+
if xu is None:
|
|
53
|
+
xu = np.full_like(xl, np.nan)
|
|
54
|
+
|
|
55
|
+
xl, xu = np.copy(xl).astype(float), np.copy(xu).astype(float)
|
|
56
|
+
|
|
57
|
+
# if both are equal then set the upper bound to none (always the 0 or lower bound will be returned then)
|
|
58
|
+
xu[xl == xu] = np.nan
|
|
59
|
+
|
|
60
|
+
# store the lower and upper bounds
|
|
61
|
+
self.xl, self.xu = xl, xu
|
|
62
|
+
|
|
63
|
+
# check out when the input values are nan
|
|
64
|
+
xl_nan, xu_nan = np.isnan(xl), np.isnan(xu)
|
|
65
|
+
|
|
66
|
+
# now create all the masks that are necessary
|
|
67
|
+
self.xl_only, self.xu_only = np.logical_and(~xl_nan, xu_nan), np.logical_and(xl_nan, ~xu_nan)
|
|
68
|
+
self.both_nan = np.logical_and(np.isnan(xl), np.isnan(xu))
|
|
69
|
+
self.neither_nan = np.logical_and(~np.isnan(xl), ~np.isnan(xu))
|
|
70
|
+
|
|
71
|
+
# if neither is nan than xu must be greater or equal than xl
|
|
72
|
+
any_nan = np.logical_or(np.isnan(xl), np.isnan(xu))
|
|
73
|
+
assert np.all(np.logical_or(xu >= xl, any_nan)), "xl must be less or equal than xu."
|
|
74
|
+
|
|
75
|
+
def forward(self, X):
|
|
76
|
+
if X is None or (self.xl is None and self.xu is None):
|
|
77
|
+
return X
|
|
78
|
+
|
|
79
|
+
# simple copy the input
|
|
80
|
+
N = np.copy(X)
|
|
81
|
+
|
|
82
|
+
# normalize between zero and one if neither of them is nan
|
|
83
|
+
N[..., self.neither_nan] = (X[..., self.neither_nan] - self.xl[self.neither_nan]) / (self.xu[self.neither_nan] - self.xl[self.neither_nan])
|
|
84
|
+
|
|
85
|
+
N[..., self.xl_only] = X[..., self.xl_only] - self.xl[self.xl_only]
|
|
86
|
+
|
|
87
|
+
N[..., self.xu_only] = 1.0 - (self.xu[self.xu_only] - X[..., self.xu_only])
|
|
88
|
+
|
|
89
|
+
return N
|
|
90
|
+
|
|
91
|
+
def backward(self, N):
|
|
92
|
+
if N is None or (self.xl is None and self.xu is None):
|
|
93
|
+
return N
|
|
94
|
+
|
|
95
|
+
xl, xu, xl_only, xu_only = self.xl, self.xu, self.xl_only, self.xu_only
|
|
96
|
+
both_nan, neither_nan = self.both_nan, self.neither_nan
|
|
97
|
+
|
|
98
|
+
X = N.copy()
|
|
99
|
+
X[..., neither_nan] = xl[neither_nan] + N[..., neither_nan] * (xu[neither_nan] - xl[neither_nan])
|
|
100
|
+
|
|
101
|
+
X[..., xl_only] = N[..., xl_only] + xl[xl_only]
|
|
102
|
+
|
|
103
|
+
X[..., xu_only] = xu[xu_only] - (1.0 - N[..., xu_only])
|
|
104
|
+
|
|
105
|
+
return X
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
109
|
+
# Simple Normalization
|
|
110
|
+
# Does not consider any none values and assumes lower as well as upper bounds are given.
|
|
111
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class SimpleZeroToOneNormalization(Normalization):
|
|
115
|
+
|
|
116
|
+
def __init__(self, xl=None, xu=None, estimate_bounds=True) -> None:
|
|
117
|
+
super().__init__()
|
|
118
|
+
self.xl = xl
|
|
119
|
+
self.xu = xu
|
|
120
|
+
self.estimate_bounds = estimate_bounds
|
|
121
|
+
|
|
122
|
+
def forward(self, X):
|
|
123
|
+
|
|
124
|
+
if self.estimate_bounds:
|
|
125
|
+
if self.xl is None:
|
|
126
|
+
self.xl = np.min(X, axis=0)
|
|
127
|
+
if self.xu is None:
|
|
128
|
+
self.xu = np.max(X, axis=0)
|
|
129
|
+
|
|
130
|
+
xl, xu = self.xl, self.xu
|
|
131
|
+
|
|
132
|
+
# if np.any(xl == xu):
|
|
133
|
+
# raise Exception("Normalization failed because lower and upper bounds are equal!")
|
|
134
|
+
|
|
135
|
+
# calculate the denominator
|
|
136
|
+
denom = xu - xl
|
|
137
|
+
|
|
138
|
+
# we can not divide by zero -> plus small epsilon
|
|
139
|
+
denom += (denom == 0) * 1e-32
|
|
140
|
+
|
|
141
|
+
# normalize the actual values
|
|
142
|
+
N = (X - xl) / denom
|
|
143
|
+
|
|
144
|
+
return N
|
|
145
|
+
|
|
146
|
+
def backward(self, X):
|
|
147
|
+
return X * (self.xu - self.xl) + self.xl
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
151
|
+
# Functional Interface
|
|
152
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def normalize(X, xl=None, xu=None, return_bounds=False, estimate_bounds_if_none=True):
|
|
156
|
+
if estimate_bounds_if_none:
|
|
157
|
+
if xl is None:
|
|
158
|
+
xl = np.min(X, axis=0)
|
|
159
|
+
if xu is None:
|
|
160
|
+
xu = np.max(X, axis=0)
|
|
161
|
+
|
|
162
|
+
if isinstance(xl, float) or isinstance(xl, int):
|
|
163
|
+
xl = np.full(X.shape[-1], xl)
|
|
164
|
+
|
|
165
|
+
if isinstance(xu, float) or isinstance(xu, int):
|
|
166
|
+
xu = np.full(X.shape[-1], xu)
|
|
167
|
+
|
|
168
|
+
norm = ZeroToOneNormalization(xl, xu)
|
|
169
|
+
X = norm.forward(X)
|
|
170
|
+
|
|
171
|
+
if not return_bounds:
|
|
172
|
+
return X
|
|
173
|
+
else:
|
|
174
|
+
return X, norm.xl, norm.xu
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def denormalize(x, xl, xu):
|
|
178
|
+
return ZeroToOneNormalization(xl, xu).backward(x)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def standardize(x, return_bounds=False):
|
|
182
|
+
mean = np.mean(x, axis=0)
|
|
183
|
+
std = np.std(x, axis=0)
|
|
184
|
+
|
|
185
|
+
# standardize
|
|
186
|
+
val = (x - mean) / std
|
|
187
|
+
|
|
188
|
+
if not return_bounds:
|
|
189
|
+
return val
|
|
190
|
+
else:
|
|
191
|
+
return val, mean, std
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def destandardize(x, mean, std):
|
|
195
|
+
return (x * std) + mean
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
199
|
+
# Pre Normalization
|
|
200
|
+
# A class inheriting from it can use the in-built feature of normalizing
|
|
201
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class PreNormalization:
|
|
205
|
+
|
|
206
|
+
def __init__(self, zero_to_one=False, ideal=None, nadir=None, **kwargs):
|
|
207
|
+
|
|
208
|
+
# normalization related stuff if that should be performed beforehand
|
|
209
|
+
self.ideal, self.nadir = ideal, nadir
|
|
210
|
+
|
|
211
|
+
if zero_to_one:
|
|
212
|
+
assert self.ideal is not None and self.nadir is not None, "For normalization either provide pf or bounds!"
|
|
213
|
+
|
|
214
|
+
n_dim = len(self.ideal)
|
|
215
|
+
self.normalization = ZeroToOneNormalization(self.ideal, self.nadir)
|
|
216
|
+
|
|
217
|
+
# now the ideal and nadir points have change to only zeros and ones
|
|
218
|
+
self.ideal, self.nadir = np.zeros(n_dim), np.ones(n_dim)
|
|
219
|
+
|
|
220
|
+
else:
|
|
221
|
+
self.normalization = NoNormalization()
|
|
222
|
+
|
|
223
|
+
def do(self, *args, **kwargs):
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
228
|
+
# Normalization in the Objective Space
|
|
229
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def find_ideal(F, current=None):
|
|
233
|
+
p = F.min(axis=0)
|
|
234
|
+
if current is not None:
|
|
235
|
+
p = np.minimum(current, p)
|
|
236
|
+
return p
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_extreme_points_c(F, ideal_point, extreme_points=None):
|
|
240
|
+
# calculate the asf which is used for the extreme point decomposition
|
|
241
|
+
weights = np.eye(F.shape[1])
|
|
242
|
+
weights[weights == 0] = 1e6
|
|
243
|
+
|
|
244
|
+
# add the old extreme points to never loose them for normalization
|
|
245
|
+
_F = F
|
|
246
|
+
if extreme_points is not None:
|
|
247
|
+
_F = np.concatenate([extreme_points, _F], axis=0)
|
|
248
|
+
|
|
249
|
+
# use __F because we substitute small values to be 0
|
|
250
|
+
__F = _F - ideal_point
|
|
251
|
+
__F[__F < 1e-3] = 0
|
|
252
|
+
|
|
253
|
+
# update the extreme points for the normalization having the highest asf value each
|
|
254
|
+
F_asf = np.max(__F * weights[:, None, :], axis=2)
|
|
255
|
+
|
|
256
|
+
I = np.argmin(F_asf, axis=1)
|
|
257
|
+
extreme_points = _F[I, :]
|
|
258
|
+
|
|
259
|
+
return extreme_points
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def get_nadir_point(extreme_points, ideal_point, worst_point, worst_of_front, worst_of_population):
|
|
263
|
+
try:
|
|
264
|
+
|
|
265
|
+
# find the intercepts using gaussian elimination
|
|
266
|
+
M = extreme_points - ideal_point
|
|
267
|
+
b = np.ones(extreme_points.shape[1])
|
|
268
|
+
plane = np.linalg.solve(M, b)
|
|
269
|
+
|
|
270
|
+
warnings.simplefilter("ignore")
|
|
271
|
+
intercepts = 1 / plane
|
|
272
|
+
|
|
273
|
+
nadir_point = ideal_point + intercepts
|
|
274
|
+
|
|
275
|
+
# check if the hyperplane makes sense
|
|
276
|
+
if not np.allclose(np.dot(M, plane), b) or np.any(intercepts <= 1e-6):
|
|
277
|
+
raise LinAlgError()
|
|
278
|
+
|
|
279
|
+
# if the nadir point should be larger than any value discovered so far set it to that value
|
|
280
|
+
# NOTE: different to the proposed version in the paper
|
|
281
|
+
b = nadir_point > worst_point
|
|
282
|
+
nadir_point[b] = worst_point[b]
|
|
283
|
+
|
|
284
|
+
except LinAlgError:
|
|
285
|
+
|
|
286
|
+
# fall back to worst of front otherwise
|
|
287
|
+
nadir_point = worst_of_front
|
|
288
|
+
|
|
289
|
+
# if the range is too small set it to worst of population
|
|
290
|
+
b = nadir_point - ideal_point <= 1e-6
|
|
291
|
+
nadir_point[b] = worst_of_population[b]
|
|
292
|
+
|
|
293
|
+
return nadir_point
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ObjectiveSpaceNormalization:
|
|
297
|
+
|
|
298
|
+
def __init__(self) -> None:
|
|
299
|
+
super().__init__()
|
|
300
|
+
self._ideal = None
|
|
301
|
+
self._infeas_ideal = None
|
|
302
|
+
self._worst = None
|
|
303
|
+
|
|
304
|
+
def update(self, pop):
|
|
305
|
+
F, feas = pop.get("F", "FEAS")
|
|
306
|
+
self._infeas_ideal = find_ideal(F, current=self._infeas_ideal)
|
|
307
|
+
|
|
308
|
+
if np.any(feas):
|
|
309
|
+
self._ideal = find_ideal(F[feas[:, 0]], self._ideal)
|
|
310
|
+
|
|
311
|
+
def ideal(self, only_feas=True):
|
|
312
|
+
return self._ideal if only_feas else self._infeas_ideal
|
pymoo/util/optimum.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.individual import Individual
|
|
4
|
+
from pymoo.core.population import Population
|
|
5
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def filter_optimum(pop, least_infeasible=False):
|
|
9
|
+
|
|
10
|
+
# if the population is none to optimum can be found
|
|
11
|
+
if pop is None or len(pop) == 0:
|
|
12
|
+
return None
|
|
13
|
+
|
|
14
|
+
# first only choose feasible solutions
|
|
15
|
+
ret = pop[pop.get("feas")]
|
|
16
|
+
|
|
17
|
+
# if at least one feasible solution was found
|
|
18
|
+
if len(ret) > 0:
|
|
19
|
+
|
|
20
|
+
# then check the objective values
|
|
21
|
+
F = ret.get("F")
|
|
22
|
+
|
|
23
|
+
if F.shape[1] > 1:
|
|
24
|
+
I = NonDominatedSorting().do(F, only_non_dominated_front=True)
|
|
25
|
+
ret = ret[I]
|
|
26
|
+
|
|
27
|
+
else:
|
|
28
|
+
ret = ret[np.argmin(F[:, 0])]
|
|
29
|
+
|
|
30
|
+
# no feasible solution was found
|
|
31
|
+
else:
|
|
32
|
+
# if flag enable report the least infeasible
|
|
33
|
+
if least_infeasible:
|
|
34
|
+
ret = pop[np.argmin(pop.get("CV"))]
|
|
35
|
+
# otherwise just return none
|
|
36
|
+
else:
|
|
37
|
+
ret = None
|
|
38
|
+
|
|
39
|
+
if isinstance(ret, Individual):
|
|
40
|
+
ret = Population().create(ret)
|
|
41
|
+
|
|
42
|
+
return ret
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.util.misc import swap
|
|
4
|
+
from pymoo.util import default_random_state
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@default_random_state
|
|
8
|
+
def randomized_argsort(A, method="numpy", order='ascending', random_state=None):
|
|
9
|
+
if method == "numpy":
|
|
10
|
+
P = random_state.permutation(len(A))
|
|
11
|
+
I = np.argsort(A[P], kind='quicksort')
|
|
12
|
+
I = P[I]
|
|
13
|
+
|
|
14
|
+
elif method == "quicksort":
|
|
15
|
+
I = quicksort(A)
|
|
16
|
+
|
|
17
|
+
else:
|
|
18
|
+
raise Exception("Randomized sort method not known.")
|
|
19
|
+
|
|
20
|
+
if order == 'ascending':
|
|
21
|
+
return I
|
|
22
|
+
elif order == 'descending':
|
|
23
|
+
return np.flip(I, axis=0)
|
|
24
|
+
else:
|
|
25
|
+
raise Exception("Unknown sorting order: ascending or descending.")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@default_random_state
|
|
29
|
+
def quicksort(A, random_state=None):
|
|
30
|
+
I = np.arange(len(A))
|
|
31
|
+
_quicksort(A, I, 0, len(A) - 1, random_state=random_state)
|
|
32
|
+
return I
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _quicksort(A, I, left, right, random_state):
|
|
36
|
+
if left < right:
|
|
37
|
+
|
|
38
|
+
index = random_state.integers(left, right + 1)
|
|
39
|
+
swap(I, right, index)
|
|
40
|
+
|
|
41
|
+
pivot = A[I[right]]
|
|
42
|
+
|
|
43
|
+
i = left - 1
|
|
44
|
+
|
|
45
|
+
for j in range(left, right):
|
|
46
|
+
|
|
47
|
+
if A[I[j]] <= pivot:
|
|
48
|
+
i += 1
|
|
49
|
+
swap(I, i, j)
|
|
50
|
+
|
|
51
|
+
index = i + 1
|
|
52
|
+
swap(I, right, index)
|
|
53
|
+
|
|
54
|
+
_quicksort(A, I, left, index - 1, random_state)
|
|
55
|
+
_quicksort(A, I, index + 1, right, random_state)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == '__main__':
|
|
59
|
+
a = np.array([5, 9, 10, 0, 0, 0, 100, -2])
|
|
60
|
+
|
|
61
|
+
for i in range(200):
|
|
62
|
+
I = randomized_argsort(a, method="numpy")
|
|
63
|
+
print(I)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pymoo.util.ref_dirs.energy import RieszEnergyReferenceDirectionFactory
|
|
2
|
+
from pymoo.util.ref_dirs.energy_layer import LayerwiseRieszEnergyReferenceDirectionFactory
|
|
3
|
+
from pymoo.util.ref_dirs.reduction import ReductionBasedReferenceDirectionFactory
|
|
4
|
+
from pymoo.util.ref_dirs.incremental import IncrementalReferenceDirectionFactory
|
|
5
|
+
from pymoo.util.reference_direction import MultiLayerReferenceDirectionFactory
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_reference_directions(name, *args, **kwargs):
|
|
9
|
+
from pymoo.util.reference_direction import UniformReferenceDirectionFactory
|
|
10
|
+
|
|
11
|
+
REF = {
|
|
12
|
+
"uniform": UniformReferenceDirectionFactory,
|
|
13
|
+
"das-dennis": UniformReferenceDirectionFactory,
|
|
14
|
+
"energy": RieszEnergyReferenceDirectionFactory,
|
|
15
|
+
"multi-layer": MultiLayerReferenceDirectionFactory,
|
|
16
|
+
"layer-energy": LayerwiseRieszEnergyReferenceDirectionFactory,
|
|
17
|
+
"reduction": ReductionBasedReferenceDirectionFactory,
|
|
18
|
+
"incremental": IncrementalReferenceDirectionFactory,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if name not in REF:
|
|
22
|
+
raise Exception("Reference directions factory not found.")
|
|
23
|
+
|
|
24
|
+
return REF[name](*args, **kwargs)()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.util.misc import vectorized_cdist
|
|
4
|
+
from pymoo.util.ref_dirs.misc import project_onto_sum_equals_zero_plane, project_onto_unit_simplex_recursive
|
|
5
|
+
from pymoo.util.ref_dirs.optimizer import Adam
|
|
6
|
+
from pymoo.util.reference_direction import ReferenceDirectionFactory, map_onto_unit_simplex
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def calc_dist_to_others(x, X):
|
|
10
|
+
return - np.sqrt(((x - X) ** 2).sum(axis=1)).min()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def calc_dist_to_others_with_gradient(x, X):
|
|
14
|
+
diff = (x - X)
|
|
15
|
+
D = np.sqrt((diff ** 2).sum(axis=1))
|
|
16
|
+
|
|
17
|
+
k = D.argmin()
|
|
18
|
+
|
|
19
|
+
obj = - D.min()
|
|
20
|
+
grad = - diff[k] / D[k]
|
|
21
|
+
|
|
22
|
+
return obj, grad
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ConstructionBasedReferenceDirectionFactory(ReferenceDirectionFactory):
|
|
26
|
+
|
|
27
|
+
def __init__(self,
|
|
28
|
+
n_dim,
|
|
29
|
+
n_points,
|
|
30
|
+
n_samples=100,
|
|
31
|
+
gradient_descent=True,
|
|
32
|
+
verbose=False,
|
|
33
|
+
**kwargs):
|
|
34
|
+
|
|
35
|
+
super().__init__(n_dim, **kwargs)
|
|
36
|
+
self.n_points = n_points
|
|
37
|
+
self.gradient_descent = gradient_descent
|
|
38
|
+
self.n_samples = n_samples
|
|
39
|
+
self.verbose = verbose
|
|
40
|
+
self.X = None
|
|
41
|
+
|
|
42
|
+
def _do(self, random_state=None, **kwargs):
|
|
43
|
+
|
|
44
|
+
self.random_state = random_state
|
|
45
|
+
self.X = np.eye(self.n_dim)
|
|
46
|
+
|
|
47
|
+
while len(self.X) < self.n_points:
|
|
48
|
+
x = self.next()
|
|
49
|
+
self.X = np.vstack([self.X, x])
|
|
50
|
+
|
|
51
|
+
if self.verbose:
|
|
52
|
+
print(len(self.X), "x", x)
|
|
53
|
+
|
|
54
|
+
return self.X
|
|
55
|
+
|
|
56
|
+
def next(self):
|
|
57
|
+
|
|
58
|
+
x = self.random_state.random((self.n_samples, self.n_dim))
|
|
59
|
+
x = map_onto_unit_simplex(x, "kraemer")
|
|
60
|
+
x = x[vectorized_cdist(x, self.X).min(axis=1).argmax()]
|
|
61
|
+
|
|
62
|
+
if self.gradient_descent:
|
|
63
|
+
|
|
64
|
+
optimizer = Adam(precision=1e-4)
|
|
65
|
+
|
|
66
|
+
# for each iteration of gradient descent
|
|
67
|
+
for i in range(1000):
|
|
68
|
+
|
|
69
|
+
# calculate the function value and the gradient
|
|
70
|
+
# auto_obj, auto_grad = value_and_grad(calc_dist_to_others)(x, self.X)
|
|
71
|
+
_obj, _grad = calc_dist_to_others_with_gradient(x, self.X)
|
|
72
|
+
|
|
73
|
+
# project the gradient to have a sum of zero - guarantees to stay on the simplex
|
|
74
|
+
proj_grad = project_onto_sum_equals_zero_plane(_grad)
|
|
75
|
+
|
|
76
|
+
# apply a step of gradient descent by subtracting the projected gradient with a learning rate
|
|
77
|
+
x = optimizer.next(x, proj_grad)
|
|
78
|
+
|
|
79
|
+
# project the out of bounds points back onto the unit simplex
|
|
80
|
+
project_onto_unit_simplex_recursive(x[None, :])
|
|
81
|
+
|
|
82
|
+
# because of floating point issues make sure it is on the unit simplex
|
|
83
|
+
x /= x.sum()
|
|
84
|
+
|
|
85
|
+
# if there was only a little movement during the last iteration -> terminate
|
|
86
|
+
if optimizer.has_converged:
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
return x
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy import special
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DasDennis:
|
|
6
|
+
|
|
7
|
+
def __init__(self, n_partitions, n_dim, scaling=None):
|
|
8
|
+
super().__init__()
|
|
9
|
+
self.n_partitions = n_partitions
|
|
10
|
+
self.n_dim = n_dim
|
|
11
|
+
self.scaling = scaling
|
|
12
|
+
|
|
13
|
+
self.stack = []
|
|
14
|
+
self.stack.append(([], self.n_partitions))
|
|
15
|
+
|
|
16
|
+
def number_of_points(self):
|
|
17
|
+
return int(special.binom(self.n_dim + self.n_partitions - 1, self.n_partitions))
|
|
18
|
+
|
|
19
|
+
def next(self, n_points=None):
|
|
20
|
+
ret = []
|
|
21
|
+
self.traverse(lambda p: ret.append(p), n_points)
|
|
22
|
+
return np.array(ret)
|
|
23
|
+
|
|
24
|
+
def has_next(self):
|
|
25
|
+
return len(self.stack) > 0
|
|
26
|
+
|
|
27
|
+
def traverse(self, func, n_points=None):
|
|
28
|
+
|
|
29
|
+
if self.n_partitions == 0:
|
|
30
|
+
return np.full((1, self.n_dim), 1 / self.n_dim)
|
|
31
|
+
|
|
32
|
+
counter = 0
|
|
33
|
+
|
|
34
|
+
while (n_points is None or counter < n_points) and len(self.stack) > 0:
|
|
35
|
+
|
|
36
|
+
point, beta = self.stack.pop()
|
|
37
|
+
|
|
38
|
+
if len(point) + 1 == self.n_dim:
|
|
39
|
+
point.append(beta / (1.0 * self.n_partitions))
|
|
40
|
+
|
|
41
|
+
if self.scaling is not None:
|
|
42
|
+
point = [p * self.scaling + ((1 - self.scaling) / len(point)) for p in point]
|
|
43
|
+
|
|
44
|
+
func(point)
|
|
45
|
+
counter += 1
|
|
46
|
+
else:
|
|
47
|
+
for i in range(beta + 1):
|
|
48
|
+
_point = list(point)
|
|
49
|
+
_point.append(1.0 * i / (1.0 * self.n_partitions))
|
|
50
|
+
self.stack.append((_point, beta - i))
|
|
51
|
+
|
|
52
|
+
return counter
|