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,309 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.algorithm import Algorithm
|
|
4
|
+
from pymoo.core.population import Population
|
|
5
|
+
from pymoo.docs import parse_doc_string
|
|
6
|
+
from pymoo.operators.repair.to_bound import set_to_bounds_if_outside
|
|
7
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
8
|
+
from pymoo.operators.survival.rank_and_crowding.metrics import get_crowding_function
|
|
9
|
+
from pymoo.termination.default import DefaultMultiObjectiveTermination
|
|
10
|
+
from pymoo.util import default_random_state
|
|
11
|
+
from pymoo.util.archive import MultiObjectiveArchive
|
|
12
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
13
|
+
from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MOPSO_CD(Algorithm):
|
|
17
|
+
"""
|
|
18
|
+
Multi-Objective Particle Swarm Optimization with Crowding Distance (MOPSO-CD) algorithm.
|
|
19
|
+
|
|
20
|
+
This implementation extends MOPSO with a crowding distance mechanism for leader selection
|
|
21
|
+
and archive management to ensure a well-distributed Pareto front, suitable for problems
|
|
22
|
+
like MO-HalfCheetah in multi-objective reinforcement learning.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
pop_size : int
|
|
27
|
+
The population size (number of particles)
|
|
28
|
+
w : float
|
|
29
|
+
Inertia weight
|
|
30
|
+
c1 : float
|
|
31
|
+
Cognitive parameter (personal best influence)
|
|
32
|
+
c2 : float
|
|
33
|
+
Social parameter (global best influence)
|
|
34
|
+
max_velocity_rate : float
|
|
35
|
+
Maximum velocity rate relative to the variable range
|
|
36
|
+
archive_size : int
|
|
37
|
+
Maximum size of the external archive
|
|
38
|
+
sampling : Sampling
|
|
39
|
+
Sampling strategy for initialization
|
|
40
|
+
output : Output
|
|
41
|
+
Output display
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
pop_size=100,
|
|
47
|
+
w=0.6, # Increased for better exploration
|
|
48
|
+
c1=2.0,
|
|
49
|
+
c2=2.0,
|
|
50
|
+
max_velocity_rate=0.5, # Increased for better exploration
|
|
51
|
+
archive_size=200, # Increased for better diversity
|
|
52
|
+
sampling=FloatRandomSampling(),
|
|
53
|
+
output=MultiObjectiveOutput(),
|
|
54
|
+
**kwargs,
|
|
55
|
+
):
|
|
56
|
+
super().__init__(output=output, **kwargs)
|
|
57
|
+
|
|
58
|
+
self.pop_size = pop_size
|
|
59
|
+
self.w = w
|
|
60
|
+
self.c1 = c1
|
|
61
|
+
self.c2 = c2
|
|
62
|
+
self.max_velocity_rate = max_velocity_rate
|
|
63
|
+
self.archive_size = archive_size
|
|
64
|
+
self.sampling = sampling
|
|
65
|
+
|
|
66
|
+
# Initialize termination if not provided
|
|
67
|
+
self.termination = DefaultMultiObjectiveTermination()
|
|
68
|
+
|
|
69
|
+
# Initialize components
|
|
70
|
+
self.nds = NonDominatedSorting()
|
|
71
|
+
self.cd = get_crowding_function("cd")
|
|
72
|
+
self.archive = None
|
|
73
|
+
self.velocities = None
|
|
74
|
+
self.pbest = None
|
|
75
|
+
self.pbest_f = None
|
|
76
|
+
self.random_state = default_random_state(kwargs.get("seed"))
|
|
77
|
+
|
|
78
|
+
def _setup(self, problem, **kwargs):
|
|
79
|
+
"""Setup the algorithm for the given problem"""
|
|
80
|
+
super()._setup(problem, **kwargs)
|
|
81
|
+
|
|
82
|
+
# Initialize the external archive
|
|
83
|
+
self.archive = MultiObjectiveArchive(max_size=self.archive_size)
|
|
84
|
+
|
|
85
|
+
# Compute maximum velocity based on problem bounds
|
|
86
|
+
xl, xu = problem.bounds()
|
|
87
|
+
self.v_max = self.max_velocity_rate * (xu - xl)
|
|
88
|
+
|
|
89
|
+
# Initialize particles, velocities, and personal bests
|
|
90
|
+
self.pop = self.sampling.do(
|
|
91
|
+
problem, self.pop_size, random_state=self.random_state
|
|
92
|
+
)
|
|
93
|
+
self.velocities = self.random_state.uniform(
|
|
94
|
+
-self.v_max, self.v_max, (self.pop_size, problem.n_var)
|
|
95
|
+
)
|
|
96
|
+
self.pbest = self.pop.copy() # Personal bests
|
|
97
|
+
self.pbest_f = np.full(
|
|
98
|
+
(self.pop_size, problem.n_obj), np.inf
|
|
99
|
+
) # Initialize with inf
|
|
100
|
+
|
|
101
|
+
# Evaluate initial population to set personal best objectives
|
|
102
|
+
self.evaluator.eval(self.problem, self.pop)
|
|
103
|
+
|
|
104
|
+
def _initialize_infill(self):
|
|
105
|
+
"""Initialize the population and velocities"""
|
|
106
|
+
# Initialize population using sampling
|
|
107
|
+
pop = self.sampling.do(self.problem, self.pop_size, random_state=self.random_state)
|
|
108
|
+
|
|
109
|
+
# Initialize velocities randomly
|
|
110
|
+
self.velocities = self.random_state.uniform(
|
|
111
|
+
-self.v_max, self.v_max, size=(self.pop_size, self.problem.n_var)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Initialize personal best (initially same as current positions)
|
|
115
|
+
self.pbest = pop.copy()
|
|
116
|
+
|
|
117
|
+
return pop
|
|
118
|
+
|
|
119
|
+
def _initialize_advance(self, infills=None, **kwargs):
|
|
120
|
+
"""Initialize after evaluation"""
|
|
121
|
+
self.pop = infills
|
|
122
|
+
|
|
123
|
+
# Update archive with initial population
|
|
124
|
+
self.archive = self._update_archive(infills)
|
|
125
|
+
|
|
126
|
+
# Initialize personal best fitness
|
|
127
|
+
self.pbest = infills.copy()
|
|
128
|
+
self.pbest_f = infills.get("F").copy()
|
|
129
|
+
|
|
130
|
+
def _infill(self):
|
|
131
|
+
"""Generate new solutions using PSO operators"""
|
|
132
|
+
# Create new population
|
|
133
|
+
X_new = np.zeros((self.pop_size, self.problem.n_var))
|
|
134
|
+
|
|
135
|
+
# Pre-select leaders for all particles to ensure diversity
|
|
136
|
+
leaders = self._select_diverse_leaders()
|
|
137
|
+
|
|
138
|
+
for i in range(self.pop_size):
|
|
139
|
+
# Use pre-selected leader for this particle
|
|
140
|
+
leader = leaders[i] if leaders[i] is not None else self.pop[i]
|
|
141
|
+
|
|
142
|
+
# Generate random coefficients
|
|
143
|
+
r1 = self.random_state.random(self.problem.n_var)
|
|
144
|
+
r2 = self.random_state.random(self.problem.n_var)
|
|
145
|
+
|
|
146
|
+
# Update velocity
|
|
147
|
+
cognitive = self.c1 * r1 * (self.pbest[i].X - self.pop[i].X)
|
|
148
|
+
social = self.c2 * r2 * (leader.X - self.pop[i].X)
|
|
149
|
+
|
|
150
|
+
self.velocities[i] = self.w * self.velocities[i] + cognitive + social
|
|
151
|
+
|
|
152
|
+
# Apply velocity bounds
|
|
153
|
+
self.velocities[i] = set_to_bounds_if_outside(
|
|
154
|
+
self.velocities[i], -self.v_max, self.v_max
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Update position
|
|
158
|
+
X_new[i] = self.pop[i].X + self.velocities[i]
|
|
159
|
+
|
|
160
|
+
# Apply bounds to positions
|
|
161
|
+
xl, xu = self.problem.bounds()
|
|
162
|
+
X_new = set_to_bounds_if_outside(X_new, xl, xu)
|
|
163
|
+
|
|
164
|
+
return Population.new("X", X_new)
|
|
165
|
+
|
|
166
|
+
def _advance(self, infills=None, **kwargs):
|
|
167
|
+
"""Advance the algorithm state"""
|
|
168
|
+
if infills is None:
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
# Update archive with crowding distance-based pruning
|
|
172
|
+
combined_pop = Population.merge(self.pop, infills)
|
|
173
|
+
self.archive = self._update_archive(combined_pop)
|
|
174
|
+
|
|
175
|
+
# Update personal best
|
|
176
|
+
self._update_pbest(infills)
|
|
177
|
+
|
|
178
|
+
# Update current population
|
|
179
|
+
self.pop = infills
|
|
180
|
+
|
|
181
|
+
def _update_archive(self, pop):
|
|
182
|
+
"""Update the external archive with non-dominated solutions using crowding distance"""
|
|
183
|
+
if len(pop) == 0:
|
|
184
|
+
return self.archive
|
|
185
|
+
|
|
186
|
+
# Combine current archive with new solutions
|
|
187
|
+
if len(self.archive) > 0:
|
|
188
|
+
combined = Population.merge(self.archive, pop)
|
|
189
|
+
else:
|
|
190
|
+
combined = pop
|
|
191
|
+
|
|
192
|
+
# Find non-dominated solutions
|
|
193
|
+
F = combined.get("F")
|
|
194
|
+
I = self.nds.do(F, only_non_dominated_front=True)
|
|
195
|
+
non_dominated = combined[I]
|
|
196
|
+
|
|
197
|
+
# Apply archive size limit using crowding distance
|
|
198
|
+
if len(non_dominated) > self.archive_size:
|
|
199
|
+
# Use tournament selection to maintain diversity while keeping quality
|
|
200
|
+
crowding = self.cd.do(non_dominated.get("F"))
|
|
201
|
+
|
|
202
|
+
# Select solutions with better crowding distance (more diverse)
|
|
203
|
+
selected_indices = []
|
|
204
|
+
remaining_indices = list(range(len(non_dominated)))
|
|
205
|
+
|
|
206
|
+
while len(selected_indices) < self.archive_size and remaining_indices:
|
|
207
|
+
# Tournament selection favoring higher crowding distance
|
|
208
|
+
tournament_size = min(3, len(remaining_indices))
|
|
209
|
+
tournament_indices = self.random_state.choice(
|
|
210
|
+
remaining_indices, size=tournament_size, replace=False
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Select the one with highest crowding distance in tournament
|
|
214
|
+
best_idx = tournament_indices[np.argmax(crowding[tournament_indices])]
|
|
215
|
+
selected_indices.append(best_idx)
|
|
216
|
+
remaining_indices.remove(best_idx)
|
|
217
|
+
|
|
218
|
+
non_dominated = non_dominated[selected_indices]
|
|
219
|
+
|
|
220
|
+
# Create new archive
|
|
221
|
+
return MultiObjectiveArchive(
|
|
222
|
+
individuals=non_dominated, max_size=self.archive_size
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def _select_diverse_leaders(self):
|
|
226
|
+
"""Select diverse leaders for all particles"""
|
|
227
|
+
leaders = []
|
|
228
|
+
|
|
229
|
+
if len(self.archive) == 0:
|
|
230
|
+
# If no archive, select randomly from population
|
|
231
|
+
for i in range(self.pop_size):
|
|
232
|
+
if len(self.pop) > 0:
|
|
233
|
+
idx = self.random_state.integers(0, len(self.pop))
|
|
234
|
+
leaders.append(self.pop[idx])
|
|
235
|
+
else:
|
|
236
|
+
leaders.append(None)
|
|
237
|
+
return leaders
|
|
238
|
+
|
|
239
|
+
# Ensure each particle gets a potentially different leader
|
|
240
|
+
for i in range(self.pop_size):
|
|
241
|
+
if len(self.archive) == 1:
|
|
242
|
+
leaders.append(self.archive[0])
|
|
243
|
+
else:
|
|
244
|
+
try:
|
|
245
|
+
# Use binary tournament selection with crowding distance
|
|
246
|
+
idx1 = self.random_state.integers(0, len(self.archive))
|
|
247
|
+
idx2 = self.random_state.integers(0, len(self.archive))
|
|
248
|
+
|
|
249
|
+
if idx1 == idx2:
|
|
250
|
+
leaders.append(self.archive[idx1])
|
|
251
|
+
else:
|
|
252
|
+
# Calculate crowding distance for comparison
|
|
253
|
+
F = self.archive.get("F")
|
|
254
|
+
crowding = self.cd.do(F)
|
|
255
|
+
|
|
256
|
+
# Select leader with higher crowding distance (more diverse)
|
|
257
|
+
if crowding[idx1] > crowding[idx2]:
|
|
258
|
+
leaders.append(self.archive[idx1])
|
|
259
|
+
else:
|
|
260
|
+
leaders.append(self.archive[idx2])
|
|
261
|
+
except Exception:
|
|
262
|
+
# Fallback to random selection
|
|
263
|
+
idx = self.random_state.integers(0, len(self.archive))
|
|
264
|
+
leaders.append(self.archive[idx])
|
|
265
|
+
|
|
266
|
+
return leaders
|
|
267
|
+
|
|
268
|
+
def _update_pbest(self, new_pop):
|
|
269
|
+
"""Update personal best positions"""
|
|
270
|
+
for i in range(len(new_pop)):
|
|
271
|
+
# Compare new position with personal best
|
|
272
|
+
if self._dominates(new_pop[i].F, self.pbest_f[i]):
|
|
273
|
+
self.pbest[i] = new_pop[i].copy()
|
|
274
|
+
self.pbest_f[i] = new_pop[i].F.copy()
|
|
275
|
+
elif self._dominates(self.pbest_f[i], new_pop[i].F):
|
|
276
|
+
# Keep current pbest
|
|
277
|
+
pass
|
|
278
|
+
else:
|
|
279
|
+
# Non-dominated case: use crowding distance to decide
|
|
280
|
+
# Combine both solutions to calculate crowding distance
|
|
281
|
+
F_combined = np.vstack([self.pbest_f[i], new_pop[i].F])
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
crowding = self.cd.do(F_combined)
|
|
285
|
+
# Select the one with higher crowding distance (more diverse)
|
|
286
|
+
if crowding[1] > crowding[0]: # new solution has higher crowding
|
|
287
|
+
self.pbest[i] = new_pop[i].copy()
|
|
288
|
+
self.pbest_f[i] = new_pop[i].F.copy()
|
|
289
|
+
# Otherwise keep current pbest
|
|
290
|
+
except Exception:
|
|
291
|
+
# Fallback to random selection if crowding distance fails
|
|
292
|
+
if self.random_state.random() < 0.5:
|
|
293
|
+
self.pbest[i] = new_pop[i].copy()
|
|
294
|
+
self.pbest_f[i] = new_pop[i].F.copy()
|
|
295
|
+
|
|
296
|
+
def _dominates(self, f1, f2):
|
|
297
|
+
"""Check if f1 dominates f2"""
|
|
298
|
+
return np.all(f1 <= f2) and np.any(f1 < f2)
|
|
299
|
+
|
|
300
|
+
def _set_optimum(self, **kwargs):
|
|
301
|
+
"""Set the optimum solutions from the archive"""
|
|
302
|
+
if len(self.archive) > 0:
|
|
303
|
+
self.opt = self.archive.copy()
|
|
304
|
+
else:
|
|
305
|
+
self.opt = Population.empty()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
# Parse docstring for documentation
|
|
309
|
+
parse_doc_string(MOPSO_CD.__init__)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
5
|
+
from pymoo.docs import parse_doc_string
|
|
6
|
+
from pymoo.operators.crossover.sbx import SBX
|
|
7
|
+
from pymoo.operators.mutation.pm import PM
|
|
8
|
+
from pymoo.operators.survival.rank_and_crowding import RankAndCrowding
|
|
9
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling
|
|
10
|
+
from pymoo.operators.selection.tournament import compare, TournamentSelection
|
|
11
|
+
from pymoo.termination.default import DefaultMultiObjectiveTermination
|
|
12
|
+
from pymoo.util.display.multi import MultiObjectiveOutput
|
|
13
|
+
from pymoo.util.dominator import Dominator
|
|
14
|
+
from pymoo.util.misc import has_feasible
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
18
|
+
# Binary Tournament Selection Function
|
|
19
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def binary_tournament(pop, P, algorithm, **kwargs):
|
|
23
|
+
n_tournaments, n_parents = P.shape
|
|
24
|
+
|
|
25
|
+
if n_parents != 2:
|
|
26
|
+
raise ValueError("Only implemented for binary tournament!")
|
|
27
|
+
|
|
28
|
+
tournament_type = algorithm.tournament_type
|
|
29
|
+
S = np.full(n_tournaments, np.nan)
|
|
30
|
+
|
|
31
|
+
for i in range(n_tournaments):
|
|
32
|
+
|
|
33
|
+
a, b = P[i, 0], P[i, 1]
|
|
34
|
+
a_cv, a_f, b_cv, b_f = pop[a].CV[0], pop[a].F, pop[b].CV[0], pop[b].F
|
|
35
|
+
rank_a, cd_a = pop[a].get("rank", "crowding")
|
|
36
|
+
rank_b, cd_b = pop[b].get("rank", "crowding")
|
|
37
|
+
|
|
38
|
+
# if at least one solution is infeasible
|
|
39
|
+
if a_cv > 0.0 or b_cv > 0.0:
|
|
40
|
+
S[i] = compare(a, a_cv, b, b_cv, method='smaller_is_better', return_random_if_equal=True, random_state=algorithm.random_state)
|
|
41
|
+
|
|
42
|
+
# both solutions are feasible
|
|
43
|
+
else:
|
|
44
|
+
|
|
45
|
+
if tournament_type == 'comp_by_dom_and_crowding':
|
|
46
|
+
rel = Dominator.get_relation(a_f, b_f)
|
|
47
|
+
if rel == 1:
|
|
48
|
+
S[i] = a
|
|
49
|
+
elif rel == -1:
|
|
50
|
+
S[i] = b
|
|
51
|
+
|
|
52
|
+
elif tournament_type == 'comp_by_rank_and_crowding':
|
|
53
|
+
S[i] = compare(a, rank_a, b, rank_b, method='smaller_is_better')
|
|
54
|
+
|
|
55
|
+
else:
|
|
56
|
+
raise Exception("Unknown tournament type.")
|
|
57
|
+
|
|
58
|
+
# if rank or domination relation didn't make a decision compare by crowding
|
|
59
|
+
if np.isnan(S[i]):
|
|
60
|
+
S[i] = compare(a, cd_a, b, cd_b, method='larger_is_better', return_random_if_equal=True, random_state=algorithm.random_state)
|
|
61
|
+
|
|
62
|
+
return S[:, None].astype(int, copy=False)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
66
|
+
# Survival Selection
|
|
67
|
+
# ---------------------------------------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RankAndCrowdingSurvival(RankAndCrowding):
|
|
71
|
+
|
|
72
|
+
def __init__(self, nds=None, crowding_func="cd"):
|
|
73
|
+
super().__init__(nds, crowding_func)
|
|
74
|
+
|
|
75
|
+
# =========================================================================================================
|
|
76
|
+
# Implementation
|
|
77
|
+
# =========================================================================================================
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class NSGA2(GeneticAlgorithm):
|
|
81
|
+
|
|
82
|
+
def __init__(self,
|
|
83
|
+
pop_size=100,
|
|
84
|
+
sampling=FloatRandomSampling(),
|
|
85
|
+
selection=TournamentSelection(func_comp=binary_tournament),
|
|
86
|
+
crossover=SBX(eta=15, prob=0.9),
|
|
87
|
+
mutation=PM(eta=20),
|
|
88
|
+
survival=RankAndCrowding(),
|
|
89
|
+
output=MultiObjectiveOutput(),
|
|
90
|
+
**kwargs):
|
|
91
|
+
|
|
92
|
+
super().__init__(
|
|
93
|
+
pop_size=pop_size,
|
|
94
|
+
sampling=sampling,
|
|
95
|
+
selection=selection,
|
|
96
|
+
crossover=crossover,
|
|
97
|
+
mutation=mutation,
|
|
98
|
+
survival=survival,
|
|
99
|
+
output=output,
|
|
100
|
+
advance_after_initial_infill=True,
|
|
101
|
+
**kwargs)
|
|
102
|
+
|
|
103
|
+
self.termination = DefaultMultiObjectiveTermination()
|
|
104
|
+
self.tournament_type = 'comp_by_dom_and_crowding'
|
|
105
|
+
|
|
106
|
+
def _set_optimum(self, **kwargs):
|
|
107
|
+
if not has_feasible(self.pop):
|
|
108
|
+
self.opt = self.pop[[np.argmin(self.pop.get("CV"))]]
|
|
109
|
+
else:
|
|
110
|
+
self.opt = self.pop[self.pop.get("rank") == 0]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
parse_doc_string(NSGA2.__init__)
|