moospread 0.1.0__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.
- moospread/__init__.py +3 -0
- moospread/core.py +1881 -0
- moospread/problem.py +193 -0
- moospread/tasks/__init__.py +4 -0
- moospread/tasks/dtlz_torch.py +139 -0
- moospread/tasks/mw_torch.py +274 -0
- moospread/tasks/re_torch.py +394 -0
- moospread/tasks/zdt_torch.py +112 -0
- moospread/utils/__init__.py +8 -0
- moospread/utils/constraint_utils/__init__.py +2 -0
- moospread/utils/constraint_utils/gradient.py +72 -0
- moospread/utils/constraint_utils/mgda_core.py +69 -0
- moospread/utils/constraint_utils/pmgda_solver.py +308 -0
- moospread/utils/constraint_utils/prefs.py +64 -0
- moospread/utils/ditmoo.py +127 -0
- moospread/utils/lhs.py +74 -0
- moospread/utils/misc.py +28 -0
- moospread/utils/mobo_utils/__init__.py +11 -0
- moospread/utils/mobo_utils/evolution/__init__.py +0 -0
- moospread/utils/mobo_utils/evolution/dom.py +60 -0
- moospread/utils/mobo_utils/evolution/norm.py +40 -0
- moospread/utils/mobo_utils/evolution/utils.py +97 -0
- moospread/utils/mobo_utils/learning/__init__.py +0 -0
- moospread/utils/mobo_utils/learning/model.py +40 -0
- moospread/utils/mobo_utils/learning/model_init.py +33 -0
- moospread/utils/mobo_utils/learning/model_update.py +51 -0
- moospread/utils/mobo_utils/learning/prediction.py +116 -0
- moospread/utils/mobo_utils/learning/utils.py +143 -0
- moospread/utils/mobo_utils/lhs_for_mobo.py +243 -0
- moospread/utils/mobo_utils/mobo/__init__.py +0 -0
- moospread/utils/mobo_utils/mobo/acquisition.py +209 -0
- moospread/utils/mobo_utils/mobo/algorithms.py +91 -0
- moospread/utils/mobo_utils/mobo/factory.py +86 -0
- moospread/utils/mobo_utils/mobo/mobo.py +132 -0
- moospread/utils/mobo_utils/mobo/selection.py +182 -0
- moospread/utils/mobo_utils/mobo/solver/__init__.py +5 -0
- moospread/utils/mobo_utils/mobo/solver/moead.py +17 -0
- moospread/utils/mobo_utils/mobo/solver/nsga2.py +10 -0
- moospread/utils/mobo_utils/mobo/solver/parego/__init__.py +1 -0
- moospread/utils/mobo_utils/mobo/solver/parego/parego.py +62 -0
- moospread/utils/mobo_utils/mobo/solver/parego/utils.py +34 -0
- moospread/utils/mobo_utils/mobo/solver/pareto_discovery/__init__.py +1 -0
- moospread/utils/mobo_utils/mobo/solver/pareto_discovery/buffer.py +364 -0
- moospread/utils/mobo_utils/mobo/solver/pareto_discovery/pareto_discovery.py +571 -0
- moospread/utils/mobo_utils/mobo/solver/pareto_discovery/utils.py +168 -0
- moospread/utils/mobo_utils/mobo/solver/solver.py +74 -0
- moospread/utils/mobo_utils/mobo/surrogate_model/__init__.py +2 -0
- moospread/utils/mobo_utils/mobo/surrogate_model/base.py +36 -0
- moospread/utils/mobo_utils/mobo/surrogate_model/gaussian_process.py +177 -0
- moospread/utils/mobo_utils/mobo/surrogate_model/thompson_sampling.py +79 -0
- moospread/utils/mobo_utils/mobo/surrogate_problem.py +44 -0
- moospread/utils/mobo_utils/mobo/transformation.py +106 -0
- moospread/utils/mobo_utils/mobo/utils.py +65 -0
- moospread/utils/mobo_utils/spread_mobo_utils.py +854 -0
- moospread/utils/offline_utils/__init__.py +10 -0
- moospread/utils/offline_utils/handle_task.py +203 -0
- moospread/utils/offline_utils/proxies.py +338 -0
- moospread/utils/spread_utils.py +91 -0
- moospread-0.1.0.dist-info/METADATA +75 -0
- moospread-0.1.0.dist-info/RECORD +63 -0
- moospread-0.1.0.dist-info/WHEEL +5 -0
- moospread-0.1.0.dist-info/licenses/LICENSE +10 -0
- moospread-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This code was originally published by the following individuals for use with
|
|
3
|
+
Scilab:
|
|
4
|
+
Copyright (C) 2012 - 2013 - Michael Baudin
|
|
5
|
+
Copyright (C) 2012 - Maria Christopoulou
|
|
6
|
+
Copyright (C) 2010 - 2011 - INRIA - Michael Baudin
|
|
7
|
+
Copyright (C) 2009 - Yann Collette
|
|
8
|
+
Copyright (C) 2009 - CEA - Jean-Marc Martinez
|
|
9
|
+
|
|
10
|
+
website: forge.scilab.org/index.php/p/scidoe/sourcetree/master/macros
|
|
11
|
+
Much thanks goes to these individuals. It has been converted to Python by
|
|
12
|
+
Abraham Lee.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from scipy import spatial
|
|
17
|
+
from scipy import stats
|
|
18
|
+
from scipy import linalg
|
|
19
|
+
from numpy import ma
|
|
20
|
+
|
|
21
|
+
__all__ = ['lhs']
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def lhs_no_evaluation(n, samples=None, criterion=None, iterations=None, correlation_matrix=None):
|
|
25
|
+
"""
|
|
26
|
+
Generate a latin-hypercube design
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
n : int
|
|
30
|
+
The number of factors to generate samples for
|
|
31
|
+
Optional
|
|
32
|
+
--------
|
|
33
|
+
samples : int
|
|
34
|
+
The number of samples to generate for each factor (Default: n)
|
|
35
|
+
criterion : str
|
|
36
|
+
Allowable values are "center" or "c", "maximin" or "m",
|
|
37
|
+
"centermaximin" or "cm", and "correlation" or "corr". If no value
|
|
38
|
+
given, the design is simply randomized.
|
|
39
|
+
iterations : int
|
|
40
|
+
The number of iterations in the maximin and correlations algorithms
|
|
41
|
+
(Default: 5).
|
|
42
|
+
NOTE: the randomstate argument and related computation are replaced
|
|
43
|
+
randomstate : np.random.RandomState, int
|
|
44
|
+
Random state (or seed-number) which controls the seed and random draws
|
|
45
|
+
correlation_matrix : ndarray
|
|
46
|
+
Enforce correlation between factors (only used in lhsmu)
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
H : 2d-array
|
|
50
|
+
An n-by-samples design matrix that has been normalized so factor values
|
|
51
|
+
are uniformly spaced between zero and one.
|
|
52
|
+
Example
|
|
53
|
+
-------
|
|
54
|
+
A 3-factor design (defaults to 3 samples)::
|
|
55
|
+
>>> lhs(3, random_state=42)
|
|
56
|
+
array([[ 0.12484671, 0.95539205, 0.24399798],
|
|
57
|
+
[ 0.53288616, 0.38533955, 0.86703834],
|
|
58
|
+
[ 0.68602787, 0.31690477, 0.38533151]])
|
|
59
|
+
A 4-factor design with 6 samples::
|
|
60
|
+
>>> lhs(4, samples=6, random_state=42)
|
|
61
|
+
array([[ 0.06242335, 0.19266575, 0.88202411, 0.89439364],
|
|
62
|
+
[ 0.19266977, 0.53538985, 0.53030416, 0.49498498],
|
|
63
|
+
[ 0.71737371, 0.75412607, 0.17634727, 0.71520486],
|
|
64
|
+
[ 0.63874044, 0.85658231, 0.33676408, 0.31102936],
|
|
65
|
+
[ 0.43351917, 0.45134543, 0.12199899, 0.53056742],
|
|
66
|
+
[ 0.93530882, 0.15845238, 0.7386575 , 0.09977641]])
|
|
67
|
+
A 2-factor design with 5 centered samples::
|
|
68
|
+
>>> lhs(2, samples=5, criterion='center', random_state=42)
|
|
69
|
+
array([[ 0.1, 0.9],
|
|
70
|
+
[ 0.5, 0.5],
|
|
71
|
+
[ 0.7, 0.1],
|
|
72
|
+
[ 0.3, 0.7],
|
|
73
|
+
[ 0.9, 0.3]])
|
|
74
|
+
A 3-factor design with 4 samples where the minimum distance between
|
|
75
|
+
all samples has been maximized::
|
|
76
|
+
>>> lhs(3, samples=4, criterion='maximin', random_state=42)
|
|
77
|
+
array([[ 0.69754389, 0.2997106 , 0.96250964],
|
|
78
|
+
[ 0.10585037, 0.09872038, 0.73157522],
|
|
79
|
+
[ 0.25351996, 0.65148999, 0.07337204],
|
|
80
|
+
[ 0.91276926, 0.97873992, 0.42783549]])
|
|
81
|
+
A 4-factor design with 5 samples where the samples are as uncorrelated
|
|
82
|
+
as possible (within 10 iterations)::
|
|
83
|
+
>>> lhs(4, samples=5, criterion='correlation', iterations=10, random_state=42)
|
|
84
|
+
array([[ 0.72088348, 0.05121366, 0.97609357, 0.92487081],
|
|
85
|
+
[ 0.49507404, 0.51265511, 0.00808672, 0.37915272],
|
|
86
|
+
[ 0.22217816, 0.2878673 , 0.24034384, 0.42786629],
|
|
87
|
+
[ 0.91977309, 0.93895699, 0.64061224, 0.14213258],
|
|
88
|
+
[ 0.04719698, 0.70796822, 0.53910322, 0.78857071]])
|
|
89
|
+
"""
|
|
90
|
+
H = None
|
|
91
|
+
|
|
92
|
+
if samples is None:
|
|
93
|
+
samples = n
|
|
94
|
+
|
|
95
|
+
if criterion is not None:
|
|
96
|
+
if not criterion.lower() in ('center', 'c', 'maximin', 'm',
|
|
97
|
+
'centermaximin', 'cm', 'correlation',
|
|
98
|
+
'corr','lhsmu'):
|
|
99
|
+
raise ValueError('Invalid value for "criterion": {}'.format(criterion))
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
H = _lhsclassic(n, samples)
|
|
103
|
+
|
|
104
|
+
if criterion is None:
|
|
105
|
+
criterion = 'center'
|
|
106
|
+
if iterations is None:
|
|
107
|
+
iterations = 5
|
|
108
|
+
|
|
109
|
+
if H is None:
|
|
110
|
+
if criterion.lower() in ('center', 'c'):
|
|
111
|
+
H = _lhscentered(n, samples)
|
|
112
|
+
elif criterion.lower() in ('maximin', 'm'):
|
|
113
|
+
H = _lhsmaximin(n, samples, iterations, 'maximin')
|
|
114
|
+
elif criterion.lower() in ('centermaximin', 'cm'):
|
|
115
|
+
H = _lhsmaximin(n, samples, iterations, 'centermaximin')
|
|
116
|
+
elif criterion.lower() in ('correlation', 'corr'):
|
|
117
|
+
H = _lhscorrelate(n, samples, iterations)
|
|
118
|
+
elif criterion.lower() in ('lhsmu'):
|
|
119
|
+
# as specified by the paper. M is set to 5
|
|
120
|
+
H = _lhsmu(n, samples, correlation_matrix, M=5)
|
|
121
|
+
|
|
122
|
+
return H
|
|
123
|
+
|
|
124
|
+
def _lhsclassic(n, samples):
|
|
125
|
+
# Generate the intervals
|
|
126
|
+
cut = np.linspace(0, 1, samples + 1)
|
|
127
|
+
|
|
128
|
+
# Fill points uniformly in each interval
|
|
129
|
+
u = np.random.random((samples, n))
|
|
130
|
+
a = cut[:samples]
|
|
131
|
+
b = cut[1:samples + 1]
|
|
132
|
+
rdpoints = np.zeros_like(u)
|
|
133
|
+
for j in range(n):
|
|
134
|
+
rdpoints[:, j] = u[:, j]*(b-a) + a
|
|
135
|
+
|
|
136
|
+
# Make the random pairings
|
|
137
|
+
H = np.zeros_like(rdpoints)
|
|
138
|
+
for j in range(n):
|
|
139
|
+
order = np.random.permutation(range(samples))
|
|
140
|
+
H[:, j] = rdpoints[order, j]
|
|
141
|
+
|
|
142
|
+
return H
|
|
143
|
+
|
|
144
|
+
def _lhscentered(n, samples):
|
|
145
|
+
# Generate the intervals
|
|
146
|
+
cut = np.linspace(0, 1, samples + 1)
|
|
147
|
+
|
|
148
|
+
# Fill points uniformly in each interval
|
|
149
|
+
u = np.random.random((samples, n))
|
|
150
|
+
a = cut[:samples]
|
|
151
|
+
b = cut[1:samples + 1]
|
|
152
|
+
_center = (a + b)/2
|
|
153
|
+
|
|
154
|
+
# Make the random pairings
|
|
155
|
+
H = np.zeros_like(u)
|
|
156
|
+
for j in range(n):
|
|
157
|
+
H[:, j] = np.random.permutation(_center)
|
|
158
|
+
|
|
159
|
+
return H
|
|
160
|
+
|
|
161
|
+
def _lhsmaximin(n, samples, iterations, lhstype):
|
|
162
|
+
maxdist = 0
|
|
163
|
+
|
|
164
|
+
# Maximize the minimum distance between points
|
|
165
|
+
for i in range(iterations):
|
|
166
|
+
if lhstype=='maximin':
|
|
167
|
+
Hcandidate = _lhsclassic(n, samples)
|
|
168
|
+
else:
|
|
169
|
+
Hcandidate = _lhscentered(n, samples)
|
|
170
|
+
|
|
171
|
+
d = spatial.distance.pdist(Hcandidate, 'euclidean')
|
|
172
|
+
if maxdist<np.min(d):
|
|
173
|
+
maxdist = np.min(d)
|
|
174
|
+
H = Hcandidate.copy()
|
|
175
|
+
|
|
176
|
+
return H
|
|
177
|
+
|
|
178
|
+
def _lhscorrelate(n, samples, iterations):
|
|
179
|
+
mincorr = np.inf
|
|
180
|
+
|
|
181
|
+
# Minimize the components correlation coefficients
|
|
182
|
+
for i in range(iterations):
|
|
183
|
+
# Generate a random LHS
|
|
184
|
+
Hcandidate = _lhsclassic(n, samples)
|
|
185
|
+
R = np.corrcoef(Hcandidate.T)
|
|
186
|
+
if np.max(np.abs(R[R!=1]))<mincorr:
|
|
187
|
+
mincorr = np.max(np.abs(R-np.eye(R.shape[0])))
|
|
188
|
+
H = Hcandidate.copy()
|
|
189
|
+
|
|
190
|
+
return H
|
|
191
|
+
|
|
192
|
+
def _lhsmu(N, samples=None, corr=None, M=5):
|
|
193
|
+
|
|
194
|
+
if samples is None:
|
|
195
|
+
samples = N
|
|
196
|
+
|
|
197
|
+
I = M*samples
|
|
198
|
+
|
|
199
|
+
rdpoints = np.random.uniform(size=(I, N))
|
|
200
|
+
|
|
201
|
+
dist = spatial.distance.cdist(rdpoints, rdpoints, metric='euclidean')
|
|
202
|
+
D_ij = ma.masked_array(dist, mask=np.identity(I))
|
|
203
|
+
|
|
204
|
+
index_rm = np.zeros(I-samples, dtype=int)
|
|
205
|
+
i = 0
|
|
206
|
+
while i < I-samples:
|
|
207
|
+
order = ma.sort(D_ij, axis=1)
|
|
208
|
+
|
|
209
|
+
avg_dist = ma.mean(order[:, 0:2], axis=1)
|
|
210
|
+
min_l = ma.argmin(avg_dist)
|
|
211
|
+
|
|
212
|
+
D_ij[min_l, :] = ma.masked
|
|
213
|
+
D_ij[:, min_l] = ma.masked
|
|
214
|
+
|
|
215
|
+
index_rm[i] = min_l
|
|
216
|
+
i += 1
|
|
217
|
+
|
|
218
|
+
rdpoints = np.delete(rdpoints, index_rm, axis=0)
|
|
219
|
+
|
|
220
|
+
if(corr is not None):
|
|
221
|
+
#check if covariance matrix is valid
|
|
222
|
+
assert type(corr) == np.ndarray
|
|
223
|
+
assert corr.ndim == 2
|
|
224
|
+
assert corr.shape[0] == corr.shape[1]
|
|
225
|
+
assert corr.shape[0] == N
|
|
226
|
+
|
|
227
|
+
norm_u = stats.norm().ppf(rdpoints)
|
|
228
|
+
L = linalg.cholesky(corr, lower=True)
|
|
229
|
+
|
|
230
|
+
norm_u = np.matmul(norm_u, L)
|
|
231
|
+
|
|
232
|
+
H = stats.norm().cdf(norm_u)
|
|
233
|
+
else:
|
|
234
|
+
H = np.zeros_like(rdpoints, dtype=float)
|
|
235
|
+
rank = np.argsort(rdpoints, axis=0)
|
|
236
|
+
|
|
237
|
+
for l in range(samples):
|
|
238
|
+
low = float(l)/samples
|
|
239
|
+
high = float(l+1)/samples
|
|
240
|
+
|
|
241
|
+
l_pos = rank == l
|
|
242
|
+
H[l_pos] = np.random.uniform(low, high, size=N)
|
|
243
|
+
return H
|
|
File without changes
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy.stats import norm
|
|
4
|
+
from moospread.utils.mobo_utils.mobo.utils import safe_divide, expand
|
|
5
|
+
|
|
6
|
+
'''
|
|
7
|
+
Acquisition functions that define the objectives for surrogate multi-objective problem
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
class Acquisition(ABC):
|
|
11
|
+
'''
|
|
12
|
+
Base class of acquisition function
|
|
13
|
+
'''
|
|
14
|
+
requires_std = False # whether requires std output from surrogate model, set False to avoid unnecessary computation
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args, **kwargs):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def fit(self, X, Y):
|
|
20
|
+
'''
|
|
21
|
+
Fit the parameters of acquisition function from data
|
|
22
|
+
'''
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def evaluate(self, val, calc_gradient=False, calc_hessian=False):
|
|
27
|
+
'''
|
|
28
|
+
Evaluate the output from surrogate model using acquisition function
|
|
29
|
+
Input:
|
|
30
|
+
val: output from surrogate model, storing mean and std of prediction, and their derivatives
|
|
31
|
+
val['F']: mean, shape (N, n_obj)
|
|
32
|
+
val['dF']: gradient of mean, shape (N, n_obj, n_var)
|
|
33
|
+
val['hF']: hessian of mean, shape (N, n_obj, n_var, n_var)
|
|
34
|
+
val['S']: std, shape (N, n_obj)
|
|
35
|
+
val['dS']: gradient of std, shape (N, n_obj, n_var)
|
|
36
|
+
val['hS']: hessian of std, shape (N, n_obj, n_var, n_var)
|
|
37
|
+
Output:
|
|
38
|
+
F: acquisition value, shape (N, n_obj)
|
|
39
|
+
dF: gradient of F, shape (N, n_obj, n_var)
|
|
40
|
+
hF: hessian of F, shape (N, n_obj, n_var, n_var)
|
|
41
|
+
'''
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class IdentityFunc(Acquisition):
|
|
46
|
+
'''
|
|
47
|
+
Identity function
|
|
48
|
+
'''
|
|
49
|
+
def evaluate(self, val, calc_gradient=False, calc_hessian=False):
|
|
50
|
+
F, dF, hF = val['F'], val['dF'], val['hF']
|
|
51
|
+
return F, dF, hF
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PI(Acquisition):
|
|
55
|
+
'''
|
|
56
|
+
Probability of Improvement
|
|
57
|
+
'''
|
|
58
|
+
requires_std = True
|
|
59
|
+
|
|
60
|
+
def __init__(self, *args, **kwargs):
|
|
61
|
+
self.y_min = None
|
|
62
|
+
|
|
63
|
+
def fit(self, X, Y):
|
|
64
|
+
self.y_min = np.min(Y, axis=0)
|
|
65
|
+
|
|
66
|
+
def evaluate(self, val, calc_gradient=False, calc_hessian=False):
|
|
67
|
+
y_mean, y_std = val['F'], val['S']
|
|
68
|
+
z = safe_divide(self.y_min - y_mean, y_std)
|
|
69
|
+
cdf_z = norm.cdf(z)
|
|
70
|
+
F = -cdf_z
|
|
71
|
+
|
|
72
|
+
dF, hF = None, None
|
|
73
|
+
dy_mean, hy_mean, dy_std, hy_std = val['dF'], val['hF'], val['dS'], val['hS']
|
|
74
|
+
|
|
75
|
+
if calc_gradient or calc_hessian:
|
|
76
|
+
dz_y_mean = -safe_divide(1, y_std)
|
|
77
|
+
dz_y_std = -safe_divide(self.y_min - y_mean, y_std ** 2)
|
|
78
|
+
|
|
79
|
+
pdf_z = norm.pdf(z)
|
|
80
|
+
dF_y_mean = -pdf_z * dz_y_mean
|
|
81
|
+
dF_y_std = -pdf_z * dz_y_std
|
|
82
|
+
|
|
83
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
84
|
+
|
|
85
|
+
if calc_gradient:
|
|
86
|
+
dF = dF_y_mean * dy_mean + dF_y_std * dy_std
|
|
87
|
+
|
|
88
|
+
if calc_hessian:
|
|
89
|
+
dpdf_z_z = -z * pdf_z
|
|
90
|
+
dpdf_z_y_mean = dpdf_z_z * dz_y_mean
|
|
91
|
+
dpdf_z_y_std = dpdf_z_z * dz_y_std
|
|
92
|
+
hz_y_std = safe_divide(self.y_min - y_mean, y_std ** 3)
|
|
93
|
+
|
|
94
|
+
hF_y_mean = -dpdf_z_y_mean * dz_y_mean
|
|
95
|
+
hF_y_std = -dpdf_z_y_std * dz_y_std - pdf_z * hz_y_std
|
|
96
|
+
|
|
97
|
+
dy_mean, dy_std = expand(dy_mean), expand(dy_std)
|
|
98
|
+
dy_mean_T, dy_std_T = dy_mean.transpose(0, 1, 3, 2), dy_std.transpose(0, 1, 3, 2)
|
|
99
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
100
|
+
hF_y_mean, hF_y_std = expand(hF_y_mean, (-1, -2)), expand(hF_y_std, (-1, -2))
|
|
101
|
+
|
|
102
|
+
hF = dF_y_mean * hy_mean + dF_y_std * hy_std + \
|
|
103
|
+
hF_y_mean * dy_mean * dy_mean_T + hF_y_std * dy_std * dy_std_T
|
|
104
|
+
|
|
105
|
+
return F, dF, hF
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class EI(Acquisition):
|
|
109
|
+
'''
|
|
110
|
+
Expected Improvement
|
|
111
|
+
'''
|
|
112
|
+
requires_std = True
|
|
113
|
+
|
|
114
|
+
def __init__(self, *args, **kwargs):
|
|
115
|
+
self.y_min = None
|
|
116
|
+
|
|
117
|
+
def fit(self, X, Y):
|
|
118
|
+
self.y_min = np.min(Y, axis=0)
|
|
119
|
+
|
|
120
|
+
def evaluate(self, val, calc_gradient=False, calc_hessian=False):
|
|
121
|
+
y_mean, y_std = val['F'], val['S']
|
|
122
|
+
z = safe_divide(self.y_min - y_mean, y_std)
|
|
123
|
+
pdf_z = norm.pdf(z)
|
|
124
|
+
cdf_z = norm.cdf(z)
|
|
125
|
+
F = -(self.y_min - y_mean) * cdf_z - y_std * pdf_z
|
|
126
|
+
|
|
127
|
+
dF, hF = None, None
|
|
128
|
+
dy_mean, hy_mean, dy_std, hy_std = val['dF'], val['hF'], val['dS'], val['hS']
|
|
129
|
+
|
|
130
|
+
if calc_gradient or calc_hessian:
|
|
131
|
+
dz_y_mean = -safe_divide(1, y_std)
|
|
132
|
+
dz_y_std = -safe_divide(self.y_min - y_mean, y_std ** 2)
|
|
133
|
+
dpdf_z_z = -z * pdf_z
|
|
134
|
+
|
|
135
|
+
dF_y_mean = cdf_z - (self.y_min - y_mean) * pdf_z * dz_y_mean - y_std * dpdf_z_z * dz_y_mean
|
|
136
|
+
dF_y_std = (self.y_min - y_mean) * pdf_z * dz_y_std + pdf_z + y_std * dpdf_z_z * dz_y_std
|
|
137
|
+
|
|
138
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
139
|
+
|
|
140
|
+
if calc_gradient:
|
|
141
|
+
dF = dF_y_mean * dy_mean + dF_y_std * dy_std
|
|
142
|
+
|
|
143
|
+
if calc_hessian:
|
|
144
|
+
dpdf_z_y_mean = dpdf_z_z * dz_y_mean
|
|
145
|
+
dpdf_z_y_std = dpdf_z_z * dz_y_std
|
|
146
|
+
ddpdf_z_z_y_mean = -z * dpdf_z_y_mean - dz_y_mean * pdf_z
|
|
147
|
+
ddpdf_z_z_y_std = -z * dpdf_z_y_std - dz_y_std * pdf_z
|
|
148
|
+
ddz_y_std_y_std = safe_divide(self.y_min - y_mean, y_std ** 3)
|
|
149
|
+
|
|
150
|
+
hF_y_mean = -pdf_z * dz_y_mean - \
|
|
151
|
+
dz_y_mean * pdf_z + (self.y_min - y_mean) * dpdf_z_z * dz_y_mean ** 2 + \
|
|
152
|
+
y_std * dz_y_mean * ddpdf_z_z_y_mean
|
|
153
|
+
hF_y_std = (self.y_min - y_mean) * (dz_y_std * dpdf_z_y_std + pdf_z * ddz_y_std_y_std) + \
|
|
154
|
+
dpdf_z_y_std + \
|
|
155
|
+
dpdf_z_z * dz_y_std + y_std * dz_y_std * ddpdf_z_z_y_std + y_std * dpdf_z_z * ddz_y_std_y_std
|
|
156
|
+
|
|
157
|
+
dy_mean, dy_std = expand(dy_mean), expand(dy_std)
|
|
158
|
+
dy_mean_T, dy_std_T = dy_mean.transpose(0, 1, 3, 2), dy_std.transpose(0, 1, 3, 2)
|
|
159
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
160
|
+
hF_y_mean, hF_y_std = expand(hF_y_mean, (-1, -2)), expand(hF_y_std, (-1, -2))
|
|
161
|
+
|
|
162
|
+
hF = dF_y_mean * hy_mean + dF_y_std * hy_std + \
|
|
163
|
+
hF_y_mean * dy_mean * dy_mean_T + hF_y_std * dy_std * dy_std_T
|
|
164
|
+
|
|
165
|
+
return F, dF, hF
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class UCB(Acquisition):
|
|
169
|
+
'''
|
|
170
|
+
Upper Confidence Bound
|
|
171
|
+
'''
|
|
172
|
+
requires_std = True
|
|
173
|
+
|
|
174
|
+
def __init__(self, *args, **kwargs):
|
|
175
|
+
self.n_sample = None
|
|
176
|
+
|
|
177
|
+
def fit(self, X, Y):
|
|
178
|
+
self.n_sample = X.shape[0]
|
|
179
|
+
|
|
180
|
+
def evaluate(self, val, calc_gradient=False, calc_hessian=False):
|
|
181
|
+
lamda = np.sqrt(np.log(self.n_sample) / self.n_sample)
|
|
182
|
+
|
|
183
|
+
y_mean, y_std = val['F'], val['S']
|
|
184
|
+
F = y_mean - lamda * y_std
|
|
185
|
+
|
|
186
|
+
dF, hF = None, None
|
|
187
|
+
dy_mean, hy_mean, dy_std, hy_std = val['dF'], val['hF'], val['dS'], val['hS']
|
|
188
|
+
|
|
189
|
+
if calc_gradient or calc_hessian:
|
|
190
|
+
dF_y_mean = np.ones_like(y_mean)
|
|
191
|
+
dF_y_std = -lamda * np.ones_like(y_std)
|
|
192
|
+
|
|
193
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
194
|
+
|
|
195
|
+
if calc_gradient:
|
|
196
|
+
dF = dF_y_mean * dy_mean + dF_y_std * dy_std
|
|
197
|
+
|
|
198
|
+
if calc_hessian:
|
|
199
|
+
hF_y_mean = 0
|
|
200
|
+
hF_y_std = 0
|
|
201
|
+
|
|
202
|
+
dy_mean, dy_std = expand(dy_mean), expand(dy_std)
|
|
203
|
+
dy_mean_T, dy_std_T = dy_mean.transpose(0, 1, 3, 2), dy_std.transpose(0, 1, 3, 2)
|
|
204
|
+
dF_y_mean, dF_y_std = expand(dF_y_mean), expand(dF_y_std)
|
|
205
|
+
|
|
206
|
+
hF = dF_y_mean * hy_mean + dF_y_std * hy_std + \
|
|
207
|
+
hF_y_mean * dy_mean * dy_mean_T + hF_y_std * dy_std * dy_std_T
|
|
208
|
+
|
|
209
|
+
return F, dF, hF
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from moospread.utils.mobo_utils.mobo.mobo import MOBO
|
|
2
|
+
|
|
3
|
+
'''
|
|
4
|
+
High-level algorithm specifications by providing config
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
class DGEMO(MOBO):
|
|
8
|
+
'''
|
|
9
|
+
DGEMO
|
|
10
|
+
'''
|
|
11
|
+
config = {
|
|
12
|
+
'surrogate': 'gp',
|
|
13
|
+
'acquisition': 'identity',
|
|
14
|
+
'solver': 'discovery',
|
|
15
|
+
'selection': 'dgemo',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TSEMO(MOBO):
|
|
20
|
+
'''
|
|
21
|
+
TSEMO
|
|
22
|
+
'''
|
|
23
|
+
config = {
|
|
24
|
+
'surrogate': 'ts',
|
|
25
|
+
'acquisition': 'identity',
|
|
26
|
+
'solver': 'nsga2',
|
|
27
|
+
'selection': 'hvi',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class USEMO_EI(MOBO):
|
|
32
|
+
'''
|
|
33
|
+
USeMO, using EI as acquisition
|
|
34
|
+
'''
|
|
35
|
+
config = {
|
|
36
|
+
'surrogate': 'gp',
|
|
37
|
+
'acquisition': 'ei',
|
|
38
|
+
'solver': 'nsga2',
|
|
39
|
+
'selection': 'uncertainty',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MOEAD_EGO(MOBO):
|
|
44
|
+
'''
|
|
45
|
+
MOEA/D-EGO
|
|
46
|
+
'''
|
|
47
|
+
config = {
|
|
48
|
+
'surrogate': 'gp',
|
|
49
|
+
'acquisition': 'ei',
|
|
50
|
+
'solver': 'moead',
|
|
51
|
+
'selection': 'moead',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ParEGO(MOBO):
|
|
56
|
+
'''
|
|
57
|
+
ParEGO
|
|
58
|
+
'''
|
|
59
|
+
config = {
|
|
60
|
+
'surrogate': 'gp',
|
|
61
|
+
'acquisition': 'ei',
|
|
62
|
+
'solver': 'parego',
|
|
63
|
+
'selection': 'random',
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
'''
|
|
68
|
+
Define new algorithms here
|
|
69
|
+
'''
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Custom(MOBO):
|
|
73
|
+
'''
|
|
74
|
+
Totally rely on user arguments to specify each component
|
|
75
|
+
'''
|
|
76
|
+
config = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_algorithm(name):
|
|
80
|
+
'''
|
|
81
|
+
Get class of algorithm by name
|
|
82
|
+
'''
|
|
83
|
+
algo = {
|
|
84
|
+
'dgemo': DGEMO,
|
|
85
|
+
'tsemo': TSEMO,
|
|
86
|
+
'usemo-ei': USEMO_EI,
|
|
87
|
+
'moead-ego': MOEAD_EGO,
|
|
88
|
+
'parego': ParEGO,
|
|
89
|
+
'custom': Custom,
|
|
90
|
+
}
|
|
91
|
+
return algo[name]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Factory for importing different components of the MOBO framework by name
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
def get_surrogate_model(name):
|
|
6
|
+
from moospread.utils.mobo_utils.mobo.surrogate_model import GaussianProcess, ThompsonSampling
|
|
7
|
+
|
|
8
|
+
surrogate_model = {
|
|
9
|
+
'gp': GaussianProcess,
|
|
10
|
+
'ts': ThompsonSampling,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
surrogate_model['default'] = GaussianProcess
|
|
14
|
+
|
|
15
|
+
return surrogate_model[name]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_acquisition(name):
|
|
19
|
+
from moospread.utils.mobo_utils.mobo.acquisition import IdentityFunc, PI, EI, UCB
|
|
20
|
+
|
|
21
|
+
acquisition = {
|
|
22
|
+
'identity': IdentityFunc,
|
|
23
|
+
'pi': PI,
|
|
24
|
+
'ei': EI,
|
|
25
|
+
'ucb': UCB,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
acquisition['default'] = IdentityFunc
|
|
29
|
+
|
|
30
|
+
return acquisition[name]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_solver(name):
|
|
34
|
+
from moospread.utils.mobo_utils.mobo.solver import NSGA2Solver, MOEADSolver, ParetoDiscoverySolver, ParEGOSolver
|
|
35
|
+
|
|
36
|
+
solver = {
|
|
37
|
+
'nsga2': NSGA2Solver,
|
|
38
|
+
'moead': MOEADSolver,
|
|
39
|
+
'discovery': ParetoDiscoverySolver,
|
|
40
|
+
'parego': ParEGOSolver,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
solver['default'] = NSGA2Solver
|
|
44
|
+
|
|
45
|
+
return solver[name]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_selection(name):
|
|
49
|
+
from moospread.utils.mobo_utils.mobo.selection import HVI, Uncertainty, Random, DGEMOSelect, MOEADSelect
|
|
50
|
+
|
|
51
|
+
selection = {
|
|
52
|
+
'hvi': HVI,
|
|
53
|
+
'uncertainty': Uncertainty,
|
|
54
|
+
'random': Random,
|
|
55
|
+
'dgemo': DGEMOSelect,
|
|
56
|
+
'moead': MOEADSelect,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
selection['default'] = HVI
|
|
60
|
+
|
|
61
|
+
return selection[name]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def init_from_config(config, framework_args):
|
|
65
|
+
'''
|
|
66
|
+
Initialize each component of the MOBO framework from config
|
|
67
|
+
'''
|
|
68
|
+
init_func = {
|
|
69
|
+
'surrogate': get_surrogate_model,
|
|
70
|
+
'acquisition': get_acquisition,
|
|
71
|
+
'selection': get_selection,
|
|
72
|
+
'solver': get_solver,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
framework = {}
|
|
76
|
+
for key, func in init_func.items():
|
|
77
|
+
kwargs = framework_args[key]
|
|
78
|
+
if config is None:
|
|
79
|
+
# no config specified, initialize from user arguments
|
|
80
|
+
name = kwargs[key]
|
|
81
|
+
else:
|
|
82
|
+
# initialize from config specifications, if certain keys are not provided, use default settings
|
|
83
|
+
name = config[key] if key in config else 'default'
|
|
84
|
+
framework[key] = func(name)(**kwargs)
|
|
85
|
+
|
|
86
|
+
return framework
|