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,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)
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)
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__)
@@ -0,0 +1,358 @@
1
+ import warnings
2
+
3
+ import numpy as np
4
+ from numpy.linalg import LinAlgError
5
+
6
+ from pymoo.algorithms.base.genetic import GeneticAlgorithm
7
+ from pymoo.core.survival import Survival
8
+ from pymoo.docs import parse_doc_string
9
+ from pymoo.operators.crossover.sbx import SBX
10
+ from pymoo.operators.mutation.pm import PM
11
+ from pymoo.operators.sampling.rnd import FloatRandomSampling
12
+ from pymoo.operators.selection.tournament import TournamentSelection, compare
13
+ from pymoo.util.display.multi import MultiObjectiveOutput
14
+ from pymoo.util.function_loader import load_function
15
+ from pymoo.util.misc import intersect, has_feasible
16
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
17
+
18
+
19
+ # =========================================================================================================
20
+ # Implementation
21
+ # =========================================================================================================
22
+
23
+ def comp_by_cv_then_random(pop, P, **kwargs):
24
+ S = np.full(P.shape[0], np.nan)
25
+
26
+ for i in range(P.shape[0]):
27
+ a, b = P[i, 0], P[i, 1]
28
+
29
+ # if at least one solution is infeasible
30
+ if pop[a].CV > 0.0 or pop[b].CV > 0.0:
31
+ S[i] = compare(a, pop[a].CV, b, pop[b].CV, method='smaller_is_better', return_random_if_equal=True)
32
+
33
+ # both solutions are feasible just set random
34
+ else:
35
+ S[i] = np.random.choice([a, b])
36
+
37
+ return S[:, None].astype(int)
38
+
39
+
40
+ class NSGA3(GeneticAlgorithm):
41
+
42
+ def __init__(self,
43
+ ref_dirs,
44
+ pop_size=None,
45
+ sampling=FloatRandomSampling(),
46
+ selection=TournamentSelection(func_comp=comp_by_cv_then_random),
47
+ crossover=SBX(eta=30, prob=1.0),
48
+ mutation=PM(eta=20),
49
+ eliminate_duplicates=True,
50
+ n_offsprings=None,
51
+ output=MultiObjectiveOutput(),
52
+ **kwargs):
53
+ """
54
+
55
+ Parameters
56
+ ----------
57
+
58
+ ref_dirs : {ref_dirs}
59
+ pop_size : int (default = None)
60
+ By default the population size is set to None which means that it will be equal to the number of reference
61
+ line. However, if desired this can be overwritten by providing a positive number.
62
+ sampling : {sampling}
63
+ selection : {selection}
64
+ crossover : {crossover}
65
+ mutation : {mutation}
66
+ eliminate_duplicates : {eliminate_duplicates}
67
+ n_offsprings : {n_offsprings}
68
+
69
+ """
70
+
71
+ self.ref_dirs = ref_dirs
72
+
73
+ # in case of R-NSGA-3 they will be None - otherwise this will be executed
74
+ if self.ref_dirs is not None:
75
+
76
+ if pop_size is None:
77
+ pop_size = len(self.ref_dirs)
78
+
79
+ if pop_size < len(self.ref_dirs):
80
+ print(
81
+ f"WARNING: pop_size={pop_size} is less than the number of reference directions ref_dirs={len(self.ref_dirs)}.\n"
82
+ "This might cause unwanted behavior of the algorithm. \n"
83
+ "Please make sure pop_size is equal or larger than the number of reference directions. ")
84
+
85
+ if 'survival' in kwargs:
86
+ survival = kwargs['survival']
87
+ del kwargs['survival']
88
+ else:
89
+ survival = ReferenceDirectionSurvival(ref_dirs)
90
+
91
+ super().__init__(pop_size=pop_size,
92
+ sampling=sampling,
93
+ selection=selection,
94
+ crossover=crossover,
95
+ mutation=mutation,
96
+ survival=survival,
97
+ eliminate_duplicates=eliminate_duplicates,
98
+ n_offsprings=n_offsprings,
99
+ output=output,
100
+ advance_after_initial_infill=True,
101
+ **kwargs)
102
+
103
+ def _setup(self, problem, **kwargs):
104
+
105
+ if self.ref_dirs is not None:
106
+ if self.ref_dirs.shape[1] != problem.n_obj:
107
+ raise Exception(
108
+ "Dimensionality of reference points must be equal to the number of objectives: %s != %s" %
109
+ (self.ref_dirs.shape[1], problem.n_obj))
110
+
111
+ def _set_optimum(self, **kwargs):
112
+ if not has_feasible(self.pop):
113
+ self.opt = self.pop[[np.argmin(self.pop.get("CV"))]]
114
+ else:
115
+ if len(self.survival.opt):
116
+ self.opt = self.survival.opt
117
+
118
+
119
+ # =========================================================================================================
120
+ # Survival
121
+ # =========================================================================================================
122
+
123
+
124
+ class ReferenceDirectionSurvival(Survival):
125
+
126
+ def __init__(self, ref_dirs):
127
+ super().__init__(filter_infeasible=True)
128
+ self.ref_dirs = ref_dirs
129
+ self.opt = None
130
+ self.norm = HyperplaneNormalization(ref_dirs.shape[1])
131
+
132
+ def _do(self, problem, pop, n_survive, D=None, **kwargs):
133
+
134
+ # attributes to be set after the survival
135
+ F = pop.get("F")
136
+
137
+ # calculate the fronts of the population
138
+ fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive)
139
+ non_dominated, last_front = fronts[0], fronts[-1]
140
+
141
+ # update the hyperplane based boundary estimation
142
+ hyp_norm = self.norm
143
+ hyp_norm.update(F, nds=non_dominated)
144
+ ideal, nadir = hyp_norm.ideal_point, hyp_norm.nadir_point
145
+
146
+ # consider only the population until we come to the splitting front
147
+ I = np.concatenate(fronts)
148
+ pop, rank, F = pop[I], rank[I], F[I]
149
+
150
+ # update the front indices for the current population
151
+ counter = 0
152
+ for i in range(len(fronts)):
153
+ for j in range(len(fronts[i])):
154
+ fronts[i][j] = counter
155
+ counter += 1
156
+ last_front = fronts[-1]
157
+
158
+ # associate individuals to niches
159
+ niche_of_individuals, dist_to_niche, dist_matrix = \
160
+ associate_to_niches(F, self.ref_dirs, ideal, nadir)
161
+
162
+ # attributes of a population
163
+ pop.set('rank', rank,
164
+ 'niche', niche_of_individuals,
165
+ 'dist_to_niche', dist_to_niche)
166
+
167
+ # set the optimum, first front and closest to all reference directions
168
+ closest = np.unique(dist_matrix[:, np.unique(niche_of_individuals)].argmin(axis=0))
169
+ self.opt = pop[intersect(fronts[0], closest)]
170
+ if len(self.opt) == 0:
171
+ self.opt = pop[fronts[0]]
172
+
173
+ # if we need to select individuals to survive
174
+ if len(pop) > n_survive:
175
+
176
+ # if there is only one front
177
+ if len(fronts) == 1:
178
+ n_remaining = n_survive
179
+ until_last_front = np.array([], dtype=int)
180
+ niche_count = np.zeros(len(self.ref_dirs), dtype=int)
181
+
182
+ # if some individuals already survived
183
+ else:
184
+ until_last_front = np.concatenate(fronts[:-1])
185
+ niche_count = calc_niche_count(len(self.ref_dirs), niche_of_individuals[until_last_front])
186
+ n_remaining = n_survive - len(until_last_front)
187
+
188
+ S = niching(pop[last_front], n_remaining, niche_count, niche_of_individuals[last_front],
189
+ dist_to_niche[last_front])
190
+
191
+ survivors = np.concatenate((until_last_front, last_front[S].tolist()))
192
+ pop = pop[survivors]
193
+
194
+ return pop
195
+
196
+
197
+ def niching(pop, n_remaining, niche_count, niche_of_individuals, dist_to_niche):
198
+ survivors = []
199
+
200
+ # boolean array of elements that are considered for each iteration
201
+ mask = np.full(len(pop), True)
202
+
203
+ while len(survivors) < n_remaining:
204
+
205
+ # number of individuals to select in this iteration
206
+ n_select = n_remaining - len(survivors)
207
+
208
+ # all niches where new individuals can be assigned to and the corresponding niche count
209
+ next_niches_list = np.unique(niche_of_individuals[mask])
210
+ next_niche_count = niche_count[next_niches_list]
211
+
212
+ # the minimum niche count
213
+ min_niche_count = next_niche_count.min()
214
+
215
+ # all niches with the minimum niche count (truncate randomly if there are more niches than remaining individuals)
216
+ next_niches = next_niches_list[np.where(next_niche_count == min_niche_count)[0]]
217
+ next_niches = next_niches[np.random.permutation(len(next_niches))[:n_select]]
218
+
219
+ for next_niche in next_niches:
220
+
221
+ # indices of individuals that are considered and assign to next_niche
222
+ next_ind = np.where(np.logical_and(niche_of_individuals == next_niche, mask))[0]
223
+
224
+ # shuffle to break random tie (equal perp. dist) or select randomly
225
+ np.random.shuffle(next_ind)
226
+
227
+ if niche_count[next_niche] == 0:
228
+ next_ind = next_ind[np.argmin(dist_to_niche[next_ind])]
229
+ else:
230
+ # already randomized through shuffling
231
+ next_ind = next_ind[0]
232
+
233
+ # add the selected individual to the survivors
234
+ mask[next_ind] = False
235
+ survivors.append(int(next_ind))
236
+
237
+ # increase the corresponding niche count
238
+ niche_count[next_niche] += 1
239
+
240
+ return survivors
241
+
242
+
243
+ def associate_to_niches(F, niches, ideal_point, nadir_point, utopian_epsilon=0.0):
244
+ utopian_point = ideal_point - utopian_epsilon
245
+
246
+ denom = nadir_point - utopian_point
247
+ denom[denom == 0] = 1e-12
248
+
249
+ # normalize by ideal point and intercepts
250
+ N = (F - utopian_point) / denom
251
+ dist_matrix = load_function("calc_perpendicular_distance")(N, niches)
252
+
253
+ niche_of_individuals = np.argmin(dist_matrix, axis=1)
254
+ dist_to_niche = dist_matrix[np.arange(F.shape[0]), niche_of_individuals]
255
+
256
+ return niche_of_individuals, dist_to_niche, dist_matrix
257
+
258
+
259
+ def calc_niche_count(n_niches, niche_of_individuals):
260
+ niche_count = np.zeros(n_niches, dtype=int)
261
+ index, count = np.unique(niche_of_individuals, return_counts=True)
262
+ niche_count[index] = count
263
+ return niche_count
264
+
265
+
266
+ # =========================================================================================================
267
+ # Normalization
268
+ # =========================================================================================================
269
+
270
+
271
+ class HyperplaneNormalization:
272
+
273
+ def __init__(self, n_dim) -> None:
274
+ super().__init__()
275
+ self.ideal_point = np.full(n_dim, np.inf)
276
+ self.worst_point = np.full(n_dim, -np.inf)
277
+ self.nadir_point = None
278
+ self.extreme_points = None
279
+
280
+ def update(self, F, nds=None):
281
+ # find or usually update the new ideal point - from feasible solutions
282
+ self.ideal_point = np.min(np.vstack((self.ideal_point, F)), axis=0)
283
+ self.worst_point = np.max(np.vstack((self.worst_point, F)), axis=0)
284
+
285
+ # this decides whether only non-dominated points or all points are used to determine the extreme points
286
+ if nds is None:
287
+ nds = np.arange(len(F))
288
+
289
+ # find the extreme points for normalization
290
+ self.extreme_points = get_extreme_points_c(F[nds, :], self.ideal_point,
291
+ extreme_points=self.extreme_points)
292
+
293
+ # find the intercepts for normalization and do backup if gaussian elimination fails
294
+ worst_of_population = np.max(F, axis=0)
295
+ worst_of_front = np.max(F[nds, :], axis=0)
296
+
297
+ self.nadir_point = get_nadir_point(self.extreme_points, self.ideal_point, self.worst_point,
298
+ worst_of_front, worst_of_population)
299
+
300
+
301
+ def get_extreme_points_c(F, ideal_point, extreme_points=None):
302
+ # calculate the asf which is used for the extreme point decomposition
303
+ weights = np.eye(F.shape[1])
304
+ weights[weights == 0] = 1e6
305
+
306
+ # add the old extreme points to never lose them for normalization
307
+ _F = F
308
+ if extreme_points is not None:
309
+ _F = np.concatenate([extreme_points, _F], axis=0)
310
+
311
+ # use __F because we substitute small values to be 0
312
+ __F = _F - ideal_point
313
+ __F[__F < 1e-3] = 0
314
+
315
+ # update the extreme points for the normalization having the highest asf value each
316
+ F_asf = np.max(__F * weights[:, None, :], axis=2)
317
+
318
+ I = np.argmin(F_asf, axis=1)
319
+ extreme_points = _F[I, :]
320
+
321
+ return extreme_points
322
+
323
+
324
+ def get_nadir_point(extreme_points, ideal_point, worst_point, worst_of_front, worst_of_population):
325
+ try:
326
+
327
+ # find the intercepts using gaussian elimination
328
+ M = extreme_points - ideal_point
329
+ b = np.ones(extreme_points.shape[1])
330
+ plane = np.linalg.solve(M, b)
331
+
332
+ warnings.simplefilter("ignore")
333
+ intercepts = 1 / plane
334
+
335
+ nadir_point = ideal_point + intercepts
336
+
337
+ # check if the hyperplane makes sense
338
+ if not np.allclose(np.dot(M, plane), b) or np.any(intercepts <= 1e-6):
339
+ raise LinAlgError()
340
+
341
+ # if the nadir point should be larger than any value discovered so far set it to that value
342
+ # NOTE: different to the proposed version in the paper
343
+ b = nadir_point > worst_point
344
+ nadir_point[b] = worst_point[b]
345
+
346
+ except LinAlgError:
347
+
348
+ # fall back to worst of front otherwise
349
+ nadir_point = worst_of_front
350
+
351
+ # if the range is too small set it to worst of population
352
+ b = nadir_point - ideal_point <= 1e-6
353
+ nadir_point[b] = worst_of_population[b]
354
+
355
+ return nadir_point
356
+
357
+
358
+ parse_doc_string(NSGA3.__init__)