Skip to content

Python: core API

This page documents the stable, __all__-driven surface of spectrafit_core — the contract a library user should rely on. The 36 shape-factory functions (gaussian, lorentzian, voigt, …) are documented separately on Shape factories since they are intentionally excluded from __all__.

Solvers

spectrafit_core.fit

Entry point: fit bridges Python contracts to the engine.

fit(graph, data, options=None)

Run a fit with the solver named in options and return the result.

Runs the solver named by options.solver (default "lm", Levenberg-Marquardt on the faer-native trust-region core); see FitOptions.solver for the full list of supported solvers.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph. expr_edges and per-parameter Parameter.expr (both equivalent constraint surfaces) are supported and evaluated per solver iteration by the engine.

required
data MeasurementInput

One or more MeasurementData datasets. All datasets must share the same number of x-dimensions. 1-D, 2-D, and N-D x are accepted (\(\geq 3\)-D is fit by the parametric gaussian_nd kernel).

required
options FitOptions | None

Solver configuration; defaults to FitOptions().

None

Returns:

Type Description
FitResult

A FitResult with fitted parameters, uncertainties, and

FitResult

goodness-of-fit statistics.

Raises:

Type Description
ValueError

If the datasets do not all share the same number of x-dimensions (the mismatching dataset indices and coordinate counts are named in the message), or if the graph, options, or array shapes are rejected by the engine.

fit_fast(graph, data, options=None)

Run a fit and return both the result and the best-fit curve as a NumPy array.

Identical to fit but avoids JSON-serialising the per-point arrays (best_fit, residuals, init_fit, components). The best-fit curve is returned directly as the second element of the tuple, saving ~2 ms per call on typical spectra (~500 points). The FitResult.best_fit field will be empty — use the returned array instead.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph. expr_edges and per-parameter Parameter.expr are both supported.

required
data MeasurementInput

One or more MeasurementData datasets. All datasets must share the same number of x-dimensions.

required
options FitOptions | None

Solver configuration; defaults to FitOptions().

None

Returns:

Type Description
FitResult

Tuple of (FitResult, best_fit_array) where best_fit_array is a

ndarray

1-D float64 NumPy array of length n_data_points.

Raises:

Type Description
ValueError

If the datasets do not all share the same number of x-dimensions (the mismatching dataset indices and coordinate counts are named in the message), or if the graph, options, or array shapes are rejected by the engine.

spectrafit_core.fit_fast(graph, data, options=None)

Run a fit and return both the result and the best-fit curve as a NumPy array.

Identical to fit but avoids JSON-serialising the per-point arrays (best_fit, residuals, init_fit, components). The best-fit curve is returned directly as the second element of the tuple, saving ~2 ms per call on typical spectra (~500 points). The FitResult.best_fit field will be empty — use the returned array instead.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph. expr_edges and per-parameter Parameter.expr are both supported.

required
data MeasurementInput

One or more MeasurementData datasets. All datasets must share the same number of x-dimensions.

required
options FitOptions | None

Solver configuration; defaults to FitOptions().

None

Returns:

Type Description
FitResult

Tuple of (FitResult, best_fit_array) where best_fit_array is a

ndarray

1-D float64 NumPy array of length n_data_points.

Raises:

Type Description
ValueError

If the datasets do not all share the same number of x-dimensions (the mismatching dataset indices and coordinate counts are named in the message), or if the graph, options, or array shapes are rejected by the engine.

Forward evaluation

spectrafit_core.evaluate

Evaluate helpers exposed at the top-level Python API.

evaluate(graph, params, data)

Evaluate the summed model over data at fixed parameters (no fit).

Validates graph into a FitGraph, then calls the spectrafit_core._core PyO3 extension's evaluate (JSON-in, JSON-out): the graph, params, and the measurement data are each serialised to JSON strings, the compiled Rust CompiledGraph sums every node's model contribution at the given parameter values (applying any expr_edges / per-parameter Parameter.expr ties), and the resulting per-point vector comes back as a JSON array that is decoded into a NumPy array. This is a pure forward evaluation — no solver iterations run.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph (or anything FitGraph.model_validate accepts). nodes must have unique ids; expr_edges and per-parameter expr constraints, if present, must form a DAG.

required
params Mapping[str, object]

Parameter values keyed by "node_id.param_name" (dotted notation), e.g. {"peak1.amplitude": 1.0, "peak1.center": 0.0}. Values are JSON-encoded as-is (BaseModel, numpy.ndarray/numpy.generic, and nested list/dict/mapping values are all converted to plain JSON via _dump_params_json).

required
data MeasurementInput

One or more datasets as a MeasurementData or a sequence thereof (anything accepted by dump_measurement_json). Only the x coordinates are used; y/sigma are ignored for evaluation.

required

Returns:

Type Description
ndarray

A 1-D numpy.ndarray of float64 model values, one per

ndarray

data point, in the same order as the flattened input x.

Raises:

Type Description
ValidationError

If graph fails FitGraph validation (duplicate node ids, unknown nodes/params referenced by an expression edge, or a cyclic constraint graph).

ValueError

Propagated across the PyO3 boundary from the Rust engine if params is missing a parameter required by a node, or if a node references an undefined model type.

evaluate_components(graph, params, data)

Evaluate each model node separately, returning per-node component arrays.

Identical in spirit to evaluate, but calls the spectrafit_core._core extension's evaluate_components instead, which evaluates every node's model contribution independently (still applying any expr_edges / Parameter.expr ties) rather than summing them, so the returned components sum to what evaluate (or best_fit) would return. Useful for plotting individual peaks/baselines under a shared fit.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph (or anything FitGraph.model_validate accepts). nodes must have unique ids; expr_edges and per-parameter expr constraints, if present, must form a DAG.

required
params Mapping[str, object]

Parameter values keyed by "node_id.param_name" (dotted notation). Values are JSON-encoded via _dump_params_json.

required
data MeasurementInput

One or more datasets as a MeasurementData or a sequence thereof. Only the x coordinates are used.

required

Returns:

Type Description
dict[str, ndarray]

A dict mapping each node's id to a 1-D numpy.ndarray of

dict[str, ndarray]

float64 model values for that node alone, one value per data

dict[str, ndarray]

point, in the same order as the flattened input x.

Raises:

Type Description
ValidationError

If graph fails FitGraph validation (duplicate node ids, unknown nodes/params referenced by an expression edge, or a cyclic constraint graph).

ValueError

Propagated across the PyO3 boundary from the Rust engine if params is missing a parameter required by a node, or if a node references an undefined model type.

spectrafit_core.evaluate_components(graph, params, data)

Evaluate each model node separately, returning per-node component arrays.

Identical in spirit to evaluate, but calls the spectrafit_core._core extension's evaluate_components instead, which evaluates every node's model contribution independently (still applying any expr_edges / Parameter.expr ties) rather than summing them, so the returned components sum to what evaluate (or best_fit) would return. Useful for plotting individual peaks/baselines under a shared fit.

Parameters:

Name Type Description Default
graph FitGraph

Model topology as a FitGraph (or anything FitGraph.model_validate accepts). nodes must have unique ids; expr_edges and per-parameter expr constraints, if present, must form a DAG.

required
params Mapping[str, object]

Parameter values keyed by "node_id.param_name" (dotted notation). Values are JSON-encoded via _dump_params_json.

required
data MeasurementInput

One or more datasets as a MeasurementData or a sequence thereof. Only the x coordinates are used.

required

Returns:

Type Description
dict[str, ndarray]

A dict mapping each node's id to a 1-D numpy.ndarray of

dict[str, ndarray]

float64 model values for that node alone, one value per data

dict[str, ndarray]

point, in the same order as the flattened input x.

Raises:

Type Description
ValidationError

If graph fails FitGraph validation (duplicate node ids, unknown nodes/params referenced by an expression edge, or a cyclic constraint graph).

ValueError

Propagated across the PyO3 boundary from the Rust engine if params is missing a parameter required by a node, or if a node references an undefined model type.

Result types

spectrafit_core.FitResult pydantic-model

Bases: BaseModel

Result of a single fit call.

Attributes:

Name Type Description
schema_version str

IR schema version of the producing engine.

parameters dict[str, ParameterResult]

Fitted parameter values and uncertainties, keyed by "node_id.param_name" (dotted notation).

covariance list[list[float | None]] | None

Parameter covariance matrix (row/column order matches parameters), or None if the solver could not estimate it.

chi2 float

Total unweighted sum of squared residuals \(\sum_i (y_i - \hat{y}_i)^2\), recomputed at the solution. This is deliberately not the weighted residual the solver minimises: it stays comparable across backends even when a per-point sigma was supplied and used to weight the fit.

reduced_chi2 float

chi2 / dof. Because chi2 above is unweighted, this is a mean squared residual in the data's own units, not the textbook \(\sigma\)-normalised statistic — the "values near 1.0 indicate a good fit" reading does not apply here. See Post-fit statistics in the Solver selection explanation page.

r_squared float

Coefficient of determination \(R^2\).

dof int

Degrees of freedom n_points − n_free, clamped to a minimum of 1 so the reduced statistics stay finite. When the fit is exactly determined or over-parameterised the true value is \(\le 0\) and reduced_chi2 is not meaningful; the clamp keeps it finite rather than undefined.

aic float

Akaike Information Criterion.

bic float

Bayesian Information Criterion.

n_iter int

Accepted solver iterations for the faer-native solvers; for lm-legacy the number of function evaluations (that engine does not expose iterations). varpro reports 0 (its engine does not expose iterations either).

n_func_evals int | None

Number of residual evaluations (None if unavailable).

n_jac_evals int | None

Number of Jacobian evaluations (None if unavailable).

success bool

True if the solver converged within max_iterations.

message str

Stable termination-reason key, not free text — one of the strings produced by TerminationReason::as_str / faer_termination_str (e.g. "converged_ftol", "converged_xtol", "converged_gtol", "converged" for lm-legacy, "max_iterations", "no_improvement_possible", "residuals_zero", "numerical_error"). Three post-fit forms replace or extend the key vocabulary, all applied by apply_postfit_guards in spectrafit-solver::postfit:

  • An originally-unbounded free parameter that escaped the data-derived domain replaces the key entirely with "diverged_off_domain (<key>=<val> escaped data domain [...])", e.g. "diverged_off_domain (p1.amplitude=1.234e5 escaped data domain [-3.900e0, 7.800e0])", and downgrades the result to success=False.
  • A post-fit guard that detects a collapsed fit (\(r^2 < 0\) with a near-zero amplitude/height) replaces the key with a "degenerate_fit (…)" string carrying the guard's reason, e.g. "degenerate_fit (r2=-1.234e0 < 0, peak amplitude collapsed)".
  • A soft-stop termination ("no_improvement_possible" or "max_iterations") upgraded to success because \(r^2 \ge 0.9\) instead keeps the original key and appends an "_accepted_at_r2_<value>" suffix, e.g. "no_improvement_possible_accepted_at_r2_0.9921".

Match on the "diverged_off_domain" / "degenerate_fit" prefix or the original key prefix respectively, rather than the whole string — the bracketed/appended part is diagnostic text, not a stable key.

best_fit list[float]

Model values at the fitted parameters, one per data point.

residuals list[float]

Signed residuals (y_observed - y_fit).

init_fit list[float]

Model values evaluated at the initial-guess parameters.

components dict[str, list[float]]

Per-node contributions summing to best_fit.

dataset_slices list[DatasetSlice] | None

Per-dataset diagnostics for multi-dataset fits, None for single-dataset fits.

condition_number float | None

Condition number of \(J^\mathsf{T}J\) at the solution (ratio of largest to smallest singular value). Large values flag an ill-conditioned, poorly determined fit. None when the solver did not compute it.

n_de_generations int | None

Number of differential-evolution generations run before the LM refinement on the solver="global" path. None for direct LM and other solvers. Makes the DE search effort visible, since n_iter counts only the post-DE refinement (often 0).

cost_history list[float]

Per-iteration cost \(\frac{1}{2}\|r\|^2\) trajectory from the faer LM / trust-region drivers (index 0 = initial point, last = terminal cost). Empty for solvers that do not track it (solver="lm-legacy" / "varpro"). Observability only — it does not affect the fit.

gradient_norm_history list[float]

