Validation
Lattent's analysis engine is a small, readable JavaScript library. The same reference datasets are fitted by Lattent and by SciPy, the standard scientific Python library, and the results must agree before any release. The test suites (tests/run-engines.js with tests/check_scipy.py for the calculators, and tests/run-studio.js with tests/check_studio.py for Studio) simulate data with known parameters and noise, fits it in both, and compare estimates, standard errors, derived quantities and p-values. A technical note further down benchmarks the fitting engine against certified reference results.
| Check | Reference | Largest relative difference |
|---|---|---|
| Dose–response, 4PL inhibitor (30 points) | scipy.optimize.curve_fit | pass parameters 1×10⁻⁸, SE 3×10⁻⁸ |
| Dose–response, 4PL agonist | curve_fit | pass parameters < 10⁻⁶, SE < 10⁻⁵ |
| Dose–response, 5PL asymmetric | curve_fit | pass parameters < 10⁻⁶, SE < 10⁻³ |
| EC80 / IC80 from the fitted curve | closed-form inverse of the 4PL | pass < 10⁻⁶ |
| Standard curve, 4PL unweighted | curve_fit | pass parameters < 10⁻⁶, SE < 10⁻³ |
| Standard curve, 4PL with 1/Y² weighting | curve_fit with sigma = Y | pass parameters < 10⁻⁶, SE < 10⁻³ |
| Sample interpolation | analytic 4PL inverse | pass < 10⁻⁶ |
| Linear standard curve | scipy.stats.linregress | pass parameters < 10⁻⁸, SE < 10⁻⁵ |
| Michaelis–Menten | curve_fit | pass parameters < 10⁻⁴, SE < 10⁻³ |
| Substrate inhibition, Hill | curve_fit | pass parameters < 10⁻⁴ |
| Competitive, non-competitive, uncompetitive, mixed (global) | curve_fit on stacked data | pass parameters < 10⁻⁴, SE < 10⁻³ |
| Mechanism selection by AICc | known simulated mechanism | pass competitive correctly preferred |
| Student t CDF and quantiles | scipy.stats.t | pass < 10⁻⁷ |
| Welch's t-test | scipy.stats.ttest_ind(equal_var=False) | pass < 10⁻⁶ |
| qPCR fold change (2^−ΔΔCt) | hand calculation | pass < 10⁻⁹ |
| qPCR p-value | ttest_ind on −ΔΔCt | pass < 10⁻⁶ |
Lattent Studio engine
| Check | Reference | Largest relative difference |
|---|---|---|
| Normality (D'Agostino–Pearson K²) | scipy.stats.normaltest | pass < 10⁻⁶ |
| Student, paired t tests | ttest_ind, ttest_rel | pass < 10⁻⁷ |
| Mann–Whitney, exact and asymptotic | mannwhitneyu | pass < 10⁻⁷ |
| One-way ANOVA, Kruskal–Wallis, Brown–Forsythe | f_oneway, kruskal, levene | pass < 10⁻⁷ |
| Welch's ANOVA | textbook formula in NumPy | pass < 10⁻⁷ |
| Tukey HSD p-values and intervals | scipy.stats.tukey_hsd | pass < 2×10⁻³ (numerical integration) |
| Studentized range distribution | scipy.stats.studentized_range | pass < 10⁻³ |
| Pearson and Spearman correlation | pearsonr, spearmanr | pass < 10⁻⁷ |
| Multiple linear regression | numpy.linalg.lstsq | pass < 10⁻⁸ |
| PCA eigenvalues | numpy.linalg.eigvalsh | pass < 10⁻⁸ |
| Arrhenius, Langmuir, KWW, Carreau fits | scipy.optimize.curve_fit | pass parameters < 10⁻⁵, SE < 2×10⁻³ |
| User-defined equation parser | curve_fit on the same expression | pass < 10⁻⁵ |
| Multi-peak fit (simulated XRD) | known peak positions and widths | pass centres < 0.2%, widths < 5% |
| d-spacing and Scherrer size | Bragg and Scherrer equations | pass < 10⁻⁹ |
| Savitzky–Golay smoothing | scipy.signal.savgol_filter | pass < 10⁻⁹ |
| Gaussian-process mean, SD and leave-one-out | closed form in NumPy | pass < 10⁻⁵ |
| Tauc band gap (simulated, 3.20 eV) | known value | pass within 0.05 eV |
| Tensile modulus (simulated, 70 GPa) | known value | pass within 2% |
Run them yourself: node tests/run-studio.js out.json && python tests/check_studio.py out.json.
Technical note: nonlinear fitting on the NIST reference problems
Agreeing with SciPy on well-behaved data shows that two implementations match; it does not show that either is right on hard problems. For that, the US National Institute of Standards and Technology publishes the Statistical Reference Datasets (StRD): nonlinear regression problems with certified parameter values computed to 11 significant digits, each with two prescribed starting points, the first further from the solution. Lattent's fitting engine (the code behind every Studio fit and user-defined equation) and scipy.optimize.curve_fit were run on all 26 single-predictor problems from both starting points: 52 fits.
curve_fit, default settings| NIST difficulty | Fits | Fits with ≥ 4 digits | Median digits | ||
|---|---|---|---|---|---|
| Lattent | SciPy | Lattent | SciPy | ||
| Lower | 16 | 16 | 16 | 10.1 | 7.8 |
| Average | 20 | 20 | 17 | 9.0 | 6.4 |
| Higher | 16 | 15 | 12 | 7.7 | 5.1 |
| Standard errors (all fits) | 52 | 47 | 43 | 8.4 | 5.1 |
Reading the numbers. Accuracy is the log relative error, −log₁₀(|estimate − certified| / |certified|), roughly the number of correct significant digits, taken for the worst parameter of each fit and capped at 11. Four digits is the usual pass mark; anything above 6 is more precision than any laboratory measurement supports.
- Where Lattent does better. It converges from the harder first start on MGH17, BoxBOD and Eckerle4, and to five digits on ENSO, where SciPy's default tolerances stop at three. Two design choices account for this. First, Levenberg–Marquardt with Moré's diagonal scaling and “delayed gratification” damping (Transtrum and Sethna, 2012). Second, a few short scouting runs from perturbed starting points, the best of which is refined to convergence. Its convergence tolerance is also tighter than SciPy's default, which explains the extra digits on easy problems.
- Where it does worse. MGH10 from the first start (b₂ = 400,000 against a solution near 6,181) does not converge; SciPy solves it. The failure is not silent: Lattent flags all three parameters as poorly determined and the residuals as systematic (runs test p = 0.003), and the second start succeeds.
- Speed. SciPy runs compiled MINPACK Fortran and, with its default tolerances, stops earlier: it is about 20 times faster at the median and up to several hundred times faster on the largest problems. Lattent evaluates the equation in JavaScript with numerical derivatives and runs the extra scouting starts: 39 of 52 fits take under 0.1 s, and the slowest (Gauss3, 250 points and 8 parameters) takes 0.77 s. On a laptop the difference is not noticeable for interactive work.
- Not compared here. SciPy run with tighter tolerances or several starts would gain digits too; this compares each tool as a user meets it. Rietveld refinement programs (GSAS-II, FullProf) solve a different problem, refining a full crystal structure against a whole pattern, and are not benchmarked: Lattent's XRD tools fit peaks and match phases, they do not do Rietveld refinement.
All 52 fits
| Problem | Difficulty | Start | Lattent digits | SciPy digits | Lattent ms | SciPy ms |
|---|---|---|---|---|---|---|
| Misra1a | Lower | 1 | 11.0 | 8.6 | 3.3 | 0.3 |
| Misra1a | Lower | 2 | 10.9 | 8.4 | 2.4 | 0.2 |
| Chwirut2 | Lower | 1 | 8.6 | 6.2 | 6.1 | 0.2 |
| Chwirut2 | Lower | 2 | 8.5 | 5.9 | 5.5 | 0.2 |
| Chwirut1 | Lower | 1 | 7.8 | 5.8 | 22 | 0.3 |
| Chwirut1 | Lower | 2 | 8.6 | 5.9 | 16 | 0.2 |
| Lanczos3 | Lower | 1 | 6.8 | 4.6 | 54 | 5.1 |
| Lanczos3 | Lower | 2 | 7.2 | 4.8 | 51 | 0.6 |
| Gauss1 | Lower | 1 | 10.6 | 6.9 | 720 | 0.8 |
| Gauss1 | Lower | 2 | 10.6 | 6.8 | 730 | 0.8 |
| Gauss2 | Lower | 1 | 9.9 | 8.5 | 510 | 1.0 |
| Gauss2 | Lower | 2 | 9.9 | 8.1 | 467 | 0.9 |
| DanWood | Lower | 1 | 11.0 | 7.9 | 1.4 | 0.1 |
| DanWood | Lower | 2 | 11.0 | 8.7 | 1.4 | 0.1 |
| Misra1b | Lower | 1 | 10.2 | 7.8 | 3.7 | 0.5 |
| Misra1b | Lower | 2 | 10.2 | 7.9 | 2.6 | 0.2 |
| Kirby2 | Average | 1 | 8.2 | 6.4 | 34 | 0.5 |
| Kirby2 | Average | 2 | 8.3 | 5.8 | 32 | 0.4 |
| Hahn1 | Average | 1 | 5.1 | 6.4 | 220 | 2.0 |
| Hahn1 | Average | 2 | 5.0 | 5.1 | 226 | 1.9 |
| MGH17 | Average | 1 | 9.0 | failed | 130 | 0.2 |
| MGH17 | Average | 2 | 9.5 | 6.4 | 10 | 0.8 |
| Lanczos1 | Average | 1 | 10.6 | 10.6 | 53 | 4.7 |
| Lanczos1 | Average | 2 | 10.6 | 10.6 | 50 | 0.6 |
| Lanczos2 | Average | 1 | 6.8 | 6.6 | 52 | 5.1 |
| Lanczos2 | Average | 2 | 7.7 | 6.4 | 52 | 0.6 |
| Gauss3 | Average | 1 | 10.0 | 9.2 | 765 | 1.1 |
| Gauss3 | Average | 2 | 10.1 | 7.7 | 695 | 1.2 |
| Misra1c | Average | 1 | 10.9 | 8.2 | 2.6 | 0.2 |
| Misra1c | Average | 2 | 10.8 | 7.9 | 2.4 | 0.1 |
| Misra1d | Average | 1 | 11.0 | 8.7 | 2.8 | 0.2 |
| Misra1d | Average | 2 | 11.0 | 8.4 | 2.3 | 0.1 |
| Roszman1 | Average | 1 | 8.3 | 5.5 | 3.5 | 0.2 |
| Roszman1 | Average | 2 | 8.3 | 5.6 | 3.4 | 0.2 |
| ENSO | Average | 1 | 5.5 | 3.2 | 436 | 6.3 |
| ENSO | Average | 2 | 5.5 | 3.1 | 593 | 5.5 |
| MGH09 | Higher | 1 | 6.3 | 4.0 | 17 | 12 |
| MGH09 | Higher | 2 | 6.4 | 4.0 | 7.0 | 0.6 |
| Thurber | Higher | 1 | 7.0 | 4.5 | 65 | 3.3 |
| Thurber | Higher | 2 | 6.9 | 4.5 | 78 | 2.1 |
| BoxBOD | Higher | 1 | 7.7 | failed | 3.5 | 0.1 |
| BoxBOD | Higher | 2 | 7.8 | 5.1 | 1.5 | 0.2 |
| Rat42 | Higher | 1 | 9.9 | 6.8 | 2.0 | 0.2 |
| Rat42 | Higher | 2 | 9.7 | 7.1 | 1.8 | 0.2 |
| MGH10 | Higher | 1 | failed | 6.7 | 60 | 4.6 |
| MGH10 | Higher | 2 | 8.8 | 6.8 | 13 | 2.2 |
| Eckerle4 | Higher | 1 | 9.6 | 7.2 | 7.2 | 0.4 |
| Eckerle4 | Higher | 2 | 9.6 | 7.2 | 3.5 | 0.2 |
| Rat43 | Higher | 1 | 7.8 | 5.1 | 6.0 | 0.7 |
| Rat43 | Higher | 2 | 7.4 | 4.9 | 3.8 | 0.3 |
| Bennett5 | Higher | 1 | 7.3 | 5.4 | 168 | 22 |
| Bennett5 | Higher | 2 | 7.5 | 4.0 | 198 | 5.9 |
Reproduce it: node tests/bench/nist.js and python tests/bench/nist_scipy.py; the data and certified values are in tests/bench/nist.json, converted from NIST's files by tests/bench/extract_nist.py, and the raw results above are in tests/bench/results.json. Continuous integration fails if fewer than 50 of the 52 fits reach four digits. Nelson, the one StRD problem with two predictors, is omitted.
Lattent's specialist models
Facet's built-in suggestions and the shape-guided auto-fit use two small neural networks trained by Lattent, not by a third party. Both run entirely in your browser.
- Shape model. It recognises 13 families of behaviour from XY data. Training used 67,600 simulated datasets with realistic ranges, spacings, replicates, outliers and noise. On 7,800 held-out datasets it is 98.4% correct first time and 99.8% within its top two. It also classifies all 25 realistic lab examples in the test suite correctly, such as a five-point Arrhenius set, XPS with a step background and a dilution-series calibration line.
- Scatter model. It judges whether measurement error grows with the signal, so that 1/Y² weighting is advised. Held-out accuracy is 72% (AUC 0.82). Without replicates this is a hard problem, so Lattent acts on it only when the model is at least 80% confident and the data span a wide range.
| Shape family | Held-out accuracy |
|---|---|
| Straight line | 99.3% |
| Polynomial curvature | 95.5% |
| Decay | 99.3% |
| Rise to a plateau | 97.0% |
| Sigmoidal | 97.2% |
| Exponential growth | 98.8% |
| Power law | 96.5% |
| Logarithmic | 99.0% |
| Thermally activated | 100.0% |
| Inverse (1/X) | 98.0% |
| Spectrum with peaks | 98.7% |
| Absorption edge | 99.8% |
| Stress–strain | 100.0% |
The models suggest; they never compute results. Every number still comes from the validated fitting engine above. The browser inference is checked against scikit-learn on every release (largest difference below 10⁻⁵). Retrain with node tests/ml/simulate.js and python tests/ml/train.py.
Equation discovery, AutoML and uncertainty
These checks use synthetic data with known answers, so success is unambiguous (tests/ml/check_learn.js).
| Check | Data | Result |
|---|---|---|
| Equation discovery: six known laws | quadratic, exponential decay, power law (5% relative noise), Arrhenius, Langmuir and Hall–Petch, each with noise | All six recovered with the right form; for example exponent 1.501 (true 1.5), 4029 K (true 4000), K = 3.24 (true 3.33) |
| Equation discovery on an example | glass transition against molar mass | Rediscovers Flory–Fox: Tg = 100.6 − 1.11×10⁵/Mn (true 100.5 − 1.1×10⁵/Mn) |
| AutoML: nonlinear benchmark | Friedman #1, 160 samples, 5 informative and 5 pure-noise inputs | Gaussian process selected, cross-validated R² 0.93; the five informative inputs all rank above the five noise inputs |
| AutoML: linear problem | 40 samples, y = 2x₁ − x₂ + noise | A linear model selected, cross-validated R² 0.99 |
| Conformal prediction intervals | 90% intervals from AutoML, tested on 400 new samples | 89.3% coverage |
| Property-network intervals | 9,511 compositions never seen in training | 90% half-widths: 0.17 eV/atom (formation energy); 0.41, 1.15 and 1.29 eV (band gap, for predicted gaps below 0.3, 0.3–2 and above 2 eV) |
- Equation discovery. Sparse symbolic regression: up to three terms from a library of powers, reciprocals, square roots, logarithms, and exponential and saturation terms on grids of scale parameters, combined linearly for y and (power-type terms only) for ln(y). Candidates are ranked by AICc, on relative errors when y spans more than a factor of 20. The best structures are refined by Levenberg–Marquardt. A recognised law is preferred unless a generic form beats it by more than 14 AICc units, a margin that corrects for the thousands of generic forms searched.
- AutoML. Linear and ridge regression (penalty by exact leave-one-out), distance-weighted k-nearest neighbours, a 150-tree random forest, gradient-boosted trees and a Gaussian process with one length-scale per input. Leave-one-out cross-validation for up to 20 samples, five-fold above; constant and duplicate inputs are removed first. Importance is the rise in held-out error when an input is permuted. Intervals are split-conformal from held-out residuals.
Discovery and generative models
Discover combines a live database, a powder-diffraction simulator and three neural networks that Lattent trained itself on Materials Project data (via Matbench, CC BY 4.0). The networks run in your browser, and the browser code reproduces PyTorch to within 2×10⁻⁶ (float32 property networks) and 1.2×10⁻³ (float16 generator).
| Check | Data | Result |
|---|---|---|
| Formation energy of the most stable polymorph, from composition | 9,511 test compositions never seen in training (85,607 for training) | MAE 0.092 eV/atom, R² 0.945 (predicting the median gives 0.98 eV/atom) |
| DFT (PBE) band gap, from composition | 10,573 test entries, split by composition (95,540 for training) | MAE 0.32 eV, R² 0.84 (median predictor 1.19 eV) |
| Generator: unconditional samples | 2,526 formulas | 97% distinct, 71% absent from the training set; ternaries are over-represented (75% against 52% in the data) |
| Generator: kesterite family, Cu–Zn–Sn–S | 800 samples | Cu₂ZnSnS₄ is the third most frequent formula (15% of samples) and in the top ten after screening |
| Generator: olivine cathode, Li–Fe–P–O | 300 samples | LiFePO₄ is proposed |
| Generator: band-gap guidance | 400 samples per target | mean predicted gap of the top candidates 0.93 eV for a 0.5 eV target, 3.73 eV for 3.5 eV |
| Powder XRD, fcc Au (a = 4.078 Å, Cu Kα₁) | reference pattern of gold | 38.19°, 44.39°, 64.59°, 77.58°, 81.74° with the fcc extinctions; I(200)/I(111) = 48% |
| Symmetrisation of distorted DFT cells | strained one-atom fcc cell; distorted hcp cell | 24 split reflections become the 5 fcc reflections; hcp cell restored to a = b, γ = 120° at the same volume |
| Phase identification, gold nanoparticles | 5 fitted peaks against all Au structures in Alexandria (PBE) | fcc Au, Fm3̅m, score 0.98 (next phase 0.71); lattice 2.2% below PBE, as expected |
| Phase identification, ZnO | 8 experimental peaks against all Zn–O structures | wurtzite, P6₃mc, ranked first (score 0.91) |
- Database. Searches query the Alexandria database (Schmidt, Marques and co-workers; 5.8 million PBE and 0.4 million PBEsol structures, CC BY 4.0) directly from your browser through the OPTIMADE API. Formation energies, hull distances and band gaps are Alexandria’s DFT values.
- XRD simulation. Reflections are generated in the conventional cell after the stored cell is symmetrised to its reported crystal system. Intensities use |F|² with approximate atomic scattering factors, the Lorentz–polarisation factor and an isotropic Debye–Waller factor (B = 0.5 Ų), so positions are exact for the DFT cell and intensities are a guide.
- Phase matching. For each candidate a uniform lattice scale between −4% and +4% is refined, because DFT lattices differ from experiment by 1–2%. The score is √(reference intensity covered × measured intensity explained).
- Generator. Masked discrete diffusion over five (element, count) slots with confidence-ordered unmasking in ten steps and classifier-free guidance (w = 1.5) on band gap and number of elements; the chemical system is enforced exactly. Candidates must be charge-balanced with common oxidation states and have a negative predicted formation energy.
- Limits. Generation is at the level of composition, not crystal structure, and a negative formation energy does not establish stability against competing phases. PBE band gaps are typically 30–50% below experiment. Treat every candidate as a lead for a calculation or a synthesis.
Run the checks yourself: node tests/gen/check.js tests/gen/props_parity.json and node tests/gen/check_xtal.js (the second queries Alexandria; add --offline to skip it). Retrain with tests/gen/train_props.py and tests/gen/train_mdiff.py.
What is computed and how
- Fitting: Levenberg–Marquardt nonlinear least squares with central-difference Jacobians and several starting points. Standard errors come from s²(JᵀWJ)⁻¹, as in SciPy and GraphPad Prism.
- Confidence intervals: t-distributed with n − k degrees of freedom. IC50 and EC50 intervals are computed on log concentration; derived quantities (ECx, interpolated concentrations, kcat) use the delta method.
- Model comparison: extra-sum-of-squares F test for nested dose–response models; corrected Akaike criterion and Akaike weights for kinetic models.
- qPCR: efficiency-corrected relative quantities normalised to the geometric mean of the reference genes (Hellemans et al., Genome Biology 2007), statistics on log₂ scale, Holm correction.
Limits
Lattent is research software. It is not validated for GLP, 21 CFR Part 11 or clinical diagnostic use. Asymptotic confidence intervals assume roughly normal residuals and a well-determined fit; Lattent warns when a parameter is poorly constrained, but judgement is still yours.