pymoo 0.6.1.5.dev0__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymoo might be problematic. Click here for more details.
- pymoo/__init__.py +3 -0
- pymoo/algorithms/__init__.py +0 -0
- pymoo/algorithms/base/__init__.py +0 -0
- pymoo/algorithms/base/bracket.py +38 -0
- pymoo/algorithms/base/genetic.py +109 -0
- pymoo/algorithms/base/line.py +62 -0
- pymoo/algorithms/base/local.py +39 -0
- pymoo/algorithms/base/meta.py +79 -0
- pymoo/algorithms/hyperparameters.py +89 -0
- pymoo/algorithms/moo/__init__.py +0 -0
- pymoo/algorithms/moo/age.py +310 -0
- pymoo/algorithms/moo/age2.py +194 -0
- pymoo/algorithms/moo/ctaea.py +298 -0
- pymoo/algorithms/moo/dnsga2.py +76 -0
- pymoo/algorithms/moo/kgb.py +446 -0
- pymoo/algorithms/moo/moead.py +183 -0
- pymoo/algorithms/moo/nsga2.py +113 -0
- pymoo/algorithms/moo/nsga3.py +358 -0
- pymoo/algorithms/moo/pinsga2.py +370 -0
- pymoo/algorithms/moo/rnsga2.py +188 -0
- pymoo/algorithms/moo/rnsga3.py +246 -0
- pymoo/algorithms/moo/rvea.py +214 -0
- pymoo/algorithms/moo/sms.py +195 -0
- pymoo/algorithms/moo/spea2.py +190 -0
- pymoo/algorithms/moo/unsga3.py +47 -0
- pymoo/algorithms/soo/__init__.py +0 -0
- pymoo/algorithms/soo/convex/__init__.py +0 -0
- pymoo/algorithms/soo/nonconvex/__init__.py +0 -0
- pymoo/algorithms/soo/nonconvex/brkga.py +161 -0
- pymoo/algorithms/soo/nonconvex/cmaes.py +554 -0
- pymoo/algorithms/soo/nonconvex/de.py +279 -0
- pymoo/algorithms/soo/nonconvex/direct.py +149 -0
- pymoo/algorithms/soo/nonconvex/es.py +203 -0
- pymoo/algorithms/soo/nonconvex/g3pcx.py +94 -0
- pymoo/algorithms/soo/nonconvex/ga.py +93 -0
- pymoo/algorithms/soo/nonconvex/ga_niching.py +223 -0
- pymoo/algorithms/soo/nonconvex/isres.py +74 -0
- pymoo/algorithms/soo/nonconvex/nelder.py +251 -0
- pymoo/algorithms/soo/nonconvex/optuna.py +80 -0
- pymoo/algorithms/soo/nonconvex/pattern.py +183 -0
- pymoo/algorithms/soo/nonconvex/pso.py +399 -0
- pymoo/algorithms/soo/nonconvex/pso_ep.py +297 -0
- pymoo/algorithms/soo/nonconvex/random_search.py +25 -0
- pymoo/algorithms/soo/nonconvex/sres.py +56 -0
- pymoo/algorithms/soo/univariate/__init__.py +0 -0
- pymoo/algorithms/soo/univariate/backtracking.py +59 -0
- pymoo/algorithms/soo/univariate/exp.py +46 -0
- pymoo/algorithms/soo/univariate/golden.py +65 -0
- pymoo/algorithms/soo/univariate/quadr_interp.py +81 -0
- pymoo/algorithms/soo/univariate/wolfe.py +163 -0
- pymoo/config.py +33 -0
- pymoo/constraints/__init__.py +3 -0
- pymoo/constraints/adaptive.py +62 -0
- pymoo/constraints/as_obj.py +56 -0
- pymoo/constraints/as_penalty.py +41 -0
- pymoo/constraints/eps.py +26 -0
- pymoo/constraints/from_bounds.py +36 -0
- pymoo/core/__init__.py +0 -0
- pymoo/core/algorithm.py +394 -0
- pymoo/core/callback.py +38 -0
- pymoo/core/crossover.py +77 -0
- pymoo/core/decision_making.py +102 -0
- pymoo/core/decomposition.py +76 -0
- pymoo/core/duplicate.py +163 -0
- pymoo/core/evaluator.py +116 -0
- pymoo/core/indicator.py +34 -0
- pymoo/core/individual.py +784 -0
- pymoo/core/infill.py +64 -0
- pymoo/core/initialization.py +42 -0
- pymoo/core/mating.py +39 -0
- pymoo/core/meta.py +21 -0
- pymoo/core/mixed.py +165 -0
- pymoo/core/mutation.py +44 -0
- pymoo/core/operator.py +40 -0
- pymoo/core/parameters.py +134 -0
- pymoo/core/plot.py +210 -0
- pymoo/core/population.py +180 -0
- pymoo/core/problem.py +460 -0
- pymoo/core/recorder.py +99 -0
- pymoo/core/repair.py +23 -0
- pymoo/core/replacement.py +96 -0
- pymoo/core/result.py +52 -0
- pymoo/core/sampling.py +43 -0
- pymoo/core/selection.py +61 -0
- pymoo/core/solution.py +10 -0
- pymoo/core/survival.py +103 -0
- pymoo/core/termination.py +70 -0
- pymoo/core/variable.py +399 -0
- pymoo/cython/__init__.py +0 -0
- pymoo/cython/calc_perpendicular_distance.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/calc_perpendicular_distance.pyx +67 -0
- pymoo/cython/decomposition.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/decomposition.pyx +165 -0
- pymoo/cython/hv.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/hv.pyx +18 -0
- pymoo/cython/info.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/info.pyx +5 -0
- pymoo/cython/mnn.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/mnn.pyx +273 -0
- pymoo/cython/non_dominated_sorting.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/non_dominated_sorting.pyx +645 -0
- pymoo/cython/pruning_cd.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/pruning_cd.pyx +197 -0
- pymoo/cython/stochastic_ranking.cpython-313-x86_64-linux-gnu.so +0 -0
- pymoo/cython/stochastic_ranking.pyx +49 -0
- pymoo/cython/utils.pxd +129 -0
- pymoo/cython/vendor/__init__.py +0 -0
- pymoo/cython/vendor/hypervolume.cpp +1621 -0
- pymoo/cython/vendor/hypervolume.h +63 -0
- pymoo/decomposition/__init__.py +0 -0
- pymoo/decomposition/aasf.py +24 -0
- pymoo/decomposition/asf.py +10 -0
- pymoo/decomposition/pbi.py +13 -0
- pymoo/decomposition/perp_dist.py +13 -0
- pymoo/decomposition/tchebicheff.py +11 -0
- pymoo/decomposition/util.py +13 -0
- pymoo/decomposition/weighted_sum.py +8 -0
- pymoo/docs.py +187 -0
- pymoo/experimental/__init__.py +0 -0
- pymoo/experimental/algorithms/__init__.py +0 -0
- pymoo/experimental/algorithms/gde3.py +57 -0
- pymoo/gradient/__init__.py +21 -0
- pymoo/gradient/automatic.py +57 -0
- pymoo/gradient/grad_autograd.py +105 -0
- pymoo/gradient/grad_complex.py +35 -0
- pymoo/gradient/grad_jax.py +51 -0
- pymoo/gradient/toolbox/__init__.py +6 -0
- pymoo/indicators/__init__.py +0 -0
- pymoo/indicators/distance_indicator.py +55 -0
- pymoo/indicators/gd.py +7 -0
- pymoo/indicators/gd_plus.py +7 -0
- pymoo/indicators/hv/__init__.py +63 -0
- pymoo/indicators/hv/exact.py +71 -0
- pymoo/indicators/hv/exact_2d.py +102 -0
- pymoo/indicators/hv/monte_carlo.py +74 -0
- pymoo/indicators/igd.py +7 -0
- pymoo/indicators/igd_plus.py +7 -0
- pymoo/indicators/kktpm.py +151 -0
- pymoo/indicators/migd.py +55 -0
- pymoo/indicators/rmetric.py +203 -0
- pymoo/indicators/spacing.py +52 -0
- pymoo/mcdm/__init__.py +0 -0
- pymoo/mcdm/compromise_programming.py +19 -0
- pymoo/mcdm/high_tradeoff.py +40 -0
- pymoo/mcdm/pseudo_weights.py +32 -0
- pymoo/operators/__init__.py +0 -0
- pymoo/operators/control.py +187 -0
- pymoo/operators/crossover/__init__.py +0 -0
- pymoo/operators/crossover/binx.py +45 -0
- pymoo/operators/crossover/dex.py +122 -0
- pymoo/operators/crossover/erx.py +162 -0
- pymoo/operators/crossover/expx.py +51 -0
- pymoo/operators/crossover/hux.py +37 -0
- pymoo/operators/crossover/nox.py +13 -0
- pymoo/operators/crossover/ox.py +84 -0
- pymoo/operators/crossover/pcx.py +82 -0
- pymoo/operators/crossover/pntx.py +49 -0
- pymoo/operators/crossover/sbx.py +125 -0
- pymoo/operators/crossover/spx.py +5 -0
- pymoo/operators/crossover/ux.py +20 -0
- pymoo/operators/mutation/__init__.py +0 -0
- pymoo/operators/mutation/bitflip.py +17 -0
- pymoo/operators/mutation/gauss.py +58 -0
- pymoo/operators/mutation/inversion.py +42 -0
- pymoo/operators/mutation/nom.py +7 -0
- pymoo/operators/mutation/pm.py +94 -0
- pymoo/operators/mutation/rm.py +23 -0
- pymoo/operators/repair/__init__.py +0 -0
- pymoo/operators/repair/bounce_back.py +32 -0
- pymoo/operators/repair/bounds_repair.py +95 -0
- pymoo/operators/repair/inverse_penalty.py +89 -0
- pymoo/operators/repair/rounding.py +18 -0
- pymoo/operators/repair/to_bound.py +31 -0
- pymoo/operators/repair/vtype.py +11 -0
- pymoo/operators/sampling/__init__.py +0 -0
- pymoo/operators/sampling/lhs.py +73 -0
- pymoo/operators/sampling/rnd.py +50 -0
- pymoo/operators/selection/__init__.py +0 -0
- pymoo/operators/selection/rnd.py +72 -0
- pymoo/operators/selection/tournament.py +76 -0
- pymoo/operators/survival/__init__.py +0 -0
- pymoo/operators/survival/rank_and_crowding/__init__.py +1 -0
- pymoo/operators/survival/rank_and_crowding/classes.py +209 -0
- pymoo/operators/survival/rank_and_crowding/metrics.py +208 -0
- pymoo/optimize.py +72 -0
- pymoo/problems/__init__.py +157 -0
- pymoo/problems/dyn.py +47 -0
- pymoo/problems/dynamic/__init__.py +0 -0
- pymoo/problems/dynamic/cec2015.py +108 -0
- pymoo/problems/dynamic/df.py +452 -0
- pymoo/problems/dynamic/misc.py +167 -0
- pymoo/problems/functional.py +48 -0
- pymoo/problems/many/__init__.py +5 -0
- pymoo/problems/many/cdtlz.py +159 -0
- pymoo/problems/many/dcdtlz.py +88 -0
- pymoo/problems/many/dtlz.py +264 -0
- pymoo/problems/many/wfg.py +550 -0
- pymoo/problems/multi/__init__.py +14 -0
- pymoo/problems/multi/bnh.py +34 -0
- pymoo/problems/multi/carside.py +48 -0
- pymoo/problems/multi/clutch.py +104 -0
- pymoo/problems/multi/csi.py +55 -0
- pymoo/problems/multi/ctp.py +198 -0
- pymoo/problems/multi/dascmop.py +213 -0
- pymoo/problems/multi/kursawe.py +25 -0
- pymoo/problems/multi/modact.py +68 -0
- pymoo/problems/multi/mw.py +400 -0
- pymoo/problems/multi/omnitest.py +48 -0
- pymoo/problems/multi/osy.py +32 -0
- pymoo/problems/multi/srn.py +28 -0
- pymoo/problems/multi/sympart.py +94 -0
- pymoo/problems/multi/tnk.py +24 -0
- pymoo/problems/multi/truss2d.py +83 -0
- pymoo/problems/multi/welded_beam.py +41 -0
- pymoo/problems/multi/wrm.py +36 -0
- pymoo/problems/multi/zdt.py +151 -0
- pymoo/problems/multi_to_single.py +22 -0
- pymoo/problems/single/__init__.py +12 -0
- pymoo/problems/single/ackley.py +24 -0
- pymoo/problems/single/cantilevered_beam.py +34 -0
- pymoo/problems/single/flowshop_scheduling.py +112 -0
- pymoo/problems/single/g.py +874 -0
- pymoo/problems/single/griewank.py +18 -0
- pymoo/problems/single/himmelblau.py +15 -0
- pymoo/problems/single/knapsack.py +48 -0
- pymoo/problems/single/mopta08.py +26 -0
- pymoo/problems/single/multimodal.py +20 -0
- pymoo/problems/single/pressure_vessel.py +30 -0
- pymoo/problems/single/rastrigin.py +20 -0
- pymoo/problems/single/rosenbrock.py +22 -0
- pymoo/problems/single/schwefel.py +18 -0
- pymoo/problems/single/simple.py +13 -0
- pymoo/problems/single/sphere.py +19 -0
- pymoo/problems/single/traveling_salesman.py +79 -0
- pymoo/problems/single/zakharov.py +19 -0
- pymoo/problems/static.py +14 -0
- pymoo/problems/util.py +42 -0
- pymoo/problems/zero_to_one.py +27 -0
- pymoo/termination/__init__.py +23 -0
- pymoo/termination/collection.py +12 -0
- pymoo/termination/cv.py +48 -0
- pymoo/termination/default.py +45 -0
- pymoo/termination/delta.py +64 -0
- pymoo/termination/fmin.py +16 -0
- pymoo/termination/ftol.py +144 -0
- pymoo/termination/indicator.py +49 -0
- pymoo/termination/max_eval.py +14 -0
- pymoo/termination/max_gen.py +15 -0
- pymoo/termination/max_time.py +20 -0
- pymoo/termination/robust.py +34 -0
- pymoo/termination/xtol.py +33 -0
- pymoo/util/__init__.py +0 -0
- pymoo/util/archive.py +150 -0
- pymoo/util/cache.py +29 -0
- pymoo/util/clearing.py +82 -0
- pymoo/util/display/__init__.py +0 -0
- pymoo/util/display/column.py +52 -0
- pymoo/util/display/display.py +34 -0
- pymoo/util/display/multi.py +96 -0
- pymoo/util/display/output.py +53 -0
- pymoo/util/display/progress.py +54 -0
- pymoo/util/display/single.py +67 -0
- pymoo/util/dominator.py +67 -0
- pymoo/util/function_loader.py +129 -0
- pymoo/util/hv.py +23 -0
- pymoo/util/matlab_engine.py +39 -0
- pymoo/util/misc.py +460 -0
- pymoo/util/mnn.py +70 -0
- pymoo/util/nds/__init__.py +0 -0
- pymoo/util/nds/dominance_degree_non_dominated_sort.py +159 -0
- pymoo/util/nds/efficient_non_dominated_sort.py +152 -0
- pymoo/util/nds/fast_non_dominated_sort.py +70 -0
- pymoo/util/nds/naive_non_dominated_sort.py +36 -0
- pymoo/util/nds/non_dominated_sorting.py +67 -0
- pymoo/util/nds/tree_based_non_dominated_sort.py +133 -0
- pymoo/util/normalization.py +312 -0
- pymoo/util/optimum.py +42 -0
- pymoo/util/plotting.py +177 -0
- pymoo/util/pruning_cd.py +89 -0
- pymoo/util/randomized_argsort.py +60 -0
- pymoo/util/ref_dirs/__init__.py +24 -0
- pymoo/util/ref_dirs/construction.py +88 -0
- pymoo/util/ref_dirs/das_dennis.py +52 -0
- pymoo/util/ref_dirs/energy.py +319 -0
- pymoo/util/ref_dirs/energy_layer.py +119 -0
- pymoo/util/ref_dirs/genetic_algorithm.py +63 -0
- pymoo/util/ref_dirs/incremental.py +68 -0
- pymoo/util/ref_dirs/misc.py +128 -0
- pymoo/util/ref_dirs/optimizer.py +59 -0
- pymoo/util/ref_dirs/performance.py +162 -0
- pymoo/util/ref_dirs/reduction.py +85 -0
- pymoo/util/ref_dirs/sample_and_map.py +24 -0
- pymoo/util/reference_direction.py +260 -0
- pymoo/util/remote.py +55 -0
- pymoo/util/roulette.py +27 -0
- pymoo/util/running_metric.py +128 -0
- pymoo/util/sliding_window.py +25 -0
- pymoo/util/stochastic_ranking.py +32 -0
- pymoo/util/value_functions.py +719 -0
- pymoo/util/vectors.py +40 -0
- pymoo/util/vf_dominator.py +99 -0
- pymoo/vendor/__init__.py +0 -0
- pymoo/vendor/cec2018.py +398 -0
- pymoo/vendor/gta.py +617 -0
- pymoo/vendor/hv.py +267 -0
- pymoo/vendor/vendor_cmaes.py +412 -0
- pymoo/vendor/vendor_coco.py +81 -0
- pymoo/vendor/vendor_scipy.py +232 -0
- pymoo/version.py +1 -0
- pymoo/visualization/__init__.py +8 -0
- pymoo/visualization/fitness_landscape.py +127 -0
- pymoo/visualization/heatmap.py +123 -0
- pymoo/visualization/pcp.py +120 -0
- pymoo/visualization/petal.py +91 -0
- pymoo/visualization/radar.py +108 -0
- pymoo/visualization/radviz.py +68 -0
- pymoo/visualization/scatter.py +150 -0
- pymoo/visualization/star_coordinate.py +75 -0
- pymoo/visualization/util.py +123 -0
- pymoo/visualization/video/__init__.py +0 -0
- pymoo/visualization/video/callback_video.py +82 -0
- pymoo/visualization/video/one_var_one_obj.py +57 -0
- pymoo/visualization/video/two_var_one_obj.py +62 -0
- pymoo-0.6.1.5.dev0.dist-info/METADATA +187 -0
- pymoo-0.6.1.5.dev0.dist-info/RECORD +328 -0
- pymoo-0.6.1.5.dev0.dist-info/WHEEL +6 -0
- pymoo-0.6.1.5.dev0.dist-info/licenses/LICENSE +191 -0
- pymoo-0.6.1.5.dev0.dist-info/top_level.txt +1 -0
pymoo/core/problem.py
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import pymoo.gradient.toolbox as anp
|
|
6
|
+
from pymoo.util.cache import Cache
|
|
7
|
+
from pymoo.util.misc import at_least_2d_array
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import ray
|
|
11
|
+
except ImportError:
|
|
12
|
+
ray = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ElementwiseEvaluationFunction:
|
|
16
|
+
|
|
17
|
+
def __init__(self, problem, args, kwargs) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
self.problem = problem
|
|
20
|
+
self.args = args
|
|
21
|
+
self.kwargs = kwargs
|
|
22
|
+
|
|
23
|
+
def __call__(self, x):
|
|
24
|
+
out = dict()
|
|
25
|
+
self.problem._evaluate(x, out, *self.args, **self.kwargs)
|
|
26
|
+
return out
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class LoopedElementwiseEvaluation:
|
|
30
|
+
|
|
31
|
+
def __call__(self, f, X):
|
|
32
|
+
return [f(x) for x in X]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class StarmapParallelization:
|
|
36
|
+
|
|
37
|
+
def __init__(self, starmap) -> None:
|
|
38
|
+
super().__init__()
|
|
39
|
+
self.starmap = starmap
|
|
40
|
+
|
|
41
|
+
def __call__(self, f, X):
|
|
42
|
+
return list(self.starmap(f, [[x] for x in X]))
|
|
43
|
+
|
|
44
|
+
def __getstate__(self):
|
|
45
|
+
state = self.__dict__.copy()
|
|
46
|
+
state.pop("starmap", None)
|
|
47
|
+
return state
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DaskParallelization:
|
|
51
|
+
|
|
52
|
+
def __init__(self, client) -> None:
|
|
53
|
+
super().__init__()
|
|
54
|
+
self.client = client
|
|
55
|
+
|
|
56
|
+
def __call__(self, f, X):
|
|
57
|
+
jobs = [self.client.submit(f, x) for x in X]
|
|
58
|
+
return [job.result() for job in jobs]
|
|
59
|
+
|
|
60
|
+
def __getstate__(self):
|
|
61
|
+
state = self.__dict__.copy()
|
|
62
|
+
state.pop("client", None)
|
|
63
|
+
return state
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class JoblibParallelization:
|
|
67
|
+
|
|
68
|
+
def __init__(self, aJoblibParallel, aJoblibDelayed, *args, **kwargs) -> None:
|
|
69
|
+
super().__init__()
|
|
70
|
+
self.parallel = aJoblibParallel
|
|
71
|
+
self.delayed = aJoblibDelayed
|
|
72
|
+
|
|
73
|
+
def __call__(self, f, X):
|
|
74
|
+
return self.parallel(self.delayed(f)(x) for x in X)
|
|
75
|
+
|
|
76
|
+
def __getstate__(self):
|
|
77
|
+
state = self.__dict__.copy()
|
|
78
|
+
state.pop("parallel", None)
|
|
79
|
+
state.pop("delayed", None)
|
|
80
|
+
return state
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class RayParallelization:
|
|
84
|
+
"""Use Ray as backend to parallelize problem evaluation.
|
|
85
|
+
|
|
86
|
+
Ray is an open-source unified framework for scaling AI and Python applicaitons.
|
|
87
|
+
Read more here: https://docs.ray.io.
|
|
88
|
+
|
|
89
|
+
You will need to install Ray to use this.
|
|
90
|
+
"""
|
|
91
|
+
def __init__(self, job_resources: dict = {'num_cpus': 1}) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
job_resources: A resource in Ray is a key-value pair where the key denotes a
|
|
96
|
+
resource name and the value is a float quantity. Ray has native support for CPU,
|
|
97
|
+
GPU, and memory resource types; `'num_cpus'`, `'num_gpus'`, and `'memory'`.
|
|
98
|
+
Read more here:
|
|
99
|
+
https://docs.ray.io/en/latest/ray-core/scheduling/resources.html.
|
|
100
|
+
"""
|
|
101
|
+
assert ray is not None, (
|
|
102
|
+
"Ray must be installed! "
|
|
103
|
+
"You can install Ray with the command: "
|
|
104
|
+
'`pip install -U "ray[default]"`'
|
|
105
|
+
)
|
|
106
|
+
super().__init__()
|
|
107
|
+
self.job_resources = job_resources
|
|
108
|
+
|
|
109
|
+
def __call__(self, f, X):
|
|
110
|
+
runnable = ray.remote(f.__call__.__func__)
|
|
111
|
+
runnable = runnable.options(**self.job_resources)
|
|
112
|
+
futures = [runnable.remote(f, x) for x in X]
|
|
113
|
+
return ray.get(futures)
|
|
114
|
+
|
|
115
|
+
def __getstate__(self):
|
|
116
|
+
state = self.__dict__.copy()
|
|
117
|
+
return state
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Problem:
|
|
121
|
+
def __init__(self,
|
|
122
|
+
n_var=-1,
|
|
123
|
+
n_obj=1,
|
|
124
|
+
n_ieq_constr=0,
|
|
125
|
+
n_eq_constr=0,
|
|
126
|
+
xl=None,
|
|
127
|
+
xu=None,
|
|
128
|
+
vtype=None,
|
|
129
|
+
vars=None,
|
|
130
|
+
elementwise=False,
|
|
131
|
+
elementwise_func=ElementwiseEvaluationFunction,
|
|
132
|
+
elementwise_runner=LoopedElementwiseEvaluation(),
|
|
133
|
+
requires_kwargs=False,
|
|
134
|
+
replace_nan_values_by=None,
|
|
135
|
+
exclude_from_serialization=None,
|
|
136
|
+
callback=None,
|
|
137
|
+
strict=True,
|
|
138
|
+
**kwargs):
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
n_var : int
|
|
145
|
+
Number of Variables
|
|
146
|
+
|
|
147
|
+
n_obj : int
|
|
148
|
+
Number of Objectives
|
|
149
|
+
|
|
150
|
+
n_ieq_constr : int
|
|
151
|
+
Number of Inequality Constraints
|
|
152
|
+
|
|
153
|
+
n_eq_constr : int
|
|
154
|
+
Number of Equality Constraints
|
|
155
|
+
|
|
156
|
+
xl : np.array, float, int
|
|
157
|
+
Lower bounds for the variables. if integer all lower bounds are equal.
|
|
158
|
+
|
|
159
|
+
xu : np.array, float, int
|
|
160
|
+
Upper bounds for the variable. if integer all upper bounds are equal.
|
|
161
|
+
|
|
162
|
+
vtype : type
|
|
163
|
+
The variable type. So far, just used as a type hint.
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
# number of variable
|
|
168
|
+
self.n_var = n_var
|
|
169
|
+
|
|
170
|
+
# number of objectives
|
|
171
|
+
self.n_obj = n_obj
|
|
172
|
+
|
|
173
|
+
# number of inequality constraints
|
|
174
|
+
self.n_ieq_constr = n_ieq_constr if "n_constr" not in kwargs else max(n_ieq_constr, kwargs["n_constr"])
|
|
175
|
+
|
|
176
|
+
# number of equality constraints
|
|
177
|
+
self.n_eq_constr = n_eq_constr
|
|
178
|
+
|
|
179
|
+
# type of the variable to be evaluated
|
|
180
|
+
self.data = dict(**kwargs)
|
|
181
|
+
|
|
182
|
+
# the lower bounds, make sure it is a numpy array with the length of n_var
|
|
183
|
+
self.xl, self.xu = xl, xu
|
|
184
|
+
|
|
185
|
+
# a callback function to be called after every evaluation
|
|
186
|
+
self.callback = callback
|
|
187
|
+
|
|
188
|
+
# if the variables are provided in their explicit form
|
|
189
|
+
if vars is not None:
|
|
190
|
+
self.vars = vars
|
|
191
|
+
self.n_var = len(vars)
|
|
192
|
+
|
|
193
|
+
if self.xl is None:
|
|
194
|
+
self.xl = {name: var.lb if hasattr(var, "lb") else None for name, var in vars.items()}
|
|
195
|
+
if self.xu is None:
|
|
196
|
+
self.xu = {name: var.ub if hasattr(var, "ub") else None for name, var in vars.items()}
|
|
197
|
+
|
|
198
|
+
# the variable type (only as a type hint at this point)
|
|
199
|
+
self.vtype = vtype
|
|
200
|
+
|
|
201
|
+
# the functions used if elementwise is enabled
|
|
202
|
+
self.elementwise = elementwise
|
|
203
|
+
self.elementwise_func = elementwise_func
|
|
204
|
+
self.elementwise_runner = elementwise_runner
|
|
205
|
+
|
|
206
|
+
# whether evaluation requires kwargs (passing them can cause overhead in parallelization)
|
|
207
|
+
self.requires_kwargs = requires_kwargs
|
|
208
|
+
|
|
209
|
+
# whether the shapes are checked strictly
|
|
210
|
+
self.strict = strict
|
|
211
|
+
|
|
212
|
+
# if it is a problem with an actual number of variables - make sure xl and xu are numpy arrays
|
|
213
|
+
if n_var > 0:
|
|
214
|
+
|
|
215
|
+
if self.xl is not None:
|
|
216
|
+
if not isinstance(self.xl, np.ndarray):
|
|
217
|
+
self.xl = np.ones(n_var) * xl
|
|
218
|
+
self.xl = self.xl.astype(float)
|
|
219
|
+
|
|
220
|
+
if self.xu is not None:
|
|
221
|
+
if not isinstance(self.xu, np.ndarray):
|
|
222
|
+
self.xu = np.ones(n_var) * xu
|
|
223
|
+
self.xu = self.xu.astype(float)
|
|
224
|
+
|
|
225
|
+
# this defines if NaN values should be replaced or not
|
|
226
|
+
self.replace_nan_values_by = replace_nan_values_by
|
|
227
|
+
|
|
228
|
+
# attribute which are excluded from being serialized
|
|
229
|
+
self.exclude_from_serialization = exclude_from_serialization
|
|
230
|
+
|
|
231
|
+
def evaluate(self,
|
|
232
|
+
X,
|
|
233
|
+
*args,
|
|
234
|
+
return_values_of=None,
|
|
235
|
+
return_as_dictionary=False,
|
|
236
|
+
**kwargs):
|
|
237
|
+
|
|
238
|
+
# if the problem does not require any kwargs they are re-initialized
|
|
239
|
+
if not self.requires_kwargs:
|
|
240
|
+
kwargs = dict()
|
|
241
|
+
|
|
242
|
+
if return_values_of is None:
|
|
243
|
+
return_values_of = ["F"]
|
|
244
|
+
if self.n_ieq_constr > 0:
|
|
245
|
+
return_values_of.append("G")
|
|
246
|
+
if self.n_eq_constr > 0:
|
|
247
|
+
return_values_of.append("H")
|
|
248
|
+
|
|
249
|
+
# make sure the array is at least 2d. store if reshaping was necessary
|
|
250
|
+
if isinstance(X, np.ndarray) and X.dtype != object:
|
|
251
|
+
X, only_single_value = at_least_2d_array(X, extend_as="row", return_if_reshaped=True)
|
|
252
|
+
assert X.shape[1] == self.n_var, f'Input dimension {X.shape[1]} are not equal to n_var {self.n_var}!'
|
|
253
|
+
else:
|
|
254
|
+
only_single_value = not (isinstance(X, list) or isinstance(X, np.ndarray))
|
|
255
|
+
|
|
256
|
+
# this is where the actual evaluation takes place
|
|
257
|
+
_out = self.do(X, return_values_of, *args, **kwargs)
|
|
258
|
+
|
|
259
|
+
out = {}
|
|
260
|
+
for k, v in _out.items():
|
|
261
|
+
|
|
262
|
+
# copy it to a numpy array (it might be one of jax at this point)
|
|
263
|
+
v = np.array(v)
|
|
264
|
+
|
|
265
|
+
# in case the input had only one dimension, then remove always the first dimension from each output
|
|
266
|
+
if only_single_value:
|
|
267
|
+
v = v[0]
|
|
268
|
+
|
|
269
|
+
# if the NaN values should be replaced
|
|
270
|
+
if self.replace_nan_values_by is not None:
|
|
271
|
+
v[np.isnan(v)] = self.replace_nan_values_by
|
|
272
|
+
|
|
273
|
+
try:
|
|
274
|
+
out[k] = v.astype(np.float64)
|
|
275
|
+
except:
|
|
276
|
+
out[k] = v
|
|
277
|
+
|
|
278
|
+
if self.callback is not None:
|
|
279
|
+
self.callback(X, out)
|
|
280
|
+
|
|
281
|
+
# now depending on what should be returned prepare the output
|
|
282
|
+
if return_as_dictionary:
|
|
283
|
+
return out
|
|
284
|
+
|
|
285
|
+
if len(return_values_of) == 1:
|
|
286
|
+
return out[return_values_of[0]]
|
|
287
|
+
else:
|
|
288
|
+
return tuple([out[e] for e in return_values_of])
|
|
289
|
+
|
|
290
|
+
def do(self, X, return_values_of, *args, **kwargs):
|
|
291
|
+
|
|
292
|
+
# create an empty dictionary
|
|
293
|
+
out = {name: None for name in return_values_of}
|
|
294
|
+
|
|
295
|
+
# do the function evaluation
|
|
296
|
+
if self.elementwise:
|
|
297
|
+
self._evaluate_elementwise(X, out, *args, **kwargs)
|
|
298
|
+
else:
|
|
299
|
+
self._evaluate_vectorized(X, out, *args, **kwargs)
|
|
300
|
+
|
|
301
|
+
# finally format the output dictionary
|
|
302
|
+
out = self._format_dict(out, len(X), return_values_of)
|
|
303
|
+
|
|
304
|
+
return out
|
|
305
|
+
|
|
306
|
+
def _evaluate_vectorized(self, X, out, *args, **kwargs):
|
|
307
|
+
self._evaluate(X, out, *args, **kwargs)
|
|
308
|
+
|
|
309
|
+
def _evaluate_elementwise(self, X, out, *args, **kwargs):
|
|
310
|
+
|
|
311
|
+
# create the function that evaluates a single individual
|
|
312
|
+
f = self.elementwise_func(self, args, kwargs)
|
|
313
|
+
|
|
314
|
+
# execute the runner
|
|
315
|
+
elems = self.elementwise_runner(f, X)
|
|
316
|
+
|
|
317
|
+
# for each evaluation call
|
|
318
|
+
for elem in elems:
|
|
319
|
+
|
|
320
|
+
# for each key stored for this evaluation
|
|
321
|
+
for k, v in elem.items():
|
|
322
|
+
|
|
323
|
+
# if the element does not exist in out yet -> create it
|
|
324
|
+
if out.get(k, None) is None:
|
|
325
|
+
out[k] = []
|
|
326
|
+
|
|
327
|
+
out[k].append(v)
|
|
328
|
+
|
|
329
|
+
# convert to arrays (the none check is important because otherwise an empty array is initialized)
|
|
330
|
+
for k in out:
|
|
331
|
+
if out[k] is not None:
|
|
332
|
+
out[k] = anp.array(out[k])
|
|
333
|
+
|
|
334
|
+
def _format_dict(self, out, N, return_values_of):
|
|
335
|
+
|
|
336
|
+
# get the default output shape for the default values
|
|
337
|
+
shape = default_shape(self, N)
|
|
338
|
+
|
|
339
|
+
# finally the array to be returned
|
|
340
|
+
ret = {}
|
|
341
|
+
|
|
342
|
+
# for all values that have been set in the user implemented function
|
|
343
|
+
for name, v in out.items():
|
|
344
|
+
|
|
345
|
+
# only if they have truly been set
|
|
346
|
+
if v is not None:
|
|
347
|
+
|
|
348
|
+
# if there is a shape to be expected
|
|
349
|
+
if name in shape:
|
|
350
|
+
|
|
351
|
+
if isinstance(v, list):
|
|
352
|
+
v = anp.column_stack(v)
|
|
353
|
+
|
|
354
|
+
try:
|
|
355
|
+
v = v.reshape(shape[name])
|
|
356
|
+
except Exception as e:
|
|
357
|
+
raise Exception(
|
|
358
|
+
f"Problem Error: {name} can not be set, expected shape {shape[name]} but provided {v.shape}",
|
|
359
|
+
e)
|
|
360
|
+
|
|
361
|
+
ret[name] = v
|
|
362
|
+
|
|
363
|
+
# if some values that are necessary have not been set
|
|
364
|
+
for name in return_values_of:
|
|
365
|
+
if name not in ret:
|
|
366
|
+
s = shape.get(name, N)
|
|
367
|
+
ret[name] = np.full(s, np.inf)
|
|
368
|
+
|
|
369
|
+
return ret
|
|
370
|
+
|
|
371
|
+
@Cache
|
|
372
|
+
def nadir_point(self, *args, **kwargs):
|
|
373
|
+
pf = self.pareto_front(*args, **kwargs)
|
|
374
|
+
if pf is not None:
|
|
375
|
+
return np.max(pf, axis=0)
|
|
376
|
+
|
|
377
|
+
@Cache
|
|
378
|
+
def ideal_point(self, *args, **kwargs):
|
|
379
|
+
pf = self.pareto_front(*args, **kwargs)
|
|
380
|
+
if pf is not None:
|
|
381
|
+
return np.min(pf, axis=0)
|
|
382
|
+
|
|
383
|
+
@Cache
|
|
384
|
+
def pareto_front(self, *args, **kwargs):
|
|
385
|
+
pf = self._calc_pareto_front(*args, **kwargs)
|
|
386
|
+
pf = at_least_2d_array(pf, extend_as='r')
|
|
387
|
+
if pf is not None and pf.shape[1] == 2:
|
|
388
|
+
pf = pf[np.argsort(pf[:, 0])]
|
|
389
|
+
return pf
|
|
390
|
+
|
|
391
|
+
@Cache
|
|
392
|
+
def pareto_set(self, *args, **kwargs):
|
|
393
|
+
ps = self._calc_pareto_set(*args, **kwargs)
|
|
394
|
+
ps = at_least_2d_array(ps, extend_as='r')
|
|
395
|
+
return ps
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def n_constr(self):
|
|
399
|
+
return self.n_ieq_constr + self.n_eq_constr
|
|
400
|
+
|
|
401
|
+
@abstractmethod
|
|
402
|
+
def _evaluate(self, x, out, *args, **kwargs):
|
|
403
|
+
pass
|
|
404
|
+
|
|
405
|
+
def has_bounds(self):
|
|
406
|
+
return self.xl is not None and self.xu is not None
|
|
407
|
+
|
|
408
|
+
def has_constraints(self):
|
|
409
|
+
return self.n_constr > 0
|
|
410
|
+
|
|
411
|
+
def bounds(self):
|
|
412
|
+
return self.xl, self.xu
|
|
413
|
+
|
|
414
|
+
def name(self):
|
|
415
|
+
return self.__class__.__name__
|
|
416
|
+
|
|
417
|
+
def _calc_pareto_front(self, *args, **kwargs):
|
|
418
|
+
pass
|
|
419
|
+
|
|
420
|
+
def _calc_pareto_set(self, *args, **kwargs):
|
|
421
|
+
pass
|
|
422
|
+
|
|
423
|
+
def __str__(self):
|
|
424
|
+
s = "# name: %s\n" % self.name()
|
|
425
|
+
s += "# n_var: %s\n" % self.n_var
|
|
426
|
+
s += "# n_obj: %s\n" % self.n_obj
|
|
427
|
+
s += "# n_ieq_constr: %s\n" % self.n_ieq_constr
|
|
428
|
+
s += "# n_eq_constr: %s\n" % self.n_eq_constr
|
|
429
|
+
return s
|
|
430
|
+
|
|
431
|
+
def __getstate__(self):
|
|
432
|
+
if self.exclude_from_serialization is not None:
|
|
433
|
+
state = self.__dict__.copy()
|
|
434
|
+
|
|
435
|
+
# exclude objects which should not be stored
|
|
436
|
+
for key in self.exclude_from_serialization:
|
|
437
|
+
state[key] = None
|
|
438
|
+
|
|
439
|
+
return state
|
|
440
|
+
else:
|
|
441
|
+
return self.__dict__
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class ElementwiseProblem(Problem):
|
|
445
|
+
|
|
446
|
+
def __init__(self, elementwise=True, **kwargs):
|
|
447
|
+
super().__init__(elementwise=elementwise, **kwargs)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def default_shape(problem, n):
|
|
451
|
+
n_var = problem.n_var
|
|
452
|
+
DEFAULTS = dict(
|
|
453
|
+
F=(n, problem.n_obj),
|
|
454
|
+
G=(n, problem.n_ieq_constr),
|
|
455
|
+
H=(n, problem.n_eq_constr),
|
|
456
|
+
dF=(n, problem.n_obj, n_var),
|
|
457
|
+
dG=(n, problem.n_ieq_constr, n_var),
|
|
458
|
+
dH=(n, problem.n_eq_constr, n_var),
|
|
459
|
+
)
|
|
460
|
+
return DEFAULTS
|
pymoo/core/recorder.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pymoo.core.callback import Callback
|
|
6
|
+
from pymoo.indicators.igd import IGD
|
|
7
|
+
from pymoo.indicators.igd_plus import IGDPlus
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Recorder(Callback):
|
|
11
|
+
|
|
12
|
+
def __init__(self, nth_evals=None) -> None:
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.data = []
|
|
15
|
+
self.nth_evals = nth_evals
|
|
16
|
+
self.rec_n_evals = 0
|
|
17
|
+
|
|
18
|
+
def notify(self, algorithm, **kwargs):
|
|
19
|
+
|
|
20
|
+
if self.nth_evals is None:
|
|
21
|
+
self.data.append(self.save(algorithm))
|
|
22
|
+
|
|
23
|
+
else:
|
|
24
|
+
n_evals = algorithm.evaluator.n_eval
|
|
25
|
+
|
|
26
|
+
if n_evals >= self.rec_n_evals:
|
|
27
|
+
self.data.append(self.save(algorithm))
|
|
28
|
+
self.rec_n_evals = (1 + (n_evals // self.nth_evals)) * self.nth_evals
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def save(self, algorithm):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def get(self, *args, as_array=True):
|
|
35
|
+
ret = []
|
|
36
|
+
for arg in args:
|
|
37
|
+
e = [entry.get(arg) for entry in self.data]
|
|
38
|
+
if as_array:
|
|
39
|
+
e = np.array(e)
|
|
40
|
+
ret.append(e)
|
|
41
|
+
return tuple(ret)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DefaultSingleObjectiveRecorder(Recorder):
|
|
45
|
+
|
|
46
|
+
def save(self, algorithm):
|
|
47
|
+
n_evals = algorithm.evaluator.n_eval
|
|
48
|
+
|
|
49
|
+
opt = algorithm.opt
|
|
50
|
+
_feas, _cv, _f = opt.get("feas", "cv", "f")
|
|
51
|
+
|
|
52
|
+
cv = _cv.min()
|
|
53
|
+
|
|
54
|
+
if np.any(_feas):
|
|
55
|
+
f = _f[_feas].min()
|
|
56
|
+
else:
|
|
57
|
+
f = np.inf
|
|
58
|
+
|
|
59
|
+
fgap = np.inf
|
|
60
|
+
try:
|
|
61
|
+
pf = algorithm.problem.pareto_front()
|
|
62
|
+
except:
|
|
63
|
+
pf = None
|
|
64
|
+
|
|
65
|
+
if pf is not None:
|
|
66
|
+
fgap = f - pf.min()
|
|
67
|
+
|
|
68
|
+
return dict(n_evals=n_evals, cv=cv, f=f, fgap=fgap)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DefaultMultiObjectiveRecorder(Recorder):
|
|
72
|
+
|
|
73
|
+
def save(self, algorithm):
|
|
74
|
+
|
|
75
|
+
igd, igd_plus = np.inf, np.inf
|
|
76
|
+
|
|
77
|
+
opt = algorithm.opt
|
|
78
|
+
|
|
79
|
+
# get all optimal solutions that are feasible
|
|
80
|
+
feas_opt = opt[opt.get("feas")]
|
|
81
|
+
|
|
82
|
+
if len(feas_opt) > 0:
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
pf = algorithm.problem.pareto_front()
|
|
86
|
+
except:
|
|
87
|
+
pf = None
|
|
88
|
+
|
|
89
|
+
if pf is not None:
|
|
90
|
+
F = feas_opt.get("F")
|
|
91
|
+
igd = IGD(pf=pf, zero_to_one=True).do(F)
|
|
92
|
+
igd_plus = IGDPlus(pf=pf, zero_to_one=True).do(F)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
"n_evals": algorithm.evaluator.n_eval,
|
|
96
|
+
"cv": opt.get("cv").min(),
|
|
97
|
+
"igd": igd,
|
|
98
|
+
"igd+": igd_plus,
|
|
99
|
+
}
|
pymoo/core/repair.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.operator import Operator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Repair(Operator):
|
|
7
|
+
|
|
8
|
+
def do(self, problem, pop, **kwargs):
|
|
9
|
+
X = np.array([ind.X for ind in pop])
|
|
10
|
+
if self.vtype is not None:
|
|
11
|
+
X = X.astype(self.vtype)
|
|
12
|
+
|
|
13
|
+
Xp = self._do(problem, X, **kwargs)
|
|
14
|
+
|
|
15
|
+
pop.set("X", Xp)
|
|
16
|
+
return pop
|
|
17
|
+
|
|
18
|
+
def _do(self, problem, X, **kwargs):
|
|
19
|
+
return X
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NoRepair(Repair):
|
|
23
|
+
pass
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from pymoo.core.duplicate import DefaultDuplicateElimination
|
|
4
|
+
from pymoo.core.individual import Individual
|
|
5
|
+
from pymoo.core.population import Population
|
|
6
|
+
from pymoo.core.survival import Survival
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_better(_new, _old, eps=0.0):
|
|
10
|
+
both_infeasible = not _old.feas and not _new.feas
|
|
11
|
+
both_feasible = _old.feas and _new.feas
|
|
12
|
+
|
|
13
|
+
if both_infeasible and _old.CV[0] - _new.CV[0] > eps:
|
|
14
|
+
return True
|
|
15
|
+
elif not _old.FEAS and _new.FEAS:
|
|
16
|
+
return True
|
|
17
|
+
elif both_feasible and _old.F[0] - _new.F[0] > eps:
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ReplacementSurvival(Survival):
|
|
24
|
+
|
|
25
|
+
def do(self, problem, pop, off, return_indices=False, inplace=False, **kwargs):
|
|
26
|
+
|
|
27
|
+
# this makes it usable as a traditional survival
|
|
28
|
+
if isinstance(off, int):
|
|
29
|
+
k = off
|
|
30
|
+
off = pop[k:]
|
|
31
|
+
pop = pop[:k]
|
|
32
|
+
|
|
33
|
+
# if the offsprings are simply empty don't do anything
|
|
34
|
+
if len(off) == 0:
|
|
35
|
+
return pop
|
|
36
|
+
|
|
37
|
+
assert len(pop) == len(off), "For the replacement pop and off must have the same number of individuals."
|
|
38
|
+
|
|
39
|
+
pop = Population.create(pop) if isinstance(pop, Individual) else pop
|
|
40
|
+
off = Population.create(off) if isinstance(off, Individual) else off
|
|
41
|
+
|
|
42
|
+
I = self._do(problem, pop, off, **kwargs)
|
|
43
|
+
|
|
44
|
+
if return_indices:
|
|
45
|
+
return I
|
|
46
|
+
else:
|
|
47
|
+
if not inplace:
|
|
48
|
+
pop = pop.copy()
|
|
49
|
+
pop[I] = off[I]
|
|
50
|
+
return pop
|
|
51
|
+
|
|
52
|
+
def _do(self, problem, pop, off, **kwargs):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ImprovementReplacement(ReplacementSurvival):
|
|
57
|
+
|
|
58
|
+
def _do(self, problem, pop, off, **kwargs):
|
|
59
|
+
|
|
60
|
+
ret = np.full((len(pop), 1), False)
|
|
61
|
+
|
|
62
|
+
pop_F, pop_CV, pop_feas = pop.get("F", "CV", "FEAS")
|
|
63
|
+
off_F, off_CV, off_feas = off.get("F", "CV", "FEAS")
|
|
64
|
+
|
|
65
|
+
if problem.has_constraints() > 0:
|
|
66
|
+
|
|
67
|
+
# 1) Both infeasible and constraints have been improved
|
|
68
|
+
ret[(~pop_feas & ~off_feas) & (off_CV < pop_CV)] = True
|
|
69
|
+
|
|
70
|
+
# 2) A solution became feasible
|
|
71
|
+
ret[~pop_feas & off_feas] = True
|
|
72
|
+
|
|
73
|
+
# 3) Both feasible but objective space value has improved
|
|
74
|
+
ret[(pop_feas & off_feas) & (off_F < pop_F)] = True
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
ret[off_F < pop_F] = True
|
|
78
|
+
|
|
79
|
+
# never allow duplicates to become part of the population when replacement is used
|
|
80
|
+
_, _, is_duplicate = DefaultDuplicateElimination(epsilon=0.0).do(off, pop, return_indices=True)
|
|
81
|
+
ret[is_duplicate] = False
|
|
82
|
+
|
|
83
|
+
return ret[:, 0]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parameter_less(f, cv):
|
|
87
|
+
v = np.copy(f)
|
|
88
|
+
infeas = cv > 0
|
|
89
|
+
v[infeas] = f.max() + cv[infeas]
|
|
90
|
+
return v
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def hierarchical_sort(f, cv=None):
|
|
94
|
+
if cv is not None:
|
|
95
|
+
f = parameter_less(f, cv)
|
|
96
|
+
return np.argsort(f)
|