midas-uq
Cross-validation based uncertainty quantification for High-Energy
Diffraction Microscopy (HEDM) grain refinement. Three diagnostics, three
modalities (far-field, point-focused, near-field), all built on the
end-to-end differentiable forward model of
midas-diffract.
Companion paper: H. Sharma, J.-S. Park, P. Kenesei, N. Andrejevic & M. Cherukara, Cross-Validation Based Uncertainty Quantification for HEDM Grain Refinement, IUCrJ (in preparation, 2026).
What it does
For each grain in a polycrystal HEDM refinement, midas-uq answers:
- How reproducible is this grain's refined state under random
resampling of its measured spots (or omega frames)? —
half_half - Which individual measurements are driving the fit, and which (if
any) appear corrupted? —
jackknife - What is the local Gaussian-posterior covariance from the inverse
Hessian? —
laplace_covariance - Is refinement overfitting in the low-spots-per-DOF regime? —
rfree_gap
No ground truth required. Half-half disagreement and jackknife influence are post-hoc and label-free. The differentiable forward model from paper I makes population-scale resampling (hundreds of grains × tens of splits) a single-CPU minute, not a day.
Installation
pip install midas-uq # adds midas-diffract automatically
Optional CPU multi-processing for population studies:
pip install "midas-uq[mp]"
Notebooks
Worked-example Jupyter notebooks live in notebooks/. They are not shipped with pip install — get them by cloning the MIDAS repository.
Quick start (Python)
import torch
import midas_uq as muq
import midas_diffract as md
# Build the forward model the usual way (see midas-diffract docs)
geom = md.HEDMGeometry(...)
model = md.HEDMForwardModel(hkls=..., thetas=..., geometry=geom)
# Grain seed (e.g., from a Grains.csv row)
init = muq.GrainState(
euler_rad=torch.tensor([phi1, Phi, phi2], dtype=torch.float64),
latc=torch.tensor([a, b, c, alpha, beta, gamma], dtype=torch.float64),
pos=torch.tensor([x, y, z], dtype=torch.float64),
)
# Observed spots: (N, 3) of (2theta, eta, omega) in radians
obs = ...
# 1. Half-half UQ
uq = muq.half_half(model, init, obs, mode="ff", n_splits=5)
print(uq.misori_median_deg, uq.lattice_median_A)
# 2. Per-spot jackknife (only on a flagged grain)
jk = muq.jackknife(model, init, obs, mode="ff")
print(jk.top_k(10)) # 10 most influential spots
# 3. Laplace baseline for comparison
sigma_vec = torch.tensor([sigma_2theta, sigma_eta, sigma_omega],
dtype=torch.float64)
lp = muq.laplace_covariance(model, init, obs, sigma_vec, refine_first=True)
print(lp.misori_p95_deg, lp.condition_number)
For NF-HEDM, observations is a (F, H, W) detector volume and the API
is the same with mode="nf"; see examples/nf_frame_split.py.
Quick start (CLI)
For a standard MIDAS dataset (Grains.csv + SpotMatrix.csv + paramstest + hkls.csv):
# Population half-half UQ (writes one row per grain)
midas-uq half-half \
--params /path/to/paramstest.txt \
--hkls /path/to/hkls.csv \
--grains /path/to/Grains.csv \
--spot-matrix /path/to/SpotMatrix.csv \
--n-splits 5 \
--out uq_half_half.csv
# Drill into a single grain
midas-uq jackknife --grain-id 5823 ...
midas-uq laplace --grain-id 5823 ...
API surface
| Symbol | Modality | Description |
|---|---|---|
half_half(model, init, obs, mode='ff'|'pf'|'nf', ...) |
all | K-split UQ dispatch |
jackknife(model, init, obs, mode=...) |
all | Leave-one-out dispatch |
half_half_spots, jackknife_spots |
FF/pf | Spot-based |
half_half_frames, jackknife_frames |
NF | Frame-based |
laplace_covariance |
FF/pf | Hessian baseline |
rfree_gap |
FF/pf | Train/holdout loss tracking |
GrainState |
- | Grain (euler, latc, pos) container |
Result dataclasses: HalfHalfResult, JackknifeResult, LaplaceResult,
RFreeResult.
When to use what
| Diagnostic | Cost per grain | Surfaces |
|---|---|---|
rfree_gap |
1× refine | overfitting at low n_obs/DOF (sparse / mosaic / pf-HEDM) |
half_half (K=5) |
10× refine | noise + model misspec + local-minimum basin |
jackknife |
N_obs × refine | per-spot leverage and corruption candidates |
laplace_covariance |
1× Hessian | local Gaussian-posterior baseline |
Half-half is the recommended population-scale diagnostic; jackknife is the drill-down on grains flagged by half-half; Laplace gives a complementary Gaussian baseline whose discrepancy from the empirical half-half spread itself diagnoses non-Gaussian posterior structure.
Reproducing the companion paper
The Ti-7Al population study, Park22 in-situ tensile sweep, synthetic
sweeps, and figure scripts live in dev/paper/ of the repository.
License
BSD-3-Clause.
Metadata
Release files for midas-uq 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| midas_uq-0.1.3.tar.gz | 38.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| midas_uq-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 74.6 kB
Release files / midas_uq-0.1.3.tar.gz
| Download URL | midas_uq-0.1.3.tar.gz |
|---|---|
| Size | 38.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4c93e420a2ec681539c5f68ea6f644f9e0638a7963fcb629e74a84545248a686
|
|
BLAKE2b-256 checksum How to use checksums |
30d93f1c96e46a6831dc66ab98d72c01d8314470024bad0b65b87bd417043d48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / midas_uq-0.1.3-py3-none-any.whl
| Download URL | midas_uq-0.1.3-py3-none-any.whl |
|---|---|
| Size | 35.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d12f2cf1370ce49a0a307f697857b1c8e02cca23a56fd7be0602f573ce38c9f6
|
|
BLAKE2b-256 checksum How to use checksums |
5919878a021c4707640498d2236101da0465eb694f9d3d50353ba3ac9a20b234
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency log