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,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Standard Python implementation of stochastic ranking.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from pymoo.util import default_random_state
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def swap(M, a, b):
|
|
10
|
+
"""Swap two elements in array M."""
|
|
11
|
+
M[a], M[b] = M[b], M[a]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@default_random_state
|
|
15
|
+
def stochastic_ranking(f, phi, pr, I=None, random_state=None):
|
|
16
|
+
"""Stochastic ranking algorithm."""
|
|
17
|
+
_lambda = len(f)
|
|
18
|
+
|
|
19
|
+
if I is None:
|
|
20
|
+
I = np.arange(_lambda)
|
|
21
|
+
|
|
22
|
+
for i in range(_lambda):
|
|
23
|
+
|
|
24
|
+
at_least_one_swap = False
|
|
25
|
+
|
|
26
|
+
for j in range(_lambda - 1):
|
|
27
|
+
|
|
28
|
+
u = random_state.random()
|
|
29
|
+
|
|
30
|
+
if u < pr or (phi[I[j]] == 0 and phi[I[j + 1]] == 0):
|
|
31
|
+
if f[I[j]] > f[I[j + 1]]:
|
|
32
|
+
swap(I, j, j + 1)
|
|
33
|
+
at_least_one_swap = True
|
|
34
|
+
else:
|
|
35
|
+
if phi[I[j]] > phi[I[j + 1]]:
|
|
36
|
+
swap(I, j, j + 1)
|
|
37
|
+
at_least_one_swap = True
|
|
38
|
+
|
|
39
|
+
if not at_least_one_swap:
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
return I
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
BACKENDS = {
|
|
5
|
+
"numpy": "numpy",
|
|
6
|
+
"autograd": "autograd.numpy",
|
|
7
|
+
"jax": "jax.numpy"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# Current active backend
|
|
11
|
+
active_backend = "numpy"
|
|
12
|
+
|
|
13
|
+
def activate(name):
|
|
14
|
+
global active_backend
|
|
15
|
+
if name not in BACKENDS:
|
|
16
|
+
raise ValueError(f"Unknown backend: {name}. Choose from: {list(BACKENDS.keys())}")
|
|
17
|
+
active_backend = name
|
|
18
|
+
# No need to delete module - LazyBackend handles dynamic switching
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def deactivate():
|
|
22
|
+
activate("numpy")
|
|
23
|
+
|
|
24
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.meta import Meta
|
|
4
|
+
from pymoo.core.problem import Problem, ElementwiseEvaluationFunction
|
|
5
|
+
from pymoo.gradient import activate, deactivate
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ElementwiseEvaluationFunctionWithGradient(ElementwiseEvaluationFunction):
|
|
9
|
+
|
|
10
|
+
def __init__(self, problem, backend='autograd', args=(), kwargs={}):
|
|
11
|
+
super().__init__(problem, args, kwargs)
|
|
12
|
+
self.backend = backend
|
|
13
|
+
|
|
14
|
+
def __call__(self, x):
|
|
15
|
+
f = super().__call__
|
|
16
|
+
|
|
17
|
+
activate(self.backend)
|
|
18
|
+
if self.backend == "jax":
|
|
19
|
+
from pymoo.gradient.grad_jax import jax_elementwise_value_and_grad
|
|
20
|
+
out, grad = jax_elementwise_value_and_grad(f, x)
|
|
21
|
+
elif self.backend == "autograd":
|
|
22
|
+
from pymoo.gradient.grad_autograd import autograd_elementwise_value_and_grad
|
|
23
|
+
out, grad = autograd_elementwise_value_and_grad(f, x)
|
|
24
|
+
else:
|
|
25
|
+
raise Exception("Unknown backend %s" % self.backend)
|
|
26
|
+
deactivate()
|
|
27
|
+
|
|
28
|
+
for k, v in grad.items():
|
|
29
|
+
out["d" + k] = np.array(v)
|
|
30
|
+
|
|
31
|
+
return out
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ElementwiseAutomaticDifferentiation(Meta, Problem):
|
|
35
|
+
|
|
36
|
+
def __init__(self, problem, backend='autograd', copy=True):
|
|
37
|
+
if not problem.elementwise:
|
|
38
|
+
raise Exception("Elementwise automatic differentiation can only be applied to elementwise problems.")
|
|
39
|
+
|
|
40
|
+
super().__init__(problem, copy)
|
|
41
|
+
self.backend = backend
|
|
42
|
+
|
|
43
|
+
# Set the elementwise_func to the class itself - it will handle the signature
|
|
44
|
+
self.elementwise_func = self._create_elementwise_func
|
|
45
|
+
|
|
46
|
+
def _create_elementwise_func(self, problem, args, kwargs):
|
|
47
|
+
"""Create an elementwise function that matches the expected signature"""
|
|
48
|
+
return ElementwiseEvaluationFunctionWithGradient(self.__object__, self.backend, args, kwargs)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AutomaticDifferentiation(Meta, Problem):
|
|
52
|
+
|
|
53
|
+
def __init__(self, object, backend='autograd', **kwargs):
|
|
54
|
+
super().__init__(object, **kwargs)
|
|
55
|
+
self.backend = backend
|
|
56
|
+
|
|
57
|
+
def do(self, x, return_values_of, *args, **kwargs):
|
|
58
|
+
|
|
59
|
+
vals_not_grad = [v for v in return_values_of if not v.startswith("d")]
|
|
60
|
+
|
|
61
|
+
class F:
|
|
62
|
+
|
|
63
|
+
def __init__(self, object):
|
|
64
|
+
self.__object__ = object
|
|
65
|
+
|
|
66
|
+
def __call__(self, xp):
|
|
67
|
+
return self.__object__.do(xp, vals_not_grad, *args, **kwargs)
|
|
68
|
+
|
|
69
|
+
f = F(self.__object__)
|
|
70
|
+
|
|
71
|
+
activate(self.backend)
|
|
72
|
+
if self.backend == "jax":
|
|
73
|
+
from pymoo.gradient.grad_jax import jax_vectorized_value_and_grad
|
|
74
|
+
out, grad = jax_vectorized_value_and_grad(f, x)
|
|
75
|
+
elif self.backend == "autograd":
|
|
76
|
+
from pymoo.gradient.grad_autograd import autograd_vectorized_value_and_grad
|
|
77
|
+
out, grad = autograd_vectorized_value_and_grad(f, x)
|
|
78
|
+
else:
|
|
79
|
+
raise Exception("Unknown backend %s" % self.backend)
|
|
80
|
+
deactivate()
|
|
81
|
+
|
|
82
|
+
for k, v in grad.items():
|
|
83
|
+
out["d" + k] = v
|
|
84
|
+
|
|
85
|
+
return out
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import autograd.numpy as anp
|
|
6
|
+
from autograd.core import VJPNode, backward_pass
|
|
7
|
+
from autograd.tracer import new_box, isbox
|
|
8
|
+
except:
|
|
9
|
+
print("autograd only supports numpy < 2.0.0 versions.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def value_and_grad(*args, **kwargs):
|
|
13
|
+
from autograd import value_and_grad as vag
|
|
14
|
+
return vag(*args, **kwargs)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def log(*args, **kwargs):
|
|
18
|
+
return anp.log(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def sqrt(*args, **kwargs):
|
|
22
|
+
return anp.sqrt(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def row_stack(*args, **kwargs):
|
|
26
|
+
return anp.row_stack(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def triu_indices(*args, **kwargs):
|
|
30
|
+
return anp.triu_indices(*args, **kwargs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def run_and_trace(f, x):
|
|
34
|
+
start_node = VJPNode.new_root()
|
|
35
|
+
|
|
36
|
+
start_box = new_box(x, 0, start_node)
|
|
37
|
+
out = f(start_box)
|
|
38
|
+
|
|
39
|
+
return out, start_box
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def autograd_elementwise_value_and_grad(f, x):
|
|
43
|
+
out, pullback = run_and_trace(f, x)
|
|
44
|
+
|
|
45
|
+
jac = dict()
|
|
46
|
+
for name in out:
|
|
47
|
+
val = out[name]
|
|
48
|
+
|
|
49
|
+
if val is not None:
|
|
50
|
+
|
|
51
|
+
if len(val.shape) == 0:
|
|
52
|
+
val = anp.array([val])
|
|
53
|
+
|
|
54
|
+
with warnings.catch_warnings():
|
|
55
|
+
warnings.simplefilter("ignore")
|
|
56
|
+
|
|
57
|
+
# the backward pass is done for each objective function once
|
|
58
|
+
grad = []
|
|
59
|
+
for j in range(len(val)):
|
|
60
|
+
b = np.zeros(val.shape)
|
|
61
|
+
b[j] = 1
|
|
62
|
+
|
|
63
|
+
n = new_box(b, 0, VJPNode.new_root())
|
|
64
|
+
_grad = backward_pass(n, val._node)
|
|
65
|
+
grad.append(_grad)
|
|
66
|
+
|
|
67
|
+
out[name] = np.array(val._value)
|
|
68
|
+
jac[name] = anp.stack(grad, axis=0)._value
|
|
69
|
+
|
|
70
|
+
return out, jac
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def autograd_vectorized_value_and_grad(f, x):
|
|
74
|
+
end, start = run_and_trace(f, x)
|
|
75
|
+
|
|
76
|
+
out, jac = dict(), dict()
|
|
77
|
+
|
|
78
|
+
end = {k: v for k, v in end.items() if v is not None}
|
|
79
|
+
|
|
80
|
+
for name, val in end.items():
|
|
81
|
+
|
|
82
|
+
v = val
|
|
83
|
+
if hasattr(v, "_value"):
|
|
84
|
+
v = np.array(v._value)
|
|
85
|
+
out[name] = v
|
|
86
|
+
|
|
87
|
+
# if the end_box is not a box - autograd can not track back
|
|
88
|
+
if not isbox(val):
|
|
89
|
+
n, m = val.shape
|
|
90
|
+
jac[name] = np.zeros((n, m, x.shape[1]))
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
|
|
94
|
+
# the backward pass is done for each objective function once
|
|
95
|
+
grad = []
|
|
96
|
+
for j in range(val.shape[1]):
|
|
97
|
+
b = anp.zeros(val.shape)
|
|
98
|
+
b[:, j] = 1
|
|
99
|
+
n = new_box(b, 0, VJPNode.new_root())
|
|
100
|
+
_grad = backward_pass(n, val._node)
|
|
101
|
+
grad.append(_grad)
|
|
102
|
+
|
|
103
|
+
jac[name] = anp.stack(grad, axis=1)._value
|
|
104
|
+
|
|
105
|
+
return out, jac
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.meta import Meta
|
|
4
|
+
from pymoo.core.problem import Problem
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calc_complex_gradient(problem, return_values_of, x, eps, *args, **kwargs):
|
|
8
|
+
xp = x + np.eye(len(x)) * complex(0, eps)
|
|
9
|
+
out = problem.do(xp, return_values_of, *args, **kwargs)
|
|
10
|
+
|
|
11
|
+
grad = {}
|
|
12
|
+
for name, value in out.items():
|
|
13
|
+
grad[name] = np.imag(value / eps).T
|
|
14
|
+
|
|
15
|
+
return grad
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ComplexNumberGradient(Meta, Problem):
|
|
19
|
+
|
|
20
|
+
def __init__(self, problem, eps=1e-8, **kwargs):
|
|
21
|
+
super().__init__(problem, **kwargs)
|
|
22
|
+
self.eps = eps
|
|
23
|
+
|
|
24
|
+
def do(self, X, return_values_of, *args, **kwargs):
|
|
25
|
+
out = self.__object__.do(X, return_values_of, *args, **kwargs)
|
|
26
|
+
|
|
27
|
+
vals_not_grad = [v for v in return_values_of if not v.startswith("d")]
|
|
28
|
+
|
|
29
|
+
for i, x in enumerate(X):
|
|
30
|
+
grad = calc_complex_gradient(self.__object__, vals_not_grad, x, self.eps, *args, **kwargs)
|
|
31
|
+
|
|
32
|
+
for name, value in grad.items():
|
|
33
|
+
out['d' + name][i] = value
|
|
34
|
+
|
|
35
|
+
return out
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
|
|
3
|
+
import jax
|
|
4
|
+
jax.config.update("jax_enable_x64", True)
|
|
5
|
+
from jax import vjp
|
|
6
|
+
from jax import vmap
|
|
7
|
+
from jax._src.api import _jacrev_unravel, _std_basis
|
|
8
|
+
from jax.tree_util import (tree_map)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
import pymoo.gradient.toolbox as anp
|
|
12
|
+
import numpy as np
|
|
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
|
pymoo/gradient/numpy.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Numpy gradient toolbox - acts as a fallback when no automatic differentiation library is used.
|
|
3
|
+
This module provides the same interface as autograd/jax but raises appropriate errors.
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
def value_and_grad(*args, **kwargs):
|
|
8
|
+
raise NotImplementedError("Numpy gradient toolbox does not support automatic differentiation. "
|
|
9
|
+
"Please use pymoo.gradient.activate('autograd.numpy') or "
|
|
10
|
+
"pymoo.gradient.activate('jax.numpy') for automatic differentiation.")
|
|
11
|
+
|
|
12
|
+
def log(*args, **kwargs):
|
|
13
|
+
return np.log(*args, **kwargs)
|
|
14
|
+
|
|
15
|
+
def sqrt(*args, **kwargs):
|
|
16
|
+
return np.sqrt(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
def row_stack(*args, **kwargs):
|
|
19
|
+
return np.vstack(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
def triu_indices(*args, **kwargs):
|
|
22
|
+
return np.triu_indices(*args, **kwargs)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import importlib
|
|
3
|
+
|
|
4
|
+
class LazyBackend:
|
|
5
|
+
"""Lazy backend that always uses the current active backend"""
|
|
6
|
+
|
|
7
|
+
def __getattr__(self, name):
|
|
8
|
+
from pymoo.gradient import active_backend, BACKENDS
|
|
9
|
+
backend_module = importlib.import_module(BACKENDS[active_backend])
|
|
10
|
+
return getattr(backend_module, name)
|
|
11
|
+
|
|
12
|
+
def __dir__(self):
|
|
13
|
+
"""Support for tab completion and introspection"""
|
|
14
|
+
from pymoo.gradient import active_backend, BACKENDS
|
|
15
|
+
backend_module = importlib.import_module(BACKENDS[active_backend])
|
|
16
|
+
return dir(backend_module)
|
|
17
|
+
|
|
18
|
+
# Replace this module with the lazy backend
|
|
19
|
+
sys.modules[__name__] = LazyBackend()
|
|
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,59 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.indicator import Indicator
|
|
4
|
+
from pymoo.functions import FunctionLoader, load_function
|
|
5
|
+
from pymoo.indicators.distance_indicator import derive_ideal_and_nadir_from_pf
|
|
6
|
+
from pymoo.util.misc import at_least_2d_array
|
|
7
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
8
|
+
from moocore import hypervolume as _hypervolume
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Hypervolume(Indicator):
|
|
12
|
+
|
|
13
|
+
def __init__(self, ref_point=None, pf=None, nds=True, norm_ref_point=True, ideal=None, nadir=None, **kwargs):
|
|
14
|
+
|
|
15
|
+
pf = at_least_2d_array(pf, extend_as="row")
|
|
16
|
+
ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir)
|
|
17
|
+
|
|
18
|
+
super().__init__(ideal=ideal, nadir=nadir, **kwargs)
|
|
19
|
+
# self.normalization = ZeroToOneNormalization(ideal, nadir)
|
|
20
|
+
|
|
21
|
+
# whether the input should be checked for domination or not (deprecated)
|
|
22
|
+
self.nds = nds
|
|
23
|
+
|
|
24
|
+
# the reference point that shall be used - either derived from pf or provided
|
|
25
|
+
ref_point = ref_point
|
|
26
|
+
if ref_point is None:
|
|
27
|
+
if pf is not None:
|
|
28
|
+
ref_point = pf.max(axis=0)
|
|
29
|
+
|
|
30
|
+
# we also have to normalize the reference point to have the same scales
|
|
31
|
+
if norm_ref_point:
|
|
32
|
+
ref_point = self.normalization.forward(ref_point)
|
|
33
|
+
|
|
34
|
+
self.ref_point = ref_point
|
|
35
|
+
assert self.ref_point is not None, "For Hypervolume a reference point needs to be provided!"
|
|
36
|
+
|
|
37
|
+
def _do(self, F):
|
|
38
|
+
# calculate the hypervolume using moocore
|
|
39
|
+
val = _hypervolume(F, ref = self.ref_point)
|
|
40
|
+
return val
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HV(Hypervolume):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def hvc_looped(ref_point, F, func):
|
|
48
|
+
hv = func(ref_point, F)
|
|
49
|
+
|
|
50
|
+
hvc = []
|
|
51
|
+
|
|
52
|
+
for k in range(len(F)):
|
|
53
|
+
v = np.full(len(F), True)
|
|
54
|
+
v[k] = False
|
|
55
|
+
_hv = func(ref_point, F[v])
|
|
56
|
+
hvc.append(hv - _hv)
|
|
57
|
+
|
|
58
|
+
hvc = np.array(hvc)
|
|
59
|
+
return hvc
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.indicators.hv.exact import DynamicHypervolume
|
|
4
|
+
from pymoo.util import default_random_state
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# from moocore import hv_approx, hv_contributions
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def alpha(N, k):
|
|
11
|
+
alpha = np.zeros(N+1)
|
|
12
|
+
|
|
13
|
+
for i in range(1, N + 1):
|
|
14
|
+
alpha[i] = np.prod([(k - j) / (N - j) for j in range(1, i)]) / i
|
|
15
|
+
|
|
16
|
+
return alpha
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def hv_monte_carlo(dom, V, n_dom=None):
|
|
20
|
+
N, n_samples = dom.shape
|
|
21
|
+
if n_dom is None:
|
|
22
|
+
n_dom = dom.sum(axis=0)
|
|
23
|
+
|
|
24
|
+
a = alpha(N, N)
|
|
25
|
+
hv = sum([a[n_dom[dom[i]]].sum() for i in range(N)]) / n_samples * V
|
|
26
|
+
return hv
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def hvc_monte_carlo(dom, V, n_dom=None, k=1):
|
|
30
|
+
N, n_samples = dom.shape
|
|
31
|
+
if n_dom is None:
|
|
32
|
+
n_dom = dom.sum(axis=0)
|
|
33
|
+
|
|
34
|
+
a = alpha(N, k)
|
|
35
|
+
hvc = np.array([(a[n_dom[dom[i]]].sum() / n_samples * V).sum() for i in range(N)])
|
|
36
|
+
return hvc
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ApproximateHypervolume(DynamicHypervolume):
|
|
40
|
+
|
|
41
|
+
@default_random_state(seed=1)
|
|
42
|
+
def __init__(self, ref_point, n_samples=10000, n_exclusive=1, random_state=None, **kwargs) -> None:
|
|
43
|
+
self.n_samples = n_samples
|
|
44
|
+
self.n_exclusive = n_exclusive
|
|
45
|
+
|
|
46
|
+
self.V = None
|
|
47
|
+
self.dom = None
|
|
48
|
+
|
|
49
|
+
self.random_state = random_state
|
|
50
|
+
|
|
51
|
+
super().__init__(ref_point, **kwargs)
|
|
52
|
+
|
|
53
|
+
def _calc(self, ref_point, F):
|
|
54
|
+
(N, M) = F.shape
|
|
55
|
+
|
|
56
|
+
ideal = F.min(axis=0)
|
|
57
|
+
V = np.prod(ref_point - ideal)
|
|
58
|
+
|
|
59
|
+
S = self.random_state.uniform(low=ideal, high=ref_point, size=(self.n_samples, M))
|
|
60
|
+
|
|
61
|
+
dom = np.array([np.all(F[i] <= S, axis=1) for i in range(N)])
|
|
62
|
+
|
|
63
|
+
n_dom = dom.sum(axis=0)
|
|
64
|
+
hv = hv_monte_carlo(dom, V, n_dom=n_dom)
|
|
65
|
+
hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
|
|
66
|
+
|
|
67
|
+
self.V = V
|
|
68
|
+
self.dom = dom
|
|
69
|
+
|
|
70
|
+
return hv, hvc
|
|
71
|
+
|
|
72
|
+
# MOOCORE VERSION (commented out for comparison)
|
|
73
|
+
# if len(F) == 0:
|
|
74
|
+
# return 0.0, np.zeros(0)
|
|
75
|
+
#
|
|
76
|
+
# # Use moocore for approximate hypervolume calculation
|
|
77
|
+
# hv = hv_approx(F, ref=ref_point, nsamples=self.n_samples, seed=self.random_state.randint(0, 2**32-1))
|
|
78
|
+
#
|
|
79
|
+
# # Use moocore for exact hypervolume contributions (no approximate version available)
|
|
80
|
+
# hvc = hv_contributions(F, ref=ref_point)
|
|
81
|
+
#
|
|
82
|
+
# return hv, hvc
|
|
83
|
+
|
|
84
|
+
def delete(self, k):
|
|
85
|
+
self.F = np.delete(self.F, k, axis=0)
|
|
86
|
+
self.dom = np.delete(self.dom, k, axis=0)
|
|
87
|
+
|
|
88
|
+
self.hv -= self.hvc[k]
|
|
89
|
+
|
|
90
|
+
V, dom = self.V, self.dom
|
|
91
|
+
n_dom = dom.sum(axis=0)
|
|
92
|
+
self.hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
|
|
93
|
+
|
|
94
|
+
# MOOCORE VERSION (commented out for comparison)
|
|
95
|
+
# # Handle empty array case
|
|
96
|
+
# if len(self.F) == 0:
|
|
97
|
+
# self.hv = 0.0
|
|
98
|
+
# self.hvc = np.zeros(0)
|
|
99
|
+
# else:
|
|
100
|
+
# # Use moocore for updated hypervolume and contributions
|
|
101
|
+
# self.hv = hv_approx(self.F, ref=self.ref_point, nsamples=self.n_samples, seed=self.random_state.randint(0, 2**32-1))
|
|
102
|
+
# self.hvc = hv_contributions(self.F, ref=self.ref_point)
|
|
103
|
+
#
|
|
104
|
+
# return self
|
|
105
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from moocore import hypervolume as hv
|
|
3
|
+
from moocore import hv_contributions as hvc
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def hv_exact(ref_point, F):
|
|
7
|
+
return hv(F, ref=ref_point)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def hvc_exact(ref_point, F):
|
|
11
|
+
return hvc(F, ref=ref_point)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DynamicHypervolume:
|
|
15
|
+
|
|
16
|
+
def __init__(self, ref_point, F=None, func_hv=None, func_hvc=None) -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.ref_point = ref_point
|
|
19
|
+
|
|
20
|
+
self.func_hv = func_hv
|
|
21
|
+
self.func_hvc = func_hvc
|
|
22
|
+
|
|
23
|
+
self.n_dim = len(ref_point)
|
|
24
|
+
self.F = np.zeros((0, self.n_dim))
|
|
25
|
+
|
|
26
|
+
self.hv = 0.0
|
|
27
|
+
self.hvc = np.zeros(0)
|
|
28
|
+
|
|
29
|
+
if F is not None:
|
|
30
|
+
self.add(F)
|
|
31
|
+
|
|
32
|
+
def add(self, F):
|
|
33
|
+
assert len(F.shape) == 2, "The points to add must be a two-dimensional array."
|
|
34
|
+
assert F.shape[1] == self.n_dim, "The dimensions of the ref_point and points to add must be equal"
|
|
35
|
+
self.F = np.vstack([self.F, F])
|
|
36
|
+
self.hv, self.hvc = self.calc()
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def delete(self, k):
|
|
40
|
+
assert k < len(self.F)
|
|
41
|
+
self.F = np.delete(self.F, k, axis=0)
|
|
42
|
+
self.hv, self.hvc = self.calc()
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
def calc(self):
|
|
46
|
+
return self._calc(self.ref_point, self.F)
|
|
47
|
+
|
|
48
|
+
# if len(self.F) == 0:
|
|
49
|
+
# return 0.0, np.zeros(0)
|
|
50
|
+
# else:
|
|
51
|
+
# return self._calc(self.ref_point, self.F)
|
|
52
|
+
|
|
53
|
+
def _calc(self, ref_point, F):
|
|
54
|
+
hv = None
|
|
55
|
+
if self.func_hv is not None:
|
|
56
|
+
hv = self.func_hv(ref_point, F)
|
|
57
|
+
|
|
58
|
+
hvc = None
|
|
59
|
+
if self.func_hvc is not None:
|
|
60
|
+
hvc = self.func_hvc(ref_point, F)
|
|
61
|
+
|
|
62
|
+
return hv, hvc
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ExactHypervolume(DynamicHypervolume):
|
|
66
|
+
|
|
67
|
+
def __init__(self, ref_point, func_hv=hv_exact, func_hvc=hvc_exact, **kwargs) -> None:
|
|
68
|
+
super().__init__(ref_point, func_hv=func_hv, func_hvc=func_hvc, **kwargs)
|