Per-iteration gradient infinity-norm \(\|J^\mathsf{T}r\|_\infty\) recorded alongside each cost_history entry. Empty when not tracked. Each entry is the gradient at the most recent point where the Jacobian was evaluated — the start of that outer iteration — not at the point whose cost sits at the same index. When the loop stopped after an accepted step the terminal entry therefore repeats the previous iteration's gradient; the final entry always equals the driver's reported final gradient norm (spectrafit_trust_region::Report::gradient_norm), which FitResult does not mirror as a scalar.

params_history list[list[float]]

Per-iteration free-parameter vector \(\theta\) recorded alongside each cost_history entry (same length/order). Raw material for the convergence-to-truth metric \(d_k = \|(\theta_k - \theta_{\text{true}})/s\|_2\) on synthetic cases. Empty for solvers that do not track it (only the faer LM driver records it today). Observability only.

covariance_param_order list[str] | None

Ordered list of free-parameter names that index the rows and columns of covariance. covariance[i][j] is the covariance between covariance_param_order[i] and covariance_param_order[j]. Use this to look up cross-terms by name rather than relying on parameters iteration order (which is non-deterministic for HashMap-backed dicts). None for payloads produced before the schema added this field (backwards-compatible default).

Show JSON schema:
{
  "$defs": {
    "DatasetSlice": {
      "additionalProperties": false,
      "description": "Per-dataset diagnostics for multi-dataset global fits.\n\nAttributes:\n    label (str | None): Optional human-readable label for this slice (may\n        be ``None``).\n    n_points (int): Number of data points in this slice.\n    best_fit (list[float]): Model values at the fitted parameters, length\n        ``n_points``.\n    residuals (list[float]): Signed residuals ``(y_observed - y_fit)``,\n        length ``n_points``.\n    chi2 (float): Sum of squared residuals for this slice only.",
      "properties": {
        "label": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Label"
        },
        "n_points": {
          "title": "N Points",
          "type": "integer"
        },
        "best_fit": {
          "items": {
            "type": "number"
          },
          "title": "Best Fit",
          "type": "array"
        },
        "residuals": {
          "items": {
            "type": "number"
          },
          "title": "Residuals",
          "type": "array"
        },
        "chi2": {
          "title": "Chi2",
          "type": "number"
        }
      },
      "required": [
        "n_points",
        "best_fit",
        "residuals",
        "chi2"
      ],
      "title": "DatasetSlice",
      "type": "object"
    },
    "ParameterResult": {
      "additionalProperties": false,
      "description": "Fitted parameter with name and uncertainty.\n\nAttributes:\n    name (str | None): Dotted parameter name (``\"node_id.param\"``), if\n        known.\n    stderr (float | None): Estimated standard error, or ``None`` when\n        unavailable.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        },
        "name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Name"
        },
        "stderr": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Stderr"
        }
      },
      "required": [
        "value"
      ],
      "title": "ParameterResult",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "Result of a single [`fit`][spectrafit_core.fit] call.\n\nAttributes:\n    schema_version (str): IR schema version of the producing engine.\n    parameters (dict[str, ParameterResult]): Fitted parameter values and\n        uncertainties, keyed by ``\"node_id.param_name\"`` (dotted\n        notation).\n    covariance (list[list[float | None]] | None): Parameter covariance\n        matrix (row/column order matches ``parameters``), or ``None`` if\n        the solver could not estimate it.\n    chi2 (float): Total *unweighted* sum of squared residuals\n        $\\sum_i (y_i - \\hat{y}_i)^2$, recomputed at the solution. This is\n        deliberately **not** the weighted residual the solver minimises:\n        it stays comparable across backends even when a per-point\n        ``sigma`` was supplied and used to weight the fit.\n    reduced_chi2 (float): ``chi2 / dof``. Because ``chi2`` above is\n        unweighted, this is a mean squared residual in the data's own\n        units, **not** the textbook $\\sigma$-normalised statistic \u2014 the\n        \"values near 1.0 indicate a good fit\" reading does *not* apply\n        here. See *Post-fit statistics* in the Solver selection\n        explanation page.\n    r_squared (float): Coefficient of determination $R^2$.\n    dof (int): Degrees of freedom ``n_points \u2212 n_free``, clamped to a\n        minimum of 1 so the reduced statistics stay finite. When the fit\n        is exactly determined or over-parameterised the true value is\n        $\\le 0$ and ``reduced_chi2`` is not meaningful; the clamp keeps\n        it finite rather than undefined.\n    aic (float): Akaike Information Criterion.\n    bic (float): Bayesian Information Criterion.\n    n_iter (int): Accepted solver iterations for the faer-native solvers;\n        for ``lm-legacy`` the number of function evaluations (that engine\n        does not expose iterations). ``varpro`` reports 0 (its engine\n        does not expose iterations either).\n    n_func_evals (int | None): Number of residual evaluations (``None``\n        if unavailable).\n    n_jac_evals (int | None): Number of Jacobian evaluations (``None`` if\n        unavailable).\n    success (bool): ``True`` if the solver converged within\n        ``max_iterations``.\n    message (str): Stable termination-reason key, not free text \u2014 one of\n        the strings produced by ``TerminationReason::as_str`` /\n        ``faer_termination_str`` (e.g. ``\"converged_ftol\"``,\n        ``\"converged_xtol\"``, ``\"converged_gtol\"``, ``\"converged\"`` for\n        lm-legacy, ``\"max_iterations\"``, ``\"no_improvement_possible\"``,\n        ``\"residuals_zero\"``, ``\"numerical_error\"``).\n        Three post-fit forms replace or extend the key vocabulary, all\n        applied by ``apply_postfit_guards`` in\n        ``spectrafit-solver::postfit``:\n\n        - An originally-unbounded free parameter that escaped the\n          data-derived domain replaces the key entirely with\n          ``\"diverged_off_domain (<key>=<val> escaped data domain\n          [...])\"``, e.g. ``\"diverged_off_domain (p1.amplitude=1.234e5\n          escaped data domain [-3.900e0, 7.800e0])\"``, and downgrades the\n          result to ``success=False``.\n        - A post-fit guard that detects a collapsed fit ($r^2 < 0$ with a\n          near-zero amplitude/height) replaces the key with a\n          ``\"degenerate_fit (\u2026)\"`` string carrying the guard's reason,\n          e.g. ``\"degenerate_fit (r2=-1.234e0 < 0, peak amplitude\n          collapsed)\"``.\n        - A soft-stop termination (``\"no_improvement_possible\"`` or\n          ``\"max_iterations\"``) upgraded to success because\n          $r^2 \\ge 0.9$ instead keeps the original key and appends an\n          ``\"_accepted_at_r2_<value>\"`` suffix, e.g.\n          ``\"no_improvement_possible_accepted_at_r2_0.9921\"``.\n\n        Match on the ``\"diverged_off_domain\"`` / ``\"degenerate_fit\"``\n        prefix or the original key prefix respectively, rather than the\n        whole string \u2014 the bracketed/appended part is diagnostic text,\n        not a stable key.\n    best_fit (list[float]): Model values at the fitted parameters, one\n        per data point.\n    residuals (list[float]): Signed residuals ``(y_observed - y_fit)``.\n    init_fit (list[float]): Model values evaluated at the initial-guess\n        parameters.\n    components (dict[str, list[float]]): Per-node contributions summing\n        to ``best_fit``.\n    dataset_slices (list[DatasetSlice] | None): Per-dataset diagnostics\n        for multi-dataset fits, ``None`` for single-dataset fits.\n    condition_number (float | None): Condition number of $J^\\mathsf{T}J$\n        at the solution (ratio of largest to smallest singular value).\n        Large values flag an ill-conditioned, poorly determined fit.\n        ``None`` when the solver did not compute it.\n    n_de_generations (int | None): Number of differential-evolution\n        generations run before the LM refinement on the\n        ``solver=\"global\"`` path. ``None`` for direct LM and other\n        solvers. Makes the DE search effort visible, since ``n_iter``\n        counts only the post-DE refinement (often 0).\n    cost_history (list[float]): Per-iteration cost\n        $\\frac{1}{2}\\|r\\|^2$ trajectory from the faer LM / trust-region\n        drivers (index 0 = initial point, last = terminal cost). Empty\n        for solvers that do not track it (``solver=\"lm-legacy\"`` /\n        ``\"varpro\"``). Observability only \u2014 it does not affect the fit.\n    gradient_norm_history (list[float]): Per-iteration gradient\n        infinity-norm $\\|J^\\mathsf{T}r\\|_\\infty$ recorded alongside each\n        [`cost_history`][spectrafit_core.FitResult] entry. Empty when not tracked.\n        Each entry is the gradient *at the most recent point where the\n        Jacobian was evaluated* \u2014 the start of that outer iteration \u2014 not\n        at the point whose cost sits at the same index. When the loop\n        stopped after an accepted step the terminal entry therefore\n        repeats the previous iteration's gradient; the final entry always\n        equals the driver's reported final gradient norm\n        (``spectrafit_trust_region::Report::gradient_norm``), which\n        ``FitResult`` does not mirror as a scalar.\n    params_history (list[list[float]]): Per-iteration free-parameter\n        vector $\\theta$ recorded alongside each [`cost_history`][spectrafit_core.FitResult]\n        entry (same length/order). Raw material for the\n        convergence-to-truth metric\n        $d_k = \\|(\\theta_k - \\theta_{\\text{true}})/s\\|_2$ on synthetic\n        cases. Empty for solvers that do not track it (only the faer LM\n        driver records it today). Observability only.\n    covariance_param_order (list[str] | None): Ordered list of\n        free-parameter names that index the rows and columns of\n        [`covariance`][spectrafit_core.FitResult]. ``covariance[i][j]`` is the covariance\n        between ``covariance_param_order[i]`` and\n        ``covariance_param_order[j]``.  Use this to look up cross-terms\n        by name rather than relying on [`parameters`][spectrafit_core.FitResult] iteration\n        order (which is non-deterministic for ``HashMap``-backed dicts).\n        ``None`` for payloads produced before the schema added this\n        field (backwards-compatible default).",
  "properties": {
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    },
    "parameters": {
      "additionalProperties": {
        "$ref": "#/$defs/ParameterResult"
      },
      "title": "Parameters",
      "type": "object"
    },
    "covariance": {
      "anyOf": [
        {
          "items": {
            "items": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Covariance"
    },
    "chi2": {
      "default": 0.0,
      "title": "Chi2",
      "type": "number"
    },
    "reduced_chi2": {
      "default": 0.0,
      "title": "Reduced Chi2",
      "type": "number"
    },
    "r_squared": {
      "default": 0.0,
      "title": "R Squared",
      "type": "number"
    },
    "dof": {
      "default": 0,
      "title": "Dof",
      "type": "integer"
    },
    "aic": {
      "default": 0.0,
      "title": "Aic",
      "type": "number"
    },
    "bic": {
      "default": 0.0,
      "title": "Bic",
      "type": "number"
    },
    "n_iter": {
      "default": 0,
      "title": "N Iter",
      "type": "integer"
    },
    "n_func_evals": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "N Func Evals"
    },
    "n_jac_evals": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "N Jac Evals"
    },
    "success": {
      "default": false,
      "title": "Success",
      "type": "boolean"
    },
    "message": {
      "default": "",
      "title": "Message",
      "type": "string"
    },
    "best_fit": {
      "items": {
        "type": "number"
      },
      "title": "Best Fit",
      "type": "array"
    },
    "residuals": {
      "items": {
        "type": "number"
      },
      "title": "Residuals",
      "type": "array"
    },
    "init_fit": {
      "items": {
        "type": "number"
      },
      "title": "Init Fit",
      "type": "array"
    },
    "components": {
      "additionalProperties": {
        "items": {
          "type": "number"
        },
        "type": "array"
      },
      "title": "Components",
      "type": "object"
    },
    "dataset_slices": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/DatasetSlice"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Dataset Slices"
    },
    "condition_number": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Condition Number"
    },
    "n_de_generations": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "N De Generations"
    },
    "cost_history": {
      "items": {
        "type": "number"
      },
      "title": "Cost History",
      "type": "array"
    },
    "gradient_norm_history": {
      "items": {
        "type": "number"
      },
      "title": "Gradient Norm History",
      "type": "array"
    },
    "params_history": {
      "description": "Per-iteration free-parameter vector $\\theta$ recorded alongside each cost_history entry (same length/order). Raw material for the convergence-to-truth metric $d_k = \\lVert (\\theta_k - \\theta_{\\mathrm{true}})/s \\rVert_2$ on synthetic cases. Empty for solvers that do not track it (only the faer LM driver records it today). Observability only.",
      "items": {
        "items": {
          "type": "number"
        },
        "type": "array"
      },
      "title": "Params History",
      "type": "array"
    },
    "covariance_param_order": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "Ordered names of the free parameters that index covariance rows/cols. `covariance[i][j]` is cov(`covariance_param_order[i]`, `covariance_param_order[j]`). None for payloads produced before the schema added this field (backwards-compatible).",
      "title": "Covariance Param Order"
    }
  },
  "title": "FitResult",
  "type": "object"
}

