Example for using exclusion constraints in discrete searchspaces

This examples shows how an exclusion constraint can be created for a discrete searchspace. This can be used if some parameter values are incompatible with values of another parameter.

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

Necessary imports for this example

import numpy as np
from baybe import Campaign
from baybe.constraints import (
    DiscreteExcludeConstraint,
    SubSelectionCondition,
    ThresholdCondition,
)
from baybe.objective import Objective
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

We begin by setting up some parameters for our experiments.

dict_solvent = {
    "water": "O",
    "C1": "C",
    "C2": "CC",
    "C3": "CCC",
    "C4": "CCCC",
    "C5": "CCCCC",
    "c6": "c1ccccc1",
    "C6": "CCCCCC",
}
solvent = SubstanceParameter(name="Solv", data=dict_solvent, encoding="RDKIT")
speed = CategoricalParameter(
    name="Speed",
    values=["very slow", "slow", "normal", "fast", "very fast"],
    encoding="INT",
)
temperature = NumericalDiscreteParameter(
    name="Temp", values=list(np.linspace(100, 200, 15)), tolerance=0.4
)
pressure = NumericalDiscreteParameter(
    name="Pressure", values=[1, 2, 5, 10], tolerance=0.4
)
parameters = [solvent, speed, temperature, pressure]

Creating the constraint

This constraint simulates a situation where solvents C2 and C4 are not compatible with temperatures larger than 151 and should thus be excluded.

constraint_1 = DiscreteExcludeConstraint(
    parameters=["Temp", "Solv"],
    combiner="AND",
    conditions=[
        ThresholdCondition(threshold=151, operator=">"),
        SubSelectionCondition(selection=["C4", "C2"]),
    ],
)

This constraint simulates a situation where solvents C5 and C6 are not compatible with pressures larger than 5 and should thus be excluded.

constraint_2 = DiscreteExcludeConstraint(
    parameters=["Pressure", "Solv"],
    combiner="AND",
    conditions=[
        ThresholdCondition(threshold=5, operator=">"),
        SubSelectionCondition(selection=["C5", "C6"]),
    ],
)

This constraint simulates a situation where pressures below 3 should never be combined with temperatures above 120.

constraint_3 = DiscreteExcludeConstraint(
    parameters=["Pressure", "Temp"],
    combiner="AND",
    conditions=[
        ThresholdCondition(threshold=3.0, operator="<"),
        ThresholdCondition(threshold=120.0, operator=">"),
    ],
)

Creating the searchspace and the objective

We now create the searchspace using the previously defined constraints.

searchspace = SearchSpace.from_product(
    parameters=parameters, constraints=[constraint_1, constraint_2, constraint_3]
)
objective = Objective(
    mode="SINGLE", targets=[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

 Search Space
          
  Search Space Type: DISCRETE
  
  Discrete Search Space
               
   Discrete Parameters
          Name                        Type  Num_Values                 Encoding
   0      Solv          SubstanceParameter           8  SubstanceEncoding.RDKIT
   1     Speed        CategoricalParameter           5  CategoricalEncoding.INT
   2      Temp  NumericalDiscreteParameter          15                     None
   3  Pressure  NumericalDiscreteParameter           4                     None
               
   Experimental Representation
               
      Solv      Speed        Temp  Pressure
   0     water  very slow  100.000000       1.0
   1     water  very slow  100.000000       2.0
   2     water  very slow  100.000000       5.0
   ...     ...        ...         ...       ...
   1147     C6  very fast  185.714286       5.0
   1148     C6  very fast  192.857143       5.0
   1149     C6  very fast  200.000000       5.0
   
   [1150 rows x 4 columns]
   
   Metadata:

was_recommended: 0/1150

was_measured: 0/1150

dont_recommend: 0/1150

   Constraints
                           Type Affected_Parameters
   0  DiscreteExcludeConstraint        [Temp, Solv]
   1  DiscreteExcludeConstraint    [Pressure, Solv]
   2  DiscreteExcludeConstraint    [Pressure, Temp]
               
   Computational Representation
               
     Solv_RDKIT_MaxAbsEStateIndex  Solv_RDKIT_MaxPartialCharge  ...        Temp

Pressure 0 0.000000 -0.411510 … 100.000000 1.0 1 0.000000 -0.411510 … 100.000000 2.0 2 0.000000 -0.411510 … 100.000000 5.0 … … … … … … 1147 2.231806 -0.053579 … 185.714286 5.0 1148 2.231806 -0.053579 … 192.857143 5.0 1149 2.231806 -0.053579 … 200.000000 5.0

   [1150 rows x 10 columns]
 
 Objective
          
  Mode: SINGLE
          
  Targets 
                Type      Name Mode  Lower_Bound  Upper_Bound Transformation  \
  0  NumericalTarget  Target_1  MAX         -inf          inf           None   
  
     Weight  
  0   100.0  
          
  Combine Function: GEOM_MEAN
 
 TwoPhaseMetaRecommender(allow_repeated_recommendations=None,

allow_recommending_already_measured=None, initial_recommender=RandomRecommender(allow_repeated_recommendations=False, allow_recommending_already_measured=True), recommender=SequentialGreedyRecommender(allow_repeated_recommendations=False, allow_recommending_already_measured=True, surrogate_model=GaussianProcessSurrogate(model_params={}, _model=None), acquisition_function_cls=’qEI’, _acquisition_function=None, hybrid_sampler=’None’, sampling_percentage=1.0), switch_after=1)

Manual verification of the constraints

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

N_ITERATIONS = 3
for kIter in range(N_ITERATIONS):
    print(f"\n\n#### ITERATION {kIter+1} ####")

    print("## ASSERTS ##")
    print(
        "Number of entries with either Solvents C2 or C4 and a temperature above 151: ",
        (
            campaign.searchspace.discrete.exp_rep["Temp"].apply(lambda x: x > 151)
            & campaign.searchspace.discrete.exp_rep["Solv"].apply(
                lambda x: x in ["C2", "C4"]
            )
        ).sum(),
    )
    print(
        "Number of entries with either Solvents C5 or C6 and a pressure above 5:      ",
        (
            campaign.searchspace.discrete.exp_rep["Pressure"].apply(lambda x: x > 5)
            & campaign.searchspace.discrete.exp_rep["Solv"].apply(
                lambda x: x in ["C5", "C6"]
            )
        ).sum(),
    )
    print(
        "Number of entries with pressure below 3 and temperature above 120:           ",
        (
            campaign.searchspace.discrete.exp_rep["Pressure"].apply(lambda x: x < 3)
            & campaign.searchspace.discrete.exp_rep["Temp"].apply(lambda x: x > 120)
        ).sum(),
    )

    rec = campaign.recommend(batch_size=5)
    add_fake_results(rec, campaign)
    campaign.add_measurements(rec)
#### ITERATION 1 ####
## ASSERTS ##
Number of entries with either Solvents C2 or C4 and a temperature above 151:  0
Number of entries with either Solvents C5 or C6 and a pressure above 5:       0
Number of entries with pressure below 3 and temperature above 120:            0


#### ITERATION 2 ####
## ASSERTS ##
Number of entries with either Solvents C2 or C4 and a temperature above 151:  0
Number of entries with either Solvents C5 or C6 and a pressure above 5:       0
Number of entries with pressure below 3 and temperature above 120:            0




#### ITERATION 3 ####
## ASSERTS ##
Number of entries with either Solvents C2 or C4 and a temperature above 151:  0
Number of entries with either Solvents C5 or C6 and a pressure above 5:       0
Number of entries with pressure below 3 and temperature above 120:            0