Parameter estimators

Optimizer and sampler schemas used inside DataFit (also available as the ionworks.pipeline.optimizers alias).

Schemas for parameter_estimators.

class ionworks.pipeline.parameter_estimators.AskTellOptimizer(method='CMAES', log_to_screen=False, sigma0=None, max_iterations=None, max_unchanged_iterations=None, max_unchanged_iterations_threshold=None, min_iterations=1, max_evaluations=1000000, population_size=None, threshold=None, absolute_tolerance=1e-05, relative_tolerance=0.01, xtol=1e-06, population_convergence_tol=0.005, flat_fitness_tol=None, convergence_patience=3, surrogate_convergence_tol=0.001, algorithm_options=None, type='AskTellOptimizer')[source]

Bases: _AskTellBase

Ask/tell optimizer for population-based and simplex optimization.

Supports CMAES, PSO, DifferentialEvolution, XNES, Nelder-Mead, BayesianOptimization, SOBER, and TuRBO.

Parameters:
  • method (str, optional) – Optimization method. Default is “CMAES”. Must be one of: “CMAES”, “Nelder-Mead”, “PSO”, “DifferentialEvolution”, “XNES”, “BayesianOptimization”, “SOBER”, or “TuRBO”.

  • log_to_screen (bool, optional) – Whether to print optimization progress. Default is False.

  • sigma0 (float | np.ndarray, optional) – Initial step size for population-based methods. Default is None.

  • max_iterations (int, optional) – Maximum number of iterations. Default is 300.

  • max_unchanged_iterations (int, optional) – Stop after this many iterations without improvement. Default is 75.

  • max_unchanged_iterations_threshold (float, optional) – Legacy alias for absolute_tolerance. Prefer absolute_tolerance on new code.

  • min_iterations (int, optional) – Minimum iterations before checking stopping criteria. Default is 1.

  • max_evaluations (int, optional) – Maximum number of function evaluations. Default is 1e6.

  • population_size (int, optional) – Population size for population-based methods. Default is method-specific.

  • threshold (float, optional) – Target objective value to stop optimization. Default is None.

  • absolute_tolerance (float, optional) – Absolute tolerance for unchanged iterations. Default is 1e-5.

  • relative_tolerance (float, optional) – Relative tolerance for unchanged iterations. Default is 1e-2.

  • xtol (float, optional) – Parameter change tolerance (L-inf norm). Default is 1e-6.

  • population_convergence_tol (float, optional) – Population convergence tolerance. The exact convergence semantics are algorithm-dependent (e.g. DE also checks position-space diversity). Default is 5e-3.

  • flat_fitness_tol (float, optional) – Flat fitness tolerance. Default is None (disabled).

  • convergence_patience (int, optional) – Number of consecutive generations the population convergence check must pass before actually stopping. Default is 3.

  • surrogate_convergence_tol (float, optional) – Relative tolerance on the model-native convergence signal for surrogate algorithms (BO, SOBER). Fires when the expected improvement at the algorithm’s best candidate is below tol times the range of observed costs for convergence_patience consecutive iterations. Default 1e-3. Set to None to disable.

  • algorithm_options (dict, optional) –

    Algorithm-specific configuration options. For PSO, supported keys are:

    • inertia_weight: tuple[float, float] - (w_start, w_end) for linear decay. Enables inertia mode and disables constriction.

    • constriction: bool - Use constriction coefficient (default: True)

    • c1: float - Cognitive coefficient (default: 2.05)

    • c2: float - Social coefficient (default: 2.05)

    • boundary_handling: str | BoundaryHandling - “absorb”, “reflect”, “random”, “ignore”

    • velocity_clamping: str | VelocityClamping - “none”, “fraction”, “adaptive”

    • v_max_fraction: float - Fraction of search space for max velocity (default: 0.2)

    • max_iterations: int - For adaptive parameter scheduling (default: 1000)

  • **kwargs – Additional arguments for the AskTellOptimizer constructor.

Notes

"CMAES" (default) is robust. "DifferentialEvolution" (small population, many generations) makes the most progress per evaluation on cheap objectives with many parameters; "BayesianOptimization" is the most sample-efficient for a small budget of expensive ones with ten or fewer; "TuRBO" suits wide parallel batches or high-dimensional constrained problems (TuRBOOptions n_initial = batch width). "SOBER" estimates by quadrature. The engine owns parallelism.

Examples

