pymoo 0.6.1.5.dev0__cp313-cp313-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.

Potentially problematic release.


This version of pymoo might be problematic. Click here for more details.

Files changed (328) 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 +109 -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 +89 -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/ctaea.py +298 -0
  14. pymoo/algorithms/moo/dnsga2.py +76 -0
  15. pymoo/algorithms/moo/kgb.py +446 -0
  16. pymoo/algorithms/moo/moead.py +183 -0
  17. pymoo/algorithms/moo/nsga2.py +113 -0
  18. pymoo/algorithms/moo/nsga3.py +358 -0
  19. pymoo/algorithms/moo/pinsga2.py +370 -0
  20. pymoo/algorithms/moo/rnsga2.py +188 -0
  21. pymoo/algorithms/moo/rnsga3.py +246 -0
  22. pymoo/algorithms/moo/rvea.py +214 -0
  23. pymoo/algorithms/moo/sms.py +195 -0
  24. pymoo/algorithms/moo/spea2.py +190 -0
  25. pymoo/algorithms/moo/unsga3.py +47 -0
  26. pymoo/algorithms/soo/__init__.py +0 -0
  27. pymoo/algorithms/soo/convex/__init__.py +0 -0
  28. pymoo/algorithms/soo/nonconvex/__init__.py +0 -0
  29. pymoo/algorithms/soo/nonconvex/brkga.py +161 -0
  30. pymoo/algorithms/soo/nonconvex/cmaes.py +554 -0
  31. pymoo/algorithms/soo/nonconvex/de.py +279 -0
  32. pymoo/algorithms/soo/nonconvex/direct.py +149 -0
  33. pymoo/algorithms/soo/nonconvex/es.py +203 -0
  34. pymoo/algorithms/soo/nonconvex/g3pcx.py +94 -0
  35. pymoo/algorithms/soo/nonconvex/ga.py +93 -0
  36. pymoo/algorithms/soo/nonconvex/ga_niching.py +223 -0
  37. pymoo/algorithms/soo/nonconvex/isres.py +74 -0
  38. pymoo/algorithms/soo/nonconvex/nelder.py +251 -0
  39. pymoo/algorithms/soo/nonconvex/optuna.py +80 -0
  40. pymoo/algorithms/soo/nonconvex/pattern.py +183 -0
  41. pymoo/algorithms/soo/nonconvex/pso.py +399 -0
  42. pymoo/algorithms/soo/nonconvex/pso_ep.py +297 -0
  43. pymoo/algorithms/soo/nonconvex/random_search.py +25 -0
  44. pymoo/algorithms/soo/nonconvex/sres.py +56 -0
  45. pymoo/algorithms/soo/univariate/__init__.py +0 -0
  46. pymoo/algorithms/soo/univariate/backtracking.py +59 -0
  47. pymoo/algorithms/soo/univariate/exp.py +46 -0
  48. pymoo/algorithms/soo/univariate/golden.py +65 -0
  49. pymoo/algorithms/soo/univariate/quadr_interp.py +81 -0
  50. pymoo/algorithms/soo/univariate/wolfe.py +163 -0
  51. pymoo/config.py +33 -0
  52. pymoo/constraints/__init__.py +3 -0
  53. pymoo/constraints/adaptive.py +62 -0
  54. pymoo/constraints/as_obj.py +56 -0
  55. pymoo/constraints/as_penalty.py +41 -0
  56. pymoo/constraints/eps.py +26 -0
  57. pymoo/constraints/from_bounds.py +36 -0
  58. pymoo/core/__init__.py +0 -0
  59. pymoo/core/algorithm.py +394 -0
  60. pymoo/core/callback.py +38 -0
  61. pymoo/core/crossover.py +77 -0
  62. pymoo/core/decision_making.py +102 -0
  63. pymoo/core/decomposition.py +76 -0
  64. pymoo/core/duplicate.py +163 -0
  65. pymoo/core/evaluator.py +116 -0
  66. pymoo/core/indicator.py +34 -0
  67. pymoo/core/individual.py +784 -0
  68. pymoo/core/infill.py +64 -0
  69. pymoo/core/initialization.py +42 -0
  70. pymoo/core/mating.py +39 -0
  71. pymoo/core/meta.py +21 -0
  72. pymoo/core/mixed.py +165 -0
  73. pymoo/core/mutation.py +44 -0
  74. pymoo/core/operator.py +40 -0
  75. pymoo/core/parameters.py +134 -0
  76. pymoo/core/plot.py +210 -0
  77. pymoo/core/population.py +180 -0
  78. pymoo/core/problem.py +460 -0
  79. pymoo/core/recorder.py +99 -0
  80. pymoo/core/repair.py +23 -0
  81. pymoo/core/replacement.py +96 -0
  82. pymoo/core/result.py +52 -0
  83. pymoo/core/sampling.py +43 -0
  84. pymoo/core/selection.py +61 -0
  85. pymoo/core/solution.py +10 -0
  86. pymoo/core/survival.py +103 -0
  87. pymoo/core/termination.py +70 -0
  88. pymoo/core/variable.py +399 -0
  89. pymoo/cython/__init__.py +0 -0
  90. pymoo/cython/calc_perpendicular_distance.cpython-313-darwin.so +0 -0
  91. pymoo/cython/calc_perpendicular_distance.pyx +67 -0
  92. pymoo/cython/decomposition.cpython-313-darwin.so +0 -0
  93. pymoo/cython/decomposition.pyx +165 -0
  94. pymoo/cython/hv.cpython-313-darwin.so +0 -0
  95. pymoo/cython/hv.pyx +18 -0
  96. pymoo/cython/info.cpython-313-darwin.so +0 -0
  97. pymoo/cython/info.pyx +5 -0
  98. pymoo/cython/mnn.cpython-313-darwin.so +0 -0
  99. pymoo/cython/mnn.pyx +273 -0
  100. pymoo/cython/non_dominated_sorting.cpython-313-darwin.so +0 -0
  101. pymoo/cython/non_dominated_sorting.pyx +645 -0
  102. pymoo/cython/pruning_cd.cpython-313-darwin.so +0 -0
  103. pymoo/cython/pruning_cd.pyx +197 -0
  104. pymoo/cython/stochastic_ranking.cpython-313-darwin.so +0 -0
  105. pymoo/cython/stochastic_ranking.pyx +49 -0
  106. pymoo/cython/utils.pxd +129 -0
  107. pymoo/cython/vendor/__init__.py +0 -0
  108. pymoo/cython/vendor/hypervolume.cpp +1621 -0
  109. pymoo/cython/vendor/hypervolume.h +63 -0
  110. pymoo/decomposition/__init__.py +0 -0
  111. pymoo/decomposition/aasf.py +24 -0
  112. pymoo/decomposition/asf.py +10 -0
  113. pymoo/decomposition/pbi.py +13 -0
  114. pymoo/decomposition/perp_dist.py +13 -0
  115. pymoo/decomposition/tchebicheff.py +11 -0
  116. pymoo/decomposition/util.py +13 -0
  117. pymoo/decomposition/weighted_sum.py +8 -0
  118. pymoo/docs.py +187 -0
  119. pymoo/experimental/__init__.py +0 -0
  120. pymoo/experimental/algorithms/__init__.py +0 -0
  121. pymoo/experimental/algorithms/gde3.py +57 -0
  122. pymoo/gradient/__init__.py +21 -0
  123. pymoo/gradient/automatic.py +57 -0
  124. pymoo/gradient/grad_autograd.py +105 -0
  125. pymoo/gradient/grad_complex.py +35 -0
  126. pymoo/gradient/grad_jax.py +51 -0
  127. pymoo/gradient/toolbox/__init__.py +6 -0
  128. pymoo/indicators/__init__.py +0 -0
  129. pymoo/indicators/distance_indicator.py +55 -0
  130. pymoo/indicators/gd.py +7 -0
  131. pymoo/indicators/gd_plus.py +7 -0
  132. pymoo/indicators/hv/__init__.py +63 -0
  133. pymoo/indicators/hv/exact.py +71 -0
  134. pymoo/indicators/hv/exact_2d.py +102 -0
  135. pymoo/indicators/hv/monte_carlo.py +74 -0
  136. pymoo/indicators/igd.py +7 -0
  137. pymoo/indicators/igd_plus.py +7 -0
  138. pymoo/indicators/kktpm.py +151 -0
  139. pymoo/indicators/migd.py +55 -0
  140. pymoo/indicators/rmetric.py +203 -0
  141. pymoo/indicators/spacing.py +52 -0
  142. pymoo/mcdm/__init__.py +0 -0
  143. pymoo/mcdm/compromise_programming.py +19 -0
  144. pymoo/mcdm/high_tradeoff.py +40 -0
  145. pymoo/mcdm/pseudo_weights.py +32 -0
  146. pymoo/operators/__init__.py +0 -0
  147. pymoo/operators/control.py +187 -0
  148. pymoo/operators/crossover/__init__.py +0 -0
  149. pymoo/operators/crossover/binx.py +45 -0
  150. pymoo/operators/crossover/dex.py +122 -0
  151. pymoo/operators/crossover/erx.py +162 -0
  152. pymoo/operators/crossover/expx.py +51 -0
  153. pymoo/operators/crossover/hux.py +37 -0
  154. pymoo/operators/crossover/nox.py +13 -0
  155. pymoo/operators/crossover/ox.py +84 -0
  156. pymoo/operators/crossover/pcx.py +82 -0
  157. pymoo/operators/crossover/pntx.py +49 -0
  158. pymoo/operators/crossover/sbx.py +125 -0
  159. pymoo/operators/crossover/spx.py +5 -0
  160. pymoo/operators/crossover/ux.py +20 -0
  161. pymoo/operators/mutation/__init__.py +0 -0
  162. pymoo/operators/mutation/bitflip.py +17 -0
  163. pymoo/operators/mutation/gauss.py +58 -0
  164. pymoo/operators/mutation/inversion.py +42 -0
  165. pymoo/operators/mutation/nom.py +7 -0
  166. pymoo/operators/mutation/pm.py +94 -0
  167. pymoo/operators/mutation/rm.py +23 -0
  168. pymoo/operators/repair/__init__.py +0 -0
  169. pymoo/operators/repair/bounce_back.py +32 -0
  170. pymoo/operators/repair/bounds_repair.py +95 -0
  171. pymoo/operators/repair/inverse_penalty.py +89 -0
  172. pymoo/operators/repair/rounding.py +18 -0
  173. pymoo/operators/repair/to_bound.py +31 -0
  174. pymoo/operators/repair/vtype.py +11 -0
  175. pymoo/operators/sampling/__init__.py +0 -0
  176. pymoo/operators/sampling/lhs.py +73 -0
  177. pymoo/operators/sampling/rnd.py +50 -0
  178. pymoo/operators/selection/__init__.py +0 -0
  179. pymoo/operators/selection/rnd.py +72 -0
  180. pymoo/operators/selection/tournament.py +76 -0
  181. pymoo/operators/survival/__init__.py +0 -0
  182. pymoo/operators/survival/rank_and_crowding/__init__.py +1 -0
  183. pymoo/operators/survival/rank_and_crowding/classes.py +209 -0
  184. pymoo/operators/survival/rank_and_crowding/metrics.py +208 -0
  185. pymoo/optimize.py +72 -0
  186. pymoo/problems/__init__.py +157 -0
  187. pymoo/problems/dyn.py +47 -0
  188. pymoo/problems/dynamic/__init__.py +0 -0
  189. pymoo/problems/dynamic/cec2015.py +108 -0
  190. pymoo/problems/dynamic/df.py +452 -0
  191. pymoo/problems/dynamic/misc.py +167 -0
  192. pymoo/problems/functional.py +48 -0
  193. pymoo/problems/many/__init__.py +5 -0
  194. pymoo/problems/many/cdtlz.py +159 -0
  195. pymoo/problems/many/dcdtlz.py +88 -0
  196. pymoo/problems/many/dtlz.py +264 -0
  197. pymoo/problems/many/wfg.py +550 -0
  198. pymoo/problems/multi/__init__.py +14 -0
  199. pymoo/problems/multi/bnh.py +34 -0
  200. pymoo/problems/multi/carside.py +48 -0
  201. pymoo/problems/multi/clutch.py +104 -0
  202. pymoo/problems/multi/csi.py +55 -0
  203. pymoo/problems/multi/ctp.py +198 -0
  204. pymoo/problems/multi/dascmop.py +213 -0
  205. pymoo/problems/multi/kursawe.py +25 -0
  206. pymoo/problems/multi/modact.py +68 -0
  207. pymoo/problems/multi/mw.py +400 -0
  208. pymoo/problems/multi/omnitest.py +48 -0
  209. pymoo/problems/multi/osy.py +32 -0
  210. pymoo/problems/multi/srn.py +28 -0
  211. pymoo/problems/multi/sympart.py +94 -0
  212. pymoo/problems/multi/tnk.py +24 -0
  213. pymoo/problems/multi/truss2d.py +83 -0
  214. pymoo/problems/multi/welded_beam.py +41 -0
  215. pymoo/problems/multi/wrm.py +36 -0
  216. pymoo/problems/multi/zdt.py +151 -0
  217. pymoo/problems/multi_to_single.py +22 -0
  218. pymoo/problems/single/__init__.py +12 -0
  219. pymoo/problems/single/ackley.py +24 -0
  220. pymoo/problems/single/cantilevered_beam.py +34 -0
  221. pymoo/problems/single/flowshop_scheduling.py +112 -0
  222. pymoo/problems/single/g.py +874 -0
  223. pymoo/problems/single/griewank.py +18 -0
  224. pymoo/problems/single/himmelblau.py +15 -0
  225. pymoo/problems/single/knapsack.py +48 -0
  226. pymoo/problems/single/mopta08.py +26 -0
  227. pymoo/problems/single/multimodal.py +20 -0
  228. pymoo/problems/single/pressure_vessel.py +30 -0
  229. pymoo/problems/single/rastrigin.py +20 -0
  230. pymoo/problems/single/rosenbrock.py +22 -0
  231. pymoo/problems/single/schwefel.py +18 -0
  232. pymoo/problems/single/simple.py +13 -0
  233. pymoo/problems/single/sphere.py +19 -0
  234. pymoo/problems/single/traveling_salesman.py +79 -0
  235. pymoo/problems/single/zakharov.py +19 -0
  236. pymoo/problems/static.py +14 -0
  237. pymoo/problems/util.py +42 -0
  238. pymoo/problems/zero_to_one.py +27 -0
  239. pymoo/termination/__init__.py +23 -0
  240. pymoo/termination/collection.py +12 -0
  241. pymoo/termination/cv.py +48 -0
  242. pymoo/termination/default.py +45 -0
  243. pymoo/termination/delta.py +64 -0
  244. pymoo/termination/fmin.py +16 -0
  245. pymoo/termination/ftol.py +144 -0
  246. pymoo/termination/indicator.py +49 -0
  247. pymoo/termination/max_eval.py +14 -0
  248. pymoo/termination/max_gen.py +15 -0
  249. pymoo/termination/max_time.py +20 -0
  250. pymoo/termination/robust.py +34 -0
  251. pymoo/termination/xtol.py +33 -0
  252. pymoo/util/__init__.py +0 -0
  253. pymoo/util/archive.py +150 -0
  254. pymoo/util/cache.py +29 -0
  255. pymoo/util/clearing.py +82 -0
  256. pymoo/util/display/__init__.py +0 -0
  257. pymoo/util/display/column.py +52 -0
  258. pymoo/util/display/display.py +34 -0
  259. pymoo/util/display/multi.py +96 -0
  260. pymoo/util/display/output.py +53 -0
  261. pymoo/util/display/progress.py +54 -0
  262. pymoo/util/display/single.py +67 -0
  263. pymoo/util/dominator.py +67 -0
  264. pymoo/util/function_loader.py +129 -0
  265. pymoo/util/hv.py +23 -0
  266. pymoo/util/matlab_engine.py +39 -0
  267. pymoo/util/misc.py +460 -0
  268. pymoo/util/mnn.py +70 -0
  269. pymoo/util/nds/__init__.py +0 -0
  270. pymoo/util/nds/dominance_degree_non_dominated_sort.py +159 -0
  271. pymoo/util/nds/efficient_non_dominated_sort.py +152 -0
  272. pymoo/util/nds/fast_non_dominated_sort.py +70 -0
  273. pymoo/util/nds/naive_non_dominated_sort.py +36 -0
  274. pymoo/util/nds/non_dominated_sorting.py +67 -0
  275. pymoo/util/nds/tree_based_non_dominated_sort.py +133 -0
  276. pymoo/util/normalization.py +312 -0
  277. pymoo/util/optimum.py +42 -0
  278. pymoo/util/plotting.py +177 -0
  279. pymoo/util/pruning_cd.py +89 -0
  280. pymoo/util/randomized_argsort.py +60 -0
  281. pymoo/util/ref_dirs/__init__.py +24 -0
  282. pymoo/util/ref_dirs/construction.py +88 -0
  283. pymoo/util/ref_dirs/das_dennis.py +52 -0
  284. pymoo/util/ref_dirs/energy.py +319 -0
  285. pymoo/util/ref_dirs/energy_layer.py +119 -0
  286. pymoo/util/ref_dirs/genetic_algorithm.py +63 -0
  287. pymoo/util/ref_dirs/incremental.py +68 -0
  288. pymoo/util/ref_dirs/misc.py +128 -0
  289. pymoo/util/ref_dirs/optimizer.py +59 -0
  290. pymoo/util/ref_dirs/performance.py +162 -0
  291. pymoo/util/ref_dirs/reduction.py +85 -0
  292. pymoo/util/ref_dirs/sample_and_map.py +24 -0
  293. pymoo/util/reference_direction.py +260 -0
  294. pymoo/util/remote.py +55 -0
  295. pymoo/util/roulette.py +27 -0
  296. pymoo/util/running_metric.py +128 -0
  297. pymoo/util/sliding_window.py +25 -0
  298. pymoo/util/stochastic_ranking.py +32 -0
  299. pymoo/util/value_functions.py +719 -0
  300. pymoo/util/vectors.py +40 -0
  301. pymoo/util/vf_dominator.py +99 -0
  302. pymoo/vendor/__init__.py +0 -0
  303. pymoo/vendor/cec2018.py +398 -0
  304. pymoo/vendor/gta.py +617 -0
  305. pymoo/vendor/hv.py +267 -0
  306. pymoo/vendor/vendor_cmaes.py +412 -0
  307. pymoo/vendor/vendor_coco.py +81 -0
  308. pymoo/vendor/vendor_scipy.py +232 -0
  309. pymoo/version.py +1 -0
  310. pymoo/visualization/__init__.py +8 -0
  311. pymoo/visualization/fitness_landscape.py +127 -0
  312. pymoo/visualization/heatmap.py +123 -0
  313. pymoo/visualization/pcp.py +120 -0
  314. pymoo/visualization/petal.py +91 -0
  315. pymoo/visualization/radar.py +108 -0
  316. pymoo/visualization/radviz.py +68 -0
  317. pymoo/visualization/scatter.py +150 -0
  318. pymoo/visualization/star_coordinate.py +75 -0
  319. pymoo/visualization/util.py +123 -0
  320. pymoo/visualization/video/__init__.py +0 -0
  321. pymoo/visualization/video/callback_video.py +82 -0
  322. pymoo/visualization/video/one_var_one_obj.py +57 -0
  323. pymoo/visualization/video/two_var_one_obj.py +62 -0
  324. pymoo-0.6.1.5.dev0.dist-info/METADATA +187 -0
  325. pymoo-0.6.1.5.dev0.dist-info/RECORD +328 -0
  326. pymoo-0.6.1.5.dev0.dist-info/WHEEL +6 -0
  327. pymoo-0.6.1.5.dev0.dist-info/licenses/LICENSE +191 -0
  328. pymoo-0.6.1.5.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,246 @@
