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 @@
1
+ # Standard (Python) function implementations
@@ -0,0 +1,20 @@
1
+ """
2
+ Standard Python implementation of perpendicular distance calculation.
3
+ """
4
+
5
+ import numpy as np
6
+
7
+
8
+ def calc_perpendicular_distance(N, ref_dirs):
9
+ """Calculate perpendicular distance from points to reference directions."""
10
+ u = np.tile(ref_dirs, (len(N), 1))
11
+ v = np.repeat(N, len(ref_dirs), axis=0)
12
+
13
+ norm_u = np.linalg.norm(u, axis=1)
14
+
15
+ scalar_proj = np.sum(v * u, axis=1) / norm_u
16
+ proj = scalar_proj[:, None] * u / norm_u[:, None]
17
+ val = np.linalg.norm(proj - v, axis=1)
18
+ matrix = np.reshape(val, (len(N), len(ref_dirs)))
19
+
20
+ return matrix
@@ -0,0 +1,18 @@
1
+ """
2
+ Standard Python implementations of decomposition functions.
3
+ """
4
+
5
+ import numpy as np
6
+
7
+
8
+ def calc_distance_to_weights(F, weights, utopian_point=None):
9
+ """Calculate distance to weights for decomposition methods."""
10
+ norm = np.linalg.norm(weights, axis=1)
11
+
12
+ if utopian_point is not None:
13
+ F = F - utopian_point
14
+
15
+ d1 = (F * weights).sum(axis=1) / norm
16
+ d2 = np.linalg.norm(F - (d1[:, None] * weights / norm[:, None]), axis=1)
17
+
18
+ return d1, d2
@@ -0,0 +1,5 @@
1
+ from moocore import hypervolume as _hypervolume
2
+
3
+
4
+ def hv(ref_point, F):
5
+ return _hypervolume(F, ref = ref_point)
@@ -0,0 +1,78 @@
1
+ """
2
+ Standard Python implementation of M-nearest neighbor calculations.
3
+ """
4
+
5
+ import numpy as np
6
+ from scipy.spatial.distance import pdist, squareform
7
+
8
+
9
+ def calc_mnn(X, n_remove=0):
10
+ """Calculate M-nearest neighbor distances."""
11
+ return calc_mnn_base(X, n_remove=n_remove, twonn=False)
12
+
13
+
14
+ def calc_2nn(X, n_remove=0):
15
+ """Calculate 2-nearest neighbor distances."""
16
+ return calc_mnn_base(X, n_remove=n_remove, twonn=True)
17
+
18
+
19
+ def calc_mnn_base(X, n_remove=0, twonn=False):
20
+ """Base function for M-nearest neighbor calculations."""
21
+ N = X.shape[0]
22
+ M = X.shape[1]
23
+
24
+ if N <= M:
25
+ return np.full(N, np.inf)
26
+
27
+ if n_remove <= (N - M):
28
+ if n_remove < 0:
29
+ n_remove = 0
30
+ else:
31
+ pass
32
+ else:
33
+ n_remove = N - M
34
+
35
+ if twonn:
36
+ M = 2
37
+
38
+ extremes_min = np.argmin(X, axis=0)
39
+ extremes_max = np.argmax(X, axis=0)
40
+
41
+ min_vals = np.min(X, axis=0)
42
+ max_vals = np.max(X, axis=0)
43
+
44
+ extremes = np.concatenate((extremes_min, extremes_max))
45
+
46
+ X = (X - min_vals) / (max_vals - min_vals)
47
+
48
+ H = np.arange(N)
49
+
50
+ D = squareform(pdist(X, metric="sqeuclidean"))
51
+ Dnn = np.partition(D, range(1, M+1), axis=1)[:, 1:M+1]
52
+ d = np.prod(Dnn, axis=1)
53
+ d[extremes] = np.inf
54
+
55
+ n_removed = 0
56
+
57
+ # While n_remove not achieved
58
+ while n_removed < (n_remove - 1):
59
+
60
+ # Obtain element to drop
61
+ _d = d[H]
62
+ _k = np.argmin(_d)
63
+ k = H[_k]
64
+ H = H[H != k]
65
+
66
+ # Update index
67
+ n_removed = n_removed + 1
68
+ if n_removed == n_remove:
69
+ break
70
+
71
+ else:
72
+
73
+ D[:, k] = np.inf
74
+ Dnn[H] = np.partition(D[H], range(1, M+1), axis=1)[:, 1:M+1]
75
+ d[H] = np.prod(Dnn[H], axis=1)
76
+ d[extremes] = np.inf
77
+
78
+ return d
@@ -0,0 +1,474 @@
1
+ """
2
+ Standard Python implementations of non-dominated sorting algorithms.
3
+ """
4
+
5
+ import numpy as np
6
+ from math import floor
7
+ import weakref
8
+ from typing import Literal, List
9
+
10
+ from pymoo.util.dominator import Dominator
11
+
12
+
13
+ def fast_non_dominated_sort(F, dominator=Dominator(), native_biobj_sorting=False, **kwargs):
14
+ """Fast non-dominated sorting algorithm.
15
+
16
+ Parameters
17
+ ----------
18
+ F : np.ndarray
19
+ Objective values for each individual.
20
+ dominator : Dominator
21
+ Dominator object for custom dominance relations.
22
+ native_biobj_sorting : bool
23
+ If True, use specialized O(N log N) algorithm for bi-objective problems.
24
+ Default is False.
25
+ """
26
+ if F.size == 0:
27
+ return []
28
+
29
+ n_points, n_objectives = F.shape
30
+
31
+ # For single objective or single point, return immediately
32
+ if n_points <= 1:
33
+ return [list(range(n_points))] if n_points == 1 else []
34
+
35
+ # For bi-objective problems, optionally use specialized O(N log N) algorithm
36
+ if native_biobj_sorting and n_objectives == 2:
37
+ return _fast_biobjective_nondominated_sort(F)
38
+
39
+ if "dominator" in kwargs:
40
+ M = Dominator.calc_domination_matrix(F)
41
+ else:
42
+ M = dominator.calc_domination_matrix(F)
43
+
44
+ # calculate the dominance matrix
45
+ n = M.shape[0]
46
+
47
+ fronts = []
48
+
49
+ if n == 0:
50
+ return fronts
51
+
52
+ # final rank that will be returned
53
+ n_ranked = 0
54
+ ranked = np.zeros(n, dtype=int)
55
+
56
+ # for each individual a list of all individuals that are dominated by this one
57
+ is_dominating = [[] for _ in range(n)]
58
+
59
+ # storage for the number of solutions dominated this one
60
+ n_dominated = np.zeros(n)
61
+
62
+ current_front = []
63
+
64
+ for i in range(n):
65
+ for j in range(i + 1, n):
66
+ rel = M[i, j]
67
+ if rel == 1:
68
+ is_dominating[i].append(j)
69
+ n_dominated[j] += 1
70
+ elif rel == -1:
71
+ is_dominating[j].append(i)
72
+ n_dominated[i] += 1
73
+
74
+ if n_dominated[i] == 0:
75
+ current_front.append(i)
76
+ ranked[i] = 1.0
77
+ n_ranked += 1
78
+
79
+ # append the first front to the current front
80
+ fronts.append(current_front)
81
+
82
+ # while not all solutions are assigned to a pareto front
83
+ while n_ranked < n:
84
+ next_front = []
85
+
86
+ # for each individual in the current front
87
+ for i in current_front:
88
+ # all solutions that are dominated by this individuals
89
+ for j in is_dominating[i]:
90
+ n_dominated[j] -= 1
91
+ if n_dominated[j] == 0:
92
+ next_front.append(j)
93
+ ranked[j] = 1.0
94
+ n_ranked += 1
95
+
96
+ fronts.append(next_front)
97
+ current_front = next_front
98
+
99
+ return fronts
100
+
101
+
102
+ def _fast_biobjective_nondominated_sort(F):
103
+ """
104
+ Specialized algorithm for bi-objective problems.
105
+ Uses the efficient skyline/multi-criteria approach with O(N log N) complexity.
106
+ """
107
+ n_points = F.shape[0]
108
+
109
+ if n_points == 0:
110
+ return []
111
+
112
+ # Sort by first objective ascending
113
+ sorted_indices = np.argsort(F[:, 0])
114
+ sorted_F = F[sorted_indices]
115
+
116
+ fronts = []
117
+ assigned = [False] * n_points
118
+ n_assigned = 0
119
+
120
+ while n_assigned < n_points:
121
+ current_front = []
122
+ current_indices = []
123
+
124
+ # Track the minimum second objective seen in the current front
125
+ min_second_obj = float('inf')
126
+
127
+ for i in range(n_points):
128
+ if assigned[i]:
129
+ continue
130
+
131
+ # Check if current point is dominated by any point in current front
132
+ is_dominated = False
133
+ if current_indices: # If there are already points in the current front
134
+ # Since points are sorted by first objective, we only need to check
135
+ # if its second objective is greater than the minimum second objective in front
136
+ if sorted_F[i, 1] >= min_second_obj:
137
+ is_dominated = True
138
+
139
+ if not is_dominated:
140
+ # Add this point to the current front
141
+ current_front.append(sorted_indices[i])
142
+ current_indices.append(i)
143
+ assigned[i] = True
144
+ n_assigned += 1
145
+ # Update the minimum second objective
146
+ min_second_obj = min(min_second_obj, sorted_F[i, 1])
147
+
148
+ if current_front:
149
+ fronts.append(current_front)
150
+ else:
151
+ break
152
+
153
+ return fronts
154
+
155
+ def find_non_dominated(F, epsilon=0.0):
156
+ """
157
+ Simple and efficient implementation to find only non-dominated points.
158
+ Uses straightforward O(n²) algorithm with early termination.
159
+ """
160
+ n_points = F.shape[0]
161
+ non_dominated_indices = []
162
+
163
+ if n_points == 0:
164
+ return np.array([], dtype=int)
165
+
166
+ # Check each point to see if it's non-dominated
167
+ for i in range(n_points):
168
+ is_dominated = False
169
+
170
+ # Check if point i is dominated by any other point j
171
+ for j in range(n_points):
172
+ if i != j:
173
+ # Check if j dominates i
174
+ dominates = True
175
+ at_least_one_better = False
176
+
177
+ for k in range(F.shape[1]): # for each objective
178
+ if F[j, k] + epsilon < F[i, k]: # j is better than i in objective k
179
+ at_least_one_better = True
180
+ elif F[j, k] > F[i, k] + epsilon: # j is worse than i in objective k
181
+ dominates = False
182
+ break # Early termination in objective loop
183
+
184
+ # j dominates i if j is at least as good in all objectives and better in at least one
185
+ if dominates and at_least_one_better:
186
+ is_dominated = True
187
+ break # Early termination - no need to check other points
188
+
189
+ # If point i is not dominated by any other point, it's non-dominated
190
+ if not is_dominated:
191
+ non_dominated_indices.append(i)
192
+
193
+ return np.array(non_dominated_indices, dtype=int)
194
+
195
+
196
+ def efficient_non_dominated_sort(F, strategy="sequential"):
197
+ """Efficient Non-dominated Sorting (ENS)"""
198
+ assert (strategy in ["sequential", 'binary']), "Invalid search strategy"
199
+
200
+ # the shape of the input
201
+ N, M = F.shape
202
+
203
+ # do a lexicographic ordering
204
+ I = np.lexsort(F.T[::-1])
205
+ F = F[I]
206
+
207
+ # front ranks for each individual
208
+ fronts = []
209
+
210
+ for i in range(N):
211
+
212
+ if strategy == 'sequential':
213
+ k = sequential_search(F, i, fronts)
214
+ else:
215
+ k = binary_search(F, i, fronts)
216
+
217
+ # create empty fronts if necessary
218
+ if k >= len(fronts):
219
+ fronts.append([])
220
+
221
+ # append the current individual to a front
222
+ fronts[k].append(i)
223
+
224
+ # now map the fronts back to the originally sorting
225
+ ret = []
226
+ for front in fronts:
227
+ ret.append(I[front])
228
+
229
+ return ret
230
+
231
+
232
+ def sequential_search(F, i, fronts) -> int:
233
+ """Find the front rank for the i-th individual through sequential search."""
234
+ num_found_fronts = len(fronts)
235
+ k = 0 # the front now checked
236
+ current = F[i]
237
+ while True:
238
+ if num_found_fronts == 0:
239
+ return 0
240
+ # solutions in the k-th front, examine in reverse order
241
+ fk_indices = fronts[k]
242
+ solutions = F[fk_indices[::-1]]
243
+ non_dominated = True
244
+ for f in solutions:
245
+ relation = Dominator.get_relation(current, f)
246
+ if relation == -1:
247
+ non_dominated = False
248
+ break
249
+ if non_dominated:
250
+ return k
251
+ else:
252
+ k += 1
253
+ if k >= num_found_fronts:
254
+ # move the individual to a new front
255
+ return num_found_fronts
256
+
257
+
258
+ def binary_search(F, i, fronts):
259
+ """Find the front rank for the i-th individual through binary search."""
260
+ num_found_fronts = len(fronts)
261
+ if num_found_fronts == 0:
262
+ return 0
263
+
264
+ k_min = 0 # the lower bound for checking
265
+ k_max = num_found_fronts # the upper bound for checking
266
+ k = floor((k_max + k_min) / 2 + 0.5) # the front now checked
267
+ current = F[i]
268
+ while True:
269
+
270
+ # solutions in the k-th front, examine in reverse order
271
+ fk_indices = fronts[k - 1]
272
+ solutions = F[fk_indices[::-1]]
273
+ non_dominated = True
274
+
275
+ for f in solutions:
276
+ relation = Dominator.get_relation(current, f)
277
+ if relation == -1:
278
+ non_dominated = False
279
+ break
280
+
281
+ # binary search
282
+ if non_dominated:
283
+ if k == k_min + 1:
284
+ return k - 1
285
+ else:
286
+ k_max = k
287
+ k = floor((k_max + k_min) / 2 + 0.5)
288
+ else:
289
+ k_min = k
290
+ if k_max == k_min + 1 and k_max < num_found_fronts:
291
+ return k_max - 1
292
+ elif k_min == num_found_fronts:
293
+ return num_found_fronts
294
+ else:
295
+ k = floor((k_max + k_min) / 2 + 0.5)
296
+
297
+
298
+ class Tree:
299
+ """Implementation of N-ary tree for tree-based non-dominated sorting."""
300
+
301
+ def __init__(self, key, num_branch, children=None, parent=None):
302
+ self.key = key
303
+ self.children = children or [None for _ in range(num_branch)]
304
+ self._parent = weakref.ref(parent) if parent else None
305
+
306
+ @property
307
+ def parent(self):
308
+ if self._parent:
309
+ return self._parent()
310
+
311
+ def __getstate__(self):
312
+ self._parent = None
313
+
314
+ def __setstate__(self, state):
315
+ self.__dict__ = state
316
+ for child in self.children:
317
+ child._parent = weakref.ref(self)
318
+
319
+ def traversal(self, visit=None, *args, **kwargs):
320
+ if visit is not None:
321
+ visit(self, *args, **kwargs)
322
+ l = [self]
323
+ for child in self.children:
324
+ if child is not None:
325
+ l += child.traversal(visit, *args, **kwargs)
326
+ return l
327
+
328
+
329
+ def tree_based_non_dominated_sort(F):
330
+ """Tree-based efficient non-dominated sorting (T-ENS)."""
331
+ N, M = F.shape
332
+ # sort the rows in F
333
+ indices = np.lexsort(F.T[::-1])
334
+ F = F[indices]
335
+
336
+ obj_seq = np.argsort(F[:, :0:-1], axis=1) + 1
337
+
338
+ k = 0
339
+
340
+ forest = []
341
+
342
+ left = np.full(N, True)
343
+ while np.any(left):
344
+ forest.append(None)
345
+ for p, flag in enumerate(left):
346
+ if flag:
347
+ update_tree(F, p, forest, k, left, obj_seq)
348
+ k += 1
349
+
350
+ # convert forest to fronts
351
+ fronts = [[] for _ in range(k)]
352
+ for k, tree in enumerate(forest):
353
+ fronts[k].extend([indices[node.key] for node in tree.traversal()])
354
+ return fronts
355
+
356
+
357
+ def update_tree(F, p, forest, k, left, obj_seq):
358
+ """Update tree for tree-based non-dominated sorting."""
359
+ _, M = F.shape
360
+ if forest[k] is None:
361
+ forest[k] = Tree(key=p, num_branch=M - 1)
362
+ left[p] = False
363
+ elif check_tree(F, p, forest[k], obj_seq, True):
364
+ left[p] = False
365
+
366
+
367
+ def check_tree(F, p, tree, obj_seq, add_pos):
368
+ """Check tree for tree-based non-dominated sorting."""
369
+ if tree is None:
370
+ return True
371
+
372
+ N, M = F.shape
373
+
374
+ # find the minimal index m satisfying that p[obj_seq[tree.root][m]] < tree.root[obj_seq[tree.root][m]]
375
+ m = 0
376
+ while m < M - 1 and F[p, obj_seq[tree.key, m]] >= F[tree.key, obj_seq[tree.key, m]]:
377
+ m += 1
378
+
379
+ # if m not found
380
+ if m == M - 1:
381
+ # p is dominated by the solution at the root
382
+ return False
383
+ else:
384
+ for i in range(m + 1):
385
+ # p is dominated by a solution in the branch of the tree
386
+ if not check_tree(F, p, tree.children[i], obj_seq, i == m and add_pos):
387
+ return False
388
+
389
+ if tree.children[m] is None and add_pos:
390
+ # add p to the branch of the tree
391
+ tree.children[m] = Tree(key=p, num_branch=M - 1)
392
+ return True
393
+
394
+
395
+ def construct_comp_matrix(vec: np.ndarray, sorted_idx: np.ndarray) -> np.ndarray:
396
+ """Construct the comparison matrix from a row-vector vec."""
397
+ n = vec.shape[0]
398
+ c = np.zeros(shape=(n, n), dtype=np.int32)
399
+
400
+ # the elements of the b(0)-th row in C are all set to 1
401
+ c[sorted_idx[0], :] = 1
402
+
403
+ for i in range(1, n):
404
+ if vec[sorted_idx[i]] == vec[sorted_idx[i - 1]]:
405
+ # the rows in C corresponding to the same elements in w are identical
406
+ c[sorted_idx[i]] = c[sorted_idx[i - 1]]
407
+ else:
408
+ c[sorted_idx[i], sorted_idx[i:]] = 1
409
+
410
+ return c
411
+
412
+
413
+ def construct_domination_matrix(f_scores: np.ndarray, **kwargs) -> np.ndarray:
414
+ """Calculate the dominance degree matrix for a set of vectors."""
415
+ d = np.zeros((f_scores.shape[0], f_scores.shape[0]), dtype=np.int32)
416
+ b = np.apply_over_axes(np.argsort, f_scores, axes=0)
417
+ for vec, srt in zip(f_scores.T, b.T):
418
+ d += construct_comp_matrix(vec, srt)
419
+ d = np.where(
420
+ np.logical_and(d == f_scores.shape[-1], d.T == f_scores.shape[-1]), 0, d
421
+ )
422
+ return d
423
+
424
+
425
+ def dda_ns(f_scores: np.ndarray, **kwargs) -> List[List[int]]:
426
+ """DDA-NS algorithm."""
427
+ d_mx = construct_domination_matrix(f_scores)
428
+ max_d = np.empty((f_scores.shape[0],), dtype=np.int32)
429
+
430
+ fronts = []
431
+ count = 0
432
+ while count < f_scores.shape[0]:
433
+ # Max(D) is the row vector containing the maximum elements from each column of D
434
+ np.max(d_mx, out=max_d, axis=0)
435
+ front = [i for i, m_d in enumerate(max_d) if 0 <= m_d < f_scores.shape[-1]]
436
+ count += len(front)
437
+ d_mx[front] = -1
438
+ d_mx[:, front] = -1
439
+ fronts.append(front)
440
+
441
+ return fronts
442
+
443
+
444
+ def dda_ens(f_scores: np.ndarray, **kwargs) -> List[List[int]]:
445
+ """DDA-ENS (efficient DDA) algorithm."""
446
+ d_mx = construct_domination_matrix(f_scores)
447
+
448
+ fronts: List[List[int]] = []
449
+ for s in np.lexsort(f_scores.T):
450
+ isinserted = False
451
+ for fk in fronts:
452
+ if not (d_mx[fk, s] == f_scores.shape[1]).any():
453
+ fk.append(s)
454
+ isinserted = True
455
+ break
456
+ if not isinserted:
457
+ fronts.append([s])
458
+ return fronts
459
+
460
+
461
+ def dominance_degree_non_dominated_sort(
462
+ f_scores: np.ndarray, strategy: Literal["efficient", "fast"] = "efficient"
463
+ ) -> List[List[int]]:
464
+ """Perform non-dominating sort with the specified algorithm."""
465
+ if strategy == "efficient":
466
+ return dda_ens(f_scores)
467
+ if strategy == "fast":
468
+ return dda_ns(f_scores)
469
+ raise ValueError("Invalid search strategy")
470
+
471
+
472
+ def fast_best_order_sort(*args, **kwargs):
473
+ """Placeholder for fast_best_order_sort - only available in Cython."""
474
+ raise NotImplementedError("fast_best_order_sort is only available in compiled (Cython) version")
@@ -0,0 +1,93 @@
1
+ """
2
+ Standard Python implementation of pruning with crowding distance.
3
+ """
4
+
5
+ import numpy as np
6
+
7
+
8
+ def calc_pcd(X, n_remove=0):
9
+ """Calculate pruning based on crowding distance."""
10
+ N = X.shape[0]
11
+ M = X.shape[1]
12
+
13
+ if n_remove <= (N - M):
14
+ if n_remove < 0:
15
+ n_remove = 0
16
+ else:
17
+ pass
18
+ else:
19
+ n_remove = N - M
20
+
21
+ extremes_min = np.argmin(X, axis=0)
22
+ extremes_max = np.argmax(X, axis=0)
23
+
24
+ min_vals = np.min(X, axis=0)
25
+ max_vals = np.max(X, axis=0)
26
+
27
+ extremes = np.concatenate((extremes_min, extremes_max))
28
+
29
+ X = (X - min_vals) / (max_vals - min_vals)
30
+
31
+ H = np.arange(N)
32
+ d = np.full(N, np.inf)
33
+
34
+ I = np.argsort(X, axis=0, kind='mergesort')
35
+
36
+ # sort the objective space values for the whole matrix
37
+ _X = X[I, np.arange(M)]
38
+
39
+ # calculate the distance from each point to the last and next
40
+ dist = np.vstack([_X, np.full(M, np.inf)]) - np.vstack([np.full(M, -np.inf), _X])
41
+
42
+ # prepare the distance to last and next vectors
43
+ dist_to_last, dist_to_next = dist, np.copy(dist)
44
+ dist_to_last, dist_to_next = dist_to_last[:-1], dist_to_next[1:]
45
+
46
+ # if we divide by zero because all values in one columns are equal replace by none
47
+ dist_to_last[np.isnan(dist_to_last)] = 0.0
48
+ dist_to_next[np.isnan(dist_to_next)] = 0.0
49
+
50
+ # sum up the distance to next and last and norm by objectives - also reorder from sorted list
51
+ J = np.argsort(I, axis=0)
52
+ _d = np.sum(dist_to_last[J, np.arange(M)] + dist_to_next[J, np.arange(M)], axis=1)
53
+ d[H] = _d
54
+ d[extremes] = np.inf
55
+
56
+ n_removed = 0
57
+
58
+ # While n_remove not achieved
59
+ while n_removed < (n_remove - 1):
60
+
61
+ # Obtain element to drop
62
+ _d = d[H]
63
+ _k = np.argmin(_d)
64
+ k = H[_k]
65
+
66
+ H = H[H != k]
67
+
68
+ # Update index
69
+ n_removed = n_removed + 1
70
+
71
+ I = np.argsort(X[H].copy(), axis=0, kind='mergesort')
72
+
73
+ # sort the objective space values for the whole matrix
74
+ _X = X[H].copy()[I, np.arange(M)]
75
+
76
+ # calculate the distance from each point to the last and next
77
+ dist = np.vstack([_X, np.full(M, np.inf)]) - np.vstack([np.full(M, -np.inf), _X])
78
+
79
+ # prepare the distance to last and next vectors
80
+ dist_to_last, dist_to_next = dist, np.copy(dist)
81
+ dist_to_last, dist_to_next = dist_to_last[:-1], dist_to_next[1:]
82
+
83
+ # if we divide by zero because all values in one columns are equal replace by none
84
+ dist_to_last[np.isnan(dist_to_last)] = 0.0
85
+ dist_to_next[np.isnan(dist_to_next)] = 0.0
86
+
87
+ # sum up the distance to next and last and norm by objectives - also reorder from sorted list
88
+ J = np.argsort(I, axis=0)
89
+ _d = np.sum(dist_to_last[J, np.arange(M)] + dist_to_next[J, np.arange(M)], axis=1)
90
+ d[H] = _d
91
+ d[extremes] = np.inf
92
+
93
+ return d