Challenges · C13 · ★★★★☆

Scale-up from a partial screen: model the yields, then price the next experiment

Difficulty
★★★★☆
Time
4-5 hours
Data
Shields et al. (2021, Nature 590:89-96): a palladium-catalysed direct arylation run under every combination of 12 ligands x 4 bases x 4 solvents x 3 concentrations x 3 temperatures (1,728 yields). You get a random 60 of them; the rest is held back for auditing
Skills
Bounded outcomes with a pile of zeros (censoring on a transformed scale) · hierarchical ANOVA with shrunk two-way interactions (ZeroSumNormal) · a noise-vs-interaction ridge and checking it in function space · LOO · predicting a whole design space · P(best) and the posterior of the best achievable yield · scoring predictions against held-out truth (CRPS, coverage) · value of information for a batch of experiments · checking a decision rule on replayed screens

The brief

A process chemistry group has to fix the conditions for scaling up a C-H arylation. There are 1,728 candidate conditions (ligand, base, solvent, concentration, temperature). Their high-throughput robot ran a seeded random 60 of them last month. The head of the group asks:

"Our best screened condition gave about 91%. Do we lock it in for the pilot plant now, or do we buy another round of screening first? If another round, how many reactions? I want an expected value and the probability that it pays off - not a feeling."

The economics, agreed with finance:

  • every percentage point of yield at scale is worth $5,000 over the planned campaigns (assume screening yields carry over to the plant);
  • one more screening reaction costs $1,500** (materials, analysis, chemist time), and any extra round delays the pilot plant by a week, which costs **$10,000 whatever its size;
  • only a condition that has actually been run can go to the plant: after an extra round they take the best measured condition (screen or new round). Treat recorded yields as exact - the table has no replicates.

Your job: build a model of yield over the whole grid from the 60 reactions, say which conditions could be the best and how good the best could be, then turn that into the decision. Because this dataset is a complete screen, you can then do what no real group can: audit every prediction and every decision against the truth.

This is the sequel to example E82 (Bayesian optimisation). E82 is about the sequential loop (which experiment next, one at a time) and uses a Gaussian process on raw yields. Here the loop is not the point: the questions are what the model of a partial screen should look like and how to price the next batch. Do E82 first; its "Try it yourself" 1 and 2 are where this starts.

How this notebook works

  • Each task states what to deliver, not how. Write your code in the YOUR CODE HERE cells.
  • Stuck? h.hint("task3") reveals hints one level at a time: nudge → approach → code skeleton. Try to get by on nudges.
  • h.check("task2", sigma=...) compares your numbers with the reference solution.
  • A full worked solution lives in notebooks/solutions/. Open it only when you are done (or truly stuck).
import logging
import time

import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pymc as pm
import xarray as xr
from scipy.special import expit, logit

from pymc_challenges import Hints, data

RANDOM_SEED = 2021
rng = np.random.default_rng(RANDOM_SEED)
az.style.use("arviz-variat")
logging.getLogger("pymc").setLevel(logging.WARNING)  # several fits: no sampler banner per fit
T_START = time.time()

h = Hints("C13")
h.tasks()

C13 - Scale-up from a partial screen: model the yields, then price the next experiment

  • task0 - Look at the screen (2 hints)
  • task1 - A Gaussian ANOVA on raw yield (3 hints, 3 check(s))
  • task2 - A likelihood that knows what a yield is (3 hints, 2 check(s))
  • task3 - Interactions from 60 reactions (3 hints, 2 check(s))
  • task4 - The whole grid (3 hints, 3 check(s))
  • task5 - Commit now, or buy k more reactions? (3 hints, 3 check(s))
  • task6 - Replay the decision (3 hints, 3 check(s))

The data

The full table is loaded once and put in grid order (ligand x base x solvent x concentration x temperature, as in E82), so row i of IDX gives the level indices of condition i. The screen is the seeded random 60. TRUTH holds all 1,728 yields: do not use it before Task 4; from then on it is your audit, never an input to a model or a decision.

