Strategies are the key ingredient of BoFire that explore the search space defined in the Domain and provide candidates for the next experiment or batch of experiments. Available strategies can be clustered in the following subclasses.
Non-predictive Strategies
BoFire offers the following strategies for sampling from the search space (no output features need to be provided in the Domain):
RandomStrategy: This strategy proposes candidates by (quasi-)random sampling from the search space. It is applicable to almost all combinations of input features and constraints.
DoEStrategy: This strategy offers model-based DoE approaches for proposing candidates.
FractionalFactorialStrategy: This strategy should be used to generate (fractional-)factorial designs.
Predictive Strategies
Predictive strategies are making use of (Bayesian) surrogate models to provide candidates with the intention to achieve certain goals depending on the provided objectives of the output features.
The following predictive strategies are available:
SoboStrategy: Bayesian optimization strategy that optimizes a single-objective acquisition function. For multi-objective domains, different scalarizations are possible as implemented in the AdditiveSoboStrategy, MultiplicativeSoboStrategy, AdditiveMultiplicativeSoboStrategy and CustomSoboStrategy.
MoboStrategy: Bayesian optimization strategy that optimizes a hypervolume based acquisition function for pareto-based multi-objective optimization.
QparegoStrategy: Parallel ParEGO strategy for multiobjective optimization.
MultiFidelityVarianceBasedStrategy: Single objective multi-fidelity BO as described here
EntingStrategy: Strategy based on the Entmootpackage that uses tree-based surrogate models to perform both single-objective and multiobjective optimization.
LLM-based Strategies
LLMStrategy: Uses a large language model to propose candidates from the problem description, feature bounds, constraints, and prior experiments. Supports multiple providers via pydantic-ai. Useful for cold-start designs and domains where written context (e.g. molecular optimization) informs proposals. Requires the optional [llm] extra and provider credentials. There is no calibrated uncertainty model — treat candidates as informed heuristics, not optima. See the LLM molecular optimization tutorial for an end-to-end example.
Combining Strategies
In BoFire, the StepwiseStrategy operates on a sequence of strategies and determines when to switch between them based on customizable logical operators.
The StepwiseStrategy is comprised of a sequence of Steps, where each Step consists of the following three attributes:
strategy_data: data model of the strategy which should be executed in this step.
condition: A logical expression that determines when this step’s strategy should be executed. The StepwiseStrategy evaluates each step in order and selects the first strategy whose condition evaluates to True.
transform: An object that can be used to transform experiments and/or candidates before they enter/leave the strategy assigned in the step.
The following example demonstrates how to combine a RandomStrategy with a SoboStrategy using the StepwiseStrategy. In this setup, the RandomStrategy is applied initially to propose candidates until 10 experiments have been completed. Once this threshold is reached, the strategy automatically switches to the SoboStrategy for subsequent candidate generation.
When dealing with output constraints, it is often beneficial to ensure that a certain number of experiments satisfy these constraints before proceeding with the main optimization. If no feasible experiments have been found, the strategy should prioritize generating candidates likely to fulfill the output constraints. This can be accomplished by using the qLogPF (Probability of Feasibility) acquisition function together with the FeasibleExperimentCondition. This approach allows the optimization process to focus first on feasibility, and only switch to the main objective once enough feasible experiments are available.
In the following example, a RandomStrategy is applied for the initial 10 experiments to broadly explore the search space. Afterward, the SoboStrategy with the qLogPF acquisition function is used to prioritize finding at least one feasible experiment that satisfies the output constraints. Once this feasibility criterion is met, the strategy transitions to the standard SoboStrategy to focus on optimizing the main objective.
import bofire.strategies.api as strategiesfrom bofire.benchmarks.api import DTLZ2from bofire.data_models.acquisition_functions.api import qLogPFfrom bofire.data_models.objectives.api import MaximizeSigmoidObjectivefrom bofire.data_models.strategies.api import ( AlwaysTrueCondition, FeasibleExperimentCondition, NumberOfExperimentsCondition, RandomStrategy, SoboStrategy, Step, StepwiseStrategy,)# create a domain with one output constraint by assigning a MaximizeSigmoidObjective# to the output with key "f_1"domain = DTLZ2(dim=6).domaindomain.outputs.get_by_key("f_1").objective = MaximizeSigmoidObjective( tp=0.5, steepness=100)strategy_data = StepwiseStrategy( domain=domain, steps=[ Step( strategy_data=RandomStrategy(domain=domain), condition=NumberOfExperimentsCondition(n_experiments=10), ), Step( strategy_data=SoboStrategy(domain=domain, acquisition_function=qLogPF()), condition=FeasibleExperimentCondition(n_required_feasible_experiments=1), ), Step( strategy_data=SoboStrategy(domain=domain), condition=AlwaysTrueCondition() ), ],)strategy = strategies.map(strategy_data)
/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(
# StrategiesStrategies are the key ingredient of BoFire that explore the search space defined in the `Domain` and provide candidates for the next experiment or batch of experiments. Available strategies can be clustered in the following subclasses.## Non-predictive StrategiesBoFire offers the following strategies for sampling from the search space (no output features need to be provided in the `Domain`):- `RandomStrategy`: This strategy proposes candidates by (quasi-)random sampling from the search space. It is applicable to almost all combinations of input features and constraints.- `DoEStrategy`: This strategy offers model-based DoE approaches for proposing candidates.- `FractionalFactorialStrategy`: This strategy should be used to generate (fractional-)factorial designs.## Predictive StrategiesPredictive strategies are making use of (Bayesian) surrogate models to provide candidates with the intention to achieve certain goals depending on the provided objectives of the output features.The following predictive strategies are available:- `SoboStrategy`: Bayesian optimization strategy that optimizes a single-objective acquisition function. For multi-objective domains, different scalarizations are possible as implemented in the `AdditiveSoboStrategy`, `MultiplicativeSoboStrategy`, `AdditiveMultiplicativeSoboStrategy` and `CustomSoboStrategy`.- `MoboStrategy`: Bayesian optimization strategy that optimizes a hypervolume based acquisition function for pareto-based multi-objective optimization.- `QparegoStrategy`: Parallel ParEGO strategy for multiobjective optimization.- `MultiFidelityVarianceBasedStrategy:` Single objective multi-fidelity BO as described [here](https://www.sciencedirect.com/science/article/pii/S0098135423000637)- `EntingStrategy`: Strategy based on the `Entmoot`[package](https://github.com/cog-imperial/entmoot) that uses tree-based surrogate models to perform both single-objective and multiobjective optimization.## LLM-based Strategies- `LLMStrategy`: Uses a large language model to propose candidates from the problem description, feature bounds, constraints, and prior experiments. Supports multiple providers via [pydantic-ai](https://ai.pydantic.dev/). Useful for cold-start designs and domains where written context (e.g. molecular optimization) informs proposals. Requires the optional `[llm]` extra and provider credentials. There is no calibrated uncertainty model — treat candidates as informed heuristics, not optima. See the [LLM molecular optimization tutorial](../tutorials/advanced_examples/llm_molecular.qmd) for an end-to-end example.## Combining StrategiesIn BoFire, the `StepwiseStrategy` operates on a sequence of strategies and determines when to switch between them based on customizable logical operators.The `StepwiseStrategy` is comprised of a sequence of `Step`s, where each `Step` consists of the following three attributes:- `strategy_data`: data model of the strategy which should be executed in this step.- `condition`: A logical expression that determines when this `step`'s strategy should be executed. The `StepwiseStrategy` evaluates each step in order and selects the first strategy whose condition evaluates to `True`.- `transform`: An object that can be used to transform experiments and/or candidates before they enter/leave the strategy assigned in the step.The following example demonstrates how to combine a `RandomStrategy` with a `SoboStrategy` using the `StepwiseStrategy`. In this setup, the `RandomStrategy` is applied initially to propose candidates until 10 experiments have been completed. Once this threshold is reached, the strategy automatically switches to the `SoboStrategy` for subsequent candidate generation.``` pythonimport bofire.strategies.api as strategiesfrom bofire.benchmarks.api import Himmelblaufrom bofire.data_models.strategies.api import ( AlwaysTrueCondition, NumberOfExperimentsCondition, RandomStrategy, SoboStrategy, Step, StepwiseStrategy,)domain = Himmelblau().domainstrategy_data = StepwiseStrategy( domain=domain, steps=[ Step( strategy_data=RandomStrategy(domain=domain), condition=NumberOfExperimentsCondition(n_experiments=10), ), Step( strategy_data=SoboStrategy(domain=domain), condition=AlwaysTrueCondition() ), ],)strategy = strategies.map(strategy_data)```When dealing with output constraints, it is often beneficial to ensure that a certain number of experiments satisfy these constraints before proceeding with the main optimization. If no feasible experiments have been found, the strategy should prioritize generating candidates likely to fulfill the output constraints. This can be accomplished by using the `qLogPF` (Probability of Feasibility) acquisition function together with the `FeasibleExperimentCondition`. This approach allows the optimization process to focus first on feasibility, and only switch to the main objective once enough feasible experiments are available.In the following example, a `RandomStrategy` is applied for the initial 10 experiments to broadly explore the search space. Afterward, the `SoboStrategy` with the `qLogPF` acquisition function is used to prioritize finding at least one feasible experiment that satisfies the output constraints. Once this feasibility criterion is met, the strategy transitions to the standard `SoboStrategy` to focus on optimizing the main objective.```{python}import bofire.strategies.api as strategiesfrom bofire.benchmarks.api import DTLZ2from bofire.data_models.acquisition_functions.api import qLogPFfrom bofire.data_models.objectives.api import MaximizeSigmoidObjectivefrom bofire.data_models.strategies.api import ( AlwaysTrueCondition, FeasibleExperimentCondition, NumberOfExperimentsCondition, RandomStrategy, SoboStrategy, Step, StepwiseStrategy,)# create a domain with one output constraint by assigning a MaximizeSigmoidObjective# to the output with key "f_1"domain = DTLZ2(dim=6).domaindomain.outputs.get_by_key("f_1").objective = MaximizeSigmoidObjective( tp=0.5, steepness=100)strategy_data = StepwiseStrategy( domain=domain, steps=[ Step( strategy_data=RandomStrategy(domain=domain), condition=NumberOfExperimentsCondition(n_experiments=10), ), Step( strategy_data=SoboStrategy(domain=domain, acquisition_function=qLogPF()), condition=FeasibleExperimentCondition(n_required_feasible_experiments=1), ), Step( strategy_data=SoboStrategy(domain=domain), condition=AlwaysTrueCondition() ), ],)strategy = strategies.map(strategy_data)```