Time dependent upper confidence bound
Time dependent Bayesian OptimizationĀ¶
In this example we demonstrate time dependent optimization. In this case we are not only interested in finding an optimum point in input space, but also maintain the ideal point over time.
InĀ [1]:
Copied!
# set values if testing
import os
SMOKE_TEST = os.environ.get("SMOKE_TEST")
N_MC_SAMPLES = 1 if SMOKE_TEST else 128
NUM_RESTARTS = 1 if SMOKE_TEST else 20
from xopt.generators.bayesian.upper_confidence_bound import TDUpperConfidenceBoundGenerator
from xopt.vocs import VOCS
from xopt.evaluator import Evaluator
import warnings
warnings.filterwarnings("ignore")
# set values if testing
import os
SMOKE_TEST = os.environ.get("SMOKE_TEST")
N_MC_SAMPLES = 1 if SMOKE_TEST else 128
NUM_RESTARTS = 1 if SMOKE_TEST else 20
from xopt.generators.bayesian.upper_confidence_bound import TDUpperConfidenceBoundGenerator
from xopt.vocs import VOCS
from xopt.evaluator import Evaluator
import warnings
warnings.filterwarnings("ignore")
Time dependent test problemĀ¶
Optimization is carried out over a single variable x
. The test function is a simple
quadratic, with a minimum location that drifts in the positive x
direction over
(real) time.
InĀ [2]:
Copied!
# test evaluate function and vocs
import time
from xopt import Xopt
start_time = time.time()
def f(inputs):
x_ = inputs["x"]
current_time = time.time()
t_ = current_time - start_time
y_ = 5*(x_ - t_*1e-2)**2
return {"y":y_, "time":current_time}
variables = {"x":[-1,1]}
objectives = {"y": "MINIMIZE"}
vocs = VOCS(variables=variables, objectives=objectives)
print(vocs)
evaluator = Evaluator(function=f)
generator = TDUpperConfidenceBoundGenerator(vocs=vocs)
generator.added_time=1.0
generator.beta = 2.0
generator.n_monte_carlo_samples = N_MC_SAMPLES
generator.numerical_optimizer.n_restarts = NUM_RESTARTS
X = Xopt(evaluator=evaluator, generator=generator, vocs=vocs)
X
# test evaluate function and vocs
import time
from xopt import Xopt
start_time = time.time()
def f(inputs):
x_ = inputs["x"]
current_time = time.time()
t_ = current_time - start_time
y_ = 5*(x_ - t_*1e-2)**2
return {"y":y_, "time":current_time}
variables = {"x":[-1,1]}
objectives = {"y": "MINIMIZE"}
vocs = VOCS(variables=variables, objectives=objectives)
print(vocs)
evaluator = Evaluator(function=f)
generator = TDUpperConfidenceBoundGenerator(vocs=vocs)
generator.added_time=1.0
generator.beta = 2.0
generator.n_monte_carlo_samples = N_MC_SAMPLES
generator.numerical_optimizer.n_restarts = NUM_RESTARTS
X = Xopt(evaluator=evaluator, generator=generator, vocs=vocs)
X
variables={'x': [-1.0, 1.0]} constraints={} objectives={'y': 'MINIMIZE'} constants={} observables=[]
Out[2]:
Xopt ________________________________ Version: 0+untagged.1.ge872ea5 Data size: 0 Config as YAML: dump_file: null evaluator: function: __main__.f function_kwargs: {} max_workers: 1 vectorized: false generator: added_time: 1.0 beta: 2.0 computation_time: null fixed_features: null gp_constructor: covar_modules: {} custom_noise_prior: null mean_modules: {} name: time_dependent trainable_mean_keys: [] transform_inputs: true use_low_noise_prior: true log_transform_acquisition_function: false max_travel_distances: null model: null n_candidates: 1 n_interpolate_points: null n_monte_carlo_samples: 128 name: time_dependent_upper_confidence_bound numerical_optimizer: max_iter: 2000 max_time: null n_restarts: 20 name: LBFGS target_prediction_time: null turbo_controller: null use_cuda: false max_evaluations: null serialize_inline: false serialize_torch: false strict: true vocs: constants: {} constraints: {} objectives: y: MINIMIZE observables: [] variables: x: - -1.0 - 1.0
InĀ [3]:
Copied!
X.random_evaluate(1)
for _ in range(20):
# note that in this example we can ignore warnings if computation time is greater
# than added time
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
X.step()
time.sleep(0.1)
print(X.generator.generate(1))
X.random_evaluate(1)
for _ in range(20):
# note that in this example we can ignore warnings if computation time is greater
# than added time
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
X.step()
time.sleep(0.1)
print(X.generator.generate(1))
[{'x': 0.23542563850185458}]
InĀ [4]:
Copied!
X.data
X.data
Out[4]:
x | y | time | xopt_runtime | xopt_error | |
---|---|---|---|---|---|
0 | 0.053185 | 0.013868 | 1.713974e+09 | 0.000007 | False |
1 | -1.000000 | 5.106040 | 1.713974e+09 | 0.000007 | False |
2 | 0.853468 | 3.460214 | 1.713974e+09 | 0.000007 | False |
3 | -0.045272 | 0.030330 | 1.713974e+09 | 0.000007 | False |
4 | 0.229456 | 0.172622 | 1.713974e+09 | 0.000006 | False |
5 | 0.049002 | 0.000161 | 1.713974e+09 | 0.000007 | False |
6 | -0.108561 | 0.151867 | 1.713974e+09 | 0.000006 | False |
7 | 0.144008 | 0.022619 | 1.713974e+09 | 0.000007 | False |
8 | 0.034281 | 0.014312 | 1.713974e+09 | 0.000006 | False |
9 | 0.137197 | 0.007367 | 1.713974e+09 | 0.000006 | False |
10 | 0.073956 | 0.006439 | 1.713974e+09 | 0.000007 | False |
11 | 0.189108 | 0.023274 | 1.713974e+09 | 0.000007 | False |
12 | 0.108551 | 0.002729 | 1.713974e+09 | 0.000007 | False |
13 | 0.150314 | 0.000271 | 1.713974e+09 | 0.000006 | False |
14 | 0.109263 | 0.010000 | 1.713974e+09 | 0.000007 | False |
15 | 0.203028 | 0.007224 | 1.713974e+09 | 0.000006 | False |
16 | 0.156583 | 0.001895 | 1.713974e+09 | 0.000006 | False |
17 | 0.198431 | 0.000644 | 1.713974e+09 | 0.000007 | False |
18 | 0.160989 | 0.006890 | 1.713974e+09 | 0.000006 | False |
19 | 0.243976 | 0.006067 | 1.713974e+09 | 0.000006 | False |
20 | 0.201211 | 0.001797 | 1.713974e+09 | 0.000007 | False |
InĀ [5]:
Copied!
# plot model
import torch
from matplotlib import pyplot as plt # plot model predictions
data = X.data
xbounds = generator.vocs.bounds
tbounds = [data["time"].min(), data["time"].max()]
def gt(inpts):
return 5*(inpts[:,1] - (inpts[:,0] - start_time)*1e-2)**2
model = X.generator.model
n = 200
t = torch.linspace(*tbounds, n, dtype=torch.double)
x = torch.linspace(*xbounds.flatten(), n, dtype=torch.double)
tt, xx = torch.meshgrid(t, x)
pts = torch.hstack([ele.reshape(-1, 1) for ele in (tt, xx)]).double()
tt, xx = tt.numpy(), xx.numpy()
#NOTE: the model inputs are such that t is the last dimension
gp_pts = torch.flip(pts, dims=[-1])
gt_vals = gt(pts)
with torch.no_grad():
post = model.posterior(gp_pts)
mean = post.mean
std = torch.sqrt(post.variance)
fig, ax = plt.subplots()
ax.set_title("model mean")
ax.set_xlabel("unix time")
ax.set_ylabel("x")
c = ax.pcolor(tt, xx, mean.reshape(n,n))
fig.colorbar(c)
fig2, ax2 = plt.subplots()
ax2.set_title("model uncertainty")
ax2.set_xlabel("unix time")
ax2.set_ylabel("x")
c = ax2.pcolor(tt, xx, std.reshape(n,n))
fig2.colorbar(c)
ax.plot(data["time"].to_numpy(), data["x"].to_numpy(),"oC1")
ax2.plot(data["time"].to_numpy(), data["x"].to_numpy(),"oC1")
fig3, ax3 = plt.subplots()
ax3.set_title("ground truth value")
ax3.set_xlabel("unix time")
ax3.set_ylabel("x")
c = ax3.pcolor(tt, xx, gt_vals.reshape(n,n))
fig3.colorbar(c)
# plot model
import torch
from matplotlib import pyplot as plt # plot model predictions
data = X.data
xbounds = generator.vocs.bounds
tbounds = [data["time"].min(), data["time"].max()]
def gt(inpts):
return 5*(inpts[:,1] - (inpts[:,0] - start_time)*1e-2)**2
model = X.generator.model
n = 200
t = torch.linspace(*tbounds, n, dtype=torch.double)
x = torch.linspace(*xbounds.flatten(), n, dtype=torch.double)
tt, xx = torch.meshgrid(t, x)
pts = torch.hstack([ele.reshape(-1, 1) for ele in (tt, xx)]).double()
tt, xx = tt.numpy(), xx.numpy()
#NOTE: the model inputs are such that t is the last dimension
gp_pts = torch.flip(pts, dims=[-1])
gt_vals = gt(pts)
with torch.no_grad():
post = model.posterior(gp_pts)
mean = post.mean
std = torch.sqrt(post.variance)
fig, ax = plt.subplots()
ax.set_title("model mean")
ax.set_xlabel("unix time")
ax.set_ylabel("x")
c = ax.pcolor(tt, xx, mean.reshape(n,n))
fig.colorbar(c)
fig2, ax2 = plt.subplots()
ax2.set_title("model uncertainty")
ax2.set_xlabel("unix time")
ax2.set_ylabel("x")
c = ax2.pcolor(tt, xx, std.reshape(n,n))
fig2.colorbar(c)
ax.plot(data["time"].to_numpy(), data["x"].to_numpy(),"oC1")
ax2.plot(data["time"].to_numpy(), data["x"].to_numpy(),"oC1")
fig3, ax3 = plt.subplots()
ax3.set_title("ground truth value")
ax3.set_xlabel("unix time")
ax3.set_ylabel("x")
c = ax3.pcolor(tt, xx, gt_vals.reshape(n,n))
fig3.colorbar(c)
InĀ [6]:
Copied!
list(model.named_parameters())
list(model.named_parameters())
Out[6]:
[('models.0.likelihood.noise_covar.raw_noise', Parameter containing: tensor([-24.7060], dtype=torch.float64, requires_grad=True)), ('models.0.mean_module.raw_constant', Parameter containing: tensor(2.4816, dtype=torch.float64, requires_grad=True)), ('models.0.covar_module.raw_outputscale', Parameter containing: tensor(2.3825, dtype=torch.float64, requires_grad=True)), ('models.0.covar_module.base_kernel.raw_lengthscale', Parameter containing: tensor([[-0.5835, 1.3818]], dtype=torch.float64, requires_grad=True))]
InĀ [7]:
Copied!
# plot the acquisition function
# note that target time is only updated during the generate call
target_time = X.generator.target_prediction_time
print(target_time-start_time)
my_acq_func = X.generator.get_acquisition(model)
with torch.no_grad():
acq_pts = x.unsqueeze(-1).unsqueeze(-1)
full_acq = my_acq_func.acq_func(gp_pts.unsqueeze(1))
fixed_acq = my_acq_func(acq_pts)
fig, ax = plt.subplots()
c = ax.pcolor(tt, xx, full_acq.reshape(n,n))
fig.colorbar(c)
fi2, ax2 = plt.subplots()
ax2.plot(x.flatten(), fixed_acq.flatten())
# plot the acquisition function
# note that target time is only updated during the generate call
target_time = X.generator.target_prediction_time
print(target_time-start_time)
my_acq_func = X.generator.get_acquisition(model)
with torch.no_grad():
acq_pts = x.unsqueeze(-1).unsqueeze(-1)
full_acq = my_acq_func.acq_func(gp_pts.unsqueeze(1))
fixed_acq = my_acq_func(acq_pts)
fig, ax = plt.subplots()
c = ax.pcolor(tt, xx, full_acq.reshape(n,n))
fig.colorbar(c)
fi2, ax2 = plt.subplots()
ax2.plot(x.flatten(), fixed_acq.flatten())
23.118663787841797
InĀ [7]:
Copied!