Parameter space#

mergen.space#

ParameterSpace : defines the parameter grid for the sampling design. GridSampler : memory-efficient bijective grid representation.

Supported parameter types#

Discrete (explicit values):

[100, 200, 300] list / tuple range(10, 110, 10) Python range np.arange(0.1, 1.1, 0.1) numpy array np.linspace(0, 1, 10) any 1-D iterable

Continuous (Mergen builds the grid):

('continuous', 0.5, 5.0)                linear grid
('continuous', 1e-4, 1e-1, 'log')       log-spaced grid

Integer (Mergen builds the grid):

('integer', 2, 10)                       all integers in [min, max]
('integer', 8, 256, 'log')              log-spaced integers (rounded, unique)

Usage#

# Dict — all at once
space = ParameterSpace({
    'voltage':  [100, 200, 300, 400],
    'pressure': ('continuous', 0.5, 5.0),
    'lr':       ('continuous', 1e-4, 1e-1, 'log'),
    'n_layers': ('integer', 2, 10),
    'batch':    ('integer', 8, 256, 'log'),
})

# Fluent — one at a time
space = ParameterSpace()
space.add_parameter('voltage', [100, 200, 300])
space.add_parameter('pressure', ('continuous', 0.5, 5.0))

# Constrained
space.add_constraint(lambda p: p['x'] + p['y'] <= 10)

References

Morris & Mitchell (1995), J. Statist. Plan. Infer. 43. [greedy maximin seed]

class mergen.space.ParameterSpace(parameters=None, resolution=100)[source]#

Bases: object

N-dimensional discrete parameter space for space-filling design.

Each parameter is internally represented as a sorted 1-D float array (the grid). Continuous and integer parameters are automatically discretised to a grid; SA operates on this grid via coordinate swap.

Parameters:
  • parameters (dict, optional) – Mapping of {name: spec} where spec is any supported parameter specification (see module docstring).

  • resolution (int)

Examples

>>> from mergen.space import ParameterSpace
>>> space = ParameterSpace({
...     'voltage':  [100, 200, 300, 400],
...     'pressure': ('continuous', 0.5, 5.0),
...     'lr':       ('continuous', 1e-4, 1e-1, 'log'),
... })
>>> space.n_parameters
3
add_parameter(name, spec, resolution=None)[source]#

Add a parameter axis to the space.

Parameters:
  • name (str) – Parameter name — used as column header in output DataFrames. May contain spaces, Greek letters, LaTeX, or any Unicode.

  • spec (array-like or tuple) –

    Supported formats:

    [100, 200, 300]                                # discrete explicit
    range(10, 110, 10)                             # discrete range
    np.arange(0.1, 1.1, 0.1)                       # discrete numpy
    ('continuous', 0.5, 5.0)                       # linear grid
    ('continuous', 1e-4, 1e-1, 'log')              # log grid
    ('integer', 2, 10)                             # integer grid
    ('integer', 8, 256, 'log')                     # log-integer grid
    ('continuous', 0.5, 5.0, {'resolution': 500})  # custom res
    ('ordinal', ['low', 'med', 'high'])            # ordered categories
    ('nominal', ['A', 'B', 'C'])                   # unordered categories
    

    Ordinal and nominal parameters are stored internally as integer level indices [0, 1, ..., k-1]; the string labels are kept for input/output round-tripping. Which criteria can be used with each type is enforced by Sampler.run().

  • resolution (int, optional) – Grid size for continuous / integer-log parameters. Overrides the space-level resolution for this parameter only. Default: space-level resolution (set at construction).

Returns:

self — enables fluent chaining.

Return type:

ParameterSpace

add_constraint(fn)[source]#

Add a feasibility constraint.

The callable receives a single dict mapping parameter names to their current values and must return True for feasible points.

Parameters:

fn (callable) –

Signature: fn(p: dict) -> bool

Examples:

lambda p: p['x'] + p['y'] <= 10
lambda p: p['pressure'] * p['temperature'] < 1000
lambda p: p['lr'] < 1e-2 or p['n_layers'] <= 3

Returns:

self

Return type:

ParameterSpace

set_resolution(resolution)[source]#

Update the default grid resolution for continuous / integer-log parameters and rebuild affected grids.

Note: parameters added with an explicit resolution override are not affected.

Parameters:

resolution (int) – New default resolution (>= 2).

Return type:

ParameterSpace

property names: List[str]#

Ordered list of parameter names.

property n_parameters: int#

Number of parameter axes.

property values: List[ndarray]#

List of grid arrays, one per parameter (in insertion order).

property n_levels: List[int]#

Number of grid levels for each parameter.

property bounds: List[tuple]#

(min, max) for each parameter.

property param_types: Dict[str, str]#

