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