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.
Files changed (337) hide show
  1. pymoo/__init__.py +3 -0
  2. pymoo/algorithms/__init__.py +0 -0
  3. pymoo/algorithms/base/__init__.py +0 -0
  4. pymoo/algorithms/base/bracket.py +38 -0
  5. pymoo/algorithms/base/genetic.py +110 -0
  6. pymoo/algorithms/base/line.py +62 -0
  7. pymoo/algorithms/base/local.py +39 -0
  8. pymoo/algorithms/base/meta.py +79 -0
  9. pymoo/algorithms/hyperparameters.py +91 -0
  10. pymoo/algorithms/moo/__init__.py +0 -0
  11. pymoo/algorithms/moo/age.py +310 -0
  12. pymoo/algorithms/moo/age2.py +194 -0
  13. pymoo/algorithms/moo/cmopso.py +239 -0
  14. pymoo/algorithms/moo/ctaea.py +305 -0
  15. pymoo/algorithms/moo/dnsga2.py +80 -0
  16. pymoo/algorithms/moo/kgb.py +450 -0
  17. pymoo/algorithms/moo/moead.py +183 -0
  18. pymoo/algorithms/moo/mopso_cd.py +309 -0
  19. pymoo/algorithms/moo/nsga2.py +113 -0
  20. pymoo/algorithms/moo/nsga3.py +361 -0
  21. pymoo/algorithms/moo/pinsga2.py +370 -0
  22. pymoo/algorithms/moo/rnsga2.py +188 -0
  23. pymoo/algorithms/moo/rnsga3.py +246 -0
  24. pymoo/algorithms/moo/rvea.py +214 -0
  25. pymoo/algorithms/moo/sms.py +196 -0
  26. pymoo/algorithms/moo/spea2.py +191 -0
  27. pymoo/algorithms/moo/unsga3.py +49 -0
  28. pymoo/algorithms/soo/__init__.py +0 -0
  29. pymoo/algorithms/soo/convex/__init__.py +0 -0
  30. pymoo/algorithms/soo/nonconvex/__init__.py +0 -0
  31. pymoo/algorithms/soo/nonconvex/brkga.py +162 -0
  32. pymoo/algorithms/soo/nonconvex/cmaes.py +556 -0
  33. pymoo/algorithms/soo/nonconvex/de.py +283 -0
  34. pymoo/algorithms/soo/nonconvex/direct.py +148 -0
  35. pymoo/algorithms/soo/nonconvex/es.py +213 -0
  36. pymoo/algorithms/soo/nonconvex/g3pcx.py +94 -0
  37. pymoo/algorithms/soo/nonconvex/ga.py +95 -0
  38. pymoo/algorithms/soo/nonconvex/ga_niching.py +223 -0
  39. pymoo/algorithms/soo/nonconvex/isres.py +74 -0
  40. pymoo/algorithms/soo/nonconvex/nelder.py +251 -0
  41. pymoo/algorithms/soo/nonconvex/nrbo.py +191 -0
  42. pymoo/algorithms/soo/nonconvex/optuna.py +80 -0
  43. pymoo/algorithms/soo/nonconvex/pattern.py +185 -0
  44. pymoo/algorithms/soo/nonconvex/pso.py +337 -0
  45. pymoo/algorithms/soo/nonconvex/pso_ep.py +307 -0
  46. pymoo/algorithms/soo/nonconvex/random_search.py +25 -0
  47. pymoo/algorithms/soo/nonconvex/sres.py +56 -0
  48. pymoo/algorithms/soo/univariate/__init__.py +0 -0
  49. pymoo/algorithms/soo/univariate/exp.py +46 -0
  50. pymoo/algorithms/soo/univariate/golden.py +65 -0
  51. pymoo/algorithms/soo/univariate/quadr_interp.py +81 -0
  52. pymoo/algorithms/soo/univariate/wolfe.py +163 -0
  53. pymoo/config.py +33 -0
  54. pymoo/constraints/__init__.py +3 -0
  55. pymoo/constraints/adaptive.py +66 -0
  56. pymoo/constraints/as_obj.py +56 -0
  57. pymoo/constraints/as_penalty.py +41 -0
  58. pymoo/constraints/eps.py +34 -0
  59. pymoo/constraints/from_bounds.py +36 -0
  60. pymoo/core/__init__.py +0 -0
  61. pymoo/core/algorithm.py +408 -0
  62. pymoo/core/callback.py +38 -0
  63. pymoo/core/crossover.py +79 -0
  64. pymoo/core/decision_making.py +102 -0
  65. pymoo/core/decomposition.py +76 -0
  66. pymoo/core/duplicate.py +163 -0
  67. pymoo/core/evaluator.py +116 -0
  68. pymoo/core/indicator.py +34 -0
  69. pymoo/core/individual.py +784 -0
  70. pymoo/core/infill.py +65 -0
  71. pymoo/core/initialization.py +44 -0
  72. pymoo/core/mating.py +39 -0
  73. pymoo/core/meta.py +21 -0
  74. pymoo/core/mixed.py +164 -0
  75. pymoo/core/mutation.py +44 -0
  76. pymoo/core/operator.py +46 -0
  77. pymoo/core/parameters.py +134 -0
  78. pymoo/core/plot.py +208 -0
  79. pymoo/core/population.py +180 -0
  80. pymoo/core/problem.py +373 -0
  81. pymoo/core/recorder.py +99 -0
  82. pymoo/core/repair.py +23 -0
  83. pymoo/core/replacement.py +96 -0
  84. pymoo/core/result.py +52 -0
  85. pymoo/core/sampling.py +45 -0
  86. pymoo/core/selection.py +61 -0
  87. pymoo/core/solution.py +10 -0
  88. pymoo/core/survival.py +107 -0
  89. pymoo/core/termination.py +70 -0
  90. pymoo/core/variable.py +415 -0
  91. pymoo/decomposition/__init__.py +0 -0
  92. pymoo/decomposition/aasf.py +24 -0
  93. pymoo/decomposition/asf.py +10 -0
  94. pymoo/decomposition/pbi.py +13 -0
  95. pymoo/decomposition/perp_dist.py +13 -0
  96. pymoo/decomposition/tchebicheff.py +11 -0
  97. pymoo/decomposition/util.py +13 -0
  98. pymoo/decomposition/weighted_sum.py +8 -0
  99. pymoo/docs.py +187 -0
  100. pymoo/experimental/__init__.py +0 -0
  101. pymoo/experimental/algorithms/__init__.py +0 -0
  102. pymoo/experimental/algorithms/gde3.py +57 -0
  103. pymoo/functions/__init__.py +135 -0
  104. pymoo/functions/compiled/__init__.py +0 -0
  105. pymoo/functions/compiled/calc_perpendicular_distance.cpp +27464 -0
  106. pymoo/functions/compiled/calc_perpendicular_distance.cpython-312-darwin.so +0 -0
  107. pymoo/functions/compiled/decomposition.cpp +28853 -0
  108. pymoo/functions/compiled/decomposition.cpython-312-darwin.so +0 -0
  109. pymoo/functions/compiled/info.cpp +7058 -0
  110. pymoo/functions/compiled/info.cpython-312-darwin.so +0 -0
  111. pymoo/functions/compiled/mnn.cpp +30095 -0
  112. pymoo/functions/compiled/mnn.cpython-312-darwin.so +0 -0
  113. pymoo/functions/compiled/non_dominated_sorting.cpp +35692 -0
  114. pymoo/functions/compiled/non_dominated_sorting.cpython-312-darwin.so +0 -0
  115. pymoo/functions/compiled/pruning_cd.cpp +29248 -0
  116. pymoo/functions/compiled/pruning_cd.cpython-312-darwin.so +0 -0
  117. pymoo/functions/compiled/stochastic_ranking.cpp +28042 -0
  118. pymoo/functions/compiled/stochastic_ranking.cpython-312-darwin.so +0 -0
  119. pymoo/functions/standard/__init__.py +1 -0
  120. pymoo/functions/standard/calc_perpendicular_distance.py +20 -0
  121. pymoo/functions/standard/decomposition.py +18 -0
  122. pymoo/functions/standard/hv.py +5 -0
  123. pymoo/functions/standard/mnn.py +78 -0
  124. pymoo/functions/standard/non_dominated_sorting.py +474 -0
  125. pymoo/functions/standard/pruning_cd.py +93 -0
  126. pymoo/functions/standard/stochastic_ranking.py +42 -0
  127. pymoo/gradient/__init__.py +24 -0
  128. pymoo/gradient/automatic.py +85 -0
  129. pymoo/gradient/grad_autograd.py +105 -0
  130. pymoo/gradient/grad_complex.py +35 -0
  131. pymoo/gradient/grad_jax.py +51 -0
  132. pymoo/gradient/numpy.py +22 -0
  133. pymoo/gradient/toolbox/__init__.py +19 -0
  134. pymoo/indicators/__init__.py +0 -0
  135. pymoo/indicators/distance_indicator.py +55 -0
  136. pymoo/indicators/gd.py +7 -0
  137. pymoo/indicators/gd_plus.py +7 -0
  138. pymoo/indicators/hv/__init__.py +59 -0
  139. pymoo/indicators/hv/approximate.py +105 -0
  140. pymoo/indicators/hv/exact.py +68 -0
  141. pymoo/indicators/hv/exact_2d.py +102 -0
  142. pymoo/indicators/igd.py +7 -0
  143. pymoo/indicators/igd_plus.py +7 -0
  144. pymoo/indicators/kktpm.py +151 -0
  145. pymoo/indicators/migd.py +55 -0
  146. pymoo/indicators/rmetric.py +203 -0
  147. pymoo/indicators/spacing.py +52 -0
  148. pymoo/mcdm/__init__.py +0 -0
  149. pymoo/mcdm/compromise_programming.py +19 -0
  150. pymoo/mcdm/high_tradeoff.py +40 -0
  151. pymoo/mcdm/pseudo_weights.py +32 -0
  152. pymoo/operators/__init__.py +0 -0
  153. pymoo/operators/control.py +190 -0
  154. pymoo/operators/crossover/__init__.py +0 -0
  155. pymoo/operators/crossover/binx.py +47 -0
  156. pymoo/operators/crossover/dex.py +125 -0
  157. pymoo/operators/crossover/erx.py +164 -0
  158. pymoo/operators/crossover/expx.py +53 -0
  159. pymoo/operators/crossover/hux.py +37 -0
  160. pymoo/operators/crossover/nox.py +25 -0
  161. pymoo/operators/crossover/ox.py +88 -0
  162. pymoo/operators/crossover/pcx.py +84 -0
  163. pymoo/operators/crossover/pntx.py +49 -0
  164. pymoo/operators/crossover/sbx.py +137 -0
  165. pymoo/operators/crossover/spx.py +5 -0
  166. pymoo/operators/crossover/ux.py +20 -0
  167. pymoo/operators/mutation/__init__.py +0 -0
  168. pymoo/operators/mutation/bitflip.py +17 -0
  169. pymoo/operators/mutation/gauss.py +60 -0
  170. pymoo/operators/mutation/inversion.py +42 -0
  171. pymoo/operators/mutation/nom.py +7 -0
  172. pymoo/operators/mutation/pm.py +96 -0
  173. pymoo/operators/mutation/rm.py +23 -0
  174. pymoo/operators/repair/__init__.py +0 -0
  175. pymoo/operators/repair/bounce_back.py +32 -0
  176. pymoo/operators/repair/bounds_repair.py +97 -0
  177. pymoo/operators/repair/inverse_penalty.py +91 -0
  178. pymoo/operators/repair/rounding.py +18 -0
  179. pymoo/operators/repair/to_bound.py +31 -0
  180. pymoo/operators/repair/vtype.py +11 -0
  181. pymoo/operators/sampling/__init__.py +0 -0
  182. pymoo/operators/sampling/lhs.py +76 -0
  183. pymoo/operators/sampling/rnd.py +52 -0
  184. pymoo/operators/selection/__init__.py +0 -0
  185. pymoo/operators/selection/rnd.py +75 -0
  186. pymoo/operators/selection/tournament.py +78 -0
  187. pymoo/operators/survival/__init__.py +0 -0
  188. pymoo/operators/survival/rank_and_crowding/__init__.py +1 -0
  189. pymoo/operators/survival/rank_and_crowding/classes.py +212 -0
  190. pymoo/operators/survival/rank_and_crowding/metrics.py +208 -0
  191. pymoo/optimize.py +72 -0
  192. pymoo/parallelization/__init__.py +15 -0
  193. pymoo/parallelization/dask.py +25 -0
  194. pymoo/parallelization/joblib.py +28 -0
  195. pymoo/parallelization/ray.py +31 -0
  196. pymoo/parallelization/starmap.py +24 -0
  197. pymoo/problems/__init__.py +157 -0
  198. pymoo/problems/dyn.py +47 -0
  199. pymoo/problems/dynamic/__init__.py +0 -0
  200. pymoo/problems/dynamic/cec2015.py +108 -0
  201. pymoo/problems/dynamic/df.py +451 -0
  202. pymoo/problems/dynamic/misc.py +167 -0
  203. pymoo/problems/functional.py +48 -0
  204. pymoo/problems/many/__init__.py +5 -0
  205. pymoo/problems/many/cdtlz.py +159 -0
  206. pymoo/problems/many/dcdtlz.py +88 -0
  207. pymoo/problems/many/dtlz.py +264 -0
  208. pymoo/problems/many/wfg.py +553 -0
  209. pymoo/problems/multi/__init__.py +14 -0
  210. pymoo/problems/multi/bnh.py +34 -0
  211. pymoo/problems/multi/carside.py +48 -0
  212. pymoo/problems/multi/clutch.py +104 -0
  213. pymoo/problems/multi/csi.py +55 -0
  214. pymoo/problems/multi/ctp.py +198 -0
  215. pymoo/problems/multi/dascmop.py +213 -0
  216. pymoo/problems/multi/kursawe.py +25 -0
  217. pymoo/problems/multi/modact.py +68 -0
  218. pymoo/problems/multi/mw.py +400 -0
  219. pymoo/problems/multi/omnitest.py +48 -0
  220. pymoo/problems/multi/osy.py +32 -0
  221. pymoo/problems/multi/srn.py +28 -0
  222. pymoo/problems/multi/sympart.py +94 -0
  223. pymoo/problems/multi/tnk.py +24 -0
  224. pymoo/problems/multi/truss2d.py +83 -0
  225. pymoo/problems/multi/welded_beam.py +41 -0
  226. pymoo/problems/multi/wrm.py +36 -0
  227. pymoo/problems/multi/zdt.py +151 -0
  228. pymoo/problems/multi_to_single.py +22 -0
  229. pymoo/problems/single/__init__.py +12 -0
  230. pymoo/problems/single/ackley.py +24 -0
  231. pymoo/problems/single/cantilevered_beam.py +34 -0
  232. pymoo/problems/single/flowshop_scheduling.py +113 -0
  233. pymoo/problems/single/g.py +874 -0
  234. pymoo/problems/single/griewank.py +18 -0
  235. pymoo/problems/single/himmelblau.py +15 -0
  236. pymoo/problems/single/knapsack.py +49 -0
  237. pymoo/problems/single/mopta08.py +26 -0
  238. pymoo/problems/single/multimodal.py +20 -0
  239. pymoo/problems/single/pressure_vessel.py +30 -0
  240. pymoo/problems/single/rastrigin.py +20 -0
  241. pymoo/problems/single/rosenbrock.py +22 -0
  242. pymoo/problems/single/schwefel.py +18 -0
  243. pymoo/problems/single/simple.py +13 -0
  244. pymoo/problems/single/sphere.py +19 -0
  245. pymoo/problems/single/traveling_salesman.py +79 -0
  246. pymoo/problems/single/zakharov.py +19 -0
  247. pymoo/problems/static.py +14 -0
  248. pymoo/problems/util.py +42 -0
  249. pymoo/problems/zero_to_one.py +27 -0
  250. pymoo/termination/__init__.py +23 -0
  251. pymoo/termination/collection.py +12 -0
  252. pymoo/termination/cv.py +48 -0
  253. pymoo/termination/default.py +45 -0
  254. pymoo/termination/delta.py +64 -0
  255. pymoo/termination/fmin.py +16 -0
  256. pymoo/termination/ftol.py +144 -0
  257. pymoo/termination/indicator.py +49 -0
  258. pymoo/termination/max_eval.py +14 -0
  259. pymoo/termination/max_gen.py +15 -0
  260. pymoo/termination/max_time.py +20 -0
  261. pymoo/termination/robust.py +34 -0
  262. pymoo/termination/xtol.py +33 -0
  263. pymoo/util/__init__.py +33 -0
  264. pymoo/util/archive.py +152 -0
  265. pymoo/util/cache.py +29 -0
  266. pymoo/util/clearing.py +82 -0
  267. pymoo/util/display/__init__.py +0 -0
  268. pymoo/util/display/column.py +52 -0
  269. pymoo/util/display/display.py +34 -0
  270. pymoo/util/display/multi.py +100 -0
  271. pymoo/util/display/output.py +53 -0
  272. pymoo/util/display/progress.py +54 -0
  273. pymoo/util/display/single.py +67 -0
  274. pymoo/util/dominator.py +67 -0
  275. pymoo/util/hv.py +21 -0
  276. pymoo/util/matlab_engine.py +39 -0
  277. pymoo/util/misc.py +447 -0
  278. pymoo/util/nds/__init__.py +0 -0
  279. pymoo/util/nds/dominance_degree_non_dominated_sort.py +159 -0
  280. pymoo/util/nds/efficient_non_dominated_sort.py +152 -0
  281. pymoo/util/nds/fast_non_dominated_sort.py +70 -0
  282. pymoo/util/nds/find_non_dominated.py +54 -0
  283. pymoo/util/nds/naive_non_dominated_sort.py +36 -0
  284. pymoo/util/nds/non_dominated_sorting.py +94 -0
  285. pymoo/util/nds/tree_based_non_dominated_sort.py +133 -0
  286. pymoo/util/normalization.py +312 -0
  287. pymoo/util/optimum.py +42 -0
  288. pymoo/util/randomized_argsort.py +63 -0
  289. pymoo/util/ref_dirs/__init__.py +24 -0
  290. pymoo/util/ref_dirs/construction.py +89 -0
  291. pymoo/util/ref_dirs/das_dennis.py +52 -0
  292. pymoo/util/ref_dirs/energy.py +317 -0
  293. pymoo/util/ref_dirs/energy_layer.py +119 -0
  294. pymoo/util/ref_dirs/genetic_algorithm.py +64 -0
  295. pymoo/util/ref_dirs/incremental.py +69 -0
  296. pymoo/util/ref_dirs/misc.py +128 -0
  297. pymoo/util/ref_dirs/optimizer.py +59 -0
  298. pymoo/util/ref_dirs/performance.py +162 -0
  299. pymoo/util/ref_dirs/reduction.py +85 -0
  300. pymoo/util/ref_dirs/sample_and_map.py +24 -0
  301. pymoo/util/reference_direction.py +258 -0
  302. pymoo/util/remote.py +55 -0
  303. pymoo/util/roulette.py +29 -0
  304. pymoo/util/running_metric.py +128 -0
  305. pymoo/util/sliding_window.py +25 -0
  306. pymoo/util/value_functions.py +720 -0
  307. pymoo/util/vectors.py +40 -0
  308. pymoo/util/vf_dominator.py +102 -0
  309. pymoo/vendor/__init__.py +0 -0
  310. pymoo/vendor/cec2018.py +398 -0
  311. pymoo/vendor/gta.py +617 -0
  312. pymoo/vendor/vendor_cmaes.py +421 -0
  313. pymoo/vendor/vendor_coco.py +81 -0
  314. pymoo/vendor/vendor_scipy.py +232 -0
  315. pymoo/version.py +1 -0
  316. pymoo/visualization/__init__.py +21 -0
  317. pymoo/visualization/app/__init__.py +0 -0
  318. pymoo/visualization/app/pso.py +61 -0
  319. pymoo/visualization/fitness_landscape.py +128 -0
  320. pymoo/visualization/heatmap.py +123 -0
  321. pymoo/visualization/matplotlib.py +61 -0
  322. pymoo/visualization/pcp.py +121 -0
  323. pymoo/visualization/petal.py +91 -0
  324. pymoo/visualization/radar.py +108 -0
  325. pymoo/visualization/radviz.py +68 -0
  326. pymoo/visualization/scatter.py +150 -0
  327. pymoo/visualization/star_coordinate.py +75 -0
  328. pymoo/visualization/util.py +296 -0
  329. pymoo/visualization/video/__init__.py +0 -0
  330. pymoo/visualization/video/callback_video.py +82 -0
  331. pymoo/visualization/video/one_var_one_obj.py +57 -0
  332. pymoo/visualization/video/two_var_one_obj.py +62 -0
  333. pymoo-0.6.1.6.dist-info/METADATA +209 -0
  334. pymoo-0.6.1.6.dist-info/RECORD +337 -0
  335. pymoo-0.6.1.6.dist-info/WHEEL +6 -0
  336. pymoo-0.6.1.6.dist-info/licenses/LICENSE +191 -0
  337. 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__)