pymoo 0.6.1.5.dev0__cp312-cp312-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-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-312-x86_64-linux-gnu.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-312-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,246 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.moo.nsga3 import calc_niche_count, niching, comp_by_cv_then_random, associate_to_niches, NSGA3
|
|
4
|
+
from pymoo.core.survival import Survival
|
|
5
|
+
from pymoo.docs import parse_doc_string
|
|
6
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
7
|
+
from pymoo.operators.selection.tournament import TournamentSelection
|
|
8
|
+
from pymoo.util.misc import intersect
|
|
9
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
10
|
+
from pymoo.util.normalization import denormalize, get_extreme_points_c, get_nadir_point
|
|
11
|
+
from pymoo.util.reference_direction import UniformReferenceDirectionFactory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# =========================================================================================================
|
|
15
|
+
# Implementation
|
|
16
|
+
# =========================================================================================================
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RNSGA3(NSGA3):
|
|
20
|
+
|
|
21
|
+
def __init__(self,
|
|
22
|
+
ref_points,
|
|
23
|
+
pop_per_ref_point,
|
|
24
|
+
mu=0.05,
|
|
25
|
+
sampling=FloatRandomSampling(),
|
|
26
|
+
selection=TournamentSelection(func_comp=comp_by_cv_then_random),
|
|
27
|
+
eliminate_duplicates=True,
|
|
28
|
+
n_offsprings=None,
|
|
29
|
+
**kwargs):
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
|
|
35
|
+
ref_points : {ref_points}
|
|
36
|
+
|
|
37
|
+
pop_per_ref_point : int
|
|
38
|
+
Size of the population used for each reference point.
|
|
39
|
+
|
|
40
|
+
mu : float
|
|
41
|
+
Defines the init_simplex_scale of the reference lines used during survival selection. Increasing mu will result
|
|
42
|
+
having solutions with a larger spread.
|
|
43
|
+
|
|
44
|
+
n_offsprings : {n_offsprings}
|
|
45
|
+
sampling : {sampling}
|
|
46
|
+
selection : {selection}
|
|
47
|
+
crossover : {crossover}
|
|
48
|
+
mutation : {mutation}
|
|
49
|
+
eliminate_duplicates : {eliminate_duplicates}
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
# number of objectives the reference lines have
|
|
54
|
+
n_obj = ref_points.shape[1]
|
|
55
|
+
|
|
56
|
+
# add the aspiration point lines
|
|
57
|
+
aspiration_ref_dirs = UniformReferenceDirectionFactory(n_dim=n_obj, n_points=pop_per_ref_point).do()
|
|
58
|
+
|
|
59
|
+
survival = AspirationPointSurvival(ref_points, aspiration_ref_dirs, mu=mu)
|
|
60
|
+
pop_size = ref_points.shape[0] * aspiration_ref_dirs.shape[0] + aspiration_ref_dirs.shape[1]
|
|
61
|
+
ref_dirs = None
|
|
62
|
+
|
|
63
|
+
super().__init__(ref_dirs,
|
|
64
|
+
pop_size=pop_size,
|
|
65
|
+
sampling=sampling,
|
|
66
|
+
selection=selection,
|
|
67
|
+
survival=survival,
|
|
68
|
+
eliminate_duplicates=eliminate_duplicates,
|
|
69
|
+
n_offsprings=n_offsprings,
|
|
70
|
+
**kwargs)
|
|
71
|
+
|
|
72
|
+
def _setup(self, problem, **kwargs):
|
|
73
|
+
if self.survival.ref_points.shape[1] != problem.n_obj:
|
|
74
|
+
raise Exception("Dimensionality of reference points must be equal to the number of objectives: %s != %s" %
|
|
75
|
+
(self.survival.ref_points.shape[1], problem.n_obj))
|
|
76
|
+
|
|
77
|
+
def _finalize(self):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AspirationPointSurvival(Survival):
|
|
82
|
+
|
|
83
|
+
def __init__(self, ref_points, aspiration_ref_dirs, mu=0.1):
|
|
84
|
+
super().__init__()
|
|
85
|
+
|
|
86
|
+
self.ref_points = ref_points
|
|
87
|
+
self.aspiration_ref_dirs = aspiration_ref_dirs
|
|
88
|
+
self.mu = mu
|
|
89
|
+
|
|
90
|
+
self.ref_dirs = aspiration_ref_dirs
|
|
91
|
+
self.extreme_points = None
|
|
92
|
+
self.intercepts = None
|
|
93
|
+
self.nadir_point = None
|
|
94
|
+
self.opt = None
|
|
95
|
+
self.ideal_point = np.full(ref_points.shape[1], np.inf)
|
|
96
|
+
self.worst_point = np.full(ref_points.shape[1], -np.inf)
|
|
97
|
+
|
|
98
|
+
def _do(self, problem, pop, n_survive, D=None, **kwargs):
|
|
99
|
+
|
|
100
|
+
# attributes to be set after the survival
|
|
101
|
+
F = pop.get("F")
|
|
102
|
+
|
|
103
|
+
# find or usually update the new ideal point - from feasible solutions
|
|
104
|
+
self.ideal_point = np.min(np.vstack((self.ideal_point, F, self.ref_points)), axis=0)
|
|
105
|
+
self.worst_point = np.max(np.vstack((self.worst_point, F, self.ref_points)), axis=0)
|
|
106
|
+
|
|
107
|
+
# calculate the fronts of the population
|
|
108
|
+
fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive)
|
|
109
|
+
non_dominated, last_front = fronts[0], fronts[-1]
|
|
110
|
+
|
|
111
|
+
# find the extreme points for normalization
|
|
112
|
+
self.extreme_points = get_extreme_points_c(
|
|
113
|
+
np.vstack([F[non_dominated], self.ref_points])
|
|
114
|
+
, self.ideal_point,
|
|
115
|
+
extreme_points=self.extreme_points)
|
|
116
|
+
|
|
117
|
+
# find the intercepts for normalization and do backup if gaussian elimination fails
|
|
118
|
+
worst_of_population = np.max(F, axis=0)
|
|
119
|
+
worst_of_front = np.max(F[non_dominated, :], axis=0)
|
|
120
|
+
|
|
121
|
+
self.nadir_point = get_nadir_point(self.extreme_points, self.ideal_point, self.worst_point,
|
|
122
|
+
worst_of_population, worst_of_front)
|
|
123
|
+
|
|
124
|
+
# consider only the population until we come to the splitting front
|
|
125
|
+
I = np.concatenate(fronts)
|
|
126
|
+
pop, rank, F = pop[I], rank[I], F[I]
|
|
127
|
+
|
|
128
|
+
# update the front indices for the current population
|
|
129
|
+
counter = 0
|
|
130
|
+
for i in range(len(fronts)):
|
|
131
|
+
for j in range(len(fronts[i])):
|
|
132
|
+
fronts[i][j] = counter
|
|
133
|
+
counter += 1
|
|
134
|
+
last_front = fronts[-1]
|
|
135
|
+
|
|
136
|
+
unit_ref_points = (self.ref_points - self.ideal_point) / (self.nadir_point - self.ideal_point)
|
|
137
|
+
ref_dirs = get_ref_dirs_from_points(unit_ref_points, self.aspiration_ref_dirs, mu=self.mu)
|
|
138
|
+
self.ref_dirs = denormalize(ref_dirs, self.ideal_point, self.nadir_point)
|
|
139
|
+
|
|
140
|
+
# associate individuals to niches
|
|
141
|
+
niche_of_individuals, dist_to_niche, dist_matrix = associate_to_niches(F, ref_dirs, self.ideal_point,
|
|
142
|
+
self.nadir_point)
|
|
143
|
+
pop.set('rank', rank, 'niche', niche_of_individuals, 'dist_to_niche', dist_to_niche)
|
|
144
|
+
|
|
145
|
+
# set the optimum, first front and closest to all reference directions
|
|
146
|
+
closest = np.unique(dist_matrix[:, np.unique(niche_of_individuals)].argmin(axis=0))
|
|
147
|
+
self.opt = pop[intersect(fronts[0], closest)]
|
|
148
|
+
|
|
149
|
+
# if we need to select individuals to survive
|
|
150
|
+
if len(pop) > n_survive:
|
|
151
|
+
|
|
152
|
+
# if there is only one front
|
|
153
|
+
if len(fronts) == 1:
|
|
154
|
+
n_remaining = n_survive
|
|
155
|
+
until_last_front = np.array([], dtype=int)
|
|
156
|
+
niche_count = np.zeros(len(ref_dirs), dtype=int)
|
|
157
|
+
|
|
158
|
+
# if some individuals already survived
|
|
159
|
+
else:
|
|
160
|
+
until_last_front = np.concatenate(fronts[:-1])
|
|
161
|
+
niche_count = calc_niche_count(len(ref_dirs), niche_of_individuals[until_last_front])
|
|
162
|
+
n_remaining = n_survive - len(until_last_front)
|
|
163
|
+
|
|
164
|
+
S = niching(pop[last_front], n_remaining, niche_count, niche_of_individuals[last_front],
|
|
165
|
+
dist_to_niche[last_front])
|
|
166
|
+
|
|
167
|
+
survivors = np.concatenate((until_last_front, last_front[S].tolist()))
|
|
168
|
+
pop = pop[survivors]
|
|
169
|
+
|
|
170
|
+
return pop
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def get_ref_dirs_from_points(ref_point, ref_dirs, mu=0.1):
|
|
174
|
+
"""
|
|
175
|
+
This function takes user specified reference points, and creates smaller sets of equidistant
|
|
176
|
+
Das-Dennis points around the projection of user points on the Das-Dennis hyperplane
|
|
177
|
+
:param ref_point: List of user specified reference points
|
|
178
|
+
:param n_obj: Number of objectives to consider
|
|
179
|
+
:param mu: Shrinkage factor (0-1), Smaller = tighter convergence, Larger= larger convergence
|
|
180
|
+
:return: Set of reference points
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
n_obj = ref_point.shape[1]
|
|
184
|
+
|
|
185
|
+
val = []
|
|
186
|
+
n_vector = np.ones(n_obj) / np.sqrt(n_obj) # Normal vector of Das Dennis plane
|
|
187
|
+
point_on_plane = np.eye(n_obj)[0] # Point on Das-Dennis
|
|
188
|
+
|
|
189
|
+
for point in ref_point:
|
|
190
|
+
|
|
191
|
+
ref_dir_for_aspiration_point = np.copy(ref_dirs) # Copy of computed reference directions
|
|
192
|
+
ref_dir_for_aspiration_point = mu * ref_dir_for_aspiration_point
|
|
193
|
+
|
|
194
|
+
cent = np.mean(ref_dir_for_aspiration_point, axis=0) # Find centroid of shrunken reference points
|
|
195
|
+
|
|
196
|
+
# Project shrunken Das-Dennis points back onto original Das-Dennis hyperplane
|
|
197
|
+
intercept = line_plane_intersection(np.zeros(n_obj), point, point_on_plane, n_vector)
|
|
198
|
+
shift = intercept - cent # shift vector
|
|
199
|
+
|
|
200
|
+
ref_dir_for_aspiration_point += shift
|
|
201
|
+
|
|
202
|
+
# If reference directions are located outside of first octant, redefine points onto the border
|
|
203
|
+
if not (ref_dir_for_aspiration_point > 0).min():
|
|
204
|
+
ref_dir_for_aspiration_point[ref_dir_for_aspiration_point < 0] = 0
|
|
205
|
+
ref_dir_for_aspiration_point = ref_dir_for_aspiration_point / np.sum(ref_dir_for_aspiration_point, axis=1)[
|
|
206
|
+
:, None]
|
|
207
|
+
val.extend(ref_dir_for_aspiration_point)
|
|
208
|
+
|
|
209
|
+
val.extend(np.eye(n_obj)) # Add extreme points
|
|
210
|
+
return np.array(val)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# intersection function
|
|
214
|
+
|
|
215
|
+
def line_plane_intersection(l0, l1, p0, p_no, epsilon=1e-6):
|
|
216
|
+
"""
|
|
217
|
+
l0, l1: define the line
|
|
218
|
+
p0, p_no: define the plane:
|
|
219
|
+
p0 is a point on the plane (plane coordinate).
|
|
220
|
+
p_no is a normal vector defining the plane direction;
|
|
221
|
+
(does not need to be normalized).
|
|
222
|
+
|
|
223
|
+
reference: https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection
|
|
224
|
+
return a Vector or None (when the intersection can't be found).
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
l = l1 - l0
|
|
228
|
+
dot = np.dot(l, p_no)
|
|
229
|
+
|
|
230
|
+
if abs(dot) > epsilon:
|
|
231
|
+
# the factor of the point between p0 -> p1 (0 - 1)
|
|
232
|
+
# if 'fac' is between (0 - 1) the point intersects with the segment.
|
|
233
|
+
# otherwise:
|
|
234
|
+
# < 0.0: behind p0.
|
|
235
|
+
# > 1.0: in front of p1.
|
|
236
|
+
w = p0 - l0
|
|
237
|
+
d = np.dot(w, p_no) / dot
|
|
238
|
+
l = l * d
|
|
239
|
+
return l0 + l
|
|
240
|
+
else:
|
|
241
|
+
# The segment is parallel to plane then return the perpendicular projection
|
|
242
|
+
ref_proj = l1 - (np.dot(l1 - p0, p_no) * p_no)
|
|
243
|
+
return ref_proj
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
parse_doc_string(RNSGA3.__init__)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
4
|
+
from pymoo.core.survival import Survival
|
|
5
|
+
from pymoo.docs import parse_doc_string
|
|
6
|
+
from pymoo.operators.crossover.sbx import SBX
|
|
7
|
+
from pymoo.operators.mutation.pm import PM
|
|
8
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
9
|
+
from pymoo.operators.selection.rnd import RandomSelection
|
|
10
|
+
from pymoo.termination.max_eval import MaximumFunctionCallTermination
|
|
11
|
+
from pymoo.termination.max_gen import MaximumGenerationTermination
|
|
12
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
13
|
+
from pymoo.util.misc import has_feasible, vectorized_cdist
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RVEA(GeneticAlgorithm):
|
|
17
|
+
|
|
18
|
+
def __init__(self,
|
|
19
|
+
ref_dirs,
|
|
20
|
+
alpha=2.0,
|
|
21
|
+
adapt_freq=0.1,
|
|
22
|
+
pop_size=None,
|
|
23
|
+
sampling=FloatRandomSampling(),
|
|
24
|
+
selection=RandomSelection(),
|
|
25
|
+
crossover=SBX(eta=30, prob=1.0),
|
|
26
|
+
mutation=PM(eta=20),
|
|
27
|
+
eliminate_duplicates=True,
|
|
28
|
+
n_offsprings=None,
|
|
29
|
+
output=MultiObjectiveOutput(),
|
|
30
|
+
**kwargs):
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
|
|
36
|
+
ref_dirs : {ref_dirs}
|
|
37
|
+
adapt_freq : float
|
|
38
|
+
Defines the ratio of generation when the reference directions are updated.
|
|
39
|
+
pop_size : int (default = None)
|
|
40
|
+
By default the population size is set to None which means that it will be equal to the number of reference
|
|
41
|
+
line. However, if desired this can be overwritten by providing a positive number.
|
|
42
|
+
sampling : {sampling}
|
|
43
|
+
selection : {selection}
|
|
44
|
+
crossover : {crossover}
|
|
45
|
+
mutation : {mutation}
|
|
46
|
+
eliminate_duplicates : {eliminate_duplicates}
|
|
47
|
+
n_offsprings : {n_offsprings}
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# set reference directions and pop_size
|
|
52
|
+
self.ref_dirs = ref_dirs
|
|
53
|
+
if self.ref_dirs is not None:
|
|
54
|
+
if pop_size is None:
|
|
55
|
+
pop_size = len(self.ref_dirs)
|
|
56
|
+
|
|
57
|
+
# the fraction of n_max_gen when the the reference directions are adapted
|
|
58
|
+
self.adapt_freq = adapt_freq
|
|
59
|
+
|
|
60
|
+
# you can override the survival if necessary
|
|
61
|
+
survival = kwargs.pop("survival", None)
|
|
62
|
+
if survival is None:
|
|
63
|
+
survival = APDSurvival(ref_dirs, alpha=alpha)
|
|
64
|
+
|
|
65
|
+
super().__init__(pop_size=pop_size,
|
|
66
|
+
sampling=sampling,
|
|
67
|
+
selection=selection,
|
|
68
|
+
crossover=crossover,
|
|
69
|
+
mutation=mutation,
|
|
70
|
+
survival=survival,
|
|
71
|
+
eliminate_duplicates=eliminate_duplicates,
|
|
72
|
+
n_offsprings=n_offsprings,
|
|
73
|
+
output=output,
|
|
74
|
+
**kwargs)
|
|
75
|
+
|
|
76
|
+
def _setup(self, problem, **kwargs):
|
|
77
|
+
|
|
78
|
+
# if maximum functions termination convert it to generations
|
|
79
|
+
if isinstance(self.termination, MaximumFunctionCallTermination):
|
|
80
|
+
n_gen = np.ceil((self.termination.n_max_evals - self.pop_size) / self.n_offsprings)
|
|
81
|
+
self.termination = MaximumGenerationTermination(n_gen)
|
|
82
|
+
|
|
83
|
+
# check whether the n_gen termination is used - otherwise this algorithm can be not run
|
|
84
|
+
if not isinstance(self.termination, MaximumGenerationTermination):
|
|
85
|
+
raise Exception("Please use the n_gen or n_eval as a termination criterion to run RVEA!")
|
|
86
|
+
|
|
87
|
+
def _advance(self, **kwargs):
|
|
88
|
+
super()._advance(**kwargs)
|
|
89
|
+
|
|
90
|
+
# get the current generation and maximum of generations
|
|
91
|
+
n_gen, n_max_gen = self.n_gen, self.termination.n_max_gen
|
|
92
|
+
|
|
93
|
+
# each i-th generation (define by fr and n_max_gen) the reference directions are updated
|
|
94
|
+
if self.adapt_freq is not None and n_gen % np.ceil(n_max_gen * self.adapt_freq) == 0:
|
|
95
|
+
self.survival.adapt()
|
|
96
|
+
|
|
97
|
+
def _set_optimum(self, **kwargs):
|
|
98
|
+
if not has_feasible(self.pop):
|
|
99
|
+
self.opt = self.pop[[np.argmin(self.pop.get("CV"))]]
|
|
100
|
+
else:
|
|
101
|
+
self.opt = self.pop
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
105
|
+
# Survival Selection
|
|
106
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
def calc_gamma(V):
|
|
109
|
+
gamma = np.arccos((- np.sort(-1 * V @ V.T))[:, 1])
|
|
110
|
+
gamma = np.maximum(gamma, 1e-64)
|
|
111
|
+
return gamma
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def calc_V(ref_dirs):
|
|
115
|
+
return ref_dirs / np.linalg.norm(ref_dirs, axis=1)[:, None]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class APDSurvival(Survival):
|
|
119
|
+
|
|
120
|
+
def __init__(self, ref_dirs, alpha=2.0) -> None:
|
|
121
|
+
super().__init__(filter_infeasible=True)
|
|
122
|
+
n_dim = ref_dirs.shape[1]
|
|
123
|
+
|
|
124
|
+
self.alpha = alpha
|
|
125
|
+
self.niches = None
|
|
126
|
+
self.V, self.gamma = None, None
|
|
127
|
+
self.ideal, self.nadir = np.full(n_dim, np.inf), None
|
|
128
|
+
|
|
129
|
+
self.ref_dirs = ref_dirs
|
|
130
|
+
self.extreme_ref_dirs = np.where(np.any(vectorized_cdist(self.ref_dirs, np.eye(n_dim)) == 0, axis=1))[0]
|
|
131
|
+
|
|
132
|
+
self.V = calc_V(self.ref_dirs)
|
|
133
|
+
self.gamma = calc_gamma(self.V)
|
|
134
|
+
|
|
135
|
+
def adapt(self):
|
|
136
|
+
if self.nadir is not None:
|
|
137
|
+
self.V = calc_V(calc_V(self.ref_dirs) * (self.nadir - self.ideal))
|
|
138
|
+
self.gamma = calc_gamma(self.V)
|
|
139
|
+
|
|
140
|
+
def _do(self, problem, pop, n_survive, algorithm=None, n_gen=None, n_max_gen=None, **kwargs):
|
|
141
|
+
|
|
142
|
+
if n_gen is None:
|
|
143
|
+
n_gen = algorithm.n_gen - 1
|
|
144
|
+
if n_max_gen is None:
|
|
145
|
+
n_max_gen = algorithm.termination.n_max_gen
|
|
146
|
+
|
|
147
|
+
# get the objective space values
|
|
148
|
+
F = pop.get("F")
|
|
149
|
+
|
|
150
|
+
# store the ideal and nadir point estimation for adapt - (and ideal for transformation)
|
|
151
|
+
self.ideal = np.minimum(F.min(axis=0), self.ideal)
|
|
152
|
+
|
|
153
|
+
# translate the population to make the ideal point the origin
|
|
154
|
+
F = F - self.ideal
|
|
155
|
+
|
|
156
|
+
# the distance to the ideal point
|
|
157
|
+
dist_to_ideal = np.linalg.norm(F, axis=1)
|
|
158
|
+
dist_to_ideal[dist_to_ideal < 1e-64] = 1e-64
|
|
159
|
+
|
|
160
|
+
# normalize by distance to ideal
|
|
161
|
+
F_prime = F / dist_to_ideal[:, None]
|
|
162
|
+
|
|
163
|
+
# calculate for each solution the acute angles to ref dirs
|
|
164
|
+
acute_angle = np.arccos(F_prime @ self.V.T)
|
|
165
|
+
niches = acute_angle.argmin(axis=1)
|
|
166
|
+
|
|
167
|
+
# assign to each reference direction the solution
|
|
168
|
+
niches_to_ind = [[] for _ in range(len(self.V))]
|
|
169
|
+
for k, i in enumerate(niches):
|
|
170
|
+
niches_to_ind[i].append(k)
|
|
171
|
+
|
|
172
|
+
# all individuals which will be surviving
|
|
173
|
+
survivors = []
|
|
174
|
+
|
|
175
|
+
# for each reference direction
|
|
176
|
+
for k in range(len(self.V)):
|
|
177
|
+
|
|
178
|
+
# individuals assigned to the niche
|
|
179
|
+
assigned_to_niche = niches_to_ind[k]
|
|
180
|
+
|
|
181
|
+
# if niche not empty
|
|
182
|
+
if len(assigned_to_niche) > 0:
|
|
183
|
+
|
|
184
|
+
# the angle of niche to nearest neighboring niche
|
|
185
|
+
gamma = self.gamma[k]
|
|
186
|
+
|
|
187
|
+
# the angle from the individuals of this niches to the niche itself
|
|
188
|
+
theta = acute_angle[assigned_to_niche, k]
|
|
189
|
+
|
|
190
|
+
# the penalty which is applied for the metric
|
|
191
|
+
M = problem.n_obj if problem.n_obj > 2.0 else 1.0
|
|
192
|
+
penalty = M * ((n_gen / n_max_gen) ** self.alpha) * (theta / gamma)
|
|
193
|
+
|
|
194
|
+
# calculate the angle-penalized penalized (APD)
|
|
195
|
+
apd = dist_to_ideal[assigned_to_niche] * (1 + penalty)
|
|
196
|
+
|
|
197
|
+
# the individual which survives
|
|
198
|
+
survivor = assigned_to_niche[apd.argmin()]
|
|
199
|
+
|
|
200
|
+
# set attributes to the individual
|
|
201
|
+
pop[assigned_to_niche].set(theta=theta, apd=apd, niche=k, opt=False)
|
|
202
|
+
pop[survivor].set("opt", True)
|
|
203
|
+
|
|
204
|
+
# select the one with smallest APD value
|
|
205
|
+
survivors.append(survivor)
|
|
206
|
+
|
|
207
|
+
ret = pop[survivors]
|
|
208
|
+
self.niches = niches_to_ind
|
|
209
|
+
self.nadir = ret.get("F").max(axis=0)
|
|
210
|
+
|
|
211
|
+
return ret
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
parse_doc_string(RVEA.__init__)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
4
|
+
from pymoo.core.population import Population
|
|
5
|
+
from pymoo.core.survival import Survival
|
|
6
|
+
from pymoo.docs import parse_doc_string
|
|
7
|
+
from pymoo.indicators.hv.exact import ExactHypervolume
|
|
8
|
+
from pymoo.indicators.hv.exact_2d import ExactHypervolume2D
|
|
9
|
+
from pymoo.indicators.hv.monte_carlo import ApproximateMonteCarloHypervolume
|
|
10
|
+
from pymoo.operators.crossover.sbx import SBX
|
|
11
|
+
from pymoo.operators.mutation.pm import PM
|
|
12
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
13
|
+
from pymoo.operators.selection.tournament import compare, TournamentSelection
|
|
14
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
15
|
+
from pymoo.util.dominator import Dominator
|
|
16
|
+
from pymoo.util.function_loader import load_function
|
|
17
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
18
|
+
from pymoo.util.normalization import normalize
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
22
|
+
# Environmental Survival - Remove the solution with the least HV contribution
|
|
23
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LeastHypervolumeContributionSurvival(Survival):
|
|
27
|
+
|
|
28
|
+
def __init__(self, eps=10.0) -> None:
|
|
29
|
+
super().__init__(filter_infeasible=True)
|
|
30
|
+
self.eps = eps
|
|
31
|
+
|
|
32
|
+
def _do(self, problem, pop, *args, n_survive=None, ideal=None, nadir=None, **kwargs):
|
|
33
|
+
|
|
34
|
+
# get the objective space values and objects
|
|
35
|
+
F = pop.get("F").astype(float, copy=False)
|
|
36
|
+
|
|
37
|
+
# if the boundary points are not provided -> estimate them from pop
|
|
38
|
+
if ideal is None:
|
|
39
|
+
ideal = F.min(axis=0)
|
|
40
|
+
if nadir is None:
|
|
41
|
+
nadir = F.max(axis=0)
|
|
42
|
+
|
|
43
|
+
# the number of objectives
|
|
44
|
+
_, n_obj = F.shape
|
|
45
|
+
|
|
46
|
+
# the final indices of surviving individuals
|
|
47
|
+
survivors = []
|
|
48
|
+
|
|
49
|
+
# do the non-dominated sorting until splitting front
|
|
50
|
+
fronts = NonDominatedSorting().do(F, n_stop_if_ranked=n_survive)
|
|
51
|
+
|
|
52
|
+
for k, front in enumerate(fronts):
|
|
53
|
+
|
|
54
|
+
# get the actual front as individuals
|
|
55
|
+
front = pop[front]
|
|
56
|
+
front.set("rank", k)
|
|
57
|
+
|
|
58
|
+
if len(survivors) + len(front) > n_survive:
|
|
59
|
+
|
|
60
|
+
# normalize all the function values for the front
|
|
61
|
+
F = front.get("F")
|
|
62
|
+
F = normalize(F, ideal, nadir)
|
|
63
|
+
|
|
64
|
+
# define the reference point and shift it a bit - F is already normalized!
|
|
65
|
+
ref_point = np.full(problem.n_obj, 1.0 + self.eps)
|
|
66
|
+
|
|
67
|
+
_, n_obj = F.shape
|
|
68
|
+
|
|
69
|
+
# choose the suitable hypervolume method
|
|
70
|
+
clazz = ExactHypervolume
|
|
71
|
+
if n_obj == 2:
|
|
72
|
+
clazz = ExactHypervolume2D
|
|
73
|
+
elif n_obj > 3:
|
|
74
|
+
clazz = ApproximateMonteCarloHypervolume
|
|
75
|
+
|
|
76
|
+
# finally do the computation
|
|
77
|
+
hv = clazz(ref_point).add(F)
|
|
78
|
+
|
|
79
|
+
# current front sorted by crowding distance if splitting
|
|
80
|
+
while len(survivors) + len(front) > n_survive:
|
|
81
|
+
k = hv.hvc.argmin()
|
|
82
|
+
hv.delete(k)
|
|
83
|
+
front = np.delete(front, k)
|
|
84
|
+
|
|
85
|
+
# extend the survivors by all or selected individuals
|
|
86
|
+
survivors.extend(front)
|
|
87
|
+
|
|
88
|
+
return Population.create(*survivors)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
92
|
+
# Binary Tournament
|
|
93
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def cv_and_dom_tournament(pop, P, *args, **kwargs):
|
|
97
|
+
n_tournaments, n_parents = P.shape
|
|
98
|
+
|
|
99
|
+
if n_parents != 2:
|
|
100
|
+
raise ValueError("Only implemented for binary tournament!")
|
|
101
|
+
|
|
102
|
+
S = np.full(n_tournaments, np.nan)
|
|
103
|
+
|
|
104
|
+
for i in range(n_tournaments):
|
|
105
|
+
|
|
106
|
+
a, b = P[i, 0], P[i, 1]
|
|
107
|
+
a_cv, a_f, b_cv, b_f, = pop[a].CV[0], pop[a].F, pop[b].CV[0], pop[b].F
|
|
108
|
+
|
|
109
|
+
# if at least one solution is infeasible
|
|
110
|
+
if a_cv > 0.0 or b_cv > 0.0:
|
|
111
|
+
S[i] = compare(a, a_cv, b, b_cv, method='smaller_is_better', return_random_if_equal=True)
|
|
112
|
+
|
|
113
|
+
# both solutions are feasible
|
|
114
|
+
else:
|
|
115
|
+
|
|
116
|
+
# if one dominates another choose the nds one
|
|
117
|
+
rel = Dominator.get_relation(a_f, b_f)
|
|
118
|
+
if rel == 1:
|
|
119
|
+
S[i] = a
|
|
120
|
+
elif rel == -1:
|
|
121
|
+
S[i] = b
|
|
122
|
+
|
|
123
|
+
# if rank or domination relation didn't make a decision compare by crowding
|
|
124
|
+
if np.isnan(S[i]):
|
|
125
|
+
S[i] = np.random.choice([a, b])
|
|
126
|
+
|
|
127
|
+
return S[:, None].astype(int, copy=False)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
131
|
+
# Algorithm
|
|
132
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
class SMSEMOA(GeneticAlgorithm):
|
|
135
|
+
|
|
136
|
+
def __init__(self,
|
|
137
|
+
pop_size=100,
|
|
138
|
+
sampling=FloatRandomSampling(),
|
|
139
|
+
selection=TournamentSelection(func_comp=cv_and_dom_tournament),
|
|
140
|
+
crossover=SBX(),
|
|
141
|
+
mutation=PM(),
|
|
142
|
+
survival=LeastHypervolumeContributionSurvival(),
|
|
143
|
+
eliminate_duplicates=True,
|
|
144
|
+
n_offsprings=None,
|
|
145
|
+
normalize=True,
|
|
146
|
+
output=MultiObjectiveOutput(),
|
|
147
|
+
**kwargs):
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
pop_size : {pop_size}
|
|
153
|
+
sampling : {sampling}
|
|
154
|
+
selection : {selection}
|
|
155
|
+
crossover : {crossover}
|
|
156
|
+
mutation : {mutation}
|
|
157
|
+
eliminate_duplicates : {eliminate_duplicates}
|
|
158
|
+
n_offsprings : {n_offsprings}
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
super().__init__(pop_size=pop_size,
|
|
162
|
+
sampling=sampling,
|
|
163
|
+
selection=selection,
|
|
164
|
+
crossover=crossover,
|
|
165
|
+
mutation=mutation,
|
|
166
|
+
survival=survival,
|
|
167
|
+
eliminate_duplicates=eliminate_duplicates,
|
|
168
|
+
n_offsprings=n_offsprings,
|
|
169
|
+
output=output,
|
|
170
|
+
advance_after_initial_infill=True,
|
|
171
|
+
**kwargs)
|
|
172
|
+
|
|
173
|
+
self.normalize = normalize
|
|
174
|
+
|
|
175
|
+
def _advance(self, infills=None, **kwargs):
|
|
176
|
+
|
|
177
|
+
ideal, nadir = None, None
|
|
178
|
+
|
|
179
|
+
# estimate ideal and nadir from the current population (more robust then from doing it from merged)
|
|
180
|
+
if self.normalize:
|
|
181
|
+
F = self.pop.get("F")
|
|
182
|
+
ideal, nadir = F.min(axis=0), F.max(axis=0) + 1e-32
|
|
183
|
+
|
|
184
|
+
# merge the offsprings with the current population
|
|
185
|
+
if infills is not None:
|
|
186
|
+
pop = Population.merge(self.pop, infills)
|
|
187
|
+
else:
|
|
188
|
+
pop = self.pop
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
self.pop = self.survival.do(self.problem, pop, n_survive=self.pop_size, algorithm=self,
|
|
192
|
+
ideal=ideal, nadir=nadir, **kwargs)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
parse_doc_string(SMSEMOA.__init__)
|