def test_optimize_acqf_list_empty_list(self): with self.assertRaises(ValueError): optimize_acqf_list( acq_function_list=[], bounds=torch.stack([torch.zeros(3), 4 * torch.ones(3)]), num_restarts=2, raw_samples=10, )
def optimize_qparego_and_get_observation(model, train_obj, sampler): """Samples a set of random weights for each candidate in the batch, performs sequential greedy optimization of the qParEGO acquisition function, and returns a new candidate and observation.""" acq_func_list = [] for _ in range(BATCH_SIZE): weights = sample_simplex(problem.num_objectives, **tkwargs).squeeze() objective = GenericMCObjective( get_chebyshev_scalarization(weights=weights, Y=train_obj)) acq_func = qExpectedImprovement( # pyre-ignore: [28] model=model, objective=objective, best_f=objective(train_obj).max(), sampler=sampler, ) acq_func_list.append(acq_func) # optimize candidates, _ = optimize_acqf_list( acq_function_list=acq_func_list, bounds=standard_bounds, num_restarts=NUM_RESTARTS, raw_samples=RAW_SAMPLES, # used for intialization heuristic options={ "batch_limit": 5, "maxiter": 200 }, ) # observe new values new_x = unnormalize(candidates.detach(), bounds=problem.bounds) new_obj = problem(new_x) return new_x, new_obj
def scipy_optimizer_list( acq_function_list: List[AcquisitionFunction], bounds: Tensor, inequality_constraints: Optional[List[Tuple[Tensor, Tensor, float]]] = None, fixed_features: Optional[Dict[int, float]] = None, rounding_func: Optional[Callable[[Tensor], Tensor]] = None, **kwargs: Any, ) -> Tuple[Tensor, Tensor]: r"""Sequential optimizer using scipy's minimize module on a numpy-adaptor. The ith acquisition in the sequence uses the ith given acquisition_function. Args: acq_function_list: A list of botorch AcquisitionFunctions, optimized sequentially. bounds: A `2 x d`-dim tensor, where `bounds[0]` (`bounds[1]`) are the lower (upper) bounds of the feasible hyperrectangle. n: The number of candidates to generate. inequality constraints: A list of tuples (indices, coefficients, rhs), with each tuple encoding an inequality constraint of the form `\sum_i (X[indices[i]] * coefficients[i]) >= rhs` fixed_features: A map {feature_index: value} for features that should be fixed to a particular value during generation. rounding_func: A function that rounds an optimization result appropriately (i.e., according to `round-trip` transformations). Returns: 2-element tuple containing - A `n x d`-dim tensor of generated candidates. - A `n`-dim tensor of conditional acquisition values, where `i`-th element is the expected acquisition value conditional on having observed candidates `0,1,...,i-1`. """ num_restarts: int = kwargs.get("num_restarts", 20) raw_samples: int = kwargs.get("num_raw_samples", 50 * num_restarts) # use SLSQP by default for small problems since it yields faster wall times if "method" not in kwargs: kwargs["method"] = "SLSQP" X, expected_acquisition_value = optimize_acqf_list( acq_function_list=acq_function_list, bounds=bounds, num_restarts=num_restarts, raw_samples=raw_samples, options=kwargs, inequality_constraints=inequality_constraints, fixed_features=fixed_features, post_processing_func=rounding_func, ) return X, expected_acquisition_value
def optimize_qparego_and_get_observation(model, train_obj, train_con, sampler, obj_func, time_list, global_start_time): """Samples a set of random weights for each candidate in the batch, performs sequential greedy optimization of the qParEGO acquisition function, and returns a new candidate and observation.""" acq_func_list = [] for _ in range(1): # sample random weights weights = sample_simplex(problem.num_objs, **tkwargs).squeeze() # construct augmented Chebyshev scalarization scalarization = get_chebyshev_scalarization(weights=weights, Y=train_obj) # initialize ConstrainedMCObjective constrained_objective = get_constrained_mc_objective(train_obj=train_obj, train_con=train_con, scalarization=scalarization) train_y = torch.cat([train_obj, train_con], dim=-1) acq_func = qExpectedImprovement( # pyre-ignore: [28] model=model, objective=constrained_objective, best_f=constrained_objective(train_y).max(), sampler=sampler, ) acq_func_list.append(acq_func) # optimize candidates, _ = optimize_acqf_list( acq_function_list=acq_func_list, bounds=standard_bounds, num_restarts=20, raw_samples=1024, # used for intialization heuristic options={"batch_limit": 5, "maxiter": 200}, ) # observe new values new_x = candidates.detach() new_obj = [] new_con = [] for x in new_x: res = obj_func(x) y = res['objs'] c = res['constraints'] new_obj.append(y) new_con.append(c) global_time = time.time() - global_start_time time_list.append(global_time) new_obj = torch.tensor(new_obj, **tkwargs).reshape(new_x.shape[0], -1) new_con = torch.tensor(new_con, **tkwargs).reshape(new_x.shape[0], -1) print(f'evaluate {new_x.shape[0]} configs on real objective') return new_x, new_obj, new_con
def test_optimize_acqf_list(self, mock_optimize_acqf): num_restarts = 2 raw_samples = 10 options = {} tkwargs = {"device": self.device} bounds = torch.stack([torch.zeros(3), 4 * torch.ones(3)]) inequality_constraints = [[ torch.tensor([3]), torch.tensor([4]), torch.tensor(5) ]] # reinitialize so that dtype mock_acq_function_1 = MockAcquisitionFunction() mock_acq_function_2 = MockAcquisitionFunction() mock_acq_function_list = [mock_acq_function_1, mock_acq_function_2] for num_acqf, dtype in itertools.product([1, 2], (torch.float, torch.double)): for m in mock_acq_function_list: # clear previous X_pending m.set_X_pending(None) tkwargs["dtype"] = dtype inequality_constraints[0] = [ t.to(**tkwargs) for t in inequality_constraints[0] ] mock_optimize_acqf.reset_mock() bounds = bounds.to(**tkwargs) candidate_rvs = [] acq_val_rvs = [] gcs_return_vals = [(torch.rand(1, 3, **tkwargs), torch.rand(1, **tkwargs)) for _ in range(num_acqf)] for rv in gcs_return_vals: candidate_rvs.append(rv[0]) acq_val_rvs.append(rv[1]) side_effect = list(zip(candidate_rvs, acq_val_rvs)) mock_optimize_acqf.side_effect = side_effect orig_candidates = candidate_rvs[0].clone() # Wrap the set_X_pending method for checking that call arguments with mock.patch.object( MockAcquisitionFunction, "set_X_pending", wraps=mock_acq_function_1.set_X_pending, ) as mock_set_X_pending_1, mock.patch.object( MockAcquisitionFunction, "set_X_pending", wraps=mock_acq_function_2.set_X_pending, ) as mock_set_X_pending_2: candidates, acq_values = optimize_acqf_list( acq_function_list=mock_acq_function_list[:num_acqf], bounds=bounds, num_restarts=num_restarts, raw_samples=raw_samples, options=options, inequality_constraints=inequality_constraints, post_processing_func=rounding_func, ) # check that X_pending is set correctly in sequential optimization if num_acqf > 1: x_pending_call_args_list = mock_set_X_pending_2.call_args_list idxr = torch.ones(num_acqf, dtype=torch.bool, device=self.device) for i in range(len(x_pending_call_args_list) - 1): idxr[i] = 0 self.assertTrue( torch.equal(x_pending_call_args_list[i][0][0], orig_candidates[idxr])) idxr[i] = 1 orig_candidates[i] = candidate_rvs[i + 1] else: mock_set_X_pending_1.assert_not_called() # check final candidates expected_candidates = (torch.cat(candidate_rvs[-num_acqf:], dim=0) if num_acqf > 1 else candidate_rvs[0]) self.assertTrue(torch.equal(candidates, expected_candidates)) # check call arguments for optimize_acqf call_args_list = mock_optimize_acqf.call_args_list expected_call_args = { "acq_function": None, "bounds": bounds, "q": 1, "num_restarts": num_restarts, "raw_samples": raw_samples, "options": options, "inequality_constraints": inequality_constraints, "equality_constraints": None, "fixed_features": None, "post_processing_func": rounding_func, "batch_initial_conditions": None, "return_best_only": True, "sequential": False, } for i in range(len(call_args_list)): expected_call_args["acq_function"] = mock_acq_function_list[i] for k, v in call_args_list[i][1].items(): if torch.is_tensor(v): self.assertTrue(torch.equal(expected_call_args[k], v)) elif k == "acq_function": self.assertIsInstance(mock_acq_function_list[i], MockAcquisitionFunction) else: self.assertEqual(expected_call_args[k], v)