Config:

  • extra: forbid
  • populate_by_name: True

Fields:

  • schema_version (str)
  • parameters (dict[str, ParameterResult])
  • covariance (list[list[float | None]] | None)
  • chi2 (float)
  • reduced_chi2 (float)
  • r_squared (float)
  • dof (int)
  • aic (float)
  • bic (float)
  • n_iter (int)
  • n_func_evals (int | None)
  • n_jac_evals (int | None)
  • success (bool)
  • message (str)
  • best_fit (list[float])
  • residuals (list[float])
  • init_fit (list[float])
  • components (dict[str, list[float]])
  • dataset_slices (list[DatasetSlice] | None)
  • condition_number (float | None)
  • n_de_generations (int | None)
  • cost_history (list[float])
  • gradient_norm_history (list[float])
  • params_history (list[list[float]])
  • covariance_param_order (list[str] | None)

Validators:

  • _validate_value_invariants

params_history pydantic-field

Per-iteration free-parameter vector \(\theta\) recorded alongside each cost_history entry (same length/order). Raw material for the convergence-to-truth metric \(d_k = \lVert (\theta_k - \theta_{\mathrm{true}})/s \rVert_2\) on synthetic cases. Empty for solvers that do not track it (only the faer LM driver records it today). Observability only.

covariance_param_order = None pydantic-field

Ordered names of the free parameters that index covariance rows/cols. covariance[i][j] is cov(covariance_param_order[i], covariance_param_order[j]). None for payloads produced before the schema added this field (backwards-compatible).

params property

Alias for parameters.

Keyed by dotted parameter name mapping to its fitted result.

explain()

Return a 4–6 sentence lab-notebook narrative of this fit.

Deterministic interpretive prose synthesised from existing FitResult fields — no new state, no I/O. Lines are anchored to numerical thresholds for reduced_chi2 and condition_number so the reader sees the "what does this number mean" verdict next to the value itself. Adds no fields to the model.

The conditioning line reports \(\kappa(J)\), the square root of the stored Gram value condition_number \(= \kappa(J^\mathsf{T}J)\), so its thresholds match the ones the benchmark contract uses.

Returns:

Type Description
str

A multi-sentence English narrative covering convergence,

str

goodness-of-fit, conditioning (when available), the dominant

str

amplitude-bearing peak (when present), and AIC (when non-zero).

spectrafit_core.DatasetSlice pydantic-model

Bases: BaseModel

Per-dataset diagnostics for multi-dataset global fits.

Attributes:

Name Type Description
label str | None

Optional human-readable label for this slice (may be None).

n_points int

Number of data points in this slice.

best_fit list[float]

Model values at the fitted parameters, length n_points.

residuals list[float]

Signed residuals (y_observed - y_fit), length n_points.

chi2 float

Sum of squared residuals for this slice only.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "Per-dataset diagnostics for multi-dataset global fits.\n\nAttributes:\n    label (str | None): Optional human-readable label for this slice (may\n        be ``None``).\n    n_points (int): Number of data points in this slice.\n    best_fit (list[float]): Model values at the fitted parameters, length\n        ``n_points``.\n    residuals (list[float]): Signed residuals ``(y_observed - y_fit)``,\n        length ``n_points``.\n    chi2 (float): Sum of squared residuals for this slice only.",
  "properties": {
    "label": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Label"
    },
    "n_points": {
      "title": "N Points",
      "type": "integer"
    },
    "best_fit": {
      "items": {
        "type": "number"
      },
      "title": "Best Fit",
      "type": "array"
    },
    "residuals": {
      "items": {
        "type": "number"
      },
      "title": "Residuals",
      "type": "array"
    },
    "chi2": {
      "title": "Chi2",
      "type": "number"
    }
  },
  "required": [
    "n_points",
    "best_fit",
    "residuals",
    "chi2"
  ],
  "title": "DatasetSlice",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • label (str | None)
  • n_points (int)
  • best_fit (list[float])
  • residuals (list[float])
  • chi2 (float)

Model dispatch

spectrafit_core.ModelType

Bases: StrEnum

Supported model kernels, identified by their canonical string name.

spectrafit_core.ModelNodeSpec pydantic-model

Bases: BaseModel

One model node: a unique id, a model_type, and its parameters.

Attributes:

Name Type Description
id str

Unique node identifier within a graph.

model_type ModelType

Which model kernel this node evaluates.

parameters dict[str, Parameter]

Parameter definitions keyed by parameter name.

dataset_index int | None

Dataset scope for simultaneous multi-dataset ("global analysis") fits. None (default) = global node, contributing to every dataset's points. i = local to dataset i, contributing residuals/Jacobian only to that dataset's contiguous point-range.

