Example for using exclusion constraints incorporating sums and products

This examples demonstrates an exclusion constraint using products and sums.

This example assumes some basic familiarity with using BayBE. We thus refer to campaign for a basic example.

Necessary imports for this example

import os
import numpy as np
from baybe import Campaign
from baybe.constraints import (
    DiscreteProductConstraint,
    DiscreteSumConstraint,
    ThresholdCondition,
)
from baybe.objectives import SingleTargetObjective
from baybe.parameters import (
    CategoricalParameter,
    NumericalDiscreteParameter,
    SubstanceParameter,
)
from baybe.searchspace import SearchSpace
from baybe.targets import NumericalTarget
from baybe.utils.dataframe import add_fake_results

Experiment setup

SMOKE_TEST = "SMOKE_TEST" in os.environ
RESOLUTION = 3 if SMOKE_TEST else 5
dict_solvent = {
    "water": "O",
    "C1": "C",
    "C2": "CC",
}
solvent = SubstanceParameter(name="Solvent", data=dict_solvent, encoding="RDKIT")
speed = CategoricalParameter(
    name="Speed", values=["slow", "normal", "fast"], encoding="INT"
)
num_parameter_1 = NumericalDiscreteParameter(
    name="NumParam1", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
num_parameter_2 = NumericalDiscreteParameter(
    name="NumParam2", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
num_parameter_3 = NumericalDiscreteParameter(
    name="NumParam3", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
num_parameter_4 = NumericalDiscreteParameter(
    name="NumParam4", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
num_parameter_5 = NumericalDiscreteParameter(
    name="NumParam5", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
num_parameter_6 = NumericalDiscreteParameter(
    name="NumParam6", values=list(np.linspace(0, 100, RESOLUTION)), tolerance=0.5
)
parameters = [
    solvent,
    speed,
    num_parameter_1,
    num_parameter_2,
    num_parameter_3,
    num_parameter_4,
    num_parameter_5,
    num_parameter_6,
]

Creating the constraints

Constraints are used when creating the searchspace object. Thus, they need to be defined prior to the searchspace creation.

sum_constraint_1 = DiscreteSumConstraint(
    parameters=["NumParam1", "NumParam2"],
    condition=ThresholdCondition(threshold=150.0, operator="<="),
)
sum_constraint_2 = DiscreteSumConstraint(
    parameters=["NumParam5", "NumParam6"],
    condition=ThresholdCondition(threshold=100, operator="=", tolerance=1.0),
)
prod_constraint = DiscreteProductConstraint(
    parameters=["NumParam3", "NumParam4"],
    condition=ThresholdCondition(threshold=30, operator=">="),
)
constraints = [sum_constraint_1, sum_constraint_2, prod_constraint]

Creating the searchspace and the objective

searchspace = SearchSpace.from_product(parameters=parameters, constraints=constraints)
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
[17:00:57] DEPRECATION WARNING: please use MorganGenerator
objective = SingleTargetObjective(target=NumericalTarget(name="Target_1", mode="MAX"))

Creating and printing the campaign

campaign = Campaign(searchspace=searchspace, objective=objective)
print(campaign)
Campaign
   Meta Data
      Batches done: 0
      Fits done: 0
   SearchSpace
      Search Space Type: DISCRETE
      SubspaceDiscrete
         Discrete Parameters
                     Name             Type  Num_Values         Encoding
            0   NumParam1  NumericalDis...           3             None
            1   NumParam2  NumericalDis...           3             None
            2   NumParam3  NumericalDis...           3             None
            ..        ...              ...         ...              ...
            5   NumParam6  NumericalDis...           3             None
            6     Solvent  SubstancePar...           3  SubstanceEnc...
            7       Speed  CategoricalP...           3  CategoricalE...
            
            [8 rows x 4 columns]
         Experimental Representation
                Solvent Speed  ...  NumParam5  NumParam6
            0        C1  fast  ...        0.0      100.0
            1        C1  fast  ...       50.0       50.0
            2        C1  fast  ...      100.0        0.0
            ..      ...   ...  ...        ...        ...
            861   water  slow  ...        0.0      100.0
            862   water  slow  ...       50.0       50.0
            863   water  slow  ...      100.0        0.0
            
            [864 rows x 8 columns]
         Meta Data
            was_recommended: 0/864
            was_measured: 0/864
            dont_recommend: 0/864
         Constraints
                          Type Affected_Paramet
            0  DiscreteSumC...  [NumParam1, ...
            1  DiscreteSumC...  [NumParam5, ...
            2  DiscreteProd...  [NumParam3, ...
         Computational Representation
                 NumParam1  NumParam2  ...  Solvent_RDKIT_Ma  Speed
            0          0.0        0.0  ...           -0.078     0.0
            1          0.0        0.0  ...           -0.078     0.0
            2          0.0        0.0  ...           -0.078     0.0
            ..         ...        ...  ...              ...     ...
            861      100.0       50.0  ...           -0.412     2.0
            862      100.0       50.0  ...           -0.412     2.0
            863      100.0       50.0  ...           -0.412     2.0
            
            [864 rows x 9 columns]
   Objective
      Type: SingleTargetObjective
      Targets
                       Type      Name  ... Upper_Bound  Transformation
         0  NumericalTarget  Target_1  ...         inf            None
         
         [1 rows x 6 columns]
   TwoPhaseMetaRecommender
      Initial recommender
         RandomRecommender
            Compatibility: SearchSpaceType.HYBRID
      Recommender
         BotorchRecommender
            Surrogate
               GaussianProcessSurrogate
                  Supports Transfer Learning: True
                  Kernel factory: DefaultKernelFactory()
            Acquisition function: qLogExpectedImprovement()
            Compatibility: SearchSpaceType.HYBRID
            Sequential continuous: False
            Hybrid sampler: None
            Sampling percentage: 1.0
      Switch after: 1

Manual verification of the constraints

The following loop performs some recommendations and manually verifies the given constraints.

N_ITERATIONS = 2 if SMOKE_TEST else 5
for kIter in range(N_ITERATIONS):
    print(f"\n\n#### ITERATION {kIter+1} ####")

    print("## ASSERTS ##")
    print(
        "Number of entries with 1,2-sum above 150:      ",
        (
            campaign.searchspace.discrete.exp_rep[["NumParam1", "NumParam2"]].sum(
                axis=1
            )
            > 150.0
        ).sum(),
    )
    print(
        "Number of entries with 3,4-product under 30:   ",
        (
            campaign.searchspace.discrete.exp_rep[["NumParam3", "NumParam4"]].prod(
                axis=1
            )
            < 30
        ).sum(),
    )
    print(
        "Number of entries with 5,6-sum unequal to 100: ",
        campaign.searchspace.discrete.exp_rep[["NumParam5", "NumParam6"]]
        .sum(axis=1)
        .apply(lambda x: x - 100.0)
        .abs()
        .gt(0.01)
        .sum(),
    )

    rec = campaign.recommend(batch_size=5)
    add_fake_results(rec, campaign.targets)
    campaign.add_measurements(rec)
#### ITERATION 1 ####
## ASSERTS ##
Number of entries with 1,2-sum above 150:       0
Number of entries with 3,4-product under 30:    0
Number of entries with 5,6-sum unequal to 100:  0


#### ITERATION 2 ####
## ASSERTS ##
Number of entries with 1,2-sum above 150:       0
Number of entries with 3,4-product under 30:    0
Number of entries with 5,6-sum unequal to 100:  0