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,42 @@
1
+ """
2
+ Standard Python implementation of stochastic ranking.
3
+ """
4
+
5
+ import numpy as np
6
+ from pymoo.util import default_random_state
7
+
8
+
9
+ def swap(M, a, b):
10
+ """Swap two elements in array M."""
11
+ M[a], M[b] = M[b], M[a]
12
+
13
+
14
+ @default_random_state
15
+ def stochastic_ranking(f, phi, pr, I=None, random_state=None):
16
+ """Stochastic ranking algorithm."""
17
+ _lambda = len(f)
18
+
19
+ if I is None:
20
+ I = np.arange(_lambda)
21
+
22
+ for i in range(_lambda):
23
+
24
+ at_least_one_swap = False
25
+
26
+ for j in range(_lambda - 1):
27
+
28
+ u = random_state.random()
29
+
30
+ if u < pr or (phi[I[j]] == 0 and phi[I[j + 1]] == 0):
31
+ if f[I[j]] > f[I[j + 1]]:
32
+ swap(I, j, j + 1)
33
+ at_least_one_swap = True
34
+ else:
35
+ if phi[I[j]] > phi[I[j + 1]]:
36
+ swap(I, j, j + 1)
37
+ at_least_one_swap = True
38
+
39
+ if not at_least_one_swap:
40
+ break
41
+
42
+ return I
@@ -0,0 +1,24 @@
1
+ import sys
2
+
3
+
4
+ BACKENDS = {
5
+ "numpy": "numpy",
6
+ "autograd": "autograd.numpy",
7
+ "jax": "jax.numpy"
8
+ }
9
+
10
+ # Current active backend
11
+ active_backend = "numpy"
12
+
13
+ def activate(name):
14
+ global active_backend
15
+ if name not in BACKENDS:
16
+ raise ValueError(f"Unknown backend: {name}. Choose from: {list(BACKENDS.keys())}")
17
+ active_backend = name
18
+ # No need to delete module - LazyBackend handles dynamic switching
19
+
20
+
21
+ def deactivate():
22
+ activate("numpy")
23
+
24
+
@@ -0,0 +1,85 @@
1
+ import numpy as np
2
+
3
+ from pymoo.core.meta import Meta
4
+ from pymoo.core.problem import Problem, ElementwiseEvaluationFunction
5
+ from pymoo.gradient import activate, deactivate
6
+
7
+
8
+ class ElementwiseEvaluationFunctionWithGradient(ElementwiseEvaluationFunction):
9
+
10
+ def __init__(self, problem, backend='autograd', args=(), kwargs={}):
11
+ super().__init__(problem, args, kwargs)
12
+ self.backend = backend
13
+
14
+ def __call__(self, x):
15
+ f = super().__call__
16
+
17
+ activate(self.backend)
18
+ if self.backend == "jax":
19
+ from pymoo.gradient.grad_jax import jax_elementwise_value_and_grad
20
+ out, grad = jax_elementwise_value_and_grad(f, x)
21
+ elif self.backend == "autograd":
22
+ from pymoo.gradient.grad_autograd import autograd_elementwise_value_and_grad
23
+ out, grad = autograd_elementwise_value_and_grad(f, x)
24
+ else:
25
+ raise Exception("Unknown backend %s" % self.backend)
26
+ deactivate()
27
+
28
+ for k, v in grad.items():
29
+ out["d" + k] = np.array(v)
30
+
31
+ return out
32
+
33
+
34
+ class ElementwiseAutomaticDifferentiation(Meta, Problem):
35
+
36
+ def __init__(self, problem, backend='autograd', copy=True):
37
+ if not problem.elementwise:
38
+ raise Exception("Elementwise automatic differentiation can only be applied to elementwise problems.")
39
+
40
+ super().__init__(problem, copy)
41
+ self.backend = backend
42
+
43
+ # Set the elementwise_func to the class itself - it will handle the signature
44
+ self.elementwise_func = self._create_elementwise_func
45
+
46
+ def _create_elementwise_func(self, problem, args, kwargs):
47
+ """Create an elementwise function that matches the expected signature"""
48
+ return ElementwiseEvaluationFunctionWithGradient(self.__object__, self.backend, args, kwargs)
49
+
50
+
51
+ class AutomaticDifferentiation(Meta, Problem):
52
+
53
+ def __init__(self, object, backend='autograd', **kwargs):
54
+ super().__init__(object, **kwargs)
55
+ self.backend = backend
56
+
57
+ def do(self, x, return_values_of, *args, **kwargs):
58
+
59
+ vals_not_grad = [v for v in return_values_of if not v.startswith("d")]
60
+
61
+ class F:
62
+
63
+ def __init__(self, object):
64
+ self.__object__ = object
65
+
66
+ def __call__(self, xp):
67
+ return self.__object__.do(xp, vals_not_grad, *args, **kwargs)
68
+
69
+ f = F(self.__object__)
70
+
71
+ activate(self.backend)
72
+ if self.backend == "jax":
73
+ from pymoo.gradient.grad_jax import jax_vectorized_value_and_grad
74
+ out, grad = jax_vectorized_value_and_grad(f, x)
75
+ elif self.backend == "autograd":
76
+ from pymoo.gradient.grad_autograd import autograd_vectorized_value_and_grad
77
+ out, grad = autograd_vectorized_value_and_grad(f, x)
78
+ else:
79
+ raise Exception("Unknown backend %s" % self.backend)
80
+ deactivate()
81
+
82
+ for k, v in grad.items():
83
+ out["d" + k] = v
84
+
85
+ return out
@@ -0,0 +1,105 @@
1
+ import warnings
2
+ import numpy as np
3
+
4
+ try:
5
+ import autograd.numpy as anp
6
+ from autograd.core import VJPNode, backward_pass
7
+ from autograd.tracer import new_box, isbox
8
+ except:
9
+ print("autograd only supports numpy < 2.0.0 versions.")
10
+
11
+
12
+ def value_and_grad(*args, **kwargs):
13
+ from autograd import value_and_grad as vag
14
+ return vag(*args, **kwargs)
15
+
16
+
17
+ def log(*args, **kwargs):
18
+ return anp.log(*args, **kwargs)
19
+
20
+
21
+ def sqrt(*args, **kwargs):
22
+ return anp.sqrt(*args, **kwargs)
23
+
24
+
25
+ def row_stack(*args, **kwargs):
26
+ return anp.row_stack(*args, **kwargs)
27
+
28
+
29
+ def triu_indices(*args, **kwargs):
30
+ return anp.triu_indices(*args, **kwargs)
31
+
32
+
33
+ def run_and_trace(f, x):
34
+ start_node = VJPNode.new_root()
35
+
36
+ start_box = new_box(x, 0, start_node)
37
+ out = f(start_box)
38
+
39
+ return out, start_box
40
+
41
+
42
+ def autograd_elementwise_value_and_grad(f, x):
43
+ out, pullback = run_and_trace(f, x)
44
+
45
+ jac = dict()
46
+ for name in out:
47
+ val = out[name]
48
+
49
+ if val is not None:
50
+
51
+ if len(val.shape) == 0:
52
+ val = anp.array([val])
53
+
54
+ with warnings.catch_warnings():
55
+ warnings.simplefilter("ignore")
56
+
57
+ # the backward pass is done for each objective function once
58
+ grad = []
59
+ for j in range(len(val)):
60
+ b = np.zeros(val.shape)
61
+ b[j] = 1
62
+
63
+ n = new_box(b, 0, VJPNode.new_root())
64
+ _grad = backward_pass(n, val._node)
65
+ grad.append(_grad)
66
+
67
+ out[name] = np.array(val._value)
68
+ jac[name] = anp.stack(grad, axis=0)._value
69
+
70
+ return out, jac
71
+
72
+
73
+ def autograd_vectorized_value_and_grad(f, x):
74
+ end, start = run_and_trace(f, x)
75
+
76
+ out, jac = dict(), dict()
77
+
78
+ end = {k: v for k, v in end.items() if v is not None}
79
+
80
+ for name, val in end.items():
81
+
82
+ v = val
83
+ if hasattr(v, "_value"):
84
+ v = np.array(v._value)
85
+ out[name] = v
86
+
87
+ # if the end_box is not a box - autograd can not track back
88
+ if not isbox(val):
89
+ n, m = val.shape
90
+ jac[name] = np.zeros((n, m, x.shape[1]))
91
+
92
+ else:
93
+
94
+ # the backward pass is done for each objective function once
95
+ grad = []
96
+ for j in range(val.shape[1]):
97
+ b = anp.zeros(val.shape)
98
+ b[:, j] = 1
99
+ n = new_box(b, 0, VJPNode.new_root())
100
+ _grad = backward_pass(n, val._node)
101
+ grad.append(_grad)
102
+
103
+ jac[name] = anp.stack(grad, axis=1)._value
104
+
105
+ return out, jac
@@ -0,0 +1,35 @@
1
+ import numpy as np
2
+
3
+ from pymoo.core.meta import Meta
4
+ from pymoo.core.problem import Problem
5
+
6
+
7
+ def calc_complex_gradient(problem, return_values_of, x, eps, *args, **kwargs):
8
+ xp = x + np.eye(len(x)) * complex(0, eps)
9
+ out = problem.do(xp, return_values_of, *args, **kwargs)
10
+
11
+ grad = {}
12
+ for name, value in out.items():
13
+ grad[name] = np.imag(value / eps).T
14
+
15
+ return grad
16
+
17
+
18
+ class ComplexNumberGradient(Meta, Problem):
19
+
20
+ def __init__(self, problem, eps=1e-8, **kwargs):
21
+ super().__init__(problem, **kwargs)
22
+ self.eps = eps
23
+
24
+ def do(self, X, return_values_of, *args, **kwargs):
25
+ out = self.__object__.do(X, return_values_of, *args, **kwargs)
26
+
27
+ vals_not_grad = [v for v in return_values_of if not v.startswith("d")]
28
+
29
+ for i, x in enumerate(X):
30
+ grad = calc_complex_gradient(self.__object__, vals_not_grad, x, self.eps, *args, **kwargs)
31
+
32
+ for name, value in grad.items():
33
+ out['d' + name][i] = value
34
+
35
+ return out
@@ -0,0 +1,51 @@
1
+ from functools import partial
2
+
3
+ import jax
4
+ jax.config.update("jax_enable_x64", True)
5
+ from jax import vjp
6
+ from jax import vmap
7
+ from jax._src.api import _jacrev_unravel, _std_basis
8
+ from jax.tree_util import (tree_map)
9
+
10
+
11
+ import pymoo.gradient.toolbox as anp
12
+ import numpy as np
13
+
14
+
15
+ def jax_elementwise_value_and_grad(f, x):
16
+ out, pullback = vjp(f, x)
17
+ u = _std_basis(out)
18
+ jac, = vmap(pullback, in_axes=0)(u)
19
+
20
+ grad = tree_map(partial(_jacrev_unravel, out), x, jac)
21
+
22
+ return out, grad
23
+
24
+
25
+ def jax_vectorized_value_and_grad(f, x):
26
+ out, pullback = vjp(f, x)
27
+
28
+ ncols = sum([v.shape[1] for v in out.values()])
29
+
30
+ u = dict()
31
+ cols = dict()
32
+ cnt = 0
33
+ for k, v in out.items():
34
+ if k not in cols:
35
+ cols[k] = []
36
+
37
+ n, m = v.shape
38
+ a = np.zeros((ncols, n, m))
39
+ for i in range(m):
40
+ cols[k].append(cnt)
41
+ a[cnt, :, i] = 1.0
42
+ cnt += 1
43
+
44
+ u[k] = anp.array(a)
45
+
46
+ jac, = vmap(pullback, in_axes=0)(u)
47
+ jac = np.array(jac)
48
+
49
+ grad = {k: np.swapaxes(jac[I], 0, 1) for k, I in cols.items()}
50
+
51
+ return out, grad
@@ -0,0 +1,22 @@
1
+ """
2
+ Numpy gradient toolbox - acts as a fallback when no automatic differentiation library is used.
3
+ This module provides the same interface as autograd/jax but raises appropriate errors.
4
+ """
5
+ import numpy as np
6
+
7
+ def value_and_grad(*args, **kwargs):
8
+ raise NotImplementedError("Numpy gradient toolbox does not support automatic differentiation. "
9
+ "Please use pymoo.gradient.activate('autograd.numpy') or "
10
+ "pymoo.gradient.activate('jax.numpy') for automatic differentiation.")
11
+
12
+ def log(*args, **kwargs):
13
+ return np.log(*args, **kwargs)
14
+
15
+ def sqrt(*args, **kwargs):
16
+ return np.sqrt(*args, **kwargs)
17
+
18
+ def row_stack(*args, **kwargs):
19
+ return np.vstack(*args, **kwargs)
20
+
21
+ def triu_indices(*args, **kwargs):
22
+ return np.triu_indices(*args, **kwargs)
@@ -0,0 +1,19 @@
1
+ import sys
2
+ import importlib
3
+
4
+ class LazyBackend:
5
+ """Lazy backend that always uses the current active backend"""
6
+
7
+ def __getattr__(self, name):
8
+ from pymoo.gradient import active_backend, BACKENDS
9
+ backend_module = importlib.import_module(BACKENDS[active_backend])
10
+ return getattr(backend_module, name)
11
+
12
+ def __dir__(self):
13
+ """Support for tab completion and introspection"""
14
+ from pymoo.gradient import active_backend, BACKENDS
15
+ backend_module = importlib.import_module(BACKENDS[active_backend])
16
+ return dir(backend_module)
17
+
18
+ # Replace this module with the lazy backend
19
+ sys.modules[__name__] = LazyBackend()
File without changes
@@ -0,0 +1,55 @@
1
+ import numpy as np
2
+
3
+ from pymoo.core.indicator import Indicator
4
+ from pymoo.util.misc import vectorized_cdist, at_least_2d_array
5
+
6
+
7
+ def euclidean_distance(a, b, norm=None):
8
+ return np.sqrt((((a - b) / norm) ** 2).sum(axis=1))
9
+
10
+
11
+ def modified_distance(z, a, norm=None):
12
+ d = a - z
13
+ d[d < 0] = 0
14
+ d = d / norm
15
+ return np.sqrt((d ** 2).sum(axis=1))
16
+
17
+
18
+ def derive_ideal_and_nadir_from_pf(pf, ideal=None, nadir=None):
19
+
20
+ # try to derive ideal and nadir if not already set and pf provided
21
+ if pf is not None:
22
+ if ideal is None:
23
+ ideal = np.min(pf, axis=0)
24
+ if nadir is None:
25
+ nadir = np.max(pf, axis=0)
26
+
27
+ return ideal, nadir
28
+
29
+
30
+ class DistanceIndicator(Indicator):
31
+
32
+ def __init__(self, pf, dist_func, axis, zero_to_one=False, ideal=None, nadir=None, norm_by_dist=False, **kwargs):
33
+
34
+ # the pareto front if necessary to calculate the indicator
35
+ pf = at_least_2d_array(pf, extend_as="row")
36
+ ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir)
37
+
38
+ super().__init__(zero_to_one=zero_to_one, ideal=ideal, nadir=nadir, **kwargs)
39
+ self.dist_func = dist_func
40
+ self.axis = axis
41
+ self.norm_by_dist = norm_by_dist
42
+ self.pf = self.normalization.forward(pf)
43
+
44
+ def _do(self, F):
45
+
46
+ # a factor to normalize the distances by (1.0 disables that by default)
47
+ norm = 1.0
48
+
49
+ # if zero_to_one is disabled this can be used to normalize the distance calculation itself
50
+ if self.norm_by_dist:
51
+ assert self.ideal is not None and self.nadir is not None, "If norm_by_dist is enabled ideal and nadir must be set!"
52
+ norm = self.nadir - self.ideal
53
+
54
+ D = vectorized_cdist(self.pf, F, func_dist=self.dist_func, norm=norm)
55
+ return np.mean(np.min(D, axis=self.axis))
pymoo/indicators/gd.py ADDED
@@ -0,0 +1,7 @@
1
+ from pymoo.indicators.distance_indicator import DistanceIndicator, euclidean_distance
2
+
3
+
4
+ class GD(DistanceIndicator):
5
+
6
+ def __init__(self, pf, **kwargs):
7
+ super().__init__(pf, euclidean_distance, 0, **kwargs)
@@ -0,0 +1,7 @@
1
+ from pymoo.indicators.distance_indicator import DistanceIndicator, modified_distance
2
+
3
+
4
+ class GDPlus(DistanceIndicator):
5
+
6
+ def __init__(self, pf, **kwargs):
7
+ super().__init__(pf, modified_distance, 0, **kwargs)
@@ -0,0 +1,59 @@
1
+ import numpy as np
2
+
3
+ from pymoo.core.indicator import Indicator
4
+ from pymoo.functions import FunctionLoader, load_function
5
+ from pymoo.indicators.distance_indicator import derive_ideal_and_nadir_from_pf
6
+ from pymoo.util.misc import at_least_2d_array
7
+ from pymoo.util.nds.non_dominated_sorting import NonDominatedSorting
8
+ from moocore import hypervolume as _hypervolume
9
+
10
+
11
+ class Hypervolume(Indicator):
12
+
13
+ def __init__(self, ref_point=None, pf=None, nds=True, norm_ref_point=True, ideal=None, nadir=None, **kwargs):
14
+
15
+ pf = at_least_2d_array(pf, extend_as="row")
16
+ ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir)
17
+
18
+ super().__init__(ideal=ideal, nadir=nadir, **kwargs)
19
+ # self.normalization = ZeroToOneNormalization(ideal, nadir)
20
+
21
+ # whether the input should be checked for domination or not (deprecated)
22
+ self.nds = nds
23
+
24
+ # the reference point that shall be used - either derived from pf or provided
25
+ ref_point = ref_point
26
+ if ref_point is None:
27
+ if pf is not None:
28
+ ref_point = pf.max(axis=0)
29
+
30
+ # we also have to normalize the reference point to have the same scales
31
+ if norm_ref_point:
32
+ ref_point = self.normalization.forward(ref_point)
33
+
34
+ self.ref_point = ref_point
35
+ assert self.ref_point is not None, "For Hypervolume a reference point needs to be provided!"
36
+
37
+ def _do(self, F):
38
+ # calculate the hypervolume using moocore
39
+ val = _hypervolume(F, ref = self.ref_point)
40
+ return val
41
+
42
+
43
+ class HV(Hypervolume):
44
+ pass
45
+
46
+
47
+ def hvc_looped(ref_point, F, func):
48
+ hv = func(ref_point, F)
49
+
50
+ hvc = []
51
+
52
+ for k in range(len(F)):
53
+ v = np.full(len(F), True)
54
+ v[k] = False
55
+ _hv = func(ref_point, F[v])
56
+ hvc.append(hv - _hv)
57
+
58
+ hvc = np.array(hvc)
59
+ return hvc
@@ -0,0 +1,105 @@
1
+ import numpy as np
2
+
3
+ from pymoo.indicators.hv.exact import DynamicHypervolume
4
+ from pymoo.util import default_random_state
5
+
6
+
7
+ # from moocore import hv_approx, hv_contributions
8
+
9
+
10
+ def alpha(N, k):
11
+ alpha = np.zeros(N+1)
12
+
13
+ for i in range(1, N + 1):
14
+ alpha[i] = np.prod([(k - j) / (N - j) for j in range(1, i)]) / i
15
+
16
+ return alpha
17
+
18
+
19
+ def hv_monte_carlo(dom, V, n_dom=None):
20
+ N, n_samples = dom.shape
21
+ if n_dom is None:
22
+ n_dom = dom.sum(axis=0)
23
+
24
+ a = alpha(N, N)
25
+ hv = sum([a[n_dom[dom[i]]].sum() for i in range(N)]) / n_samples * V
26
+ return hv
27
+
28
+
29
+ def hvc_monte_carlo(dom, V, n_dom=None, k=1):
30
+ N, n_samples = dom.shape
31
+ if n_dom is None:
32
+ n_dom = dom.sum(axis=0)
33
+
34
+ a = alpha(N, k)
35
+ hvc = np.array([(a[n_dom[dom[i]]].sum() / n_samples * V).sum() for i in range(N)])
36
+ return hvc
37
+
38
+
39
+ class ApproximateHypervolume(DynamicHypervolume):
40
+
41
+ @default_random_state(seed=1)
42
+ def __init__(self, ref_point, n_samples=10000, n_exclusive=1, random_state=None, **kwargs) -> None:
43
+ self.n_samples = n_samples
44
+ self.n_exclusive = n_exclusive
45
+
46
+ self.V = None
47
+ self.dom = None
48
+
49
+ self.random_state = random_state
50
+
51
+ super().__init__(ref_point, **kwargs)
52
+
53
+ def _calc(self, ref_point, F):
54
+ (N, M) = F.shape
55
+
56
+ ideal = F.min(axis=0)
57
+ V = np.prod(ref_point - ideal)
58
+
59
+ S = self.random_state.uniform(low=ideal, high=ref_point, size=(self.n_samples, M))
60
+
61
+ dom = np.array([np.all(F[i] <= S, axis=1) for i in range(N)])
62
+
63
+ n_dom = dom.sum(axis=0)
64
+ hv = hv_monte_carlo(dom, V, n_dom=n_dom)
65
+ hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
66
+
67
+ self.V = V
68
+ self.dom = dom
69
+
70
+ return hv, hvc
71
+
72
+ # MOOCORE VERSION (commented out for comparison)
73
+ # if len(F) == 0:
74
+ # return 0.0, np.zeros(0)
75
+ #
76
+ # # Use moocore for approximate hypervolume calculation
77
+ # hv = hv_approx(F, ref=ref_point, nsamples=self.n_samples, seed=self.random_state.randint(0, 2**32-1))
78
+ #
79
+ # # Use moocore for exact hypervolume contributions (no approximate version available)
80
+ # hvc = hv_contributions(F, ref=ref_point)
81
+ #
82
+ # return hv, hvc
83
+
84
+ def delete(self, k):
85
+ self.F = np.delete(self.F, k, axis=0)
86
+ self.dom = np.delete(self.dom, k, axis=0)
87
+
88
+ self.hv -= self.hvc[k]
89
+
90
+ V, dom = self.V, self.dom
91
+ n_dom = dom.sum(axis=0)
92
+ self.hvc = hvc_monte_carlo(dom, V, n_dom=n_dom, k=self.n_exclusive)
93
+
94
+ # MOOCORE VERSION (commented out for comparison)
95
+ # # Handle empty array case
96
+ # if len(self.F) == 0:
97
+ # self.hv = 0.0
98
+ # self.hvc = np.zeros(0)
99
+ # else:
100
+ # # Use moocore for updated hypervolume and contributions
101
+ # self.hv = hv_approx(self.F, ref=self.ref_point, nsamples=self.n_samples, seed=self.random_state.randint(0, 2**32-1))
102
+ # self.hvc = hv_contributions(self.F, ref=self.ref_point)
103
+ #
104
+ # return self
105
+
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+ from moocore import hypervolume as hv
3
+ from moocore import hv_contributions as hvc
4
+
5
+
6
+ def hv_exact(ref_point, F):
7
+ return hv(F, ref=ref_point)
8
+
9
+
10
+ def hvc_exact(ref_point, F):
11
+ return hvc(F, ref=ref_point)
12
+
13
+
14
+ class DynamicHypervolume:
15
+
16
+ def __init__(self, ref_point, F=None, func_hv=None, func_hvc=None) -> None:
17
+ super().__init__()
18
+ self.ref_point = ref_point
19
+
20
+ self.func_hv = func_hv
21
+ self.func_hvc = func_hvc
22
+
23
+ self.n_dim = len(ref_point)
24
+ self.F = np.zeros((0, self.n_dim))
25
+
26
+ self.hv = 0.0
27
+ self.hvc = np.zeros(0)
28
+
29
+ if F is not None:
30
+ self.add(F)
31
+
32
+ def add(self, F):
33
+ assert len(F.shape) == 2, "The points to add must be a two-dimensional array."
34
+ assert F.shape[1] == self.n_dim, "The dimensions of the ref_point and points to add must be equal"
35
+ self.F = np.vstack([self.F, F])
36
+ self.hv, self.hvc = self.calc()
37
+ return self
38
+
39
+ def delete(self, k):
40
+ assert k < len(self.F)
41
+ self.F = np.delete(self.F, k, axis=0)
42
+ self.hv, self.hvc = self.calc()
43
+ return self
44
+
45
+ def calc(self):
46
+ return self._calc(self.ref_point, self.F)
47
+
48
+ # if len(self.F) == 0:
49
+ # return 0.0, np.zeros(0)
50
+ # else:
51
+ # return self._calc(self.ref_point, self.F)
52
+
53
+ def _calc(self, ref_point, F):
54
+ hv = None
55
+ if self.func_hv is not None:
56
+ hv = self.func_hv(ref_point, F)
57
+
58
+ hvc = None
59
+ if self.func_hvc is not None:
60
+ hvc = self.func_hvc(ref_point, F)
61
+
62
+ return hv, hvc
63
+
64
+
65
+ class ExactHypervolume(DynamicHypervolume):
66
+
67
+ def __init__(self, ref_point, func_hv=hv_exact, func_hvc=hvc_exact, **kwargs) -> None:
68
+ super().__init__(ref_point, func_hv=func_hv, func_hvc=func_hvc, **kwargs)