API examples#

smt-optim proposes three different APIs, each with its own trade-off between ease of use and customizability. In order of increasing complexity, the three APIs are:

  • Function API

  • Component-based API

  • Ask-Tell API

from smt_optim.benchmarks.registry import get_problem

problem = get_problem("Branin1")

Functional API#

The functional API enables users to initiate an optimization process through a single method call. The following example starts a constrained optimization problem. The minimize method returns a State object containing the optimization dataset (final DoE). Although the minimize method is simple, it offers less flexibility than the other APIs.

from smt_optim import minimize

constraint = [
    {
        "fun": [problem.constraints[0][-1]],
        "upper": 0.0        # the following can also be defined: "lower" or "equal"
    },
]

state = minimize(
    [problem.objective[-1]],
    design_space=problem.bounds,
    constraints=constraint,
    max_budget=15,
    method="mfsego",
    driver_kwargs={"seed": 0},
)

sample = state.get_best_sample(ctol=1e-4)
print(f"best sample = \n{sample}")
          iter         budget           fmin           rscv       fidelity        gp_time       acq_time
             1              6    5.47552e+01      0.000e+00              1          0.402          0.232
             2              7    5.47552e+01      0.000e+00              1          0.460          0.276
             3              8    3.21653e+01      0.000e+00              1          0.417          0.274
             4              9    3.21653e+01      0.000e+00              1          0.350          0.288
             5             10    3.21653e+01      0.000e+00              1          0.423          0.521
             6             11    6.94315e+00      0.000e+00              1          0.319          0.662
             7             12    5.62261e+00      0.000e+00              1          0.314          1.487
             8             13    5.58405e+00      0.000e+00              1          0.380          3.159
             9             14    5.57583e+00      0.000e+00              1          0.364          1.794
            10             15    5.57583e+00      0.000e+00              1          0.342          1.529
best sample = 
======= sample data =======
x =             [0.96792398 0.20662896]
obj =           [5.57583277]
cstr =          [-1.1237681e-06]
eval_time =     [7.57300586e-06 1.34400034e-06]
------- meta data -------
iter =     9
budget =     14
fidelity =     0
rscv =     0.0
===========================

Component-Based API#

The component-based API enables users to define the surrogate model and acquisition strategy. It also makes the driver class available to the user. Note that the minimize method automates the driver and problem initialization, as shown below.

from smt_optim.core import Driver, ObjectiveConfig, ConstraintConfig, DriverConfig, Problem
from smt_optim.surrogate_models import SmtAutoModel
from smt_optim.acquisition_strategies import MFSEGO

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

# configure the constraint
cstr_config = ConstraintConfig(
    [problem.constraints[0][-1]],
    upper=0.0,                      # g(x) <= 0
    surrogate=SmtAutoModel,         # 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 = 100,
    max_budget = 15,
    nt_init = 5,
    verbose = True,
    scaling = True,
    seed=0,
)

driver = Driver(prob_definition, opt_config, MFSEGO)

state = driver.optimize()

sample = state.get_best_sample(ctol=1e-4)
print(f"best sample = \n{sample}")
          iter         budget           fmin           rscv       fidelity        gp_time       acq_time
             1              6    5.47552e+01      0.000e+00              1          0.393          0.233
             2              7    5.47552e+01      0.000e+00              1          0.426          0.272
             3              8    3.21653e+01      0.000e+00              1          0.403          0.292
             4              9    3.21653e+01      0.000e+00              1          0.348          0.287
             5             10    3.21653e+01      0.000e+00              1          0.424          0.516
             6             11    6.94315e+00      0.000e+00              1          0.318          0.652
             7             12    5.62261e+00      0.000e+00              1          0.343          1.371
             8             13    5.58405e+00      0.000e+00              1          0.359          3.125
             9             14    5.57583e+00      0.000e+00              1          0.306          1.715
            10             15    5.57583e+00      0.000e+00              1          0.341          1.452
best sample = 
======= sample data =======
x =             [0.96792398 0.20662896]
obj =           [5.57583277]
cstr =          [-1.1237681e-06]
eval_time =     [7.79800757e-06 1.26300438e-06]
------- meta data -------
iter =     9
budget =     14
fidelity =     0
rscv =     0.0
===========================

Ask-Tell API#

The Ask-Tell API uses the same driver and problem initialization as previously shown. Each iteration is called individually with the iteration class method. In the example below, the last iteration is defined entirely manually, similarly to within the driver’s iteration class method. It provides for the maximum control and customization over the optimization process.

import time

from smt_optim.core import Driver, ObjectiveConfig, ConstraintConfig, DriverConfig, Problem
from smt_optim.surrogate_models import SmtAutoModel
from smt_optim.acquisition_strategies import MFSEGO


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

# configure the constraint
cstr_config = ConstraintConfig(
    [problem.constraints[0][-1]],
    upper=0.0,                      # g(x) <= 0
    surrogate=SmtAutoModel,         # 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 = 100,
    max_budget = 20,
    nt_init = 5,
    verbose = True,
    scaling = True,
    seed=0,
)

driver = Driver(prob_definition, opt_config, MFSEGO)

# ------- initializes state object -------
# generate initial DoE
driver.start_optim()
print(f"Number of samples: {len(driver.state.dataset.samples)}")


# ------- individually called iterations -------
for idx in range(7):
    driver.iteration(driver.state)

print(f"Number of samples: {len(driver.state.dataset.samples)}")


# ------- start of manually defined iteration -------
state = driver.state

# increment iteration counter
state.iter += 1

# scale data
state.scale_dataset(opt_config.scaling)

# build models
state.build_models()

# get infill
t0 = time.perf_counter()
infill = driver.strategy.get_infill(state)
t1 = time.perf_counter()
state.iter_log["acq_opt_time"] = t1 - t0


for i in range(len(infill)):
    if infill[i] is not None:
        infill[i] *= state.x_factor
        infill[i] += state.x_step
        state.iter_log["fidelity"] = i + 1


# evaluate infill points
driver.evaluator.sample_func(infill, state)

# log iteration data
driver.call_loggers(state)
# ------- end of manually defined iteration -------

print(f"Number of samples: {len(driver.state.dataset.samples)}")

sample = state.get_best_sample(ctol=1e-4)
print(f"best sample = \n{sample}")
Number of samples: 5
          iter         budget           fmin           rscv       fidelity        gp_time       acq_time
             1              6    5.47552e+01      0.000e+00              1          0.381          0.222
             2              7    5.47552e+01      0.000e+00              1          0.456          0.281
             3              8    3.21653e+01      0.000e+00              1          0.437          0.324
             4              9    3.21653e+01      0.000e+00              1          0.353          0.274
             5             10    3.21653e+01      0.000e+00              1          0.407          0.487
             6             11    6.94315e+00      0.000e+00              1          0.318          0.661
             7             12    5.62261e+00      0.000e+00              1          0.307          1.374
Number of samples: 12
             8             13    5.58405e+00      0.000e+00              1          0.375          3.310
Number of samples: 13
best sample = 
======= sample data =======
x =             [0.97010966 0.20616457]
obj =           [5.58405051]
cstr =          [-2.23611547e-06]
eval_time =     [1.18830067e-05 1.48700201e-06]
------- meta data -------
iter =     8
budget =     13
fidelity =     0
rscv =     0.0
===========================