Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
RWTH-EBC
GitHub Repository: RWTH-EBC/ebcpy
Path: blob/master/examples/e4_optimization_example.py
505 views
1
"""
2
Goals of this part of the examples:
3
1. Learn how to create a custom `Optimizer` class
4
2. Learn the different optimizer frameworks
5
3. See the difference in optimization when using newton-based methods and evolutionary algorithms.
6
The difference is, that newton based methods (like L-BFGS-B) are vastly faster in both convex and
7
concave problems, but they are not guaranteed to find the global minimum and can get stuck in local optima.
8
Evolutionary algorithms (like the genetic algorithm) are substantially slower,
9
but they can overcome local optima, as shown in the concave examples.
10
"""
11
12
import time
13
from pprint import pformat
14
15
# Start by importing all relevant packages
16
import matplotlib.pyplot as plt
17
import numpy as np
18
19
# Imports from ebcpy
20
from ebcpy.optimization import Optimizer
21
22
PLT_STANDARD_COLORS = ['go', 'rs', 'c^', 'm+', 'yv', 'k<']
23
24
FRAMEWORK_METHODS = {
25
"scipy_differential_evolution": ("best1bin", {}),
26
"scipy_minimize": ("L-BFGS-B", {"x0": [0.3]}),
27
"dlib_minimize": (None, {"num_function_calls": 1000}),
28
"pymoo": ("ga", {"verbose": False}),
29
"bayesian_optimization": (None, {"xi": 1.2,
30
"kind_of_utility_function": "ucb"})
31
}
32
33
34
def main_loop(optimizer: Optimizer,
35
plot_step: callable,
36
n_vars: int):
37
"""
38
Execute the main optimization loop for different frameworks and methods.
39
40
This function iterates through predefined optimization frameworks and methods,
41
applies them to the given optimizer, and records the execution time for each.
42
It also calls a plotting function for each optimization result.
43
44
Args:
45
optimizer (Optimizer): An instance of a custom Optimizer class.
46
plot_step (callable): A function to plot each optimization result.
47
n_vars (int): The number of variables in the optimization problem.
48
49
Returns:
50
None
51
"""
52
summary = {}
53
for i, (framework, method_kwargs) in enumerate(FRAMEWORK_METHODS.items()):
54
method, kwargs = method_kwargs
55
if method == "L-BFGS-B":
56
kwargs["x0"] = n_vars * [-1]
57
optimizer.logger.info(f"Optimizing framework {framework} with method {method}")
58
try:
59
start = time.perf_counter()
60
res = optimizer.optimize(framework=framework, method=method, **kwargs)
61
dur = time.perf_counter() - start
62
optimizer.logger.info(f"Optimization took {dur} seconds")
63
summary[framework] = dur
64
except ImportError as err:
65
optimizer.logger.error(f"Could not optimize due to import error: {err}")
66
continue
67
plot_step(res, framework, method, i)
68
69
optimizer.logger.info(f"Optimization times summary:\n{pformat(summary)}")
70
71
72
def concave_1d_example(with_plot=True):
73
"""
74
Run the main optimization routine for a 1D concave problem.
75
76
This function defines a 1D concave optimization problem, sets up the optimizer,
77
and runs the optimization using various frameworks. It also plots the results
78
if specified.
79
80
Args:
81
with_plot (bool, optional): Whether to display the plot. Defaults to True.
82
83
Returns:
84
None
85
"""
86
87
class ConcaveProblemOptimizer1D(Optimizer):
88
"""
89
Define a custom Optimizer by inheriting.
90
This optimizer tries to find the minimum for the function
91
f(x) = -1*(exp(-(x - 2) ** 2) + exp(-(x - 6) ** 2 / 10) + 1/ (x ** 2 + 1)), which is an arbitrary
92
concave function.
93
"""
94
95
def __init__(self, **kwargs):
96
"""
97
Theoretically, additional data which is needed for the optimization can be passed here and
98
stored as class attributes. Not necessary for this example.
99
"""
100
super().__init__(**kwargs)
101
102
def obj(self, xk, *args):
103
return -1 * (np.exp(-(xk[0] - 2) ** 2) + np.exp(-(xk[0] - 6) ** 2 / 10) + 1 / (xk[0] ** 2 + 1))
104
105
bounds = [(-2, 10)]
106
x_area = np.linspace(-2, 10, 1000).reshape(1, -1)
107
cpo = ConcaveProblemOptimizer1D(bounds=bounds)
108
y = cpo.obj(x_area)
109
plt.figure()
110
plt.plot(x_area.flatten(), y)
111
112
def concav_1d_plot_step(res, framework, method, i):
113
plt.plot(res.x, res.fun, PLT_STANDARD_COLORS[i], label=f"{framework}: {method}")
114
115
main_loop(optimizer=cpo,
116
plot_step=concav_1d_plot_step,
117
n_vars=len(bounds))
118
119
if with_plot:
120
plt.title("1D Concave Problem Optimization")
121
plt.xlabel("X")
122
plt.ylabel("objective function value")
123
plt.legend()
124
plt.show()
125
126
127
def convex_1d_example(with_plot=True):
128
"""
129
Run the main optimization routine for a 1D convex problem.
130
131
This function defines a 1D convex optimization problem, sets up the optimizer,
132
and runs the optimization using various frameworks. It also plots the results
133
if specified.
134
135
Args:
136
with_plot (bool, optional): Whether to display the plot. Defaults to True.
137
138
Returns:
139
None
140
"""
141
142
class ConvexProblemOptimizer1D(Optimizer):
143
"""
144
Define a custom Optimizer by inheriting.
145
This optimizer tries to find the minimum for the function
146
f(x) = (x - 3)**2 + 2, which is an arbitrary convex function.
147
"""
148
149
def __init__(self, **kwargs):
150
"""
151
Theoretically, additional data which is needed for the optimization can be passed here and
152
stored as class attributes. Not necessary for this example.
153
"""
154
super().__init__(**kwargs)
155
156
def obj(self, xk, *args):
157
return (xk[0] - 3) ** 2 + 2
158
159
bounds = [(-5, 10)]
160
x_area = np.linspace(-2, 10, 1000).reshape(1, -1)
161
cpo = ConvexProblemOptimizer1D(bounds=bounds)
162
y = cpo.obj(x_area)
163
plt.figure()
164
plt.plot(x_area.flatten(), y)
165
166
def convex_1d_plot_step(res, framework, method, i):
167
plt.plot(res.x, res.fun, PLT_STANDARD_COLORS[i], label=f"{framework}: {method}")
168
169
main_loop(optimizer=cpo,
170
plot_step=convex_1d_plot_step,
171
n_vars=len(bounds))
172
173
if with_plot:
174
plt.title("1D Convex Problem Optimization")
175
plt.xlabel("X")
176
plt.ylabel("objective function value")
177
plt.legend()
178
plt.show()
179
180
181
def concave_2d_example(with_plot=True):
182
"""
183
Run the main optimization routine for a 2D concave problem.
184
185
This function defines a 2D concave optimization problem, sets up the optimizer,
186
and runs the optimization using various frameworks. It also plots the results
187
if specified.
188
189
Args:
190
with_plot (bool, optional): Whether to display the plot. Defaults to True.
191
192
Returns:
193
None
194
"""
195
196
class ConcaveProblemOptimizer2D(Optimizer):
197
"""
198
Define a custom Optimizer by inheriting.
199
This optimizer tries to find the minimum for the function
200
f=(x, y) = -1*(exp(-(x - 2)**2 - (y - 2)**2) + exp(-((x - 6)**2 / 10) -
201
((y - 6)**2 / 10)) + 1 / (x**2 + y**2 + 1)),
202
which is an arbitrary concave function.
203
"""
204
205
def __init__(self, **kwargs):
206
"""
207
Theoretically, additional data which is needed for the optimization can be passed here and
208
stored as class attributes. Not necessary for this example.
209
"""
210
super().__init__(**kwargs)
211
212
def obj(self, xk, *args):
213
x, y = xk
214
return -1 * (np.exp(-(x - 2) ** 2 - (y - 2) ** 2) +
215
np.exp(-((x - 6) ** 2 / 10) - ((y - 6) ** 2 / 10)) +
216
1 / (x ** 2 + y ** 2 + 1))
217
218
bounds = [(-2, 10), (-2, 10)]
219
x_area = np.linspace(-2, 10, 100)
220
y_area = np.linspace(-2, 10, 100)
221
X, Y = np.meshgrid(x_area, y_area)
222
cpo = ConcaveProblemOptimizer2D(bounds=bounds)
223
Z = np.array([cpo.obj([x, y]) for x, y in zip(X.flatten(), Y.flatten())]).reshape(X.shape)
224
225
plt.figure(figsize=(12, 10))
226
contour = plt.contour(X, Y, Z, levels=20)
227
plt.colorbar(contour)
228
229
def concav_2d_plot_step(res, framework, method, i):
230
plt.plot(res.x[0], res.x[1], PLT_STANDARD_COLORS[i],
231
markersize=10, label=f"{framework}: {method} (obj: {res.fun:.2f})")
232
233
main_loop(optimizer=cpo,
234
plot_step=concav_2d_plot_step,
235
n_vars=len(bounds))
236
237
if with_plot:
238
plt.title("2D Concave Problem Optimization")
239
plt.xlabel("X")
240
plt.ylabel("Y")
241
plt.legend()
242
plt.show()
243
244
245
def convex_2d_example(with_plot=True):
246
"""
247
Run the main optimization routine for a 2D convex problem.
248
249
This function defines a 2D convex optimization problem, sets up the optimizer,
250
and runs the optimization using various frameworks. It also plots the results
251
if specified.
252
253
Args:
254
with_plot (bool, optional): Whether to display the plot. Defaults to True.
255
256
Returns:
257
None
258
"""
259
260
class ConvexProblemOptimizer2D(Optimizer):
261
"""
262
Define a custom Optimizer by inheriting.
263
This optimizer tries to find the minimum for the function
264
f=(x, y) = (x - 2)**2 + (y - 3)**2 + x*y,
265
which is an arbitrary convex function.
266
"""
267
268
def __init__(self, **kwargs):
269
"""
270
Theoretically, additional data which is needed for the optimization can be passed here and
271
stored as class attributes. Not necessary for this example.
272
"""
273
super().__init__(**kwargs)
274
275
def obj(self, xk, *args):
276
x, y = xk
277
return (x - 2) ** 2 + (y - 3) ** 2 + x * y
278
279
bounds = [(-5, 10), (-5, 10)]
280
x_area = np.linspace(-5, 10, 100)
281
y_area = np.linspace(-5, 10, 100)
282
X, Y = np.meshgrid(x_area, y_area)
283
cpo = ConvexProblemOptimizer2D(bounds=bounds)
284
Z = np.array([cpo.obj([x, y]) for x, y in zip(X.flatten(), Y.flatten())]).reshape(X.shape)
285
286
plt.figure(figsize=(12, 10))
287
contour = plt.contour(X, Y, Z, levels=20)
288
plt.colorbar(contour)
289
290
def convex_2d_plot_step(res, framework, method, i):
291
plt.plot(res.x[0], res.x[1], PLT_STANDARD_COLORS[i],
292
markersize=10, label=f"{framework}: {method} (obj: {res.fun:.2f})")
293
294
main_loop(optimizer=cpo,
295
plot_step=convex_2d_plot_step,
296
n_vars=len(bounds))
297
298
if with_plot:
299
plt.title("2D Convex Problem Optimization")
300
plt.xlabel("X")
301
plt.ylabel("Y")
302
plt.legend()
303
plt.show()
304
305
306
def main(with_plot=True):
307
convex_1d_example(with_plot=with_plot)
308
concave_1d_example(with_plot=with_plot)
309
convex_2d_example(with_plot=with_plot)
310
concave_2d_example(with_plot=with_plot)
311
312
313
if __name__ == '__main__':
314
main()
315
316