Show JSON schema:
{
  "$defs": {
    "ModelType": {
      "description": "Supported model kernels, identified by their canonical string name.",
      "enum": [
        "gaussian",
        "gaussian2d",
        "gaussian_nd",
        "lorentzian",
        "voigt",
        "constant",
        "linear",
        "quadratic",
        "arctan_step",
        "tanh_step",
        "erfc_step",
        "pseudo_voigt",
        "fano",
        "double_exponential",
        "true_voigt",
        "skewed_gaussian",
        "exp_gaussian",
        "doniach_sunjic",
        "log_normal",
        "pearson7",
        "split_gaussian",
        "moffat",
        "students_t",
        "split_pearson7",
        "breit_wigner",
        "asym_ir",
        "harmonic_ir",
        "tauc",
        "cauchy_dispersion",
        "kww",
        "saturating_exponential",
        "power_saturation",
        "power_law_offset",
        "mgh09_rational",
        "rational_cubic",
        "generalised_logistic",
        "exp_over_linear"
      ],
      "title": "ModelType",
      "type": "string"
    },
    "Parameter": {
      "additionalProperties": false,
      "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        }
      },
      "required": [
        "value"
      ],
      "title": "Parameter",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "One model node: a unique ``id``, a ``model_type``, and its parameters.\n\nAttributes:\n    id (str): Unique node identifier within a graph.\n    model_type (ModelType): Which model kernel this node evaluates.\n    parameters (dict[str, Parameter]): Parameter definitions keyed by\n        parameter name.\n    dataset_index (int | None): Dataset scope for simultaneous\n        multi-dataset (\"global analysis\") fits. ``None`` (default) =\n        global node, contributing to every dataset's points. ``i`` =\n        local to dataset ``i``, contributing residuals/Jacobian only to\n        that dataset's contiguous point-range.",
  "properties": {
    "id": {
      "title": "Id",
      "type": "string"
    },
    "model_type": {
      "$ref": "#/$defs/ModelType"
    },
    "parameters": {
      "additionalProperties": {
        "$ref": "#/$defs/Parameter"
      },
      "title": "Parameters",
      "type": "object"
    },
    "dataset_index": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Dataset Index"
    }
  },
  "required": [
    "id",
    "model_type",
    "parameters"
  ],
  "title": "ModelNodeSpec",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • id (str)
  • model_type (ModelType)
  • parameters (dict[str, Parameter])
  • dataset_index (int | None)

Validators:

  • _dataset_index_non_negative → dataset_index

Solver configuration

spectrafit_core.FitOptions pydantic-model

Bases: BaseModel

Solver configuration for fit.

See docs/how-to/choosing-a-solver.md for the primary-reference citations and per-solver complexity analysis behind the summaries below; this docstring deliberately does not duplicate them.

Attributes:

Name Type Description
schema_version str

IR schema version (do not change).

solver str

Which solver to use. Supported values, in canonical order (lm (default), lm-legacy, trf, geodesic, dogleg, newton-cg, irls[:huber|bisquare|cauchy], global, varpro, auto):

"lm" Levenberg-Marquardt (default), on the faer-native trust-region core (pure-Rust SIMD, no BLAS). Regime-adaptive: normal equations for tall-skinny problems, SVD for many parameters. Fast, gradient-based; best for well-conditioned, unimodal fits.

"lm-legacy" The previous levenberg-marquardt (nalgebra) implementation, retained as a parity/regression oracle. Slower than "lm"; use only to cross-check results.

"trf" Trust Region Reflective. Levenberg-Marquardt with Coleman–Li bound scaling so steps shrink as a parameter approaches an active bound. Use when bounds are active constraints (e.g. widths > 0).

"geodesic" (alias "lm-geodesic") Levenberg-Marquardt with geodesic acceleration (Transtrum). Adds a second-order correction; faster on sloppy / degenerate surfaces typical of overlapping multi-peak spectra.

"dogleg" Powell's dogleg trust-region method. Interpolates between the Gauss-Newton and steepest-descent steps within an explicit trust radius. Robust, cheap (one Cholesky per iteration); a solid alternative to "lm" on mildly nonlinear fits.

"newton-cg" (aliases "newton_cg", "newtoncg", "steihaug") Matrix-free Newton-CG (Steihaug-Toint truncated conjugate gradients) trust-region method. Never forms \(J^\mathsf{T}J\) — its per-iteration cost scales with the residual count, not the squared parameter count — so it is the choice for large-scale / many-parameter or ill-conditioned fits.

"irls" (i.e. "irls:huber") Iteratively Re-weighted Least Squares with Huber weights. Robust against mild outliers.

"irls:bisquare" IRLS with Tukey bisquare weights. Recommended for heavy outlier contamination (> 5–10 % of data points corrupted).

"irls:cauchy" IRLS with Cauchy weights. Very heavy-tailed noise / extreme outliers.

"global" Differential Evolution + LM refinement. Explores the full parameter space before refining with LM. Use for multi-modal surfaces (Ackley, Rastrigin) or when initial guesses are poor.

"varpro" Variable Projection. Separates linear coefficients from nonlinear parameters, solving them analytically at each step. Fastest for models where amplitudes are the only linear params.

"auto" Structure-based routing. Picks "varpro" when the graph is separable with no tied parameters and all nonlinear parameters unconstrained (VarPro's preconditions); otherwise uses "trf" (Coleman–Li bound-scaled LM). Across a representative case per scenario family, TRF is the fastest LM-family strategy at top accuracy in most problem classes, though the TRF-over-VarPro speed result may invert for very large separable problems. Data-dependent strategies ("global" for multimodal, "irls" for heavy outliers) are not auto-selected — choose them explicitly when the data calls for them.

max_iterations int

Solver patience (default 200). The function-evaluation budget is max_iterations × (n_free + 1); the LM family stops with a max_iterations termination when it is exhausted. Must be >= 1 — max_iterations=0 would silently produce a no-op fit.

tolerance float

Convergence tolerance passed to the solver (default 1e-8). Smaller values produce tighter convergence at the cost of more iterations. Set to 0.0 to use each solver's built-in default (the lower bound is inclusive zero for exactly this reason).

delta0 float | None

Initial trust-region radius \(\Delta\) for "dogleg" and "newton-cg". None (default) keeps the library default (problem-derived from the initial scaled-gradient norm). Set explicitly for research / debugging on ill-conditioned problems. Must be strictly positive — a radius of zero blocks all trust-region progress. Plumbs through crates/spectrafit-types::FitOptionsSpec to dispatch.rs::TrustRegionConfig. Read only on the "dogleg" / "newton-cg" dispatch arm; setting it alongside any other solver (including "trf") is silently ignored rather than raising an error.

max_delta float | None

Hard upper bound on the trust-region radius for "dogleg" and "newton-cg". None keeps the library default (1e3). Lower values cap step size for noisy / sloppy surfaces. Must be strictly positive, for the same reason as delta0. Read only on the "dogleg" / "newton-cg" dispatch arm; setting it alongside any other solver (including "trf") is silently ignored rather than raising an error.

eta float | None

Step-acceptance threshold for "dogleg" and "newton-cg" — accept the trial step when \(\rho > \eta\). None keeps the library default (1e-4). Lower values accept smaller improvements (faster but less robust); higher values reject borderline steps. A probability-like ratio, so it is bounded to \([0, 0.25)\): the trust-region driver only shrinks \(\Delta\) when \(\rho < 0.25\), so an eta >= 0.25 would open a band in which a step is rejected without shrinking the radius, and the solver could spin to max_nfev instead of failing cleanly — enforced on the Rust side by a debug_assert!(eta < 0.25) in the trust-region driver. Read only on the "dogleg" / "newton-cg" dispatch arm; setting it alongside any other solver (including "trf") is silently ignored rather than raising an error.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "Solver configuration for [`fit`][spectrafit_core.fit].\n\nSee ``docs/how-to/choosing-a-solver.md`` for the primary-reference\ncitations and per-solver complexity analysis behind the summaries\nbelow; this docstring deliberately does not duplicate them.\n\nAttributes:\n    schema_version (str): IR schema version (do not change).\n    solver (str): Which solver to use.  Supported values, in canonical\n        order (``lm`` (default), ``lm-legacy``, ``trf``, ``geodesic``,\n        ``dogleg``, ``newton-cg``, ``irls[:huber|bisquare|cauchy]``,\n        ``global``, ``varpro``, ``auto``):\n\n        ``\"lm\"``\n            Levenberg-Marquardt (default), on the faer-native trust-region\n            core (pure-Rust SIMD, no BLAS).  Regime-adaptive: normal\n            equations for tall-skinny problems, SVD for many parameters.\n            Fast, gradient-based; best for well-conditioned, unimodal fits.\n\n        ``\"lm-legacy\"``\n            The previous ``levenberg-marquardt`` (nalgebra) implementation,\n            retained as a parity/regression oracle.  Slower than ``\"lm\"``;\n            use only to cross-check results.\n\n        ``\"trf\"``\n            Trust Region Reflective.  Levenberg-Marquardt with Coleman\u2013Li\n            bound scaling so steps shrink as a parameter approaches an\n            active bound.  Use when bounds are active constraints\n            (e.g. widths > 0).\n\n        ``\"geodesic\"`` (alias ``\"lm-geodesic\"``)\n            Levenberg-Marquardt with geodesic acceleration (Transtrum).\n            Adds a second-order correction; faster on sloppy / degenerate\n            surfaces typical of overlapping multi-peak spectra.\n\n        ``\"dogleg\"``\n            Powell's dogleg trust-region method.  Interpolates between the\n            Gauss-Newton and steepest-descent steps within an explicit trust\n            radius.  Robust, cheap (one Cholesky per iteration); a solid\n            alternative to ``\"lm\"`` on mildly nonlinear fits.\n\n        ``\"newton-cg\"`` (aliases ``\"newton_cg\"``, ``\"newtoncg\"``,\n        ``\"steihaug\"``)\n            Matrix-free Newton-CG (Steihaug-Toint truncated conjugate\n            gradients) trust-region method.  Never forms $J^\\mathsf{T}J$ \u2014\n            its per-iteration cost scales with the residual count, not the\n            squared parameter count \u2014 so it is the choice for large-scale /\n            many-parameter or ill-conditioned fits.\n\n        ``\"irls\"`` (i.e. ``\"irls:huber\"``)\n            Iteratively Re-weighted Least Squares with Huber weights.\n            Robust against mild outliers.\n\n        ``\"irls:bisquare\"``\n            IRLS with Tukey bisquare weights.  Recommended for heavy\n            outlier contamination (> 5\u201310 % of data points corrupted).\n\n        ``\"irls:cauchy\"``\n            IRLS with Cauchy weights.  Very heavy-tailed noise / extreme\n            outliers.\n\n        ``\"global\"``\n            Differential Evolution + LM refinement.  Explores the full\n            parameter space before refining with LM.  Use for multi-modal\n            surfaces (Ackley, Rastrigin) or when initial guesses are poor.\n\n        ``\"varpro\"``\n            Variable Projection.  Separates linear coefficients from\n            nonlinear parameters, solving them analytically at each step.\n            Fastest for models where amplitudes are the only linear params.\n\n        ``\"auto\"``\n            Structure-based routing.  Picks ``\"varpro\"`` when the graph is\n            separable with no tied parameters and all nonlinear parameters\n            unconstrained (VarPro's preconditions); otherwise uses ``\"trf\"``\n            (Coleman\u2013Li bound-scaled LM).  Across a representative case per\n            scenario family, TRF is the fastest LM-family strategy at top\n            accuracy in most problem classes, though the TRF-over-VarPro\n            speed result may invert for very large separable\n            problems.  Data-dependent strategies (``\"global\"`` for multimodal,\n            ``\"irls\"`` for heavy outliers) are *not* auto-selected \u2014 choose\n            them explicitly when the data calls for them.\n\n    max_iterations (int): Solver patience (default 200). The\n        function-evaluation budget is ``max_iterations \u00d7 (n_free + 1)``;\n        the LM family stops with a ``max_iterations`` termination when it\n        is exhausted. Must be ``>= 1`` \u2014 ``max_iterations=0`` would\n        silently produce a no-op fit.\n    tolerance (float): Convergence tolerance passed to the solver\n        (default 1e-8). Smaller values produce tighter convergence at the\n        cost of more iterations.  Set to ``0.0`` to use each solver's\n        built-in default (the lower bound is inclusive zero for exactly\n        this reason).\n    delta0 (float | None): Initial trust-region radius $\\Delta$ for\n        ``\"dogleg\"`` and ``\"newton-cg\"``.  ``None`` (default) keeps the\n        library default (problem-derived from the initial scaled-gradient\n        norm). Set explicitly for research / debugging on ill-conditioned\n        problems. Must be strictly positive \u2014 a radius of zero blocks all\n        trust-region progress. Plumbs through\n        ``crates/spectrafit-types::FitOptionsSpec`` to\n        ``dispatch.rs::TrustRegionConfig``. Read only on the ``\"dogleg\"``\n        / ``\"newton-cg\"`` dispatch arm; setting it alongside any other\n        ``solver`` (including ``\"trf\"``) is silently ignored rather than\n        raising an error.\n    max_delta (float | None): Hard upper bound on the trust-region radius\n        for ``\"dogleg\"`` and ``\"newton-cg\"``.  ``None`` keeps the library\n        default (1e3). Lower values cap step size for noisy / sloppy\n        surfaces. Must be strictly positive, for the same reason as\n        ``delta0``. Read only on the ``\"dogleg\"`` / ``\"newton-cg\"``\n        dispatch arm; setting it alongside any other ``solver``\n        (including ``\"trf\"``) is silently ignored rather than raising an\n        error.\n    eta (float | None): Step-acceptance threshold for ``\"dogleg\"`` and\n        ``\"newton-cg\"`` \u2014 accept the trial step when $\\rho > \\eta$.\n        ``None`` keeps the library default (1e-4).  Lower values accept\n        smaller improvements (faster but less robust); higher values\n        reject borderline steps. A probability-like ratio, so it is\n        bounded to $[0, 0.25)$: the trust-region driver only shrinks\n        $\\Delta$ when $\\rho < 0.25$, so an ``eta >= 0.25`` would open a\n        band in which a step is rejected without shrinking the radius,\n        and the solver could spin to ``max_nfev`` instead of failing\n        cleanly \u2014 enforced on the Rust side by a\n        ``debug_assert!(eta < 0.25)`` in the trust-region driver. Read\n        only on the ``\"dogleg\"`` / ``\"newton-cg\"`` dispatch arm; setting\n        it alongside any other ``solver`` (including ``\"trf\"``) is\n        silently ignored rather than raising an error.",
  "properties": {
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    },
    "solver": {
      "default": "lm",
      "title": "Solver",
      "type": "string"
    },
    "max_iterations": {
      "default": 200,
      "minimum": 1,
      "title": "Max Iterations",
      "type": "integer"
    },
    "tolerance": {
      "default": 1e-08,
      "minimum": 0.0,
      "title": "Tolerance",
      "type": "number"
    },
    "delta0": {
      "anyOf": [
        {
          "exclusiveMinimum": 0.0,
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Delta0"
    },
    "max_delta": {
      "anyOf": [
        {
          "exclusiveMinimum": 0.0,
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Max Delta"
    },
    "eta": {
      "anyOf": [
        {
          "exclusiveMaximum": 0.25,
          "minimum": 0.0,
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Eta"
    }
  },
  "title": "FitOptions",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • schema_version (str)
  • solver (str)
  • max_iterations (int)
  • tolerance (float)
  • delta0 (float | None)
  • max_delta (float | None)
  • eta (float | None)

Graph / joint-fit types

spectrafit_core.FitGraph pydantic-model

Bases: BaseModel

A directed acyclic graph specifying the model topology for a fit.

Attributes:

Name Type Description
schema_version str

IR schema version string (default "0.1").

nodes list[ModelNodeSpec]

Ordered list of model nodes (each with a unique id).

expr_edges list[ExprEdge]

Optional parameter-constraint edges. Must form a DAG.

Note

The Rust engine parses expr_edges into a dependency-ordered, cycle-checked plan (CompiledGraph.tied_plan) and the LM/TRF solver loop applies that plan per iteration, so calling fit() with non-empty expr_edges evaluates the tied parameters during the fit. Per-parameter Parameter.expr is an equivalent constraint surface: both expr_edges and Parameter.expr are validated for cycles and unknown nodes at construction time and evaluated identically at fit-time.

Show JSON schema:
{
  "$defs": {
    "ExprEdge": {
      "additionalProperties": false,
      "description": "A directed expression edge that constrains one parameter to a formula.\n\nAttributes:\n    target_node (str): ID of the node whose parameter is constrained.\n    target_param (str): Name of the parameter to constrain.\n    expression (str): Formula referencing other node params as\n        ``node_id.param``.\n\nNote:\n    Expression edges are validated for cycles and unknown nodes at\n    construction time.  The engine parses them into a dependency-ordered,\n    cycle-checked plan (a DAG) and evaluates them per solver iteration, so\n    ``fit()`` applies the tie during fitting \u2014 they are not rejected.",
      "properties": {
        "target_node": {
          "title": "Target Node",
          "type": "string"
        },
        "target_param": {
          "title": "Target Param",
          "type": "string"
        },
        "expression": {
          "title": "Expression",
          "type": "string"
        }
      },
      "required": [
        "target_node",
        "target_param",
        "expression"
      ],
      "title": "ExprEdge",
      "type": "object"
    },
    "ModelNodeSpec": {
      "additionalProperties": false,
      "description": "One model node: a unique ``id``, a ``model_type``, and its parameters.\n\nAttributes:\n    id (str): Unique node identifier within a graph.\n    model_type (ModelType): Which model kernel this node evaluates.\n    parameters (dict[str, Parameter]): Parameter definitions keyed by\n        parameter name.\n    dataset_index (int | None): Dataset scope for simultaneous\n        multi-dataset (\"global analysis\") fits. ``None`` (default) =\n        global node, contributing to every dataset's points. ``i`` =\n        local to dataset ``i``, contributing residuals/Jacobian only to\n        that dataset's contiguous point-range.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "model_type": {
          "$ref": "#/$defs/ModelType"
        },
        "parameters": {
          "additionalProperties": {
            "$ref": "#/$defs/Parameter"
          },
          "title": "Parameters",
          "type": "object"
        },
        "dataset_index": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dataset Index"
        }
      },
      "required": [
        "id",
        "model_type",
        "parameters"
      ],
      "title": "ModelNodeSpec",
      "type": "object"
    },
    "ModelType": {
      "description": "Supported model kernels, identified by their canonical string name.",
      "enum": [
        "gaussian",
        "gaussian2d",
        "gaussian_nd",
        "lorentzian",
        "voigt",
        "constant",
        "linear",
        "quadratic",
        "arctan_step",
        "tanh_step",
        "erfc_step",
        "pseudo_voigt",
        "fano",
        "double_exponential",
        "true_voigt",
        "skewed_gaussian",
        "exp_gaussian",
        "doniach_sunjic",
        "log_normal",
        "pearson7",
        "split_gaussian",
        "moffat",
        "students_t",
        "split_pearson7",
        "breit_wigner",
        "asym_ir",
        "harmonic_ir",
        "tauc",
        "cauchy_dispersion",
        "kww",
        "saturating_exponential",
        "power_saturation",
        "power_law_offset",
        "mgh09_rational",
        "rational_cubic",
        "generalised_logistic",
        "exp_over_linear"
      ],
      "title": "ModelType",
      "type": "string"
    },
    "Parameter": {
      "additionalProperties": false,
      "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        }
      },
      "required": [
        "value"
      ],
      "title": "Parameter",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "A directed acyclic graph specifying the model topology for a fit.\n\nAttributes:\n    schema_version (str): IR schema version string (default ``\"0.1\"``).\n    nodes (list[ModelNodeSpec]): Ordered list of model nodes (each with a\n        unique ``id``).\n    expr_edges (list[ExprEdge]): Optional parameter-constraint edges.\n        Must form a DAG.\n\nNote:\n    The Rust engine parses ``expr_edges`` into a dependency-ordered,\n    cycle-checked plan (``CompiledGraph.tied_plan``) and the LM/TRF solver\n    loop applies that plan per iteration, so calling ``fit()`` with\n    non-empty ``expr_edges`` evaluates the tied parameters during the fit.\n    Per-parameter ``Parameter.expr`` is an equivalent constraint surface:\n    both ``expr_edges`` and ``Parameter.expr`` are validated for cycles and\n    unknown nodes at construction time and evaluated identically at fit-time.",
  "properties": {
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    },
    "nodes": {
      "items": {
        "$ref": "#/$defs/ModelNodeSpec"
      },
      "title": "Nodes",
      "type": "array"
    },
    "expr_edges": {
      "items": {
        "$ref": "#/$defs/ExprEdge"
      },
      "title": "Expr Edges",
      "type": "array"
    }
  },
  "required": [
    "nodes"
  ],
  "title": "FitGraph",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

Validators:

  • _validate_graph

compile()

Return this graph unchanged (validation happens at construction).

eval(params, data)

Evaluate the summed model over data at the given parameters.

Parameters:

Name Type Description Default
params Mapping[str, object]

Parameter values keyed by "node_id.param_name" (dotted notation).

required
data MeasurementInput

Exactly one 1-D dataset; only its x coordinates are used.

required

Returns:

Type Description
ndarray

A 1-D numpy.ndarray of float64 model values, one per data

ndarray

point, in the same order as the flattened input x.

Raises:

Type Description
ValueError

If more than one dataset is passed or the coordinates are n-D (use fit for those).

eval_components(params, data)

Evaluate each node separately, returning per-node model arrays.

Parameters:

Name Type Description Default
params Mapping[str, object]

Parameter values keyed by "node_id.param_name" (dotted notation).

required
data MeasurementInput

Exactly one 1-D dataset; only its x coordinates are used.

required

Returns:

Type Description
dict[str, ndarray]

A dict mapping each node's id to a 1-D numpy.ndarray of

dict[str, ndarray]

float64 model values for that node alone.

Raises:

Type Description
ValueError

If more than one dataset is passed or the coordinates are n-D (use fit for those).

spectrafit_core.GlobalFitGraph pydantic-model

Bases: BaseModel

Multi-dataset graph with globally shared and locally free parameters.

Use this when you have multiple datasets that share the same peak positions and widths (globally shared) but have independent amplitudes or other per-dataset parameters (locally free). A typical use case is time-resolved spectroscopy: N spectra at different time points, all sharing peak centers and widths, with amplitudes that evolve over time.

graph TD
    G["global_nodes<br/>(one copy, shared parameters)"]
    L0["local_nodes<br/>id_s0"]
    L1["local_nodes<br/>id_s1"]
    Ln["local_nodes<br/>id_s(n-1)"]
    D0["slice 0 data"]
    D1["slice 1 data"]
    Dn["slice n-1 data"]
    G --> D0
    G --> D1
    G --> Dn
    L0 --> D0
    L1 --> D1
    Ln --> Dn

to_fit_graph assembles global_nodes plus one _s{i}-suffixed replica of local_nodes per slice into a flat FitGraph; fit solves that assembled graph jointly in one pass, while fit_all_slices fits global_nodes first against all stacked data, then refines each slice's local_nodes with the global parameters held fixed.

Attributes:

Name Type Description
global_nodes list[ModelNodeSpec]

Nodes whose parameters are shared across all datasets. Each node appears once in the assembled FitGraph.

local_nodes list[ModelNodeSpec]

Nodes whose parameters are replicated per dataset. Each local node id gains a slice-index suffix "{id}_s{i}" (0-based) in the assembled graph.

n_slices int

Number of dataset slices to replicate local nodes for.

shared_local_params list[str] | dict[str, list[str]]

Local-node parameter names shared (tied) across slices — per-parameter global analysis. Two forms: a flat list[str] (applied to every local node that has the named params) or a {local_node_id: [param_names]} mapping (per-node control, so different local nodes can share different parameters). Defaults to an empty list (no sharing).

schema_version str

IR schema version string (default "0.1").

Example

g = GlobalFitGraph( ... global_nodes=[ModelNodeSpec(id="peak", model_type="gaussian", ... parameters={...})], ... local_nodes=[ModelNodeSpec(id="bg", model_type="constant", ... parameters={...})], ... n_slices=10, ... ) flat = g.to_fit_graph() # returns a standard FitGraph

Show JSON schema:
{
  "$defs": {
    "ModelNodeSpec": {
      "additionalProperties": false,
      "description": "One model node: a unique ``id``, a ``model_type``, and its parameters.\n\nAttributes:\n    id (str): Unique node identifier within a graph.\n    model_type (ModelType): Which model kernel this node evaluates.\n    parameters (dict[str, Parameter]): Parameter definitions keyed by\n        parameter name.\n    dataset_index (int | None): Dataset scope for simultaneous\n        multi-dataset (\"global analysis\") fits. ``None`` (default) =\n        global node, contributing to every dataset's points. ``i`` =\n        local to dataset ``i``, contributing residuals/Jacobian only to\n        that dataset's contiguous point-range.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "model_type": {
          "$ref": "#/$defs/ModelType"
        },
        "parameters": {
          "additionalProperties": {
            "$ref": "#/$defs/Parameter"
          },
          "title": "Parameters",
          "type": "object"
        },
        "dataset_index": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dataset Index"
        }
      },
      "required": [
        "id",
        "model_type",
        "parameters"
      ],
      "title": "ModelNodeSpec",
      "type": "object"
    },
    "ModelType": {
      "description": "Supported model kernels, identified by their canonical string name.",
      "enum": [
        "gaussian",
        "gaussian2d",
        "gaussian_nd",
        "lorentzian",
        "voigt",
        "constant",
        "linear",
        "quadratic",
        "arctan_step",
        "tanh_step",
        "erfc_step",
        "pseudo_voigt",
        "fano",
        "double_exponential",
        "true_voigt",
        "skewed_gaussian",
        "exp_gaussian",
        "doniach_sunjic",
        "log_normal",
        "pearson7",
        "split_gaussian",
        "moffat",
        "students_t",
        "split_pearson7",
        "breit_wigner",
        "asym_ir",
        "harmonic_ir",
        "tauc",
        "cauchy_dispersion",
        "kww",
        "saturating_exponential",
        "power_saturation",
        "power_law_offset",
        "mgh09_rational",
        "rational_cubic",
        "generalised_logistic",
        "exp_over_linear"
      ],
      "title": "ModelType",
      "type": "string"
    },
    "Parameter": {
      "additionalProperties": false,
      "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        }
      },
      "required": [
        "value"
      ],
      "title": "Parameter",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "Multi-dataset graph with globally shared and locally free parameters.\n\nUse this when you have multiple datasets that share the same peak positions\nand widths (globally shared) but have independent amplitudes or other\nper-dataset parameters (locally free).  A typical use case is time-resolved\nspectroscopy: N spectra at different time points, all sharing peak centers\nand widths, with amplitudes that evolve over time.\n\n```mermaid\ngraph TD\n    G[\"global_nodes<br/>(one copy, shared parameters)\"]\n    L0[\"local_nodes<br/>id_s0\"]\n    L1[\"local_nodes<br/>id_s1\"]\n    Ln[\"local_nodes<br/>id_s(n-1)\"]\n    D0[\"slice 0 data\"]\n    D1[\"slice 1 data\"]\n    Dn[\"slice n-1 data\"]\n    G --> D0\n    G --> D1\n    G --> Dn\n    L0 --> D0\n    L1 --> D1\n    Ln --> Dn\n```\n\n`to_fit_graph` assembles `global_nodes` plus one `_s{i}`-suffixed replica of\n`local_nodes` per slice into a flat `FitGraph`; `fit` solves that assembled\ngraph jointly in one pass, while `fit_all_slices` fits `global_nodes` first\nagainst all stacked data, then refines each slice's `local_nodes` with the\nglobal parameters held fixed.\n\nAttributes:\n    global_nodes (list[ModelNodeSpec]): Nodes whose parameters are shared\n        across **all** datasets. Each node appears once in the assembled\n        ``FitGraph``.\n    local_nodes (list[ModelNodeSpec]): Nodes whose parameters are\n        **replicated per dataset**. Each local node ``id`` gains a\n        slice-index suffix ``\"{id}_s{i}\"`` (0-based) in the assembled\n        graph.\n    n_slices (int): Number of dataset slices to replicate local nodes for.\n    shared_local_params (list[str] | dict[str, list[str]]): Local-node\n        parameter names shared (tied) across slices \u2014 per-*parameter*\n        global analysis. Two forms: a flat ``list[str]`` (applied to\n        every local node that has the named params) or a\n        ``{local_node_id: [param_names]}`` mapping (per-node control, so\n        different local nodes can share different parameters). Defaults\n        to an empty list (no sharing).\n    schema_version (str): IR schema version string (default ``\"0.1\"``).\n\nExample:\n    >>> g = GlobalFitGraph(\n    ...     global_nodes=[ModelNodeSpec(id=\"peak\", model_type=\"gaussian\",\n    ...                                 parameters={...})],\n    ...     local_nodes=[ModelNodeSpec(id=\"bg\", model_type=\"constant\",\n    ...                               parameters={...})],\n    ...     n_slices=10,\n    ... )\n    >>> flat = g.to_fit_graph()  # returns a standard FitGraph",
  "properties": {
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    },
    "global_nodes": {
      "items": {
        "$ref": "#/$defs/ModelNodeSpec"
      },
      "title": "Global Nodes",
      "type": "array"
    },
    "local_nodes": {
      "items": {
        "$ref": "#/$defs/ModelNodeSpec"
      },
      "title": "Local Nodes",
      "type": "array"
    },
    "n_slices": {
      "minimum": 1,
      "title": "N Slices",
      "type": "integer"
    },
    "shared_local_params": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "additionalProperties": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "type": "object"
        }
      ],
      "title": "Shared Local Params"
    }
  },
  "required": [
    "global_nodes",
    "local_nodes",
    "n_slices"
  ],
  "title": "GlobalFitGraph",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

