pydmoo 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.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.
- pydmoo/algorithms/base/core/genetic.py +2 -2
- pydmoo/algorithms/base/dmoo/dmoead.py +8 -5
- pydmoo/algorithms/base/dmoo/dmoeadde.py +8 -5
- pydmoo/algorithms/base/dmoo/dnsga2.py +8 -5
- pydmoo/algorithms/base/moo/moead.py +2 -1
- pydmoo/algorithms/classic/__init__.py +0 -0
- pydmoo/algorithms/classic/moead_ae.py +77 -0
- pydmoo/algorithms/classic/moead_pps.py +94 -0
- pydmoo/algorithms/classic/moeadde_ae.py +77 -0
- pydmoo/algorithms/classic/moeadde_pps.py +94 -0
- pydmoo/algorithms/classic/nsga2_ae.py +76 -0
- pydmoo/algorithms/classic/nsga2_pps.py +94 -0
- pydmoo/algorithms/modern/moead_imkt.py +2 -1
- pydmoo/algorithms/modern/moead_imkt_igp.py +2 -1
- pydmoo/algorithms/modern/moead_imkt_lstm.py +2 -1
- pydmoo/algorithms/modern/moead_imkt_n.py +2 -1
- pydmoo/algorithms/modern/moead_imkt_n_igp.py +2 -1
- pydmoo/algorithms/modern/moead_imkt_n_lstm.py +2 -1
- pydmoo/algorithms/modern/moead_ktmm.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_clstm.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_igp.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_lstm.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_n.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_n_clstm.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_n_igp.py +2 -1
- pydmoo/algorithms/modern/moeadde_imkt_n_lstm.py +2 -1
- pydmoo/algorithms/modern/moeadde_ktmm.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_clstm.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_igp.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_lstm.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_n.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_n_clstm.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_n_igp.py +2 -1
- pydmoo/algorithms/modern/nsga2_imkt_n_lstm.py +2 -1
- pydmoo/algorithms/modern/nsga2_ktmm.py +2 -1
- pydmoo/problems/dyn.py +110 -15
- pydmoo/problems/dynamic/cec2015.py +2 -1
- pydmoo/problems/dynamic/df.py +2 -1
- pydmoo/problems/dynamic/gts.py +641 -82
- {pydmoo-0.1.0.dist-info → pydmoo-0.1.2.dist-info}/METADATA +1 -1
- pydmoo-0.1.2.dist-info/RECORD +77 -0
- pydmoo-0.1.0.dist-info/RECORD +0 -70
- {pydmoo-0.1.0.dist-info → pydmoo-0.1.2.dist-info}/WHEEL +0 -0
- {pydmoo-0.1.0.dist-info → pydmoo-0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -92,7 +92,7 @@ class GeneticAlgorithm(Algorithm):
|
|
|
92
92
|
random_state=self.random_state, algorithm=self, **kwargs)
|
|
93
93
|
|
|
94
94
|
def _infill(self):
|
|
95
|
-
# Added by DynOpt on Dec 21, 2025
|
|
95
|
+
# Added by DynOpt Team on Dec 21, 2025
|
|
96
96
|
pop = self._infill_static_dynamic()
|
|
97
97
|
|
|
98
98
|
# do the mating using the current population
|
|
@@ -110,7 +110,7 @@ class GeneticAlgorithm(Algorithm):
|
|
|
110
110
|
|
|
111
111
|
return off
|
|
112
112
|
|
|
113
|
-
# Added by DynOpt on Dec 21, 2025
|
|
113
|
+
# Added by DynOpt Team on Dec 21, 2025
|
|
114
114
|
def _infill_static_dynamic(self):
|
|
115
115
|
pop = self.pop
|
|
116
116
|
|
|
@@ -52,7 +52,7 @@ class DMOEAD(MOEAD):
|
|
|
52
52
|
|
|
53
53
|
start_time = time.time()
|
|
54
54
|
|
|
55
|
-
pop = self.
|
|
55
|
+
pop = self._response_mechanism()
|
|
56
56
|
|
|
57
57
|
# reevaluate because we know there was a change
|
|
58
58
|
self.evaluator.eval(self.problem, pop)
|
|
@@ -69,8 +69,9 @@ class DMOEAD(MOEAD):
|
|
|
69
69
|
|
|
70
70
|
return pop
|
|
71
71
|
|
|
72
|
-
def
|
|
73
|
-
|
|
72
|
+
def _response_mechanism(self):
|
|
73
|
+
"""Response mechanism."""
|
|
74
|
+
raise NotImplementedError
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
class DMOEADA(DMOEAD):
|
|
@@ -86,7 +87,8 @@ class DMOEADA(DMOEAD):
|
|
|
86
87
|
|
|
87
88
|
self.perc_diversity = perc_diversity
|
|
88
89
|
|
|
89
|
-
def
|
|
90
|
+
def _response_mechanism(self):
|
|
91
|
+
"""Response mechanism."""
|
|
90
92
|
pop = self.pop
|
|
91
93
|
X = pop.get("X")
|
|
92
94
|
|
|
@@ -115,7 +117,8 @@ class DMOEADB(DMOEAD):
|
|
|
115
117
|
|
|
116
118
|
self.perc_diversity = perc_diversity
|
|
117
119
|
|
|
118
|
-
def
|
|
120
|
+
def _response_mechanism(self):
|
|
121
|
+
"""Response mechanism."""
|
|
119
122
|
pop = self.pop
|
|
120
123
|
X = pop.get("X")
|
|
121
124
|
|
|
@@ -52,7 +52,7 @@ class DMOEADDE(MOEADDE):
|
|
|
52
52
|
|
|
53
53
|
start_time = time.time()
|
|
54
54
|
|
|
55
|
-
pop = self.
|
|
55
|
+
pop = self._response_mechanism()
|
|
56
56
|
|
|
57
57
|
# reevaluate because we know there was a change
|
|
58
58
|
self.evaluator.eval(self.problem, pop)
|
|
@@ -69,8 +69,9 @@ class DMOEADDE(MOEADDE):
|
|
|
69
69
|
|
|
70
70
|
return pop
|
|
71
71
|
|
|
72
|
-
def
|
|
73
|
-
|
|
72
|
+
def _response_mechanism(self):
|
|
73
|
+
"""Response mechanism."""
|
|
74
|
+
raise NotImplementedError
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
class DMOEADDEA(DMOEADDE):
|
|
@@ -86,7 +87,8 @@ class DMOEADDEA(DMOEADDE):
|
|
|
86
87
|
|
|
87
88
|
self.perc_diversity = perc_diversity
|
|
88
89
|
|
|
89
|
-
def
|
|
90
|
+
def _response_mechanism(self):
|
|
91
|
+
"""Response mechanism."""
|
|
90
92
|
pop = self.pop
|
|
91
93
|
X = pop.get("X")
|
|
92
94
|
|
|
@@ -115,7 +117,8 @@ class DMOEADDEB(DMOEADDE):
|
|
|
115
117
|
|
|
116
118
|
self.perc_diversity = perc_diversity
|
|
117
119
|
|
|
118
|
-
def
|
|
120
|
+
def _response_mechanism(self):
|
|
121
|
+
"""Response mechanism."""
|
|
119
122
|
pop = self.pop
|
|
120
123
|
X = pop.get("X")
|
|
121
124
|
|
|
@@ -59,7 +59,7 @@ class DNSGA2(NSGA2):
|
|
|
59
59
|
|
|
60
60
|
start_time = time.time()
|
|
61
61
|
|
|
62
|
-
pop = self.
|
|
62
|
+
pop = self._response_mechanism()
|
|
63
63
|
|
|
64
64
|
# reevaluate because we know there was a change
|
|
65
65
|
self.evaluator.eval(self.problem, pop)
|
|
@@ -75,8 +75,9 @@ class DNSGA2(NSGA2):
|
|
|
75
75
|
|
|
76
76
|
return pop
|
|
77
77
|
|
|
78
|
-
def
|
|
79
|
-
|
|
78
|
+
def _response_mechanism(self):
|
|
79
|
+
"""Response mechanism."""
|
|
80
|
+
raise NotImplementedError
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
class DNSGA2A(DNSGA2):
|
|
@@ -92,7 +93,8 @@ class DNSGA2A(DNSGA2):
|
|
|
92
93
|
|
|
93
94
|
self.perc_diversity = perc_diversity
|
|
94
95
|
|
|
95
|
-
def
|
|
96
|
+
def _response_mechanism(self):
|
|
97
|
+
"""Response mechanism."""
|
|
96
98
|
pop = self.pop
|
|
97
99
|
X = pop.get("X")
|
|
98
100
|
|
|
@@ -121,7 +123,8 @@ class DNSGA2B(DNSGA2):
|
|
|
121
123
|
|
|
122
124
|
self.perc_diversity = perc_diversity
|
|
123
125
|
|
|
124
|
-
def
|
|
126
|
+
def _response_mechanism(self):
|
|
127
|
+
"""Response mechanism."""
|
|
125
128
|
pop = self.pop
|
|
126
129
|
X = pop.get("X")
|
|
127
130
|
|
|
@@ -105,6 +105,7 @@ class MOEAD(LoopwiseAlgorithm, GeneticAlgorithm):
|
|
|
105
105
|
self.ideal = np.min(self.pop.get("F"), axis=0)
|
|
106
106
|
|
|
107
107
|
def _next(self):
|
|
108
|
+
# Added by DynOpt Team on Dec 21, 2025
|
|
108
109
|
pop = self._next_static_dynamic()
|
|
109
110
|
|
|
110
111
|
# iterate for each member of the population in random order
|
|
@@ -124,7 +125,7 @@ class MOEAD(LoopwiseAlgorithm, GeneticAlgorithm):
|
|
|
124
125
|
# now actually do the replacement of the individual is better
|
|
125
126
|
self._replace(k, off)
|
|
126
127
|
|
|
127
|
-
# Added by DynOpt on Dec 21, 2025
|
|
128
|
+
# Added by DynOpt Team on Dec 21, 2025
|
|
128
129
|
def _next_static_dynamic(self):
|
|
129
130
|
pop = self.pop
|
|
130
131
|
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
from pymoo.operators.survival.rank_and_crowding import RankAndCrowding
|
|
4
|
+
|
|
5
|
+
from pydmoo.algorithms.base.dmoo.dmoead import DMOEAD
|
|
6
|
+
from pydmoo.core.inverse import closed_form_solution
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MOEADAE(DMOEAD):
|
|
10
|
+
"""Autoencoding.
|
|
11
|
+
|
|
12
|
+
References
|
|
13
|
+
----------
|
|
14
|
+
Feng, L., Zhou, W., Liu, W., Ong, Y.-S., and Tan, K. C. (2022).
|
|
15
|
+
Solving dynamic multiobjective problem via autoencoding evolutionary search.
|
|
16
|
+
IEEE Transactions on Cybernetics, 52(5), 2649–2662.
|
|
17
|
+
https://doi.org/10.1109/TCYB.2020.3017017
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, **kwargs):
|
|
21
|
+
|
|
22
|
+
super().__init__(**kwargs)
|
|
23
|
+
|
|
24
|
+
def _response_mechanism(self):
|
|
25
|
+
"""Response mechanism."""
|
|
26
|
+
pop = self.pop
|
|
27
|
+
X = pop.get("X")
|
|
28
|
+
|
|
29
|
+
# recreate the current population without being evaluated
|
|
30
|
+
pop = Population.new(X=X)
|
|
31
|
+
|
|
32
|
+
# predict via denoising autoencoding
|
|
33
|
+
PSs = self.data.get("PSs", [])
|
|
34
|
+
PSs.append(self.opt.get("X")) # Parate Set
|
|
35
|
+
PSs = PSs[-2:]
|
|
36
|
+
self.data["PSs"] = PSs
|
|
37
|
+
|
|
38
|
+
a = 0
|
|
39
|
+
if len(PSs) == 2:
|
|
40
|
+
# Pareto Set
|
|
41
|
+
P, Q = PSs
|
|
42
|
+
|
|
43
|
+
# Q = PM
|
|
44
|
+
min_len = min(len(P), len(Q))
|
|
45
|
+
M = closed_form_solution(Q[:min_len], P[:min_len])
|
|
46
|
+
|
|
47
|
+
# X = QM
|
|
48
|
+
X = np.dot(Q, M)
|
|
49
|
+
|
|
50
|
+
# bounds
|
|
51
|
+
if self.problem.has_bounds():
|
|
52
|
+
xl, xu = self.problem.bounds()
|
|
53
|
+
X = np.clip(X, xl, xu) # not provided in the original reference literature
|
|
54
|
+
|
|
55
|
+
# evalutate new population
|
|
56
|
+
samples = self.evaluator.eval(self.problem, Population.new(X=X))
|
|
57
|
+
a = min(int(self.pop_size / 2), len(samples))
|
|
58
|
+
|
|
59
|
+
# do a survival to recreate rank and crowding of all individuals
|
|
60
|
+
samples = RankAndCrowding().do(self.problem, samples, n_survive=a, random_state=self.random_state)
|
|
61
|
+
|
|
62
|
+
pop[:a] = samples[:a]
|
|
63
|
+
|
|
64
|
+
# randomly select solutions from previous Parate Set
|
|
65
|
+
# This is to, first, preserve the high-quality solutions found along the evolutionary search process
|
|
66
|
+
# second, to maintain the diversity of the population for further exploration of the evolutionary search.
|
|
67
|
+
Q = self.opt.get("X") # no-dominated solutions
|
|
68
|
+
b = min(int(self.pop_size / 2), len(Q))
|
|
69
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
70
|
+
pop[a:(a + b)] = Population.new(X=Q[idx])
|
|
71
|
+
|
|
72
|
+
# randomly generated solutions will be used to fill the population
|
|
73
|
+
c = self.pop_size - a - b
|
|
74
|
+
if c > 0:
|
|
75
|
+
pop[(a + b):(a + b + c)] = self.initialization.sampling(self.problem, c, random_state=self.random_state)
|
|
76
|
+
|
|
77
|
+
return pop
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
|
|
4
|
+
from pydmoo.algorithms.base.dmoo.dmoead import DMOEAD
|
|
5
|
+
from pydmoo.core.ar_model import ARModel
|
|
6
|
+
from pydmoo.core.bounds import matrix_conditional_update
|
|
7
|
+
from pydmoo.core.manifold import manifold_prediction
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MOEADPPS(DMOEAD):
|
|
11
|
+
"""Population Prediction Strategy (Center point prediction and manifold prediction).
|
|
12
|
+
|
|
13
|
+
References
|
|
14
|
+
----------
|
|
15
|
+
Zhou, A., Jin, Y., and Zhang, Q. (2014).
|
|
16
|
+
A population prediction strategy for evolutionary dynamic multiobjective optimization.
|
|
17
|
+
IEEE Transactions on Cybernetics, 44(1), 40–53.
|
|
18
|
+
https://doi.org/10.1109/TCYB.2013.2245892
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
|
|
25
|
+
self.p = 3 # the order of the AR model
|
|
26
|
+
self.M = 23 # the length of history mean point series
|
|
27
|
+
|
|
28
|
+
def _response_mechanism(self):
|
|
29
|
+
"""Response mechanism."""
|
|
30
|
+
pop = self.pop
|
|
31
|
+
X = pop.get("X")
|
|
32
|
+
|
|
33
|
+
# archive center points
|
|
34
|
+
center_points = self.data.get("center_points", [])
|
|
35
|
+
center_points.append(np.mean(self.opt.get("X"), axis=0))
|
|
36
|
+
|
|
37
|
+
# the maximum length
|
|
38
|
+
center_points = center_points[(-self.M):]
|
|
39
|
+
self.data["center_points"] = center_points
|
|
40
|
+
|
|
41
|
+
# archive populations
|
|
42
|
+
Xs = self.data.get("Xs", [])
|
|
43
|
+
Xs.append(self.pop.get("X")) # pop
|
|
44
|
+
Xs = Xs[-2:]
|
|
45
|
+
self.data["Xs"] = Xs
|
|
46
|
+
|
|
47
|
+
if len(center_points) >= (self.p + 1):
|
|
48
|
+
|
|
49
|
+
C1, distance = manifold_prediction(Xs[0], Xs[1])
|
|
50
|
+
n = C1.shape[1] # Dimensionality of the manifold
|
|
51
|
+
variance = (distance ** 2) / n
|
|
52
|
+
|
|
53
|
+
center, variances = self.center_points_prediction(center_points)
|
|
54
|
+
|
|
55
|
+
X = center + C1 + self.random_state.normal(loc=0, scale=np.sqrt(variances + variance), size=X.shape)
|
|
56
|
+
|
|
57
|
+
# bounds
|
|
58
|
+
if self.problem.has_bounds():
|
|
59
|
+
xl, xu = self.problem.bounds()
|
|
60
|
+
X = matrix_conditional_update(X, xl, xu, self.pop.get("X"))
|
|
61
|
+
|
|
62
|
+
# recreate the current population without being evaluated
|
|
63
|
+
pop = Population.new(X=X)
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
|
|
67
|
+
# recreate the current population without being evaluated
|
|
68
|
+
pop = Population.new(X=X)
|
|
69
|
+
|
|
70
|
+
# randomly sample half of the population and reuse half from the previous search
|
|
71
|
+
# when the history information is not enough to build an AR(p) model.
|
|
72
|
+
|
|
73
|
+
# randomly sample half of the population
|
|
74
|
+
a = int(self.pop_size / 2)
|
|
75
|
+
pop[:a] = self.initialization.sampling(self.problem, a, random_state=self.random_state)
|
|
76
|
+
|
|
77
|
+
# randomly reuse the other half from t Population
|
|
78
|
+
Q = self.pop.get("X")
|
|
79
|
+
b = self.pop_size - a
|
|
80
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
81
|
+
pop[a:] = Population.new(X=Q[idx])
|
|
82
|
+
|
|
83
|
+
return pop
|
|
84
|
+
|
|
85
|
+
def center_points_prediction(self, center_points):
|
|
86
|
+
n = len(center_points[0])
|
|
87
|
+
center = np.zeros(n)
|
|
88
|
+
variances = np.zeros(n)
|
|
89
|
+
for i in range(len(center)):
|
|
90
|
+
data = [c[i] for c in center_points]
|
|
91
|
+
model = ARModel(self.p).fit(data)
|
|
92
|
+
predictions = model.predict(data, 1)
|
|
93
|
+
center[i], variances[i] = predictions[0], np.mean(model.resid_ ** 2)
|
|
94
|
+
return center, variances
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
from pymoo.operators.survival.rank_and_crowding import RankAndCrowding
|
|
4
|
+
|
|
5
|
+
from pydmoo.algorithms.base.dmoo.dmoeadde import DMOEADDE
|
|
6
|
+
from pydmoo.core.inverse import closed_form_solution
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MOEADDEAE(DMOEADDE):
|
|
10
|
+
"""Autoencoding.
|
|
11
|
+
|
|
12
|
+
References
|
|
13
|
+
----------
|
|
14
|
+
Feng, L., Zhou, W., Liu, W., Ong, Y.-S., and Tan, K. C. (2022).
|
|
15
|
+
Solving dynamic multiobjective problem via autoencoding evolutionary search.
|
|
16
|
+
IEEE Transactions on Cybernetics, 52(5), 2649–2662.
|
|
17
|
+
https://doi.org/10.1109/TCYB.2020.3017017
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, **kwargs):
|
|
21
|
+
|
|
22
|
+
super().__init__(**kwargs)
|
|
23
|
+
|
|
24
|
+
def _response_mechanism(self):
|
|
25
|
+
"""Response mechanism."""
|
|
26
|
+
pop = self.pop
|
|
27
|
+
X = pop.get("X")
|
|
28
|
+
|
|
29
|
+
# recreate the current population without being evaluated
|
|
30
|
+
pop = Population.new(X=X)
|
|
31
|
+
|
|
32
|
+
# predict via denoising autoencoding
|
|
33
|
+
PSs = self.data.get("PSs", [])
|
|
34
|
+
PSs.append(self.opt.get("X")) # Parate Set
|
|
35
|
+
PSs = PSs[-2:]
|
|
36
|
+
self.data["PSs"] = PSs
|
|
37
|
+
|
|
38
|
+
a = 0
|
|
39
|
+
if len(PSs) == 2:
|
|
40
|
+
# Pareto Set
|
|
41
|
+
P, Q = PSs
|
|
42
|
+
|
|
43
|
+
# Q = PM
|
|
44
|
+
min_len = min(len(P), len(Q))
|
|
45
|
+
M = closed_form_solution(Q[:min_len], P[:min_len])
|
|
46
|
+
|
|
47
|
+
# X = QM
|
|
48
|
+
X = np.dot(Q, M)
|
|
49
|
+
|
|
50
|
+
# bounds
|
|
51
|
+
if self.problem.has_bounds():
|
|
52
|
+
xl, xu = self.problem.bounds()
|
|
53
|
+
X = np.clip(X, xl, xu) # not provided in the original reference literature
|
|
54
|
+
|
|
55
|
+
# evalutate new population
|
|
56
|
+
samples = self.evaluator.eval(self.problem, Population.new(X=X))
|
|
57
|
+
a = min(int(self.pop_size / 2), len(samples))
|
|
58
|
+
|
|
59
|
+
# do a survival to recreate rank and crowding of all individuals
|
|
60
|
+
samples = RankAndCrowding().do(self.problem, samples, n_survive=a, random_state=self.random_state)
|
|
61
|
+
|
|
62
|
+
pop[:a] = samples[:a]
|
|
63
|
+
|
|
64
|
+
# randomly select solutions from previous Parate Set
|
|
65
|
+
# This is to, first, preserve the high-quality solutions found along the evolutionary search process
|
|
66
|
+
# second, to maintain the diversity of the population for further exploration of the evolutionary search.
|
|
67
|
+
Q = self.opt.get("X") # no-dominated solutions
|
|
68
|
+
b = min(int(self.pop_size / 2), len(Q))
|
|
69
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
70
|
+
pop[a:(a + b)] = Population.new(X=Q[idx])
|
|
71
|
+
|
|
72
|
+
# randomly generated solutions will be used to fill the population
|
|
73
|
+
c = self.pop_size - a - b
|
|
74
|
+
if c > 0:
|
|
75
|
+
pop[(a + b):(a + b + c)] = self.initialization.sampling(self.problem, c, random_state=self.random_state)
|
|
76
|
+
|
|
77
|
+
return pop
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
|
|
4
|
+
from pydmoo.algorithms.base.dmoo.dmoeadde import DMOEADDE
|
|
5
|
+
from pydmoo.core.ar_model import ARModel
|
|
6
|
+
from pydmoo.core.bounds import matrix_conditional_update
|
|
7
|
+
from pydmoo.core.manifold import manifold_prediction
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MOEADDEPPS(DMOEADDE):
|
|
11
|
+
"""Population Prediction Strategy (Center point prediction and manifold prediction).
|
|
12
|
+
|
|
13
|
+
References
|
|
14
|
+
----------
|
|
15
|
+
Zhou, A., Jin, Y., and Zhang, Q. (2014).
|
|
16
|
+
A population prediction strategy for evolutionary dynamic multiobjective optimization.
|
|
17
|
+
IEEE Transactions on Cybernetics, 44(1), 40–53.
|
|
18
|
+
https://doi.org/10.1109/TCYB.2013.2245892
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
|
|
25
|
+
self.p = 3 # the order of the AR model
|
|
26
|
+
self.M = 23 # the length of history mean point series
|
|
27
|
+
|
|
28
|
+
def _response_mechanism(self):
|
|
29
|
+
"""Response mechanism."""
|
|
30
|
+
pop = self.pop
|
|
31
|
+
X = pop.get("X")
|
|
32
|
+
|
|
33
|
+
# archive center points
|
|
34
|
+
center_points = self.data.get("center_points", [])
|
|
35
|
+
center_points.append(np.mean(self.opt.get("X"), axis=0))
|
|
36
|
+
|
|
37
|
+
# the maximum length
|
|
38
|
+
center_points = center_points[(-self.M):]
|
|
39
|
+
self.data["center_points"] = center_points
|
|
40
|
+
|
|
41
|
+
# archive populations
|
|
42
|
+
Xs = self.data.get("Xs", [])
|
|
43
|
+
Xs.append(self.pop.get("X")) # pop
|
|
44
|
+
Xs = Xs[-2:]
|
|
45
|
+
self.data["Xs"] = Xs
|
|
46
|
+
|
|
47
|
+
if len(center_points) >= (self.p + 1):
|
|
48
|
+
|
|
49
|
+
C1, distance = manifold_prediction(Xs[0], Xs[1])
|
|
50
|
+
n = C1.shape[1] # Dimensionality of the manifold
|
|
51
|
+
variance = (distance ** 2) / n
|
|
52
|
+
|
|
53
|
+
center, variances = self.center_points_prediction(center_points)
|
|
54
|
+
|
|
55
|
+
X = center + C1 + self.random_state.normal(loc=0, scale=np.sqrt(variances + variance), size=X.shape)
|
|
56
|
+
|
|
57
|
+
# bounds
|
|
58
|
+
if self.problem.has_bounds():
|
|
59
|
+
xl, xu = self.problem.bounds()
|
|
60
|
+
X = matrix_conditional_update(X, xl, xu, self.pop.get("X"))
|
|
61
|
+
|
|
62
|
+
# recreate the current population without being evaluated
|
|
63
|
+
pop = Population.new(X=X)
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
|
|
67
|
+
# recreate the current population without being evaluated
|
|
68
|
+
pop = Population.new(X=X)
|
|
69
|
+
|
|
70
|
+
# randomly sample half of the population and reuse half from the previous search
|
|
71
|
+
# when the history information is not enough to build an AR(p) model.
|
|
72
|
+
|
|
73
|
+
# randomly sample half of the population
|
|
74
|
+
a = int(self.pop_size / 2)
|
|
75
|
+
pop[:a] = self.initialization.sampling(self.problem, a, random_state=self.random_state)
|
|
76
|
+
|
|
77
|
+
# randomly reuse the other half from t Population
|
|
78
|
+
Q = self.pop.get("X")
|
|
79
|
+
b = self.pop_size - a
|
|
80
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
81
|
+
pop[a:] = Population.new(X=Q[idx])
|
|
82
|
+
|
|
83
|
+
return pop
|
|
84
|
+
|
|
85
|
+
def center_points_prediction(self, center_points):
|
|
86
|
+
n = len(center_points[0])
|
|
87
|
+
center = np.zeros(n)
|
|
88
|
+
variances = np.zeros(n)
|
|
89
|
+
for i in range(len(center)):
|
|
90
|
+
data = [c[i] for c in center_points]
|
|
91
|
+
model = ARModel(self.p).fit(data)
|
|
92
|
+
predictions = model.predict(data, 1)
|
|
93
|
+
center[i], variances[i] = predictions[0], np.mean(model.resid_ ** 2)
|
|
94
|
+
return center, variances
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
|
|
4
|
+
from pydmoo.algorithms.base.dmoo.dnsga2 import DNSGA2
|
|
5
|
+
from pydmoo.core.inverse import closed_form_solution
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NSGA2AE(DNSGA2):
|
|
9
|
+
"""Autoencoding (AE).
|
|
10
|
+
|
|
11
|
+
References
|
|
12
|
+
----------
|
|
13
|
+
Feng, L., Zhou, W., Liu, W., Ong, Y.-S., and Tan, K. C. (2022).
|
|
14
|
+
Solving dynamic multiobjective problem via autoencoding evolutionary search.
|
|
15
|
+
IEEE Transactions on Cybernetics, 52(5), 2649–2662.
|
|
16
|
+
https://doi.org/10.1109/TCYB.2020.3017017
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, **kwargs):
|
|
20
|
+
|
|
21
|
+
super().__init__(**kwargs)
|
|
22
|
+
|
|
23
|
+
def _response_mechanism(self):
|
|
24
|
+
"""Response mechanism."""
|
|
25
|
+
pop = self.pop
|
|
26
|
+
X = pop.get("X")
|
|
27
|
+
|
|
28
|
+
# recreate the current population without being evaluated
|
|
29
|
+
pop = Population.new(X=X)
|
|
30
|
+
|
|
31
|
+
# predict via denoising autoencoding
|
|
32
|
+
PSs = self.data.get("PSs", [])
|
|
33
|
+
PSs.append(self.opt.get("X")) # Parate Set
|
|
34
|
+
PSs = PSs[-2:]
|
|
35
|
+
self.data["PSs"] = PSs
|
|
36
|
+
|
|
37
|
+
a = 0
|
|
38
|
+
if len(PSs) == 2:
|
|
39
|
+
# Pareto Set
|
|
40
|
+
P, Q = PSs
|
|
41
|
+
|
|
42
|
+
# Q = PM
|
|
43
|
+
min_len = min(len(P), len(Q))
|
|
44
|
+
M = closed_form_solution(Q[:min_len], P[:min_len])
|
|
45
|
+
|
|
46
|
+
# X = QM
|
|
47
|
+
X = np.dot(Q, M)
|
|
48
|
+
|
|
49
|
+
# bounds
|
|
50
|
+
if self.problem.has_bounds():
|
|
51
|
+
xl, xu = self.problem.bounds()
|
|
52
|
+
X = np.clip(X, xl, xu) # not provided in the original reference literature
|
|
53
|
+
|
|
54
|
+
# evalutate new population
|
|
55
|
+
samples = self.evaluator.eval(self.problem, Population.new(X=X))
|
|
56
|
+
a = min(int(self.pop_size / 2), len(samples))
|
|
57
|
+
|
|
58
|
+
# do a survival to recreate rank and crowding of all individuals
|
|
59
|
+
samples = self.survival.do(self.problem, samples, n_survive=a, random_state=self.random_state)
|
|
60
|
+
|
|
61
|
+
pop[:a] = samples[:a]
|
|
62
|
+
|
|
63
|
+
# randomly select solutions from previous Parate Set
|
|
64
|
+
# This is to, first, preserve the high-quality solutions found along the evolutionary search process
|
|
65
|
+
# second, to maintain the diversity of the population for further exploration of the evolutionary search.
|
|
66
|
+
Q = self.opt.get("X") # no-dominated solutions
|
|
67
|
+
b = min(int(self.pop_size / 2), len(Q))
|
|
68
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
69
|
+
pop[a:(a + b)] = Population.new(X=Q[idx])
|
|
70
|
+
|
|
71
|
+
# randomly generated solutions will be used to fill the population
|
|
72
|
+
c = self.pop_size - a - b
|
|
73
|
+
if c > 0:
|
|
74
|
+
pop[(a + b):(a + b + c)] = self.initialization.sampling(self.problem, c, random_state=self.random_state)
|
|
75
|
+
|
|
76
|
+
return pop
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pymoo.core.population import Population
|
|
3
|
+
|
|
4
|
+
from pydmoo.algorithms.base.dmoo.dnsga2 import DNSGA2
|
|
5
|
+
from pydmoo.core.ar_model import ARModel
|
|
6
|
+
from pydmoo.core.bounds import matrix_conditional_update
|
|
7
|
+
from pydmoo.core.manifold import manifold_prediction
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NSGA2PPS(DNSGA2):
|
|
11
|
+
"""Population Prediction Strategy (Center point prediction and manifold prediction).
|
|
12
|
+
|
|
13
|
+
References
|
|
14
|
+
----------
|
|
15
|
+
Zhou, A., Jin, Y., and Zhang, Q. (2014).
|
|
16
|
+
A population prediction strategy for evolutionary dynamic multiobjective optimization.
|
|
17
|
+
IEEE Transactions on Cybernetics, 44(1), 40–53.
|
|
18
|
+
https://doi.org/10.1109/TCYB.2013.2245892
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, **kwargs):
|
|
22
|
+
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
|
|
25
|
+
self.p = 3 # the order of the AR model
|
|
26
|
+
self.M = 23 # the length of history mean point series
|
|
27
|
+
|
|
28
|
+
def _response_mechanism(self):
|
|
29
|
+
"""Response mechanism."""
|
|
30
|
+
pop = self.pop
|
|
31
|
+
X = pop.get("X")
|
|
32
|
+
|
|
33
|
+
# archive center points
|
|
34
|
+
center_points = self.data.get("center_points", [])
|
|
35
|
+
center_points.append(np.mean(self.opt.get("X"), axis=0))
|
|
36
|
+
|
|
37
|
+
# the maximum length
|
|
38
|
+
center_points = center_points[(-self.M):]
|
|
39
|
+
self.data["center_points"] = center_points
|
|
40
|
+
|
|
41
|
+
# archive populations
|
|
42
|
+
Xs = self.data.get("Xs", [])
|
|
43
|
+
Xs.append(self.pop.get("X")) # pop
|
|
44
|
+
Xs = Xs[-2:]
|
|
45
|
+
self.data["Xs"] = Xs
|
|
46
|
+
|
|
47
|
+
if len(center_points) >= (self.p + 1):
|
|
48
|
+
|
|
49
|
+
C1, distance = manifold_prediction(Xs[0], Xs[1])
|
|
50
|
+
n = C1.shape[1] # Dimensionality of the manifold
|
|
51
|
+
variance = (distance ** 2) / n
|
|
52
|
+
|
|
53
|
+
center, variances = self.center_points_prediction(center_points)
|
|
54
|
+
|
|
55
|
+
X = center + C1 + self.random_state.normal(loc=0, scale=np.sqrt(variances + variance), size=X.shape)
|
|
56
|
+
|
|
57
|
+
# bounds
|
|
58
|
+
if self.problem.has_bounds():
|
|
59
|
+
xl, xu = self.problem.bounds()
|
|
60
|
+
X = matrix_conditional_update(X, xl, xu, self.pop.get("X"))
|
|
61
|
+
|
|
62
|
+
# recreate the current population without being evaluated
|
|
63
|
+
pop = Population.new(X=X)
|
|
64
|
+
|
|
65
|
+
else:
|
|
66
|
+
|
|
67
|
+
# recreate the current population without being evaluated
|
|
68
|
+
pop = Population.new(X=X)
|
|
69
|
+
|
|
70
|
+
# randomly sample half of the population and reuse half from the previous search
|
|
71
|
+
# when the history information is not enough to build an AR(p) model.
|
|
72
|
+
|
|
73
|
+
# randomly sample half of the population
|
|
74
|
+
a = int(self.pop_size / 2)
|
|
75
|
+
pop[:a] = self.initialization.sampling(self.problem, a, random_state=self.random_state)
|
|
76
|
+
|
|
77
|
+
# randomly reuse the other half from t Population
|
|
78
|
+
Q = self.pop.get("X")
|
|
79
|
+
b = self.pop_size - a
|
|
80
|
+
idx = self.random_state.choice(np.arange(len(Q)), size=b)
|
|
81
|
+
pop[a:] = Population.new(X=Q[idx])
|
|
82
|
+
|
|
83
|
+
return pop
|
|
84
|
+
|
|
85
|
+
def center_points_prediction(self, center_points):
|
|
86
|
+
n = len(center_points[0])
|
|
87
|
+
center = np.zeros(n)
|
|
88
|
+
variances = np.zeros(n)
|
|
89
|
+
for i in range(len(center)):
|
|
90
|
+
data = [c[i] for c in center_points]
|
|
91
|
+
model = ARModel(self.p).fit(data)
|
|
92
|
+
predictions = model.predict(data, 1)
|
|
93
|
+
center[i], variances[i] = predictions[0], np.mean(model.resid_ ** 2)
|
|
94
|
+
return center, variances
|