Sequential utilities#
Sequential and augmented design utilities for Mergen.
This module collects design-time operations that work on existing designs: extending them with new points, filling in around reference locations, sub-selecting space-filling subsets from a pool, and assigning execution orders.
All functions in this module are surrogate-free — they look only at parameter-space geometry, never at simulation outputs. Adaptive sampling, active learning, expected improvement, and batch Bayesian optimisation are intentionally out of scope; they belong in a surrogate-modelling package.
Public API#
- extend(sampler, existing_design, n_new, …)
Append n_new space-filling points to an existing design while preserving every input row (Wang 2003; Qian 2009).
- fill_around(sampler, reference_points, n_new, …)
Generate n_new points that space-fill around a set of reference points; the reference points are criterion-visible but excluded from the output.
- subsample(sampler, pool, n_select, anchor=’center’)
Pick n_select space-filling points from an arbitrary candidate pool via Kennard-Stone selection in normalised coordinates (Kennard & Stone 1969).
- run_order(sampler, design, anchor=’center’, column=’run_order’)
Assign an execution rank so that every cumulative prefix of the design remains space-filling — useful for serial execution, early stopping, interim analyses.
Notes
The first argument of every public function is a Sampler. The
sampler supplies the parameter-space metadata (axis ranges,
parameter names, constraints) but its existing state — prescribed
points, focus regions, exclusions, n_samples, n_validation
— is preserved across the call via the private snapshot/restore
hooks _snapshot_state / _restore_state.
References
- Kennard, R. W. & Stone, L. A. (1969). Computer aided design of
experiments. Technometrics, 11(1), 137-148.
- Wang, G. G. (2003). Adaptive response surface method using
inherited Latin hypercube design points. Journal of Mechanical Design, 125(2), 210-220.
- Qian, P. Z. G. (2009). Nested Latin hypercube designs.
Biometrika, 96(4), 957-970.
- mergen.sequential.extend(sampler, existing_design, n_new, criteria='cd2', algorithm='sa', n_validation=0, seed=44, verbose=True)[source]#
Extend an existing design by
n_newspace-filling points.Every row of
existing_designis treated as a fixed, criterion-visible anchor;n_newadditional points are then optimised on top of them. The final design has shape(len(existing_design) + n_new, d)and the firstlen(existing_design)rows are exactly the input rows.This is the surrogate-free augmentation strategy of Wang (2003) and Qian (2009): preserve the historical runs, add new runs that maximise space-filling of the combined design.
- Parameters:
sampler (Sampler) – Parameter space, constraints, and any pre-set design state (focus regions, exclusions, etc.). The sampler is not permanently modified; any state added during this call is rolled back on exit.
existing_design (np.ndarray or DataFrame or list) – Points that must appear in the final design unchanged. DataFrame columns must include all parameter names.
n_new (int) – Number of new points to add.
criteria (str) – Passed through to
Sampler.run(). Defaults match the one-shot workflow.algorithm (Union[str, List[str]]) – Passed through to
Sampler.run(). Defaults match the one-shot workflow.n_validation (int) – Passed through to
Sampler.run(). Defaults match the one-shot workflow.seed (Optional[int]) – Passed through to
Sampler.run(). Defaults match the one-shot workflow.verbose (bool) – Passed through to
Sampler.run(). Defaults match the one-shot workflow.
- Returns:
SamplingResult
- Return type:
References
Wang, G. G. (2003). Journal of Mechanical Design, 125(2). Qian, P. Z. G. (2009). Biometrika, 96(4).
- mergen.sequential.fill_around(sampler, reference_points, n_new, criteria='cd2', algorithm='sa', n_validation=0, seed=44, verbose=True)[source]#
Generate
n_newpoints that space-fill the region around a set of reference points.Reference points are visible to the criterion (so new points keep their distance from them) but are not included in the final design. Use this when you have e.g. literature or pilot points that you do not want to re-run, and you want subsequent runs to cover the rest of the space efficiently.
- Parameters:
sampler (Sampler)
reference_points (np.ndarray or DataFrame or list) – Coordinates of the points to fill around. Not included in the output.
n_new (int) – Number of new points to generate.
criteria (str) – See
Sampler.run().n_validation (int) – See
Sampler.run().seed (Optional[int]) – See
Sampler.run().verbose (bool) – See
Sampler.run().
- Returns:
SamplingResult – Contains exactly
n_newdesign points.- Return type:
- mergen.sequential.subsample(sampler, pool, n_select, anchor='center')[source]#
Pick
n_selectspace-filling points from a candidate pool.Uses Kennard-Stone selection in normalised parameter coordinates: each pick maximises the minimum distance to the already-picked points.
- Parameters:
sampler (Sampler) – Used only for parameter-space metadata (axis ranges, names). The sampler is not modified.
pool (np.ndarray or DataFrame) – Candidate pool. DataFrame columns must include all parameter names; the DataFrame index and extra columns are preserved on the output. ndarrays return a plain ndarray.
n_select (int) – Number of points to select.
1 ≤ n_select ≤ len(pool).anchor ({'center', 'maximin', int, None}, default 'center') – How to seed the selection. See
_kennard_stone_arrayfor details.'center'is the most common choice;'maximin'reproduces the original Kennard-Stone (1969) behaviour.
- Returns:
np.ndarray or DataFrame – The selected rows, in selection order. Type matches the input.
- Return type:
Union[np.ndarray, pd.DataFrame]
References
- Kennard, R. W. & Stone, L. A. (1969). Technometrics, 11(1),
137-148.
- mergen.sequential.run_order(sampler, design, anchor='center', column='run_order')[source]#
Assign an execution order so that every cumulative prefix of the design remains space-filling.
Implements a sequential Kennard-Stone reordering: row 0 is the chosen anchor; row k (for
k > 0) is the design point most distant (in normalised Euclidean distance) from rows0, …, k-1. Stopping the run after any prefix yields a balanced sub-design rather than a clustered one.Use this when:
Runs are executed serially (wetlab, paid HPC time, single-node simulations)
You may stop early or hit a budget cut
Interim analyses or progress reporting may happen before completion
- Parameters:
sampler (Sampler) – Provides parameter-space metadata for normalisation.
design (np.ndarray or DataFrame) – Design to reorder. DataFrame columns must include all parameter names; existing DataFrame columns are preserved.
anchor ({'center', 'maximin', int, None}, default 'center') – How to pick the first run. See
subsample().column (str, default 'run_order') – Name of the column added to the returned DataFrame holding the execution rank (
0= first to run).
- Returns:
DataFrame – A copy of
designreordered by execution rank, with the newcolumncolumn. The original DataFrame index is preserved.- Return type:
pd.DataFrame
References
- Kennard, R. W. & Stone, L. A. (1969). Technometrics, 11(1),
137-148.
- mergen.sequential.k_fold_split(sampler, design, k, anchor='center')[source]#
Split a design into
kspace-filling folds for cross-validation.Returns
k(train_indices, test_indices)pairs in the same format assklearn.model_selection.KFold. Within each pair, the test fold and the training set are both space-filling — every fold covers the parameter space rather than clustering in a sub-region, so cross-validation estimates are not biased by an unlucky split.Algorithm#
The design is first reordered with
run_order()(sequential Kennard-Stone). Then a round-robin assignment distributes rows to folds: row 0 → fold 0, row 1 → fold 1, …, row k-1 → fold k-1, row k → fold 0, and so on. Because consecutive rows of the KS-ordering are placed as far apart as possible, each fold ends up with one representative from every region of the space.- param sampler:
Provides parameter-space metadata for the normalisation used inside
run_order().- type sampler:
Sampler
- param design:
The full design to split. DataFrame columns must include all parameter names.
- type design:
np.ndarray or DataFrame
- param k:
Number of folds.
2 ≤ k ≤ len(design). Folds are as balanced as possible — the firstlen(design) mod kfolds receive one extra row each.- type k:
int
- param anchor:
First-point strategy for the underlying KS ordering. See
subsample().- type anchor:
{‘center’, ‘maximin’, int, None}, default ‘center’
- returns:
list of (train_indices, test_indices) – Position-based indices (
0 ≤ idx < len(design)) into the inputdesign. Compatible withdesign.iloc[idx]for DataFrames anddesign[idx]for ndarrays.
References
- Kennard, R. W. & Stone, L. A. (1969). Technometrics, 11(1),
137-148.
- Qian, P. Z. G. (2012). Sliced Latin hypercube designs.
Journal of the American Statistical Association, 107(497), 393-399. [Related concept of slicing a design into space-filling sub-designs.]
- mergen.sequential.nested(sampler, n_outer, n_inner, criteria='cd2', algorithm='sa', seed=44, verbose=True)[source]#
Build a nested pair of designs: a small inner design that is a subset of a larger outer design, both space-filling.
Useful for multi-fidelity computer experiments, hierarchical sensitivity studies, and any setting where two budget tiers run on the same parameter space (e.g. fast/slow simulation, pilot vs. main wetlab study). Every row of
innerappears verbatim inouter.Algorithm#
Build the outer design via
Sampler.run()on the current Sampler state (n_samples = n_outer).Pick the inner design as a maximin Kennard-Stone subsample of the outer (
subsample()withanchor='maximin').
The resulting inner design is space-filling within outer, and every outer point is space-filling in the full parameter space.
The classical He & Qian (2011) construction enforces a strict LHS-preserving nesting; this pragmatic implementation does not guarantee that, which is acceptable for the typical use case of multi-fidelity computer experiments (where space-fillingness matters more than the LHS marginals of the inner design). A strict LHS-nested variant is planned for a future release.
- param sampler:
- type sampler:
Sampler
- param n_outer:
Size of the outer (larger) design.
- type n_outer:
int
- param n_inner:
Size of the inner (smaller) design.
1 ≤ n_inner < n_outer.- type n_inner:
int
- param criteria:
Passed to
Sampler.run()when building the outer design.- param algorithm:
Passed to
Sampler.run()when building the outer design.- param seed:
Passed to
Sampler.run()when building the outer design.- param verbose:
Passed to
Sampler.run()when building the outer design.- returns:
outer (DataFrame, shape (n_outer, d + 1)) – The outer design with one extra boolean column
'in_inner'flagging the rows that belong to the inner design.inner (DataFrame, shape (n_inner, d + 1)) – The inner design (subset of outer, in KS selection order). Same columns as
outer; the'in_inner'column is constantTrue.
References
- He, X. & Qian, P. Z. G. (2011). Nested orthogonal array-based
Latin hypercube designs. Biometrika, 98(3), 721-731.
- Qian, P. Z. G. (2009). Nested Latin hypercube designs.
Biometrika, 96(4), 957-970.