shared_local_params pydantic-field

Local-node parameter names shared (tied) across slices — per-parameter global analysis (the lmfit fit_multi_datasets pattern, e.g. ["sigma"] to share peak width while amplitude/center stay per-dataset).

Two forms:

  • a flat list[str] — applied to every local node that has the named params (e.g. ["sigma"]); or
  • a {local_node_id: [param_names]} mapping — per-node control (e.g. {"peak": ["sigma"], "bg": []}), so different local nodes can share different parameters.

Implemented by tying each slice \(i \geq 1\) replica's parameter to slice 0's via an expr_edge, so the shared value is optimised jointly over all datasets. Names absent on a node, or non-varying, are ignored. Use global_nodes to share every parameter of a node.

to_fit_graph()

Assemble a flat FitGraph by replicating local nodes per slice.

Returns:

Type Description
FitGraph

A FitGraph with global nodes followed by n_slices copies of

FitGraph

each local node. Local node ids are suffixed "_s{i}" (e.g.

FitGraph

"bg_s0", "bg_s1" …), each dataset_index-scoped to its

FitGraph

slice. Any shared_local_params are tied across slices via

FitGraph

expr_edges (slice \(i \geq 1\) follows slice 0), giving

FitGraph

per-parameter global analysis.

Note

Per-parameter sharing builds one ExprEdge per shared name per slice \(i \geq 1\), tying that replica's parameter to slice 0's value (shared_local_params documents the flat-list vs per-node-mapping forms this accepts). A name absent from a node, or fixed (vary=False), is silently skipped for that node.

