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.
Files changed (46) hide show
  1. pydmoo/algorithms/base/core/genetic.py +2 -2
  2. pydmoo/algorithms/base/dmoo/dmoead.py +8 -5
  3. pydmoo/algorithms/base/dmoo/dmoeadde.py +8 -5
  4. pydmoo/algorithms/base/dmoo/dnsga2.py +8 -5
  5. pydmoo/algorithms/base/moo/moead.py +2 -1
  6. pydmoo/algorithms/classic/__init__.py +0 -0
  7. pydmoo/algorithms/classic/moead_ae.py +77 -0
  8. pydmoo/algorithms/classic/moead_pps.py +94 -0
  9. pydmoo/algorithms/classic/moeadde_ae.py +77 -0
  10. pydmoo/algorithms/classic/moeadde_pps.py +94 -0
  11. pydmoo/algorithms/classic/nsga2_ae.py +76 -0
  12. pydmoo/algorithms/classic/nsga2_pps.py +94 -0
  13. pydmoo/algorithms/modern/moead_imkt.py +2 -1
  14. pydmoo/algorithms/modern/moead_imkt_igp.py +2 -1
  15. pydmoo/algorithms/modern/moead_imkt_lstm.py +2 -1
  16. pydmoo/algorithms/modern/moead_imkt_n.py +2 -1
  17. pydmoo/algorithms/modern/moead_imkt_n_igp.py +2 -1
  18. pydmoo/algorithms/modern/moead_imkt_n_lstm.py +2 -1
  19. pydmoo/algorithms/modern/moead_ktmm.py +2 -1
  20. pydmoo/algorithms/modern/moeadde_imkt.py +2 -1
  21. pydmoo/algorithms/modern/moeadde_imkt_clstm.py +2 -1
  22. pydmoo/algorithms/modern/moeadde_imkt_igp.py +2 -1
  23. pydmoo/algorithms/modern/moeadde_imkt_lstm.py +2 -1
  24. pydmoo/algorithms/modern/moeadde_imkt_n.py +2 -1
  25. pydmoo/algorithms/modern/moeadde_imkt_n_clstm.py +2 -1
  26. pydmoo/algorithms/modern/moeadde_imkt_n_igp.py +2 -1
  27. pydmoo/algorithms/modern/moeadde_imkt_n_lstm.py +2 -1
  28. pydmoo/algorithms/modern/moeadde_ktmm.py +2 -1
  29. pydmoo/algorithms/modern/nsga2_imkt.py +2 -1
  30. pydmoo/algorithms/modern/nsga2_imkt_clstm.py +2 -1
  31. pydmoo/algorithms/modern/nsga2_imkt_igp.py +2 -1
  32. pydmoo/algorithms/modern/nsga2_imkt_lstm.py +2 -1
  33. pydmoo/algorithms/modern/nsga2_imkt_n.py +2 -1
  34. pydmoo/algorithms/modern/nsga2_imkt_n_clstm.py +2 -1
  35. pydmoo/algorithms/modern/nsga2_imkt_n_igp.py +2 -1
  36. pydmoo/algorithms/modern/nsga2_imkt_n_lstm.py +2 -1
  37. pydmoo/algorithms/modern/nsga2_ktmm.py +2 -1
  38. pydmoo/problems/dyn.py +110 -15
  39. pydmoo/problems/dynamic/cec2015.py +2 -1
  40. pydmoo/problems/dynamic/df.py +2 -1
  41. pydmoo/problems/dynamic/gts.py +641 -82
  42. {pydmoo-0.1.0.dist-info → pydmoo-0.1.2.dist-info}/METADATA +1 -1
  43. pydmoo-0.1.2.dist-info/RECORD +77 -0
  44. pydmoo-0.1.0.dist-info/RECORD +0 -70
  45. {pydmoo-0.1.0.dist-info → pydmoo-0.1.2.dist-info}/WHEEL +0 -0
  46. {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._response_change()
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 _response_change(self):
73
- pass
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 _response_change(self):
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 _response_change(self):
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._response_change()
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 _response_change(self):
73
- pass
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 _response_change(self):
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 _response_change(self):
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._response_change()
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 _response_change(self):
79
- pass
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 _response_change(self):
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 _response_change(self):
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
@@ -19,7 +19,8 @@ class MOEADIMKT(MOEADKTMM):
19
19
  self.size_pool = 10
20
20
  self.denominator = 0.5
21
21
 
22
- def _response_change(self):
22
+ def _response_mechanism(self):
23
+ """Response mechanism."""
23
24
  pop = self.pop
24
25
  X = pop.get("X")
25
26