pydmoo 0.1.1__py3-none-any.whl → 0.1.3__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 (48) hide show
  1. pydmoo/algorithms/base/core/genetic.py +2 -2
  2. pydmoo/algorithms/base/dmoo/dmoead.py +55 -8
  3. pydmoo/algorithms/base/dmoo/dmoeadde.py +55 -7
  4. pydmoo/algorithms/base/dmoo/dnsga2.py +81 -15
  5. pydmoo/algorithms/base/moo/moead.py +2 -1
  6. pydmoo/algorithms/base/moo/moeadde.py +12 -7
  7. pydmoo/algorithms/classic/moead_ae.py +2 -2
  8. pydmoo/algorithms/classic/moead_pps.py +2 -2
  9. pydmoo/algorithms/classic/moead_svr.py +86 -0
  10. pydmoo/algorithms/classic/moeadde_ae.py +2 -2
  11. pydmoo/algorithms/classic/moeadde_pps.py +2 -2
  12. pydmoo/algorithms/classic/moeadde_svr.py +86 -0
  13. pydmoo/algorithms/classic/nsga2_ae.py +2 -2
  14. pydmoo/algorithms/classic/nsga2_pps.py +2 -2
  15. pydmoo/algorithms/learning/moead_tr.py +98 -0
  16. pydmoo/algorithms/learning/moeadde_tr.py +98 -0
  17. pydmoo/algorithms/learning/nsga2_tr.py +98 -0
  18. pydmoo/algorithms/modern/moead_imkt.py +2 -1
  19. pydmoo/algorithms/modern/moead_imkt_igp.py +2 -1
  20. pydmoo/algorithms/modern/moead_imkt_lstm.py +2 -1
  21. pydmoo/algorithms/modern/moead_imkt_n.py +2 -1
  22. pydmoo/algorithms/modern/moead_imkt_n_igp.py +2 -1
  23. pydmoo/algorithms/modern/moead_imkt_n_lstm.py +2 -1
  24. pydmoo/algorithms/modern/moead_ktmm.py +2 -1
  25. pydmoo/algorithms/modern/moeadde_imkt.py +2 -1
  26. pydmoo/algorithms/modern/moeadde_imkt_clstm.py +2 -1
  27. pydmoo/algorithms/modern/moeadde_imkt_igp.py +2 -1
  28. pydmoo/algorithms/modern/moeadde_imkt_lstm.py +2 -1
  29. pydmoo/algorithms/modern/moeadde_imkt_n.py +2 -1
  30. pydmoo/algorithms/modern/moeadde_imkt_n_clstm.py +2 -1
  31. pydmoo/algorithms/modern/moeadde_imkt_n_igp.py +2 -1
  32. pydmoo/algorithms/modern/moeadde_imkt_n_lstm.py +2 -1
  33. pydmoo/algorithms/modern/moeadde_ktmm.py +2 -1
  34. pydmoo/algorithms/modern/nsga2_imkt.py +2 -1
  35. pydmoo/algorithms/modern/nsga2_imkt_clstm.py +2 -1
  36. pydmoo/algorithms/modern/nsga2_imkt_igp.py +2 -1
  37. pydmoo/algorithms/modern/nsga2_imkt_lstm.py +2 -1
  38. pydmoo/algorithms/modern/nsga2_imkt_n.py +2 -1
  39. pydmoo/algorithms/modern/nsga2_imkt_n_clstm.py +2 -1
  40. pydmoo/algorithms/modern/nsga2_imkt_n_igp.py +2 -1
  41. pydmoo/algorithms/modern/nsga2_imkt_n_lstm.py +2 -1
  42. pydmoo/algorithms/modern/nsga2_ktmm.py +2 -1
  43. pydmoo/problems/dyn.py +28 -0
  44. {pydmoo-0.1.1.dist-info → pydmoo-0.1.3.dist-info}/METADATA +1 -1
  45. pydmoo-0.1.3.dist-info/RECORD +82 -0
  46. pydmoo-0.1.1.dist-info/RECORD +0 -77
  47. {pydmoo-0.1.1.dist-info → pydmoo-0.1.3.dist-info}/WHEEL +0 -0
  48. {pydmoo-0.1.1.dist-info → pydmoo-0.1.3.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
 
@@ -8,7 +8,21 @@ from pydmoo.algorithms.base.moo.moead import MOEAD
8
8
 
9
9
 
10
10
  class DMOEAD(MOEAD):
11
-
11
+ """
12
+ Dynamic MOEA/D (DMOEAD).
13
+
14
+ Extension of MOEAD for dynamic optimization problems.
15
+
16
+ Parameters
17
+ ----------
18
+ perc_detect_change : float, default=0.1
19
+ Percentage of population to sample for change detection (0 to 1).
20
+ eps : float, default=0.0
21
+ Threshold for change detection. Change is detected when mean squared
22
+ difference exceeds this value.
23
+ **kwargs
24
+ Additional arguments passed to MOEAD parent class.
25
+ """
12
26
  def __init__(self,
13
27
  perc_detect_change=0.1,
14
28
  eps=0.0,
@@ -22,7 +36,15 @@ class DMOEAD(MOEAD):
22
36
  assert not problem.has_constraints(), f"{self.__class__.__name__} only works for unconstrained problems."
23
37
  return super().setup(problem, **kwargs)
24
38
 
25
- def _detect_change_sample_part_population(self):
39
+ def _detect_change_sample_part_population(self) -> bool:
40
+ """
41
+ Detect environmental changes by sampling part of the population.
42
+
43
+ Returns
44
+ -------
45
+ change_detected : bool
46
+ True if environmental change is detected, False otherwise.
47
+ """
26
48
  pop = self.pop
27
49
  X, F = pop.get("X", "F")
28
50
 
@@ -40,7 +62,15 @@ class DMOEAD(MOEAD):
40
62
  change_detected = delta > self.eps
41
63
  return change_detected
42
64
 
43
- def _next_static_dynamic(self):
65
+ def _next_static_dynamic(self) -> Population:
66
+ """
67
+ Perform next with dynamic change detection and response.
68
+
69
+ Returns
70
+ -------
71
+ Population
72
+ Current population after potential response to environmental change.
73
+ """
44
74
  # for dynamic environment
45
75
  pop = self.pop
46
76
 
@@ -52,7 +82,7 @@ class DMOEAD(MOEAD):
52
82
 
53
83
  start_time = time.time()
54
84
 
55
- pop = self._response_change()
85
+ pop = self._response_mechanism()
56
86
 
57
87
  # reevaluate because we know there was a change
58
88
  self.evaluator.eval(self.problem, pop)
@@ -69,11 +99,25 @@ class DMOEAD(MOEAD):
69
99
 
70
100
  return pop
71
101
 
72
- def _response_change(self):
73
- pass
102
+ def _response_mechanism(self):
103
+ """
104
+ Response mechanism for environmental change.
105
+
106
+ Returns
107
+ -------
108
+ Population
109
+ Population after applying response strategy.
110
+
111
+ Raises
112
+ ------
113
+ NotImplementedError
114
+ Must be implemented by subclasses.
115
+ """
116
+ raise NotImplementedError
74
117
 
75
118
 
76
119
  class DMOEADA(DMOEAD):
120
+ """DMOEADA."""
77
121
 
78
122
  def __init__(self,
79
123
  perc_detect_change=0.1,
@@ -86,7 +130,8 @@ class DMOEADA(DMOEAD):
86
130
 
87
131
  self.perc_diversity = perc_diversity
88
132
 
89
- def _response_change(self):
133
+ def _response_mechanism(self):
134
+ """Response mechanism."""
90
135
  pop = self.pop
91
136
  X = pop.get("X")
92
137
 
@@ -103,6 +148,7 @@ class DMOEADA(DMOEAD):
103
148
 
104
149
 
105
150
  class DMOEADB(DMOEAD):
151
+ """DMOEADB."""
106
152
 
107
153
  def __init__(self,
108
154
  perc_detect_change=0.1,
@@ -115,7 +161,8 @@ class DMOEADB(DMOEAD):
115
161
 
116
162
  self.perc_diversity = perc_diversity
117
163
 
118
- def _response_change(self):
164
+ def _response_mechanism(self):
165
+ """Response mechanism."""
119
166
  pop = self.pop
120
167
  X = pop.get("X")
121
168
 
@@ -8,6 +8,21 @@ from pydmoo.algorithms.base.moo.moeadde import MOEADDE
8
8
 
9
9
 
10
10
  class DMOEADDE(MOEADDE):
11
+ """
12
+ Dynamic MOEA/D-DE (DMOEADDE).
13
+
14
+ Extension of MOEADDE for dynamic optimization problems.
15
+
16
+ Parameters
17
+ ----------
18
+ perc_detect_change : float, default=0.1
19
+ Percentage of population to sample for change detection (0 to 1).
20
+ eps : float, default=0.0
21
+ Threshold for change detection. Change is detected when mean squared
22
+ difference exceeds this value.
23
+ **kwargs
24
+ Additional arguments passed to MOEADDE parent class.
25
+ """
11
26
 
12
27
  def __init__(self,
13
28
  perc_detect_change=0.1,
@@ -22,7 +37,15 @@ class DMOEADDE(MOEADDE):
22
37
  assert not problem.has_constraints(), f"{self.__class__.__name__} only works for unconstrained problems."
23
38
  return super().setup(problem, **kwargs)
24
39
 
25
- def _detect_change_sample_part_population(self):
40
+ def _detect_change_sample_part_population(self) -> bool:
41
+ """
42
+ Detect environmental changes by sampling part of the population.
43
+
44
+ Returns
45
+ -------
46
+ change_detected : bool
47
+ True if environmental change is detected, False otherwise.
48
+ """
26
49
  pop = self.pop
27
50
  X, F = pop.get("X", "F")
28
51
 
@@ -40,7 +63,15 @@ class DMOEADDE(MOEADDE):
40
63
  change_detected = delta > self.eps
41
64
  return change_detected
42
65
 
43
- def _next_static_dynamic(self):
66
+ def _next_static_dynamic(self) -> Population:
67
+ """
68
+ Perform next with dynamic change detection and response.
69
+
70
+ Returns
71
+ -------
72
+ Population
73
+ Current population after potential response to environmental change.
74
+ """
44
75
  # for dynamic environment
45
76
  pop = self.pop
46
77
 
@@ -52,7 +83,7 @@ class DMOEADDE(MOEADDE):
52
83
 
53
84
  start_time = time.time()
54
85
 
55
- pop = self._response_change()
86
+ pop = self._response_mechanism()
56
87
 
57
88
  # reevaluate because we know there was a change
58
89
  self.evaluator.eval(self.problem, pop)
@@ -69,11 +100,25 @@ class DMOEADDE(MOEADDE):
69
100
 
70
101
  return pop
71
102
 
72
- def _response_change(self):
73
- pass
103
+ def _response_mechanism(self) -> Population:
104
+ """
105
+ Response mechanism for environmental change.
106
+
107
+ Returns
108
+ -------
109
+ Population
110
+ Population after applying response strategy.
111
+
112
+ Raises
113
+ ------
114
+ NotImplementedError
115
+ Must be implemented by subclasses.
116
+ """
117
+ raise NotImplementedError
74
118
 
75
119
 
76
120
  class DMOEADDEA(DMOEADDE):
121
+ """DMOEADDEA."""
77
122
 
78
123
  def __init__(self,
79
124
  perc_detect_change=0.1,
@@ -86,7 +131,8 @@ class DMOEADDEA(DMOEADDE):
86
131
 
87
132
  self.perc_diversity = perc_diversity
88
133
 
89
- def _response_change(self):
134
+ def _response_mechanism(self):
135
+ """Response mechanism."""
90
136
  pop = self.pop
91
137
  X = pop.get("X")
92
138
 
@@ -103,6 +149,7 @@ class DMOEADDEA(DMOEADDE):
103
149
 
104
150
 
105
151
  class DMOEADDEB(DMOEADDE):
152
+ """DMOEADDEB."""
106
153
 
107
154
  def __init__(self,
108
155
  perc_detect_change=0.1,
@@ -115,7 +162,8 @@ class DMOEADDEB(DMOEADDE):
115
162
 
116
163
  self.perc_diversity = perc_diversity
117
164
 
118
- def _response_change(self):
165
+ def _response_mechanism(self):
166
+ """Response mechanism."""
119
167
  pop = self.pop
120
168
  X = pop.get("X")
121
169
 
@@ -17,10 +17,25 @@ from pydmoo.algorithms.base.moo.nsga2 import NSGA2
17
17
 
18
18
 
19
19
  class DNSGA2(NSGA2):
20
+ """
21
+ Dynamic Non-dominated Sorting Genetic Algorithm II (DNSGA2).
22
+
23
+ Extension of NSGA2 for dynamic optimization problems.
24
+
25
+ Parameters
26
+ ----------
27
+ perc_detect_change : float, default=0.1
28
+ Percentage of population to sample for change detection (0 to 1).
29
+ eps : float, default=0.0
30
+ Threshold for change detection. Change is detected when mean squared
31
+ difference exceeds this value.
32
+ **kwargs
33
+ Additional arguments passed to NSGA2 parent class.
34
+ """
20
35
 
21
36
  def __init__(self,
22
- perc_detect_change=0.1,
23
- eps=0.0,
37
+ perc_detect_change: float = 0.1,
38
+ eps: float = 0.0,
24
39
  **kwargs):
25
40
 
26
41
  super().__init__(**kwargs)
@@ -31,7 +46,15 @@ class DNSGA2(NSGA2):
31
46
  assert not problem.has_constraints(), f"{self.__class__.__name__} only works for unconstrained problems."
32
47
  return super().setup(problem, **kwargs)
33
48
 
34
- def _detect_change_sample_part_population(self):
49
+ def _detect_change_sample_part_population(self) -> bool:
50
+ """
51
+ Detect environmental changes by sampling part of the population.
52
+
53
+ Returns
54
+ -------
55
+ change_detected : bool
56
+ True if environmental change is detected, False otherwise.
57
+ """
35
58
  pop = self.pop
36
59
  X, F = pop.get("X", "F")
37
60
 
@@ -49,7 +72,15 @@ class DNSGA2(NSGA2):
49
72
  change_detected = delta > self.eps
50
73
  return change_detected
51
74
 
52
- def _infill_static_dynamic(self):
75
+ def _infill_static_dynamic(self) -> Population:
76
+ """
77
+ Perform infill with dynamic change detection and response.
78
+
79
+ Returns
80
+ -------
81
+ Population
82
+ Current population after potential response to environmental change.
83
+ """
53
84
  # for dynamic environment
54
85
  pop = self.pop
55
86
 
@@ -59,7 +90,7 @@ class DNSGA2(NSGA2):
59
90
 
60
91
  start_time = time.time()
61
92
 
62
- pop = self._response_change()
93
+ pop = self._response_mechanism()
63
94
 
64
95
  # reevaluate because we know there was a change
65
96
  self.evaluator.eval(self.problem, pop)
@@ -75,16 +106,39 @@ class DNSGA2(NSGA2):
75
106
 
76
107
  return pop
77
108
 
78
- def _response_change(self):
79
- pass
109
+ def _response_mechanism(self) -> Population:
110
+ """
111
+ Response mechanism for environmental change.
112
+
113
+ Returns
114
+ -------
115
+ Population
116
+ Population after applying response strategy.
117
+
118
+ Raises
119
+ ------
120
+ NotImplementedError
121
+ Must be implemented by subclasses.
122
+ """
123
+ raise NotImplementedError
80
124
 
81
125
 
82
126
  class DNSGA2A(DNSGA2):
127
+ """DNSGA2A.
128
+
129
+ References
130
+ ----------
131
+ Deb, K., Rao N., U. B., and Karthik, S. (2007).
132
+ Dynamic multi-objective optimization and decision-making using modified NSGA-II:
133
+ A case study on hydro-thermal power scheduling.
134
+ Evolutionary Multi-Criterion Optimization, 803–817.
135
+ https://doi.org/10.1007/978-3-540-70928-2_60
136
+ """
83
137
 
84
138
  def __init__(self,
85
- perc_detect_change=0.1,
86
- eps=0.0,
87
- perc_diversity=0.3,
139
+ perc_detect_change: float = 0.1,
140
+ eps: float = 0.0,
141
+ perc_diversity: float = 0.3,
88
142
  **kwargs):
89
143
  super().__init__(perc_detect_change=perc_detect_change,
90
144
  eps=eps,
@@ -92,7 +146,8 @@ class DNSGA2A(DNSGA2):
92
146
 
93
147
  self.perc_diversity = perc_diversity
94
148
 
95
- def _response_change(self):
149
+ def _response_mechanism(self) -> Population:
150
+ """Response mechanism."""
96
151
  pop = self.pop
97
152
  X = pop.get("X")
98
153
 
@@ -109,11 +164,21 @@ class DNSGA2A(DNSGA2):
109
164
 
110
165
 
111
166
  class DNSGA2B(DNSGA2):
167
+ """DNSGA2B.
168
+
169
+ References
170
+ ----------
171
+ Deb, K., Rao N., U. B., and Karthik, S. (2007).
172
+ Dynamic multi-objective optimization and decision-making using modified NSGA-II:
173
+ A case study on hydro-thermal power scheduling.
174
+ Evolutionary Multi-Criterion Optimization, 803–817.
175
+ https://doi.org/10.1007/978-3-540-70928-2_60
176
+ """
112
177
 
113
178
  def __init__(self,
114
- perc_detect_change=0.1,
115
- eps=0.0,
116
- perc_diversity=0.3,
179
+ perc_detect_change: float = 0.1,
180
+ eps: float = 0.0,
181
+ perc_diversity: float = 0.3,
117
182
  **kwargs):
118
183
  super().__init__(perc_detect_change=perc_detect_change,
119
184
  eps=eps,
@@ -121,7 +186,8 @@ class DNSGA2B(DNSGA2):
121
186
 
122
187
  self.perc_diversity = perc_diversity
123
188
 
124
- def _response_change(self):
189
+ def _response_mechanism(self) -> Population:
190
+ """Response mechanism."""
125
191
  pop = self.pop
126
192
  X = pop.get("X")
127
193
 
@@ -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
 
@@ -7,13 +7,18 @@ from pydmoo.algorithms.base.moo.moead import MOEAD
7
7
 
8
8
 
9
9
  class MOEADDE(MOEAD):
10
- """MOEA/D-DE (Updated by Cao).
11
-
12
- It is worth noting that there is a distinct modification in line 28 compared with the original framework of MOEA/D-DE.
13
- The newly generated solution competes with each member from the corresponding mating neighborhood (denoted as Pool in Algorithm 2).
14
- But in the original MOEA/D-DE framework, it only competes with two members from the corresponding mating neighborhood.
15
- This modification expands the replacement neighborhood to enhance the exploitation capability that is extremely important in dealing with DMOPs.
16
-
10
+ """MOEA/D-DE.
11
+
12
+ Notes
13
+ -----
14
+ It is worth noting that there is a distinct modification in line 28 compared with the original framework of
15
+ MOEA/D-DE. The newly generated solution competes with each member from the corresponding mating neighborhood
16
+ (denoted as Pool in Algorithm 2). But in the original MOEA/D-DE framework, it only competes with two members from
17
+ the corresponding mating neighborhood. This modification expands the replacement neighborhood to enhance the
18
+ exploitation capability that is extremely important in dealing with DMOPs (Cao et al., 2020).
19
+
20
+ References
21
+ ----------
17
22
  Cao, L., Xu, L., Goodman, E. D., Bao, C., and Zhu, S. (2020).
18
23
  Evolutionary dynamic multiobjective optimization assisted by a support vector regression predictor.
19
24
  IEEE Transactions on Evolutionary Computation, 24(2), 305–319.
@@ -21,8 +21,8 @@ class MOEADAE(DMOEAD):
21
21
 
22
22
  super().__init__(**kwargs)
23
23
 
24
- def _response_change(self):
25
- """Response."""
24
+ def _response_mechanism(self):
25
+ """Response mechanism."""
26
26
  pop = self.pop
27
27
  X = pop.get("X")
28
28
 
@@ -25,8 +25,8 @@ class MOEADPPS(DMOEAD):
25
25
  self.p = 3 # the order of the AR model
26
26
  self.M = 23 # the length of history mean point series
27
27
 
28
- def _response_change(self):
29
- """Response."""
28
+ def _response_mechanism(self):
29
+ """Response mechanism."""
30
30
  pop = self.pop
31
31
  X = pop.get("X")
32
32
 
@@ -0,0 +1,86 @@
1
+ import numpy as np
2
+ from pymoo.core.population import Population
3
+ from sklearn.svm import SVR
4
+
5
+ from pydmoo.algorithms.base.dmoo.dmoead import DMOEAD
6
+
7
+
8
+ class MOEADSVR(DMOEAD):
9
+ """Support Vector Regression (SVR).
10
+
11
+ Notes
12
+ -----
13
+ [Official Python Code](https://github.com/LeileiCao/MOEA-D-SVR/blob/master/MOEAD-SVR%20.py)
14
+
15
+ References
16
+ ----------
17
+ Cao, L., Xu, L., Goodman, E. D., Bao, C., and Zhu, S. (2020).
18
+ Evolutionary dynamic multiobjective optimization assisted by a support vector regression predictor.
19
+ IEEE Transactions on Evolutionary Computation, 24(2), 305–319.
20
+ https://doi.org/10.1109/TEVC.2019.2925722
21
+ """
22
+
23
+ def __init__(self, **kwargs):
24
+ super().__init__(**kwargs)
25
+
26
+ # SVR
27
+ self._q = 4 # the number of preceding values that are correlated with the target value (dimension of input samples in the SVR model)
28
+ self._C = 1000 # the regularization constant in SVR model
29
+ self._epsilon = 0.05 # the insensitive tube size in SVR model
30
+ # self._gamma = 1/d # the Gaussian RBF kernel parameter used in SVR model, and d is the number of variables
31
+
32
+ def _response_mechanism(self):
33
+ """Response mechanism."""
34
+ pop = self.pop
35
+ X = pop.get("X")
36
+
37
+ old = self.data.get("stacked_X", None)
38
+ if old is None:
39
+ stacked_X = np.expand_dims(X, axis=0)
40
+ else:
41
+ stacked_X = np.concatenate((old, np.expand_dims(X, axis=0)), axis=0)
42
+ self.data["stacked_X"] = stacked_X
43
+
44
+ N, d = X.shape
45
+ sol = np.zeros((N, d))
46
+ K = len(stacked_X)
47
+
48
+ if K < self._q + 2:
49
+ # recreate the current population without being evaluated
50
+ # Re-evaluate the current population, and update the reference point
51
+ pop = Population.new(X=X)
52
+
53
+ return pop
54
+
55
+ # Precompute sliding window indices to avoid redundant calculations
56
+ window_indices = np.lib.stride_tricks.sliding_window_view(np.arange(K), self._q + 1)
57
+
58
+ for i in range(N):
59
+ for j in range(d):
60
+ # Extract the time series for this (i,j) position
61
+ ts = stacked_X[:K, i, j]
62
+
63
+ # Create training data using vectorized sliding windows
64
+ train = ts[window_indices]
65
+ x_train = train[:, :-1]
66
+ y_train = train[:, -1]
67
+
68
+ # Train SVR model (consider moving this outside loops if possible)
69
+ # gamma if 'auto', uses 1 / n_features (not provided in code but provided in paper)
70
+ # versionchanged:: 0.22
71
+ # The default value of ``gamma`` changed from 'auto' to 'scale'.
72
+ svr = SVR(kernel='rbf', epsilon=self._epsilon, C=self._C, gamma=1/d)
73
+ model = svr.fit(x_train, y_train)
74
+
75
+ # Make prediction
76
+ sol[i, j] = model.predict(ts[-self._q:].reshape(1, -1))
77
+
78
+ # bounds
79
+ if self.problem.has_bounds():
80
+ xl, xu = self.problem.bounds()
81
+ sol = np.clip(sol, xl, xu) # provided in the original reference literature
82
+
83
+ # recreate the current population without being evaluated
84
+ pop = Population.new(X=sol)
85
+
86
+ return pop
@@ -21,8 +21,8 @@ class MOEADDEAE(DMOEADDE):
21
21
 
22
22
  super().__init__(**kwargs)
23
23
 
24
- def _response_change(self):
25
- """Response."""
24
+ def _response_mechanism(self):
25
+ """Response mechanism."""
26
26
  pop = self.pop
27
27
  X = pop.get("X")
28
28
 
@@ -25,8 +25,8 @@ class MOEADDEPPS(DMOEADDE):
25
25
  self.p = 3 # the order of the AR model
26
26
  self.M = 23 # the length of history mean point series
27
27
 
28
- def _response_change(self):
29
- """Response."""
28
+ def _response_mechanism(self):
29
+ """Response mechanism."""
30
30
  pop = self.pop
31
31
  X = pop.get("X")
32
32
 
@@ -0,0 +1,86 @@
1
+ import numpy as np
2
+ from pymoo.core.population import Population
3
+ from sklearn.svm import SVR
4
+
5
+ from pydmoo.algorithms.base.dmoo.dmoeadde import DMOEADDE
6
+
7
+
8
+ class MOEADDESVR(DMOEADDE):
9
+ """Support Vector Regression (SVR).
10
+
11
+ Notes
12
+ -----
13
+ [Official Python Code](https://github.com/LeileiCao/MOEA-D-SVR/blob/master/MOEAD-SVR%20.py)
14
+
15
+ References
16
+ ----------
17
+ Cao, L., Xu, L., Goodman, E. D., Bao, C., and Zhu, S. (2020).
18
+ Evolutionary dynamic multiobjective optimization assisted by a support vector regression predictor.
19
+ IEEE Transactions on Evolutionary Computation, 24(2), 305–319.
20
+ https://doi.org/10.1109/TEVC.2019.2925722
21
+ """
22
+
23
+ def __init__(self, **kwargs):
24
+ super().__init__(**kwargs)
25
+
26
+ # SVR
27
+ self._q = 4 # the number of preceding values that are correlated with the target value (dimension of input samples in the SVR model)
28
+ self._C = 1000 # the regularization constant in SVR model
29
+ self._epsilon = 0.05 # the insensitive tube size in SVR model
30
+ # self._gamma = 1/d # the Gaussian RBF kernel parameter used in SVR model, and d is the number of variables
31
+
32
+ def _response_mechanism(self):
33
+ """Response mechanism."""
34
+ pop = self.pop
35
+ X = pop.get("X")
36
+
37
+ old = self.data.get("stacked_X", None)
38
+ if old is None:
39
+ stacked_X = np.expand_dims(X, axis=0)
40
+ else:
41
+ stacked_X = np.concatenate((old, np.expand_dims(X, axis=0)), axis=0)
42
+ self.data["stacked_X"] = stacked_X
43
+
44
+ N, d = X.shape
45
+ sol = np.zeros((N, d))
46
+ K = len(stacked_X)
47
+
48
+ if K < self._q + 2:
49
+ # recreate the current population without being evaluated
50
+ # Re-evaluate the current population, and update the reference point
51
+ pop = Population.new(X=X)
52
+
53
+ return pop
54
+
55
+ # Precompute sliding window indices to avoid redundant calculations
56
+ window_indices = np.lib.stride_tricks.sliding_window_view(np.arange(K), self._q + 1)
57
+
58
+ for i in range(N):
59
+ for j in range(d):
60
+ # Extract the time series for this (i,j) position
61
+ ts = stacked_X[:K, i, j]
62
+
63
+ # Create training data using vectorized sliding windows
64
+ train = ts[window_indices]
65
+ x_train = train[:, :-1]
66
+ y_train = train[:, -1]
67
+
68
+ # Train SVR model (consider moving this outside loops if possible)
69
+ # gamma if 'auto', uses 1 / n_features (not provided in code but provided in paper)
70
+ # versionchanged:: 0.22
71
+ # The default value of ``gamma`` changed from 'auto' to 'scale'.
72
+ svr = SVR(kernel='rbf', epsilon=self._epsilon, C=self._C, gamma=1/d)
73
+ model = svr.fit(x_train, y_train)
74
+
75
+ # Make prediction
76
+ sol[i, j] = model.predict(ts[-self._q:].reshape(1, -1))[0]
77
+
78
+ # bounds
79
+ if self.problem.has_bounds():
80
+ xl, xu = self.problem.bounds()
81
+ sol = np.clip(sol, xl, xu) # provided in the original reference literature
82
+
83
+ # recreate the current population without being evaluated
84
+ pop = Population.new(X=sol)
85
+
86
+ return pop