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 |
required |
data
|
MeasurementInput
|
One or more |
required |
options
|
FitOptions | None
|
Solver configuration; defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
FitResult
|
A |
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 |
required |
data
|
MeasurementInput
|
One or more |
required |
options
|
FitOptions | None
|
Solver configuration; defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
FitResult
|
Tuple of |
ndarray
|
1-D float64 NumPy array of length |
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 |
required |
data
|
MeasurementInput
|
One or more |
required |
options
|
FitOptions | None
|
Solver configuration; defaults to
|
None
|
Returns:
| Type | Description |
|---|---|
FitResult
|
Tuple of |
ndarray
|
1-D float64 NumPy array of length |
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
|
required |
params
|
Mapping[str, object]
|
Parameter values keyed by
|
required |
data
|
MeasurementInput
|
One or more datasets as a
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
A 1-D |
ndarray
|
data point, in the same order as the flattened input |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
ValueError
|
Propagated across the PyO3 boundary from the Rust engine
if |
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
|
required |
params
|
Mapping[str, object]
|
Parameter values keyed by
|
required |
data
|
MeasurementInput
|
One or more datasets as a
|
required |
Returns:
| Type | Description |
|---|---|
dict[str, ndarray]
|
A dict mapping each node's |
dict[str, ndarray]
|
|
dict[str, ndarray]
|
point, in the same order as the flattened input |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
ValueError
|
Propagated across the PyO3 boundary from the Rust engine
if |
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
|
required |
params
|
Mapping[str, object]
|
Parameter values keyed by
|
required |
data
|
MeasurementInput
|
One or more datasets as a
|
required |
Returns:
| Type | Description |
|---|---|
dict[str, ndarray]
|
A dict mapping each node's |
dict[str, ndarray]
|
|
dict[str, ndarray]
|
point, in the same order as the flattened input |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If |
ValueError
|
Propagated across the PyO3 boundary from the Rust engine
if |
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 |
covariance |
list[list[float | None]] | None
|
Parameter covariance
matrix (row/column order matches |
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
|
reduced_chi2 |
float
|
|
r_squared |
float
|
Coefficient of determination \(R^2\). |
dof |
int
|
Degrees of freedom |
aic |
float
|
Akaike Information Criterion. |
bic |
float
|
Bayesian Information Criterion. |
n_iter |
int
|
Accepted solver iterations for the faer-native solvers;
for |
n_func_evals |
int | None
|
Number of residual evaluations ( |
n_jac_evals |
int | None
|
Number of Jacobian evaluations ( |
success |
bool
|
|
message |
str
|
Stable termination-reason key, not free text — one of
the strings produced by
Match on the |
best_fit |
list[float]
|
Model values at the fitted parameters, one per data point. |
residuals |
list[float]
|
Signed residuals |
init_fit |
list[float]
|
Model values evaluated at the initial-guess parameters. |
components |
dict[str, list[float]]
|
Per-node contributions summing
to |
dataset_slices |
list[DatasetSlice] | None
|
Per-dataset diagnostics
for multi-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.
|
n_de_generations |
int | None
|
Number of differential-evolution
generations run before the LM refinement on the
|
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 ( |
gradient_norm_history |
list[float]
|
Per-iteration gradient
infinity-norm \(\|J^\mathsf{T}r\|_\infty\) recorded alongside each
|
params_history |
list[list[float]]
|
Per-iteration free-parameter
vector \(\theta\) recorded alongside each |
covariance_param_order |
list[str] | None
|
Ordered list of
free-parameter names that index the rows and columns of
|
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:forbidpopulate_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 |
n_points |
int
|
Number of data points in this slice. |
best_fit |
list[float]
|
Model values at the fitted parameters, length
|
residuals |
list[float]
|
Signed residuals |
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. |
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:
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 (
|
max_iterations |
int
|
Solver patience (default 200). The
function-evaluation budget is |
tolerance |
float
|
Convergence tolerance passed to the solver
(default 1e-8). Smaller values produce tighter convergence at the
cost of more iterations. Set to |
delta0 |
float | None
|
Initial trust-region radius \(\Delta\) for
|
max_delta |
float | None
|
Hard upper bound on the trust-region radius
for |
eta |
float | None
|
Step-acceptance threshold for |
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 |
nodes |
list[ModelNodeSpec]
|
Ordered list of model nodes (each with a
unique |
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:
-
schema_version(str) -
nodes(list[ModelNodeSpec]) -
expr_edges(list[ExprEdge])
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
|
required |
data
|
MeasurementInput
|
Exactly one 1-D dataset; only its |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
A 1-D |
ndarray
|
point, in the same order as the flattened input |
Raises:
| Type | Description |
|---|---|
ValueError
|
If more than one dataset is passed or the
coordinates are n-D (use |
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
|
required |
data
|
MeasurementInput
|
Exactly one 1-D dataset; only its |
required |
Returns:
| Type | Description |
|---|---|
dict[str, ndarray]
|
A dict mapping each node's |
dict[str, ndarray]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If more than one dataset is passed or the
coordinates are n-D (use |
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
|
local_nodes |
list[ModelNodeSpec]
|
Nodes whose parameters are
replicated per dataset. Each local node |
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 |
schema_version |
str
|
IR schema version string (default |
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:
-
schema_version(str) -
global_nodes(list[ModelNodeSpec]) -
local_nodes(list[ModelNodeSpec]) -
n_slices(int) -
shared_local_params(list[str] | dict[str, list[str]])
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
|
each local node. Local node ids are suffixed |
FitGraph
|
|
FitGraph
|
slice. Any |
FitGraph
|
|
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 |
required |
options
|
FitOptions | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
FitResult
|
A single |
FitResult
|
per-dataset local params by |
FitResult
|
per-dataset diagnostics in |
Raises:
| Type | Description |
|---|---|
SpecificationError
|
If |
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:
- Jointly fit
global_nodesagainst all stacked data, then read each fitted value back out of the resultingFitResult. - Per slice, build a
FitGraphof 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 |
required |
options
|
FitOptions | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
list[FitResult]
|
List of |
Raises:
| Type | Description |
|---|---|
SpecificationError
|
If |
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
|
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 |
y |
list[float]
|
Observed values, length |
sigma |
list[float] | None
|
Optional per-point uncertainties, length
|
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 |
max |
float
|
Upper bound; defaults to |
vary |
bool
|
Whether the solver may adjust this parameter. Ignored
when |
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 |
scale |
float | None
|
Positive scale factor for the solver's working
variable: the optimiser iterates on |
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 ( |
stderr |
float | None
|
Estimated standard error, or |
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
ModelTypemember (gaussian,lorentzian,voigt, …) that take the model's parameters as keyword arguments and return a ready-to-useModelNodeSpec. Bounds and otherParameterfields 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 optionalExprEdgeconstraints, then builds aFitGraph. The builder is also iterable, so existingFitGraph(nodes=[…])call-sites work unchanged when fedcompose([…])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/*.rspython/oracles/models.py(the parity oracle)- the
test_compose_param_names_match_bench_registrytest, 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
|
expr_edges |
list[ExprEdge]
|
Parameter-constraint edges added via
|
schema_version |
str
|
IR schema version forwarded to
|
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:
-
nodes(list[ModelNodeSpec]) -
expr_edges(list[ExprEdge]) -
schema_version(str)
bind(expression, to)
¶
Add an ExprEdge tying to to expression.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
str
|
A formula referencing other nodes' parameters
as |
required |
to
|
str
|
The target |
required |
Returns:
| Type | Description |
|---|---|
ComposeBuilder
|
|
Raises:
| Type | Description |
|---|---|
SpecificationError
|
If |
__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.
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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).
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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.
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
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Unique node identifier within the graph. |
required |
dataset_index
|
int | None
|
Forwarded to
|
None
|
**params
|
ParamKwarg
|
Required keyword values for |
{}
|
Returns:
| Type | Description |
|---|---|
ModelNodeSpec
|
A validated |
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
|
required |
Returns:
| Type | Description |
|---|---|
ComposeBuilder
|
A fresh |
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
|
expr_edges |
list[ExprEdge]
|
Parameter-constraint edges added via
|
schema_version |
str
|
IR schema version forwarded to
|
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:
-
nodes(list[ModelNodeSpec]) -
expr_edges(list[ExprEdge]) -
schema_version(str)
bind(expression, to)
¶
Add an ExprEdge tying to to expression.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expression
|
str
|
A formula referencing other nodes' parameters
as |
required |
to
|
str
|
The target |
required |
Returns:
| Type | Description |
|---|---|
ComposeBuilder
|
|
Raises:
| Type | Description |
|---|---|
SpecificationError
|
If |
__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.