This notebook demonstrates how to put objectives on input features or a combination of input features. Possible usecases are favoring lower or higher amounts of an ingredient or to take into account a known (linear) cost function. In case of categorical inputs it can be used to penalize the optimizer for choosing specific categories.
/opt/hostedtoolcache/Python/3.12.14/x64/lib/python3.12/site-packages/torch/jit/_script.py:1491: FutureWarning: `torch.jit.script` is deprecated. Please switch to `torch.compile` or `torch.export`.
warnings.warn(
/opt/hostedtoolcache/Python/3.12.14/x64/lib/python3.12/site-packages/torch/jit/_script.py:1491: FutureWarning: `torch.jit.script` is deprecated. Please switch to `torch.compile` or `torch.export`.
warnings.warn(
Setup an Example
We use Himmelblau as example with an additional objective on x_2 which pushes it to be larger 3 during the optimization. In addition, we introduce a categorical feature called x_cat which is mapped by an CategoricalDeterministicSurrogate to a continuous output called y_cat.
bench = Himmelblau()experiments = bench.f(bench.domain.inputs.sample(10), return_complete=True)domain = bench.domain# setup extra feature `y_x2` that is the same as `x_2` and is taken into account in the optimization by a sigmoid objectivedomain.outputs.features.append( ContinuousOutput(key="y_x2", objective=MaximizeSigmoidObjective(tp=3, steepness=10)))experiments["y_x2"] = experiments.x_2# add extra categorical input feature and corresponding output featuredomain.inputs.features.append(CategoricalInput(key="x_cat", categories=["a", "b", "c"]))domain.outputs.features.append( ContinuousOutput(key="y_cat", objective=MaximizeObjective()))# generate random values for the new categorical featureexperiments["x_cat"] = np.random.choice(["a", "b", "c"], size=experiments.shape[0])
The LinearDeterministicSurrogate can be used to model that y_x2 = x_2.
/opt/hostedtoolcache/Python/3.12.14/x64/lib/python3.12/site-packages/bofire/utils/torch_tools.py:51: UserWarning: The given NumPy array is not writable, and PyTorch does not support non-writable tensors. This means writing to this tensor will result in undefined behavior. You may want to copy the array to protect its data or make it writable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at /__w/pytorch/pytorch/torch/csrc/utils/tensor_numpy.cpp:213.)
return torch.from_numpy(np.ascontiguousarray(df.to_numpy())).to(**tkwargs)
y_x2_pred
y_x2_sd
0
3.246039
0.0
1
0.192887
0.0
2
3.730756
0.0
3
2.824307
0.0
4
-0.405028
0.0
5
-5.798657
0.0
6
-2.546550
0.0
7
1.334314
0.0
8
0.945511
0.0
9
1.788390
0.0
The CategoricalDeterministicSurrogate can be used to map categories to specific continuous values.
Next we setup a SoboStrategy using the custom surrogates for outputs y_x2 and y_cat and ask for a candidate. Note that the surrogate specs for output y is automatically generated and defaulted to be a SingleTaskGPSurrogate.
---title: Input Features as Output Objectivesjupyter: python3---This notebook demonstrates how to put objectives on input features or a combination of input features. Possible usecases are favoring lower or higher amounts of an ingredient or to take into account a known (linear) cost function. In case of categorical inputs it can be used to penalize the optimizer for choosing specific categories.## Imports```{python}import numpy as npimport bofire.strategies.api as strategiesimport bofire.surrogates.api as surrogatesfrom bofire.benchmarks.api import Himmelblaufrom bofire.data_models.features.api import CategoricalInput, ContinuousOutputfrom bofire.data_models.objectives.api import ( MaximizeObjective, MaximizeSigmoidObjective,)from bofire.data_models.strategies.api import MultiplicativeSoboStrategyfrom bofire.data_models.surrogates.api import ( BotorchSurrogates, CategoricalDeterministicSurrogate, LinearDeterministicSurrogate,)```## Setup an ExampleWe use Himmelblau as example with an additional objective on `x_2` which pushes it to be larger 3 during the optimization. In addition, we introduce a categorical feature called `x_cat` which is mapped by an `CategoricalDeterministicSurrogate` to a continuous output called `y_cat`.```{python}bench = Himmelblau()experiments = bench.f(bench.domain.inputs.sample(10), return_complete=True)domain = bench.domain# setup extra feature `y_x2` that is the same as `x_2` and is taken into account in the optimization by a sigmoid objectivedomain.outputs.features.append( ContinuousOutput(key="y_x2", objective=MaximizeSigmoidObjective(tp=3, steepness=10)))experiments["y_x2"] = experiments.x_2# add extra categorical input feature and corresponding output featuredomain.inputs.features.append(CategoricalInput(key="x_cat", categories=["a", "b", "c"]))domain.outputs.features.append( ContinuousOutput(key="y_cat", objective=MaximizeObjective()))# generate random values for the new categorical featureexperiments["x_cat"] = np.random.choice(["a", "b", "c"], size=experiments.shape[0])```The `LinearDeterministicSurrogate` can be used to model that `y_x2 = x_2`.```{python}surrogate_data = LinearDeterministicSurrogate( inputs=domain.inputs.get_by_keys(["x_2"]), outputs=domain.outputs.get_by_keys(["y_x2"]), coefficients={"x_2": 1}, intercept=0,)surrogate = surrogates.map(surrogate_data)surrogate.predict(experiments[domain.inputs.get_keys()].copy())```The `CategoricalDeterministicSurrogate` can be used to map categories to specific continuous values.```{python}categorical_surrogate_data = CategoricalDeterministicSurrogate( inputs=domain.inputs.get_by_keys(["x_cat"]), outputs=domain.outputs.get_by_keys(["y_cat"]), mapping={"a": 1, "b": 0.2, "c": 0.3},)surrogate = surrogates.map(categorical_surrogate_data)surrogate.predict(experiments[domain.inputs.get_keys()].copy())experiments["y_cat"] = surrogate.predict(experiments[domain.inputs.get_keys()].copy())["y_cat_pred"]experiments```Next we setup a `SoboStrategy` using the custom surrogates for outputs `y_x2` and `y_cat` and ask for a candidate. Note that the surrogate specs for output `y` is automatically generated and defaulted to be a `SingleTaskGPSurrogate`.```{python}strategy_data = MultiplicativeSoboStrategy( domain=domain, surrogate_specs=BotorchSurrogates( surrogates=[surrogate_data, categorical_surrogate_data] ),)strategy = strategies.map(strategy_data)strategy.tell(experiments)strategy.ask(4)```