Skip to content

Strategies

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.
  • MultifidelityStrategy: Single objective multi-fidelity BO as described here
  • EntingStrategy: Strategy based on the Entmoot package that uses tree-based surrogate models to perform both single-objective and multiobjective optimization.

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.

import bofire.strategies.api as strategies
from bofire.benchmarks.api import Himmelblau
from bofire.data_models.strategies.api import (
    AlwaysTrueCondition,
    NumberOfExperimentsCondition,
    RandomStrategy,
    SoboStrategy,
    Step,
    StepwiseStrategy,
)


domain = Himmelblau().domain

strategy_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.

import bofire.strategies.api as strategies
from bofire.benchmarks.api import DTLZ2
from bofire.data_models.acquisition_functions.api import qLogPF
from bofire.data_models.objectives.api import MaximizeSigmoidObjective
from 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).domain
domain.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)