botorch
botorch copied to clipboard
[Bug] batch_initial_conditions must be given if there are non-linear inequality constraints
🐛 Bug
In the for loop of function optimize_acqf in optimize.py, batch_initial_conditions is set to None when passing arguments, which introduces the error batch_initial_conditions must be given if there are non-linear inequality constraints
Thanks for flagging this! Looks like we ignore batch_initial_conditions
whenever using sequential optimization (pointer). And this doesn't play well with non-linear constraints since we require batch_initial_conditions
. Two easy fixes here: Either reuse the same batch_initial_conditions
or do not support sequential
with non-linear constraints. Thoughts @Balandat, @dme65 ?
Also, I don't think we should be quietly throwing away batch_initial_conditions
in either case. So, reusing batch_initial_conditions
or requiring a batch x q x d
batch_initial_conditions
and chopping it up over the q
dimension and feeding it through that way seems like a better solution to me.
Related discussion here: https://github.com/pytorch/botorch/issues/1326
tl;dr is that it's really hard to come up with a generic initializer for generic nonlinear constraints, so I don't think we should support that.
From that issue:
I just pushed https://github.com/Balandat/botorch/commit/fedd2e7c2ca4af6d16f0dafab2983835a62225c3 which allows you to use a ic_gen kwarg to optimize_acqf that allows you to implement your own custom initial condition generator. The API of that will need to comply with that of gen_batch_initial_conditions.
So this would allow people to still use custom initializers if they want to implement them themselves. We'll also need to update the logic so as to pass down batch_initial_conditions
in the sequential optimization.
Thanks for your @saitcakmak @Balandat reply!
I am wondering if it works when I use a for loop and my old candidates as batch_initial_conditions to generate a series of candidates instead of using the option sequence = true.
The results I got is that the simulation kept generating the same 10 candidates when I iterated Bayesian optimization. My first question is if I should not generate 10 candidates with a for loop (code below). Keeping generating the same 10 candidates means that the acquisition function ended up choosing the batch_initial_conditions (old candidates) as the candidates. So my second question is if this is caused by the fact I set num_restarts=1 where the gradient found a local minimum.
In the acquisition function, I have
new_x = []
for k in range(BATCH_SIZE):
# train_x = torch.cat([train_x, new_x]), use much older train_x to generate initial seeds.
X_init = train_x[k+i*BATCH_SIZE:k+1+i*BATCH_SIZE].unsqueeze(0)
candidates, _ = optimize_acqf(
acq_function=acq_func,
bounds=standard_bounds,
q=1,
batch_initial_conditions = X_init,
num_restarts=1, #Number of starting points for multistart acquisition function optimization
raw_samples=RAW_SAMPLES, # used for initialization heuristic
#options={"batch_limit": 1, "maxiter": 200}, # batch_limit: number of restarts
sequential=True,
# constrain absolute values on the normalized values
inequality_constraints=inequality_constraints,
nonlinear_inequality_constraints=[nonlinear_constraints1, nonlinear_constraints2,nonlinear_constraints3]
)
# observe new values
nx = unnormalize(candidates.detach(), bounds=bounds)
new_x.append(nx.detach().numpy()[0])
new_x = np.array(new_x)
The key point in sequential candidate generation is you need to let the acquisition function know about the candidates you already generated. This will make sure that those are taken into account and not generated again, like you observed. You do this by updating the X_pending
to include the points you generated. Here's the bit of code that does that.