pymoo 0.6.1.5.dev0__cp311-cp311-macosx_11_0_arm64.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-311-darwin.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-311-darwin.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-311-darwin.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-311-darwin.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-311-darwin.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-311-darwin.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-311-darwin.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-311-darwin.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
pymoo/util/misc.py
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from itertools import combinations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from pymoo.core.population import Population
|
|
9
|
+
from pymoo.core.sampling import Sampling
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parameter_less(F, CV, fmax=None, inplace=False):
|
|
14
|
+
assert len(F) == len(CV)
|
|
15
|
+
|
|
16
|
+
if not inplace:
|
|
17
|
+
F = np.copy(F)
|
|
18
|
+
|
|
19
|
+
if fmax is None:
|
|
20
|
+
fmax = np.max(F)
|
|
21
|
+
|
|
22
|
+
param_less = fmax + CV
|
|
23
|
+
|
|
24
|
+
infeas = (CV > 0).flatten()
|
|
25
|
+
F[infeas] = param_less[infeas]
|
|
26
|
+
|
|
27
|
+
return F
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def swap(M, a, b):
|
|
31
|
+
tmp = M[a]
|
|
32
|
+
M[a] = M[b]
|
|
33
|
+
M[b] = tmp
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# repairs a numpy array to be in bounds
|
|
37
|
+
def repair(X, xl, xu):
|
|
38
|
+
larger_than_xu = X[0, :] > xu
|
|
39
|
+
X[0, larger_than_xu] = xu[larger_than_xu]
|
|
40
|
+
|
|
41
|
+
smaller_than_xl = X[0, :] < xl
|
|
42
|
+
X[0, smaller_than_xl] = xl[smaller_than_xl]
|
|
43
|
+
|
|
44
|
+
return X
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def unique_rows(a):
|
|
48
|
+
a = np.ascontiguousarray(a)
|
|
49
|
+
unique_a = np.unique(a.view([('', a.dtype)] * a.shape[1]))
|
|
50
|
+
return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parameter_less_constraints(F, CV, F_max=None):
|
|
54
|
+
if F_max is None:
|
|
55
|
+
F_max = np.max(F)
|
|
56
|
+
has_constraint_violation = CV > 0
|
|
57
|
+
F[has_constraint_violation] = CV[has_constraint_violation] + F_max
|
|
58
|
+
return F
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def random_permutations(n, l, concat=True):
|
|
62
|
+
P = []
|
|
63
|
+
for i in range(n):
|
|
64
|
+
P.append(np.random.permutation(l))
|
|
65
|
+
if concat:
|
|
66
|
+
P = np.concatenate(P)
|
|
67
|
+
return P
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_duplicates(M):
|
|
71
|
+
res = []
|
|
72
|
+
I = np.lexsort([M[:, i] for i in reversed(range(0, M.shape[1]))])
|
|
73
|
+
S = M[I, :]
|
|
74
|
+
|
|
75
|
+
i = 0
|
|
76
|
+
|
|
77
|
+
while i < S.shape[0] - 1:
|
|
78
|
+
l = []
|
|
79
|
+
while np.all(S[i, :] == S[i + 1, :]):
|
|
80
|
+
l.append(I[i])
|
|
81
|
+
i += 1
|
|
82
|
+
if len(l) > 0:
|
|
83
|
+
l.append(I[i])
|
|
84
|
+
res.append(l)
|
|
85
|
+
i += 1
|
|
86
|
+
|
|
87
|
+
return res
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# -----------------------------------------------
|
|
91
|
+
# Euclidean Distance
|
|
92
|
+
# -----------------------------------------------
|
|
93
|
+
|
|
94
|
+
def func_euclidean_distance(a, b):
|
|
95
|
+
return np.sqrt(((a - b) ** 2).sum(axis=1))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def func_norm_euclidean_distance(xl, xu):
|
|
99
|
+
return lambda a, b: np.sqrt((((a - b) / (xu - xl)) ** 2).sum(axis=1))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def norm_eucl_dist_by_bounds(A, B, xl, xu, **kwargs):
|
|
103
|
+
return vectorized_cdist(A, B, func_dist=func_norm_euclidean_distance(xl, xu), **kwargs)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def norm_eucl_dist(problem, A, B, **kwargs):
|
|
107
|
+
return norm_eucl_dist_by_bounds(A, B, *problem.bounds(), **kwargs)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# -----------------------------------------------
|
|
111
|
+
# Manhatten Distance
|
|
112
|
+
# -----------------------------------------------
|
|
113
|
+
|
|
114
|
+
def func_manhatten_distance(a, b):
|
|
115
|
+
return np.abs(a - b).sum(axis=1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def func_norm_manhatten_distance(xl, xu):
|
|
119
|
+
return lambda a, b: np.abs((a - b) / (xu - xl)).sum(axis=1)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def norm_manhatten_dist_by_bounds(A, B, xl, xu, **kwargs):
|
|
123
|
+
return vectorized_cdist(A, B, func_dist=func_norm_manhatten_distance(xl, xu), **kwargs)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def norm_manhatten_dist(problem, A, B, **kwargs):
|
|
127
|
+
return norm_manhatten_dist_by_bounds(A, B, *problem.bounds(), **kwargs)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# -----------------------------------------------
|
|
131
|
+
# Tchebychev Distance
|
|
132
|
+
# -----------------------------------------------
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def func_tchebychev_distance(a, b):
|
|
136
|
+
return np.abs(a - b).max(axis=1)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def func_norm_tchebychev_distance(xl, xu):
|
|
140
|
+
return lambda a, b: np.abs((a - b) / (xu - xl)).max(axis=1)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def norm_tchebychev_dist_by_bounds(A, B, xl, xu, **kwargs):
|
|
144
|
+
return vectorized_cdist(A, B, func_dist=func_norm_tchebychev_distance(xl, xu), **kwargs)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def norm_tchebychev_dist(problem, A, B, **kwargs):
|
|
148
|
+
return norm_tchebychev_dist_by_bounds(A, B, *problem.bounds(), **kwargs)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# -----------------------------------------------
|
|
152
|
+
# Others
|
|
153
|
+
# -----------------------------------------------
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def cdist(A, B, **kwargs):
|
|
157
|
+
from scipy.spatial import distance
|
|
158
|
+
return distance.cdist(A.astype(float), B.astype(float), **kwargs)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def vectorized_cdist(A, B, func_dist=func_euclidean_distance, fill_diag_with_inf=False, **kwargs) -> object:
|
|
162
|
+
assert A.ndim <= 2 and B.ndim <= 2
|
|
163
|
+
|
|
164
|
+
A, only_row = at_least_2d_array(A, extend_as="row", return_if_reshaped=True)
|
|
165
|
+
B, only_column = at_least_2d_array(B, extend_as="row", return_if_reshaped=True)
|
|
166
|
+
|
|
167
|
+
u = np.repeat(A, B.shape[0], axis=0)
|
|
168
|
+
v = np.tile(B, (A.shape[0], 1))
|
|
169
|
+
|
|
170
|
+
D = func_dist(u, v, **kwargs)
|
|
171
|
+
M = np.reshape(D, (A.shape[0], B.shape[0]))
|
|
172
|
+
|
|
173
|
+
if fill_diag_with_inf:
|
|
174
|
+
np.fill_diagonal(M, np.inf)
|
|
175
|
+
|
|
176
|
+
if only_row and only_column:
|
|
177
|
+
M = M[0, 0]
|
|
178
|
+
elif only_row:
|
|
179
|
+
M = M[0]
|
|
180
|
+
elif only_column:
|
|
181
|
+
M = M[:, [0]]
|
|
182
|
+
|
|
183
|
+
return M
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def covert_to_type(problem, X):
|
|
187
|
+
if problem.vtype == float:
|
|
188
|
+
return X.astype(np.double)
|
|
189
|
+
elif problem.vtype == int:
|
|
190
|
+
return np.round(X).astype(int)
|
|
191
|
+
elif problem.vtype == bool:
|
|
192
|
+
return X < (problem.xu - problem.xl) / 2
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def find_duplicates(X, epsilon=1e-16):
|
|
196
|
+
# calculate the distance matrix from each point to another
|
|
197
|
+
D = cdist(X, X)
|
|
198
|
+
|
|
199
|
+
# set the diagonal to infinity
|
|
200
|
+
D[np.triu_indices(len(X))] = np.inf
|
|
201
|
+
|
|
202
|
+
# set as duplicate if a point is really close to this one
|
|
203
|
+
is_duplicate = np.any(D <= epsilon, axis=1)
|
|
204
|
+
|
|
205
|
+
return is_duplicate
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def at_least_2d(*args, **kwargs):
|
|
209
|
+
ret = tuple([at_least_2d_array(arg, **kwargs) for arg in args])
|
|
210
|
+
if len(ret) == 1:
|
|
211
|
+
ret = ret[0]
|
|
212
|
+
return ret
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def at_least_2d_array(x, extend_as="row", return_if_reshaped=False):
|
|
216
|
+
if x is None:
|
|
217
|
+
return x
|
|
218
|
+
elif not isinstance(x, np.ndarray):
|
|
219
|
+
x = np.array([x])
|
|
220
|
+
|
|
221
|
+
has_been_reshaped = False
|
|
222
|
+
|
|
223
|
+
if x.ndim == 1:
|
|
224
|
+
if extend_as.startswith("r"):
|
|
225
|
+
x = x[None, :]
|
|
226
|
+
elif extend_as.startswith("c"):
|
|
227
|
+
x = x[:, None]
|
|
228
|
+
else:
|
|
229
|
+
raise Exception("The option `extend_as` should be either `row` or `column`.")
|
|
230
|
+
|
|
231
|
+
has_been_reshaped = True
|
|
232
|
+
|
|
233
|
+
if return_if_reshaped:
|
|
234
|
+
return x, has_been_reshaped
|
|
235
|
+
else:
|
|
236
|
+
return x
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def to_1d_array_if_possible(x):
|
|
240
|
+
if not isinstance(x, np.ndarray):
|
|
241
|
+
x = np.array([x])
|
|
242
|
+
|
|
243
|
+
if x.ndim == 2:
|
|
244
|
+
if x.shape[0] == 1 or x.shape[1] == 1:
|
|
245
|
+
x = x.flatten()
|
|
246
|
+
|
|
247
|
+
return x
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def stack(*args, flatten=True):
|
|
251
|
+
if not flatten:
|
|
252
|
+
ps = np.concatenate([e[None, ...] for e in args])
|
|
253
|
+
else:
|
|
254
|
+
ps = np.row_stack(args)
|
|
255
|
+
return ps
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def all_except(x, *args):
|
|
259
|
+
if len(args) == 0:
|
|
260
|
+
return x
|
|
261
|
+
else:
|
|
262
|
+
H = set(args) if len(args) > 5 else args
|
|
263
|
+
I = [k for k in range(len(x)) if k not in H]
|
|
264
|
+
return x[I]
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def all_combinations(A, B):
|
|
268
|
+
u = np.repeat(A, B.shape[0], axis=0)
|
|
269
|
+
v = np.tile(B, A.shape[0])
|
|
270
|
+
return np.column_stack([u, v])
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def pop_from_sampling(problem, sampling, n_initial_samples, pop=None):
|
|
274
|
+
# the population type can be different - (different type of individuals)
|
|
275
|
+
if pop is None:
|
|
276
|
+
pop = Population()
|
|
277
|
+
|
|
278
|
+
# provide a whole population object - (individuals might be already evaluated)
|
|
279
|
+
if isinstance(sampling, Population):
|
|
280
|
+
pop = sampling
|
|
281
|
+
|
|
282
|
+
else:
|
|
283
|
+
# if just an X array create a pop
|
|
284
|
+
if isinstance(sampling, np.ndarray):
|
|
285
|
+
pop = pop.new("X", sampling)
|
|
286
|
+
|
|
287
|
+
elif isinstance(sampling, Sampling):
|
|
288
|
+
# use the sampling
|
|
289
|
+
pop = sampling.do(problem, n_initial_samples, pop=pop)
|
|
290
|
+
|
|
291
|
+
else:
|
|
292
|
+
return None
|
|
293
|
+
|
|
294
|
+
return pop
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def evaluate_if_not_done_yet(evaluator, problem, pop, algorithm=None):
|
|
298
|
+
I = np.where(pop.get("F") == None)[0]
|
|
299
|
+
if len(I) > 0:
|
|
300
|
+
pop[I] = evaluator.process(problem, pop[I], algorithm=algorithm)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def set_if_none(kwargs, str, val):
|
|
304
|
+
if str not in kwargs:
|
|
305
|
+
kwargs[str] = val
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def set_if_none_from_tuples(kwargs, *args):
|
|
309
|
+
for key, val in args:
|
|
310
|
+
if key not in kwargs:
|
|
311
|
+
kwargs[key] = val
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def calc_perpendicular_distance(N, ref_dirs):
|
|
315
|
+
u = np.tile(ref_dirs, (len(N), 1))
|
|
316
|
+
v = np.repeat(N, len(ref_dirs), axis=0)
|
|
317
|
+
|
|
318
|
+
norm_u = np.linalg.norm(u, axis=1)
|
|
319
|
+
|
|
320
|
+
scalar_proj = np.sum(v * u, axis=1) / norm_u
|
|
321
|
+
proj = scalar_proj[:, None] * u / norm_u[:, None]
|
|
322
|
+
val = np.linalg.norm(proj - v, axis=1)
|
|
323
|
+
matrix = np.reshape(val, (len(N), len(ref_dirs)))
|
|
324
|
+
|
|
325
|
+
return matrix
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def distance_of_closest_points_to_others(X):
|
|
329
|
+
D = vectorized_cdist(X, X)
|
|
330
|
+
np.fill_diagonal(D, np.inf)
|
|
331
|
+
return D.argmin(axis=1), D.min(axis=1)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def time_to_int(t):
|
|
335
|
+
vals = [int(e) for e in t.split(":")][::-1]
|
|
336
|
+
s = vals[0]
|
|
337
|
+
if len(vals) > 1:
|
|
338
|
+
s += 60 * vals[1]
|
|
339
|
+
if len(vals) > 2:
|
|
340
|
+
s += 3600 * vals[2]
|
|
341
|
+
return s
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def powerset(iterable):
|
|
345
|
+
for n in range(len(iterable) + 1):
|
|
346
|
+
yield from combinations(iterable, n)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def intersect(a, b):
|
|
350
|
+
H = set()
|
|
351
|
+
for entry in b:
|
|
352
|
+
H.add(entry)
|
|
353
|
+
|
|
354
|
+
ret = []
|
|
355
|
+
for entry in a:
|
|
356
|
+
if entry in H:
|
|
357
|
+
ret.append(entry)
|
|
358
|
+
|
|
359
|
+
return ret
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def has_feasible(pop):
|
|
363
|
+
return np.any(pop.get("FEAS"))
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def to_numpy(a):
|
|
367
|
+
return np.array(a)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def termination_from_tuple(termination):
|
|
371
|
+
from pymoo.core.termination import Termination
|
|
372
|
+
|
|
373
|
+
# get the termination if provided as a tuple - create an object
|
|
374
|
+
if termination is not None and not isinstance(termination, Termination):
|
|
375
|
+
from pymoo.termination import get_termination
|
|
376
|
+
if isinstance(termination, str):
|
|
377
|
+
termination = get_termination(termination)
|
|
378
|
+
else:
|
|
379
|
+
termination = get_termination(*termination)
|
|
380
|
+
|
|
381
|
+
return termination
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def unique_and_all_indices(arr):
|
|
385
|
+
sort_indexes = np.argsort(arr)
|
|
386
|
+
arr = np.asarray(arr)[sort_indexes]
|
|
387
|
+
vals, first_indexes, inverse, counts = np.unique(arr,
|
|
388
|
+
return_index=True, return_inverse=True, return_counts=True)
|
|
389
|
+
indexes = np.split(sort_indexes, first_indexes[1:])
|
|
390
|
+
for x in indexes:
|
|
391
|
+
x.sort()
|
|
392
|
+
return vals, indexes
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def from_dict(D, *keys):
|
|
396
|
+
return [D.get(k) for k in keys]
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def list_of_dicts_unique(l, k):
|
|
400
|
+
return list(OrderedDict([(e[k], None) for e in l]).keys())
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def list_of_dicts_filter(l, *pairs):
|
|
404
|
+
return [e for e in l if all(e[k] == v for (k, v) in pairs)]
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def logical_op(func, a, b, *args):
|
|
408
|
+
ret = func(a, b)
|
|
409
|
+
for c in args:
|
|
410
|
+
ret = func(ret, c)
|
|
411
|
+
return ret
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def replace_nan_by(x, val, inplace=False):
|
|
415
|
+
is_nan = np.isnan(x)
|
|
416
|
+
if np.sum(is_nan) > 0:
|
|
417
|
+
if not inplace:
|
|
418
|
+
x = x.copy()
|
|
419
|
+
x[is_nan] = val
|
|
420
|
+
return x
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def set_defaults(kwargs, defaults, overwrite=False, func_get=lambda x: x):
|
|
424
|
+
for k, v in defaults.items():
|
|
425
|
+
if overwrite or k not in kwargs:
|
|
426
|
+
kwargs[k] = func_get(v)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def filter_params(params, prefix, delete_prefix=True):
|
|
430
|
+
ret = {}
|
|
431
|
+
for k, v in params.items():
|
|
432
|
+
if k.startswith(prefix):
|
|
433
|
+
if delete_prefix:
|
|
434
|
+
k = k[len(prefix):]
|
|
435
|
+
ret[k] = v
|
|
436
|
+
return ret
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def where_is_what(x):
|
|
440
|
+
H = {}
|
|
441
|
+
for k, e in enumerate(x):
|
|
442
|
+
if e not in H:
|
|
443
|
+
H[e] = []
|
|
444
|
+
H[e].append(k)
|
|
445
|
+
return H
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def crossover_mask(X, M):
|
|
449
|
+
# convert input to output by flatting along the first axis
|
|
450
|
+
_X = np.copy(X)
|
|
451
|
+
_X[0][M] = X[1][M]
|
|
452
|
+
_X[1][M] = X[0][M]
|
|
453
|
+
return _X
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def row_at_least_once_true(M):
|
|
457
|
+
_, d = M.shape
|
|
458
|
+
for k in np.where(~np.any(M, axis=1))[0]:
|
|
459
|
+
M[k, np.random.randint(d)] = True
|
|
460
|
+
return M
|
pymoo/util/mnn.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.spatial.distance import pdist, squareform
|
|
3
|
+
|
|
4
|
+
def calc_mnn(X, n_remove=0):
|
|
5
|
+
return calc_mnn_base(X, n_remove=n_remove, twonn=False)
|
|
6
|
+
|
|
7
|
+
def calc_2nn(X, n_remove=0):
|
|
8
|
+
return calc_mnn_base(X, n_remove=n_remove, twonn=True)
|
|
9
|
+
|
|
10
|
+
def calc_mnn_base(X, n_remove=0, twonn=False):
|
|
11
|
+
|
|
12
|
+
N = X.shape[0]
|
|
13
|
+
M = X.shape[1]
|
|
14
|
+
|
|
15
|
+
if N <= M:
|
|
16
|
+
return np.full(N, np.inf)
|
|
17
|
+
|
|
18
|
+
if n_remove <= (N - M):
|
|
19
|
+
if n_remove < 0:
|
|
20
|
+
n_remove = 0
|
|
21
|
+
else:
|
|
22
|
+
pass
|
|
23
|
+
else:
|
|
24
|
+
n_remove = N - M
|
|
25
|
+
|
|
26
|
+
if twonn:
|
|
27
|
+
M = 2
|
|
28
|
+
|
|
29
|
+
extremes_min = np.argmin(X, axis=0)
|
|
30
|
+
extremes_max = np.argmax(X, axis=0)
|
|
31
|
+
|
|
32
|
+
min_vals = np.min(X, axis=0)
|
|
33
|
+
max_vals = np.max(X, axis=0)
|
|
34
|
+
|
|
35
|
+
extremes = np.concatenate((extremes_min, extremes_max))
|
|
36
|
+
|
|
37
|
+
X = (X - min_vals) / (max_vals - min_vals)
|
|
38
|
+
|
|
39
|
+
H = np.arange(N)
|
|
40
|
+
|
|
41
|
+
D = squareform(pdist(X, metric="sqeuclidean"))
|
|
42
|
+
Dnn = np.partition(D, range(1, M+1), axis=1)[:, 1:M+1]
|
|
43
|
+
d = np.prod(Dnn, axis=1)
|
|
44
|
+
d[extremes] = np.inf
|
|
45
|
+
|
|
46
|
+
n_removed = 0
|
|
47
|
+
|
|
48
|
+
#While n_remove not acheived
|
|
49
|
+
while n_removed < (n_remove - 1):
|
|
50
|
+
|
|
51
|
+
#Obtain element to drop
|
|
52
|
+
_d = d[H]
|
|
53
|
+
_k = np.argmin(_d)
|
|
54
|
+
k = H[_k]
|
|
55
|
+
H = H[H != k]
|
|
56
|
+
|
|
57
|
+
#Update index
|
|
58
|
+
n_removed = n_removed + 1
|
|
59
|
+
if n_removed == n_remove:
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
|
|
64
|
+
D[:, k] = np.inf
|
|
65
|
+
Dnn[H] = np.partition(D[H], range(1, M+1), axis=1)[:, 1:M+1]
|
|
66
|
+
d[H] = np.prod(Dnn[H], axis=1)
|
|
67
|
+
d[extremes] = np.inf
|
|
68
|
+
|
|
69
|
+
return d
|
|
70
|
+
|
|
File without changes
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Module which implements Dominance Degree Approaches for Non-dominated Sorting.
|
|
2
|
+
|
|
3
|
+
For the original work see:
|
|
4
|
+
DDA-NS https://ieeexplore.ieee.org/document/7469397
|
|
5
|
+
DDA-ENS https://ieeexplore.ieee.org/document/9282978
|
|
6
|
+
|
|
7
|
+
Adapted from https://github.com/rsenwar/Non-Dominated-Sorting-Algorithms/tree/master
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
from typing import Literal, List
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def construct_comp_matrix(vec: np.ndarray, sorted_idx: np.ndarray) -> np.ndarray:
|
|
16
|
+
"""
|
|
17
|
+
const_comp_mat construct the comparison matrix from a row-vector vec.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
vec : np.ndarray
|
|
22
|
+
The vector of scores for the population on a single objective
|
|
23
|
+
sorted_idx : np.ndarray
|
|
24
|
+
The indices which would sort `vec`
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
np.ndarray
|
|
29
|
+
The comparison matrix indicating whether each member in the population dominates the other member for the
|
|
30
|
+
objective in `vec`
|
|
31
|
+
"""
|
|
32
|
+
n = vec.shape[0]
|
|
33
|
+
c = np.zeros(shape=(n, n), dtype=np.int32)
|
|
34
|
+
|
|
35
|
+
# the elements of the b(0)-th row in C are all set to 1
|
|
36
|
+
c[sorted_idx[0], :] = 1
|
|
37
|
+
|
|
38
|
+
for i in range(1, n):
|
|
39
|
+
if vec[sorted_idx[i]] == vec[sorted_idx[i - 1]]:
|
|
40
|
+
# the rows in C corresponding to the same elements in w are identical
|
|
41
|
+
c[sorted_idx[i]] = c[sorted_idx[i - 1]]
|
|
42
|
+
else:
|
|
43
|
+
c[sorted_idx[i], sorted_idx[i:]] = 1
|
|
44
|
+
|
|
45
|
+
return c
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def construct_domination_matrix(f_scores: np.ndarray, **kwargs) -> np.ndarray:
|
|
49
|
+
"""
|
|
50
|
+
construct_domination_matrix calculates the dominance degree matrix for a set of vectors.
|
|
51
|
+
|
|
52
|
+
The dominance degree indicate the degree of dominance of a solution, which is the number of
|
|
53
|
+
objectives for which it is the dominating solution.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
f_scores : np.ndarray
|
|
58
|
+
an N x M matrix of N (population size) objective function values for M objectives
|
|
59
|
+
"""
|
|
60
|
+
d = np.zeros((f_scores.shape[0], f_scores.shape[0]), dtype=np.int32)
|
|
61
|
+
b = np.apply_over_axes(np.argsort, f_scores, axes=0)
|
|
62
|
+
for vec, srt in zip(f_scores.T, b.T):
|
|
63
|
+
d += construct_comp_matrix(vec, srt)
|
|
64
|
+
d = np.where(
|
|
65
|
+
np.logical_and(d == f_scores.shape[-1], d.T == f_scores.shape[-1]), 0, d
|
|
66
|
+
)
|
|
67
|
+
return d
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def dda_ns(f_scores: np.ndarray, **kwargs) -> List[List[int]]:
|
|
71
|
+
"""
|
|
72
|
+
dda_ns runs the DDA-NS algorithm.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
f_scores : np.ndarray
|
|
77
|
+
an N x M matrix of N (population size) objective function values for M objectives
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
List[List[int]]
|
|
82
|
+
A list of members of each Pareto front. The index in the outer most list corresponds to the level in the Pareto front
|
|
83
|
+
while the value in the inner-most list is the id of the member of the population belonging to that front.
|
|
84
|
+
"""
|
|
85
|
+
d_mx = construct_domination_matrix(f_scores)
|
|
86
|
+
max_d = np.empty((f_scores.shape[0],), dtype=np.int32)
|
|
87
|
+
|
|
88
|
+
fronts = []
|
|
89
|
+
count = 0
|
|
90
|
+
while count < f_scores.shape[0]:
|
|
91
|
+
# Max(D) is the row vector containing the maximum elements from each column of D
|
|
92
|
+
np.max(d_mx, out=max_d, axis=0)
|
|
93
|
+
front = [i for i, m_d in enumerate(max_d) if 0 <= m_d < f_scores.shape[-1]]
|
|
94
|
+
count += len(front)
|
|
95
|
+
d_mx[front] = -1
|
|
96
|
+
d_mx[:, front] = -1
|
|
97
|
+
fronts.append(front)
|
|
98
|
+
|
|
99
|
+
return fronts
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def dda_ens(f_scores: np.ndarray, **kwargs) -> List[List[int]]:
|
|
103
|
+
"""
|
|
104
|
+
dda_ens runs the DDA-ENS (efficient DDA) algorithm
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
f_scores : np.ndarray
|
|
109
|
+
The N x M matrix of N (population size) objective function values for M objectives
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
List[List[int]]
|
|
114
|
+
an N x M matrix of N (population size) objective function values for M objectives
|
|
115
|
+
"""
|
|
116
|
+
d_mx = construct_domination_matrix(f_scores)
|
|
117
|
+
|
|
118
|
+
fronts: List[List[int]] = []
|
|
119
|
+
for s in np.lexsort(f_scores.T):
|
|
120
|
+
isinserted = False
|
|
121
|
+
for fk in fronts:
|
|
122
|
+
if not (d_mx[fk, s] == f_scores.shape[1]).any():
|
|
123
|
+
fk.append(s)
|
|
124
|
+
isinserted = True
|
|
125
|
+
break
|
|
126
|
+
if not isinserted:
|
|
127
|
+
fronts.append([s])
|
|
128
|
+
return fronts
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def dominance_degree_non_dominated_sort(
|
|
132
|
+
f_scores: np.ndarray, strategy: Literal["efficient", "fast"] = "efficient"
|
|
133
|
+
) -> List[List[int]]:
|
|
134
|
+
"""
|
|
135
|
+
dominance_degree_non_dominated_sort performs the non-dominating sort with the specified algorithm
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
f_scores : np.ndarray
|
|
140
|
+
The N x M matrix of N (population size) objective function values for M objectives
|
|
141
|
+
strategy : Literal["efficient", "fast"], optional
|
|
142
|
+
The dominance degree algorithm to use, by default "efficient"
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
List[List[int]]
|
|
147
|
+
A list of members of each Pareto front. The index in the outer most list corresponds to the level in the Pareto front
|
|
148
|
+
while the value in the inner-most list is the id of the member of the population belonging to that front.
|
|
149
|
+
|
|
150
|
+
Raises
|
|
151
|
+
------
|
|
152
|
+
ValueError
|
|
153
|
+
If an invalid strategy is specified
|
|
154
|
+
"""
|
|
155
|
+
if strategy == "efficient":
|
|
156
|
+
return dda_ens(f_scores)
|
|
157
|
+
if strategy == "fast":
|
|
158
|
+
return dda_ns(f_scores)
|
|
159
|
+
raise ValueError("Invalid search strategy")
|