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,152 @@
|
|
|
1
|
+
from math import floor
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pymoo.util.dominator import Dominator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def efficient_non_dominated_sort(F, strategy="sequential"):
|
|
9
|
+
"""
|
|
10
|
+
Efficient Non-dominated Sorting (ENS)
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
F: numpy.ndarray
|
|
15
|
+
objective values for each individual.
|
|
16
|
+
strategy: str
|
|
17
|
+
search strategy, can be "sequential" or "binary".
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
fronts: list
|
|
22
|
+
Indices of the individuals in each front.
|
|
23
|
+
|
|
24
|
+
References
|
|
25
|
+
----------
|
|
26
|
+
X. Zhang, Y. Tian, R. Cheng, and Y. Jin,
|
|
27
|
+
An efficient approach to nondominated sorting for evolutionary multiobjective optimization,
|
|
28
|
+
IEEE Transactions on Evolutionary Computation, 2015, 19(2): 201-213.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
assert (strategy in ["sequential", 'binary']), "Invalid search strategy"
|
|
32
|
+
|
|
33
|
+
# the shape of the input
|
|
34
|
+
N, M = F.shape
|
|
35
|
+
|
|
36
|
+
# do a lexicographic ordering
|
|
37
|
+
I = np.lexsort(F.T[::-1])
|
|
38
|
+
F = F[I]
|
|
39
|
+
|
|
40
|
+
# front ranks for each individual
|
|
41
|
+
fronts = []
|
|
42
|
+
|
|
43
|
+
for i in range(N):
|
|
44
|
+
|
|
45
|
+
if strategy == 'sequential':
|
|
46
|
+
k = sequential_search(F, i, fronts)
|
|
47
|
+
else:
|
|
48
|
+
k = binary_search(F, i, fronts)
|
|
49
|
+
|
|
50
|
+
# create empty fronts if necessary
|
|
51
|
+
if k >= len(fronts):
|
|
52
|
+
fronts.append([])
|
|
53
|
+
|
|
54
|
+
# append the current individual to a front
|
|
55
|
+
fronts[k].append(i)
|
|
56
|
+
|
|
57
|
+
# now map the fronts back to the originally sorting
|
|
58
|
+
ret = []
|
|
59
|
+
for front in fronts:
|
|
60
|
+
ret.append(I[front])
|
|
61
|
+
|
|
62
|
+
return ret
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def sequential_search(F, i, fronts) -> int:
|
|
66
|
+
"""
|
|
67
|
+
Find the front rank for the i-th individual through sequential search.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
F: np.ndarray
|
|
72
|
+
the objective values
|
|
73
|
+
i: int
|
|
74
|
+
the index of the individual
|
|
75
|
+
fronts: list
|
|
76
|
+
individuals in each front
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
num_found_fronts = len(fronts)
|
|
80
|
+
k = 0 # the front now checked
|
|
81
|
+
current = F[i]
|
|
82
|
+
while True:
|
|
83
|
+
if num_found_fronts == 0:
|
|
84
|
+
return 0
|
|
85
|
+
# solutions in the k-th front, examine in reverse order
|
|
86
|
+
fk_indices = fronts[k]
|
|
87
|
+
solutions = F[fk_indices[::-1]]
|
|
88
|
+
non_dominated = True
|
|
89
|
+
for f in solutions:
|
|
90
|
+
relation = Dominator.get_relation(current, f)
|
|
91
|
+
if relation == -1:
|
|
92
|
+
non_dominated = False
|
|
93
|
+
break
|
|
94
|
+
if non_dominated:
|
|
95
|
+
return k
|
|
96
|
+
else:
|
|
97
|
+
k += 1
|
|
98
|
+
if k >= num_found_fronts:
|
|
99
|
+
# move the individual to a new front
|
|
100
|
+
return num_found_fronts
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def binary_search(F, i, fronts):
|
|
104
|
+
"""
|
|
105
|
+
Find the front rank for the i-th individual through binary search.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
F: np.ndarray
|
|
110
|
+
the objective values
|
|
111
|
+
i: int
|
|
112
|
+
the index of the individual
|
|
113
|
+
fronts: list
|
|
114
|
+
individuals in each front
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
num_found_fronts = len(fronts)
|
|
118
|
+
if num_found_fronts == 0:
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
k_min = 0 # the lower bound for checking
|
|
122
|
+
k_max = num_found_fronts # the upper bound for checking
|
|
123
|
+
k = floor((k_max + k_min) / 2 + 0.5) # the front now checked
|
|
124
|
+
current = F[i]
|
|
125
|
+
while True:
|
|
126
|
+
|
|
127
|
+
# solutions in the k-th front, examine in reverse order
|
|
128
|
+
fk_indices = fronts[k - 1]
|
|
129
|
+
solutions = F[fk_indices[::-1]]
|
|
130
|
+
non_dominated = True
|
|
131
|
+
|
|
132
|
+
for f in solutions:
|
|
133
|
+
relation = Dominator.get_relation(current, f)
|
|
134
|
+
if relation == -1:
|
|
135
|
+
non_dominated = False
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
# binary search
|
|
139
|
+
if non_dominated:
|
|
140
|
+
if k == k_min + 1:
|
|
141
|
+
return k - 1
|
|
142
|
+
else:
|
|
143
|
+
k_max = k
|
|
144
|
+
k = floor((k_max + k_min) / 2 + 0.5)
|
|
145
|
+
else:
|
|
146
|
+
k_min = k
|
|
147
|
+
if k_max == k_min + 1 and k_max < num_found_fronts:
|
|
148
|
+
return k_max - 1
|
|
149
|
+
elif k_min == num_found_fronts:
|
|
150
|
+
return num_found_fronts
|
|
151
|
+
else:
|
|
152
|
+
k = floor((k_max + k_min) / 2 + 0.5)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.util.dominator import Dominator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def fast_non_dominated_sort(F, dominator=Dominator(), **kwargs):
|
|
7
|
+
if "dominator" in kwargs:
|
|
8
|
+
M = Dominator.calc_domination_matrix(F)
|
|
9
|
+
else:
|
|
10
|
+
M = dominator.calc_domination_matrix(F)
|
|
11
|
+
|
|
12
|
+
# calculate the dominance matrix
|
|
13
|
+
n = M.shape[0]
|
|
14
|
+
|
|
15
|
+
fronts = []
|
|
16
|
+
|
|
17
|
+
if n == 0:
|
|
18
|
+
return fronts
|
|
19
|
+
|
|
20
|
+
# final rank that will be returned
|
|
21
|
+
n_ranked = 0
|
|
22
|
+
ranked = np.zeros(n, dtype=int)
|
|
23
|
+
|
|
24
|
+
# for each individual a list of all individuals that are dominated by this one
|
|
25
|
+
is_dominating = [[] for _ in range(n)]
|
|
26
|
+
|
|
27
|
+
# storage for the number of solutions dominated this one
|
|
28
|
+
n_dominated = np.zeros(n)
|
|
29
|
+
|
|
30
|
+
current_front = []
|
|
31
|
+
|
|
32
|
+
for i in range(n):
|
|
33
|
+
|
|
34
|
+
for j in range(i + 1, n):
|
|
35
|
+
rel = M[i, j]
|
|
36
|
+
if rel == 1:
|
|
37
|
+
is_dominating[i].append(j)
|
|
38
|
+
n_dominated[j] += 1
|
|
39
|
+
elif rel == -1:
|
|
40
|
+
is_dominating[j].append(i)
|
|
41
|
+
n_dominated[i] += 1
|
|
42
|
+
|
|
43
|
+
if n_dominated[i] == 0:
|
|
44
|
+
current_front.append(i)
|
|
45
|
+
ranked[i] = 1.0
|
|
46
|
+
n_ranked += 1
|
|
47
|
+
|
|
48
|
+
# append the first front to the current front
|
|
49
|
+
fronts.append(current_front)
|
|
50
|
+
|
|
51
|
+
# while not all solutions are assigned to a pareto front
|
|
52
|
+
while n_ranked < n:
|
|
53
|
+
|
|
54
|
+
next_front = []
|
|
55
|
+
|
|
56
|
+
# for each individual in the current front
|
|
57
|
+
for i in current_front:
|
|
58
|
+
|
|
59
|
+
# all solutions that are dominated by this individuals
|
|
60
|
+
for j in is_dominating[i]:
|
|
61
|
+
n_dominated[j] -= 1
|
|
62
|
+
if n_dominated[j] == 0:
|
|
63
|
+
next_front.append(j)
|
|
64
|
+
ranked[j] = 1.0
|
|
65
|
+
n_ranked += 1
|
|
66
|
+
|
|
67
|
+
fronts.append(next_front)
|
|
68
|
+
current_front = next_front
|
|
69
|
+
|
|
70
|
+
return fronts
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def find_non_dominated(F, epsilon=0.0):
|
|
5
|
+
"""
|
|
6
|
+
Simple and efficient implementation to find only non-dominated points.
|
|
7
|
+
Uses straightforward O(n²) algorithm with early termination.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
F : np.ndarray
|
|
12
|
+
Objective values matrix of shape (n_points, n_objectives)
|
|
13
|
+
epsilon : float, optional
|
|
14
|
+
Epsilon value for dominance comparison (default: 0.0)
|
|
15
|
+
|
|
16
|
+
Returns
|
|
17
|
+
-------
|
|
18
|
+
np.ndarray
|
|
19
|
+
Array of indices of non-dominated points
|
|
20
|
+
"""
|
|
21
|
+
n_points = F.shape[0]
|
|
22
|
+
non_dominated_indices = []
|
|
23
|
+
|
|
24
|
+
if n_points == 0:
|
|
25
|
+
return np.array([], dtype=int)
|
|
26
|
+
|
|
27
|
+
# Check each point to see if it's non-dominated
|
|
28
|
+
for i in range(n_points):
|
|
29
|
+
is_dominated = False
|
|
30
|
+
|
|
31
|
+
# Check if point i is dominated by any other point j
|
|
32
|
+
for j in range(n_points):
|
|
33
|
+
if i != j:
|
|
34
|
+
# Check if j dominates i
|
|
35
|
+
dominates = True
|
|
36
|
+
at_least_one_better = False
|
|
37
|
+
|
|
38
|
+
for k in range(F.shape[1]): # for each objective
|
|
39
|
+
if F[j, k] + epsilon < F[i, k]: # j is better than i in objective k
|
|
40
|
+
at_least_one_better = True
|
|
41
|
+
elif F[j, k] > F[i, k] + epsilon: # j is worse than i in objective k
|
|
42
|
+
dominates = False
|
|
43
|
+
break # Early termination in objective loop
|
|
44
|
+
|
|
45
|
+
# j dominates i if j is at least as good in all objectives and better in at least one
|
|
46
|
+
if dominates and at_least_one_better:
|
|
47
|
+
is_dominated = True
|
|
48
|
+
break # Early termination - no need to check other points
|
|
49
|
+
|
|
50
|
+
# If point i is not dominated by any other point, it's non-dominated
|
|
51
|
+
if not is_dominated:
|
|
52
|
+
non_dominated_indices.append(i)
|
|
53
|
+
|
|
54
|
+
return np.array(non_dominated_indices, dtype=int)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from pymoo.util.dominator import Dominator
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def naive_non_dominated_sort(F, **kwargs):
|
|
5
|
+
M = Dominator.calc_domination_matrix(F)
|
|
6
|
+
|
|
7
|
+
fronts = []
|
|
8
|
+
remaining = set(range(M.shape[0]))
|
|
9
|
+
|
|
10
|
+
while len(remaining) > 0:
|
|
11
|
+
|
|
12
|
+
front = []
|
|
13
|
+
|
|
14
|
+
for i in remaining:
|
|
15
|
+
|
|
16
|
+
is_dominated = False
|
|
17
|
+
dominating = set()
|
|
18
|
+
|
|
19
|
+
for j in front:
|
|
20
|
+
rel = M[i, j]
|
|
21
|
+
if rel == 1:
|
|
22
|
+
dominating.add(j)
|
|
23
|
+
elif rel == -1:
|
|
24
|
+
is_dominated = True
|
|
25
|
+
break
|
|
26
|
+
|
|
27
|
+
if is_dominated:
|
|
28
|
+
continue
|
|
29
|
+
else:
|
|
30
|
+
front = [x for x in front if x not in dominating]
|
|
31
|
+
front.append(i)
|
|
32
|
+
|
|
33
|
+
[remaining.remove(e) for e in front]
|
|
34
|
+
fronts.append(front)
|
|
35
|
+
|
|
36
|
+
return fronts
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pymoo.functions import load_function
|
|
6
|
+
from pymoo.util.dominator import Dominator
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NonDominatedSorting:
|
|
10
|
+
|
|
11
|
+
def __init__(self, epsilon=None, method="fast_non_dominated_sort", dominator=None) -> None:
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.epsilon = epsilon
|
|
14
|
+
self.method = method
|
|
15
|
+
self.dominator = dominator
|
|
16
|
+
|
|
17
|
+
def do(self, F, return_rank=False, only_non_dominated_front=False, n_stop_if_ranked=None, n_fronts=None, **kwargs):
|
|
18
|
+
F = F.astype(float)
|
|
19
|
+
|
|
20
|
+
# if not set just set it to a very large values because the cython algorithms do not take None
|
|
21
|
+
if n_stop_if_ranked is None:
|
|
22
|
+
n_stop_if_ranked = int(1e8)
|
|
23
|
+
|
|
24
|
+
# if only_non_dominated_front is True, we only need 1 front
|
|
25
|
+
if only_non_dominated_front:
|
|
26
|
+
n_fronts = 1
|
|
27
|
+
elif n_fronts is None:
|
|
28
|
+
n_fronts = int(1e8)
|
|
29
|
+
|
|
30
|
+
# if a custom dominator is provided, use the custom dominator and run fast_non_dominated_sort
|
|
31
|
+
if self.dominator is not None:
|
|
32
|
+
# Use the custom dominator directly
|
|
33
|
+
from pymoo.util.nds.fast_non_dominated_sort import fast_non_dominated_sort
|
|
34
|
+
fronts = fast_non_dominated_sort(F, dominator=self.dominator, **kwargs)
|
|
35
|
+
else:
|
|
36
|
+
# Use the standard function loader approach
|
|
37
|
+
func = load_function(self.method)
|
|
38
|
+
|
|
39
|
+
# set the epsilon if it should be set
|
|
40
|
+
if self.epsilon is not None:
|
|
41
|
+
kwargs["epsilon"] = float(self.epsilon)
|
|
42
|
+
|
|
43
|
+
# add n_fronts parameter if the method supports it
|
|
44
|
+
if self.method == "fast_non_dominated_sort":
|
|
45
|
+
kwargs["n_fronts"] = n_fronts
|
|
46
|
+
kwargs["n_stop_if_ranked"] = n_stop_if_ranked
|
|
47
|
+
|
|
48
|
+
fronts = func(F, **kwargs)
|
|
49
|
+
|
|
50
|
+
# convert to numpy array for each front and filter by n_stop_if_ranked
|
|
51
|
+
_fronts = []
|
|
52
|
+
n_ranked = 0
|
|
53
|
+
for front in fronts:
|
|
54
|
+
|
|
55
|
+
_fronts.append(np.array(front, dtype=int))
|
|
56
|
+
|
|
57
|
+
# increment the n_ranked solution counter
|
|
58
|
+
n_ranked += len(front)
|
|
59
|
+
|
|
60
|
+
# stop if more solutions than n_ranked are ranked
|
|
61
|
+
if n_ranked >= n_stop_if_ranked:
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
fronts = _fronts
|
|
65
|
+
|
|
66
|
+
if only_non_dominated_front:
|
|
67
|
+
return fronts[0]
|
|
68
|
+
|
|
69
|
+
if return_rank:
|
|
70
|
+
rank = rank_from_fronts(fronts, F.shape[0])
|
|
71
|
+
return fronts, rank
|
|
72
|
+
|
|
73
|
+
return fronts
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def rank_from_fronts(fronts, n):
|
|
77
|
+
# create the rank array and set values
|
|
78
|
+
rank = np.full(n, sys.maxsize, dtype=int)
|
|
79
|
+
for i, front in enumerate(fronts):
|
|
80
|
+
rank[front] = i
|
|
81
|
+
|
|
82
|
+
return rank
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Returns all indices of F that are not dominated by the other objective values
|
|
86
|
+
def find_non_dominated(F, _F=None, func=load_function("find_non_dominated")):
|
|
87
|
+
if _F is None:
|
|
88
|
+
indices = func(F.astype(float))
|
|
89
|
+
return np.array(indices, dtype=int)
|
|
90
|
+
else:
|
|
91
|
+
# Fallback to the matrix-based approach when _F is provided
|
|
92
|
+
M = Dominator.calc_domination_matrix(F, _F)
|
|
93
|
+
I = np.where(np.all(M >= 0, axis=1))[0]
|
|
94
|
+
return I
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import weakref
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Tree:
|
|
8
|
+
'''
|
|
9
|
+
Implementation of Nary-tree.
|
|
10
|
+
The source code is modified based on https://github.com/lianemeth/forest/blob/master/forest/NaryTree.py
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
key: object
|
|
15
|
+
key of the node
|
|
16
|
+
num_branch: int
|
|
17
|
+
how many branches in each node
|
|
18
|
+
children: Iterable[Tree]
|
|
19
|
+
reference of the children
|
|
20
|
+
parent: Tree
|
|
21
|
+
reference of the parent node
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
an N-ary tree.
|
|
25
|
+
'''
|
|
26
|
+
|
|
27
|
+
def __init__(self, key, num_branch, children=None, parent=None):
|
|
28
|
+
self.key = key
|
|
29
|
+
self.children = children or [None for _ in range(num_branch)]
|
|
30
|
+
|
|
31
|
+
self._parent = weakref.ref(parent) if parent else None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def parent(self):
|
|
35
|
+
if self._parent:
|
|
36
|
+
return self._parent()
|
|
37
|
+
|
|
38
|
+
def __getstate__(self):
|
|
39
|
+
self._parent = None
|
|
40
|
+
|
|
41
|
+
def __setstate__(self, state):
|
|
42
|
+
self.__dict__ = state
|
|
43
|
+
for child in self.children:
|
|
44
|
+
child._parent = weakref.ref(self)
|
|
45
|
+
|
|
46
|
+
def traversal(self, visit=None, *args, **kwargs):
|
|
47
|
+
if visit is not None:
|
|
48
|
+
visit(self, *args, **kwargs)
|
|
49
|
+
l = [self]
|
|
50
|
+
for child in self.children:
|
|
51
|
+
if child is not None:
|
|
52
|
+
l += child.traversal(visit, *args, **kwargs)
|
|
53
|
+
return l
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def tree_based_non_dominated_sort(F):
|
|
57
|
+
"""
|
|
58
|
+
Tree-based efficient non-dominated sorting (T-ENS).
|
|
59
|
+
This algorithm is very efficient in many-objective optimization problems (MaOPs).
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
F: np.array
|
|
63
|
+
objective values for each individual.
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
indices of the individuals in each front.
|
|
67
|
+
References
|
|
68
|
+
----------
|
|
69
|
+
X. Zhang, Y. Tian, R. Cheng, and Y. Jin,
|
|
70
|
+
A decision variable clustering based evolutionary algorithm for large-scale many-objective optimization,
|
|
71
|
+
IEEE Transactions on Evolutionary Computation, 2018, 22(1): 97-112.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
N, M = F.shape
|
|
75
|
+
# sort the rows in F
|
|
76
|
+
indices = np.lexsort(F.T[::-1])
|
|
77
|
+
F = F[indices]
|
|
78
|
+
|
|
79
|
+
obj_seq = np.argsort(F[:, :0:-1], axis=1) + 1
|
|
80
|
+
|
|
81
|
+
k = 0
|
|
82
|
+
|
|
83
|
+
forest = []
|
|
84
|
+
|
|
85
|
+
left = np.full(N, True)
|
|
86
|
+
while np.any(left):
|
|
87
|
+
forest.append(None)
|
|
88
|
+
for p, flag in enumerate(left):
|
|
89
|
+
if flag:
|
|
90
|
+
update_tree(F, p, forest, k, left, obj_seq)
|
|
91
|
+
k += 1
|
|
92
|
+
|
|
93
|
+
# convert forest to fronts
|
|
94
|
+
fronts = [[] for _ in range(k)]
|
|
95
|
+
for k, tree in enumerate(forest):
|
|
96
|
+
fronts[k].extend([indices[node.key] for node in tree.traversal()])
|
|
97
|
+
return fronts
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def update_tree(F, p, forest, k, left, obj_seq):
|
|
101
|
+
_, M = F.shape
|
|
102
|
+
if forest[k] is None:
|
|
103
|
+
forest[k] = Tree(key=p, num_branch=M - 1)
|
|
104
|
+
left[p] = False
|
|
105
|
+
elif check_tree(F, p, forest[k], obj_seq, True):
|
|
106
|
+
left[p] = False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def check_tree(F, p, tree, obj_seq, add_pos):
|
|
110
|
+
if tree is None:
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
N, M = F.shape
|
|
114
|
+
|
|
115
|
+
# find the minimal index m satisfying that p[obj_seq[tree.root][m]] < tree.root[obj_seq[tree.root][m]]
|
|
116
|
+
m = 0
|
|
117
|
+
while m < M - 1 and F[p, obj_seq[tree.key, m]] >= F[tree.key, obj_seq[tree.key, m]]:
|
|
118
|
+
m += 1
|
|
119
|
+
|
|
120
|
+
# if m not found
|
|
121
|
+
if m == M - 1:
|
|
122
|
+
# p is dominated by the solution at the root
|
|
123
|
+
return False
|
|
124
|
+
else:
|
|
125
|
+
for i in range(m + 1):
|
|
126
|
+
# p is dominated by a solution in the branch of the tree
|
|
127
|
+
if not check_tree(F, p, tree.children[i], obj_seq, i == m and add_pos):
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
if tree.children[m] is None and add_pos:
|
|
131
|
+
# add p to the branch of the tree
|
|
132
|
+
tree.children[m] = Tree(key=p, num_branch=M - 1)
|
|
133
|
+
return True
|