>>> opt = iw.pipeline.parameter_estimators.AskTellOptimizer(
...     method="CMAES", max_iterations=200,
... )
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['AskTellOptimizer', 'Pints']
method: str
algorithm_options: dict | CMAESOptions | PSOOptions | DEOptions | XNESOptions | BayesianOptimizationOptions | SOBEROptions | TuRBOOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.BayesianOptimization(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['BayesianOptimization'] = 'BayesianOptimization', algorithm_options: BayesianOptimizationOptions | None = None)[source]

Bases: _AskTellBase

Bayesian Optimization with a Gaussian-process surrogate.

Sample-efficient for expensive black-box objectives with fewer than ~30 parameters. Convenience wrapper around AskTellOptimizer(method="BayesianOptimization"); see AskTellOptimizer for the full list of accepted kwargs and pass algorithm_options=iw.pipeline.parameter_estimators.BayesianOptimizationOptions(...) to configure the surrogate.

Examples

>>> opt = iw.pipeline.parameter_estimators.BayesianOptimization()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['BayesianOptimization']
algorithm_options: BayesianOptimizationOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.BayesianOptimizationOptions(*, n_initial: int | None = None, noise_floor: str | tuple[float, float] | list[float] | None = None)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="BayesianOptimization").

Any field left as None falls back to the Bayesian-optimization defaults set by the Ionworks pipeline. Strict (extra="forbid"): an unknown option key is rejected.

Parameters:
  • n_initial (int, optional) – Number of quasi-random warm-up evaluations before the surrogate model is first fitted. None (default) uses max(5, 2 * n_parameters + 1). Set it to the parallel evaluation capacity (e.g. the batch size on a cluster) so the warm-up round uses the full available width.

  • noise_floor (str or tuple of (float, float), optional) – Constrain the objective-GP likelihood noise. None (default) omits the key, so the pipeline default applies ("deterministic", i.e. noise constrained to (1e-8, 1e-3) — suited to deterministic simulator objectives). Pass "standard" to restore the uncapped default for genuinely noisy objectives; "low" / "deterministic" set the low interval explicitly; a (lo, hi) tuple sets a custom interval.

n_initial: int | None
noise_floor: str | tuple[float, float] | list[float] | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.CMAES(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['CMAES'] = 'CMAES', algorithm_options: CMAESOptions | None = None)[source]

Bases: _AskTellBase

Covariance Matrix Adaptation Evolution Strategy (CMA-ES).

A strong default for ill-conditioned, noisy, non-linear fits. Convenience wrapper around AskTellOptimizer(method="CMAES"); see AskTellOptimizer for the full list of accepted kwargs.

Examples

>>> opt = iw.pipeline.parameter_estimators.CMAES()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['CMAES']
algorithm_options: CMAESOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.CMAESOptions(**extra_data: Any)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="CMAES") passed through to pycma.

Accepts any key valid for cma.CMAOptions() — see pycma documentation for the full list. Common keys: CMA_diagonal, ftarget, CMA_stds, scaling_of_variables.

This is a genuine passthrough leaf: cma.CMAOptions() is the source of truth for the (large) option surface and rejects unknown keys at runtime, so this class keeps extra="allow" rather than enumerating dozens of pycma keys. It is on the strictness-guard allowlist for this reason.

model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.DEOptions(*, mutation_strategy: str | None = None, crossover_method: str | None = None, F: float | None = None, CR: float | None = None, dither: bool | tuple[float, float] | list[float] | None = None, p_best_rate: float | None = None, archive_size_ratio: float | None = None, memory_size: int | None = None, boundary_handling: str | None = None)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="DifferentialEvolution").

Any field left as None falls back to the Differential-Evolution defaults set by the Ionworks pipeline.

Parameters:
  • mutation_strategy (str, optional) – One of "rand_1", "best_1", "current_to_best_1", "rand_2", "best_2", "current_to_pbest_1". Default "current_to_pbest_1" (SHADE adaptive).

  • crossover_method (str, optional) – One of "binomial", "exponential". Default "binomial".

  • F (float, optional) – Mutation scale factor; initial mean for SHADE adaptive sampling. Default 0.5.

  • CR (float, optional) – Crossover probability; initial mean for SHADE adaptive sampling. Default 0.7.

  • dither (bool or tuple of (float, float), optional) – Randomise F per generation when using classic strategies. True uses the default range [0.5, 1.0]. Default True.

  • p_best_rate (float, optional) – Fraction of top individuals for pbest selection. Default 0.1.

  • archive_size_ratio (float, optional) – Archive size as a multiple of population. Default 1.0.

  • memory_size (int, optional) – Number of history entries for F/CR adaptation. Default equal to population size.

  • boundary_handling (str, optional) – Boundary handling strategy. Default "reflect".