data.describe("edbo_arylation")
grid = data.load("edbo_arylation")
FACT = ["ligand", "base", "solvent", "concentration", "temperature"]
LEV = [sorted(grid[f].unique()) for f in FACT]
NL = [len(lv) for lv in LEV]
for f, lv in zip(FACT, LEV):
    grid[f + "_i"] = pd.Index(lv).get_indexer(grid[f])
grid = grid.sort_values([f + "_i" for f in FACT]).reset_index(drop=True)
IDX = grid[[f + "_i" for f in FACT]].to_numpy()
N = len(grid)
assert N == np.prod(NL) and (np.ravel_multi_index(IDX.T, NL) == np.arange(N)).all()

SCREEN = np.sort(np.random.default_rng(13).choice(N, 60, replace=False))  # the robot's 60 runs
screen = grid.loc[SCREEN, FACT + ["yield"]]
TRUTH = grid["yield"].to_numpy()  # the audit: hands off until Task 4
CANDIDATES = np.setdiff1d(np.arange(N), SCREEN)  # the 1,668 conditions nobody has run
for f, lv in zip(FACT, LEV):
    print(f"{f:13s} {len(lv):2d} levels: {', '.join(map(str, lv))}")
screen.sort_values("yield", ascending=False).head(8)
edbo_arylation: A fully enumerated high-throughput screen of a palladium-catalysed direct (C-H) arylation: every combination of 12 phosphine ligands x 4 carboxylate bases x 4 solvents x 3 concentrations x 3 temperatures, one yield each (1,728 rows): ligand, base, solvent, concentration (M: 0.057, 0.1, 0.153), temperature (C: 90, 105, 120), yield (%, 0-100).
  source: Shields, Stevens, Li, Parasram, Damani, Martinez Alvarado, Janey, Adams & Doyle (2021), Bayesian reaction optimization as a tool for chemical synthesis, Nature 590:89-96; data from the authors' repository github.com/b-shields/edbo (MIT licence, (c) 2020 Benjamin J. Shields). BUILT by tools/build_e82_arylation.py (SMILES replaced by the names in the repository's *-list.csv files) - keep the cached file.
  url:    https://raw.githubusercontent.com/b-shields/edbo/master/experiments/data/direct_arylation/experiment_index.csv
ligand        12 levels: BrettPhos, CgMe-PPh, GorlosPhos HBF4, JackiePhos, P(fur)3, PCy3 HBF4, PPh2Me, PPh3, PPhMe2, PPhtBu2, X-Phos, tBPh-CPhos
base           4 levels: CsOAc, CsOPiv, KOAc, KOPiv
solvent        4 levels: BuCN, BuOAc, DMAc, p-Xylene
concentration  3 levels: 0.057, 0.1, 0.153
temperature    3 levels: 90, 105, 120
ligand base solvent concentration temperature yield
202 CgMe-PPh CsOPiv DMAc 0.100 105 91.11
199 CgMe-PPh CsOPiv DMAc 0.057 105 91.06
1452 X-Phos CsOAc BuOAc 0.100 90 85.24
1444 X-Phos CsOAc BuCN 0.100 105 83.83
1678 tBPh-CPhos KOAc DMAc 0.100 105 78.95
1534 X-Phos KOAc DMAc 0.100 105 65.37
1525 X-Phos KOAc BuOAc 0.100 105 64.62
4 BrettPhos CsOAc BuCN 0.100 105 64.48

Task 0 · Look at the screen

Only the 60 screened yields. Deliver

  1. The distribution of the yields: how many are exactly 0, how many are traces (below 1%), how close does anything come to 100%?
  2. How much of the design does the screen cover: reactions per ligand, and how many of the 48 ligand x solvent and 48 ligand x base combinations were run at all?
  3. One sentence: what does a likelihood for these numbers have to be able to do?
Your turn Work this task in the notebook.

Task 1 · The default: a Gaussian ANOVA on raw yield

The model most people write first: yield (%) = intercept + one effect per level of each of the five factors + Normal noise, with the effects of each factor shrunk towards zero by a shared scale (a hierarchical ANOVA, E02).

