pymoo 0.6.1.5.dev0__cp310-cp310-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-310-darwin.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-310-darwin.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-310-darwin.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-310-darwin.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-310-darwin.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-310-darwin.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-310-darwin.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-310-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
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
7
|
+
from pymoo.algorithms.moo.nsga2 import binary_tournament
|
|
8
|
+
from pymoo.docs import parse_doc_string
|
|
9
|
+
from pymoo.operators.crossover.sbx import SBX
|
|
10
|
+
from pymoo.operators.mutation.pm import PM
|
|
11
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
12
|
+
from pymoo.operators.selection.tournament import TournamentSelection
|
|
13
|
+
from pymoo.operators.survival.rank_and_crowding import RankAndCrowding
|
|
14
|
+
from pymoo.termination.default import DefaultMultiObjectiveTermination
|
|
15
|
+
from pymoo.util import value_functions as mvf
|
|
16
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
17
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
18
|
+
from pymoo.util.reference_direction import select_points_with_maximum_distance
|
|
19
|
+
from pymoo.util.vf_dominator import VFDominator
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# =========================================================================================================
|
|
23
|
+
# Implementation
|
|
24
|
+
# =========================================================================================================
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AutomatedDM(ABC):
|
|
28
|
+
|
|
29
|
+
def __init__(self, get_pairwise_ranks_func=None):
|
|
30
|
+
self.get_pairwise_ranks_func = get_pairwise_ranks_func
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def makeDecision(self, F):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def makePairwiseDecision(self, F):
|
|
37
|
+
|
|
38
|
+
dm = lambda F: self.makeDecision(F)
|
|
39
|
+
ranks = self.get_pairwise_ranks_func(F, 1, dm=dm)
|
|
40
|
+
|
|
41
|
+
return ranks
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class PINSGA2(GeneticAlgorithm):
|
|
46
|
+
|
|
47
|
+
def __init__(self,
|
|
48
|
+
pop_size=100,
|
|
49
|
+
sampling=FloatRandomSampling(),
|
|
50
|
+
selection=TournamentSelection(func_comp=binary_tournament),
|
|
51
|
+
crossover=SBX(eta=15, prob=0.9),
|
|
52
|
+
mutation=PM(eta=20),
|
|
53
|
+
output=MultiObjectiveOutput(),
|
|
54
|
+
tau=10,
|
|
55
|
+
eta=4,
|
|
56
|
+
opt_method="trust-constr",
|
|
57
|
+
vf_type="poly",
|
|
58
|
+
eps_max=1000,
|
|
59
|
+
ranking_type='pairwise',
|
|
60
|
+
presi_signs=None,
|
|
61
|
+
automated_dm=None,
|
|
62
|
+
verbose=False,
|
|
63
|
+
**kwargs):
|
|
64
|
+
|
|
65
|
+
self.survival = RankAndCrowding(nds=NonDominatedSorting(dominator=VFDominator(self)))
|
|
66
|
+
|
|
67
|
+
super().__init__(
|
|
68
|
+
pop_size=pop_size,
|
|
69
|
+
sampling=sampling,
|
|
70
|
+
selection=selection,
|
|
71
|
+
crossover=crossover,
|
|
72
|
+
mutation=mutation,
|
|
73
|
+
survival=self.survival,
|
|
74
|
+
output=output,
|
|
75
|
+
advance_after_initial_infill=True,
|
|
76
|
+
**kwargs)
|
|
77
|
+
|
|
78
|
+
self.termination = DefaultMultiObjectiveTermination()
|
|
79
|
+
self.tournament_type = 'comp_by_dom_and_crowding'
|
|
80
|
+
|
|
81
|
+
self.ranking_type=ranking_type
|
|
82
|
+
self.presi_signs=presi_signs
|
|
83
|
+
|
|
84
|
+
self.vf_type = vf_type
|
|
85
|
+
self.opt_method = opt_method
|
|
86
|
+
self.tau = tau
|
|
87
|
+
self.eta = eta
|
|
88
|
+
self.eta_F = []
|
|
89
|
+
self.vf_res = None
|
|
90
|
+
self.v2 = None
|
|
91
|
+
self.vf_plot_flag = False
|
|
92
|
+
self.vf_plot = None
|
|
93
|
+
self.historical_F = None
|
|
94
|
+
self.prev_pop = None
|
|
95
|
+
self.fronts = []
|
|
96
|
+
self.eps_max = eps_max
|
|
97
|
+
|
|
98
|
+
self.verbose = verbose
|
|
99
|
+
|
|
100
|
+
if automated_dm is not None:
|
|
101
|
+
automated_dm.get_pairwise_ranks_func = self._get_pairwise_ranks
|
|
102
|
+
self.automated_dm = automated_dm
|
|
103
|
+
|
|
104
|
+
def _warn(self, msg):
|
|
105
|
+
if self.verbose:
|
|
106
|
+
sys.stderr.write(msg)
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def _prompt_for_ranks(F, presi_signs):
|
|
110
|
+
|
|
111
|
+
for (e, f) in enumerate(F):
|
|
112
|
+
print("Solution %d %s" % (e + 1, f * presi_signs))
|
|
113
|
+
|
|
114
|
+
dim = F.shape[0]
|
|
115
|
+
|
|
116
|
+
raw_ranks = input(f"Ranks (e.g., \"3, {dim}, ..., 1\" for 3rd best, {dim}th best, ..., 1st best): ")
|
|
117
|
+
|
|
118
|
+
if raw_ranks == "":
|
|
119
|
+
ranks = []
|
|
120
|
+
else:
|
|
121
|
+
ranks = [int(raw_rank) for raw_rank in raw_ranks.split() ]
|
|
122
|
+
|
|
123
|
+
return ranks
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def _present_ranks(F, dm_ranks, presi_signs):
|
|
127
|
+
|
|
128
|
+
print("Solutions are ranked as:")
|
|
129
|
+
|
|
130
|
+
for (e, f) in enumerate(F):
|
|
131
|
+
print("Solution %d %s: Rank %d" % (e + 1, f * presi_signs, dm_ranks[e]))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@staticmethod
|
|
135
|
+
def _get_pairwise_ranks(F, presi_signs, dm=None):
|
|
136
|
+
|
|
137
|
+
if not dm:
|
|
138
|
+
|
|
139
|
+
dm = lambda F: input("\nWhich solution do you like best?\n" + \
|
|
140
|
+
f"[a] {F[0]}\n" + \
|
|
141
|
+
f"[b] {F[1]}\n" + \
|
|
142
|
+
"[c] These solutions are equivalent.\n--> " )
|
|
143
|
+
|
|
144
|
+
# initialize empty ranking
|
|
145
|
+
_ranks = []
|
|
146
|
+
for i, f in enumerate( F ):
|
|
147
|
+
|
|
148
|
+
# handle empty case, put first element in first place
|
|
149
|
+
if not _ranks:
|
|
150
|
+
_ranks.append( [i] )
|
|
151
|
+
|
|
152
|
+
else:
|
|
153
|
+
inserted = False
|
|
154
|
+
|
|
155
|
+
# for each remaining elements, compare to all currently ranked elements
|
|
156
|
+
for j, group in enumerate( _ranks ):
|
|
157
|
+
|
|
158
|
+
# get pairwise preference from user
|
|
159
|
+
while True:
|
|
160
|
+
|
|
161
|
+
points_to_compare = np.array( [f*presi_signs, F[ group[0] ]*presi_signs] )
|
|
162
|
+
preference_raw = dm( points_to_compare )
|
|
163
|
+
|
|
164
|
+
preference = preference_raw.strip().lower()
|
|
165
|
+
|
|
166
|
+
if preference in ['a', 'b', 'c']:
|
|
167
|
+
break
|
|
168
|
+
print("Invalid input. Please enter 'a', 'b', or 'c'.")
|
|
169
|
+
|
|
170
|
+
# if better than currently ranked element place before that element
|
|
171
|
+
if preference == 'a':
|
|
172
|
+
_ranks.insert( j, [i] )
|
|
173
|
+
inserted = True
|
|
174
|
+
break
|
|
175
|
+
|
|
176
|
+
# if equal to currently ranked element place with that element
|
|
177
|
+
elif preference == 'c':
|
|
178
|
+
group.append( i )
|
|
179
|
+
inserted = True
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
# if found to be worse than all place at the end
|
|
183
|
+
if not inserted:
|
|
184
|
+
_ranks.append( [i] )
|
|
185
|
+
|
|
186
|
+
ranks = np.zeros ( len( F ), dtype=int )
|
|
187
|
+
|
|
188
|
+
for rank, group in enumerate( _ranks ):
|
|
189
|
+
for index in group:
|
|
190
|
+
ranks[index] = rank
|
|
191
|
+
|
|
192
|
+
return np.array( ranks ) + 1
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def _get_ranks(F, presi_signs):
|
|
197
|
+
|
|
198
|
+
ranks_invalid = True
|
|
199
|
+
|
|
200
|
+
dim = F.shape[0]
|
|
201
|
+
|
|
202
|
+
print(f"Give each solution a ranking, with 1 being the highest score, and {dim} being the lowest score:")
|
|
203
|
+
|
|
204
|
+
ranks = PINSGA2._prompt_for_ranks(F, presi_signs)
|
|
205
|
+
|
|
206
|
+
while ranks_invalid:
|
|
207
|
+
|
|
208
|
+
fc = F.shape[0]
|
|
209
|
+
|
|
210
|
+
if len(ranks) > 0 and max(ranks) <= fc and min(ranks) >= 1:
|
|
211
|
+
|
|
212
|
+
ranks_invalid = False
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
|
|
216
|
+
print("Invalid ranks given. Please try again")
|
|
217
|
+
|
|
218
|
+
ranks = PINSGA2._prompt_for_ranks(F, presi_signs)
|
|
219
|
+
|
|
220
|
+
return np.array(ranks);
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _reset_dm_preference(self):
|
|
224
|
+
|
|
225
|
+
self._warn("Back-tracking and removing DM preference from search.")
|
|
226
|
+
|
|
227
|
+
self.eta_F = []
|
|
228
|
+
self.vf_res = None
|
|
229
|
+
self.v2 = None
|
|
230
|
+
self.vf_plot_flag = False
|
|
231
|
+
self.vf_plot = None
|
|
232
|
+
self.pop = self.prev_pop
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _advance(self, infills=None, **kwargs):
|
|
236
|
+
|
|
237
|
+
super()._advance(infills=infills, **kwargs)
|
|
238
|
+
|
|
239
|
+
rank, F = self.pop.get("rank", "F")
|
|
240
|
+
|
|
241
|
+
self.fronts = rank
|
|
242
|
+
|
|
243
|
+
F = F[rank == 0]
|
|
244
|
+
|
|
245
|
+
if self.historical_F is not None:
|
|
246
|
+
self.historical_F = np.vstack((self.historical_F, F))
|
|
247
|
+
else:
|
|
248
|
+
self.historical_F = F
|
|
249
|
+
|
|
250
|
+
to_find = self.eta if F.shape[0] >= self.eta else F.shape[0]
|
|
251
|
+
|
|
252
|
+
if self.presi_signs is None:
|
|
253
|
+
self.presi_signs = np.ones(F.shape[1])
|
|
254
|
+
|
|
255
|
+
# Eta is the number of solutions displayed to the DM
|
|
256
|
+
eta_F_indices = select_points_with_maximum_distance(F, to_find)
|
|
257
|
+
|
|
258
|
+
self.eta_F = F[eta_F_indices]
|
|
259
|
+
self.eta_F = self.eta_F[self.eta_F[:,0].argsort()]
|
|
260
|
+
|
|
261
|
+
# Remove duplicate rows
|
|
262
|
+
self.eta_F = np.unique(self.eta_F, axis=0)
|
|
263
|
+
|
|
264
|
+
# A frozen view of the optimization each tau generations
|
|
265
|
+
self.paused_F = F
|
|
266
|
+
|
|
267
|
+
# Record the previous population in case we need to back track
|
|
268
|
+
self.prev_pop = self.pop
|
|
269
|
+
|
|
270
|
+
dm_time = self.n_gen % self.tau == 0
|
|
271
|
+
|
|
272
|
+
# Check whether we have more than one solution
|
|
273
|
+
if dm_time and len(self.eta_F) < 2:
|
|
274
|
+
|
|
275
|
+
self._warn("Population only contains one non-dominated solution. ")
|
|
276
|
+
|
|
277
|
+
self._reset_dm_preference()
|
|
278
|
+
|
|
279
|
+
elif dm_time:
|
|
280
|
+
|
|
281
|
+
# Check if the DM is a machine or a human
|
|
282
|
+
if self.automated_dm is None:
|
|
283
|
+
|
|
284
|
+
# Human DM
|
|
285
|
+
if self.ranking_type == "absolute":
|
|
286
|
+
dm_ranks = PINSGA2._get_ranks(self.eta_F, self.presi_signs)
|
|
287
|
+
elif self.ranking_type == "pairwise":
|
|
288
|
+
dm_ranks = PINSGA2._get_pairwise_ranks(self.eta_F, self.presi_signs)
|
|
289
|
+
PINSGA2._present_ranks(self.eta_F, dm_ranks, self.presi_signs)
|
|
290
|
+
else:
|
|
291
|
+
raise ValueError("Invalid ranking type [%s] given." % self.ranking_type)
|
|
292
|
+
else:
|
|
293
|
+
|
|
294
|
+
# Automated DM
|
|
295
|
+
if self.ranking_type == "absolute":
|
|
296
|
+
dm_ranks = self.automated_dm.makeDecision(self.eta_F)
|
|
297
|
+
elif self.ranking_type == "pairwise":
|
|
298
|
+
dm_ranks = self.automated_dm.makePairwiseDecision(self.eta_F)
|
|
299
|
+
else:
|
|
300
|
+
raise ValueError("Invalid ranking type [%s] given." % self.ranking_type)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if len(set(rank)) == 0:
|
|
305
|
+
|
|
306
|
+
self._warn("No preference between any two points provided.")
|
|
307
|
+
|
|
308
|
+
self._reset_dm_preference()
|
|
309
|
+
|
|
310
|
+
return
|
|
311
|
+
|
|
312
|
+
eta_F = self.eta_F
|
|
313
|
+
|
|
314
|
+
while eta_F.shape[0] > 1:
|
|
315
|
+
|
|
316
|
+
if self.vf_type == "linear":
|
|
317
|
+
|
|
318
|
+
vf_res = mvf.create_linear_vf(eta_F * -1,
|
|
319
|
+
dm_ranks.tolist(),
|
|
320
|
+
eps_max=self.eps_max,
|
|
321
|
+
method=self.opt_method)
|
|
322
|
+
|
|
323
|
+
elif self.vf_type == "poly":
|
|
324
|
+
|
|
325
|
+
vf_res = mvf.create_poly_vf(eta_F * -1,
|
|
326
|
+
dm_ranks.tolist(),
|
|
327
|
+
eps_max=self.eps_max,
|
|
328
|
+
method=self.opt_method)
|
|
329
|
+
|
|
330
|
+
else:
|
|
331
|
+
|
|
332
|
+
raise ValueError("Value function %s not supported" % self.vf_type)
|
|
333
|
+
|
|
334
|
+
# check if we were able to model the VF
|
|
335
|
+
if vf_res.fit:
|
|
336
|
+
|
|
337
|
+
self.vf_res = vf_res
|
|
338
|
+
self.vf_plot_flag = True
|
|
339
|
+
self.v2 = self.vf_res.vf(eta_F[dm_ranks[1] - 1] * -1).item()
|
|
340
|
+
break
|
|
341
|
+
|
|
342
|
+
else:
|
|
343
|
+
|
|
344
|
+
# If we didn't the model, try to remove the least preferred point and try to refit
|
|
345
|
+
self._warn("Could not fit a function to the DM preference")
|
|
346
|
+
|
|
347
|
+
if eta_F.shape[0] == 2:
|
|
348
|
+
|
|
349
|
+
# If not, reset and use normal domination
|
|
350
|
+
self._warn("Removing DM preference")
|
|
351
|
+
self._reset_dm_preference()
|
|
352
|
+
break
|
|
353
|
+
|
|
354
|
+
else:
|
|
355
|
+
|
|
356
|
+
self._warn("Removing the second best preferred solution from the fit.")
|
|
357
|
+
|
|
358
|
+
# ranks start at 1, not zero
|
|
359
|
+
rank_to_remove = dm_ranks[1]
|
|
360
|
+
eta_F = np.delete(eta_F, rank_to_remove - 1, axis=0)
|
|
361
|
+
|
|
362
|
+
dm_ranks = np.concatenate(([dm_ranks[0]], dm_ranks[2:]))
|
|
363
|
+
|
|
364
|
+
# update the ranks, since we just removed one
|
|
365
|
+
dm_ranks[dm_ranks > rank_to_remove] = dm_ranks[dm_ranks > rank_to_remove] - 1
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
parse_doc_string(PINSGA2.__init__)
|
|
369
|
+
|
|
370
|
+
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.moo.nsga2 import NSGA2
|
|
4
|
+
from pymoo.docs import parse_doc_string
|
|
5
|
+
from pymoo.core.survival import Survival
|
|
6
|
+
from pymoo.operators.selection.rnd import RandomSelection
|
|
7
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
8
|
+
from pymoo.util.normalization import get_extreme_points_c
|
|
9
|
+
|
|
10
|
+
# =========================================================================================================
|
|
11
|
+
# Implementation
|
|
12
|
+
# =========================================================================================================
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RNSGA2(NSGA2):
|
|
16
|
+
|
|
17
|
+
def __init__(self,
|
|
18
|
+
ref_points,
|
|
19
|
+
epsilon=0.001,
|
|
20
|
+
normalization="front",
|
|
21
|
+
weights=None,
|
|
22
|
+
extreme_points_as_reference_points=False,
|
|
23
|
+
**kwargs):
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
|
|
29
|
+
ref_points : {ref_points}
|
|
30
|
+
epsilon : float
|
|
31
|
+
weights : np.array
|
|
32
|
+
normalization : {{'no', 'front', 'ever'}}
|
|
33
|
+
extreme_points_as_reference_points : bool
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
self.epsilon = epsilon
|
|
38
|
+
self.weights = weights
|
|
39
|
+
self.normalization = normalization
|
|
40
|
+
self.selection = RandomSelection()
|
|
41
|
+
|
|
42
|
+
super().__init__(**kwargs)
|
|
43
|
+
|
|
44
|
+
self.survival = RankAndModifiedCrowdingSurvival(ref_points, epsilon, weights, normalization,
|
|
45
|
+
extreme_points_as_reference_points)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RankAndModifiedCrowdingSurvival(Survival):
|
|
49
|
+
|
|
50
|
+
def __init__(self, ref_points,
|
|
51
|
+
epsilon,
|
|
52
|
+
weights,
|
|
53
|
+
normalization,
|
|
54
|
+
extreme_points_as_reference_points
|
|
55
|
+
) -> None:
|
|
56
|
+
|
|
57
|
+
super().__init__(True)
|
|
58
|
+
self.n_obj = ref_points.shape[1]
|
|
59
|
+
self.ref_points = ref_points
|
|
60
|
+
self.epsilon = epsilon
|
|
61
|
+
self.extreme_points_as_reference_points = extreme_points_as_reference_points
|
|
62
|
+
|
|
63
|
+
self.weights = weights
|
|
64
|
+
if self.weights is None:
|
|
65
|
+
self.weights = np.full(self.n_obj, 1 / self.n_obj)
|
|
66
|
+
|
|
67
|
+
self.normalization = normalization
|
|
68
|
+
self.ideal_point = np.full(self.n_obj, np.inf)
|
|
69
|
+
self.nadir_point = np.full(self.n_obj, -np.inf)
|
|
70
|
+
|
|
71
|
+
def _do(self, problem, pop, n_survive=None, **kwargs):
|
|
72
|
+
|
|
73
|
+
# get the objective space values and objects
|
|
74
|
+
F = pop.get("F")
|
|
75
|
+
|
|
76
|
+
# the final indices of surviving individuals
|
|
77
|
+
survivors = []
|
|
78
|
+
|
|
79
|
+
# do the non-dominated sorting until splitting front
|
|
80
|
+
fronts = NonDominatedSorting().do(F)
|
|
81
|
+
|
|
82
|
+
if self.normalization == "ever":
|
|
83
|
+
# find or usually update the new ideal point - from feasible solutions
|
|
84
|
+
self.ideal_point = np.min(np.vstack((self.ideal_point, F)), axis=0)
|
|
85
|
+
self.nadir_point = np.max(np.vstack((self.nadir_point, F)), axis=0)
|
|
86
|
+
|
|
87
|
+
elif self.normalization == "front":
|
|
88
|
+
front = fronts[0]
|
|
89
|
+
if len(front) > 1:
|
|
90
|
+
self.ideal_point = np.min(F[front], axis=0)
|
|
91
|
+
self.nadir_point = np.max(F[front], axis=0)
|
|
92
|
+
|
|
93
|
+
elif self.normalization == "no":
|
|
94
|
+
self.ideal_point = np.zeros(self.n_obj)
|
|
95
|
+
self.nadir_point = np.ones(self.n_obj)
|
|
96
|
+
|
|
97
|
+
if self.extreme_points_as_reference_points:
|
|
98
|
+
self.ref_points = np.row_stack([self.ref_points, get_extreme_points_c(F, self.ideal_point)])
|
|
99
|
+
|
|
100
|
+
# calculate the distance matrix from ever solution to all reference point
|
|
101
|
+
dist_to_ref_points = calc_norm_pref_distance(F, self.ref_points, self.weights, self.ideal_point,
|
|
102
|
+
self.nadir_point)
|
|
103
|
+
|
|
104
|
+
for k, front in enumerate(fronts):
|
|
105
|
+
|
|
106
|
+
# save rank attributes to the individuals - rank = front here
|
|
107
|
+
pop[front].set("rank", np.full(len(front), k))
|
|
108
|
+
|
|
109
|
+
# number of individuals remaining
|
|
110
|
+
n_remaining = n_survive - len(survivors)
|
|
111
|
+
|
|
112
|
+
# the ranking of each point regarding each reference point (two times argsort is necessary)
|
|
113
|
+
rank_by_distance = np.argsort(np.argsort(dist_to_ref_points[front], axis=0), axis=0)
|
|
114
|
+
|
|
115
|
+
# the reference point where the best ranking is coming from
|
|
116
|
+
ref_point_of_best_rank = np.argmin(rank_by_distance, axis=1)
|
|
117
|
+
|
|
118
|
+
# the actual ranking which is used as crowding
|
|
119
|
+
ranking = rank_by_distance[np.arange(len(front)), ref_point_of_best_rank]
|
|
120
|
+
|
|
121
|
+
if len(front) <= n_remaining:
|
|
122
|
+
|
|
123
|
+
# we can simply copy the crowding to ranking. not epsilon selection here
|
|
124
|
+
crowding = ranking
|
|
125
|
+
I = np.arange(len(front))
|
|
126
|
+
|
|
127
|
+
else:
|
|
128
|
+
|
|
129
|
+
# Distance from solution to every other solution and set distance to itself to infinity
|
|
130
|
+
dist_to_others = calc_norm_pref_distance(F[front], F[front], self.weights, self.ideal_point,
|
|
131
|
+
self.nadir_point)
|
|
132
|
+
np.fill_diagonal(dist_to_others, np.inf)
|
|
133
|
+
|
|
134
|
+
# the crowding that will be used for selection
|
|
135
|
+
crowding = np.full(len(front), np.nan)
|
|
136
|
+
|
|
137
|
+
# solutions which are not already selected - for
|
|
138
|
+
not_selected = np.argsort(ranking)
|
|
139
|
+
|
|
140
|
+
# until we have saved a crowding for each solution
|
|
141
|
+
while len(not_selected) > 0:
|
|
142
|
+
|
|
143
|
+
# select the closest solution
|
|
144
|
+
idx = not_selected[0]
|
|
145
|
+
|
|
146
|
+
# set crowding for that individual
|
|
147
|
+
crowding[idx] = ranking[idx]
|
|
148
|
+
|
|
149
|
+
# need to remove myself from not-selected array
|
|
150
|
+
to_remove = [idx]
|
|
151
|
+
|
|
152
|
+
# Group of close solutions
|
|
153
|
+
dist = dist_to_others[idx][not_selected]
|
|
154
|
+
group = not_selected[np.where(dist < self.epsilon)[0]]
|
|
155
|
+
|
|
156
|
+
# if there exists solution with a distance less than epsilon
|
|
157
|
+
if len(group):
|
|
158
|
+
# discourage them by giving them a high crowding
|
|
159
|
+
crowding[group] = ranking[group] + np.round(len(front) / 2)
|
|
160
|
+
|
|
161
|
+
# remove group from not_selected array
|
|
162
|
+
to_remove.extend(group)
|
|
163
|
+
|
|
164
|
+
not_selected = np.array([i for i in not_selected if i not in to_remove])
|
|
165
|
+
|
|
166
|
+
# now sort by the crowding (actually modified rank) ascending and let the best survive
|
|
167
|
+
I = np.argsort(crowding)[:n_remaining]
|
|
168
|
+
|
|
169
|
+
# set the crowding to all individuals
|
|
170
|
+
pop[front].set("crowding", crowding)
|
|
171
|
+
|
|
172
|
+
# extend the survivors by all or selected individuals
|
|
173
|
+
survivors.extend(front[I])
|
|
174
|
+
|
|
175
|
+
# inverse of crowding because nsga2 does maximize it (then tournament selection can stay the same)
|
|
176
|
+
pop.set("crowding", -pop.get("crowding"))
|
|
177
|
+
|
|
178
|
+
return pop[survivors]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def calc_norm_pref_distance(A, B, weights, ideal, nadir):
|
|
182
|
+
D = np.repeat(A, B.shape[0], axis=0) - np.tile(B, (A.shape[0], 1))
|
|
183
|
+
N = ((D / (nadir - ideal)) ** 2) * weights
|
|
184
|
+
N = np.sqrt(np.sum(N, axis=1) * len(weights))
|
|
185
|
+
return np.reshape(N, (A.shape[0], B.shape[0]))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
parse_doc_string(RNSGA2.__init__)
|