mutation_strategy: str | None
crossover_method: str | None
F: float | None
CR: float | None
dither: bool | tuple[float, float] | list[float] | None
p_best_rate: float | None
archive_size_ratio: float | None
memory_size: int | None
boundary_handling: str | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.DifferentialEvolution(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['DifferentialEvolution'] = 'DifferentialEvolution', algorithm_options: DEOptions | None = None)[source]

Bases: _AskTellBase

Differential Evolution (DE) with SHADE adaptive control.

Uses success-history based adaptive F/CR with current-to-pbest/1 mutation by default. Convenience wrapper around AskTellOptimizer(method="DifferentialEvolution"). See AskTellOptimizer class for full parameter documentation.

Examples

>>> opt = iw.pipeline.parameter_estimators.DifferentialEvolution()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['DifferentialEvolution']
algorithm_options: DEOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.DummyOptimizer(*, type: Literal['DummyOptimizer', 'Dummy'] = 'DummyOptimizer')[source]

Bases: BaseSchema

Alias for PointEstimateOptimizer.

Returns the supplied initial guess without running any optimisation — useful as a no-op in tests and pipeline scaffolding.

type: Literal['DummyOptimizer', 'Dummy']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.DummySampler(*, type: Literal['DummySampler'] = 'DummySampler')[source]

Bases: BaseSchema

Alias for PointEstimateSampler.

Returns a single sample equal to the initial guess — useful as a no-op in sampling pipelines.

type: Literal['DummySampler']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.GridSearch(npts=10, type='GridSearch')[source]

Bases: BaseSchema

Grid search sampler.

Evaluates the objective at all combinations of grid points across dimensions — a deterministic, exhaustive sweep. Cost grows as npts ** n_parameters; keep the parameter count small.

type: Literal['GridSearch']
npts: int
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.Nested(*, type: Literal['Nested'] = 'Nested', parameters: list[str], optimizer: DifferentialEvolution | CMAES | PSO | XNES | BayesianOptimization | SOBER | TuRBO | AskTellOptimizer | Nested | ScipyDifferentialEvolution | ScipyMinimize | ScipyLeastSquares | ScipyLsqLinear | ScipyBasinhopping | ScipyDualAnnealing | ScipyShgo | GridSearch | PointEstimateOptimizer | PointEstimateSampler | DummyOptimizer | DummySampler | PintsSampler, inner: DifferentialEvolution | CMAES | PSO | XNES | BayesianOptimization | SOBER | TuRBO | AskTellOptimizer | Nested | ScipyDifferentialEvolution | ScipyMinimize | ScipyLeastSquares | ScipyLsqLinear | ScipyBasinhopping | ScipyDualAnnealing | ScipyShgo | GridSearch | PointEstimateOptimizer | PointEstimateSampler | DummyOptimizer | DummySampler | PintsSampler)[source]

Bases: BaseSchema

Bi-level optimizer: an outer optimizer over parameters wrapping an inner over the remaining parameters, which is run to optimality at each outer point (variable projection when inner is least-squares). inner may itself be a Nested for deeper nesting.

Examples

>>> opt = iw.pipeline.parameter_estimators.Nested(
...     parameters=["a", "b"],
...     optimizer=iw.pipeline.parameter_estimators.CMAES(),
...     inner=iw.pipeline.parameter_estimators.ScipyLeastSquares(),
... )
type: Literal['Nested']
parameters: list[str]
optimizer: OptimizerUnion
inner: OptimizerUnion
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.Optimizer[source]

Bases: BaseSchema

Base class for all optimizers.

Optimisers seek a single optimal point in parameter space that minimises the objective. Not used directly — pick a concrete subclass such as AskTellOptimizer or one of the Scipy* wrappers.

Parameters:

**kwargs – Arguments passed to the underlying optimiser algorithm.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.PSO(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['PSO'] = 'PSO', algorithm_options: PSOOptions | None = None)[source]

Bases: _AskTellBase

Particle Swarm Optimization (PSO). Population-based optimiser inspired by bird-flocking behaviour.

Convenience wrapper around AskTellOptimizer(method="PSO"); see AskTellOptimizer for the full list of accepted kwargs.

Examples