fit(datasets, options=None)

Fit all datasets in a single simultaneous (joint) solve.

The lmfit fit_multi_datasets pattern. Builds one flattened FitGraph via to_fit_graph (global nodes + per-dataset local replicas scoped through dataset_index) and minimises every shared and local parameter together over the concatenated residual. Shared parameters live on the global nodes; each dataset's local parameters live on its "{id}_s{i}" replica and only affect that dataset's points.

Parameters:

Name Type Description Default
datasets list[MeasurementData]

One MeasurementData per slice. Must have len(datasets) == n_slices.

required
options FitOptions | None

FitOptions (or None for defaults).

None

Returns:

Type Description
FitResult

A single FitResult: global params keyed by "{node_id}.{param}",

FitResult

per-dataset local params by "{node_id}_s{i}.{param}", and

FitResult

per-dataset diagnostics in dataset_slices.

Raises:

Type Description
SpecificationError

If len(datasets) != n_slices. Subclasses ValueError, so except ValueError still catches it.

Note

For the legacy two-stage sequential approximation (fit globals on the stack, freeze, then fit each slice's locals), use fit_all_slices.

fit_all_slices(datasets, options=None)

Fit each slice and return all per-slice results.

Two-stage strategy:

  1. Jointly fit global_nodes against all stacked data, then read each fitted value back out of the resulting FitResult.
  2. Per slice, build a FitGraph of the global nodes pinned to their Stage-1 values (vary=False) plus that slice's local-node replica, and fit it alone.

Parameters:

Name Type Description Default
datasets list[MeasurementData]

One MeasurementData per slice.

required
options FitOptions | None

FitOptions (or None for defaults).

None

Returns:

Type Description
list[FitResult]

List of FitResult, one per slice, in dataset order.

Raises:

Type Description
SpecificationError

If len(datasets) != n_slices. Subclasses ValueError, so except ValueError still catches it.

spectrafit_core.ExprEdge pydantic-model

Bases: BaseModel

A directed expression edge that constrains one parameter to a formula.

Attributes:

Name Type Description
target_node str

ID of the node whose parameter is constrained.

target_param str

Name of the parameter to constrain.

expression str

Formula referencing other node params as node_id.param.

Note

Expression edges are validated for cycles and unknown nodes at construction time. The engine parses them into a dependency-ordered, cycle-checked plan (a DAG) and evaluates them per solver iteration, so fit() applies the tie during fitting — they are not rejected.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "A directed expression edge that constrains one parameter to a formula.\n\nAttributes:\n    target_node (str): ID of the node whose parameter is constrained.\n    target_param (str): Name of the parameter to constrain.\n    expression (str): Formula referencing other node params as\n        ``node_id.param``.\n\nNote:\n    Expression edges are validated for cycles and unknown nodes at\n    construction time.  The engine parses them into a dependency-ordered,\n    cycle-checked plan (a DAG) and evaluates them per solver iteration, so\n    ``fit()`` applies the tie during fitting \u2014 they are not rejected.",
  "properties": {
    "target_node": {
      "title": "Target Node",
      "type": "string"
    },
    "target_param": {
      "title": "Target Param",
      "type": "string"
    },
    "expression": {
      "title": "Expression",
      "type": "string"
    }
  },
  "required": [
    "target_node",
    "target_param",
    "expression"
  ],
  "title": "ExprEdge",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • target_node (str)
  • target_param (str)
  • expression (str)

Input data

spectrafit_core.MeasurementData pydantic-model

Bases: BaseModel

One dataset to fit: coordinates x, observations y, and weights.

Attributes:

Name Type Description
schema_version str

IR schema version (do not change).

x list[list[float]] | list[float]

Independent coordinates on the wire as an (N, D) array: one row per data point, D columns for a D-dimensional fit (a plain length-N list for 1-D). The _validate_x before-validator promotes flat 1-D input to (N, 1). The union keeps the constructor's declared type matching what callers actually pass.

y list[float]

Observed values, length N.

sigma list[float] | None

Optional per-point uncertainties, length N; None weights every point equally.

label str | None

Optional human-readable dataset label.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "One dataset to fit: coordinates ``x``, observations ``y``, and weights.\n\nAttributes:\n    schema_version (str): IR schema version (do not change).\n    x (list[list[float]] | list[float]): Independent coordinates on the\n        wire as an ``(N, D)`` array: one row per data point, ``D``\n        columns for a ``D``-dimensional fit (a plain length-``N`` list\n        for 1-D). The ``_validate_x`` before-validator promotes flat 1-D\n        input to ``(N, 1)``. The union keeps the constructor's declared\n        type matching what callers actually pass.\n    y (list[float]): Observed values, length ``N``.\n    sigma (list[float] | None): Optional per-point uncertainties, length\n        ``N``; ``None`` weights every point equally.\n    label (str | None): Optional human-readable dataset label.",
  "properties": {
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    },
    "x": {
      "anyOf": [
        {
          "items": {
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "type": "array"
        },
        {
          "items": {
            "type": "number"
          },
          "type": "array"
        }
      ],
      "title": "X"
    },
    "y": {
      "items": {
        "type": "number"
      },
      "title": "Y",
      "type": "array"
    },
    "sigma": {
      "anyOf": [
        {
          "items": {
            "type": "number"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Sigma"
    },
    "label": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Label"
    }
  },
  "required": [
    "x",
    "y"
  ],
  "title": "MeasurementData",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • schema_version (str)
  • x (list[list[float]] | list[float])
  • y (list[float])
  • sigma (list[float] | None)
  • label (str | None)

Validators:

  • _validate_x → x
  • _validate_vector → y, sigma
  • _validate_lengths

n_points property

Return the number of data points in this dataset.

Parameters

spectrafit_core.Parameter pydantic-model

Bases: BaseModel

A single fit parameter: a value, optional bounds, and constraints.

Attributes:

Name Type Description
value float

Initial (or fixed) parameter value.

min float

Lower bound; defaults to -inf.

max float

Upper bound; defaults to +inf.

vary bool

Whether the solver may adjust this parameter. Ignored when expr is set — the engine always derives the value from the expression and excludes the parameter from the free set.

expr str | None

Optional symbolic expression tying this parameter to others; re-evaluated on every solver iteration (dependency-ordered), so the model always sees the current tied value. References other parameters as node_id.param (e.g. "g1.sigma"). When set, the parameter is excluded from the free set regardless of vary.

scale float | None

Positive scale factor for the solver's working variable: the optimiser iterates on value / scale so parameters of very different magnitude are conditioned alike. Does not change the reported physical value. None (default) disables scaling for this parameter.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
  "properties": {
    "value": {
      "title": "Value",
      "type": "number"
    },
    "min": {
      "default": -Infinity,
      "title": "Min",
      "type": "number"
    },
    "max": {
      "default": Infinity,
      "title": "Max",
      "type": "number"
    },
    "vary": {
      "default": true,
      "title": "Vary",
      "type": "boolean"
    },
    "expr": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Expr"
    },
    "scale": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Scale"
    }
  },
  "required": [
    "value"
  ],
  "title": "Parameter",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

  • value (float)
  • min (float)
  • max (float)
  • vary (bool)
  • expr (str | None)
  • scale (float | None)

Validators:

  • _null_min_to_neginf → min
  • _null_max_to_posinf → max
  • _validate_bounds
  • _validate_expr

spectrafit_core.ParameterResult pydantic-model

Bases: Parameter

Fitted parameter with name and uncertainty.

Attributes:

Name Type Description
name str | None

Dotted parameter name ("node_id.param"), if known.

stderr float | None

Estimated standard error, or None when unavailable.

Show JSON schema:
{
  "additionalProperties": false,
  "description": "Fitted parameter with name and uncertainty.\n\nAttributes:\n    name (str | None): Dotted parameter name (``\"node_id.param\"``), if\n        known.\n    stderr (float | None): Estimated standard error, or ``None`` when\n        unavailable.",
  "properties": {
    "value": {
      "title": "Value",
      "type": "number"
    },
    "min": {
      "default": -Infinity,
      "title": "Min",
      "type": "number"
    },
    "max": {
      "default": Infinity,
      "title": "Max",
      "type": "number"
    },
    "vary": {
      "default": true,
      "title": "Vary",
      "type": "boolean"
    },
    "expr": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Expr"
    },
    "scale": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Scale"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Name"
    },
    "stderr": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Stderr"
    }
  },
  "required": [
    "value"
  ],
  "title": "ParameterResult",
  "type": "object"
}

Fields:

  • value (float)
  • min (float)
  • max (float)
  • vary (bool)
  • expr (str | None)
  • scale (float | None)
  • name (str | None)
  • stderr (float | None)

Validators:

  • _null_min_to_neginf → min
  • _null_max_to_posinf → max
  • _validate_bounds
  • _validate_expr

Compose builder

spectrafit_core.compose

compose() DSL — sugar for building FitGraph.

The FitGraph Pydantic contract is unchanged. This module adds two layers on top of it:

  • Factory functions per canonical ModelType member (gaussian, lorentzian, voigt, …) that take the model's parameters as keyword arguments and return a ready-to-use ModelNodeSpec. Bounds and other Parameter fields propagate via the <param>_<field>= convention (e.g. amplitude_min=0.0).

For the amplitude / center / sigma family — the dominant spectral convention — three single-letter shorthands are also accepted:

Shorthand Canonical
a amplitude
c center
s sigma

Bound kwargs follow the same shorthand: a_min= is equivalent to amplitude_min=. The shorthand is only accepted on models that actually have those canonical params; on e.g. constant (whose only parameter is c), c=... is itself the canonical kwarg and is not treated as a shorthand for center.

  • compose + ComposeBuilder — a chainable helper that gathers a list of nodes and optional ExprEdge constraints, then builds a FitGraph. The builder is also iterable, so existing FitGraph(nodes=[…]) call-sites work unchanged when fed compose([…]) directly.

The output is byte-identical to a hand-rolled FitGraph: compose(...).build().model_dump_json() == handrolled.model_dump_json(). This is asserted in tests/test_compose_dsl.py for every ModelType member.

CANONICAL_PARAMS = {ModelType.GAUSSIAN: ('amplitude', 'center', 'sigma'), ModelType.GAUSSIAN2D: ('amplitude', 'center_x', 'center_y', 'sigma_x', 'sigma_y'), ModelType.LORENTZIAN: ('amplitude', 'center', 'sigma'), ModelType.VOIGT: ('amplitude', 'center', 'sigma', 'fraction'), ModelType.CONSTANT: ('c',), ModelType.LINEAR: ('slope', 'intercept'), ModelType.QUADRATIC: ('amplitude', 'center', 'offset'), ModelType.ARCTAN_STEP: ('amplitude', 'center', 'sigma'), ModelType.TANH_STEP: ('amplitude', 'center', 'sigma'), ModelType.ERFC_STEP: ('amplitude', 'center', 'sigma'), ModelType.PSEUDO_VOIGT: ('amplitude', 'center', 'sigma', 'fraction'), ModelType.FANO: ('amplitude', 'center', 'gamma', 'q'), ModelType.DOUBLE_EXPONENTIAL: ('A1', 'lam1', 'A2', 'lam2'), ModelType.TRUE_VOIGT: ('amplitude', 'center', 'sigma', 'gamma'), ModelType.SKEWED_GAUSSIAN: ('amplitude', 'center', 'sigma', 'gamma'), ModelType.EXP_GAUSSIAN: ('amplitude', 'center', 'sigma', 'gamma'), ModelType.DONIACH: ('amplitude', 'center', 'sigma', 'gamma'), ModelType.LOG_NORMAL: ('amplitude', 'center', 'sigma'), ModelType.PEARSON7: ('amplitude', 'center', 'sigma', 'm'), ModelType.SPLIT_GAUSSIAN: ('amplitude', 'center', 'sigma_l', 'sigma_r'), ModelType.MOFFAT: ('amplitude', 'center', 'sigma', 'beta'), ModelType.STUDENTS_T: ('amplitude', 'center', 'sigma', 'nu'), ModelType.SPLIT_PEARSON7: ('amplitude', 'center', 'sigma_l', 'sigma_r', 'm_l', 'm_r'), ModelType.BREIT_WIGNER: ('amplitude', 'center', 'sigma', 'q'), ModelType.ASYM_IR: ('amplitude', 'center', 'sigma', 'k'), ModelType.HARMONIC_IR: ('amplitude', 'center', 'sigma'), ModelType.TAUC: ('amplitude', 'e_gap', 'exponent'), ModelType.CAUCHY_DISPERSION: ('a', 'b', 'c'), ModelType.KWW: ('amplitude', 'tau', 'beta'), ModelType.SATURATING_EXPONENTIAL: ('amplitude', 'rate'), ModelType.POWER_SATURATION: ('amplitude', 'rate'), ModelType.POWER_LAW_OFFSET: ('amplitude', 'offset', 'shape'), ModelType.MGH09_RATIONAL: ('amplitude', 'num_lin', 'den_lin', 'den_const'), ModelType.RATIONAL_CUBIC: ('a0', 'a1', 'a2', 'a3', 'b1', 'b2', 'b3'), ModelType.GENERALISED_LOGISTIC: ('amplitude', 'shift', 'rate', 'shape'), ModelType.EXP_OVER_LINEAR: ('rate', 'lin_const', 'lin_slope')} module-attribute

