Sequential / adaptive DoE¶
Classical DoE is often taught as a one-shot plan. In practice you run a wave, look at the data, and decide what to run next. From doekit 0.5 that loop is first-class — without abandoning D/A/G-efficiency, SPV or power as the common language.
Augment a design¶
Keep the runs you already have; add points that maximize information of the combined design:
import doekit as ed
base = ed.random_design(
[ed.ContinuousFactor("x1", -1, 1), ed.ContinuousFactor("x2", -1, 1)],
n=6, seed=0,
)
base.model = ed.Model.parse("0 ~ x1 + x2 + x1:x2")
aug = ed.augment_design(base, n_add=4, criterion="D")
# aug.metadata: n_original, n_added, criterion, kind="AugmentedDesign"
Propose the next batch¶
nxt = ed.propose_next_runs(base, response=y, n_add=4, budget=16)
print(nxt.rationale)
print(nxt.comparison.summary) # "worth the extra runs?"
nxt.added.matrix # run sheet for the lab
nxt.to_dict() # schema: doekit.NextRunsProposal/1
- Without
response: information-based augmentation (D/I/…). - With
response: residualsigma_hat, active terms (p-value cutoff), power deltas use the empirical noise.
Compare designs¶
cmp = ed.compare_designs(current, augmented)
print(cmp.summary)
cmp.table # Δ D/A/G, SPV_mean, mean_power, n_runs
Bridge to Bayesian optimization¶
The two intentions share one call: learn (this page — sharpen the model) and
optimize (move the result). For doekit's own surrogate + acquisition loop — a GP
with an OLS prior mean, EI/UCB/PI/EHVI, and LOO calibration — see
Bayesian optimization (the intent="optimize"
path). doekit also does not replace Optuna/Ax: you can turn a search space into
a candidate set and keep evaluating with the same metrics:
cand = ed.candidates_from_bounds([("lr", 1e-4, 1e-1), ("wd", 1e-6, 1e-2)], n=200)
# or: ed.candidates_from_skopt_space(space) # requires doekit[bo]
nxt = ed.propose_next_runs(design, n_add=4, candidates=cand)
Related shipping surface (doekit ≥ 0.6 / 0.7): mixture & split-plot,
and the aggregate ed.experiment(...) / Experiment loop in the
agents cheat sheet.