>>> opt = iw.pipeline.parameter_estimators.PSO()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['PSO']
algorithm_options: PSOOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.PSOOptions(*, inertia_weight: tuple[float, float] | list[float] | None = None, constriction: bool | None = None, c1: float | None = None, c2: float | None = None, boundary_handling: str | None = None, velocity_clamping: str | None = None, v_max_fraction: float | None = None, max_iterations: int | None = None)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="PSO").

Any field left as None falls back to the PSO defaults set by the Ionworks pipeline.

Parameters:
  • inertia_weight (tuple of (float, float), optional) – (w_start, w_end) for linear decay. When set, enables inertia mode and disables constriction.

  • constriction (bool, optional) – Use constriction coefficient. Default True (when inertia_weight is not set).

  • c1 (float, optional) – Cognitive coefficient (attraction to personal best). Default 2.05.

  • c2 (float, optional) – Social coefficient (attraction to global best). Default 2.05.

  • boundary_handling (str, optional) – One of "absorb", "reflect", "random", "ignore". Default "reflect".

  • velocity_clamping (str, optional) – One of "none", "fraction", "adaptive". Default "fraction".

  • v_max_fraction (float, optional) – Fraction of search space for max velocity. Default 0.2.

  • max_iterations (int, optional) – Horizon used for adaptive parameter scheduling. Default 1000.

inertia_weight: tuple[float, float] | list[float] | None
constriction: bool | None
c1: float | None
c2: float | None
boundary_handling: str | None
velocity_clamping: str | None
v_max_fraction: float | None
max_iterations: int | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ParameterEstimator[source]

Bases: BaseSchema

Base class for all parameter estimators.

Parameter estimators find an optimal (or posterior) parameter set by minimising an objective. Not used directly — pick a concrete Optimizer or Sampler subclass.

Parameters:

**kwargs – Arguments passed to the underlying estimation algorithm.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

ionworks.pipeline.parameter_estimators.PintsOptimizer

alias of AskTellOptimizer

class ionworks.pipeline.parameter_estimators.PintsSampler(method='DramACMC', log_to_screen=False, max_iterations=1000, burnin_iterations=None, initial_phase_iterations=None, type='PintsSampler')[source]

Bases: BaseSchema

Sampler using the Pints library: Probabilistic Inference for Bayesian Models.

Wraps Pints’ MCMCController class for MCMC sampling.

Parameters:
  • method (str, optional) – Sampling method. Default is “DramACMC”. Must be one of: “DramACMC”, “HamiltonianMCMC”, “HaarioBardenetACMC”, “MALAMCMC”, “MetropolisRandomWalkMCMC”, “MonomialGammaHamiltonianMCMC”, “NoUTurnMCMC”, “PopulationMCMC”, “RelativisticMCMC”, “SliceDoublingMCMC”, “SliceRankShrinkingMCMC”, or “SliceStepoutMCMC”.

  • log_to_screen (bool, optional) – Whether to print information at runtime. Default is False.

  • max_iterations (int, optional) – Maximum number of iterations. Default is 1000.

  • burnin_iterations (int, optional) – Number of initial iterations to discard. Default is 10% of max_iterations.

  • initial_phase_iterations (int, optional) – Number of iterations in initial phase. Only used for methods that need an initial phase. Default is equal to burnin_iterations.

  • **kwargs – Additional parameters passed to the pints MCMC sampler. See pints documentation for details.

Notes

  • The log PDF values are cached internally to work around a pints limitation.

  • For methods that need an initial phase, initial_phase_iterations must not exceed burnin_iterations, which in turn must not exceed max_iterations.

type: Literal['PintsSampler']
method: str
log_to_screen: bool
max_iterations: int
burnin_iterations: int | None
initial_phase_iterations: int | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.PointEstimateOptimizer(*, type: Literal['PointEstimateOptimizer', 'PointEstimate'] = 'PointEstimateOptimizer')[source]

Bases: BaseSchema

No-op optimiser — returns the initial guess unchanged.

Useful for evaluating a single parameter set or initialising pipelines without paying the cost of a real optimiser run.

Examples

>>> opt = iw.pipeline.parameter_estimators.PointEstimateOptimizer()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['PointEstimateOptimizer', 'PointEstimate']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.PointEstimateSampler(*, type: Literal['PointEstimateSampler'] = 'PointEstimateSampler')[source]

Bases: BaseSchema

No-op sampler — returns the initial guess as a single sample.

