Optimisers#
mergen.algorithms#
Registry of optimisation algorithms for space-filling design.
This subpackage holds Mergen’s optimisation engines (SA, SCE, ESE, …) and a lightweight registry that lets users discover, look up, and extend the set of algorithms without modifying Mergen’s core.
Public functions#
get_optimizer()Look up an optimiser class by its registered name.
list_optimizers()List the names of all registered optimisers.
register_optimizer()Register a new optimiser (typically called by third-party packages or by Mergen’s own algorithm modules at import time).
Examples
>>> from mergen.algorithms import list_optimizers, get_optimizer
>>> list_optimizers()
[] # algorithms are added in subsequent phases (SA, SCE, ESE)
>>> # Once SA is implemented:
>>> # SAOptimizer = get_optimizer('sa')
>>> # sa = SAOptimizer(n_restarts=5, max_iter=10000)
Registering a custom optimiser#
>>> from mergen.algorithms import BaseOptimizer, register_optimizer
>>> class MyOptimizer(BaseOptimizer):
... name = 'my_algo'
... @classmethod
... def get_default_params(cls): return {'param': 1.0}
... def optimize(self, *a, **kw): ...
>>> register_optimizer('my_algo', MyOptimizer)
>>> # Now usable via Sampler.set_optimizer('my_algo', ...)
- class mergen.algorithms.BaseOptimizer(**kwargs)[source]#
Bases:
ABCAbstract base class for space-filling design optimisers.
Every optimisation algorithm in Mergen (SA, SCE, ESE, …) inherits from this class and implements two methods:
get_default_params()(classmethod) — return default hyperparamsoptimize()— run the optimisation
Hyperparameters are stored as instance attributes; the constructor populates them from the class defaults and any keyword arguments supplied by the user. Validation of parameter values (range checks, type checks) happens lazily inside
optimize(), not in__init__(), following the scikit-learn convention. This allowssklearn.base.clone()-style cloning and grid search over hyperparameters.Subclass requirements#
Set the class attribute
nameto a short identifier ('sa','sce','ese', …). This is the key used in the registry and bySampler.set_optimizer(name, **kwargs).Implement
get_default_params()to return adictof hyperparameter names → default values. Every key here becomes a valid keyword argument to__init__()andset_params().Implement
optimize()to perform the actual optimisation.
Examples
>>> class DummyOptimizer(BaseOptimizer): ... name = 'dummy' ... ... @classmethod ... def get_default_params(cls): ... return {'max_iter': 1000, 'seed_offset': 0} ... ... def optimize(self, initial_design, space, criterion, ... n_frozen=0, crit_start=0, seed=44, verbose=True): ... # ... actual algorithm here ... ... return OptimisationResult( ... design=initial_design, ... score=0.0, ... metadata={'algorithm': self.name}, ... )
See also
mergen.algorithms.register_optimizerRegister a subclass.
mergen.algorithms.get_optimizerLook up a registered algorithm.
- abstractmethod classmethod get_default_params()[source]#
Return default hyperparameters for this optimiser.
Subclasses override this to declare their hyperparameter schema. The dict keys define the legal keyword arguments to
__init__()andset_params(); the values are used when the user does not supply an override.
- set_params(**kwargs)[source]#
Set hyperparameters on this instance (scikit-learn convention).
- Parameters:
**kwargs (Any) – Hyperparameter values to update. Unknown keys raise
ValueError.- Returns:
self – Enables fluent chaining:
opt.set_params(max_iter=1000).optimize(...).- Raises:
ValueError – If any key is not a valid hyperparameter for this optimiser.
- Return type:
- abstractmethod optimize(initial_design, space, criterion, n_frozen=0, crit_start=0, seed=44, verbose=True, banned=None)[source]#
Run the optimisation.
bannedis an optional set of grid indices that the swap operators must never select (used for user-supplied sets and externally loaded designs).- Parameters:
initial_design (np.ndarray, shape (n, d)) – Starting design in the original parameter space. Built by
prepare_initial_design()(typically a balanced LHS plus the prescribed / focus anchor rows).space (ParameterSpace) – The parameter space — provides grid, constraints, parameter names, and value ranges.
criterion (BaseCriterion) – The criterion to minimise. Algorithms call
criterion.evaluate,criterion.incremental,criterion.begin_1d/criterion.try_1das appropriate.n_frozen (int, optional) – The first
n_frozenrows ofinitial_designare fixed and must not be altered. These are typically prescribed points the user requires in the final design. Default: 0.crit_start (int, optional) – The criterion is evaluated on
initial_design[crit_start:]. Rows[0, crit_start)participate in optimisation moves (they may be swapped) but are excluded from the criterion. Used for prescribed-but- not-criterion-included points. Default: 0.seed (int, optional) – Random seed for reproducibility. Default: 44.
verbose (bool, optional) – Print progress information. Default: True.
banned (Optional[set])
- Returns:
OptimisationResult – The optimised design and metadata.
- Return type:
Notes
Subclasses must:
Treat the first
n_frozenrows as immutable.Evaluate the criterion only on rows
[crit_start:].Respect any constraints in
space._constraints.Populate
metadatawith at least the algorithm name and hyperparameters used.
- prepare_initial_design(anchors, budget, space, reserved, seed=44)[source]#
Build the starting design for this optimiser.
The default implementation returns a balanced Latin Hypercube seed on top of the supplied
anchors. This matches the literature convention for SA (Morris & Mitchell 1995), SCE (Kang 2019), and ESE (Jin et al. 2005), which all expect an LHS as input.Subclasses may override this method if their algorithm requires a different initial design (e.g. a greedy maximin seed, a Sobol sequence, or a user-supplied design).
- Parameters:
anchors (np.ndarray, shape (k, d)) – Rows that must appear in the output unchanged (prescribed and focus points placed by the Sampler).
budget (int) – Number of additional rows to add on top of the anchors. The returned design has
len(anchors) + budgetrows.space (ParameterSpace) – The parameter space.
reserved (set) – Grid indices already in use; updated in place.
seed (int, default 44) – RNG seed for the LHS shuffles.
- Returns:
design (np.ndarray, shape (len(anchors) + budget, d))
reserved (set (updated))
- Return type:
- Parameters:
kwargs (Any)
- class mergen.algorithms.OptimisationResult(design, score, converged=False, n_iter=0, elapsed=0.0, metadata=<factory>)[source]#
Bases:
objectResult of a single optimisation run.
Modelled on
scipy.optimize.OptimizeResultbut implemented as a dataclass for type safety and IDE support. Algorithm-specific data (acceptance rates, temperatures, restart counts, etc.) are stored undermetadata.- Variables:
design (np.ndarray, shape (n, d)) – Final optimised design in the original parameter space.
score (float) – Final criterion value at the returned design. Lower is better (all criteria are minimised).
converged (bool) – Whether the optimiser believes it converged to a (local) optimum. Algorithm-specific definition; see each algorithm’s documentation.
n_iter (int) – Total number of iterations performed (across all restarts if any).
elapsed (float) – Wall-clock time of the optimisation in seconds.
metadata (dict) –
Algorithm-specific information. Standard keys (when applicable):
"n_accepted": int — accepted moves (SA, SCE, ESE)"n_rejected": int — rejected moves"n_restarts": int — number of restarts performed"score_history": list[float] — best score per restart"T_start": float — initial temperature (SA, ESE)"T_end": float — final temperature"acceptance_rate": float — fraction of accepted moves"algorithm": str — algorithm name (e.g. ‘sa’, ‘sce’)"params": dict — hyperparameters used
- Parameters:
Notes
Use
result.xas an alias forresult.designandresult.funforresult.scoreto maintain partial compatibility with thescipy.optimize.OptimizeResultinterface.
- mergen.algorithms.register_optimizer(name, cls)[source]#
Register an optimiser class under a short identifier.
Once registered, the class becomes accessible via
get_optimizer()and selectable viaSampler.set_optimizer(name, **kwargs)andSampler.run(algorithm=name, ...).- Parameters:
name (str) – Short identifier for the algorithm (e.g.
'sa','sce','ese'). Convention: lowercase, underscore-separated.cls (type) – A subclass of
BaseOptimizer.
- Raises:
TypeError – If
clsis not a subclass ofBaseOptimizer.ValueError – If
nameis empty.
- Return type:
None
Examples
>>> from mergen.algorithms import BaseOptimizer, register_optimizer >>> class MyOpt(BaseOptimizer): ... name = 'my_algo' ... @classmethod ... def get_default_params(cls): return {} ... def optimize(self, *a, **kw): ... >>> register_optimizer('my_algo', MyOpt)
Notes
Re-registering an existing name overwrites the previous entry. A warning is not issued because legitimate use cases include swapping implementations during testing.
- mergen.algorithms.get_optimizer(name)[source]#
Look up a registered optimiser class by name.
- Parameters:
name (str) – The identifier under which the optimiser was registered.
- Returns:
type – The optimiser class. Instantiate it as
get_optimizer('sa')(n_restarts=5).- Raises:
KeyError – If no optimiser is registered under
name.- Return type:
Examples
>>> SAOptimizer = get_optimizer('sa') # after SA is implemented >>> sa = SAOptimizer(n_restarts=5)