Deliver

  1. A fitted model with clean diagnostics.
  2. A posterior predictive check aimed at the features you listed in Task 0.
  3. Predictions for all 1,728 conditions (the "table" the model expects - latent mean plus noise): what share of them is negative, and what is the posterior of the best achievable yield (the largest predicted yield over the grid)? Would you show this to the head of the group?
Your turn Work this task in the notebook.

Task 2 · A likelihood that knows what a yield is

Deliver

  1. A likelihood that respects the bounds of a yield and produces a pile of zeros/traces and a ceiling naturally. Justify the scale on which factors act, and how you treat 0 and 100.
  2. A prior predictive check on the yield scale: what share of reactions does your prior expect to fail, and what share to exceed 95%?
  3. The fit, diagnostics, and the same posterior predictive check as in Task 1.
Your turn Work this task in the notebook.

Task 3 · Interactions from 60 reactions

Chemists will tell you that a ligand can work in one solvent and fail in another. With 60 reactions spread over 48 ligand x solvent cells, most cells have been run once or never.

Deliver

  1. Add ligand x solvent, ligand x base and base x solvent interactions to the model of Task 2, each shrunk towards zero by its own scale. Read the diagnostics carefully: something will be harder to sample than before. Find which parameters, explain why in terms of the data, and decide whether it matters for what the group needs - predictions of yields.
  2. Posterior of the three interaction scales, and a LOO comparison with the additive model.
  3. Which ligand x solvent combinations does the model think are special, and how sure is it?
Your turn Work this task in the notebook.

Task 4 · The whole grid: which condition is best, and how good can it get?

Deliver, from your best model (and, for contrast, the Task 1 model):

  1. For every one of the 1,728 conditions, the posterior probability that it is the best one (screened conditions keep their measured yield). The ten most likely, with their posterior yield. How concentrated is the probability?
  2. The posterior of the best achievable yield and of the probability that some condition reaches 95%.
  3. Now open the audit: score each model's predictions of the 1,668 unscreened yields against TRUTH (a proper scoring rule, interval coverage overall and among the truly good conditions), and say where the probability of being best actually went.
Your turn Work this task in the notebook.

Task 5 · Commit now, or buy k more reactions?

Deliver

  1. For k = 0, 4, 8, 12, 16, 24, 32, 48 extra reactions: which k conditions you would run (your rule, from the posterior), the posterior distribution of the gain in yield over the current best measured condition, the expected net value in dollars (brief's economics), and the probability that it pays (net value > 0).
  2. The same table from the Task 1 model. Do the two models recommend the same thing?
  3. A recommendation to the head of the group in three sentences.
  4. Then, using TRUTH: what would each recommendation actually have earned?
Your turn Work this task in the notebook.

Task 6 · One screen is one anecdote: replay the decision

A single realised outcome cannot tell you whether a decision rule is good. Because you have the full table, you can rerun the group's situation on other random screens of 60.

Deliver for at least six other screens, with both the Task 1 model and your best model: the recommended k, the expected net value, and the realised net value against TRUTH. Then a calibration check: where does the realised gain fall in each model's predictive distribution of the gain (a PIT value)? Is either model systematically over- or under-optimistic?

Your turn Work this task in the notebook.

Going further

  • Correlated residuals. The interaction model's noise is independent across conditions, so a batch of near-identical conditions looks like several independent lottery tickets. Add a ligand x base x solvent effect (or a GP over concentration and temperature within each cell, E82) and see whether the batch spreads out and the value of a round changes.
  • Batches that learn. The greedy batch ignores that running a reaction also teaches the model about its neighbours. Compare it with two rounds of half the size, refitting in between (a two-stage decision; E82's loop is the limit).
  • The chemists' game. edbo_arylation_game holds 50 chemists' choices on the same table. Give each chemist's first 60 reactions to the model as the screen: is a chemist's (non-random) screen worth more or less to the model than a random one, and does the decision change?
  • Noisy yields. Add replicate noise (say sd 5 points) to the screen and to the extra round, and let the group re-run the apparent winner before committing (E82 part H). What is a replicate worth?
h.progress()

C13: 0/20 hints used, 16/16 checks passed.

Reference solution

A complete, executed solution with commentary on every task — including the ways the obvious model fails. Try the hint ladder first.

Open the worked solution →