Useful for evaluating a single parameter set within a sampling-style pipeline without paying the cost of real MCMC.

type: Literal['PointEstimateSampler']
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.SOBER(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['SOBER'] = 'SOBER', algorithm_options: SOBEROptions | None = None)[source]

Bases: _AskTellBase

SOBER: batch Bayesian optimization via quadrature-style recombination.

Selects whole batches by Bayesian quadrature; strongest when the goal is quadrature-style estimation rather than pure optimization. Convenience wrapper around AskTellOptimizer(method="SOBER"); see AskTellOptimizer for the full list of accepted kwargs and pass algorithm_options=iw.pipeline.parameter_estimators.SOBEROptions(...) to configure the surrogate.

Examples

>>> opt = iw.pipeline.parameter_estimators.SOBER()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['SOBER']
algorithm_options: SOBEROptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.Sampler[source]

Bases: BaseSchema

Base class for all Monte Carlo-style samplers.

Samplers explore parameter space to characterise the posterior, in contrast to optimisers which return a single best point. Not used directly — pick a concrete subclass such as PintsSampler or GridSearch.

Parameters:

**kwargs – Arguments passed to the underlying sampling algorithm.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyBasinhopping(*, type: Literal['ScipyBasinhopping'] = 'ScipyBasinhopping', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Global optimizer using basin-hopping with local minimization.

Basin-hopping alternates random perturbations of the current minimum, accepted or rejected by the Metropolis criterion, with local minimization from the new point.

Notes

  • Accepts initial guess x0 as the starting point for optimization

  • Local minimizer can be customized via minimizer_kwargs parameter

  • Stochastic algorithm (use seed parameter for reproducibility)

  • Constraint objects are applied as a penalty on the cost

  • Temperature parameter controls acceptance of uphill moves (higher = more exploration)

Parameters:
  • niter (int, default=100) – Number of basin-hopping iterations.

  • T (float, default=1.0) – Temperature parameter for the Metropolis acceptance criterion. Higher values increase the probability of accepting uphill moves.

  • stepsize (float, default=0.5) – Initial step size for random displacement of coordinates.

  • minimizer_kwargs (dict, optional) – Extra keyword arguments passed to the local minimizer (scipy.optimize.minimize). Can specify the local method, bounds, constraints, and convergence criteria. Default uses L-BFGS-B with provided bounds.

  • take_step (callable, optional) – Custom step-taking function. If None, uses random displacement.

  • accept_test (callable, optional) – Custom acceptance test function. If None, uses Metropolis criterion.

  • interval (int, default=50) – Interval for updating stepsize (adaptive step size adjustment).

  • seed (int, optional) – Random seed for reproducible results.

  • **kwargs – Additional arguments passed to scipy.optimize.basinhopping. See scipy documentation for complete options.

type: Literal['ScipyBasinhopping']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyDifferentialEvolution(*, type: Literal['ScipyDifferentialEvolution'] = 'ScipyDifferentialEvolution', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Global stochastic optimizer using differential evolution with parallel evaluation.

Evolves a population of candidate solutions across generations; suited to multi-modal, non-convex landscapes and needs no gradient information.

Notes

  • Constraint objects are applied as a penalty on the cost

  • Initial guess x0 is ignored; initial population is generated from bounds

  • Polish option disabled by default as it conventionally significantly decreases performance

  • Callback logs only best solution per generation (not individual evaluations)

  • workers parallelises evaluations within scipy’s DE loop; fit-level parallelism (multistarts, objectives) is managed by the execution engine

Parameters:
  • workers (int, default=1) – Number of parallel workers for function evaluations. Use -1 for all CPU cores.

  • max_iterations (int, default=1000) – Maximum number of generations. Passed to scipy as maxiter.

  • population_size (int, default=15) – Population size multiplier (total population = population_size * dimensionality). Passed to scipy as popsize.

  • strategy (str, default='best1bin') – Differential evolution strategy. Options include ‘best1bin’, ‘rand1bin’, ‘best2bin’, ‘rand2bin’, ‘currenttobest1bin’.

  • mutation (float or tuple, default=(0.5, 1)) – Mutation constant. Can be float or (min, max) tuple for adaptive mutation.

  • recombination (float, default=0.7) – Crossover probability for parameter mixing.

  • seed (int, optional) – Random seed for reproducible results.

  • atol (float, optional) – Absolute and relative tolerance for convergence.

  • tol (float, optional) – Absolute and relative tolerance for convergence.

  • **kwargs – Additional arguments passed to scipy.optimize.differential_evolution. See scipy documentation for complete options.

Examples

>>> opt = iw.pipeline.parameter_estimators.ScipyDifferentialEvolution()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['ScipyDifferentialEvolution']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyDualAnnealing(*, type: Literal['ScipyDualAnnealing'] = 'ScipyDualAnnealing', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Global stochastic optimizer using dual annealing.

Dual annealing combines generalized simulated annealing with fast local search. It’s designed for global optimization with a good balance between exploration and exploitation, particularly effective for rugged objective landscapes.

Notes

  • Constraint objects are applied as a penalty on the cost

  • Accepts optional initial guess x0 to seed the search

  • Stochastic algorithm (use seed parameter for reproducibility)

  • Good choice when gradient information is unavailable

Parameters:
  • max_iterations (int, default=1000) – Maximum number of global search iterations. Passed to scipy as maxiter.

  • initial_temp (float, default=5230) – Initial temperature for the annealing schedule.

  • restart_temp_ratio (float, default=2e-5) – Temperature ratio for restart condition during local search.

  • visit (float, default=2.62) – Parameter for the visiting distribution (higher = more exploration).

  • accept (float, default=-5.0) – Parameter for the acceptance distribution (lower = more exploitation).

  • seed (int, optional) – Random seed for reproducible results.

  • no_local_search (bool, default=False) – If True, skip local minimization (pure generalized simulated annealing).

  • **kwargs – Additional arguments passed to scipy.optimize.dual_annealing. See scipy documentation for complete options.

type: Literal['ScipyDualAnnealing']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyLeastSquares(*, type: Literal['ScipyLeastSquares'] = 'ScipyLeastSquares', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Nonlinear least squares optimizer using scipy’s Trust Region Reflective algorithm.

This optimizer is designed for problems where the objective returns a residual vector rather than a scalar cost. It minimizes the sum of squares of the residuals. Best suited for well-behaved, smooth problems with a clear residual structure.

Notes

  • Requires objective functions that return an array (residual vector)

  • Automatically handles linear algebra errors by returning NaN values

  • More efficient than general minimization for least-squares structure

  • Constraint objects are applied as penalty residuals

Parameters:
  • method (str, optional) – Algorithm to use. Options: ‘trf’ (default), ‘dogbox’, ‘lm’.

  • ftol (float, optional) – Tolerance parameters for convergence criteria.

  • xtol (float, optional) – Tolerance parameters for convergence criteria.

  • gtol (float, optional) – Tolerance parameters for convergence criteria.

  • max_nfev (int, optional) – Maximum number of function evaluations.

  • **kwargs – Additional arguments passed to scipy.optimize.least_squares. See scipy documentation for complete options.

type: Literal['ScipyLeastSquares']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyLsqLinear(*, type: Literal['ScipyLsqLinear'] = 'ScipyLsqLinear', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Bounded linear least-squares via scipy.optimize.lsq_linear.

Solves a bounded least-squares problem whose residual is linear in the fitted parameters, reaching the bounded optimum in a single solve (no Gauss-Newton iteration). Intended as the inner solver of a variable- projection / nested fit whose inner parameters enter the model linearly; for nonlinear residuals use ScipyLeastSquares.

Parameters:
  • method (str, optional) – scipy.optimize.lsq_linear method: 'bvls' (default) or 'trf'.

  • jac_rel_step (float, optional) – Relative step for the finite-difference design matrix (default 1e-3); larger than a derivative step since the residual is assumed linear.

  • warm_start (bool, optional) – Reuse the previous solve’s active set (default True) so the inner solve stays consistent across outer perturbations and the concentrated objective stays smooth; falls back to a cold solve when it is no longer optimal.

  • **kwargs – Additional arguments passed to scipy.optimize.lsq_linear (e.g. tol, max_iter). See scipy documentation for complete options.

type: Literal['ScipyLsqLinear']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyMinimize(*, type: Literal['ScipyMinimize'] = 'ScipyMinimize', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

General-purpose scalar minimization.

Wraps scipy’s minimize local optimization algorithms (e.g., L-BFGS-B, Powell, SLSQP, trust-constr, COBYQA).

Notes

  • Requires objective functions that return a scalar value

  • Constraint objects are applied as a penalty on the cost for every method, never as scipy constraints=

Parameters:
  • method (str, optional) – Optimization algorithm. Common choices: - ‘L-BFGS-B’: Bound-constrained, gradient-based (default for bounded problems) - ‘Powell’: Derivative-free, bound-constrained - ‘SLSQP’: Sequential Least Squares Programming - ‘trust-constr’: Trust-region method - ‘COBYQA’: Derivative-free quadratic-model method

  • max_iterations (int, optional) – Maximum number of iterations. Passed to scipy as maxiter.

  • tol (float, optional) – Tolerance for termination.

  • **kwargs – Additional arguments passed to scipy.optimize.minimize. See scipy documentation for complete options.

Examples

>>> opt = iw.pipeline.parameter_estimators.ScipyMinimize(method="L-BFGS-B")
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['ScipyMinimize']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.ScipyShgo(*, type: Literal['ScipyShgo'] = 'ScipyShgo', **extra_data: Any)[source]

Bases: _PassthroughOptimizer

Global optimizer using simplicial homology techniques.

SHGO (Simplicial Homology Global Optimization) uses topological techniques to identify and sample from all local minima basins. It’s particularly effective for problems with many local minima.

Notes

  • Deterministic algorithm (reproducible results without random seed)

  • Constraint objects are applied as a penalty on the cost

  • May be slower than stochastic methods for high-dimensional problems

  • Initial guess x0 is ignored; sampling points determined by algorithm

Parameters:
  • n (int, default=100) – Number of sampling points used in the algorithm.

  • iters (int, default=1) – Number of iterations for algorithm convergence.

  • sampling_method (str, default='simplicial') – Sampling strategy: ‘simplicial’ (default) or ‘sobol’.

  • minimizer_kwargs (dict, optional) – Additional arguments passed to the local minimizer.

  • **kwargs – Additional arguments passed to scipy.optimize.shgo. See scipy documentation for complete options.

type: Literal['ScipyShgo']
model_config = {'arbitrary_types_allowed': True, 'extra': 'allow', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.SOBEROptions(*, n_initial: int | None = None, noise_floor: str | tuple[float, float] | list[float] | None = None, n_candidates: int | None = None, n_nystrom: int | None = None, gp_kernel: str | None = None)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="SOBER").

Any field left as None falls back to the SOBER defaults set by the Ionworks pipeline. Strict (extra="forbid"): an unknown option key is rejected.

SOBER selects batches by Bayesian quadrature. Its strength is quadrature-style estimation rather than pure optimization; when the goal is simply the best parameter set, prefer method="TuRBO", method="BayesianOptimization", or method="DifferentialEvolution" — see the AskTellOptimizer Notes for method-selection guidance.

Parameters:
  • n_initial (int, optional) – Number of quasi-random warm-up evaluations before the surrogate model is first fitted — see BayesianOptimizationOptions.

  • noise_floor (str or tuple of (float, float), optional) – Constrain the objective-GP likelihood noise — see BayesianOptimizationOptions.

  • n_candidates (int, optional) – Number of candidate samples per batch for kernel recombination. Default 1024. The recombination’s selection pressure is n_candidates : batch size — keep it near ~100:1 (e.g. 20000 at batch 200) when running wide batches; the default is only adequate for small batches.

  • n_nystrom (int, optional) – Number of samples for the Nystrom approximation of the kernel. Default min(512, n_candidates // 2). Must exceed the batch size.

  • gp_kernel (str, optional) – Objective-GP kernel style. "botorch" (default) uses an ARD RBF kernel with per-dimension lengthscales — the best general choice, especially on anisotropic or constrained problems. "reference" uses the isotropic kernel of the original SOBER implementation, whose sharper sampling measure converges deeper on smooth low-dimensional problems with an interior optimum.

n_initial: int | None
noise_floor: str | tuple[float, float] | list[float] | None
n_candidates: int | None
n_nystrom: int | None
gp_kernel: str | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.TuRBO(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['TuRBO'] = 'TuRBO', algorithm_options: TuRBOOptions | None = None)[source]

Bases: _AskTellBase

TuRBO: trust-region Bayesian optimization.

Suited to expensive parallel or constrained problems, scaling to higher dimensions than global BO via a local trust region. Convenience wrapper around AskTellOptimizer(method="TuRBO"); see AskTellOptimizer for the full list of accepted kwargs and pass algorithm_options=iw.pipeline.parameter_estimators.TuRBOOptions(...) to configure the surrogate.

Examples

>>> opt = iw.pipeline.parameter_estimators.TuRBO()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['TuRBO']
algorithm_options: TuRBOOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.TuRBOOptions(*, n_initial: int | None = None, noise_floor: str | tuple[float, float] | list[float] | None = None, n_candidates: int | None = None, tr_length_init: float | None = None, tr_length_min: float | None = None, tr_length_max: float | None = None, tr_success_tolerance: int | None = None, tr_failure_tolerance: int | None = None, gp_max_points: int | None = None)[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="TuRBO").

Any field left as None falls back to the TuRBO defaults set by the Ionworks pipeline. Strict (extra="forbid"): an unknown option key is rejected.

Parameters:
  • n_initial (int, optional) – Number of quasi-random warm-up evaluations before the surrogate model is first fitted — see BayesianOptimizationOptions. On a parallel cluster, set it to the batch width when that is much larger than the dimension-based default so the warm-up round uses the full available width.

  • noise_floor (str or tuple of (float, float), optional) – Constrain the objective-GP likelihood noise — see BayesianOptimizationOptions.

  • n_candidates (int, optional) – Candidate-pool size for Thompson sampling inside the trust region. Default min(5000, 200 * n_parameters).

  • tr_length_init (float, optional) – Initial trust-region edge length in normalized [0, 1] coordinates. Default 0.8.

  • tr_length_min (float, optional) – Trust-region edge length below which the region restarts. Default 2**-7.

  • tr_length_max (float, optional) – Maximum trust-region edge length. Default 1.6.

  • tr_success_tolerance (int, optional) – Consecutive improving batches before the region doubles. Default 3.

  • tr_failure_tolerance (int, optional) – Consecutive non-improving batches before the region halves. Default ceil(max(4, n_parameters) / batch_size).

  • gp_max_points (int, optional) – Maximum number of observations used per surrogate refit (trust-region-local windowing). Bounds the cubic cost of exact GP fitting on long or wide campaigns. Default 1000.

n_initial: int | None
noise_floor: str | tuple[float, float] | list[float] | None
n_candidates: int | None
tr_length_init: float | None
tr_length_min: float | None
tr_length_max: float | None
tr_success_tolerance: int | None
tr_failure_tolerance: int | None
gp_max_points: int | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.XNES(*, log_to_screen: bool | None = False, sigma0: float | int | list | tuple | None = None, max_iterations: int | None = None, max_unchanged_iterations: int | None = None, max_unchanged_iterations_threshold: float | int | None = None, min_iterations: int | None = 1, max_evaluations: int | None = 1000000, population_size: int | None = None, threshold: float | int | None = None, absolute_tolerance: float | int | None = 1e-05, relative_tolerance: float | int | None = 0.01, xtol: float | int | None = 1e-06, population_convergence_tol: float | int | None = 0.005, flat_fitness_tol: float | int | None = None, convergence_patience: int | None = 3, surrogate_convergence_tol: float | int | None = 0.001, type: Literal['XNES'] = 'XNES', algorithm_options: XNESOptions | None = None)[source]

Bases: _AskTellBase

Exponential Natural Evolution Strategy (xNES). Adapts its covariance matrix to the local fitness landscape.

Convenience wrapper around AskTellOptimizer(method="XNES"); see AskTellOptimizer for the full list of accepted kwargs.

Examples

>>> opt = iw.pipeline.parameter_estimators.XNES()
>>> fit = iw.pipeline.DataFit(
...     objectives={"ocp": iw.pipeline.objectives.OCPHalfCell(
...         electrode="positive", data_input="path/to/ocp.csv",
...     )},
...     parameters={
...         "x": iw.pipeline.Parameter("x", initial_value=1.0, bounds=(0.0, 2.0))
...     },
...     optimizer=opt,
... )
type: Literal['XNES']
algorithm_options: XNESOptions | None
model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.

class ionworks.pipeline.parameter_estimators.XNESOptions[source]

Bases: _AlgorithmOptions

Options for AskTellOptimizer(method="XNES").

XNES has no algorithm-specific options today — its search is fully parameterised by the top-level AskTellOptimizer fields (sigma0, population_size, …). This class exists as a typed marker so the same algorithm_options=iw.pipeline.parameter_estimators.XNESOptions(...) pattern works across methods. It is strict (extra="forbid"): it declares no option keys, so any key passed to it is rejected.

model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid', 'populate_by_name': True, 'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) → None

This function is meant to behave like a BaseModel method to initialize private attributes.

It takes context as an argument since that’s what pydantic-core passes when calling it.

Args:

self: The BaseModel instance. context: The context.