Constrained optimization - SEGO

Constrained optimization - SEGO#

import numpy as np
from matplotlib import pyplot as plt

from smt_optim.benchmarks.registry import get_problem

from smt_optim.core import Driver, ObjectiveConfig, ConstraintConfig, DriverConfig, Problem

from smt_optim.surrogate_models.smt import SmtAutoModel

from smt_optim.acquisition_strategies import MFSEGO

from smt_optim.surrogate_models.smt import SmtGPX

Importing a test function#

problem = get_problem("Branin1")

Constrained 2D optimization#

As with unconstrained optimisation, we must first define the problem. The objective is configured using the ObjectiveConfig data class, and each constraint must be defined using the ConstraintConfig data class.

obj_config = ObjectiveConfig(
    [problem.objective[-1]],
    type="minimize",
    surrogate=SmtGPX,
)

# configure the constraint
cstr_config = ConstraintConfig(
    [problem.constraints[0][-1]],
    upper=0.0,                      # g(x) <= 0
    surrogate=SmtGPX,         # set which GP to model this constraint
)

prob_definition = Problem(
    obj_configs=[obj_config],
    design_space=problem.bounds,    # problem bounds
    cstr_configs=[cstr_config],     # list the constraints
)

opt_config = DriverConfig(
    max_iter = 10,
    nt_init = 6,
    verbose = True,
    scaling = True,
    seed=42,
)

driver = Driver(prob_definition, opt_config, MFSEGO, strategy_kwargs={"n_start":10, "sp_method": "SLSQP"})

state = driver.optimize()
          iter         budget           fmin           rscv       fidelity        gp_time       acq_time
             1              7    6.86090e+01      0.000e+00              1          0.037          0.053
             2              8    1.45399e+01      0.000e+00              1          0.044          0.045
             3              9    7.19746e+00      0.000e+00              1          0.044          0.049
             4             10    7.19746e+00      0.000e+00              1          0.047          0.039
             5             11    6.94315e+00      6.076e-08              1          0.041          0.061
             6             12    6.94314e+00      0.000e+00              1          0.043          0.038
             7             13    6.94314e+00      0.000e+00              1          0.044          0.061
             8             14    6.94314e+00      0.000e+00              1          0.059          0.079
             9             15    6.94314e+00      0.000e+00              1          0.068          0.052
            10             16    6.94314e+00      0.000e+00              1          0.068          0.047

The get_best_sample class method enables us to obtain the optimal sample within a specific constraint tolerance by passing the ctol argument. The code snippet below demonstrates how imposing a stricter tolerance constraint can affect the best sample found.

tolerances = [1.2e-1, 1e-4]

for tol in tolerances:
    sample = state.get_best_sample(ctol=tol)
    print(sample)
======= sample data =======
x =             [0.12899267 0.74173099]
obj =           [2.00404723]
cstr =          [0.10432214]
eval_time =     [4.56100679e-06 5.37984306e-07]
------- meta data -------
iter =     0
budget =     2
fidelity =     0
rscv =     0.10432213569325897
===========================

======= sample data =======
x =             [1.         0.20020248]
obj =           [6.94314067]
cstr =          [-0.00020248]
eval_time =     [1.40369812e-05 1.82198710e-06]
------- meta data -------
iter =     7
budget =     13
fidelity =     0
rscv =     0.0
===========================
# get the best sample in the dataset
sample = state.get_best_sample(ctol=1e-4)
xt = state.dataset.export_as_dict()["x"]

X = np.linspace(0, 1, 201)

XX, YY = np.meshgrid(X, X)

data = np.vstack((XX.ravel(), YY.ravel())).T
z = np.empty(data.shape[0])
c = np.empty(data.shape[0])

for i in range(data.shape[0]):
    z[i] = problem.objective[-1](data[i, :])
    c[i] = problem.constraints[0][-1](data[i, :])

Z = z.reshape(XX.shape)
C = c.reshape(XX.shape)

fig, ax = plt.subplots()

ax.set_title(problem.name)

ax.contour(XX, YY, Z, levels=20)

ax.contourf(XX, YY, np.where(C <= 0, np.nan, C), levels=0, colors="C7", alpha=0.20)
ax.contour(XX, YY, C, levels=[0], colors="C7")

ax.scatter(xt[:, 0], xt[:, 1], 10, color="C7", alpha=0.75, label="DoE")
ax.scatter(sample.x[0], sample.x[1], c="C3", marker="*", label=r"$f_{min}$", zorder=10)

ax.set_xlabel(r"$x_1$")
ax.set_ylabel(r"$x_2$")

ax.legend()
ax.set_aspect("equal")
plt.show()
../../_images/253f78b8497ab8e6a904cc7a1789f9fdfd3f07d0a4bd2fdb4a77b8556a9c95b3.png