pymoo 0.6.1.5.dev0__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-313-x86_64-linux-gnu.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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
|
|
3
|
+
from jax.config import config
|
|
4
|
+
|
|
5
|
+
config.update("jax_enable_x64", True)
|
|
6
|
+
|
|
7
|
+
import pymoo.gradient.toolbox as anp
|
|
8
|
+
import numpy as np
|
|
9
|
+
from jax import vjp
|
|
10
|
+
from jax import vmap
|
|
11
|
+
from jax._src.api import _jacrev_unravel, _std_basis
|
|
12
|
+
from jax.tree_util import (tree_map)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def jax_elementwise_value_and_grad(f, x):
|
|
16
|
+
out, pullback = vjp(f, x)
|
|
17
|
+
u = _std_basis(out)
|
|
18
|
+
jac, = vmap(pullback, in_axes=0)(u)
|
|
19
|
+
|
|
20
|
+
grad = tree_map(partial(_jacrev_unravel, out), x, jac)
|
|
21
|
+
|
|
22
|
+
return out, grad
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def jax_vectorized_value_and_grad(f, x):
|
|
26
|
+
out, pullback = vjp(f, x)
|
|
27
|
+
|
|
28
|
+
ncols = sum([v.shape[1] for v in out.values()])
|
|
29
|
+
|
|
30
|
+
u = dict()
|
|
31
|
+
cols = dict()
|
|
32
|
+
cnt = 0
|
|
33
|
+
for k, v in out.items():
|
|
34
|
+
if k not in cols:
|
|
35
|
+
cols[k] = []
|
|
36
|
+
|
|
37
|
+
n, m = v.shape
|
|
38
|
+
a = np.zeros((ncols, n, m))
|
|
39
|
+
for i in range(m):
|
|
40
|
+
cols[k].append(cnt)
|
|
41
|
+
a[cnt, :, i] = 1.0
|
|
42
|
+
cnt += 1
|
|
43
|
+
|
|
44
|
+
u[k] = anp.array(a)
|
|
45
|
+
|
|
46
|
+
jac, = vmap(pullback, in_axes=0)(u)
|
|
47
|
+
jac = np.array(jac)
|
|
48
|
+
|
|
49
|
+
grad = {k: np.swapaxes(jac[I], 0, 1) for k, I in cols.items()}
|
|
50
|
+
|
|
51
|
+
return out, grad
|
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.indicator import Indicator
|
|
4
|
+
from pymoo.util.misc import vectorized_cdist, at_least_2d_array
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def euclidean_distance(a, b, norm=None):
|
|
8
|
+
return np.sqrt((((a - b) / norm) ** 2).sum(axis=1))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def modified_distance(z, a, norm=None):
|
|
12
|
+
d = a - z
|
|
13
|
+
d[d < 0] = 0
|
|
14
|
+
d = d / norm
|
|
15
|
+
return np.sqrt((d ** 2).sum(axis=1))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def derive_ideal_and_nadir_from_pf(pf, ideal=None, nadir=None):
|
|
19
|
+
|
|
20
|
+
# try to derive ideal and nadir if not already set and pf provided
|
|
21
|
+
if pf is not None:
|
|
22
|
+
if ideal is None:
|
|
23
|
+
ideal = np.min(pf, axis=0)
|
|
24
|
+
if nadir is None:
|
|
25
|
+
nadir = np.max(pf, axis=0)
|
|
26
|
+
|
|
27
|
+
return ideal, nadir
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DistanceIndicator(Indicator):
|
|
31
|
+
|
|
32
|
+
def __init__(self, pf, dist_func, axis, zero_to_one=False, ideal=None, nadir=None, norm_by_dist=False, **kwargs):
|
|
33
|
+
|
|
34
|
+
# the pareto front if necessary to calculate the indicator
|
|
35
|
+
pf = at_least_2d_array(pf, extend_as="row")
|
|
36
|
+
ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir)
|
|
37
|
+
|
|
38
|
+
super().__init__(zero_to_one=zero_to_one, ideal=ideal, nadir=nadir, **kwargs)
|
|
39
|
+
self.dist_func = dist_func
|
|
40
|
+
self.axis = axis
|
|
41
|
+
self.norm_by_dist = norm_by_dist
|
|
42
|
+
self.pf = self.normalization.forward(pf)
|
|
43
|
+
|
|
44
|
+
def _do(self, F):
|
|
45
|
+
|
|
46
|
+
# a factor to normalize the distances by (1.0 disables that by default)
|
|
47
|
+
norm = 1.0
|
|
48
|
+
|
|
49
|
+
# if zero_to_one is disabled this can be used to normalize the distance calculation itself
|
|
50
|
+
if self.norm_by_dist:
|
|
51
|
+
assert self.ideal is not None and self.nadir is not None, "If norm_by_dist is enabled ideal and nadir must be set!"
|
|
52
|
+
norm = self.nadir - self.ideal
|
|
53
|
+
|
|
54
|
+
D = vectorized_cdist(self.pf, F, func_dist=self.dist_func, norm=norm)
|
|
55
|
+
return np.mean(np.min(D, axis=self.axis))
|
pymoo/indicators/gd.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.indicator import Indicator
|
|
4
|
+
from pymoo.indicators.distance_indicator import derive_ideal_and_nadir_from_pf
|
|
5
|
+
from pymoo.util.misc import at_least_2d_array
|
|
6
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
7
|
+
from pymoo.vendor.hv import HyperVolume as _HyperVolume
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Hypervolume(Indicator):
|
|
11
|
+
|
|
12
|
+
def __init__(self, ref_point=None, pf=None, nds=True, norm_ref_point=True, ideal=None, nadir=None, **kwargs):
|
|
13
|
+
|
|
14
|
+
pf = at_least_2d_array(pf, extend_as="row")
|
|
15
|
+
ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir)
|
|
16
|
+
|
|
17
|
+
super().__init__(ideal=ideal, nadir=nadir, **kwargs)
|
|
18
|
+
# self.normalization = ZeroToOneNormalization(ideal, nadir)
|
|
19
|
+
|
|
20
|
+
# whether the input should be checked for domination or not
|
|
21
|
+
self.nds = nds
|
|
22
|
+
|
|
23
|
+
# the reference point that shall be used - either derived from pf or provided
|
|
24
|
+
ref_point = ref_point
|
|
25
|
+
if ref_point is None:
|
|
26
|
+
if pf is not None:
|
|
27
|
+
ref_point = pf.max(axis=0)
|
|
28
|
+
|
|
29
|
+
# we also have to normalize the reference point to have the same scales
|
|
30
|
+
if norm_ref_point:
|
|
31
|
+
ref_point = self.normalization.forward(ref_point)
|
|
32
|
+
|
|
33
|
+
self.ref_point = ref_point
|
|
34
|
+
assert self.ref_point is not None, "For Hypervolume a reference point needs to be provided!"
|
|
35
|
+
|
|
36
|
+
def _do(self, F):
|
|
37
|
+
if self.nds:
|
|
38
|
+
non_dom = NonDominatedSorting().do(F, only_non_dominated_front=True)
|
|
39
|
+
F = np.copy(F[non_dom, :])
|
|
40
|
+
|
|
41
|
+
# calculate the hypervolume using a vendor library
|
|
42
|
+
hv = _HyperVolume(self.ref_point)
|
|
43
|
+
val = hv.compute(F)
|
|
44
|
+
return val
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class HV(Hypervolume):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def hvc_looped(ref_point, F, func):
|
|
52
|
+
hv = func(ref_point, F)
|
|
53
|
+
|
|
54
|
+
hvc = []
|
|
55
|
+
|
|
56
|
+
for k in range(len(F)):
|
|
57
|
+
v = np.full(len(F), True)
|
|
58
|
+
v[k] = False
|
|
59
|
+
_hv = func(ref_point, F[v])
|
|
60
|
+
hvc.append(hv - _hv)
|
|
61
|
+
|
|
62
|
+
hvc = np.array(hvc)
|
|
63
|
+
return hvc
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.indicators.hv import hvc_looped
|
|
4
|
+
from pymoo.util.function_loader import load_function
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def hv_exact(ref_point, F):
|
|
8
|
+
func = load_function("hv")
|
|
9
|
+
hv = func(ref_point, F)
|
|
10
|
+
return hv
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def hvc_exact_loopwise(ref_point, F):
|
|
14
|
+
return hvc_looped(ref_point, F, hv_exact)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DynamicHypervolume:
|
|
18
|
+
|
|
19
|
+
def __init__(self, ref_point, F=None, func_hv=None, func_hvc=None) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.ref_point = ref_point
|
|
22
|
+
|
|
23
|
+
self.func_hv = func_hv
|
|
24
|
+
self.func_hvc = func_hvc
|
|
25
|
+
|
|
26
|
+
self.n_dim = len(ref_point)
|
|
27
|
+
self.F = np.zeros((0, self.n_dim))
|
|
28
|
+
|
|
29
|
+
self.hv = 0.0
|
|
30
|
+
self.hvc = np.zeros(0)
|
|
31
|
+
|
|
32
|
+
if F is not None:
|
|
33
|
+
self.add(F)
|
|
34
|
+
|
|
35
|
+
def add(self, F):
|
|
36
|
+
assert len(F.shape) == 2, "The points to add must be a two-dimensional array."
|
|
37
|
+
assert F.shape[1] == self.n_dim, "The dimensions of the ref_point and points to add must be equal"
|
|
38
|
+
self.F = np.row_stack([self.F, F])
|
|
39
|
+
self.hv, self.hvc = self.calc()
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
def delete(self, k):
|
|
43
|
+
assert k < len(self.F)
|
|
44
|
+
self.F = np.delete(self.F, k, axis=0)
|
|
45
|
+
self.hv, self.hvc = self.calc()
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def calc(self):
|
|
49
|
+
return self._calc(self.ref_point, self.F)
|
|
50
|
+
|
|
51
|
+
# if len(self.F) == 0:
|
|
52
|
+
# return 0.0, np.zeros(0)
|
|
53
|
+
# else:
|
|
54
|
+
# return self._calc(self.ref_point, self.F)
|
|
55
|
+
|
|
56
|
+
def _calc(self, ref_point, F):
|
|
57
|
+
hv = None
|
|
58
|
+
if self.func_hv is not None:
|
|
59
|
+
hv = self.func_hv(ref_point, F)
|
|
60
|
+
|
|
61
|
+
hvc = None
|
|
62
|
+
if self.func_hvc is not None:
|
|
63
|
+
hvc = self.func_hvc(ref_point, F)
|
|
64
|
+
|
|
65
|
+
return hv, hvc
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ExactHypervolume(DynamicHypervolume):
|
|
69
|
+
|
|
70
|
+
def __init__(self, ref_point, func_hv=hv_exact, func_hvc=hvc_exact_loopwise, **kwargs) -> None:
|
|
71
|
+
super().__init__(ref_point, func_hv=func_hv, func_hvc=func_hvc, **kwargs)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.indicators.hv.exact import ExactHypervolume
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def hvc_2d_slow(ref_point, F):
|
|
7
|
+
n = len(F)
|
|
8
|
+
|
|
9
|
+
I = np.lexsort((-F[:, 1], F[:, 0]))
|
|
10
|
+
|
|
11
|
+
V = np.row_stack([ref_point, F[I], ref_point])
|
|
12
|
+
|
|
13
|
+
hvi = np.zeros(n)
|
|
14
|
+
|
|
15
|
+
for k in range(1, n + 1):
|
|
16
|
+
height = V[k - 1, 1] - V[k, 1]
|
|
17
|
+
width = V[k + 1, 0] - V[k, 0]
|
|
18
|
+
|
|
19
|
+
hvi[I[k - 1]] = width * height
|
|
20
|
+
|
|
21
|
+
return np.array(hvi)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def hvc_2d_fast(ref_point, F_sorted, left=None, right=None):
|
|
25
|
+
if left is None:
|
|
26
|
+
left = [F_sorted[0, 0], ref_point[1]]
|
|
27
|
+
|
|
28
|
+
if right is None:
|
|
29
|
+
right = [ref_point[0], F_sorted[-1, 1]]
|
|
30
|
+
|
|
31
|
+
V = np.row_stack([left, F_sorted, right])
|
|
32
|
+
height = (V[:-1, 1] - V[1:, 1])[:-1]
|
|
33
|
+
width = (V[1:, 0] - V[:-1, 0])[1:]
|
|
34
|
+
|
|
35
|
+
hvc = height * width
|
|
36
|
+
return hvc
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def hv_2d_fast(ref_point, F_sorted):
|
|
40
|
+
V = np.row_stack([ref_point, F_sorted])
|
|
41
|
+
height = (V[:-1, 1] - V[1:, 1])
|
|
42
|
+
width = ref_point[0] - V[1:, 0]
|
|
43
|
+
return (height * width).sum()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ExactHypervolume2D(ExactHypervolume):
|
|
47
|
+
|
|
48
|
+
def __init__(self, ref_point, **kwargs) -> None:
|
|
49
|
+
assert len(ref_point) == 2, "This hypervolume calculation only works in 2 dimensions."
|
|
50
|
+
super().__init__(ref_point, func_hv=hv_2d_fast, func_hvc=hvc_2d_fast, **kwargs)
|
|
51
|
+
self.S = None
|
|
52
|
+
self.I = None
|
|
53
|
+
|
|
54
|
+
def _calc(self, ref_point, F):
|
|
55
|
+
if len(F) == 0:
|
|
56
|
+
self.I, self.S = [], []
|
|
57
|
+
return 0.0, np.zeros(0)
|
|
58
|
+
|
|
59
|
+
F = np.minimum(self.ref_point, self.F)
|
|
60
|
+
I = np.lexsort((-F[:, 1], F[:, 0]))
|
|
61
|
+
S = np.argsort(I)
|
|
62
|
+
|
|
63
|
+
hv, hvc = super()._calc(ref_point, F[I])
|
|
64
|
+
hvc = hvc[S]
|
|
65
|
+
self.I, self.S = I, S
|
|
66
|
+
|
|
67
|
+
return hv, hvc
|
|
68
|
+
|
|
69
|
+
# this very efficiently just recomputes hvc of the points necessary
|
|
70
|
+
# however has not shown to be much faster because of reindexing
|
|
71
|
+
# def delete(self, k):
|
|
72
|
+
# assert k < len(self.F)
|
|
73
|
+
#
|
|
74
|
+
# F, I, S, hv, hvc = self.F, self.I, self.S, self.hv, self.hvc
|
|
75
|
+
#
|
|
76
|
+
# hv -= hvc[k]
|
|
77
|
+
#
|
|
78
|
+
# i = S[k]
|
|
79
|
+
#
|
|
80
|
+
# S = np.delete(S, k, axis=0)
|
|
81
|
+
# S[S > i] -= 1
|
|
82
|
+
#
|
|
83
|
+
# I = np.delete(I, i, axis=0)
|
|
84
|
+
# I[I > k] -= 1
|
|
85
|
+
#
|
|
86
|
+
# F = np.delete(F, k, axis=0)
|
|
87
|
+
# hvc = np.delete(self.hvc, k, axis=0)
|
|
88
|
+
#
|
|
89
|
+
# v = [I[i] if 0 <= i < len(I) else None for i in np.arange(i-2, i+2)]
|
|
90
|
+
# left, middle, right = v[0], v[1:-1], v[-1]
|
|
91
|
+
#
|
|
92
|
+
# middle = [e for e in middle if e is not None]
|
|
93
|
+
#
|
|
94
|
+
# if len(middle) > 0:
|
|
95
|
+
#
|
|
96
|
+
# hvc[middle] = hvc_2d_fast(self.ref_point,
|
|
97
|
+
# F[middle],
|
|
98
|
+
# left=F[left] if left is not None else None,
|
|
99
|
+
# right=F[right] if right is not None else None,
|
|
100
|
+
# )
|
|
101
|
+
#
|
|
102
|
+
# self.F, self.I, self.S, self.hv, self.hvc = F, I, S, hv, hvc
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.indicators.hv.exact import DynamicHypervolume
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def alpha(N, k):
|
|
7
|
+
alpha = np.zeros(N+1)
|
|
8
|
+
|
|
9
|
+
for i in range(1, N + 1):
|
|
10
|
+
alpha[i] = np.prod([(k - j) / (N - j) for j in range(1, i)]) / i
|
|
11
|
+
|
|
12
|
+
return alpha
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def hv_monte_carlo(dom, V, n_dom=None):
|
|
16
|
+
N, n_samples = dom.shape
|
|
17
|
+
if n_dom is None:
|
|
18
|
+
n_dom = dom.sum(axis=0)
|
|
19
|
+
|
|
20
|
+
a = alpha(N, N)
|
|
21
|
+
hv = sum([a[n_dom[dom[i]]].sum() for i in range(N)]) / n_samples * V
|
|
22
|
+
return hv
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def hvc_monte_carlo(dom, V, n_dom=None, k=1):
|
|
26
|
+
N, n_samples = dom.shape
|
|
27
|
+
if n_dom is None:
|
|
28
|
+
n_dom = dom.sum(axis=0)
|
|
29
|
+
|
|
30
|
+
a = alpha(N, k)
|
|
31
|
+
hvc = np.array([(a[n_dom[dom[i]]].sum() / n_samples * V).sum() for i in range(N)])
|
|
32
|
+
return hvc
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ApproximateMonteCarloHypervolume(DynamicHypervolume):
|
|
36
|
+
|
|
37
|
+
def __init__(self, ref_point, n_samples=10000, n_exclusive=1, **kwargs) -> None:
|
|
38
|
+
self.n_samples = n_samples
|
|
39
|
+
self.n_exclusive = n_exclusive
|
|
40
|
+
|
|
41
|
+
self.V = None
|
|
42
|
+
self.dom = None
|
|
43
|
+
|
|
44
|
+
super().__init__(ref_point, **kwargs)
|
|
45
|
+
|
|
46
|
+
def _calc(self, ref_point, F):
|
|
47
|
+
(N, M) = F.shape
|
|
48
|
+
|
|
49
|
+
ideal = F.min(axis=0)
|
|
50
|
+
V = np.prod(ref_point - ideal)
|
|
51
|
+
|
|
52
|
+
S = np.random.uniform(low=ideal, high=ref_point, size=(self.n_samples, M))
|
|
53
|
+
|
|
54
|
+
dom = np.array([np.all(F[i] <= S, axis=1) for i in range(N)])
|
|
55
|
+
|
|
56
|
+
n_dom = dom.sum(axis=0)
|
|
57
|
+
hv = hv_monte_carlo(dom, V, n_dom=n_dom)
|
|
58
|
+
hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
|
|
59
|
+
|
|
60
|
+
self.V = V
|
|
61
|
+
self.dom = dom
|
|
62
|
+
|
|
63
|
+
return hv, hvc
|
|
64
|
+
|
|
65
|
+
def delete(self, k):
|
|
66
|
+
self.F = np.delete(self.F, k, axis=0)
|
|
67
|
+
self.dom = np.delete(self.dom, k, axis=0)
|
|
68
|
+
|
|
69
|
+
self.hv -= self.hvc[k]
|
|
70
|
+
|
|
71
|
+
V, dom = self.V, self.dom
|
|
72
|
+
n_dom = dom.sum(axis=0)
|
|
73
|
+
self.hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
|
|
74
|
+
|
pymoo/indicators/igd.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.individual import calc_cv
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class KKTPM:
|
|
7
|
+
|
|
8
|
+
def calc(self, X, problem, ideal=None, utopian_eps=1e-4, rho=1e-3):
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
Returns the Karush-Kuhn-Tucker Approximate Measure.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
X : np.array
|
|
16
|
+
|
|
17
|
+
problem : pymoo.core.problem
|
|
18
|
+
ideal : np.array
|
|
19
|
+
The ideal point if not in the problem defined or intentionally overwritten.
|
|
20
|
+
utopian_eps : float
|
|
21
|
+
The epsilon used for decrease the ideal point to get the utopian point.
|
|
22
|
+
rho : float
|
|
23
|
+
Since augmented achievement scalarization function is used the F for all other weights
|
|
24
|
+
- here rho - needs to be defined.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# the final result to be returned
|
|
32
|
+
kktpm = np.full((X.shape[0], 1), np.inf)
|
|
33
|
+
fval = np.full((X.shape[0], 1), np.inf)
|
|
34
|
+
|
|
35
|
+
# set the ideal point for normalization
|
|
36
|
+
z = ideal
|
|
37
|
+
|
|
38
|
+
# if not provided take the one defined in the problem
|
|
39
|
+
if z is None:
|
|
40
|
+
z = problem.ideal_point()
|
|
41
|
+
z -= utopian_eps
|
|
42
|
+
|
|
43
|
+
# for convenience get the counts directly
|
|
44
|
+
n_solutions, n_var, n_obj, n_ieq_constr = X.shape[0], problem.n_var, problem.n_obj, problem.n_ieq_constr
|
|
45
|
+
|
|
46
|
+
F, G, dF, dG = problem.evaluate(X, return_values_of=["F", "G", "dF", "dG"])
|
|
47
|
+
CV = calc_cv(G=G)
|
|
48
|
+
|
|
49
|
+
# loop through each solution to be considered
|
|
50
|
+
for i in range(n_solutions):
|
|
51
|
+
|
|
52
|
+
# get the corresponding values for this solution
|
|
53
|
+
x, f, cv, df = X[i, :], F[i, :], CV[i], dF[i, :].swapaxes(1, 0)
|
|
54
|
+
if n_ieq_constr > 0:
|
|
55
|
+
g, dg = G[i, :], dG[i].T
|
|
56
|
+
|
|
57
|
+
# if the solution that is provided is infeasible
|
|
58
|
+
if cv > 0:
|
|
59
|
+
_kktpm = 1 + cv
|
|
60
|
+
_fval = None
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
|
|
64
|
+
w = np.sqrt(np.sum(np.power(f - z, 2))) / (f - z)
|
|
65
|
+
a_m = (df * w + (rho * np.sum(df * w, axis=1))[:, None]).T
|
|
66
|
+
|
|
67
|
+
A = np.ones((problem.n_obj, problem.n_obj)) + a_m @ a_m.T
|
|
68
|
+
b = np.ones(problem.n_obj)
|
|
69
|
+
|
|
70
|
+
if n_ieq_constr > 0:
|
|
71
|
+
# a_j is just the transpose of the differential of constraints
|
|
72
|
+
a_j = dg.T
|
|
73
|
+
|
|
74
|
+
# part of the matrix for additional constraints
|
|
75
|
+
gsq = np.zeros((n_ieq_constr, n_ieq_constr))
|
|
76
|
+
np.fill_diagonal(gsq, g * g)
|
|
77
|
+
|
|
78
|
+
# now add the constraints to the optimization problem
|
|
79
|
+
A = np.vstack([np.hstack([A, a_m @ a_j.T]), np.hstack([a_j @ a_m.T, a_j @ a_j.T + gsq])])
|
|
80
|
+
b = np.hstack([b, np.zeros(n_ieq_constr)])
|
|
81
|
+
|
|
82
|
+
method = "qr"
|
|
83
|
+
u = solve(A, b, method=method)
|
|
84
|
+
|
|
85
|
+
# until all the lagrange multiplier are positive
|
|
86
|
+
while np.any(u < 0):
|
|
87
|
+
|
|
88
|
+
# go through one by one
|
|
89
|
+
for j in range(len(u)):
|
|
90
|
+
|
|
91
|
+
# if a lagrange multiplier is negative - we need to fix it
|
|
92
|
+
if u[j] < 0:
|
|
93
|
+
# modify the optimization problem
|
|
94
|
+
A[j, :], A[:, j], A[j, j] = 0, 0, 1
|
|
95
|
+
b[j] = 0
|
|
96
|
+
|
|
97
|
+
# resolve the problem and redefine u. for sure all preview u[j] are positive now
|
|
98
|
+
u = solve(A, b, method=method)
|
|
99
|
+
|
|
100
|
+
# split up the lagrange multiplier for objective and not
|
|
101
|
+
u_m, u_j = u[:n_obj], u[n_obj:]
|
|
102
|
+
|
|
103
|
+
if n_ieq_constr > 0:
|
|
104
|
+
_kktpm = (1 - np.sum(u_m)) ** 2 + np.sum((np.vstack([a_m, a_j]).T @ u) ** 2)
|
|
105
|
+
_fval = _kktpm + np.sum((u_j * g.T) ** 2)
|
|
106
|
+
else:
|
|
107
|
+
_kktpm = (1 - np.sum(u_m)) ** 2 + np.sum((a_m.T @ u) ** 2)
|
|
108
|
+
_fval = _kktpm
|
|
109
|
+
|
|
110
|
+
ujgj = -g @ u_j
|
|
111
|
+
if np.sum(u_m) + ujgj * (1 + ujgj) > 1:
|
|
112
|
+
adjusted_kktpm = - (u_j @ g.T)
|
|
113
|
+
projected_kktpm = (_kktpm * g @ g.T - g @ u_j) / (1 + g @ g.T)
|
|
114
|
+
_kktpm = (_kktpm + adjusted_kktpm + projected_kktpm) / 3
|
|
115
|
+
|
|
116
|
+
# assign to the values to be returned
|
|
117
|
+
kktpm[i] = _kktpm
|
|
118
|
+
fval[i] = _fval
|
|
119
|
+
|
|
120
|
+
return kktpm[:, 0]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def solve(A, b, method="elim"):
|
|
124
|
+
if method == "elim":
|
|
125
|
+
return np.linalg.solve(A, b)
|
|
126
|
+
|
|
127
|
+
elif method == "qr":
|
|
128
|
+
Q, R = np.linalg.qr(A)
|
|
129
|
+
y = np.dot(Q.T, b)
|
|
130
|
+
return np.linalg.solve(R, y)
|
|
131
|
+
|
|
132
|
+
elif method == "svd":
|
|
133
|
+
U, s, V = np.linalg.svd(A) # SVD decomposition of A
|
|
134
|
+
A_inv = np.dot(np.dot(V.T, np.linalg.inv(np.diag(s))), U.T)
|
|
135
|
+
return A_inv @ b
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == '__main__':
|
|
139
|
+
from pymoo.problems import get_problem
|
|
140
|
+
from pymoo.gradient.automatic import AutomaticDifferentiation
|
|
141
|
+
|
|
142
|
+
from pymoo.constraints.from_bounds import ConstraintsFromBounds
|
|
143
|
+
problem = ConstraintsFromBounds(AutomaticDifferentiation(get_problem("zdt2", n_var=30)))
|
|
144
|
+
|
|
145
|
+
# X = (0.5 * np.ones(10))[None, :]
|
|
146
|
+
X = np.array(
|
|
147
|
+
[0.394876, 0.963263, 0.173956, 0.126330, 0.135079, 0.505662, 0.021525, 0.947970, 0.827115, 0.015019, 0.176196,
|
|
148
|
+
0.332064, 0.130997, 0.809491, 0.344737, 0.940107, 0.582014, 0.878832, 0.844734, 0.905392, 0.459880, 0.546347,
|
|
149
|
+
0.798604, 0.285719, 0.490254, 0.599110, 0.015533, 0.593481, 0.433676, 0.807361])
|
|
150
|
+
|
|
151
|
+
print(KKTPM().calc(X[None, :], problem))
|
pymoo/indicators/migd.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.callback import Callback
|
|
4
|
+
from pymoo.indicators.igd import IGD
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MIGD(Callback):
|
|
8
|
+
|
|
9
|
+
def __init__(self, reevaluate=True) -> None:
|
|
10
|
+
"""
|
|
11
|
+
Mean Inverted Generational Distance (MIGD)
|
|
12
|
+
|
|
13
|
+
For dynamic optimization problems the performance metric needs to involve the IGD value over time as the
|
|
14
|
+
problem is changing. Thus, the performance needs to be evaluated in each iteration for which
|
|
15
|
+
defining a callback is ideal.
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
super().__init__()
|
|
20
|
+
|
|
21
|
+
# whether the MIGD should be based on reevaluated solutions
|
|
22
|
+
self.reevaluate = reevaluate
|
|
23
|
+
|
|
24
|
+
# the list where each of the recordings are stored: timesteps and igd
|
|
25
|
+
self.records = []
|
|
26
|
+
|
|
27
|
+
def update(self, algorithm, **kwargs):
|
|
28
|
+
|
|
29
|
+
# the problem to be solved
|
|
30
|
+
problem = algorithm.problem
|
|
31
|
+
assert problem.n_constr == 0, "The current implementation only works for unconstrained problems!"
|
|
32
|
+
|
|
33
|
+
# the current time
|
|
34
|
+
t = problem.time
|
|
35
|
+
|
|
36
|
+
# the current pareto-front of the problem (at the specific time step)
|
|
37
|
+
pf = problem.pareto_front()
|
|
38
|
+
|
|
39
|
+
# the current population of the algorithm
|
|
40
|
+
pop = algorithm.pop
|
|
41
|
+
|
|
42
|
+
# if the callback should reevaluate to match the current time step and avoid deprecated values
|
|
43
|
+
if self.reevaluate:
|
|
44
|
+
X = pop.get("X")
|
|
45
|
+
F = problem.evaluate(X, return_values_of=["F"])
|
|
46
|
+
else:
|
|
47
|
+
F = pop.get("F")
|
|
48
|
+
|
|
49
|
+
# calculate the current igd values
|
|
50
|
+
igd = IGD(pf).do(F)
|
|
51
|
+
|
|
52
|
+
self.records.append((t, igd))
|
|
53
|
+
|
|
54
|
+
def value(self):
|
|
55
|
+
return np.array([igd for _, igd in self.records]).mean()
|