Sampler and results#
mergen.sampler#
Space-filling design via the Stochastic Coordinate Exchange (SCE) engine.
This module exposes four objects:
Sampler— configure and run the design construction.
SamplingResult— container for design + validation + extras.
FocusPoint— denser sampling around a critical region.
ExclusionPoint— repel sampling away from a region.
Quick start#
from mergen.space import ParameterSpace
from mergen.sampler import Sampler
space = ParameterSpace({'temperature': range(100, 400, 10),
'pressure' : ('continuous', 0.5, 5.0)})
sampler = Sampler(space)
sampler.add_prescribed([[200, 2.5]], in_design=True, in_optim=False)
sampler.add_focus ([350, 4.5], spread=1.5, in_design=True, in_optim=True)
sampler.add_exclusion([100, 0.5], spread=1.0)
sampler.set_design(n_samples=30)
sampler.set_sce(n_restarts=5)
result = sampler.run(criteria='umaxpro', seed=44)
result.summary()
Algorithm#
The optimisation is a Stochastic Coordinate Exchange (SCE) outer-inner loop:
Inner loop — for each restart, repeatedly pick one row of the design and propose new values along one coordinate at a time, accepting changes that improve the criterion. The 1D nature makes the proposal cost O(n) (see
mergen.criteria) and reduces multi-dimensional feasibility constraints to one-dimensional box constraints, which is the key advantage demonstrated by Kang (2019).Outer loop — Iterated Local Search (Lourenço, Martin & Stützle 2003): when the inner loop stalls, perturb the current best design and run the inner loop again. The best design across all restarts is returned.
The first restart is seeded by a greedy maximin scheme (Morris & Mitchell 1995); subsequent restarts are seeded by ILS kicks from the current best (rather than random restart) for variance reduction across seeds.
References
- Meyer, R. K. & Nachtsheim, C. J. (1995).
The coordinate-exchange algorithm for constructing exact optimal experimental designs. Technometrics, 37(1), 60–69.
- Kang, L. (2019).
Stochastic coordinate-exchange optimal designs with complex constraints. Quality Engineering, 31(3), 401–416.
- You, Y., Jin, G., Pan, Z. & Guo, R. (2021).
MP-CE method for space-filling design in constrained space with multiple types of factors. Mathematics, 9(24), 3314.
- Lourenço, H. R., Martin, O. C. & Stützle, T. (2003).
Iterated Local Search. In Handbook of Metaheuristics, Springer, 320–353.
- Morris, M. D. & Mitchell, T. J. (1995).
Exploratory designs for computational experiments. J. Statist. Plan. Infer., 43, 381–402.
- Loeppky, J. L., Sacks, J. & Welch, W. J. (2009).
Choosing the sample size of a computer experiment: A practical guide. Technometrics, 51(4), 366–376.
- Kennard, R. W. & Stone, L. A. (1969).
Computer aided design of experiments. Technometrics, 11(1), 137–148.
- class mergen.sampler.FocusPoint(point, spread=1.0, n_samples=None, include_center=None, in_design=True, in_optim=True)[source]#
Bases:
objectA critical region where denser sampling is desired.
- Parameters:
point (array-like, shape (n_parameters,)) – Grid coordinates of the focus centre.
spread (float) – Gaussian kernel width in normalised grid steps.
n_samples (int or None) – Number of focus samples to draw.
None→ auto:max(1, int((2 * d + 1) * spread)).include_center (bool or None) –
True→ the centre is guaranteed to appear in the design.False→ the centre is excluded from the candidate pool.None→ stochastic (the centre competes with its neighbours through the Gaussian-weighted draw).in_design (bool) –
True→ focus samples count toward then_samplesbudget.False→ focus samples are added on top ofn_samplesand the total design size grows.in_optim (bool) –
True→ the optimiser sees these points and pushes optimised points away.False→ the optimiser ignores these points (they are only reserved against re-selection).
- class mergen.sampler.ExclusionPoint(point, spread=1.0)[source]#
Bases:
objectA region to avoid: the centre is hard-excluded from the candidate pool and its neighbourhood receives a Gaussian soft-repulsion penalty during coordinate-exchange candidate selection.
- Parameters:
point (array-like, shape (n_parameters,)) – Grid coordinates of the exclusion centre.
spread (float) – Gaussian repulsion kernel width in normalised grid steps.
- class mergen.sampler.SamplingResult(samples, validation, space)[source]#
Bases:
objectContainer returned by
Sampler.run().- Variables:
samples (pd.DataFrame) – Main design (prescribed + focus + optimised points).
validation (pd.DataFrame) – Kennard-Stone validation set.
sets (dict[str, pd.DataFrame]) – Extra named sets (e.g.
{'test': df, 'holdout': df}).designs (dict[str, pd.DataFrame]) – Designs per criterion when multiple criteria are run.
space (ParameterSpace)
- Parameters:
samples (pd.DataFrame)
validation (pd.DataFrame)
space (ParameterSpace)
- PRESCRIBED = 'Prescribed'#
- FOCUS = 'Focus'#
- OPTIMISED = 'Optimised'#
- VALIDATION = 'Validation'#
- quality_report(metrics='default', criteria_metrics=None, mc_samples=300, verbose=True)[source]#
Compute and print design quality metrics.
Delegates to
mergen.metrics.quality_report().Each metric is reported both as a raw value and as a percentile rank against a Monte Carlo baseline of random designs of the same size, so the table shows not just the value but whether it is good. Pass
mc_samples=0to skip the baseline (values only).- Parameters:
metrics (
'default'or list of metric names)criteria_metrics (list of criterion names to evaluate post-hoc)
mc_samples (number of random designs for the Monte Carlo) – baseline (
0disables it; default300)verbose (print the metrics table (default
True))
- Returns:
dict of metric values and (optionally) baseline statistics
- Return type:
- comparison()[source]#
Compare quality metrics across all criteria in
self.designs. Returns a DataFrame (criteria × metrics).- Return type:
- plot(kind='pairplot', **kwargs)[source]#
Visualise the design. Delegates to
mergen.output.- Parameters:
kind (
'pairplot'|'1d'|'2d'|'distances') –'quality'|'all'- Return type:
None
- class mergen.sampler.ComparisonResult(table, results, best, priority)[source]#
Bases:
objectOutcome of
Sampler.compare().- Variables:
table (pandas.DataFrame) – One row per (criterion, algorithm) combination, sorted by the priority metrics; columns hold percentile ranks (0-100, higher is better) against the shared Monte Carlo baseline. The winner is flagged in the
bestcolumn.results (dict) – Maps
(criterion, algorithm)to the fullSamplingResult, so any candidate design can be inspected or exported, not only the winner.best (tuple of (str, str)) – The winning (criterion, algorithm) pair.
priority (tuple of str) – The metrics that defined the ranking, in order.
- property best_result: SamplingResult#
The full SamplingResult of the winning combination, identical to calling run() for that combination with the same seed and n_repeats.
- plot(save=False, filename=None, show=True, **kwargs)[source]#
Heat map of the percentile-rank table.
Rows are (criterion, algorithm) combinations, columns are the quality metrics, cell colour encodes the percentile rank; the winning row is starred. Saved under
outputs/whensave=True.
- to_markdown(filename)[source]#
Save the ranked comparison table as a Markdown file under the output directory.
Unlike calling
comparison.table.to_markdown(...)directly, this writes intooutputs/(creating it if needed), matching where the plots and design exports are saved.- Parameters:
filename (str)
- Return type:
None
- class mergen.sampler.Sampler(space)[source]#
Bases:
objectSpace-filling design sampler using Stochastic Coordinate Exchange (SCE).
The sampler is configured fluently —
add_*methods register prescribed/focus/exclusion points,set_*methods set sizes and optimiser hyperparameters, andrun()produces the design.- Parameters:
space (ParameterSpace)
Examples
>>> space = ParameterSpace({'x': range(1, 21), 'y': range(1, 21)}) >>> sampler = Sampler(space) >>> sampler.set_design(n_samples=20) >>> result = sampler.run(seed=44) >>> result.summary()
- add_prescribed(points, in_design=True, in_optim=False)[source]#
Add one or more prescribed (fixed) points.
Prescribed points are always present in the final design and are never moved by the optimiser.
- Parameters:
points (array-like) – Single point
[x1, x2, ...]or list of points. All points must lie on the parameter grid.in_design (bool) –
True→ counted within then_samplesbudget.False→ added on top ofn_samples(the total design size grows accordingly).in_optim (bool) –
True→ the optimiser sees these points and pushes optimised points away from them.False→ the optimiser ignores them (the points are only reserved against being re-selected).
- Returns:
self
- Return type:
- add_set(name, points, color=None)[source]#
Add a user-supplied named point set (e.g. an external test set).
The points are validated against the parameter grid, reserved so the optimiser cannot re-select them, and carried into the result as
result.sets[name]. They appear in plots undernameand in exports as a separate table.- Parameters:
name (str) – Label of the set (e.g.
'test'). Must not collide with the built-in labels'Optimised','Validation','Prescribed'or'Focus', nor with a generated extra set or a previously added set.points (array-like) – Single point
[x1, x2, ...]or list of points. All points must lie on the parameter grid.color (str, optional) – Matplotlib colour used for this set in plots (e.g.
'#3a86ff'). If omitted, a neutral fallback colour is assigned by the plotting layer.
- Returns:
self
- Return type:
- load_design(points, name='Existing', color=None)[source]#
Load an existing design instead of optimising a new one.
The supplied points become the design itself:
run()skips optimisation entirely and only generates the requested validation set and any extra sets (Kennard-Stone) around them. Usemergen.sequential.extend()instead if you want to grow the design with new optimised points.- Parameters:
points (array-like or pandas.DataFrame) – The existing design. A DataFrame is matched to the parameter space by column names; an array must have one column per parameter in space order. All points must lie on the parameter grid.
name (str) – Label under which the points appear in plots, summaries and exports (default
'Existing').color (str, optional) – Matplotlib colour for this label in plots. Defaults to the Optimised blue (
'#3a86ff').
- Returns:
self
- Return type:
- add_focus(point, spread=1.0, n_samples=None, include_center=None, in_design=True, in_optim=True)[source]#
Add a focus point — denser sampling near a critical region.
- Parameters:
point (grid coordinates of the focus centre)
spread (Gaussian kernel width in normalised grid steps)
n_samples (number of focus samples (
None→ auto))include_center (
True/False/None) – (guaranteed / excluded / stochastic)in_design (
True→ within budget;False→ extra)in_optim (
True→ the optimiser sees these points)
- Returns:
self
- Return type:
- add_exclusion(point, spread=1.0)[source]#
Add an exclusion point — sampling avoids this region.
The centre is hard-excluded from the candidate pool. Its neighbourhood receives a Gaussian soft-repulsion weight that downweights candidate values during coordinate-exchange selection.
- Parameters:
point (grid coordinates of the exclusion centre)
spread (Gaussian repulsion kernel width)
- Returns:
self
- Return type:
- set_design(n_samples=None, n_validation=None, extra_sets=None)[source]#
Configure design size and holdout sets.
- Parameters:
n_samples (total design size (
None→ 10 × n_parameters,) – Loeppky, Sacks & Welch 2009).n_validation (validation set size (
None→ 20% of) –n_samples).extra_sets (dict of
{name: size}for additional) – Kennard-Stone holdout sets, e.g.{'test': 10, 'holdout': 5}.
- Returns:
self
- Return type:
- set_optimizer(name, **kwargs)[source]#
Configure hyperparameters for an optimisation algorithm.
The named algorithm must be registered in
mergen.algorithms(e.g.'sa','sce','ese'). Hyperparameters are validated against the algorithm’sget_default_params()schema; unknown keys raiseValueError.Calling this method does not run the optimiser — it only stores the parameters for later use by
run(). You can call it multiple times to configure different algorithms.- Parameters:
name (str) – The registered name of the optimiser (e.g.
'sa').**kwargs – Hyperparameters specific to the chosen algorithm. See each algorithm’s documentation for the full list.
- Returns:
self – Enables fluent chaining.
- Raises:
KeyError – If
nameis not a registered optimiser.ValueError – If any
kwargskey is not a valid hyperparameter for the algorithm.
- Return type:
Examples
>>> sampler.set_optimizer('sa', n_restarts=5, max_iter=10000) >>> sampler.set_optimizer('sce', n_complexes=4) >>> sampler.set_optimizer('ese', n_outer=20, n_inner=100)
- set_dimension_weights(weights)[source]#
Set per-dimension importance weights for greedy maximin seeding.
- Parameters:
weights (dict
{name: weight}or list of floats) – (one weight per parameter, in parameter order).- Returns:
self
- Return type:
- run(criteria='umaxpro', algorithm='sa', seed=44, n_repeats=1, priority=None, n_jobs=None, verbose=True)[source]#
Generate the space-filling design.
- Parameters:
criteria (str, list of str, or BaseCriterion instance) – Optimisation criterion (or list of criteria). Available built-in names:
'umaxpro','maxpro','phi_p','cd2','stratified'. Default'umaxpro'.algorithm (str or list of str, optional) – Name of the optimiser (or list of names) to run. Each name must be a registered optimiser (see
mergen.list_optimizers()). When a list is given, every algorithm is run independently and the results are collected inSamplingResult.algorithm_results. Default'sa'.seed (int or None) – Random seed for reproducibility (default 44).
n_repeats (int, default 1) – Number of independent optimisation repeats. Repeat seeds are derived from
seedvia NumPy’s SeedSequence.spawn; the representative repeat is returned (seepriority).priority (tuple of str, optional) – Quality-metric names used to pick the representative repeat when
n_repeats > 1.None(default) keeps the repeat with the lowest criterion score (classic best-of-k restarts). When given — e.g.('min_distance', 'max_abs_correlation')— the repeat whose design lies closest to the Utopia point of these metrics is returned instead (optimise-by-proxy, select-by-objective), which keeps the delivered design stable when near-optimal criterion scores correspond to different coverage trade-offs. Pass the same tuple used incompare()to reproduce itsbest_resultexactly.n_jobs (int or None, optional) – Number of parallel workers for the multi-algorithm dispatcher.
None(default) and1run sequentially. Positive integers request that many workers (up to the number of CPUs). Negative integers follow the joblib convention:-1for all CPUs,-2for all but one, and so on. Parallelism applies only across distinct algorithms; the per-algorithm restart loop is ILS-based and therefore intentionally serial. Whenn_jobsis greater than the number of algorithms, the extra workers are unused.verbose (bool, optional) – Print progress information. Default
True.
- Returns:
SamplingResult – Contains the (per-algorithm) optimised designs and quality metadata. When a single algorithm is requested,
SamplingResult.samplesexposes its design directly; when multiple algorithms are requested, useSamplingResult.algorithm_resultsto access each individually andSamplingResult.comparison()to tabulate them.- Return type:
- compare(criteria=None, algorithms=None, priority=('min_distance', 'max_abs_correlation'), mc_samples=300, seed=44, n_repeats=5, n_jobs=None, verbose=True)[source]#
Run a criterion/algorithm sweep and rank the resulting designs.
Every (criterion, algorithm) combination is optimised with the current sampler configuration. Because raw criterion scores are on incomparable scales, designs are ranked on criterion-agnostic quality metrics expressed as percentile ranks against a single shared Monte Carlo baseline of random designs of the same size (Joseph 2016; Pronzato & Mueller 2012).
- Parameters:
criteria (list of str, optional) – Criteria to sweep. If None, all criteria compatible with the space are used: nominal-supporting criteria when the space contains a nominal factor, all remaining criteria otherwise. An explicit list is used as given (with a warning if it mixes in criteria that do not match the factor types).
algorithms (list of str, optional) – Optimisers to sweep. If None,
['sa'].priority (tuple of str) – Quality metrics treated as competing objectives when selecting the best design. All are on a common 0-100 percentile scale (higher is better) and are honoured jointly via a Pareto/Utopia rule (Lu, Anderson-Cook & Robinson 2011): the non-dominated designs are kept, and the one closest to the Utopia point (every metric = 100) is chosen. No weights are needed and no single metric dominates. Available metrics: min_distance, mean_distance, cv_distances, minimax, max_abs_correlation, projection_cd2.
mc_samples (int, default 300) – Size of the shared Monte Carlo baseline.
seed (int, default 44) – Base seed. All randomness (the repeats and the Monte Carlo baseline) is derived from it, so the whole comparison reproduces exactly from this one number.
n_repeats (int, default 5) – Number of independent optimiser runs per combination. Each run uses a distinct seed derived from
seedvia NumPy’s SeedSequence.spawn (independent, not consecutive integers); the best-scoring repeat represents the combination, exactly asrun(seed, n_repeats)would deliver it, and the table reports that design’s metric percentiles. More repeats therefore improve every combination’s delivered design rather than averaging over seed luck. Usen_repeats=1for a single run.n_jobs (int or None, default None) – Number of parallel workers for the combination x repeat runs, following the joblib convention (None or 1 -> one worker, -1 -> all cores). The runs are independent, so this scales well; results are identical regardless of n_jobs.
verbose (bool, default True) – Print progress and the final ranking table.
- Returns:
ComparisonResult –
.table(ranked DataFrame of percentile ranks),.results(dict mapping (criterion, algorithm) to the full SamplingResult),.best(winning key) and.priority.- Return type: