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,239 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.moo.spea2 import SPEA2Survival
|
|
4
|
+
from pymoo.core.algorithm import Algorithm
|
|
5
|
+
from pymoo.core.initialization import Initialization
|
|
6
|
+
from pymoo.core.population import Population
|
|
7
|
+
from pymoo.core.survival import Survival
|
|
8
|
+
from pymoo.docs import parse_doc_string
|
|
9
|
+
from pymoo.operators.mutation.pm import PolynomialMutation
|
|
10
|
+
from pymoo.operators.repair.to_bound import (
|
|
11
|
+
ToBoundOutOfBoundsRepair,
|
|
12
|
+
set_to_bounds_if_outside,
|
|
13
|
+
)
|
|
14
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
15
|
+
from pymoo.operators.survival.rank_and_crowding.metrics import get_crowding_function
|
|
16
|
+
from pymoo.util import default_random_state
|
|
17
|
+
from pymoo.util.archive import MultiObjectiveArchive, SurvivalTruncation
|
|
18
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CrowdingDistanceTournamentSurvival(Survival):
|
|
22
|
+
def __init__(self, tournament_size=3):
|
|
23
|
+
"""
|
|
24
|
+
Survival strategy that uses tournament selection based on crowding distance.
|
|
25
|
+
|
|
26
|
+
This class inherits from :class:`~pymoo.core.survival.Survival` and implements a
|
|
27
|
+
tournament selection mechanism where individuals with higher crowding distance
|
|
28
|
+
(indicating better diversity) are preferred.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
tournament_size : int, optional
|
|
33
|
+
The number of individuals participating in each tournament. Defaults to 3.
|
|
34
|
+
"""
|
|
35
|
+
super().__init__(filter_infeasible=True)
|
|
36
|
+
self._tournament_size = tournament_size
|
|
37
|
+
self._cd = get_crowding_function("cd")
|
|
38
|
+
|
|
39
|
+
def _do(self, problem, pop, n_survive, random_state=None, **kwargs):
|
|
40
|
+
crowding = self._cd.do(pop.get("F"))
|
|
41
|
+
|
|
42
|
+
# Select solutions with better crowding distance (more diverse)
|
|
43
|
+
selected_indices = []
|
|
44
|
+
remaining_indices = list(range(len(pop)))
|
|
45
|
+
|
|
46
|
+
while len(selected_indices) < n_survive and remaining_indices:
|
|
47
|
+
# Tournament selection favoring higher crowding distance
|
|
48
|
+
tournament_size = min(self._tournament_size, len(remaining_indices))
|
|
49
|
+
tournament_indices = random_state.choice(
|
|
50
|
+
remaining_indices, size=tournament_size, replace=False
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Select the one with highest crowding distance in tournament
|
|
54
|
+
best_idx = tournament_indices[np.argmax(crowding[tournament_indices])]
|
|
55
|
+
selected_indices.append(best_idx)
|
|
56
|
+
remaining_indices.remove(best_idx)
|
|
57
|
+
|
|
58
|
+
return pop[selected_indices]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def angle_between(v1, v2):
|
|
62
|
+
v1 = v1 / (np.linalg.norm(v1) + 1e-6) # unit vector; +1-e6 to avoid zero-division
|
|
63
|
+
v2 = v2 / (np.linalg.norm(v2) + 1e-6) # unit vector
|
|
64
|
+
return np.arccos(np.clip(np.dot(v1, v2), -1.0, 1.0))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@default_random_state
|
|
68
|
+
def cmopso_equation(X, L, V, V_max, random_state=None):
|
|
69
|
+
"""
|
|
70
|
+
Calculates the new positions and velocities of particles based on the CMOPSO
|
|
71
|
+
competition-based learning strategy.
|
|
72
|
+
|
|
73
|
+
This function implements the core update equations for the Competitive Mechanism
|
|
74
|
+
based Multi-objective Particle Swarm Optimizer (CMOPSO). Each particle's
|
|
75
|
+
velocity is updated by learning from a "winner" selected from the elite archive
|
|
76
|
+
(`L`) through a binary tournament. The winner is chosen based on the smallest
|
|
77
|
+
angle between the particle's current position and the elite's position, promoting
|
|
78
|
+
diversity.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
X : numpy.ndarray
|
|
83
|
+
Current positions of the particles (population). Shape `(n_particles, n_var)`.
|
|
84
|
+
L : numpy.ndarray
|
|
85
|
+
Positions of the elite particles from the archive. Shape `(n_elites, n_var)`.
|
|
86
|
+
V : numpy.ndarray
|
|
87
|
+
Current velocities of the particles. Shape `(n_particles, n_var)`.
|
|
88
|
+
V_max : numpy.ndarray
|
|
89
|
+
Maximum allowed velocity for each decision variable. Shape `(n_var,)`.
|
|
90
|
+
random_state : numpy.random.RandomState, optional
|
|
91
|
+
Random state for reproducibility. If None, a new default random number
|
|
92
|
+
generator is used.
|
|
93
|
+
|
|
94
|
+
Returns
|
|
95
|
+
-------
|
|
96
|
+
Xp : numpy.ndarray
|
|
97
|
+
New positions of the particles after the update. Shape `(n_particles, n_var)`.
|
|
98
|
+
Vp : numpy.ndarray
|
|
99
|
+
New velocities of the particles after the update. Shape `(n_particles, n_var)`.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
W_X = []
|
|
103
|
+
for i in range(np.shape(X)[0]): # binary tournament selection on elites
|
|
104
|
+
aidx = random_state.choice(range(len(L)))
|
|
105
|
+
bidx = random_state.choice(range(len(L)))
|
|
106
|
+
a = L[aidx]
|
|
107
|
+
b = L[bidx]
|
|
108
|
+
pw = min(a, b, key=lambda x: angle_between(x, X[i]))
|
|
109
|
+
W_X.append(pw)
|
|
110
|
+
W_X = np.asarray(W_X)
|
|
111
|
+
|
|
112
|
+
r1 = random_state.random(X.shape)
|
|
113
|
+
r2 = random_state.random(X.shape)
|
|
114
|
+
|
|
115
|
+
# calculate the velocity vector
|
|
116
|
+
Vp = r1 * V + r2 * (W_X - X)
|
|
117
|
+
Vp = set_to_bounds_if_outside(Vp, -V_max, V_max)
|
|
118
|
+
|
|
119
|
+
Xp = X + Vp
|
|
120
|
+
|
|
121
|
+
return Xp, Vp
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CMOPSO(Algorithm):
|
|
125
|
+
def __init__(
|
|
126
|
+
self,
|
|
127
|
+
pop_size=100,
|
|
128
|
+
max_velocity_rate=0.2,
|
|
129
|
+
elite_size=10,
|
|
130
|
+
initial_velocity="random", # 'random' | 'zero'
|
|
131
|
+
mutation_rate=0.5,
|
|
132
|
+
sampling=FloatRandomSampling(),
|
|
133
|
+
repair=ToBoundOutOfBoundsRepair(),
|
|
134
|
+
output=MultiObjectiveOutput(),
|
|
135
|
+
**kwargs,
|
|
136
|
+
):
|
|
137
|
+
"""
|
|
138
|
+
Competitive mechanism based Multi-objective Particle Swarm Optimizer (CMOPSO).
|
|
139
|
+
|
|
140
|
+
Particle updates are based on learning from the "winner" of binary tournaments
|
|
141
|
+
of randomly selected elites. Replacement strategy is based on SPEA2.
|
|
142
|
+
|
|
143
|
+
Zhang, X., Zheng, X., Cheng, R., Qiu, J., & Jin, Y. (2018). A competitive mechanism
|
|
144
|
+
based multi-objective particle swarm optimizer with fast convergence.
|
|
145
|
+
Inf. Sci., 427, 63-76.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
pop_size : int, optional
|
|
150
|
+
The population size. Defaults to 100.
|
|
151
|
+
max_velocity_rate : float, optional
|
|
152
|
+
The maximum velocity rate. Defaults to 0.2.
|
|
153
|
+
max_elite_size : int, optional
|
|
154
|
+
The maximum size of the elite archive. Defaults to 10.
|
|
155
|
+
initial_velocity : str, optional
|
|
156
|
+
Defines how the initial velocity of particles is set. Can be "random" or "zero".
|
|
157
|
+
Defaults to "random".
|
|
158
|
+
mutate_rate : float, optional
|
|
159
|
+
Rate at which to apply polynomial mutation to the offspring. Defaults to 0.5.
|
|
160
|
+
sampling : :class:`~pymoo.core.sampling.Sampling`, optional
|
|
161
|
+
Sampling strategy used to generate the initial population. Defaults to
|
|
162
|
+
:class:`~pymoo.operators.sampling.rnd.FloatRandomSampling`.
|
|
163
|
+
repair : :class:`~pymoo.operators.repair.Repair`, optional
|
|
164
|
+
Repair method for out-of-bounds variables. Defaults to
|
|
165
|
+
:class:`~pymoo.operators.repair.to_bound.ToBoundOutOfBoundsRepair`.
|
|
166
|
+
output : :class:`~pymoo.util.display.output.Output`, optional
|
|
167
|
+
Output object to be used for logging. Defaults to
|
|
168
|
+
:class:`~pymoo.util.display.multi.MultiObjectiveOutput`.
|
|
169
|
+
**kwargs
|
|
170
|
+
Additional keyword arguments to be passed to the Algorithm superclass.
|
|
171
|
+
"""
|
|
172
|
+
super().__init__(output=output, **kwargs)
|
|
173
|
+
self.pop_size = pop_size
|
|
174
|
+
self.max_velocity_rate = max_velocity_rate
|
|
175
|
+
self.elite_size = elite_size
|
|
176
|
+
self.initialization = Initialization(sampling)
|
|
177
|
+
self.initial_velocity = initial_velocity
|
|
178
|
+
self.repair = repair
|
|
179
|
+
self._cd = get_crowding_function("cd")
|
|
180
|
+
self.mutation = PolynomialMutation(prob=mutation_rate)
|
|
181
|
+
self.survival = SPEA2Survival()
|
|
182
|
+
|
|
183
|
+
def _setup(self, problem, **kwargs):
|
|
184
|
+
super()._setup(problem, **kwargs)
|
|
185
|
+
self.elites = MultiObjectiveArchive(
|
|
186
|
+
truncation=SurvivalTruncation(
|
|
187
|
+
CrowdingDistanceTournamentSurvival(), problem=problem
|
|
188
|
+
),
|
|
189
|
+
max_size=self.pop_size,
|
|
190
|
+
truncate_size=self.elite_size,
|
|
191
|
+
)
|
|
192
|
+
self.V_max = self.max_velocity_rate * (problem.xu - problem.xl)
|
|
193
|
+
|
|
194
|
+
def _initialize_infill(self):
|
|
195
|
+
return self.initialization.do(
|
|
196
|
+
self.problem, self.pop_size, algorithm=self, random_state=self.random_state
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def _initialize_advance(self, infills=None, **kwargs):
|
|
200
|
+
self.pop = infills
|
|
201
|
+
|
|
202
|
+
if self.initial_velocity == "random":
|
|
203
|
+
init_V = (
|
|
204
|
+
self.random_state.random((len(self.pop), self.problem.n_var))
|
|
205
|
+
* self.V_max[None, :]
|
|
206
|
+
)
|
|
207
|
+
elif self.initial_velocity == "zero":
|
|
208
|
+
init_V = np.zeros((len(self.pop), self.problem.n_var))
|
|
209
|
+
else:
|
|
210
|
+
raise Exception("Unknown velocity initialization.")
|
|
211
|
+
|
|
212
|
+
self.pop.set("V", init_V)
|
|
213
|
+
self.elites = self.elites.add(self.pop)
|
|
214
|
+
|
|
215
|
+
def _infill(self):
|
|
216
|
+
(X, V) = self.pop.get("X", "V")
|
|
217
|
+
L = self.elites.get("X")
|
|
218
|
+
|
|
219
|
+
Xp, Vp = cmopso_equation(X, L, V, self.V_max, random_state=self.random_state)
|
|
220
|
+
|
|
221
|
+
# create the offspring population
|
|
222
|
+
off = Population.new(X=Xp, V=Vp)
|
|
223
|
+
off = self.mutation(self.problem, off, random_state=self.random_state)
|
|
224
|
+
off = self.repair(self.problem, off)
|
|
225
|
+
|
|
226
|
+
return off
|
|
227
|
+
|
|
228
|
+
def _advance(self, infills=None, **kwargs):
|
|
229
|
+
assert infills is not None, (
|
|
230
|
+
"This algorithm uses the AskAndTell interface thus 'infills' must to be provided."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
particles = Population.merge(self.pop, infills)
|
|
234
|
+
self.elites = self.elites.add(particles)
|
|
235
|
+
self.pop = self.survival.do(self.problem, particles, n_survive=self.pop_size)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
parse_doc_string(CrowdingDistanceTournamentSurvival.__init__)
|
|
239
|
+
parse_doc_string(CMOPSO.__init__)
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.spatial.distance import cdist, pdist, squareform
|
|
5
|
+
|
|
6
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
7
|
+
from pymoo.core.population import Population
|
|
8
|
+
from pymoo.decomposition.asf import ASF
|
|
9
|
+
from pymoo.docs import parse_doc_string
|
|
10
|
+
from pymoo.functions import load_function
|
|
11
|
+
from pymoo.operators.crossover.sbx import SBX
|
|
12
|
+
from pymoo.operators.mutation.pm import PM
|
|
13
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
14
|
+
from pymoo.operators.selection.tournament import TournamentSelection
|
|
15
|
+
from pymoo.util import default_random_state
|
|
16
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
17
|
+
from pymoo.util.dominator import Dominator
|
|
18
|
+
from pymoo.util.misc import has_feasible, random_permutations
|
|
19
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# =========================================================================================================
|
|
23
|
+
# Implementation
|
|
24
|
+
# Following original code by K. Li https://cola-laboratory.github.io/codes/CTAEA.zip
|
|
25
|
+
# =========================================================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@default_random_state
|
|
29
|
+
def comp_by_cv_dom_then_random(pop, P, random_state=None, **kwargs):
|
|
30
|
+
S = np.full(P.shape[0], np.nan)
|
|
31
|
+
|
|
32
|
+
for i in range(P.shape[0]):
|
|
33
|
+
a, b = P[i, 0], P[i, 1]
|
|
34
|
+
|
|
35
|
+
if pop[a].CV <= 0.0 and pop[b].CV <= 0.0:
|
|
36
|
+
rel = Dominator.get_relation(pop[a].F, pop[b].F)
|
|
37
|
+
if rel == 1:
|
|
38
|
+
S[i] = a
|
|
39
|
+
elif rel == -1:
|
|
40
|
+
S[i] = b
|
|
41
|
+
else:
|
|
42
|
+
S[i] = random_state.choice([a, b])
|
|
43
|
+
elif pop[a].CV <= 0.0:
|
|
44
|
+
S[i] = a
|
|
45
|
+
elif pop[b].CV <= 0.0:
|
|
46
|
+
S[i] = b
|
|
47
|
+
else:
|
|
48
|
+
S[i] = random_state.choice([a, b])
|
|
49
|
+
|
|
50
|
+
return S[:, None].astype(int)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class RestrictedMating(TournamentSelection):
|
|
54
|
+
"""Restricted mating approach to balance convergence and diversity archives"""
|
|
55
|
+
|
|
56
|
+
@default_random_state
|
|
57
|
+
def _do(self, problem, Hm, n_select, n_parents, random_state=None, **kwargs):
|
|
58
|
+
n_pop = len(Hm) // 2
|
|
59
|
+
|
|
60
|
+
_, rank = NonDominatedSorting().do(Hm.get('F'), return_rank=True)
|
|
61
|
+
|
|
62
|
+
Pc = (rank[:n_pop] == 0).sum() / len(Hm)
|
|
63
|
+
Pd = (rank[n_pop:] == 0).sum() / len(Hm)
|
|
64
|
+
|
|
65
|
+
# number of random individuals needed
|
|
66
|
+
n_random = n_select * n_parents * self.pressure
|
|
67
|
+
n_perms = math.ceil(n_random / n_pop)
|
|
68
|
+
# get random permutations and reshape them
|
|
69
|
+
P = random_permutations(n_perms, n_pop, random_state=random_state)[:n_random]
|
|
70
|
+
P = np.reshape(P, (n_select * n_parents, self.pressure))
|
|
71
|
+
if Pc <= Pd:
|
|
72
|
+
# Choose from DA
|
|
73
|
+
P[::n_parents, :] += n_pop
|
|
74
|
+
pf = random_state.random(n_select)
|
|
75
|
+
P[1::n_parents, :][pf >= Pc] += n_pop
|
|
76
|
+
|
|
77
|
+
# compare using tournament function
|
|
78
|
+
S = self.func_comp(Hm, P, random_state=random_state, **kwargs)
|
|
79
|
+
|
|
80
|
+
return np.reshape(S, (n_select, n_parents))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CADASurvival:
|
|
84
|
+
|
|
85
|
+
def __init__(self, ref_dirs):
|
|
86
|
+
self.ref_dirs = ref_dirs
|
|
87
|
+
self.opt = None
|
|
88
|
+
self.ideal_point = np.full(ref_dirs.shape[1], np.inf)
|
|
89
|
+
self._decomposition = ASF()
|
|
90
|
+
self._calc_perpendicular_distance = load_function("calc_perpendicular_distance")
|
|
91
|
+
|
|
92
|
+
@default_random_state
|
|
93
|
+
def do(self, _, pop, da, n_survive=None, random_state=None, **kwargs):
|
|
94
|
+
# Store random_state for use in methods
|
|
95
|
+
self.random_state = random_state
|
|
96
|
+
# Offspring are last of merged population
|
|
97
|
+
off = pop[-n_survive:]
|
|
98
|
+
# Update ideal point
|
|
99
|
+
self.ideal_point = np.min(np.vstack((self.ideal_point, off.get("F"))), axis=0)
|
|
100
|
+
# Update CA
|
|
101
|
+
pop = self._updateCA(pop, n_survive, random_state=random_state)
|
|
102
|
+
# Update DA
|
|
103
|
+
Hd = Population.merge(da, off)
|
|
104
|
+
da = self._updateDA(pop, Hd, n_survive)
|
|
105
|
+
return pop, da
|
|
106
|
+
|
|
107
|
+
def _associate(self, pop):
|
|
108
|
+
"""Associate each individual with a F vector and calculate decomposed fitness"""
|
|
109
|
+
F = pop.get("F")
|
|
110
|
+
dist_matrix = self._calc_perpendicular_distance(F - self.ideal_point, self.ref_dirs)
|
|
111
|
+
niche_of_individuals = np.argmin(dist_matrix, axis=1)
|
|
112
|
+
FV = self._decomposition.do(F, weights=self.ref_dirs[niche_of_individuals, :],
|
|
113
|
+
ideal_point=self.ideal_point, weight_0=1e-4)
|
|
114
|
+
pop.set("niche", niche_of_individuals)
|
|
115
|
+
pop.set("FV", FV)
|
|
116
|
+
return niche_of_individuals, FV
|
|
117
|
+
|
|
118
|
+
@default_random_state
|
|
119
|
+
def _updateCA(self, pop, n_survive, random_state=None):
|
|
120
|
+
"""Update the Convergence archive (CA)"""
|
|
121
|
+
CV = pop.get("CV").flatten()
|
|
122
|
+
|
|
123
|
+
Sc = pop[CV == 0] # ConstraintsAsObjective population
|
|
124
|
+
if len(Sc) == n_survive: # Exactly n_survive feasible individuals
|
|
125
|
+
F = Sc.get("F")
|
|
126
|
+
fronts, rank = NonDominatedSorting().do(F, return_rank=True)
|
|
127
|
+
Sc.set('rank', rank)
|
|
128
|
+
self.opt = Sc[fronts[0]]
|
|
129
|
+
return Sc
|
|
130
|
+
elif len(Sc) < n_survive: # Not enough feasible individuals
|
|
131
|
+
remainder = n_survive - len(Sc)
|
|
132
|
+
# Solve sub-problem CV, tche
|
|
133
|
+
SI = pop[CV > 0]
|
|
134
|
+
f1 = SI.get("CV")
|
|
135
|
+
_, f2 = self._associate(SI)
|
|
136
|
+
sub_F = np.column_stack([f1, f2])
|
|
137
|
+
fronts = NonDominatedSorting().do(sub_F, n_stop_if_ranked=remainder)
|
|
138
|
+
I = []
|
|
139
|
+
for front in fronts:
|
|
140
|
+
if len(I) + len(front) <= remainder:
|
|
141
|
+
I.extend(front)
|
|
142
|
+
else:
|
|
143
|
+
n_missing = remainder - len(I)
|
|
144
|
+
last_front_CV = np.argsort(f1.flatten()[front])
|
|
145
|
+
I.extend(front[last_front_CV[:n_missing]])
|
|
146
|
+
SI = SI[I]
|
|
147
|
+
S = Population.merge(Sc, SI)
|
|
148
|
+
F = S.get("F")
|
|
149
|
+
fronts, rank = NonDominatedSorting().do(F, return_rank=True)
|
|
150
|
+
S.set('rank', rank)
|
|
151
|
+
self.opt = S[fronts[0]]
|
|
152
|
+
return S
|
|
153
|
+
else: # Too many feasible individuals
|
|
154
|
+
F = Sc.get("F")
|
|
155
|
+
# Filter by non-dominated sorting
|
|
156
|
+
fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive)
|
|
157
|
+
I = np.concatenate(fronts)
|
|
158
|
+
S, rank, F = Sc[I], rank[I], F[I]
|
|
159
|
+
if len(S) > n_survive:
|
|
160
|
+
# Remove individual in most crowded niche and with worst fitness
|
|
161
|
+
niche_of_individuals, FV = self._associate(S)
|
|
162
|
+
index, count = np.unique(niche_of_individuals, return_counts=True)
|
|
163
|
+
survivors = np.full(S.shape[0], True)
|
|
164
|
+
while survivors.sum() > n_survive:
|
|
165
|
+
crowdest_niches, = np.where(count == count.max())
|
|
166
|
+
worst_idx = None
|
|
167
|
+
worst_niche = None
|
|
168
|
+
worst_fit = -1
|
|
169
|
+
for crowdest_niche in crowdest_niches:
|
|
170
|
+
crowdest, = np.where((niche_of_individuals == index[crowdest_niche]) & survivors)
|
|
171
|
+
niche_worst = crowdest[FV[crowdest].argmax()]
|
|
172
|
+
dist_to_max_fit = cdist(F[[niche_worst], :], F).flatten()
|
|
173
|
+
dist_to_max_fit[niche_worst] = np.inf
|
|
174
|
+
dist_to_max_fit[~survivors] = np.inf
|
|
175
|
+
min_d_to_max_fit = dist_to_max_fit.min()
|
|
176
|
+
|
|
177
|
+
dist_in_niche = squareform(pdist(F[crowdest]))
|
|
178
|
+
np.fill_diagonal(dist_in_niche, np.inf)
|
|
179
|
+
|
|
180
|
+
delta_d = dist_in_niche - min_d_to_max_fit
|
|
181
|
+
min_d_i = np.unravel_index(np.argmin(delta_d, axis=None), dist_in_niche.shape)
|
|
182
|
+
if (delta_d[min_d_i] < 0) or (
|
|
183
|
+
delta_d[min_d_i] == 0 and (FV[crowdest[list(min_d_i)]] > niche_worst).any()):
|
|
184
|
+
min_d_i = list(min_d_i)
|
|
185
|
+
random_state.shuffle(min_d_i)
|
|
186
|
+
closest = crowdest[min_d_i]
|
|
187
|
+
niche_worst = closest[np.argmax(FV[closest])]
|
|
188
|
+
if (FV[niche_worst] > worst_fit).all():
|
|
189
|
+
worst_fit = FV[niche_worst]
|
|
190
|
+
worst_idx = niche_worst
|
|
191
|
+
worst_niche = crowdest_niche
|
|
192
|
+
survivors[worst_idx] = False
|
|
193
|
+
count[worst_niche] -= 1
|
|
194
|
+
S, rank = S[survivors], rank[survivors]
|
|
195
|
+
S.set('rank', rank)
|
|
196
|
+
self.opt = S[rank == 0]
|
|
197
|
+
return S
|
|
198
|
+
|
|
199
|
+
def _updateDA(self, pop, Hd, n_survive):
|
|
200
|
+
"""Update the Diversity archive (DA)"""
|
|
201
|
+
niche_Hd, FV = self._associate(Hd)
|
|
202
|
+
niche_CA, _ = self._associate(pop)
|
|
203
|
+
|
|
204
|
+
itr = 1
|
|
205
|
+
S = []
|
|
206
|
+
while len(S) < n_survive:
|
|
207
|
+
for i in range(n_survive):
|
|
208
|
+
current_ca, = np.where(niche_CA == i)
|
|
209
|
+
if len(current_ca) < itr:
|
|
210
|
+
for _ in range(itr - len(current_ca)):
|
|
211
|
+
current_da = np.where(niche_Hd == i)[0]
|
|
212
|
+
if current_da.size > 0:
|
|
213
|
+
F = Hd[current_da].get('F')
|
|
214
|
+
nd = NonDominatedSorting().do(F, only_non_dominated_front=True, n_stop_if_ranked=0)
|
|
215
|
+
i_best = current_da[nd[np.argmin(FV[current_da[nd]])]]
|
|
216
|
+
niche_Hd[i_best] = -1
|
|
217
|
+
if len(S) < n_survive:
|
|
218
|
+
S.append(i_best)
|
|
219
|
+
else:
|
|
220
|
+
break
|
|
221
|
+
if len(S) == n_survive:
|
|
222
|
+
break
|
|
223
|
+
itr += 1
|
|
224
|
+
return Hd[S]
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class CTAEA(GeneticAlgorithm):
|
|
228
|
+
|
|
229
|
+
def __init__(self,
|
|
230
|
+
ref_dirs,
|
|
231
|
+
sampling=FloatRandomSampling(),
|
|
232
|
+
selection=RestrictedMating(func_comp=comp_by_cv_dom_then_random),
|
|
233
|
+
crossover=SBX(n_offsprings=1, eta=30, prob=1.0),
|
|
234
|
+
mutation=PM(prob_var=None, eta=20),
|
|
235
|
+
eliminate_duplicates=True,
|
|
236
|
+
output=MultiObjectiveOutput(),
|
|
237
|
+
**kwargs):
|
|
238
|
+
"""
|
|
239
|
+
CTAEA
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
ref_dirs : {ref_dirs}
|
|
244
|
+
sampling : {sampling}
|
|
245
|
+
selection : {selection}
|
|
246
|
+
crossover : {crossover}
|
|
247
|
+
mutation : {mutation}
|
|
248
|
+
eliminate_duplicates : {eliminate_duplicates}
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
self.ref_dirs = ref_dirs
|
|
252
|
+
pop_size = len(ref_dirs)
|
|
253
|
+
|
|
254
|
+
if 'survival' in kwargs:
|
|
255
|
+
survival = kwargs['survival']
|
|
256
|
+
del kwargs['survival']
|
|
257
|
+
else:
|
|
258
|
+
survival = CADASurvival(ref_dirs)
|
|
259
|
+
|
|
260
|
+
# Initialize diversity archives
|
|
261
|
+
self.da = None
|
|
262
|
+
|
|
263
|
+
super().__init__(pop_size=pop_size,
|
|
264
|
+
sampling=sampling,
|
|
265
|
+
selection=selection,
|
|
266
|
+
crossover=crossover,
|
|
267
|
+
mutation=mutation,
|
|
268
|
+
survival=survival,
|
|
269
|
+
eliminate_duplicates=eliminate_duplicates,
|
|
270
|
+
n_offsprings=pop_size,
|
|
271
|
+
output=output,
|
|
272
|
+
**kwargs)
|
|
273
|
+
|
|
274
|
+
def _setup(self, problem, **kwargs):
|
|
275
|
+
|
|
276
|
+
if self.ref_dirs is not None and self.ref_dirs.shape[1] != problem.n_obj:
|
|
277
|
+
raise Exception(
|
|
278
|
+
"Dimensionality of reference points must be equal to the number of objectives: %s != %s" %
|
|
279
|
+
(self.ref_dirs.shape[1], problem.n_obj))
|
|
280
|
+
|
|
281
|
+
def _initialize_infill(self):
|
|
282
|
+
return self.initialization.do(self.problem, self.pop_size, algorithm=self, random_state=self.random_state)
|
|
283
|
+
|
|
284
|
+
def _initialize_advance(self, infills=None, **kwargs):
|
|
285
|
+
super()._initialize_advance(infills, **kwargs)
|
|
286
|
+
self.pop, self.da = self.survival.do(self.problem, self.pop, Population(), n_survive=len(self.pop),
|
|
287
|
+
algorithm=self, random_state=self.random_state)
|
|
288
|
+
|
|
289
|
+
def _infill(self):
|
|
290
|
+
Hm = Population.merge(self.pop, self.da)
|
|
291
|
+
return self.mating.do(self.problem, Hm, n_offsprings=self.n_offsprings, algorithm=self, random_state=self.random_state)
|
|
292
|
+
|
|
293
|
+
def _advance(self, infills=None, **kwargs):
|
|
294
|
+
assert infills is not None, "This algorithms uses the AskAndTell interface thus infills must to be provided."
|
|
295
|
+
pop = Population.merge(self.pop, infills)
|
|
296
|
+
self.pop, self.da = self.survival.do(self.problem, pop, self.da, self.pop_size, algorithm=self, random_state=self.random_state)
|
|
297
|
+
|
|
298
|
+
def _set_optimum(self, **kwargs):
|
|
299
|
+
if not has_feasible(self.pop):
|
|
300
|
+
self.opt = self.pop[[np.argmin(self.pop.get("CV"))]]
|
|
301
|
+
else:
|
|
302
|
+
self.opt = self.survival.opt
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
parse_doc_string(CTAEA.__init__)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.moo.nsga2 import NSGA2
|
|
4
|
+
from pymoo.core.population import Population
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DNSGA2(NSGA2):
|
|
8
|
+
|
|
9
|
+
def __init__(self,
|
|
10
|
+
perc_detect_change=0.1,
|
|
11
|
+
perc_diversity=0.3,
|
|
12
|
+
eps=0.0,
|
|
13
|
+
version="A",
|
|
14
|
+
**kwargs):
|
|
15
|
+
|
|
16
|
+
super().__init__(**kwargs)
|
|
17
|
+
self.perc_detect_change = perc_detect_change
|
|
18
|
+
self.perc_diversity = perc_diversity
|
|
19
|
+
self.eps = eps
|
|
20
|
+
self.version = version
|
|
21
|
+
|
|
22
|
+
def setup(self, problem, **kwargs):
|
|
23
|
+
assert not problem.has_constraints(), "DNSGA2 only works for unconstrained problems."
|
|
24
|
+
return super().setup(problem, **kwargs)
|
|
25
|
+
|
|
26
|
+
def _infill(self):
|
|
27
|
+
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
def _advance(self, **kwargs):
|
|
31
|
+
|
|
32
|
+
pop = self.pop
|
|
33
|
+
X, F = pop.get("X", "F")
|
|
34
|
+
|
|
35
|
+
# the number of solutions to sample from the population to detect the change
|
|
36
|
+
n_samples = int(np.ceil(len(pop) * self.perc_detect_change))
|
|
37
|
+
|
|
38
|
+
# choose randomly some individuals of the current population to test if there was a change
|
|
39
|
+
I = self.random_state.choice(np.arange(len(pop)), size=n_samples)
|
|
40
|
+
samples = self.evaluator.eval(self.problem, Population.new(X=X[I]))
|
|
41
|
+
|
|
42
|
+
# calculate the differences between the old and newly evaluated pop
|
|
43
|
+
delta = ((samples.get("F") - F[I]) ** 2).mean()
|
|
44
|
+
|
|
45
|
+
# if there is an average deviation bigger than eps -> we have a change detected
|
|
46
|
+
change_detected = delta > self.eps
|
|
47
|
+
|
|
48
|
+
if change_detected:
|
|
49
|
+
|
|
50
|
+
# recreate the current population without being evaluated
|
|
51
|
+
pop = Population.new(X=X)
|
|
52
|
+
|
|
53
|
+
# find indices to be replaced (introduce diversity)
|
|
54
|
+
I = np.where(self.random_state.random(len(pop)) < self.perc_diversity)[0]
|
|
55
|
+
|
|
56
|
+
# replace with randomly sampled individuals
|
|
57
|
+
if self.version == "A":
|
|
58
|
+
pop[I] = self.initialization.sampling(self.problem, len(I), random_state=self.random_state)
|
|
59
|
+
|
|
60
|
+
# replace by mutations of existing solutions (this occurs inplace)
|
|
61
|
+
elif self.version == "B":
|
|
62
|
+
self.mating.mutation(self.problem, pop[I])
|
|
63
|
+
else:
|
|
64
|
+
raise Exception(f"Unknown version of D-NSGA-II: {self.version}")
|
|
65
|
+
|
|
66
|
+
# reevaluate because we know there was a change
|
|
67
|
+
self.evaluator.eval(self.problem, pop)
|
|
68
|
+
|
|
69
|
+
# do a survival to recreate rank and crowding of all individuals
|
|
70
|
+
pop = self.survival.do(self.problem, pop, n_survive=len(pop), random_state=self.random_state)
|
|
71
|
+
|
|
72
|
+
# create the offsprings from the current population
|
|
73
|
+
off = self.mating.do(self.problem, pop, self.n_offsprings, algorithm=self, random_state=self.random_state)
|
|
74
|
+
self.evaluator.eval(self.problem, off)
|
|
75
|
+
|
|
76
|
+
# merge the parent population and offsprings
|
|
77
|
+
pop = Population.merge(pop, off)
|
|
78
|
+
|
|
79
|
+
# execute the survival to find the fittest solutions
|
|
80
|
+
self.pop = self.survival.do(self.problem, pop, n_survive=self.pop_size, algorithm=self, random_state=self.random_state)
|