def test_linear_mc_objective(self): for dtype in (torch.float, torch.double): weights = torch.rand(3, device=self.device, dtype=dtype) obj = LinearMCObjective(weights=weights) samples = torch.randn(4, 2, 3, device=self.device, dtype=dtype) self.assertTrue( torch.allclose(obj(samples), (samples * weights).sum(dim=-1)) ) samples = torch.randn(5, 4, 2, 3, device=self.device, dtype=dtype) self.assertTrue( torch.allclose(obj(samples), (samples * weights).sum(dim=-1)) ) # make sure this errors if sample output dimensions are incompatible with self.assertRaises(RuntimeError): obj(samples=torch.randn(2, device=self.device, dtype=dtype)) with self.assertRaises(RuntimeError): obj(samples=torch.randn(1, device=self.device, dtype=dtype)) # make sure we can't construct objectives with multi-dim. weights with self.assertRaises(ValueError): LinearMCObjective( weights=torch.rand(2, 3, device=self.device, dtype=dtype) ) with self.assertRaises(ValueError): LinearMCObjective( weights=torch.tensor(1.0, device=self.device, dtype=dtype) )
def test_construct_inputs_qEI(self): c = get_acqf_input_constructor(qExpectedImprovement) mock_model = mock.Mock() kwargs = c(model=mock_model, training_data=self.bd_td) self.assertEqual(kwargs["model"], mock_model) self.assertIsNone(kwargs["objective"]) self.assertIsNone(kwargs["X_pending"]) self.assertIsNone(kwargs["sampler"]) X_pending = torch.rand(2, 2) objective = LinearMCObjective(torch.rand(2)) kwargs = c( model=mock_model, training_data=self.bd_td_mo, objective=objective, X_pending=X_pending, ) self.assertEqual(kwargs["model"], mock_model) self.assertTrue( torch.equal(kwargs["objective"].weights, objective.weights)) self.assertTrue(torch.equal(kwargs["X_pending"], X_pending)) self.assertIsNone(kwargs["sampler"]) best_f_expected = objective(self.bd_td_mo.Y).max() self.assertEqual(kwargs["best_f"], best_f_expected) # Check explicitly specifying `best_f`. best_f_expected = best_f_expected - 1 # Random value. kwargs = c( model=mock_model, training_data=self.bd_td_mo, objective=objective, X_pending=X_pending, best_f=best_f_expected, ) self.assertEqual(kwargs["best_f"], best_f_expected)
def get_botorch_objective( model: Model, objective_weights: Tensor, use_scalarized_objective: bool = True, outcome_constraints: Optional[Tuple[Tensor, Tensor]] = None, objective_thresholds: Optional[Tensor] = None, X_observed: Optional[Tensor] = None, ) -> AcquisitionObjective: """Constructs a BoTorch `AcquisitionObjective` object. Args: model: A BoTorch Model objective_weights: The objective is to maximize a weighted sum of the columns of f(x). These are the weights. use_scalarized_objective: A boolean parameter that defaults to True, specifying whether ScalarizedObjective should be used. NOTE: when using outcome_constraints, use_scalarized_objective will be ignored. outcome_constraints: A tuple of (A, b). For k outcome constraints and m outputs at f(x), A is (k x m) and b is (k x 1) such that A f(x) <= b. (Not used by single task models) objective_thresholds: A tensor containing thresholds forming a reference point from which to calculate pareto frontier hypervolume. Points that do not dominate the objective_thresholds contribute nothing to hypervolume. X_observed: Observed points that are feasible and appear in the objective or the constraints. None if there are no such points. Returns: A BoTorch `AcquisitionObjective` object. It will be one of: `ScalarizedObjective`, `LinearMCOObjective`, `ConstrainedMCObjective`. """ if objective_thresholds is not None: nonzero_idcs = torch.nonzero(objective_weights).view(-1) objective_weights = objective_weights[nonzero_idcs] return WeightedMCMultiOutputObjective(weights=objective_weights, outcomes=nonzero_idcs.tolist()) if X_observed is None: raise UnsupportedError( "X_observed is required to construct a BoTorch Objective.") if outcome_constraints: if use_scalarized_objective: logger.warning( "Currently cannot use ScalarizedObjective when there are outcome " "constraints. Ignoring (default) kwarg `use_scalarized_objective`" "= True. Creating ConstrainedMCObjective.") obj_tf = get_objective_weights_transform(objective_weights) def objective(samples: Tensor, X: Optional[Tensor] = None) -> Tensor: return obj_tf(samples) con_tfs = get_outcome_constraint_transforms(outcome_constraints) inf_cost = get_infeasible_cost(X=X_observed, model=model, objective=obj_tf) return ConstrainedMCObjective(objective=objective, constraints=con_tfs or [], infeasible_cost=inf_cost) elif use_scalarized_objective: return ScalarizedObjective(weights=objective_weights) return LinearMCObjective(weights=objective_weights)
def get_NEI( model: Model, objective_weights: Tensor, outcome_constraints: Optional[Tuple[Tensor, Tensor]] = None, X_observed: Optional[Tensor] = None, X_pending: Optional[Tensor] = None, **kwargs: Any, ) -> AcquisitionFunction: r"""Instantiates a qNoisyExpectedImprovement acquisition function. Args: objective_weights: The objective is to maximize a weighted sum of the columns of f(x). These are the weights. outcome_constraints: A tuple of (A, b). For k outcome constraints and m outputs at f(x), A is (k x m) and b is (k x 1) such that A f(x) <= b. (Not used by single task models) X_observed: A tensor containing points observed for all objective outcomes and outcomes that appear in the outcome constraints (if there are any). X_pending: A tensor containing points whose evaluation is pending (i.e. that have been submitted for evaluation) present for all objective outcomes and outcomes that appear in the outcome constraints (if there are any). mc_samples: The number of MC samples to use (default: 512). qmc: If True, use qMC instead of MC (default: True). prune_baseline: If True, prune the baseline points for NEI (default: True). Returns: qNoisyExpectedImprovement: The instantiated acquisition function. """ if X_observed is None: raise ValueError("There are no feasible observed points.") # Parse random_scalarization params objective_weights = _extract_random_scalarization_settings( objective_weights, outcome_constraints, **kwargs) # construct Objective module if outcome_constraints is None: objective = LinearMCObjective(weights=objective_weights) else: obj_tf = get_objective_weights_transform(objective_weights) con_tfs = get_outcome_constraint_transforms(outcome_constraints) X_observed = torch.as_tensor(X_observed) inf_cost = get_infeasible_cost(X=X_observed, model=model, objective=obj_tf) objective = ConstrainedMCObjective(objective=obj_tf, constraints=con_tfs or [], infeasible_cost=inf_cost) return get_acquisition_function( acquisition_function_name="qNEI", model=model, objective=objective, X_observed=X_observed, X_pending=X_pending, prune_baseline=kwargs.get("prune_baseline", True), mc_samples=kwargs.get("mc_samples", 512), qmc=kwargs.get("qmc", True), seed=torch.randint(1, 10000, (1, )).item(), )
def test_init(self): mm = MockModel(MockPosterior(mean=None)) MPS = MaxPosteriorSampling(mm) self.assertEqual(MPS.model, mm) self.assertTrue(MPS.replacement) self.assertIsInstance(MPS.objective, IdentityMCObjective) obj = LinearMCObjective(torch.rand(2)) MPS = MaxPosteriorSampling(mm, objective=obj, replacement=False) self.assertEqual(MPS.objective, obj) self.assertFalse(MPS.replacement)
def test_get_best_f_mc(self): with self.assertRaises(NotImplementedError): get_best_f_mc(training_data=self.nbd_td) best_f = get_best_f_mc(training_data=self.bd_td) best_f_expected = self.bd_td.Y.squeeze().max() self.assertEqual(best_f, best_f_expected) with self.assertRaises(UnsupportedError): get_best_f_mc(training_data=self.bd_td_mo) obj = LinearMCObjective(weights=torch.rand(2)) best_f = get_best_f_mc(training_data=self.bd_td_mo, objective=obj) best_f_expected = (self.bd_td_mo.Y @ obj.weights).max() self.assertEqual(best_f, best_f_expected)
def test_optimize_objective(self, mock_optimize_acqf): from botorch.acquisition.input_constructors import optimize_objective mock_model = MockModel( posterior=MockPosterior(mean=None, variance=None)) bounds = torch.rand(2, len(self.bounds)) A = torch.rand(1, bounds.shape[-1]) b = torch.zeros([1, 1]) idx = A[0].nonzero(as_tuple=False).squeeze() inequality_constraints = ((idx, -A[0, idx], -b[0, 0]), ) with self.subTest("scalarObjective_linearConstraints"): post_tf = ScalarizedPosteriorTransform( weights=torch.rand(bounds.shape[-1])) _ = optimize_objective( model=mock_model, bounds=bounds, q=1, posterior_transform=post_tf, linear_constraints=(A, b), fixed_features=None, ) kwargs = mock_optimize_acqf.call_args[1] self.assertIsInstance(kwargs["acq_function"], PosteriorMean) self.assertTrue(torch.equal(kwargs["bounds"], bounds)) self.assertEqual(len(kwargs["inequality_constraints"]), 1) for a, b in zip(kwargs["inequality_constraints"][0], inequality_constraints[0]): self.assertTrue(torch.equal(a, b)) with self.subTest("mcObjective_fixedFeatures"): _ = optimize_objective( model=mock_model, bounds=bounds, q=1, objective=LinearMCObjective( weights=torch.rand(bounds.shape[-1])), fixed_features={0: 0.5}, ) kwargs = mock_optimize_acqf.call_args[1] self.assertIsInstance(kwargs["acq_function"], FixedFeatureAcquisitionFunction) self.assertIsInstance(kwargs["acq_function"].acq_func, qSimpleRegret) self.assertTrue(torch.equal(kwargs["bounds"], bounds[:, 1:]))
def test_construct_inputs_mes(self): func = get_acqf_input_constructor(qMaxValueEntropy) kwargs = func( model=mock.Mock(), training_data=self.bd_td, objective=LinearMCObjective(torch.rand(2)), bounds=self.bounds, candidate_size=17, maximize=False, ) self.assertFalse(kwargs["maximize"]) self.assertGreaterEqual(kwargs["candidate_set"].min(), 0.0) self.assertLessEqual(kwargs["candidate_set"].max(), 1.0) self.assertEqual([int(s) for s in kwargs["candidate_set"].shape], [17, len(self.bounds)])
def get_botorch_objective( model: Model, objective_weights: Tensor, use_scalarized_objective: bool = True, outcome_constraints: Optional[Tuple[Tensor, Tensor]] = None, X_observed: Optional[Tensor] = None, ) -> AcquisitionObjective: """Constructs a BoTorch `AcquisitionObjective` object. Args: model: A BoTorch Model objective_weights: The objective is to maximize a weighted sum of the columns of f(x). These are the weights. use_scalarized_objective: A boolean parameter that defaults to True, specifying whether ScalarizedObjective should be used. NOTE: when using outcome_constraints, use_scalarized_objective will be ignored. outcome_constraints: A tuple of (A, b). For k outcome constraints and m outputs at f(x), A is (k x m) and b is (k x 1) such that A f(x) <= b. (Not used by single task models) X_observed: Observed points that are feasible and appear in the objective or the constraints. None if there are no such points. Returns: A BoTorch `AcquisitionObjective` object. It will be one of: `ScalarizedObjective`, `LinearMCOObjective`, `ConstrainedMCObjective`. """ if X_observed is None: raise UnsupportedError( "X_observed is required to construct a BoTorch Objective.") if outcome_constraints: if use_scalarized_objective: logger.warning( "Currently cannot use ScalarizedObjective when there are outcome " "constraints. Ignoring (default) kwarg `use_scalarized_objective`" "= True. Creating ConstrainedMCObjective.") obj_tf = get_objective_weights_transform(objective_weights) con_tfs = get_outcome_constraint_transforms(outcome_constraints) inf_cost = get_infeasible_cost(X=X_observed, model=model, objective=obj_tf) return ConstrainedMCObjective(objective=obj_tf, constraints=con_tfs or [], infeasible_cost=inf_cost) if use_scalarized_objective: return ScalarizedObjective(weights=objective_weights) return LinearMCObjective(weights=objective_weights)
def test_get_best_f_mc(self): with self.assertRaises(NotImplementedError): get_best_f_mc(training_data=self.nbd_td) best_f = get_best_f_mc(training_data=self.bd_td) best_f_expected = self.bd_td.Y.squeeze().max() self.assertEqual(best_f, best_f_expected) with self.assertRaises(UnsupportedError): get_best_f_mc(training_data=self.bd_td_mo) obj = LinearMCObjective(weights=torch.rand(2)) best_f = get_best_f_mc(training_data=self.bd_td_mo, objective=obj) best_f_expected = (self.bd_td_mo.Y @ obj.weights).max() self.assertEqual(best_f, best_f_expected) post_tf = ScalarizedPosteriorTransform(weights=torch.ones(2)) best_f = get_best_f_mc(training_data=self.bd_td_mo, posterior_transform=post_tf) best_f_expected = (self.bd_td_mo.Y.sum(dim=-1)).max() self.assertEqual(best_f, best_f_expected)
def test_construct_inputs_mc_base(self): c = get_acqf_input_constructor(qSimpleRegret) mock_model = mock.Mock() kwargs = c(model=mock_model, training_data=self.bd_td) self.assertEqual(kwargs["model"], mock_model) self.assertIsNone(kwargs["objective"]) self.assertIsNone(kwargs["X_pending"]) self.assertIsNone(kwargs["sampler"]) X_pending = torch.rand(2, 2) objective = LinearMCObjective(torch.rand(2)) kwargs = c( model=mock_model, training_data=self.bd_td, objective=objective, X_pending=X_pending, ) self.assertEqual(kwargs["model"], mock_model) self.assertTrue( torch.equal(kwargs["objective"].weights, objective.weights)) self.assertTrue(torch.equal(kwargs["X_pending"], X_pending)) self.assertIsNone(kwargs["sampler"])
def test_construct_inputs_kg(self): current_value = torch.tensor(1.23) with mock.patch( target= "botorch.acquisition.input_constructors.optimize_objective", return_value=(None, current_value), ): from botorch.acquisition import input_constructors func = input_constructors.get_acqf_input_constructor( qKnowledgeGradient) kwargs = func( model=mock.Mock(), training_data=self.bd_td, objective=LinearMCObjective(torch.rand(2)), bounds=self.bounds, num_fantasies=33, ) self.assertEqual(kwargs["num_fantasies"], 33) self.assertEqual(kwargs["current_value"], current_value)
def test_construct_inputs_qUCB(self): c = get_acqf_input_constructor(qUpperConfidenceBound) mock_model = mock.Mock() kwargs = c(model=mock_model, training_data=self.bd_td) self.assertEqual(kwargs["model"], mock_model) self.assertIsNone(kwargs["objective"]) self.assertIsNone(kwargs["X_pending"]) self.assertIsNone(kwargs["sampler"]) self.assertEqual(kwargs["beta"], 0.2) X_pending = torch.rand(2, 2) objective = LinearMCObjective(torch.rand(2)) kwargs = c( model=mock_model, training_data=self.bd_td, objective=objective, X_pending=X_pending, beta=0.1, ) self.assertEqual(kwargs["model"], mock_model) self.assertTrue( torch.equal(kwargs["objective"].weights, objective.weights)) self.assertTrue(torch.equal(kwargs["X_pending"], X_pending)) self.assertIsNone(kwargs["sampler"]) self.assertEqual(kwargs["beta"], 0.1)
def test_KnowledgeGradient_helpers(self): model = KnowledgeGradient() model.fit( Xs=self.Xs, Ys=self.Ys, Yvars=self.Yvars, search_space_digest=SearchSpaceDigest( feature_names=self.feature_names, bounds=self.bounds, ), metric_names=self.metric_names, ) # test _instantiate_KG objective = ScalarizedObjective(weights=self.objective_weights) # test acquisition setting acq_function = _instantiate_KG(model=model.model, objective=objective, n_fantasies=10, qmc=True) self.assertIsInstance(acq_function.sampler, SobolQMCNormalSampler) self.assertIsInstance(acq_function.objective, ScalarizedObjective) self.assertEqual(acq_function.num_fantasies, 10) acq_function = _instantiate_KG(model=model.model, objective=objective, n_fantasies=10, qmc=False) self.assertIsInstance(acq_function.sampler, IIDNormalSampler) acq_function = _instantiate_KG(model=model.model, objective=objective, qmc=False) self.assertIsNone(acq_function.inner_sampler) acq_function = _instantiate_KG(model=model.model, objective=objective, qmc=True, X_pending=self.X_dummy) self.assertIsNone(acq_function.inner_sampler) self.assertTrue(torch.equal(acq_function.X_pending, self.X_dummy)) # test _get_best_point_acqf acq_function, non_fixed_idcs = model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, ) self.assertIsInstance(acq_function, qSimpleRegret) self.assertIsInstance(acq_function.sampler, SobolQMCNormalSampler) self.assertIsNone(non_fixed_idcs) acq_function, non_fixed_idcs = model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, qmc=False, ) self.assertIsInstance(acq_function.sampler, IIDNormalSampler) self.assertIsNone(non_fixed_idcs) with self.assertRaises(RuntimeError): model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, target_fidelities={1: 1.0}, ) # multi-fidelity tests model = KnowledgeGradient() model.fit( Xs=self.Xs, Ys=self.Ys, Yvars=self.Yvars, search_space_digest=SearchSpaceDigest( feature_names=self.feature_names, bounds=self.bounds, fidelity_features=[-1], ), metric_names=self.metric_names, ) acq_function = _instantiate_KG( model=model.model, objective=objective, target_fidelities={2: 1.0}, current_value=0, ) self.assertIsInstance(acq_function, qMultiFidelityKnowledgeGradient) acq_function = _instantiate_KG( model=model.model, objective=LinearMCObjective(weights=self.objective_weights), ) self.assertIsInstance(acq_function.inner_sampler, SobolQMCNormalSampler) # test error that target fidelity and fidelity weight indices must match with self.assertRaises(RuntimeError): _instantiate_KG( model=model.model, objective=objective, target_fidelities={1: 1.0}, fidelity_weights={2: 1.0}, current_value=0, ) # test _get_best_point_acqf acq_function, non_fixed_idcs = model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, target_fidelities={2: 1.0}, ) self.assertIsInstance(acq_function, FixedFeatureAcquisitionFunction) self.assertIsInstance(acq_function.acq_func.sampler, SobolQMCNormalSampler) self.assertEqual(non_fixed_idcs, [0, 1]) acq_function, non_fixed_idcs = model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, target_fidelities={2: 1.0}, qmc=False, ) self.assertIsInstance(acq_function, FixedFeatureAcquisitionFunction) self.assertIsInstance(acq_function.acq_func.sampler, IIDNormalSampler) self.assertEqual(non_fixed_idcs, [0, 1]) # test error that fixed features are provided with self.assertRaises(RuntimeError): model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, qmc=False, ) # test error if fixed features are also fidelity features with self.assertRaises(RuntimeError): model._get_best_point_acqf( objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=self.X_dummy, fixed_features={2: 2.0}, target_fidelities={2: 1.0}, qmc=False, )
def test_init( self, mock_botorch_acqf_class, mock_compute_model_deps, mock_get_objective_and_transform, mock_subset_model, mock_get_X, ): with self.assertRaisesRegex(TypeError, ".* missing .* 'botorch_acqf_class'"): Acquisition( surrogate=self.surrogate, search_space_digest=self.search_space_digest, objective_weights=self.objective_weights, ) botorch_objective = LinearMCObjective(weights=torch.tensor([1.0])) mock_get_objective_and_transform.return_value = (botorch_objective, None) mock_get_X.return_value = (self.pending_observations[0], self.X[:1]) acquisition = Acquisition( surrogate=self.surrogate, search_space_digest=self.search_space_digest, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, options=self.options, objective_thresholds=self.objective_thresholds, ) # Check `_get_X_pending_and_observed` kwargs mock_get_X.assert_called_with( Xs=[self.training_data.X], pending_observations=self.pending_observations, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, bounds=self.search_space_digest.bounds, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, ) # Call `subset_model` only when needed mock_subset_model.assert_called_with( model=acquisition.surrogate.model, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, objective_thresholds=self.objective_thresholds, ) mock_subset_model.reset_mock() mock_get_objective_and_transform.reset_mock() self.mock_input_constructor.reset_mock() mock_botorch_acqf_class.reset_mock() self.options[Keys.SUBSET_MODEL] = False acquisition = Acquisition( surrogate=self.surrogate, search_space_digest=self.search_space_digest, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, options=self.options, ) mock_subset_model.assert_not_called() # Check `get_botorch_objective_and_transform` kwargs mock_get_objective_and_transform.assert_called_once() _, ckwargs = mock_get_objective_and_transform.call_args self.assertIs(ckwargs["model"], self.acquisition.surrogate.model) self.assertIs(ckwargs["objective_weights"], self.objective_weights) self.assertIs(ckwargs["outcome_constraints"], self.outcome_constraints) self.assertTrue(torch.equal(ckwargs["X_observed"], self.X[:1])) # Check final `acqf` creation model_deps = {Keys.CURRENT_VALUE: 1.2} self.mock_input_constructor.assert_called_once() mock_botorch_acqf_class.assert_called_once() _, ckwargs = self.mock_input_constructor.call_args self.assertIs(ckwargs["model"], self.acquisition.surrogate.model) self.assertIs(ckwargs["objective"], botorch_objective) self.assertTrue(torch.equal(ckwargs["X_pending"], self.pending_observations[0])) for k, v in chain(self.options.items(), model_deps.items()): self.assertEqual(ckwargs[k], v)
def test_init( self, mock_botorch_acqf_class, mock_compute_model_deps, mock_get_objective, mock_subset_model, mock_get_X, ): self.acquisition.default_botorch_acqf_class = None with self.assertRaisesRegex( ValueError, ".*`botorch_acqf_class` argument must be specified."): Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, ) botorch_objective = LinearMCObjective(weights=torch.tensor([1.0])) mock_get_objective.return_value = botorch_objective mock_get_X.return_value = (self.pending_observations[0], self.X[:1]) acquisition = Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, target_fidelities=self.target_fidelities, options=self.options, ) # Check `_get_X_pending_and_observed` kwargs mock_get_X.assert_called_with( Xs=[self.training_data.X], pending_observations=self.pending_observations, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, bounds=self.bounds, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, ) # Call `subset_model` only when needed mock_subset_model.assert_called_with( acquisition.surrogate.model, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, objective_thresholds=self.objective_thresholds, ) mock_subset_model.reset_mock() mock_get_objective.reset_mock() mock_botorch_acqf_class.reset_mock() self.options[Keys.SUBSET_MODEL] = False acquisition = Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, target_fidelities=self.target_fidelities, options=self.options, ) mock_subset_model.assert_not_called() # Check `get_botorch_objective` kwargs mock_get_objective.assert_called_once() _, ckwargs = mock_get_objective.call_args self.assertIs(ckwargs["model"], self.acquisition.surrogate.model) self.assertIs(ckwargs["objective_weights"], self.objective_weights) self.assertIs(ckwargs["outcome_constraints"], self.outcome_constraints) self.assertTrue(torch.equal(ckwargs["X_observed"], self.X[:1])) self.assertFalse(ckwargs["use_scalarized_objective"]) # Check final `acqf` creation model_deps = {Keys.CURRENT_VALUE: 1.2} mock_botorch_acqf_class.assert_called_once() _, ckwargs = mock_botorch_acqf_class.call_args self.assertIs(ckwargs["model"], self.acquisition.surrogate.model) self.assertIs(ckwargs["objective"], botorch_objective) self.assertTrue( torch.equal(ckwargs["X_pending"], self.pending_observations[0])) for k, v in chain(self.options.items(), model_deps.items()): self.assertEqual(ckwargs[k], v)
def test_init( self, mock_botorch_acqf_class, mock_compute_data_deps, mock_compute_model_deps, mock_get_objective, mock_subset_model, mock_get_X, ): self.acquisition.default_botorch_acqf_class = None with self.assertRaisesRegex( ValueError, ".*`botorch_acqf_class` argument must be specified." ): Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, ) botorch_objective = LinearMCObjective(weights=torch.tensor([1.0])) mock_get_objective.return_value = botorch_objective acquisition = Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, target_fidelities=self.target_fidelities, options=self.options, ) # Check `_get_X_pending_and_observed` kwargs mock_get_X.assert_called_with( Xs=[self.training_data.X], pending_observations=self.pending_observations, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, bounds=self.bounds, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, ) # Call `subset_model` only when needed mock_subset_model.assert_called_with( acquisition.surrogate.model, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, ) mock_subset_model.reset_mock() self.options[Keys.SUBSET_MODEL] = False acquisition = Acquisition( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, botorch_acqf_class=self.botorch_acqf_class, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, target_fidelities=self.target_fidelities, options=self.options, ) mock_subset_model.assert_not_called() # Check `get_botorch_objective` kwargs mock_get_objective.assert_called_with( model=self.acquisition.surrogate.model, objective_weights=self.objective_weights, outcome_constraints=self.outcome_constraints, X_observed=torch.tensor([3.0]), use_scalarized_objective=False, ) # Check `compute_model_dependencies` kwargs mock_compute_model_deps.assert_called_with( surrogate=self.surrogate, bounds=self.bounds, objective_weights=self.objective_weights, pending_observations=self.pending_observations, outcome_constraints=self.outcome_constraints, linear_constraints=self.linear_constraints, fixed_features=self.fixed_features, target_fidelities=self.target_fidelities, options=self.options, ) # Check `compute_data_dependencies` kwargs mock_compute_data_deps.assert_called_with(training_data=self.training_data) # Check final `acqf` creation model_deps = {Keys.CURRENT_VALUE: 1.2} data_deps = {} mock_botorch_acqf_class.assert_called_with( model=self.acquisition.surrogate.model, objective=botorch_objective, X_pending=torch.tensor([2.0]), X_baseline=torch.tensor([3.0]), **self.options, **model_deps, **data_deps, )
def test_construct_inputs_mf_base(self): target_fidelities = {0: 0.123} fidelity_weights = {0: 0.456} cost_intercept = 0.789 num_trace_observations = 0 with self.subTest("test_fully_specified"): kwargs = construct_inputs_mf_base( model=mock.Mock(), training_data=self.bd_td, objective=LinearMCObjective(torch.rand(2)), target_fidelities=target_fidelities, fidelity_weights=fidelity_weights, cost_intercept=cost_intercept, num_trace_observations=num_trace_observations, ) self.assertEqual(kwargs["target_fidelities"], target_fidelities) X = torch.rand(3, 2) self.assertTrue(isinstance(kwargs["expand"], Callable)) self.assertTrue( torch.equal( kwargs["expand"](X), expand_trace_observations( X=X, fidelity_dims=sorted(target_fidelities), num_trace_obs=num_trace_observations, ), )) self.assertTrue(isinstance(kwargs["project"], Callable)) self.assertTrue( torch.equal( kwargs["project"](X), project_to_target_fidelity( X, target_fidelities=target_fidelities), )) cm = kwargs["cost_aware_utility"].cost_model w = torch.tensor(list(fidelity_weights.values()), dtype=cm.weights.dtype) self.assertEqual(cm.fixed_cost, cost_intercept) self.assertTrue(torch.allclose(cm.weights, w)) with self.subTest("test_missing_fidelity_weights"): kwargs = construct_inputs_mf_base( model=mock.Mock(), training_data=self.bd_td, objective=LinearMCObjective(torch.rand(2)), target_fidelities=target_fidelities, cost_intercept=cost_intercept, ) cm = kwargs["cost_aware_utility"].cost_model self.assertTrue( torch.allclose(cm.weights, torch.ones_like(cm.weights))) with self.subTest("test_mismatched_weights"): with self.assertRaisesRegex(RuntimeError, "Must provide the same indices for"): _ = construct_inputs_mf_base( model=mock.Mock(), training_data=self.bd_td, objective=LinearMCObjective(torch.rand(2)), target_fidelities={0: 1.0}, fidelity_weights={1: 0.5}, cost_intercept=cost_intercept, )