Constrained Optimization Example

This script demonstrates the optimization of a constrained problem using Multi-Objective Bayesian Optimization.

It leverages the QPOTS framework to optimize the Disc Brake problem, a multi-objective problem with constraints.

Overview

  • Uses BoTorch and PyTorch for Gaussian Process (GP) modeling.

  • Implements Pareto Optimal Thompson Sampling (QPOTS) for optimization.

  • Evaluates hypervolume (HV) to measure performance.

  • Saves results (candidates, hypervolume, and timing) for post-analysis.

Script Details

constrained_optimization.py
 1"""
 2This file demonstrates an optimization of a constrained problem
 3"""
 4
 5import warnings
 6import time
 7import os
 8import numpy as np
 9
10warnings.filterwarnings('ignore')
11
12from qpots.acquisition import Acquisition
13from qpots.model_object import ModelObject
14from qpots.utils.utils import expected_hypervolume
15from qpots.function import Function
16
17import torch
18from botorch.utils.transforms import unnormalize, normalize
19
20device = torch.device("cpu")
21args = dict(
22        {
23            "ntrain": 40,
24            "iters": 200,
25            "reps": 20,
26            "q": 1,
27            "wd": ".",
28            "ref_point": -1*torch.tensor([5.8, 4.0]),
29            "dim": 4,
30            "nobj": 2,
31            "ncons": 4,
32            "nystrom": 0,
33            "nychoice": "pareto",
34            "ngen": 10,
35        }
36    )
37
38tf = Function('discbrake', dim=args["dim"], nobj=args["nobj"])
39f = tf.evaluate
40bounds = tf.get_bounds()
41cons = tf.get_cons()
42
43os.makedirs(args["wd"], exist_ok=True)
44torch.manual_seed(1023)
45
46train_x = torch.rand([args["ntrain"], args["dim"]], dtype=torch.double)
47train_y = f(unnormalize(train_x, bounds))
48train_y = torch.column_stack([train_y, cons(unnormalize(train_x, bounds))]) # Stack constraints on top of objectives
49
50print(train_y.shape, train_x.shape) # This should be n_train x (nobj + ncons) tensor
51
52gps = ModelObject(train_x=train_x, train_y=train_y, bounds=bounds, nobj=args["nobj"], ncons=args["ncons"], device=device)
53gps.fit_gp()
54
55acq = Acquisition(tf, gps, cons=cons, device=device, q=args["q"])
56
57hvs, times = [], []
58for i in range(args["iters"]):
59    t1 = time.time()
60    newx = acq.qpots(bounds=bounds, iteration=i, **args)
61    t2 = time.time()
62    times.append(t2 - t1)
63
64    newy = f(unnormalize(newx.reshape(-1, args["dim"]), bounds))
65    newconsy = cons(unnormalize(newx.reshape(-1, args["dim"]), bounds))
66    newy = torch.column_stack([newy.reshape(args["q"], args["nobj"]),
67                                newconsy.reshape(args["q"], args["ncons"])])
68    hv, _ = expected_hypervolume(gps, ref_point=args['ref_point'])
69    hvs.append(hv)
70        
71    print(f"Iteration: {i}, New candidate: {newx}, Time: {t2 - t1}, HV: {hv}")
72
73    train_x = torch.row_stack([train_x, newx.view(-1, args["dim"])])
74    train_y = torch.row_stack([train_y, newy])
75    gps = ModelObject(train_x, train_y, bounds, args["nobj"], args["ncons"], device=device)
76    gps.fit_gp()
77
78    np.save(f"{args["wd"]}/train_x.npy", train_x)
79    np.save(f"{args["wd"]}/train_y.npy", train_y)
80    np.save(f"{args["wd"]}/hv.npy", hvs)
81    np.save(f"{args["wd"]}/times.npy", times)
82

Example Output

Iteration: 0, New candidate: tensor([...]), Time: 0.23s, HV: 107.278
Iteration: 1, New candidate: tensor([...]), Time: 0.19s, HV: 123.899
...
Iteration: 199, New candidate: tensor([...]), Time: 0.30s, HV: 151.326

Usage

Run the script using:

python constrained_example.py

Ensure that dependencies such as BoTorch, PyTorch, and PyMoo are installed.