pymoo 0.6.1.5.dev0__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.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-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-313-x86_64-linux-gnu.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,399 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.algorithms.soo.nonconvex.ga import FitnessSurvival
|
|
4
|
+
from pymoo.core.algorithm import Algorithm
|
|
5
|
+
from pymoo.core.individual import Individual
|
|
6
|
+
from pymoo.core.initialization import Initialization
|
|
7
|
+
from pymoo.core.population import Population
|
|
8
|
+
from pymoo.core.repair import NoRepair
|
|
9
|
+
from pymoo.core.replacement import ImprovementReplacement
|
|
10
|
+
from pymoo.docs import parse_doc_string
|
|
11
|
+
from pymoo.operators.crossover.dex import repair_random_init
|
|
12
|
+
from pymoo.operators.mutation.pm import PM
|
|
13
|
+
from pymoo.operators.repair.bounds_repair import is_out_of_bounds_by_problem
|
|
14
|
+
from pymoo.operators.repair.to_bound import set_to_bounds_if_outside
|
|
15
|
+
from pymoo.operators.sampling.lhs import LHS
|
|
16
|
+
from pymoo.util.display.column import Column
|
|
17
|
+
from pymoo.util.display.single import SingleObjectiveOutput
|
|
18
|
+
from pymoo.util.misc import norm_eucl_dist
|
|
19
|
+
from pymoo.visualization.fitness_landscape import FitnessLandscape
|
|
20
|
+
from pymoo.visualization.video.callback_video import AnimationCallback
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# =========================================================================================================
|
|
24
|
+
# Display
|
|
25
|
+
# =========================================================================================================
|
|
26
|
+
|
|
27
|
+
class PSOFuzzyOutput(SingleObjectiveOutput):
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
super().__init__()
|
|
31
|
+
|
|
32
|
+
self.f = Column(name="f", width=8)
|
|
33
|
+
self.S = Column(name="S", width=7)
|
|
34
|
+
self.w = Column(name="w", width=7)
|
|
35
|
+
self.c1 = Column(name="c1", width=8)
|
|
36
|
+
self.c2 = Column(name="c2", width=8)
|
|
37
|
+
|
|
38
|
+
self.columns += [self.f, self.S, self.w, self.c1, self.c2]
|
|
39
|
+
|
|
40
|
+
def update(self, algorithm):
|
|
41
|
+
super().update(algorithm)
|
|
42
|
+
|
|
43
|
+
self.f.set(algorithm.f)
|
|
44
|
+
self.S.set(algorithm.strategy)
|
|
45
|
+
self.w.set(algorithm.w)
|
|
46
|
+
self.c1.set(algorithm.c1)
|
|
47
|
+
self.c2.set(algorithm.c2)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# =========================================================================================================
|
|
51
|
+
# Adaptation Constants
|
|
52
|
+
# =========================================================================================================
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def S1_exploration(f):
|
|
56
|
+
if f <= 0.4:
|
|
57
|
+
return 0
|
|
58
|
+
elif 0.4 < f <= 0.6:
|
|
59
|
+
return 5 * f - 2
|
|
60
|
+
elif 0.6 < f <= 0.7:
|
|
61
|
+
return 1
|
|
62
|
+
elif 0.7 < f <= 0.8:
|
|
63
|
+
return -10 * f + 8
|
|
64
|
+
elif 0.8 < f:
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def S2_exploitation(f):
|
|
69
|
+
if f <= 0.2:
|
|
70
|
+
return 0
|
|
71
|
+
elif 0.2 < f <= 0.3:
|
|
72
|
+
return 10 * f - 2
|
|
73
|
+
elif 0.3 < f <= 0.4:
|
|
74
|
+
return 1
|
|
75
|
+
elif 0.4 < f <= 0.6:
|
|
76
|
+
return -5 * f + 3
|
|
77
|
+
elif 0.6 < f:
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def S3_convergence(f):
|
|
82
|
+
if f <= 0.1:
|
|
83
|
+
return 1
|
|
84
|
+
elif 0.1 < f <= 0.3:
|
|
85
|
+
return -5 * f + 1.5
|
|
86
|
+
elif 0.3 < f:
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def S4_jumping_out(f):
|
|
91
|
+
if f <= 0.7:
|
|
92
|
+
return 0
|
|
93
|
+
elif 0.7 < f <= 0.9:
|
|
94
|
+
return 5 * f - 3.5
|
|
95
|
+
elif 0.9 < f:
|
|
96
|
+
return 1
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# =========================================================================================================
|
|
100
|
+
# Equation
|
|
101
|
+
# =========================================================================================================
|
|
102
|
+
|
|
103
|
+
def pso_equation(X, P_X, S_X, V, V_max, w, c1, c2, r1=None, r2=None):
|
|
104
|
+
n_particles, n_var = X.shape
|
|
105
|
+
|
|
106
|
+
if r1 is None:
|
|
107
|
+
r1 = np.random.random((n_particles, n_var))
|
|
108
|
+
|
|
109
|
+
if r2 is None:
|
|
110
|
+
r2 = np.random.random((n_particles, n_var))
|
|
111
|
+
|
|
112
|
+
inerta = w * V
|
|
113
|
+
cognitive = c1 * r1 * (P_X - X)
|
|
114
|
+
social = c2 * r2 * (S_X - X)
|
|
115
|
+
|
|
116
|
+
# calculate the velocity vector
|
|
117
|
+
Vp = inerta + cognitive + social
|
|
118
|
+
Vp = set_to_bounds_if_outside(Vp, - V_max, V_max)
|
|
119
|
+
|
|
120
|
+
Xp = X + Vp
|
|
121
|
+
|
|
122
|
+
return Xp, Vp
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# =========================================================================================================
|
|
126
|
+
# Implementation
|
|
127
|
+
# =========================================================================================================
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class PSO(Algorithm):
|
|
131
|
+
|
|
132
|
+
def __init__(self,
|
|
133
|
+
pop_size=25,
|
|
134
|
+
sampling=LHS(),
|
|
135
|
+
w=0.9,
|
|
136
|
+
c1=2.0,
|
|
137
|
+
c2=2.0,
|
|
138
|
+
adaptive=True,
|
|
139
|
+
initial_velocity="random",
|
|
140
|
+
max_velocity_rate=0.20,
|
|
141
|
+
pertube_best=True,
|
|
142
|
+
repair=NoRepair(),
|
|
143
|
+
output=PSOFuzzyOutput(),
|
|
144
|
+
**kwargs):
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
pop_size : The size of the swarm being used.
|
|
150
|
+
|
|
151
|
+
sampling : {sampling}
|
|
152
|
+
|
|
153
|
+
adaptive : bool
|
|
154
|
+
Whether w, c1, and c2 are changed dynamically over time. The update uses the spread from the global
|
|
155
|
+
optimum to determine suitable values.
|
|
156
|
+
|
|
157
|
+
w : float
|
|
158
|
+
The inertia F to be used in each iteration for the velocity update. This can be interpreted
|
|
159
|
+
as the momentum term regarding the velocity. If `adaptive=True` this is only the
|
|
160
|
+
initially used value.
|
|
161
|
+
|
|
162
|
+
c1 : float
|
|
163
|
+
The cognitive impact (personal best) during the velocity update. If `adaptive=True` this is only the
|
|
164
|
+
initially used value.
|
|
165
|
+
c2 : float
|
|
166
|
+
The social impact (global best) during the velocity update. If `adaptive=True` this is only the
|
|
167
|
+
initially used value.
|
|
168
|
+
|
|
169
|
+
initial_velocity : str - ('random', or 'zero')
|
|
170
|
+
How the initial velocity of each particle should be assigned. Either 'random' which creates a
|
|
171
|
+
random velocity vector or 'zero' which makes the particles start to find the direction through the
|
|
172
|
+
velocity update equation.
|
|
173
|
+
|
|
174
|
+
max_velocity_rate : float
|
|
175
|
+
The maximum velocity rate. It is determined variable (and not vector) wise. We consider the rate here
|
|
176
|
+
since the value is normalized regarding the `xl` and `xu` defined in the problem.
|
|
177
|
+
|
|
178
|
+
pertube_best : bool
|
|
179
|
+
Some studies have proposed to mutate the global best because it has been found to converge better.
|
|
180
|
+
Which means the population size is reduced by one particle and one function evaluation is spend
|
|
181
|
+
additionally to permute the best found solution so far.
|
|
182
|
+
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
super().__init__(output=output, **kwargs)
|
|
186
|
+
|
|
187
|
+
self.initialization = Initialization(sampling)
|
|
188
|
+
|
|
189
|
+
self.pop_size = pop_size
|
|
190
|
+
self.adaptive = adaptive
|
|
191
|
+
self.pertube_best = pertube_best
|
|
192
|
+
self.V_max = None
|
|
193
|
+
self.initial_velocity = initial_velocity
|
|
194
|
+
self.max_velocity_rate = max_velocity_rate
|
|
195
|
+
self.repair = repair
|
|
196
|
+
|
|
197
|
+
self.w = w
|
|
198
|
+
self.c1 = c1
|
|
199
|
+
self.c2 = c2
|
|
200
|
+
|
|
201
|
+
self.particles = None
|
|
202
|
+
self.sbest = None
|
|
203
|
+
|
|
204
|
+
def _setup(self, problem, **kwargs):
|
|
205
|
+
self.V_max = self.max_velocity_rate * (problem.xu - problem.xl)
|
|
206
|
+
self.f, self.strategy = None, None
|
|
207
|
+
|
|
208
|
+
def _initialize_infill(self):
|
|
209
|
+
return self.initialization.do(self.problem, self.pop_size, algorithm=self)
|
|
210
|
+
|
|
211
|
+
def _initialize_advance(self, infills=None, **kwargs):
|
|
212
|
+
particles = self.pop
|
|
213
|
+
|
|
214
|
+
if self.initial_velocity == "random":
|
|
215
|
+
init_V = np.random.random((len(particles), self.problem.n_var)) * self.V_max[None, :]
|
|
216
|
+
elif self.initial_velocity == "zero":
|
|
217
|
+
init_V = np.zeros((len(particles), self.problem.n_var))
|
|
218
|
+
else:
|
|
219
|
+
raise Exception("Unknown velocity initialization.")
|
|
220
|
+
|
|
221
|
+
particles.set("V", init_V)
|
|
222
|
+
self.particles = particles
|
|
223
|
+
|
|
224
|
+
super()._initialize_advance(infills=infills, **kwargs)
|
|
225
|
+
|
|
226
|
+
def _infill(self):
|
|
227
|
+
problem, particles, pbest = self.problem, self.particles, self.pop
|
|
228
|
+
|
|
229
|
+
(X, V) = particles.get("X", "V")
|
|
230
|
+
P_X = pbest.get("X")
|
|
231
|
+
|
|
232
|
+
sbest = self._social_best()
|
|
233
|
+
S_X = sbest.get("X")
|
|
234
|
+
|
|
235
|
+
Xp, Vp = pso_equation(X, P_X, S_X, V, self.V_max, self.w, self.c1, self.c2)
|
|
236
|
+
|
|
237
|
+
# if the problem has boundaries to be considered
|
|
238
|
+
if problem.has_bounds():
|
|
239
|
+
|
|
240
|
+
for k in range(20):
|
|
241
|
+
# find the individuals which are still infeasible
|
|
242
|
+
m = is_out_of_bounds_by_problem(problem, Xp)
|
|
243
|
+
|
|
244
|
+
if len(m) == 0:
|
|
245
|
+
break
|
|
246
|
+
|
|
247
|
+
# actually execute the differential equation
|
|
248
|
+
Xp[m], Vp[m] = pso_equation(X[m], P_X[m], S_X[m], V[m], self.V_max, self.w, self.c1, self.c2)
|
|
249
|
+
|
|
250
|
+
# if still infeasible do a random initialization
|
|
251
|
+
Xp = repair_random_init(Xp, X, *problem.bounds())
|
|
252
|
+
|
|
253
|
+
# create the offspring population
|
|
254
|
+
off = Population.new(X=Xp, V=Vp)
|
|
255
|
+
|
|
256
|
+
# try to improve the current best with a pertubation
|
|
257
|
+
if self.pertube_best:
|
|
258
|
+
k = FitnessSurvival().do(problem, pbest, n_survive=1, return_indices=True)[0]
|
|
259
|
+
mut = PM(prob=0.9, eta=np.random.uniform(5, 30), at_least_once=False)
|
|
260
|
+
mutant = mut(problem, Population(Individual(X=pbest[k].X)))[0]
|
|
261
|
+
off[k].set("X", mutant.X)
|
|
262
|
+
|
|
263
|
+
self.repair(problem, off)
|
|
264
|
+
self.sbest = sbest
|
|
265
|
+
|
|
266
|
+
return off
|
|
267
|
+
|
|
268
|
+
def _advance(self, infills=None, **kwargs):
|
|
269
|
+
assert infills is not None, "This algorithms uses the AskAndTell interface thus 'infills' must to be provided."
|
|
270
|
+
|
|
271
|
+
# set the new population to be equal to the offsprings
|
|
272
|
+
self.particles = infills
|
|
273
|
+
|
|
274
|
+
# if an offspring has improved the personal store that index
|
|
275
|
+
has_improved = ImprovementReplacement().do(self.problem, self.pop, infills, return_indices=True)
|
|
276
|
+
|
|
277
|
+
# set the personal best which have been improved
|
|
278
|
+
self.pop[has_improved] = infills[has_improved]
|
|
279
|
+
|
|
280
|
+
if self.adaptive:
|
|
281
|
+
self._adapt()
|
|
282
|
+
|
|
283
|
+
def _social_best(self):
|
|
284
|
+
return Population([self.opt[0]] * len(self.pop))
|
|
285
|
+
|
|
286
|
+
def _adapt(self):
|
|
287
|
+
pop = self.pop
|
|
288
|
+
|
|
289
|
+
X, F = pop.get("X", "F")
|
|
290
|
+
sbest = self.sbest
|
|
291
|
+
w, c1, c2, = self.w, self.c1, self.c2
|
|
292
|
+
|
|
293
|
+
# get the average distance from one to another for normalization
|
|
294
|
+
D = norm_eucl_dist(self.problem, X, X)
|
|
295
|
+
mD = D.sum(axis=1) / (len(pop) - 1)
|
|
296
|
+
_min, _max = mD.min(), mD.max()
|
|
297
|
+
|
|
298
|
+
# get the average distance to the best
|
|
299
|
+
g_D = norm_eucl_dist(self.problem, sbest.get("X"), X).mean()
|
|
300
|
+
f = (g_D - _min) / (_max - _min + 1e-32)
|
|
301
|
+
|
|
302
|
+
S = np.array([S1_exploration(f), S2_exploitation(f), S3_convergence(f), S4_jumping_out(f)])
|
|
303
|
+
strategy = S.argmax() + 1
|
|
304
|
+
|
|
305
|
+
delta = 0.05 + (np.random.random() * 0.05)
|
|
306
|
+
|
|
307
|
+
if strategy == 1:
|
|
308
|
+
c1 += delta
|
|
309
|
+
c2 -= delta
|
|
310
|
+
elif strategy == 2:
|
|
311
|
+
c1 += 0.5 * delta
|
|
312
|
+
c2 -= 0.5 * delta
|
|
313
|
+
elif strategy == 3:
|
|
314
|
+
c1 += 0.5 * delta
|
|
315
|
+
c2 += 0.5 * delta
|
|
316
|
+
elif strategy == 4:
|
|
317
|
+
c1 -= delta
|
|
318
|
+
c2 += delta
|
|
319
|
+
|
|
320
|
+
c1 = max(1.5, min(2.5, c1))
|
|
321
|
+
c2 = max(1.5, min(2.5, c2))
|
|
322
|
+
|
|
323
|
+
if c1 + c2 > 4.0:
|
|
324
|
+
c1 = 4.0 * (c1 / (c1 + c2))
|
|
325
|
+
c2 = 4.0 * (c2 / (c1 + c2))
|
|
326
|
+
|
|
327
|
+
w = 1 / (1 + 1.5 * np.exp(-2.6 * f))
|
|
328
|
+
|
|
329
|
+
self.f = f
|
|
330
|
+
self.strategy = strategy
|
|
331
|
+
self.c1 = c1
|
|
332
|
+
self.c2 = c2
|
|
333
|
+
self.w = w
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# =========================================================================================================
|
|
337
|
+
# Animation
|
|
338
|
+
# =========================================================================================================
|
|
339
|
+
|
|
340
|
+
class PSOAnimation(AnimationCallback):
|
|
341
|
+
|
|
342
|
+
def __init__(self,
|
|
343
|
+
nth_gen=1,
|
|
344
|
+
n_samples_for_surface=200,
|
|
345
|
+
dpi=200,
|
|
346
|
+
**kwargs):
|
|
347
|
+
|
|
348
|
+
super().__init__(nth_gen=nth_gen, dpi=dpi, **kwargs)
|
|
349
|
+
self.n_samples_for_surface = n_samples_for_surface
|
|
350
|
+
self.last_pop = None
|
|
351
|
+
|
|
352
|
+
def do(self, problem, algorithm):
|
|
353
|
+
import matplotlib.pyplot as plt
|
|
354
|
+
|
|
355
|
+
if problem.n_var != 2 or problem.n_obj != 1:
|
|
356
|
+
raise Exception(
|
|
357
|
+
"This visualization can only be used for problems with two variables and one objective!")
|
|
358
|
+
|
|
359
|
+
# draw the problem surface
|
|
360
|
+
FitnessLandscape(problem,
|
|
361
|
+
_type="contour",
|
|
362
|
+
kwargs_contour=dict(alpha=0.3),
|
|
363
|
+
n_samples=self.n_samples_for_surface,
|
|
364
|
+
close_on_destroy=False).do()
|
|
365
|
+
|
|
366
|
+
# get the population
|
|
367
|
+
off = algorithm.particles
|
|
368
|
+
pop = algorithm.particles if self.last_pop is None else self.last_pop
|
|
369
|
+
pbest = algorithm.pop
|
|
370
|
+
|
|
371
|
+
for i in range(len(pop)):
|
|
372
|
+
plt.plot([off[i].X[0], pop[i].X[0]], [off[i].X[1], pop[i].X[1]], color="blue", alpha=0.5)
|
|
373
|
+
plt.plot([pbest[i].X[0], pop[i].X[0]], [pbest[i].X[1], pop[i].X[1]], color="red", alpha=0.5)
|
|
374
|
+
plt.plot([pbest[i].X[0], off[i].X[0]], [pbest[i].X[1], off[i].X[1]], color="red", alpha=0.5)
|
|
375
|
+
|
|
376
|
+
X, F, CV = pbest.get("X", "F", "CV")
|
|
377
|
+
plt.scatter(X[:, 0], X[:, 1], edgecolors="red", marker="*", s=70, facecolors='none', label="pbest")
|
|
378
|
+
|
|
379
|
+
X, F, CV = off.get("X", "F", "CV")
|
|
380
|
+
plt.scatter(X[:, 0], X[:, 1], color="blue", marker="o", s=30, label="particle")
|
|
381
|
+
|
|
382
|
+
X, F, CV = pop.get("X", "F", "CV")
|
|
383
|
+
plt.scatter(X[:, 0], X[:, 1], color="blue", marker="o", s=30, alpha=0.5)
|
|
384
|
+
|
|
385
|
+
opt = algorithm.opt
|
|
386
|
+
X, F, CV = opt.get("X", "F", "CV")
|
|
387
|
+
plt.scatter(X[:, 0], X[:, 1], color="black", marker="x", s=100, label="gbest")
|
|
388
|
+
|
|
389
|
+
xl, xu = problem.bounds()
|
|
390
|
+
plt.xlim(xl[0], xu[0])
|
|
391
|
+
plt.ylim(xl[1], xu[1])
|
|
392
|
+
|
|
393
|
+
plt.title(f"Generation: %s \nf: %.5E" % (algorithm.n_gen, opt[0].F[0]))
|
|
394
|
+
plt.legend()
|
|
395
|
+
|
|
396
|
+
self.last_pop = off.copy(deep=True)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
parse_doc_string(PSO.__init__)
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""
|
|
2
|
+
|
|
3
|
+
Particle Swarm Optimization (PSO)
|
|
4
|
+
|
|
5
|
+
-------------------------------- Description -------------------------------
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
-------------------------------- References --------------------------------
|
|
10
|
+
|
|
11
|
+
[1] J. Blank and K. Deb, pymoo: Multi-Objective Optimization in Python, in IEEE Access,
|
|
12
|
+
vol. 8, pp. 89497-89509, 2020, DOI: 10.1109/ACCESS.2020.2990567
|
|
13
|
+
|
|
14
|
+
-------------------------------- License -----------------------------------
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
----------------------------------------------------------------------------
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from pymoo.algorithms.base.genetic import GeneticAlgorithm
|
|
23
|
+
from pymoo.algorithms.soo.nonconvex.ga import FitnessSurvival
|
|
24
|
+
from pymoo.core.duplicate import NoDuplicateElimination
|
|
25
|
+
from pymoo.core.infill import InfillCriterion
|
|
26
|
+
from pymoo.core.population import Population
|
|
27
|
+
from pymoo.core.replacement import ImprovementReplacement, is_better
|
|
28
|
+
from pymoo.core.variable import Real, Choice, get
|
|
29
|
+
from pymoo.docs import parse_doc_string
|
|
30
|
+
from pymoo.operators.control import EvolutionaryParameterControl
|
|
31
|
+
from pymoo.operators.repair.bounds_repair import repair_random_init, repair_clamp
|
|
32
|
+
from pymoo.operators.sampling.rnd import FloatRandomSampling, random
|
|
33
|
+
from pymoo.termination.default import DefaultSingleObjectiveTermination
|
|
34
|
+
from pymoo.util.display.single import SingleObjectiveOutput
|
|
35
|
+
from pymoo.util.sliding_window import SlidingWindow
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# =========================================================================================================
|
|
39
|
+
# Mating
|
|
40
|
+
# =========================================================================================================
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def pso_canonical(V, X, P_X, L_X, w, c1, c2):
|
|
44
|
+
n_particles, n_var = X.shape
|
|
45
|
+
r1, r2 = np.random.random((n_particles, n_var)), np.random.random((n_particles, n_var))
|
|
46
|
+
Vp = w * V + c1 * r1 * (P_X - X) + c2 * r2 * (L_X - X)
|
|
47
|
+
return Vp
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def pso_rotation_invariant(V, X, P_X, L_X, inertia, c1, c2):
|
|
51
|
+
n_particles, n_var = X.shape
|
|
52
|
+
|
|
53
|
+
r1 = np.random.random((n_particles, n_var))
|
|
54
|
+
p = X + c1 * r1 * (P_X - X)
|
|
55
|
+
|
|
56
|
+
r2 = np.random.random((n_particles, n_var))
|
|
57
|
+
l = X + c2 * r2 * (L_X - X)
|
|
58
|
+
|
|
59
|
+
G = (X + p + l) / 3
|
|
60
|
+
r = np.linalg.norm(G - X, axis=1, keepdims=True)
|
|
61
|
+
|
|
62
|
+
Vp = inertia * V + alea_sphere(G, r) - X
|
|
63
|
+
|
|
64
|
+
return Vp
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def alea_sphere(G, radius):
|
|
68
|
+
n, m = G.shape
|
|
69
|
+
|
|
70
|
+
x = np.random.normal(size=(n, m))
|
|
71
|
+
l = np.sqrt(np.sum(x ** 2, axis=1, keepdims=True))
|
|
72
|
+
|
|
73
|
+
r = np.random.random(size=(n, 1))
|
|
74
|
+
x = r * radius * x / l
|
|
75
|
+
return x + G
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Swarm(InfillCriterion):
|
|
79
|
+
|
|
80
|
+
def __init__(self,
|
|
81
|
+
w=0.7,
|
|
82
|
+
c1=1.4,
|
|
83
|
+
c2=1.4,
|
|
84
|
+
V_max=0.2,
|
|
85
|
+
prob_mut=0.33,
|
|
86
|
+
control=EvolutionaryParameterControl,
|
|
87
|
+
**kwargs):
|
|
88
|
+
|
|
89
|
+
super().__init__(**kwargs)
|
|
90
|
+
self.w = Real(w, bounds=(0.7, 0.9), strict=(0.0, 1.0))
|
|
91
|
+
self.c1 = Real(c1, bounds=(1.2, 1.6), strict=(0.0, None))
|
|
92
|
+
self.c2 = Real(c2, bounds=(1.2, 1.6), strict=(0.0, None))
|
|
93
|
+
self.V_max = V_max
|
|
94
|
+
self.prob_mut = prob_mut
|
|
95
|
+
|
|
96
|
+
# of parameter control should be applied on the mating level
|
|
97
|
+
self.control = control(self)
|
|
98
|
+
|
|
99
|
+
def do(self, problem, pop, n_offsprings, algorithm=None, **kwargs):
|
|
100
|
+
control = self.control
|
|
101
|
+
|
|
102
|
+
# let the parameter control now some information
|
|
103
|
+
control.tell(pop=pop)
|
|
104
|
+
|
|
105
|
+
# set the controlled parameter for the desired number of offsprings
|
|
106
|
+
control.do(n_offsprings)
|
|
107
|
+
|
|
108
|
+
# get the parameters that will be used
|
|
109
|
+
w, c1, c2 = get(self.w, self.c1, self.c2, size=(len(pop), 1))
|
|
110
|
+
|
|
111
|
+
# get all the population that play a role for the mating
|
|
112
|
+
swarm, pbest, lbest = algorithm.swarm, algorithm.pbest, algorithm.lbest
|
|
113
|
+
|
|
114
|
+
V, X, P_X, L_X = swarm.get("V"), swarm.get("X"), pbest.get("X"), lbest.get("X")
|
|
115
|
+
|
|
116
|
+
Vp = pso_canonical(V, X, P_X, L_X, w, c1, c2)
|
|
117
|
+
# Vp = pso_rotation_invariant(V, X, P_X, L_X, w, c1, c2)
|
|
118
|
+
|
|
119
|
+
# if a maximum velocity has been defined
|
|
120
|
+
V_max = self.V_max
|
|
121
|
+
if V_max is not None:
|
|
122
|
+
xl, xu = problem.bounds()
|
|
123
|
+
Vp = repair_clamp(Vp, -V_max * (xu - xl), V_max * (xu - xl))
|
|
124
|
+
|
|
125
|
+
# the position of the new swarm particles
|
|
126
|
+
Xp = X + Vp
|
|
127
|
+
|
|
128
|
+
# if adding the velocity has brought them out of bounds -> bring them back
|
|
129
|
+
if problem.has_bounds():
|
|
130
|
+
Xp = repair_random_init(Xp, X, *problem.bounds())
|
|
131
|
+
|
|
132
|
+
# do a mutation of the global best solution (helps to keep some diversity)
|
|
133
|
+
# Xm = PM(prob=1.0, eta=20).do(problem, swarm).get("X")
|
|
134
|
+
# mut = pbest.get("rank") == 0
|
|
135
|
+
# Xp[mut] = Xm[mut]
|
|
136
|
+
|
|
137
|
+
# recalculate the velocity after the repair has happened
|
|
138
|
+
Vp = Xp - X
|
|
139
|
+
|
|
140
|
+
# create the population
|
|
141
|
+
off = Population.new(X=Xp, V=Vp)
|
|
142
|
+
|
|
143
|
+
# do the reset of particles if their personal bests have not moved much
|
|
144
|
+
# for k, ind in enumerate(pbest):
|
|
145
|
+
# delta = algorithm.delta[k]
|
|
146
|
+
#
|
|
147
|
+
# if k != algorithm.best and delta.is_full() and np.array(delta).mean() < 0.001:
|
|
148
|
+
# particle = FloatRandomSampling().do(problem, 1)[0]
|
|
149
|
+
# particle.set("V", np.zeros(problem.n_var))
|
|
150
|
+
# off[k], pbest[k], lbest[k] = particle, particle, particle
|
|
151
|
+
# delta.clear()
|
|
152
|
+
|
|
153
|
+
# repair the individuals if necessary - disabled if repair is NoRepair
|
|
154
|
+
off = self.repair(problem, off, **kwargs)
|
|
155
|
+
|
|
156
|
+
# advance the parameter control by attaching them to the offsprings
|
|
157
|
+
control.advance(off)
|
|
158
|
+
|
|
159
|
+
return off
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_neighbors(name, N):
|
|
163
|
+
if name == "star":
|
|
164
|
+
return np.tile(np.arange(N), (N, 1))
|
|
165
|
+
elif name == "ring":
|
|
166
|
+
return (np.array([np.arange(3) for _ in range(N)]) + np.arange(N)[:, None] - 1) % N
|
|
167
|
+
elif name.startswith("random"):
|
|
168
|
+
K = 3
|
|
169
|
+
neighbors = []
|
|
170
|
+
for i in range(N):
|
|
171
|
+
vals = np.random.permutation(N)[:K]
|
|
172
|
+
neighbors.append([i] + vals.tolist())
|
|
173
|
+
return neighbors
|
|
174
|
+
else:
|
|
175
|
+
raise Exception(f"Unknown topology: {name}")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# =========================================================================================================
|
|
179
|
+
# Implementation
|
|
180
|
+
# =========================================================================================================
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class EPPSO(GeneticAlgorithm):
|
|
184
|
+
|
|
185
|
+
def __init__(self,
|
|
186
|
+
pop_size=100,
|
|
187
|
+
sampling=FloatRandomSampling(),
|
|
188
|
+
swarm=Swarm(),
|
|
189
|
+
topology="star",
|
|
190
|
+
init_V="zero",
|
|
191
|
+
output=SingleObjectiveOutput(),
|
|
192
|
+
**kwargs):
|
|
193
|
+
|
|
194
|
+
super().__init__(pop_size=pop_size,
|
|
195
|
+
sampling=sampling,
|
|
196
|
+
mating=swarm,
|
|
197
|
+
init_V=init_V,
|
|
198
|
+
n_offsprings=None,
|
|
199
|
+
eliminate_duplicates=NoDuplicateElimination(),
|
|
200
|
+
output=output,
|
|
201
|
+
**kwargs)
|
|
202
|
+
|
|
203
|
+
# how the initial weights should be created
|
|
204
|
+
self.init_V = Choice(init_V, options=["zero", "random"])
|
|
205
|
+
|
|
206
|
+
# how the individual are connected to determine the local (or also global) best
|
|
207
|
+
self.topology = Choice(topology, options=["star", "ring"])
|
|
208
|
+
|
|
209
|
+
# create the neighbors of each particle given the topology
|
|
210
|
+
self.neighbors = get_neighbors(get(self.topology), pop_size)
|
|
211
|
+
|
|
212
|
+
# choose the single-objective default termination
|
|
213
|
+
self.termination = DefaultSingleObjectiveTermination()
|
|
214
|
+
|
|
215
|
+
# the particles that fly around to find good solutions (pop is the pbest)
|
|
216
|
+
self.swarm = None
|
|
217
|
+
|
|
218
|
+
# the personal and local best solution
|
|
219
|
+
self.lbest = None
|
|
220
|
+
self.pbest = None
|
|
221
|
+
|
|
222
|
+
# the integer of the currently best individual
|
|
223
|
+
self.best = None
|
|
224
|
+
|
|
225
|
+
def _initialize_infill(self):
|
|
226
|
+
swarm = super()._initialize_infill()
|
|
227
|
+
|
|
228
|
+
n_var = self.problem.n_var
|
|
229
|
+
init_V = get(self.init_V)
|
|
230
|
+
if init_V == "zero":
|
|
231
|
+
V = np.zeros((len(swarm), n_var))
|
|
232
|
+
elif init_V == "random":
|
|
233
|
+
Xp = random(self.problem, len(swarm))
|
|
234
|
+
V = (swarm.get("X") - Xp) / 2
|
|
235
|
+
else:
|
|
236
|
+
raise Exception("Unknown velocity initialization.")
|
|
237
|
+
swarm.set("V", V)
|
|
238
|
+
|
|
239
|
+
return swarm
|
|
240
|
+
|
|
241
|
+
def _initialize_advance(self, infills=None, **kwargs):
|
|
242
|
+
self.swarm = infills
|
|
243
|
+
self.pbest = self.pop
|
|
244
|
+
self.lbest = Population.create(*self.pbest)
|
|
245
|
+
self.delta = [SlidingWindow(30) for _ in range(len(infills))]
|
|
246
|
+
|
|
247
|
+
FitnessSurvival().do(self.problem, self.pbest, return_indices=True)
|
|
248
|
+
|
|
249
|
+
def _advance(self, infills=None, **kwargs):
|
|
250
|
+
assert infills is not None, "This algorithms uses the AskAndTell interface thus 'infills' must to be provided."
|
|
251
|
+
|
|
252
|
+
X = self.pbest.get("X")
|
|
253
|
+
|
|
254
|
+
self.swarm = infills
|
|
255
|
+
ImprovementReplacement().do(self.problem, self.pbest, infills, inplace=True)
|
|
256
|
+
Xp = self.pbest.get("X")
|
|
257
|
+
|
|
258
|
+
xl, xu = self.problem.bounds()
|
|
259
|
+
delta = np.max(np.abs(X - Xp) / (xu - xl), axis=1)
|
|
260
|
+
[self.delta[k].append(delta[k]) for k in range(len(delta))]
|
|
261
|
+
|
|
262
|
+
pbest = self.pbest
|
|
263
|
+
S = FitnessSurvival().do(self.problem, pbest, return_indices=True)
|
|
264
|
+
rank = pbest.get("rank")
|
|
265
|
+
self.best = S[0]
|
|
266
|
+
|
|
267
|
+
if get(self.topology) == "random-adaptive" and pbest[self.best].get("n_gen") != self.n_gen:
|
|
268
|
+
self.neighbors = get_neighbors(get(self.topology), len(pbest))
|
|
269
|
+
|
|
270
|
+
# send the message from each particle to all its neighbors
|
|
271
|
+
msgs = [[] for _ in range(len(pbest))]
|
|
272
|
+
for k, neighbors in enumerate(self.neighbors):
|
|
273
|
+
for neighbor in neighbors:
|
|
274
|
+
msgs[neighbor].append(k)
|
|
275
|
+
|
|
276
|
+
# now receive the messages and set the new local best (if an improvement has been found)
|
|
277
|
+
for k, msg in enumerate(msgs):
|
|
278
|
+
|
|
279
|
+
# if messages have been received
|
|
280
|
+
if len(msg) > 0:
|
|
281
|
+
|
|
282
|
+
# find the best one from the swarm that have been send
|
|
283
|
+
i = msg[rank[msg].argmin()]
|
|
284
|
+
|
|
285
|
+
# if the best from the message is better than the current local best
|
|
286
|
+
if is_better(pbest[i], self.lbest[k]):
|
|
287
|
+
self.lbest[k] = pbest[i]
|
|
288
|
+
|
|
289
|
+
self.pop = self.pbest
|
|
290
|
+
|
|
291
|
+
def _set_optimum(self, **kwargs):
|
|
292
|
+
k = self.pop.get("rank") == 0
|
|
293
|
+
self.opt = self.pop[k]
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
parse_doc_string(EPPSO.__init__)
|
|
297
|
+
|