Dict mapping parameter name -> type. Possible values are 'discrete', 'continuous', 'integer', 'ordinal', or 'nominal'.

is_nominal(name)[source]#

Return True if name is a nominal (unordered) parameter.

Parameters:

name (str)

Return type:

bool

is_ordinal(name)[source]#

Return True if name is an ordinal (ordered) parameter.

Parameters:

name (str)

Return type:

bool

is_categorical(name)[source]#

Return True if name is either nominal or ordinal.

Parameters:

name (str)

Return type:

bool

category_labels(name)[source]#

Return the level labels of a nominal or ordinal parameter.

Returns an empty list for numeric parameters. The order matches the internal integer level indices [0, 1, ..., k-1], so category_labels(name)[i] is the label for level i.

Parameters:

name (str)

Return type:

List[str]

property nominal_names: List[str]#

Names of all nominal parameters, in insertion order.

property ordinal_names: List[str]#

Names of all ordinal parameters, in insertion order.

property categorical_names: List[str]#

Names of all categorical (nominal or ordinal) parameters.

property has_nominal: bool#

True if at least one parameter is nominal.

property has_categorical: bool#

True if at least one parameter is nominal or ordinal.

property is_mask: ndarray#

Boolean mask, one entry per parameter (in names order), marking which parameters are nominal. Useful for downstream code that needs to switch metrics on a per-column basis (Wilson & Martinez 1997 HEOM).

property n_candidates: int#

Number of feasible candidate points (after constraints).

property candidate_pool: ndarray#

Full Cartesian product of all parameter grids, filtered by constraints. Shape: (n_candidates, n_parameters).

The result is cached; invalidated automatically when parameters or constraints are added.

property gmins: ndarray#

Per-dimension minimum of the feasible candidate pool.

property granges: ndarray#

Per-dimension range of the feasible candidate pool. Never zero (degenerate dimensions get range = 1e-9).

property centroid: ndarray#

Nearest feasible grid point to the centroid of the candidate pool.

Computed as the mean of all feasible candidates, then snapped to the closest grid point.

Returns:

np.ndarray, shape (n_parameters,)

Example

>>> sampler.add_prescribed([space.centroid])
property corners: ndarray#

Feasible corner points of the parameter space.

Returns all combinations of per-parameter min/max values that satisfy the feasibility constraints. Infeasible corners are silently excluded.

Returns:

np.ndarray, shape (n_feasible_corners, n_parameters)

Example

>>> sampler.add_prescribed(space.corners)
property bounds_as_dict: Dict[str, tuple]#

{name: (min, max)}.

Returns:

dict[str, tuple]

Example

>>> space.bounds_as_dict
{'temperature': (100.0, 400.0), 'pressure': (0.5, 5.0)}
Type:

Parameter bounds as a dict

random_point(seed=44)[source]#

Return a single random feasible point from the candidate pool.

Parameters:

seed (int, default 44) – Random seed for reproducibility. Pass seed=None for a different point each call.

Returns:

np.ndarray, shape (n_parameters,)

Return type:

ndarray

Example

>>> sampler.add_prescribed([space.random_point()])
>>> space.random_point(seed=None)  # different each call
normalise(points)[source]#

Map points to [0, 1]^d using the feasible pool’s min/range.

Parameters:

points (array-like, shape (n,) or (n, d))

Returns:

np.ndarray, same shape as input

Return type:

ndarray

denormalise(points)[source]#

Inverse of normalise().

Parameters:

points (ndarray)

Return type:

ndarray

distance(x, y)[source]#

Normalised Euclidean distance between two points in [0, 1]^d.

Both points are normalised before distance computation so that all parameter axes contribute equally regardless of scale.

Parameters:
  • x (array-like, shape (d,))

  • y (array-like, shape (d,))

Returns:

float in [0, sqrt(d)]

Return type:

float

is_valid()[source]#

Return True if the space has at least one parameter and one candidate.

Return type:

bool

on_grid(point)[source]#

Return the row index of point in the candidate pool, or -1.

Parameters:

point (array-like, shape (n_parameters,))

Return type:

int

validate_point(point, label='Point')[source]#

Assert that point lies on the grid.

Parameters:
  • point (array-like, shape (n_parameters,))

  • label (str, prefix for the error message)

Returns:

pt (np.ndarray, shape (n_parameters,))

Raises:

ValueError if the point is not on the grid.

Return type:

ndarray

grid_sampler()[source]#

Return a GridSampler for this space.

Return type:

GridSampler

class mergen.space.GridSampler(space)[source]#

Bases: object

Bijective mapping between integer indices and grid points.

Represents the full Cartesian product grid without materialising it in memory. Uses a mixed-radix number system:

Mathematical basis#

For a grid with n_1 × n_2 × … × n_d points, define strides:

s_k = prod(n_{k+1}, ..., n_d)

Then for index i:

level_k = (i // s_k) mod n_k
x_k     = values_k[level_k]

This is the standard mixed-radix decomposition — exact and reversible.

Memory#

O(d × max_levels) for the value arrays only. A 10^10-point grid uses ~48 bytes vs ~800 GB for a full array.

param space:

type space:

ParameterSpace

References

Morris & Mitchell (1995), J. Statist. Plan. Infer. 43.

FULL_GREEDY_THRESHOLD: int = 500000#

Grids with fewer candidates than this are iterated exactly in greedy. Above the threshold, random sampling is used (unbiased in expectation).

index_to_point(idx)[source]#

O(d) bijection: integer index → grid point.

Parameters:

idx (int in [0, n_candidates))

Returns:

np.ndarray, shape (d,)

Return type:

ndarray

point_to_index(point)[source]#

O(d) bijection: grid point → integer index.

Parameters:

point (array-like, shape (d,))

Returns:

int — index in [0, n_candidates), or -1 if off-grid.

Return type:

int

random_point_excluding(reserved, max_tries=100000, rng=None)[source]#

Draw a uniformly random feasible grid point not in reserved.

For large grids where len(reserved) << n_candidates the rejection rate is negligible and this terminates in O(1) expected tries.

Parameters:
  • reserved (set of int) – Indices to skip.

  • max_tries (int, default 100000) – Upper bound on rejection sampling attempts.

  • rng (numpy.random.Generator, optional) – Random source. If None (default), Python’s global random module is used for backward compatibility with existing callers. Pass an explicit generator to make the call reproducible independently of the global random state — essential when running under joblib workers, where each process has its own global state that is not synchronised with the parent process.

Returns:

(point, index) ((np.ndarray, int) or (None, None) if exhausted)

greedy_maximin_seed(selected, budget, reserved, weights=None)[source]#

Greedy farthest-point seeding for SA initialisation.

Adds budget points to selected by iteratively choosing the candidate that maximises the minimum distance to already-selected points (weighted maximin criterion).

Small grids (≤ FULL_GREEDY_THRESHOLD):

Exact — iterates every non-reserved index. O(N × n × d).

Large grids (> FULL_GREEDY_THRESHOLD):

Samples K = min(N, max(10_000, 50 × n_selected)) random candidates per step. Unbiased in expectation; every point has positive probability of selection.

Parameters:
  • selected (np.ndarray, shape (n, d) — anchor points)

  • budget (int — number of additional points to add)

  • reserved (set of int — indices already in use (updated in-place))

  • weights (array-like, shape (d,), optional) – Per-dimension importance weights for distance computation. None → uniform weights.

Returns:

  • selected (np.ndarray, shape (n + budget, d))

  • reserved (set (updated))

References

Morris & Mitchell (1995), J. Statist. Plan. Infer. 43.

balanced_lhs_seed(selected, budget, reserved, rng=None, max_repair_attempts=50)[source]#

Build a balanced Latin-Hypercube initial design.

Adds budget rows on top of the anchor rows in selected so that the resulting full design (anchors + new rows) is balanced on every parameter axis: each level appears either floor(n_total / L_v) or ceil(n_total / L_v) times, where L_v is the number of levels on axis v.

When the anchor rows over-use a level (frequent in user scenarios with many prescribed points at the same value), the excess is redistributed to under-used levels so the whole design stays as balanced as possible.

This is the level-collapsing approach of Joseph, Gul & Ba (2018) adapted to a fixed grid.

Parameters:
  • selected (np.ndarray, shape (k, d)) – Anchor rows that must appear in the output (e.g. prescribed and focus points). Pass np.empty((0, d)) if none.

  • budget (int) – Number of additional rows to add. len(selected) + budget equals the final design size n_total.

  • reserved (set) – Grid indices already in use; updated in place to include the new rows’ indices.

  • rng (numpy.random.Generator, optional) – RNG for the level-pool shuffles. None → fresh default_rng(44).

  • max_repair_attempts (int, default 50) – Number of attempts to repair constraint violations via in-axis swaps before giving up.

Returns:

  • design (np.ndarray, shape (k + budget, d)) – Anchor rows stacked on top of the new balanced LHS rows.

  • reserved (set (updated))

References

Joseph, V. R., Gul, E. & Ba, S. (2018). Designing computer

experiments with multiple types of factors: the MaxPro approach. Journal of Quality Technology, 50(4).

Morris, M. D. & Mitchell, T. J. (1995).

J. Statist. Plan. Infer., 43, 381–402.

McKay, M. D., Conover, W. J. & Beckman, R. J. (1979).

Technometrics, 21(2), 239–245.

Parameters:

space (ParameterSpace)