Info

Canonical parameter table: single source of truth for the canonical parameter names of every ModelType.

Warning

Keep in lock-step with:

  • crates/spectrafit-models/src/*.rs
  • python/oracles/models.py (the parity oracle)
  • the test_compose_param_names_match_bench_registry test, which pins this

ComposeBuilder pydantic-model

Bases: BaseModel

Chainable accumulator that turns a list of nodes + ties into a graph.

Iterating over a ComposeBuilder yields its nodes, so the builder can be passed directly into FitGraph(nodes=...) for backward compatibility. Call build to get a fully validated FitGraph, including any ExprEdge constraints added via bind.

Attributes:

Name Type Description
nodes list[ModelNodeSpec]

The model nodes collected by compose.

expr_edges list[ExprEdge]

Parameter-constraint edges added via bind.

schema_version str

IR schema version forwarded to FitGraph.

Show JSON schema:
{
  "$defs": {
    "ExprEdge": {
      "additionalProperties": false,
      "description": "A directed expression edge that constrains one parameter to a formula.\n\nAttributes:\n    target_node (str): ID of the node whose parameter is constrained.\n    target_param (str): Name of the parameter to constrain.\n    expression (str): Formula referencing other node params as\n        ``node_id.param``.\n\nNote:\n    Expression edges are validated for cycles and unknown nodes at\n    construction time.  The engine parses them into a dependency-ordered,\n    cycle-checked plan (a DAG) and evaluates them per solver iteration, so\n    ``fit()`` applies the tie during fitting \u2014 they are not rejected.",
      "properties": {
        "target_node": {
          "title": "Target Node",
          "type": "string"
        },
        "target_param": {
          "title": "Target Param",
          "type": "string"
        },
        "expression": {
          "title": "Expression",
          "type": "string"
        }
      },
      "required": [
        "target_node",
        "target_param",
        "expression"
      ],
      "title": "ExprEdge",
      "type": "object"
    },
    "ModelNodeSpec": {
      "additionalProperties": false,
      "description": "One model node: a unique ``id``, a ``model_type``, and its parameters.\n\nAttributes:\n    id (str): Unique node identifier within a graph.\n    model_type (ModelType): Which model kernel this node evaluates.\n    parameters (dict[str, Parameter]): Parameter definitions keyed by\n        parameter name.\n    dataset_index (int | None): Dataset scope for simultaneous\n        multi-dataset (\"global analysis\") fits. ``None`` (default) =\n        global node, contributing to every dataset's points. ``i`` =\n        local to dataset ``i``, contributing residuals/Jacobian only to\n        that dataset's contiguous point-range.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "model_type": {
          "$ref": "#/$defs/ModelType"
        },
        "parameters": {
          "additionalProperties": {
            "$ref": "#/$defs/Parameter"
          },
          "title": "Parameters",
          "type": "object"
        },
        "dataset_index": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dataset Index"
        }
      },
      "required": [
        "id",
        "model_type",
        "parameters"
      ],
      "title": "ModelNodeSpec",
      "type": "object"
    },
    "ModelType": {
      "description": "Supported model kernels, identified by their canonical string name.",
      "enum": [
        "gaussian",
        "gaussian2d",
        "gaussian_nd",
        "lorentzian",
        "voigt",
        "constant",
        "linear",
        "quadratic",
        "arctan_step",
        "tanh_step",
        "erfc_step",
        "pseudo_voigt",
        "fano",
        "double_exponential",
        "true_voigt",
        "skewed_gaussian",
        "exp_gaussian",
        "doniach_sunjic",
        "log_normal",
        "pearson7",
        "split_gaussian",
        "moffat",
        "students_t",
        "split_pearson7",
        "breit_wigner",
        "asym_ir",
        "harmonic_ir",
        "tauc",
        "cauchy_dispersion",
        "kww",
        "saturating_exponential",
        "power_saturation",
        "power_law_offset",
        "mgh09_rational",
        "rational_cubic",
        "generalised_logistic",
        "exp_over_linear"
      ],
      "title": "ModelType",
      "type": "string"
    },
    "Parameter": {
      "additionalProperties": false,
      "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        }
      },
      "required": [
        "value"
      ],
      "title": "Parameter",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "Chainable accumulator that turns a list of nodes + ties into a graph.\n\nIterating over a [`ComposeBuilder`][spectrafit_core.ComposeBuilder] yields its nodes, so the\nbuilder can be passed directly into ``FitGraph(nodes=...)`` for backward\ncompatibility.  Call [`build`][spectrafit_core.ComposeBuilder.build] to get a fully validated\n[`FitGraph`][spectrafit_core.FitGraph], including any\n[`ExprEdge`][spectrafit_core.ExprEdge] constraints added via\n[`bind`][spectrafit_core.ComposeBuilder.bind].\n\nAttributes:\n    nodes (list[ModelNodeSpec]): The model nodes collected by\n        [`compose`][spectrafit_core.compose].\n    expr_edges (list[ExprEdge]): Parameter-constraint edges added via\n        [`bind`][spectrafit_core.ComposeBuilder.bind].\n    schema_version (str): IR schema version forwarded to\n        [`FitGraph`][spectrafit_core.FitGraph].",
  "properties": {
    "nodes": {
      "items": {
        "$ref": "#/$defs/ModelNodeSpec"
      },
      "title": "Nodes",
      "type": "array"
    },
    "expr_edges": {
      "default": [],
      "items": {
        "$ref": "#/$defs/ExprEdge"
      },
      "title": "Expr Edges",
      "type": "array"
    },
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    }
  },
  "required": [
    "nodes"
  ],
  "title": "ComposeBuilder",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

bind(expression, to)

Add an ExprEdge tying to to expression.

Parameters:

Name Type Description Default
expression str

A formula referencing other nodes' parameters as "node_id.param".

required
to str

The target "node_id.param" whose value is constrained. Both positional and keyword forms are accepted, so bind("g0.sigma", "g1.sigma") and bind("g0.sigma", to="g1.sigma") are equivalent.

required

Returns:

Type Description
ComposeBuilder

self, so calls can be chained.

Raises:

Type Description
SpecificationError

If to is not in "node_id.param" form. Subclasses ValueError, so except ValueError still catches it.

build()

Build an immutable FitGraph.

__iter__()

Yield the collected nodes so FitGraph(nodes=compose([…])) works.

Overriding the Pydantic-default __iter__ (which yields field (name, value) pairs) with a node iterator is intentional — this is the public iteration protocol callers rely on. Use self.model_dump() if you need the underlying field view.

Note

ty is this repo's authoritative checker, so one ty: ignore suppression is kept above; a matching mypy suppression was dropped since mypy is not a CI gate here.

gaussian(id, *, dataset_index=None, **params)

Build a Gaussian peak node.

\[ A \exp\left(-\frac{1}{2}\left(\frac{x-c}{\sigma}\right)^2\right) \]

Required params: amplitude (or a), center (or c), sigma (or s). Bound suffixes (a_min=, sigma_max=, …) propagate to the underlying Parameter.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

gaussian2d(id, *, dataset_index=None, **params)

Build a 2-D Gaussian node.

Required params: amplitude, center_x, center_y, sigma_x, sigma_y.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center_x, center_y, sigma_x, sigma_y. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

lorentzian(id, *, dataset_index=None, **params)

Build a Lorentzian peak node.

\[ \frac{A}{1 + \left(\frac{x-c}{\sigma}\right)^2} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

voigt(id, *, dataset_index=None, **params)

Build a Voigt (pseudo-Voigt alias) node.

Required params: amplitude, center, sigma, fraction.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, fraction. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

constant(id, *, dataset_index=None, **params)

Build a constant background node.

Required param: c (the constant value). Note c here is the canonical param name, not a shorthand for center.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for c. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. c_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

linear(id, *, dataset_index=None, **params)

Build a linear background node.

\[ \text{slope} \cdot x + \text{intercept} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for slope, intercept. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. slope_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

quadratic(id, *, dataset_index=None, **params)

Build a quadratic bowl node.

\[ A (x-c)^2 + \text{offset} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, offset. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

arctan_step(id, *, dataset_index=None, **params)

Build an arctan edge node.

\[ A \left(\frac{1}{2} + \frac{1}{\pi} \arctan\left(\frac{x-c}{\sigma}\right)\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

tanh_step(id, *, dataset_index=None, **params)

Build a tanh edge node.

\[ \frac{A}{2} \left(1 + \tanh\left(\frac{x-c}{\sigma}\right)\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

erfc_step(id, *, dataset_index=None, **params)

Build an erfc edge node.

\[ \frac{A}{2} \operatorname{erfc}\left(\frac{x-c}{\sigma\sqrt{2}}\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

pseudo_voigt(id, *, dataset_index=None, **params)

Build a pseudo-Voigt node.

\[ \eta \cdot L + (1-\eta) \cdot G, \quad \eta = \text{fraction} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, fraction. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

fano(id, *, dataset_index=None, **params)

Build a Fano resonance node.

\[ A \frac{(q+\epsilon)^2}{1+\epsilon^2}, \quad \epsilon = \frac{x-c}{\gamma} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, gamma, q. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

double_exponential(id, *, dataset_index=None, **params)

Build a bi-exponential decay node.

\[ A_1 \exp(-\lambda_1 x) + A_2 \exp(-\lambda_2 x) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for A1, lam1, A2, lam2. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. A1_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

true_voigt(id, *, dataset_index=None, **params)

Build a true Voigt (Faddeeva) node.

Combines a Gaussian \(\sigma\) with a Lorentzian \(\gamma\).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, gamma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

skewed_gaussian(id, *, dataset_index=None, **params)

Build a skewed Gaussian node (\(\gamma\) = skew).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, gamma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

exp_gaussian(id, *, dataset_index=None, **params)

Build an exponentially-modified Gaussian (EMG) node.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, gamma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

doniach_sunjic(id, *, dataset_index=None, **params)

Build a Doniach–Šunjić node (\(\gamma\) = asymmetry).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, gamma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

log_normal(id, *, dataset_index=None, **params)

Build a log-normal peak node.

\[ A \exp\left(-\frac{(\ln(x/c))^2}{2\sigma^2}\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

pearson7(id, *, dataset_index=None, **params)

Build a Pearson VII node (shape exponent m).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, m. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

split_gaussian(id, *, dataset_index=None, **params)

Build a split-\(\sigma\) asymmetric Gaussian node (sigma_l, sigma_r).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma_l, sigma_r. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

moffat(id, *, dataset_index=None, **params)

Build a Moffat node.

\[ \frac{A}{\left(\left(\frac{x-c}{\sigma}\right)^2+1\right)^\beta} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, beta. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

students_t(id, *, dataset_index=None, **params)

Build a Student's-t node (degrees-of-freedom nu).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, nu. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

split_pearson7(id, *, dataset_index=None, **params)

Build a split Pearson VII node (split width + exponent each side).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma_l, sigma_r, m_l, m_r. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

breit_wigner(id, *, dataset_index=None, **params)

Build a Breit-Wigner-Fano node.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, q. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

asym_ir(id, *, dataset_index=None, **params)

Build an asymmetric IR band node (sigmoid asymmetry k).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma, k. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

harmonic_ir(id, *, dataset_index=None, **params)

Build a harmonic-oscillator IR band node.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, center, sigma. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

tauc(id, *, dataset_index=None, **params)

Build a Tauc band-gap edge node (e_gap, exponent).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, e_gap, exponent. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

cauchy_dispersion(id, *, dataset_index=None, **params)

Build a Cauchy dispersion node.

\[ a + \frac{b}{x^2} + \frac{c}{x^4} \]

Note a, b, c here are the canonical Cauchy coefficients, not the a/c shorthand (the shorthand only applies to models that carry amplitude/center/sigma).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for a, b, c. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. a_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

kww(id, *, dataset_index=None, **params)

Build a KWW stretched-exponential node.

\[ A \exp\left(-(x/\tau)^\beta\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, tau, beta. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

saturating_exponential(id, *, dataset_index=None, **params)

Build a saturating-exponential node (BoxBOD).

\[ \text{amplitude} \left(1 - \exp(-\text{rate} \cdot x)\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, rate. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

power_saturation(id, *, dataset_index=None, **params)

Build a power-saturation node (Misra1b).

\[ \text{amplitude} \left(1 - \left(1 + \frac{\text{rate} \cdot x}{2}\right)^{-2}\right) \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, rate. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

power_law_offset(id, *, dataset_index=None, **params)

Build a power-law-with-offset node (amplitude, offset, shape).

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, offset, shape. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

mgh09_rational(id, *, dataset_index=None, **params)

Build an MGH09 rational node.

Parameters are amplitude, num_lin, den_lin, and den_const.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, num_lin, den_lin, den_const. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

rational_cubic(id, *, dataset_index=None, **params)

Build a rational cubic node (a0-a3 over b1-b3).

\[ \frac{a_0 + a_1 x + a_2 x^2 + a_3 x^3}{1 + b_1 x + b_2 x^2 + b_3 x^3} \]

A lower-order rational is this node with the unused coefficients held fixed at zero, so a quadratic/quadratic model fixes a3 and b3.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for a0, a1, a2, a3, b1, b2, b3. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. a0_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

generalised_logistic(id, *, dataset_index=None, **params)

Build a generalised-logistic node.

\[ \frac{\text{amplitude}}{\left(1 + \exp(\text{shift} - \text{rate} \cdot x)\right) ^{1/\text{shape}}} \]

Holding shape fixed at 1 gives the plain logistic.

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for amplitude, shift, rate, shape. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. amplitude_min=0.0); the a/c/s shorthand described in the module docstring applies wherever amplitude/center/sigma appear above.

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

exp_over_linear(id, *, dataset_index=None, **params)

Build an exponential-over-line node (rate, lin_const, lin_slope).

\[ \frac{\exp(-\text{rate} \cdot x)}{\text{lin\_const} + \text{lin\_slope} \cdot x} \]

Parameters:

Name Type Description Default
id str

Unique node identifier within the graph.

required
dataset_index int | None

Forwarded to ModelNodeSpec.dataset_index; None (default) marks a global node contributing to every dataset, or an integer scopes the node to that dataset.

None
**params ParamKwarg

Required keyword values for rate, lin_const, lin_slope. Each name also accepts a <param>_min / _max / _vary / _expr / _scale suffix to populate that Parameter field (e.g. rate_min=0.0).

{}

Returns:

Type Description
ModelNodeSpec

A validated ModelNodeSpec for this node.

Raises:

Type Description
TypeError

If a required parameter has no value, or a kwarg does not map to a canonical parameter name (or accepted shorthand/suffix).

compose(nodes)

Build a ComposeBuilder from model nodes.

Parameters:

Name Type Description Default
nodes Sequence[ModelNodeSpec]

Nodes built via the factory functions above (or any other ModelNodeSpec instances).

required

Returns:

Type Description
ComposeBuilder

A fresh ComposeBuilder ready for

ComposeBuilder
ComposeBuilder

spectrafit_core.ComposeBuilder pydantic-model

Bases: BaseModel

Chainable accumulator that turns a list of nodes + ties into a graph.

Iterating over a ComposeBuilder yields its nodes, so the builder can be passed directly into FitGraph(nodes=...) for backward compatibility. Call build to get a fully validated FitGraph, including any ExprEdge constraints added via bind.

Attributes:

Name Type Description
nodes list[ModelNodeSpec]

The model nodes collected by compose.

expr_edges list[ExprEdge]

Parameter-constraint edges added via bind.

schema_version str

IR schema version forwarded to FitGraph.

Show JSON schema:
{
  "$defs": {
    "ExprEdge": {
      "additionalProperties": false,
      "description": "A directed expression edge that constrains one parameter to a formula.\n\nAttributes:\n    target_node (str): ID of the node whose parameter is constrained.\n    target_param (str): Name of the parameter to constrain.\n    expression (str): Formula referencing other node params as\n        ``node_id.param``.\n\nNote:\n    Expression edges are validated for cycles and unknown nodes at\n    construction time.  The engine parses them into a dependency-ordered,\n    cycle-checked plan (a DAG) and evaluates them per solver iteration, so\n    ``fit()`` applies the tie during fitting \u2014 they are not rejected.",
      "properties": {
        "target_node": {
          "title": "Target Node",
          "type": "string"
        },
        "target_param": {
          "title": "Target Param",
          "type": "string"
        },
        "expression": {
          "title": "Expression",
          "type": "string"
        }
      },
      "required": [
        "target_node",
        "target_param",
        "expression"
      ],
      "title": "ExprEdge",
      "type": "object"
    },
    "ModelNodeSpec": {
      "additionalProperties": false,
      "description": "One model node: a unique ``id``, a ``model_type``, and its parameters.\n\nAttributes:\n    id (str): Unique node identifier within a graph.\n    model_type (ModelType): Which model kernel this node evaluates.\n    parameters (dict[str, Parameter]): Parameter definitions keyed by\n        parameter name.\n    dataset_index (int | None): Dataset scope for simultaneous\n        multi-dataset (\"global analysis\") fits. ``None`` (default) =\n        global node, contributing to every dataset's points. ``i`` =\n        local to dataset ``i``, contributing residuals/Jacobian only to\n        that dataset's contiguous point-range.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "model_type": {
          "$ref": "#/$defs/ModelType"
        },
        "parameters": {
          "additionalProperties": {
            "$ref": "#/$defs/Parameter"
          },
          "title": "Parameters",
          "type": "object"
        },
        "dataset_index": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dataset Index"
        }
      },
      "required": [
        "id",
        "model_type",
        "parameters"
      ],
      "title": "ModelNodeSpec",
      "type": "object"
    },
    "ModelType": {
      "description": "Supported model kernels, identified by their canonical string name.",
      "enum": [
        "gaussian",
        "gaussian2d",
        "gaussian_nd",
        "lorentzian",
        "voigt",
        "constant",
        "linear",
        "quadratic",
        "arctan_step",
        "tanh_step",
        "erfc_step",
        "pseudo_voigt",
        "fano",
        "double_exponential",
        "true_voigt",
        "skewed_gaussian",
        "exp_gaussian",
        "doniach_sunjic",
        "log_normal",
        "pearson7",
        "split_gaussian",
        "moffat",
        "students_t",
        "split_pearson7",
        "breit_wigner",
        "asym_ir",
        "harmonic_ir",
        "tauc",
        "cauchy_dispersion",
        "kww",
        "saturating_exponential",
        "power_saturation",
        "power_law_offset",
        "mgh09_rational",
        "rational_cubic",
        "generalised_logistic",
        "exp_over_linear"
      ],
      "title": "ModelType",
      "type": "string"
    },
    "Parameter": {
      "additionalProperties": false,
      "description": "A single fit parameter: a value, optional bounds, and constraints.\n\nAttributes:\n    value (float): Initial (or fixed) parameter value.\n    min (float): Lower bound; defaults to ``-inf``.\n    max (float): Upper bound; defaults to ``+inf``.\n    vary (bool): Whether the solver may adjust this parameter.  Ignored\n        when ``expr`` is set \u2014 the engine always derives the value from\n        the expression and excludes the parameter from the free set.\n    expr (str | None): Optional symbolic expression tying this parameter\n        to others; re-evaluated on every solver iteration\n        (dependency-ordered), so the model always sees the current tied\n        value. References other parameters as ``node_id.param`` (e.g.\n        ``\"g1.sigma\"``). When set, the parameter is excluded from the\n        free set regardless of ``vary``.\n    scale (float | None): Positive scale factor for the solver's working\n        variable: the optimiser iterates on ``value / scale`` so\n        parameters of very different magnitude are conditioned alike.\n        Does not change the reported physical value. ``None`` (default)\n        disables scaling for this parameter.",
      "properties": {
        "value": {
          "title": "Value",
          "type": "number"
        },
        "min": {
          "default": -Infinity,
          "title": "Min",
          "type": "number"
        },
        "max": {
          "default": Infinity,
          "title": "Max",
          "type": "number"
        },
        "vary": {
          "default": true,
          "title": "Vary",
          "type": "boolean"
        },
        "expr": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Expr"
        },
        "scale": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Scale"
        }
      },
      "required": [
        "value"
      ],
      "title": "Parameter",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "description": "Chainable accumulator that turns a list of nodes + ties into a graph.\n\nIterating over a [`ComposeBuilder`][spectrafit_core.ComposeBuilder] yields its nodes, so the\nbuilder can be passed directly into ``FitGraph(nodes=...)`` for backward\ncompatibility.  Call [`build`][spectrafit_core.ComposeBuilder.build] to get a fully validated\n[`FitGraph`][spectrafit_core.FitGraph], including any\n[`ExprEdge`][spectrafit_core.ExprEdge] constraints added via\n[`bind`][spectrafit_core.ComposeBuilder.bind].\n\nAttributes:\n    nodes (list[ModelNodeSpec]): The model nodes collected by\n        [`compose`][spectrafit_core.compose].\n    expr_edges (list[ExprEdge]): Parameter-constraint edges added via\n        [`bind`][spectrafit_core.ComposeBuilder.bind].\n    schema_version (str): IR schema version forwarded to\n        [`FitGraph`][spectrafit_core.FitGraph].",
  "properties": {
    "nodes": {
      "items": {
        "$ref": "#/$defs/ModelNodeSpec"
      },
      "title": "Nodes",
      "type": "array"
    },
    "expr_edges": {
      "default": [],
      "items": {
        "$ref": "#/$defs/ExprEdge"
      },
      "title": "Expr Edges",
      "type": "array"
    },
    "schema_version": {
      "default": "0.1",
      "title": "Schema Version",
      "type": "string"
    }
  },
  "required": [
    "nodes"
  ],
  "title": "ComposeBuilder",
  "type": "object"
}

Config:

  • extra: forbid

Fields:

bind(expression, to)

Add an ExprEdge tying to to expression.

Parameters:

Name Type Description Default
expression str

A formula referencing other nodes' parameters as "node_id.param".

required
to str

The target "node_id.param" whose value is constrained. Both positional and keyword forms are accepted, so bind("g0.sigma", "g1.sigma") and bind("g0.sigma", to="g1.sigma") are equivalent.

required

Returns:

Type Description
ComposeBuilder

self, so calls can be chained.

Raises:

Type Description
SpecificationError

If to is not in "node_id.param" form. Subclasses ValueError, so except ValueError still catches it.

build()

Build an immutable FitGraph.

__iter__()

Yield the collected nodes so FitGraph(nodes=compose([…])) works.

Overriding the Pydantic-default __iter__ (which yields field (name, value) pairs) with a node iterator is intentional — this is the public iteration protocol callers rely on. Use self.model_dump() if you need the underlying field view.

Note

ty is this repo's authoritative checker, so one ty: ignore suppression is kept above; a matching mypy suppression was dropped since mypy is not a CI gate here.