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,370 @@
1
+ import sys
2
+ from abc import ABC, abstractmethod
3
+
4
+ import numpy as np
5
+
6
+ from pymoo.algorithms.base.genetic import GeneticAlgorithm
7
+ from pymoo.algorithms.moo.nsga2 import binary_tournament
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
13
+ from pymoo.operators.survival.rank_and_crowding import RankAndCrowding
14
+ from pymoo.termination.default import DefaultMultiObjectiveTermination
15
+ from pymoo.util import value_functions as mvf
16
+ from pymoo.util.display.multi import MultiObjectiveOutput
17
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
18
+ from pymoo.util.reference_direction import select_points_with_maximum_distance
19
+ from pymoo.util.vf_dominator import VFDominator
20
+
21
+
22
+ # =========================================================================================================
23
+ # Implementation
24
+ # =========================================================================================================
25
+
26
+
27
+ class AutomatedDM(ABC):
28
+
29
+ def __init__(self, get_pairwise_ranks_func=None):
30
+ self.get_pairwise_ranks_func = get_pairwise_ranks_func
31
+
32
+ @abstractmethod
33
+ def makeDecision(self, F):
34
+ pass
35
+
36
+ def makePairwiseDecision(self, F):
37
+
38
+ dm = lambda F: self.makeDecision(F)
39
+ ranks = self.get_pairwise_ranks_func(F, 1, dm=dm)
40
+
41
+ return ranks
42
+
43
+
44
+
45
+ class PINSGA2(GeneticAlgorithm):
46
+
47
+ def __init__(self,
48
+ pop_size=100,
49
+ sampling=FloatRandomSampling(),
50
+ selection=TournamentSelection(func_comp=binary_tournament),
51
+ crossover=SBX(eta=15, prob=0.9),
52
+ mutation=PM(eta=20),
53
+ output=MultiObjectiveOutput(),
54
+ tau=10,
55
+ eta=4,
56
+ opt_method="trust-constr",
57
+ vf_type="poly",
58
+ eps_max=1000,
59
+ ranking_type='pairwise',
60
+ presi_signs=None,
61
+ automated_dm=None,
62
+ verbose=False,
63
+ **kwargs):
64
+
65
+ self.survival = RankAndCrowding(nds=NonDominatedSorting(dominator=VFDominator(self)))
66
+
67
+ super().__init__(
68
+ pop_size=pop_size,
69
+ sampling=sampling,
70
+ selection=selection,
71
+ crossover=crossover,
72
+ mutation=mutation,
73
+ survival=self.survival,
74
+ output=output,
75
+ advance_after_initial_infill=True,
76
+ **kwargs)
77
+
78
+ self.termination = DefaultMultiObjectiveTermination()
79
+ self.tournament_type = 'comp_by_dom_and_crowding'
80
+
81
+ self.ranking_type=ranking_type
82
+ self.presi_signs=presi_signs
83
+
84
+ self.vf_type = vf_type
85
+ self.opt_method = opt_method
86
+ self.tau = tau
87
+ self.eta = eta
88
+ self.eta_F = []
89
+ self.vf_res = None
90
+ self.v2 = None
91
+ self.vf_plot_flag = False
92
+ self.vf_plot = None
93
+ self.historical_F = None
94
+ self.prev_pop = None
95
+ self.fronts = []
96
+ self.eps_max = eps_max
97
+
98
+ self.verbose = verbose
99
+
100
+ if automated_dm is not None:
101
+ automated_dm.get_pairwise_ranks_func = self._get_pairwise_ranks
102
+ self.automated_dm = automated_dm
103
+
104
+ def _warn(self, msg):
105
+ if self.verbose:
106
+ sys.stderr.write(msg)
107
+
108
+ @staticmethod
109
+ def _prompt_for_ranks(F, presi_signs):
110
+
111
+ for (e, f) in enumerate(F):
112
+ print("Solution %d %s" % (e + 1, f * presi_signs))
113
+
114
+ dim = F.shape[0]
115
+
116
+ raw_ranks = input(f"Ranks (e.g., \"3, {dim}, ..., 1\" for 3rd best, {dim}th best, ..., 1st best): ")
117
+
118
+ if raw_ranks == "":
119
+ ranks = []
120
+ else:
121
+ ranks = [int(raw_rank) for raw_rank in raw_ranks.split() ]
122
+
123
+ return ranks
124
+
125
+ @staticmethod
126
+ def _present_ranks(F, dm_ranks, presi_signs):
127
+
128
+ print("Solutions are ranked as:")
129
+
130
+ for (e, f) in enumerate(F):
131
+ print("Solution %d %s: Rank %d" % (e + 1, f * presi_signs, dm_ranks[e]))
132
+
133
+
134
+ @staticmethod
135
+ def _get_pairwise_ranks(F, presi_signs, dm=None):
136
+
137
+ if not dm:
138
+
139
+ dm = lambda F: input("\nWhich solution do you like best?\n" + \
140
+ f"[a] {F[0]}\n" + \
141
+ f"[b] {F[1]}\n" + \
142
+ "[c] These solutions are equivalent.\n--> " )
143
+
144
+ # initialize empty ranking
145
+ _ranks = []
146
+ for i, f in enumerate( F ):
147
+
148
+ # handle empty case, put first element in first place
149
+ if not _ranks:
150
+ _ranks.append( [i] )
151
+
152
+ else:
153
+ inserted = False
154
+
155
+ # for each remaining elements, compare to all currently ranked elements
156
+ for j, group in enumerate( _ranks ):
157
+
158
+ # get pairwise preference from user
159
+ while True:
160
+
161
+ points_to_compare = np.array( [f*presi_signs, F[ group[0] ]*presi_signs] )
162
+ preference_raw = dm( points_to_compare )
163
+
164
+ preference = preference_raw.strip().lower()
165
+
166
+ if preference in ['a', 'b', 'c']:
167
+ break
168
+ print("Invalid input. Please enter 'a', 'b', or 'c'.")
169
+
170
+ # if better than currently ranked element place before that element
171
+ if preference == 'a':
172
+ _ranks.insert( j, [i] )
173
+ inserted = True
174
+ break
175
+
176
+ # if equal to currently ranked element place with that element
177
+ elif preference == 'c':
178
+ group.append( i )
179
+ inserted = True
180
+ break
181
+
182
+ # if found to be worse than all place at the end
183
+ if not inserted:
184
+ _ranks.append( [i] )
185
+
186
+ ranks = np.zeros ( len( F ), dtype=int )
187
+
188
+ for rank, group in enumerate( _ranks ):
189
+ for index in group:
190
+ ranks[index] = rank
191
+
192
+ return np.array( ranks ) + 1
193
+
194
+
195
+ @staticmethod
196
+ def _get_ranks(F, presi_signs):
197
+
198
+ ranks_invalid = True
199
+
200
+ dim = F.shape[0]
201
+
202
+ print(f"Give each solution a ranking, with 1 being the highest score, and {dim} being the lowest score:")
203
+
204
+ ranks = PINSGA2._prompt_for_ranks(F, presi_signs)
205
+
206
+ while ranks_invalid:
207
+
208
+ fc = F.shape[0]
209
+
210
+ if len(ranks) > 0 and max(ranks) <= fc and min(ranks) >= 1:
211
+
212
+ ranks_invalid = False
213
+
214
+ else:
215
+
216
+ print("Invalid ranks given. Please try again")
217
+
218
+ ranks = PINSGA2._prompt_for_ranks(F, presi_signs)
219
+
220
+ return np.array(ranks);
221
+
222
+
223
+ def _reset_dm_preference(self):
224
+
225
+ self._warn("Back-tracking and removing DM preference from search.")
226
+
227
+ self.eta_F = []
228
+ self.vf_res = None
229
+ self.v2 = None
230
+ self.vf_plot_flag = False
231
+ self.vf_plot = None
232
+ self.pop = self.prev_pop
233
+
234
+
235
+ def _advance(self, infills=None, **kwargs):
236
+
237
+ super()._advance(infills=infills, **kwargs)
238
+
239
+ rank, F = self.pop.get("rank", "F")
240
+
241
+ self.fronts = rank
242
+
243
+ F = F[rank == 0]
244
+
245
+ if self.historical_F is not None:
246
+ self.historical_F = np.vstack((self.historical_F, F))
247
+ else:
248
+ self.historical_F = F
249
+
250
+ to_find = self.eta if F.shape[0] >= self.eta else F.shape[0]
251
+
252
+ if self.presi_signs is None:
253
+ self.presi_signs = np.ones(F.shape[1])
254
+
255
+ # Eta is the number of solutions displayed to the DM
256
+ eta_F_indices = select_points_with_maximum_distance(F, to_find, random_state=self.random_state)
257
+
258
+ self.eta_F = F[eta_F_indices]
259
+ self.eta_F = self.eta_F[self.eta_F[:,0].argsort()]
260
+
261
+ # Remove duplicate rows
262
+ self.eta_F = np.unique(self.eta_F, axis=0)
263
+
264
+ # A frozen view of the optimization each tau generations
265
+ self.paused_F = F
266
+
267
+ # Record the previous population in case we need to back track
268
+ self.prev_pop = self.pop
269
+
270
+ dm_time = self.n_gen % self.tau == 0
271
+
272
+ # Check whether we have more than one solution
273
+ if dm_time and len(self.eta_F) < 2:
274
+
275
+ self._warn("Population only contains one non-dominated solution. ")
276
+
277
+ self._reset_dm_preference()
278
+
279
+ elif dm_time:
280
+
281
+ # Check if the DM is a machine or a human
282
+ if self.automated_dm is None:
283
+
284
+ # Human DM
285
+ if self.ranking_type == "absolute":
286
+ dm_ranks = PINSGA2._get_ranks(self.eta_F, self.presi_signs)
287
+ elif self.ranking_type == "pairwise":
288
+ dm_ranks = PINSGA2._get_pairwise_ranks(self.eta_F, self.presi_signs)
289
+ PINSGA2._present_ranks(self.eta_F, dm_ranks, self.presi_signs)
290
+ else:
291
+ raise ValueError("Invalid ranking type [%s] given." % self.ranking_type)
292
+ else:
293
+
294
+ # Automated DM
295
+ if self.ranking_type == "absolute":
296
+ dm_ranks = self.automated_dm.makeDecision(self.eta_F)
297
+ elif self.ranking_type == "pairwise":
298
+ dm_ranks = self.automated_dm.makePairwiseDecision(self.eta_F)
299
+ else:
300
+ raise ValueError("Invalid ranking type [%s] given." % self.ranking_type)
301
+
302
+
303
+
304
+ if len(set(rank)) == 0:
305
+
306
+ self._warn("No preference between any two points provided.")
307
+
308
+ self._reset_dm_preference()
309
+
310
+ return
311
+
312
+ eta_F = self.eta_F
313
+
314
+ while eta_F.shape[0] > 1:
315
+
316
+ if self.vf_type == "linear":
317
+
318
+ vf_res = mvf.create_linear_vf(eta_F * -1,
319
+ dm_ranks.tolist(),
320
+ eps_max=self.eps_max,
321
+ method=self.opt_method)
322
+
323
+ elif self.vf_type == "poly":
324
+
325
+ vf_res = mvf.create_poly_vf(eta_F * -1,
326
+ dm_ranks.tolist(),
327
+ eps_max=self.eps_max,
328
+ method=self.opt_method)
329
+
330
+ else:
331
+
332
+ raise ValueError("Value function %s not supported" % self.vf_type)
333
+
334
+ # check if we were able to model the VF
335
+ if vf_res.fit:
336
+
337
+ self.vf_res = vf_res
338
+ self.vf_plot_flag = True
339
+ self.v2 = self.vf_res.vf(eta_F[dm_ranks[1] - 1] * -1).item()
340
+ break
341
+
342
+ else:
343
+
344
+ # If we didn't the model, try to remove the least preferred point and try to refit
345
+ self._warn("Could not fit a function to the DM preference")
346
+
347
+ if eta_F.shape[0] == 2:
348
+
349
+ # If not, reset and use normal domination
350
+ self._warn("Removing DM preference")
351
+ self._reset_dm_preference()
352
+ break
353
+
354
+ else:
355
+
356
+ self._warn("Removing the second best preferred solution from the fit.")
357
+
358
+ # ranks start at 1, not zero
359
+ rank_to_remove = dm_ranks[1]
360
+ eta_F = np.delete(eta_F, rank_to_remove - 1, axis=0)
361
+
362
+ dm_ranks = np.concatenate(([dm_ranks[0]], dm_ranks[2:]))
363
+
364
+ # update the ranks, since we just removed one
365
+ dm_ranks[dm_ranks > rank_to_remove] = dm_ranks[dm_ranks > rank_to_remove] - 1
366
+
367
+
368
+ parse_doc_string(PINSGA2.__init__)
369
+
370
+
@@ -0,0 +1,188 @@
1
+ import numpy as np
2
+
3
+ from pymoo.algorithms.moo.nsga2 import NSGA2
4
+ from pymoo.docs import parse_doc_string
5
+ from pymoo.core.survival import Survival
6
+ from pymoo.operators.selection.rnd import RandomSelection
7
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
8
+ from pymoo.util.normalization import get_extreme_points_c
9
+
10
+ # =========================================================================================================
11
+ # Implementation
12
+ # =========================================================================================================
13
+
14
+
15
+ class RNSGA2(NSGA2):
16
+
17
+ def __init__(self,
18
+ ref_points,
19
+ epsilon=0.001,
20
+ normalization="front",
21
+ weights=None,
22
+ extreme_points_as_reference_points=False,
23
+ **kwargs):
24
+ """
25
+
26
+ Parameters
27
+ ----------
28
+
29
+ ref_points : {ref_points}
30
+ epsilon : float
31
+ weights : np.array
32
+ normalization : {{'no', 'front', 'ever'}}
33
+ extreme_points_as_reference_points : bool
34
+
35
+ """
36
+
37
+ self.epsilon = epsilon
38
+ self.weights = weights
39
+ self.normalization = normalization
40
+ self.selection = RandomSelection()
41
+
42
+ super().__init__(**kwargs)
43
+
44
+ self.survival = RankAndModifiedCrowdingSurvival(ref_points, epsilon, weights, normalization,
45
+ extreme_points_as_reference_points)
46
+
47
+
48
+ class RankAndModifiedCrowdingSurvival(Survival):
49
+
50
+ def __init__(self, ref_points,
51
+ epsilon,
52
+ weights,
53
+ normalization,
54
+ extreme_points_as_reference_points
55
+ ) -> None:
56
+
57
+ super().__init__(True)
58
+ self.n_obj = ref_points.shape[1]
59
+ self.ref_points = ref_points
60
+ self.epsilon = epsilon
61
+ self.extreme_points_as_reference_points = extreme_points_as_reference_points
62
+
63
+ self.weights = weights
64
+ if self.weights is None:
65
+ self.weights = np.full(self.n_obj, 1 / self.n_obj)
66
+
67
+ self.normalization = normalization
68
+ self.ideal_point = np.full(self.n_obj, np.inf)
69
+ self.nadir_point = np.full(self.n_obj, -np.inf)
70
+
71
+ def _do(self, problem, pop, n_survive=None, **kwargs):
72
+
73
+ # get the objective space values and objects
74
+ F = pop.get("F")
75
+
76
+ # the final indices of surviving individuals
77
+ survivors = []
78
+
79
+ # do the non-dominated sorting until splitting front
80
+ fronts = NonDominatedSorting().do(F)
81
+
82
+ if self.normalization == "ever":
83
+ # find or usually update the new ideal point - from feasible solutions
84
+ self.ideal_point = np.min(np.vstack((self.ideal_point, F)), axis=0)
85
+ self.nadir_point = np.max(np.vstack((self.nadir_point, F)), axis=0)
86
+
87
+ elif self.normalization == "front":
88
+ front = fronts[0]
89
+ if len(front) > 1:
90
+ self.ideal_point = np.min(F[front], axis=0)
91
+ self.nadir_point = np.max(F[front], axis=0)
92
+
93
+ elif self.normalization == "no":
94
+ self.ideal_point = np.zeros(self.n_obj)
95
+ self.nadir_point = np.ones(self.n_obj)
96
+
97
+ if self.extreme_points_as_reference_points:
98
+ self.ref_points = np.vstack([self.ref_points, get_extreme_points_c(F, self.ideal_point)])
99
+
100
+ # calculate the distance matrix from ever solution to all reference point
101
+ dist_to_ref_points = calc_norm_pref_distance(F, self.ref_points, self.weights, self.ideal_point,
102
+ self.nadir_point)
103
+
104
+ for k, front in enumerate(fronts):
105
+
106
+ # save rank attributes to the individuals - rank = front here
107
+ pop[front].set("rank", np.full(len(front), k))
108
+
109
+ # number of individuals remaining
110
+ n_remaining = n_survive - len(survivors)
111
+
112
+ # the ranking of each point regarding each reference point (two times argsort is necessary)
113
+ rank_by_distance = np.argsort(np.argsort(dist_to_ref_points[front], axis=0), axis=0)
114
+
115
+ # the reference point where the best ranking is coming from
116
+ ref_point_of_best_rank = np.argmin(rank_by_distance, axis=1)
117
+
118
+ # the actual ranking which is used as crowding
119
+ ranking = rank_by_distance[np.arange(len(front)), ref_point_of_best_rank]
120
+
121
+ if len(front) <= n_remaining:
122
+
123
+ # we can simply copy the crowding to ranking. not epsilon selection here
124
+ crowding = ranking
125
+ I = np.arange(len(front))
126
+
127
+ else:
128
+
129
+ # Distance from solution to every other solution and set distance to itself to infinity
130
+ dist_to_others = calc_norm_pref_distance(F[front], F[front], self.weights, self.ideal_point,
131
+ self.nadir_point)
132
+ np.fill_diagonal(dist_to_others, np.inf)
133
+
134
+ # the crowding that will be used for selection
135
+ crowding = np.full(len(front), np.nan)
136
+
137
+ # solutions which are not already selected - for
138
+ not_selected = np.argsort(ranking)
139
+
140
+ # until we have saved a crowding for each solution
141
+ while len(not_selected) > 0:
142
+
143
+ # select the closest solution
144
+ idx = not_selected[0]
145
+
146
+ # set crowding for that individual
147
+ crowding[idx] = ranking[idx]
148
+
149
+ # need to remove myself from not-selected array
150
+ to_remove = [idx]
151
+
152
+ # Group of close solutions
153
+ dist = dist_to_others[idx][not_selected]
154
+ group = not_selected[np.where(dist < self.epsilon)[0]]
155
+
156
+ # if there exists solution with a distance less than epsilon
157
+ if len(group):
158
+ # discourage them by giving them a high crowding
159
+ crowding[group] = ranking[group] + np.round(len(front) / 2)
160
+
161
+ # remove group from not_selected array
162
+ to_remove.extend(group)
163
+
164
+ not_selected = np.array([i for i in not_selected if i not in to_remove])
165
+
166
+ # now sort by the crowding (actually modified rank) ascending and let the best survive
167
+ I = np.argsort(crowding)[:n_remaining]
168
+
169
+ # set the crowding to all individuals
170
+ pop[front].set("crowding", crowding)
171
+
172
+ # extend the survivors by all or selected individuals
173
+ survivors.extend(front[I])
174
+
175
+ # inverse of crowding because nsga2 does maximize it (then tournament selection can stay the same)
176
+ pop.set("crowding", -pop.get("crowding"))
177
+
178
+ return pop[survivors]
179
+
180
+
181
+ def calc_norm_pref_distance(A, B, weights, ideal, nadir):
182
+ D = np.repeat(A, B.shape[0], axis=0) - np.tile(B, (A.shape[0], 1))
183
+ N = ((D / (nadir - ideal)) ** 2) * weights
184
+ N = np.sqrt(np.sum(N, axis=1) * len(weights))
185
+ return np.reshape(N, (A.shape[0], B.shape[0]))
186
+
187
+
188
+ parse_doc_string(RNSGA2.__init__)