1
+ import numpy as np
2
+
3
+ from pymoo.algorithms.moo.nsga3 import calc_niche_count, niching, comp_by_cv_then_random, associate_to_niches, NSGA3
4
+ from pymoo.core.survival import Survival
5
+ from pymoo.docs import parse_doc_string
6
+ from pymoo.operators.sampling.rnd import FloatRandomSampling
7
+ from pymoo.operators.selection.tournament import TournamentSelection
8
+ from pymoo.util.misc import intersect
9
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
10
+ from pymoo.util.normalization import denormalize, get_extreme_points_c, get_nadir_point
11
+ from pymoo.util.reference_direction import UniformReferenceDirectionFactory
12
+
13
+
14
+ # =========================================================================================================
15
+ # Implementation
16
+ # =========================================================================================================
17
+
18
+
19
+ class RNSGA3(NSGA3):
20
+
21
+ def __init__(self,
22
+ ref_points,
23
+ pop_per_ref_point,
24
+ mu=0.05,
25
+ sampling=FloatRandomSampling(),
26
+ selection=TournamentSelection(func_comp=comp_by_cv_then_random),
27
+ eliminate_duplicates=True,
28
+ n_offsprings=None,
29
+ **kwargs):
30
+ """
31
+
32
+ Parameters
33
+ ----------
34
+
35
+ ref_points : {ref_points}
36
+
37
+ pop_per_ref_point : int
38
+ Size of the population used for each reference point.
39
+
40
+ mu : float
41
+ Defines the init_simplex_scale of the reference lines used during survival selection. Increasing mu will result
42
+ having solutions with a larger spread.
43
+
44
+ n_offsprings : {n_offsprings}
45
+ sampling : {sampling}
46
+ selection : {selection}
47
+ crossover : {crossover}
48
+ mutation : {mutation}
49
+ eliminate_duplicates : {eliminate_duplicates}
50
+
51
+ """
52
+
53
+ # number of objectives the reference lines have
54
+ n_obj = ref_points.shape[1]
55
+
56
+ # add the aspiration point lines
57
+ aspiration_ref_dirs = UniformReferenceDirectionFactory(n_dim=n_obj, n_points=pop_per_ref_point).do()
58
+
59
+ survival = AspirationPointSurvival(ref_points, aspiration_ref_dirs, mu=mu)
60
+ pop_size = ref_points.shape[0] * aspiration_ref_dirs.shape[0] + aspiration_ref_dirs.shape[1]
61
+ ref_dirs = None
62
+
63
+ super().__init__(ref_dirs,
64
+ pop_size=pop_size,
65
+ sampling=sampling,
66
+ selection=selection,
67
+ survival=survival,
68
+ eliminate_duplicates=eliminate_duplicates,
69
+ n_offsprings=n_offsprings,
70
+ **kwargs)
71
+
72
+ def _setup(self, problem, **kwargs):
73
+ if self.survival.ref_points.shape[1] != problem.n_obj:
74
+ raise Exception("Dimensionality of reference points must be equal to the number of objectives: %s != %s" %
75
+ (self.survival.ref_points.shape[1], problem.n_obj))
76
+
77
+ def _finalize(self):
78
+ pass
79
+
80
+
81
+ class AspirationPointSurvival(Survival):
82
+
83
+ def __init__(self, ref_points, aspiration_ref_dirs, mu=0.1):
84
+ super().__init__()
85
+
86
+ self.ref_points = ref_points
87
+ self.aspiration_ref_dirs = aspiration_ref_dirs
88
+ self.mu = mu
89
+
90
+ self.ref_dirs = aspiration_ref_dirs
91
+ self.extreme_points = None
92
+ self.intercepts = None
93
+ self.nadir_point = None
94
+ self.opt = None
95
+ self.ideal_point = np.full(ref_points.shape[1], np.inf)
96
+ self.worst_point = np.full(ref_points.shape[1], -np.inf)
97
+
98
+ def _do(self, problem, pop, n_survive, D=None, **kwargs):
99
+
100
+ # attributes to be set after the survival
101
+ F = pop.get("F")
102
+
103
+ # find or usually update the new ideal point - from feasible solutions
104
+ self.ideal_point = np.min(np.vstack((self.ideal_point, F, self.ref_points)), axis=0)
105
+ self.worst_point = np.max(np.vstack((self.worst_point, F, self.ref_points)), axis=0)
106
+
107
+ # calculate the fronts of the population
108
+ fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive)
109
+ non_dominated, last_front = fronts[0], fronts[-1]
110
+
111
+ # find the extreme points for normalization
112
+ self.extreme_points = get_extreme_points_c(
113
+ np.vstack([F[non_dominated], self.ref_points])
114
+ , self.ideal_point,
115
+ extreme_points=self.extreme_points)
116
+
117
+ # find the intercepts for normalization and do backup if gaussian elimination fails
118
+ worst_of_population = np.max(F, axis=0)
119
+ worst_of_front = np.max(F[non_dominated, :], axis=0)
120
+
121
+ self.nadir_point = get_nadir_point(self.extreme_points, self.ideal_point, self.worst_point,
122
+ worst_of_population, worst_of_front)
123
+
124
+ # consider only the population until we come to the splitting front
125
+ I = np.concatenate(fronts)
126
+ pop, rank, F = pop[I], rank[I], F[I]
127
+
128
+ # update the front indices for the current population
129
+ counter = 0
130
+ for i in range(len(fronts)):
131
+ for j in range(len(fronts[i])):
132
+ fronts[i][j] = counter
133
+ counter += 1
134
+ last_front = fronts[-1]
135
+
136
+ unit_ref_points = (self.ref_points - self.ideal_point) / (self.nadir_point - self.ideal_point)
137
+ ref_dirs = get_ref_dirs_from_points(unit_ref_points, self.aspiration_ref_dirs, mu=self.mu)
138
+ self.ref_dirs = denormalize(ref_dirs, self.ideal_point, self.nadir_point)
139
+
140
+ # associate individuals to niches
141
+ niche_of_individuals, dist_to_niche, dist_matrix = associate_to_niches(F, ref_dirs, self.ideal_point,
142
+ self.nadir_point)
143
+ pop.set('rank', rank, 'niche', niche_of_individuals, 'dist_to_niche', dist_to_niche)
144
+
145
+ # set the optimum, first front and closest to all reference directions
146
+ closest = np.unique(dist_matrix[:, np.unique(niche_of_individuals)].argmin(axis=0))
147
+ self.opt = pop[intersect(fronts[0], closest)]
148
+
149
+ # if we need to select individuals to survive
150
+ if len(pop) > n_survive:
151
+
152
+ # if there is only one front
153
+ if len(fronts) == 1:
154
+ n_remaining = n_survive
155
+ until_last_front = np.array([], dtype=int)
156
+ niche_count = np.zeros(len(ref_dirs), dtype=int)
157
+
158
+ # if some individuals already survived
159
+ else:
160
+ until_last_front = np.concatenate(fronts[:-1])
161
+ niche_count = calc_niche_count(len(ref_dirs), niche_of_individuals[until_last_front])
162
+ n_remaining = n_survive - len(until_last_front)
163
+
164
+ S = niching(pop[last_front], n_remaining, niche_count, niche_of_individuals[last_front],
165
+ dist_to_niche[last_front])
166
+
167
+ survivors = np.concatenate((until_last_front, last_front[S].tolist()))
168
+ pop = pop[survivors]
169
+
170
+ return pop
171
+
172
+
173
+ def get_ref_dirs_from_points(ref_point, ref_dirs, mu=0.1):
174
+ """
175
+ This function takes user specified reference points, and creates smaller sets of equidistant
176
+ Das-Dennis points around the projection of user points on the Das-Dennis hyperplane
177
+ :param ref_point: List of user specified reference points
178
+ :param n_obj: Number of objectives to consider
179
+ :param mu: Shrinkage factor (0-1), Smaller = tighter convergence, Larger= larger convergence
180
+ :return: Set of reference points
181
+ """
182
+
183
+ n_obj = ref_point.shape[1]
184
+
185
+ val = []
186
+ n_vector = np.ones(n_obj) / np.sqrt(n_obj) # Normal vector of Das Dennis plane
187
+ point_on_plane = np.eye(n_obj)[0] # Point on Das-Dennis
188
+
189
+ for point in ref_point:
190
+
191
+ ref_dir_for_aspiration_point = np.copy(ref_dirs) # Copy of computed reference directions
192
+ ref_dir_for_aspiration_point = mu * ref_dir_for_aspiration_point
193
+
194
+ cent = np.mean(ref_dir_for_aspiration_point, axis=0) # Find centroid of shrunken reference points
195
+
196
+ # Project shrunken Das-Dennis points back onto original Das-Dennis hyperplane
197
+ intercept = line_plane_intersection(np.zeros(n_obj), point, point_on_plane, n_vector)
198
+ shift = intercept - cent # shift vector
199
+
200
+ ref_dir_for_aspiration_point += shift
201
+
202
+ # If reference directions are located outside of first octant, redefine points onto the border
203
+ if not (ref_dir_for_aspiration_point > 0).min():
204
+ ref_dir_for_aspiration_point[ref_dir_for_aspiration_point < 0] = 0
205
+ ref_dir_for_aspiration_point = ref_dir_for_aspiration_point / np.sum(ref_dir_for_aspiration_point, axis=1)[
206
+ :, None]
207
+ val.extend(ref_dir_for_aspiration_point)
208
+
209
+ val.extend(np.eye(n_obj)) # Add extreme points
210
+ return np.array(val)
211
+
212
+
213
+ # intersection function
214
+
215
+ def line_plane_intersection(l0, l1, p0, p_no, epsilon=1e-6):
216
+ """
217
+ l0, l1: define the line
218
+ p0, p_no: define the plane:
219
+ p0 is a point on the plane (plane coordinate).
220
+ p_no is a normal vector defining the plane direction;
221
+ (does not need to be normalized).
222
+
223
+ reference: https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection
224
+ return a Vector or None (when the intersection can't be found).
225
+ """
226
+
227
+ l = l1 - l0
228
+ dot = np.dot(l, p_no)
229
+
230
+ if abs(dot) > epsilon:
231
+ # the factor of the point between p0 -> p1 (0 - 1)
232
+ # if 'fac' is between (0 - 1) the point intersects with the segment.
233
+ # otherwise:
234
+ # < 0.0: behind p0.
235
+ # > 1.0: in front of p1.
236
+ w = p0 - l0
237
+ d = np.dot(w, p_no) / dot
238
+ l = l * d
239
+ return l0 + l
240
+ else:
241
+ # The segment is parallel to plane then return the perpendicular projection
242
+ ref_proj = l1 - (np.dot(l1 - p0, p_no) * p_no)
243
+ return ref_proj
244
+
245
+
246
+ parse_doc_string(RNSGA3.__init__)
@@ -0,0 +1,214 @@
1
+ import numpy as np
2
+
3
+ from pymoo.algorithms.base.genetic import GeneticAlgorithm
4
+ from pymoo.core.survival import Survival
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.sampling.rnd import FloatRandomSampling
9
+ from pymoo.operators.selection.rnd import RandomSelection
10
+ from pymoo.termination.max_eval import MaximumFunctionCallTermination
11
+ from pymoo.termination.max_gen import MaximumGenerationTermination
12
+ from pymoo.util.display.multi import MultiObjectiveOutput
13
+ from pymoo.util.misc import has_feasible, vectorized_cdist
14
+
15
+
16
+ class RVEA(GeneticAlgorithm):
17
+
18
+ def __init__(self,
19
+ ref_dirs,
20
+ alpha=2.0,
21
+ adapt_freq=0.1,
22
+ pop_size=None,
23
+ sampling=FloatRandomSampling(),
24
+ selection=RandomSelection(),
25
+ crossover=SBX(eta=30, prob=1.0),
26
+ mutation=PM(eta=20),
27
+ eliminate_duplicates=True,
28
+ n_offsprings=None,
29
+ output=MultiObjectiveOutput(),
30
+ **kwargs):
31
+ """
32
+
33
+ Parameters
34
+ ----------
35
+
36
+ ref_dirs : {ref_dirs}
37
+ adapt_freq : float
38
+ Defines the ratio of generation when the reference directions are updated.
39
+ pop_size : int (default = None)
40
+ By default the population size is set to None which means that it will be equal to the number of reference
41
+ line. However, if desired this can be overwritten by providing a positive number.
42
+ sampling : {sampling}
43
+ selection : {selection}
44
+ crossover : {crossover}
45
+ mutation : {mutation}
46
+ eliminate_duplicates : {eliminate_duplicates}
47
+ n_offsprings : {n_offsprings}
48
+
49
+ """
50
+
51
+ # set reference directions and pop_size
52
+ self.ref_dirs = ref_dirs
53
+ if self.ref_dirs is not None:
54
+ if pop_size is None:
55
+ pop_size = len(self.ref_dirs)
56
+
57
+ # the fraction of n_max_gen when the the reference directions are adapted
58
+ self.adapt_freq = adapt_freq
59
+
60
+ # you can override the survival if necessary
61
+ survival = kwargs.pop("survival", None)
62
+ if survival is None:
63
+ survival = APDSurvival(ref_dirs, alpha=alpha)
64
+
65
+ super().__init__(pop_size=pop_size,
66
+ sampling=sampling,
67
+ selection=selection,
68
+ crossover=crossover,
69
+ mutation=mutation,
70
+ survival=survival,
71
+ eliminate_duplicates=eliminate_duplicates,
72
+ n_offsprings=n_offsprings,
73
+ output=output,
74
+ **kwargs)
75
+
76
+ def _setup(self, problem, **kwargs):
77
+
78
+ # if maximum functions termination convert it to generations
79
+ if isinstance(self.termination, MaximumFunctionCallTermination):
80
+ n_gen = np.ceil((self.termination.n_max_evals - self.pop_size) / self.n_offsprings)
81
+ self.termination = MaximumGenerationTermination(n_gen)
82
+
83
+ # check whether the n_gen termination is used - otherwise this algorithm can be not run
84
+ if not isinstance(self.termination, MaximumGenerationTermination):
85
+ raise Exception("Please use the n_gen or n_eval as a termination criterion to run RVEA!")
86
+
87
+ def _advance(self, **kwargs):
88
+ super()._advance(**kwargs)
89
+
90
+ # get the current generation and maximum of generations
91
+ n_gen, n_max_gen = self.n_gen, self.termination.n_max_gen
92
+
93
+ # each i-th generation (define by fr and n_max_gen) the reference directions are updated
94
+ if self.adapt_freq is not None and n_gen % np.ceil(n_max_gen * self.adapt_freq) == 0:
95
+ self.survival.adapt()
96
+
97
+ def _set_optimum(self, **kwargs):
98
+ if not has_feasible(self.pop):
99
+ self.opt = self.pop[[np.argmin(self.pop.get("CV"))]]
100
+ else:
101
+ self.opt = self.pop
102
+
103
+
104
+ # ---------------------------------------------------------------------------------------------------------
105
+ # Survival Selection
106
+ # ---------------------------------------------------------------------------------------------------------
107
+
108
+ def calc_gamma(V):
109
+ gamma = np.arccos((- np.sort(-1 * V @ V.T))[:, 1])
110
+ gamma = np.maximum(gamma, 1e-64)
111
+ return gamma
112
+
113
+
114
+ def calc_V(ref_dirs):
115
+ return ref_dirs / np.linalg.norm(ref_dirs, axis=1)[:, None]
116
+
117
+
118
+ class APDSurvival(Survival):
119
+
120
+ def __init__(self, ref_dirs, alpha=2.0) -> None:
121
+ super().__init__(filter_infeasible=True)
122
+ n_dim = ref_dirs.shape[1]
123
+
124
+ self.alpha = alpha
125
+ self.niches = None
126
+ self.V, self.gamma = None, None
127
+ self.ideal, self.nadir = np.full(n_dim, np.inf), None
128
+
129
+ self.ref_dirs = ref_dirs
130
+ self.extreme_ref_dirs = np.where(np.any(vectorized_cdist(self.ref_dirs, np.eye(n_dim)) == 0, axis=1))[0]
131
+
132
+ self.V = calc_V(self.ref_dirs)
133
+ self.gamma = calc_gamma(self.V)
134
+
135
+ def adapt(self):
136
+ if self.nadir is not None:
137
+ self.V = calc_V(calc_V(self.ref_dirs) * (self.nadir - self.ideal))
138
+ self.gamma = calc_gamma(self.V)
139
+
140
+ def _do(self, problem, pop, n_survive, algorithm=None, n_gen=None, n_max_gen=None, **kwargs):
141
+
142
+ if n_gen is None:
143
+ n_gen = algorithm.n_gen - 1
144
+ if n_max_gen is None:
145
+ n_max_gen = algorithm.termination.n_max_gen
146
+
147
+ # get the objective space values
148
+ F = pop.get("F")
149
+
150
+ # store the ideal and nadir point estimation for adapt - (and ideal for transformation)
151
+ self.ideal = np.minimum(F.min(axis=0), self.ideal)
152
+
153
+ # translate the population to make the ideal point the origin
154
+ F = F - self.ideal
155
+
156
+ # the distance to the ideal point
157
+ dist_to_ideal = np.linalg.norm(F, axis=1)
158
+ dist_to_ideal[dist_to_ideal < 1e-64] = 1e-64
159
+
160
+ # normalize by distance to ideal
161
+ F_prime = F / dist_to_ideal[:, None]
162
+
163
+ # calculate for each solution the acute angles to ref dirs
164
+ acute_angle = np.arccos(F_prime @ self.V.T)
165
+ niches = acute_angle.argmin(axis=1)
166
+
167
+ # assign to each reference direction the solution
168
+ niches_to_ind = [[] for _ in range(len(self.V))]
169
+ for k, i in enumerate(niches):
170
+ niches_to_ind[i].append(k)
171
+
172
+ # all individuals which will be surviving
173
+ survivors = []
174
+
175
+ # for each reference direction
176
+ for k in range(len(self.V)):
177
+
178
+ # individuals assigned to the niche
179
+ assigned_to_niche = niches_to_ind[k]
180
+
181
+ # if niche not empty
182
+ if len(assigned_to_niche) > 0:
183
+
184
+ # the angle of niche to nearest neighboring niche
185
+ gamma = self.gamma[k]
186
+
187
+ # the angle from the individuals of this niches to the niche itself
188
+ theta = acute_angle[assigned_to_niche, k]
189
+
190
+ # the penalty which is applied for the metric
191
+ M = problem.n_obj if problem.n_obj > 2.0 else 1.0
192
+ penalty = M * ((n_gen / n_max_gen) ** self.alpha) * (theta / gamma)
193
+
194
+ # calculate the angle-penalized penalized (APD)
195
+ apd = dist_to_ideal[assigned_to_niche] * (1 + penalty)
196
+
197
+ # the individual which survives
198
+ survivor = assigned_to_niche[apd.argmin()]
199
+
200
+ # set attributes to the individual
201
+ pop[assigned_to_niche].set(theta=theta, apd=apd, niche=k, opt=False)
202
+ pop[survivor].set("opt", True)
203
+
204
+ # select the one with smallest APD value
205
+ survivors.append(survivor)
206
+
207
+ ret = pop[survivors]
208
+ self.niches = niches_to_ind
209
+ self.nadir = ret.get("F").max(axis=0)
210
+
211
+ return ret
212
+
213
+
214
+ parse_doc_string(RVEA.__init__)
@@ -0,0 +1,195 @@
1
+ import numpy as np
2
+
3
+ from pymoo.algorithms.base.genetic import GeneticAlgorithm
4
+ from pymoo.core.population import Population
5
+ from pymoo.core.survival import Survival
6
+ from pymoo.docs import parse_doc_string
7
+ from pymoo.indicators.hv.exact import ExactHypervolume
8
+ from pymoo.indicators.hv.exact_2d import ExactHypervolume2D
9
+ from pymoo.indicators.hv.monte_carlo import ApproximateMonteCarloHypervolume
10
+ from pymoo.operators.crossover.sbx import SBX
11
+ from pymoo.operators.mutation.pm import PM
12
+ from pymoo.operators.sampling.rnd import FloatRandomSampling
13
+ from pymoo.operators.selection.tournament import compare, TournamentSelection
14
+ from pymoo.util.display.multi import MultiObjectiveOutput
15
+ from pymoo.util.dominator import Dominator
16
+ from pymoo.util.function_loader import load_function
17
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
18
+ from pymoo.util.normalization import normalize
19
+
20
+
21
+ # ---------------------------------------------------------------------------------------------------------
22
+ # Environmental Survival - Remove the solution with the least HV contribution
23
+ # ---------------------------------------------------------------------------------------------------------
24
+
25
+
26
+ class LeastHypervolumeContributionSurvival(Survival):
27
+
28
+ def __init__(self, eps=10.0) -> None:
29
+ super().__init__(filter_infeasible=True)
30
+ self.eps = eps
31
+
32
+ def _do(self, problem, pop, *args, n_survive=None, ideal=None, nadir=None, **kwargs):
33
+
34
+ # get the objective space values and objects
35
+ F = pop.get("F").astype(float, copy=False)
36
+
37
+ # if the boundary points are not provided -> estimate them from pop
38
+ if ideal is None:
39
+ ideal = F.min(axis=0)
40
+ if nadir is None:
41
+ nadir = F.max(axis=0)
42
+
43
+ # the number of objectives
44
+ _, n_obj = F.shape
45
+
46
+ # the final indices of surviving individuals
47
+ survivors = []
48
+
49
+ # do the non-dominated sorting until splitting front
50
+ fronts = NonDominatedSorting().do(F, n_stop_if_ranked=n_survive)
51
+
52
+ for k, front in enumerate(fronts):
53
+
54
+ # get the actual front as individuals
55
+ front = pop[front]
56
+ front.set("rank", k)
57
+
58
+ if len(survivors) + len(front) > n_survive:
59
+
60
+ # normalize all the function values for the front
61
+ F = front.get("F")
62
+ F = normalize(F, ideal, nadir)
63
+
64
+ # define the reference point and shift it a bit - F is already normalized!
65
+ ref_point = np.full(problem.n_obj, 1.0 + self.eps)
66
+
67
+ _, n_obj = F.shape
68
+
69
+ # choose the suitable hypervolume method
70
+ clazz = ExactHypervolume
71
+ if n_obj == 2:
72
+ clazz = ExactHypervolume2D
73
+ elif n_obj > 3:
74
+ clazz = ApproximateMonteCarloHypervolume
75
+
76
+ # finally do the computation
77
+ hv = clazz(ref_point).add(F)
78
+
79
+ # current front sorted by crowding distance if splitting
80
+ while len(survivors) + len(front) > n_survive:
81
+ k = hv.hvc.argmin()
82
+ hv.delete(k)
83
+ front = np.delete(front, k)
84
+
85
+ # extend the survivors by all or selected individuals
86
+ survivors.extend(front)
87
+
88
+ return Population.create(*survivors)
89
+
90
+
91
+ # ---------------------------------------------------------------------------------------------------------
92
+ # Binary Tournament
93
+ # ---------------------------------------------------------------------------------------------------------
94
+
95
+
96
+ def cv_and_dom_tournament(pop, P, *args, **kwargs):
97
+ n_tournaments, n_parents = P.shape
98
+
99
+ if n_parents != 2:
100
+ raise ValueError("Only implemented for binary tournament!")
101
+
102
+ S = np.full(n_tournaments, np.nan)
103
+
104
+ for i in range(n_tournaments):
105
+
106
+ a, b = P[i, 0], P[i, 1]
107
+ a_cv, a_f, b_cv, b_f, = pop[a].CV[0], pop[a].F, pop[b].CV[0], pop[b].F
108
+
109
+ # if at least one solution is infeasible
110
+ if a_cv > 0.0 or b_cv > 0.0:
111
+ S[i] = compare(a, a_cv, b, b_cv, method='smaller_is_better', return_random_if_equal=True)
112
+
113
+ # both solutions are feasible
114
+ else:
115
+
116
+ # if one dominates another choose the nds one
117
+ rel = Dominator.get_relation(a_f, b_f)
118
+ if rel == 1:
119
+ S[i] = a
120
+ elif rel == -1:
121
+ S[i] = b
122
+
123
+ # if rank or domination relation didn't make a decision compare by crowding
124
+ if np.isnan(S[i]):
125
+ S[i] = np.random.choice([a, b])
126
+
127
+ return S[:, None].astype(int, copy=False)
128
+
129
+
130
+ # ---------------------------------------------------------------------------------------------------------
131
+ # Algorithm
132
+ # ---------------------------------------------------------------------------------------------------------
133
+
134
+ class SMSEMOA(GeneticAlgorithm):
135
+
136
+ def __init__(self,
137
+ pop_size=100,
138
+ sampling=FloatRandomSampling(),
139
+ selection=TournamentSelection(func_comp=cv_and_dom_tournament),
140
+ crossover=SBX(),
141
+ mutation=PM(),
142
+ survival=LeastHypervolumeContributionSurvival(),
143
+ eliminate_duplicates=True,
144
+ n_offsprings=None,
145
+ normalize=True,
146
+ output=MultiObjectiveOutput(),
147
+ **kwargs):
148
+ """
149
+
150
+ Parameters
151
+ ----------
152
+ pop_size : {pop_size}
153
+ sampling : {sampling}
154
+ selection : {selection}
155
+ crossover : {crossover}
156
+ mutation : {mutation}
157
+ eliminate_duplicates : {eliminate_duplicates}
158
+ n_offsprings : {n_offsprings}
159
+
160
+ """
161
+ super().__init__(pop_size=pop_size,
162
+ sampling=sampling,
163
+ selection=selection,
164
+ crossover=crossover,
165
+ mutation=mutation,
166
+ survival=survival,
167
+ eliminate_duplicates=eliminate_duplicates,
168
+ n_offsprings=n_offsprings,
169
+ output=output,
170
+ advance_after_initial_infill=True,
171
+ **kwargs)
172
+
173
+ self.normalize = normalize
174
+
175
+ def _advance(self, infills=None, **kwargs):
176
+
177
+ ideal, nadir = None, None
178
+
179
+ # estimate ideal and nadir from the current population (more robust then from doing it from merged)
180
+ if self.normalize:
181
+ F = self.pop.get("F")
182
+ ideal, nadir = F.min(axis=0), F.max(axis=0) + 1e-32
183
+
184
+ # merge the offsprings with the current population
185
+ if infills is not None:
186
+ pop = Population.merge(self.pop, infills)
187
+ else:
188
+ pop = self.pop
189
+
190
+
191
+ self.pop = self.survival.do(self.problem, pop, n_survive=self.pop_size, algorithm=self,
192
+ ideal=ideal, nadir=nadir, **kwargs)
193
+
194
+
195
+ parse_doc_string(SMSEMOA.__init__)