Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Contributing and the Rust API

CONTRIBUTING.md owns branch naming, lint policy, and verification commands. See development environments for setup.

Run cargo deny check from the workspace root to check dependency advisories, licenses, duplicate versions, and dependency sources. The same check runs in a dedicated GitHub Actions job; its policy is defined in deny.toml.

The distribution script requires Python with tarfile extraction filters (Python 3.12+, or patched 3.10.12+/3.11.4+). The rust and full Nix shells provide cargo-about. Outside Nix, install cargo-about 0.9.2. After dependency or notice-generation changes, run python3 tools/distribution.py notices, then python3 tools/distribution.py check. Commit the generated notice text and its checksum with the change. See licenses and redistribution for the distribution policy and Nickel’s dependency advisories.

The workspace keeps CLI parsing and presentation in the root package and scientific configuration and calculations in rustiq-chem-core. The core’s calculation module orchestrates work in molecules, basis, eri, hf, and mp2.

Rustdoc remains the canonical public API reference. Generate and open it with MathJax support:

tools/generate-rustdoc.sh --open

On Windows:

.\tools\generate-rustdoc.ps1 -Open

The generated entry point is target/doc/rustiq_core/index.html. MathJax equations require browser access to the CDN. The Pages site publishes the manual; rustdoc is generated separately.

For book equations, use MathJax delimiters \\( ... \\) inline and \\[ ... \\] for display math. Markdown consumes one backslash, so write TeX commands with doubled backslashes as well. Avoid starting a continuation line with Markdown syntax such as +, which can split an equation into a list. Build the book and inspect the generated HTML and a browser-rendered equation, including multiline equations. MathJax requires JavaScript in the browser.

Runnable API examples and public API tests provide executable workflows and verification. Link to these sources instead of duplicating item-level documentation in the book.

Public API imports

Use rustiq_core::prelude::* for direct calculations. It includes CalculationExecution, the trait providing execute() and execute_with_events() for builders and prepared calculations. With explicit imports, bring rustiq_core::calculation::CalculationExecution into scope to call those methods. The builder supports mutable setters such as hf(...), accessors such as get_hf(), and consuming with_hf(...) variants.

Explicit imports use molecules for molecular inputs, config for options, random distributions and validated scalars, basis for basis data, and calculation for execution and results. Located<T> remains available from config and the prelude for optional frontend source positions.

Extensible option structs, including HF, DIIS, orthogonalization, molecular, integral, MP2, random-guess, and guess-perturbation configurations, are #[non_exhaustive]. Create them with Default::default() and then assign the public fields you want to customize. External Rust callers cannot use struct literals or ..Default::default() to construct these types. See the core config module’s Rustdoc for executable examples.

RandomConfig is also non-exhaustive, but has no Default implementation because no distribution is appropriate for every calculation. Start with RandomConfig::new(distribution) and assign seed when reproducible sampling is required. An absent seed continues to initialize sampling from system randomness.

New options must have defaults that preserve the previous behavior when the option is omitted. Maintain this contract in both the core’s Default implementations and the input frontend’s defaults. Non-exhaustiveness protects Rust source compatibility when fields are added; preserving default behavior also requires checking the existing calculation and input regression tests.

The core produces ScfResult, ScfIteration, SCF timings, HfCalculationResult, and Mp2MemoryPlan; these are non-exhaustive inspection records, not extension points for constructing scientific output. Public fields remain readable, while new diagnostics can be added without requiring downstream struct literals to change. Mp2Result remains constructible because the public Mp2Calculation trait lets downstream implementations return the same result type.

Enums are non-exhaustive when callers select among evolving configurations or consume results that may gain another variant, such as HfMethod, ResolvedHfMethod, density guesses, random distributions, and MP2 sectors. HfOutcome remains exhaustive because a completed HF run has exactly two convergence states; setup and calculation failures are returned as errors. RestrictedMethod and UnrestrictedMethod also remain exhaustive and name only the currently implemented method for each reference. Adding a supported method is an explicit API change, rather than an open-ended configuration choice.

For portable archives and raw AO ERIs, import RustiQData, RustiQBundle, and CompactEri explicitly from rustiq_core::persistence. Import ArtifactReuseDecision and ArtifactReuseEvent from rustiq_core::calculation; these advanced types require explicit imports. The portable examples linked above demonstrate the persistence imports, and the portable prelude test shows how to combine them with the calculation prelude.

Architecture and calculation lifecycle

The root CLI package owns terminal reports, application directories, and src/runfile/, which resolves TOML/Nickel through the embedded Nickel schema. The library owns scientific configuration and calculations without a Nickel dependency. This boundary lets a different frontend supply scientific options without embedding its input syntax or terminal behavior into numerical code.

Within the core, molecules owns geometry, units, charge and electron counts; basis constructs Gaussian functions; eri computes and stores electron repulsion integrals; hf solves RHF/UHF; and mp2 consumes HF orbitals. Rust makes ownership and mutation explicit for matrices, integral storage, and shared results. Enums model choices such as the HF method, while Cargo brings dependency locking, feature selection, and tests into one workflow. These engineering tools help make assumptions inspectable; they do not establish numerical correctness or a performance advantage over other languages.

UHF builds the alpha and beta Fock matrices in one pass over AO pairs. Each pair shares the Coulomb contribution from the total density and the integral loads, while retaining a separate exchange contribution for each spin density. The independent scalar contraction regression covers unequal spin densities, equal spin densities, and an empty beta density across different Rayon pools: cargo test -p rustiq-chem-core spin_fock_matrices_match_independent_contractions.

Typed errors distinguish invalid input from numerical failures. Optional source locations let the CLI attach diagnostics to input text without making the library own that text or its presentation.

CalculationBuilder accepts geometry, basis data, and scientific options. prepare() validates the state, converts coordinates to Bohr, constructs the basis, and resolves RHF/UHF. A prepared calculation can execute repeatedly. run_hf() distinguishes converged and unconverged outcomes; only the converged solution exposes mp2(). This type boundary prevents accidental MP2 execution on unconverged orbitals. Cloned solutions share immutable scientific data; execution errors retain completed HF outcomes when available.

The core emits CalculationCompleted once after all requested stages complete. An HF-only run emits it even when the returned HF outcome is unconverged; execution completion and convergence are separate facts. A failing requested MP2 stage leaves the earlier HF notification available but emits no calculation completion. Callbacks borrow results synchronously; retaining them requires copying. The CLI copies only numerical summaries and artifact reports for batch presentation, so collecting a campaign does not retain orbital matrices or AO integrals. Campaign execution returns one ordered CampaignResult directly, including recoverable errors. Fatal errors stop execution before presentation. The CLI then derives the campaign summary, publishes artifacts when allowed, and renders the complete text or versioned JSON document. Publication failure after successful scientific execution still leaves the completed JSON on stdout while returning a failing process status. Portable bundle reruns use the same result presentation.

The normalized CalculationRequest records scientific input without geometry paths or frontend source spans. Canonical TOML/XYZ rendering belongs to CLI adapters. Core consumers supply explicit paths to BasisStore::new; application storage environment variables are CLI policy. The core has no default features; its optional online feature enables online basis support. See the runnable examples and public API tests above for complete construction and error handling.

Data preparation, execution, and reuse

The lifecycle is:

CLI TOML/Nickel adapter or another frontend
    -> geometry + basis data + scientific options
    -> CalculationBuilder::prepare()
       molecule in Bohr, Gaussian basis, resolved HF method and random seeds
    -> PreparedCalculation::run_hf()
       one-electron integrals, overlap orthogonalization, AO ERI acquisition
       -> iterative SCF; final canonicalization if converged
       -> HfOutcome::Converged or HfOutcome::Unconverged
    -> extract the typed converged RHF/UHF solution
    -> Mp2Calculation::mp2()
       blocked AO-to-MO integral transformation and MP2 correlation-energy evaluation

HfOutcome records whether HF converged and contains an AnyHfSolution<State> whose Restricted or Unrestricted variant retains the reference-specific solution. HfSolution<R, State> carries both the reference and convergence state in its type; the RhfSolution and UhfSolution aliases name the common profiles. AnyHfSolution does not expose MP2. For HF followed by MP2, call prepared.run_hf()?.into_converged()?, match the runtime variant, then call mp2(config) on the resulting typed RHF or UHF solution. Both typed solutions implement the Mp2Calculation capability trait, exported in the prelude; unconverged and generic runtime solutions do not. The conversion returns an execution error retaining the unconverged outcome, recoverable with into_hf(). Typed/runtime conversions preserve state and return a typed HfReferenceMismatch error that retains the original solution when the requested reference does not match.

The owned-output implementation lives in crates/rustiq-core/src/calculation/solution/, with separate modules for generic results, HF adaptation, runtime dispatch, outcomes and execution errors. The calculation/solution.rs facade preserves the existing public exports through rustiq_core::calculation and the prelude. Convergence markers stay together, and private shared storage stays with the result that owns it.

HfSolution<R, State> wraps ScfSolution<HfMethodDescriptor<R>, State>. The following conversions preserve the physical method, reference, convergence state and shared scientific allocation:

SourceTargetConversion
HfSolution<R, State>ScfSolution<HfMethodDescriptor<R>, State>From / Into, or into_scf()
ScfSolution<HfMethodDescriptor<R>, State>HfSolution<R, State>From / Into, or from_scf()
&HfSolution<R, State>&ScfSolution<HfMethodDescriptor<R>, State>AsRef, or as_scf()
Restricted or unrestricted generic HF outputAnyHfSolution<State>From / Into
AnyHfSolution<State>Restricted or unrestricted generic HF outputTryFrom / TryInto

The checked runtime extraction returns HfReferenceMismatch<State> on a reference mismatch. It reports the expected and actual HF references and retains the original AnyHfSolution<State>, recoverable with into_solution(). These conversions transfer or borrow existing ownership; they do not clone scientific data or allocate another result. The named methods delegate to the standard traits. The HF wrapper keeps its explicit interface, including occupied_orbitals(); existing HF callers need no migration.

ScfSolution<M, State> owns scientific output; ScfResult contains numerical metrics. The public ScfMethod trait associates independent coefficient, energy, occupation, summary and RetainedData types. It describes output rather than an executable SCF algorithm. The implementing descriptor type identifies the physical method separately from those representations. The generic occupations() accessor borrows method-specific occupations, which need not be integer orbital counts. retained_data() borrows scientific artifacts retained after execution; the generic contract requires neither an HF summary nor AO ERIs.

Implementing this trait does not provide an SCF engine or a public solution constructor: the library establishes the method-specific convergence state. For HF, HfMethodDescriptor<R> identifies the compatible method, with an HfCalculationResult summary and retained AO ERIs. Its ScfSolution specialization exposes scf_result() to borrow metrics from the summary. Only compatible converged HF wrappers implement Mp2Calculation; generic HF output can be restored through Into or from_scf(). AnyHfSolution itself does not imply that MP2 is available for every contained or future HF reference. Supported converged HF implementations also guarantee final canonicalization; this is an HF guarantee, rather than a universal requirement on every SCF method.

The sealed HfReference contract associates a compatible HF method, coefficient representation, orbital energies, and occupations. Generic code converts the associated method into ResolvedHfMethod; conversion into the input selector HfMethod is not required by the trait. RHF has one shared coefficient matrix and energy vector with one doubly occupied spatial-orbital count. UHF has separate alpha and beta coefficients, energies, and electron counts, represented by Spin<T>. Read-only accessors expose these values; energies are in Hartree, and each occupation count selects a prefix of its coefficient columns. Cloning a solution shares retained scientific data. An unconverged solution keeps diagnostic orbitals, while final canonical orbitals are guaranteed only after convergence.

Initial guesses use the same reference markers through DensityGuess<R> and OrbitalGuess<R>. Reference-specific Fock-like inputs contain one matrix for Restricted and a Spin pair for Unrestricted, using the same HfReference component contract as HfSolution. The zero startup sentinel is available for both. Unperturbed guesses retain one core matrix for RHF or duplicate it for UHF. Random and perturbed guesses generate one matrix for RHF or two ordered alpha/beta matrices for UHF without constructing a discarded beta matrix; the seeded alpha sequence is preserved across references.

Only converged solutions expose MP2. Method dispatch is exhaustive, so each supported reference has an explicit correlation implementation. An MP2 error retains the completed HF result for inspection or retry. See the hf_solution example and API tests for executable workflows.

The complete offline example in crates/rustiq-core/examples/hf_solution.rs is also included directly in the HfOutcome API documentation. Run it from the repository root:

cargo run -p rustiq-chem-core --example hf_solution --no-default-features

It embeds the checked-in STO-3G basis and the H2 singlet and OH doublet geometries, with coordinates interpreted in Angstrom. It prints reference-specific occupied counts, total HF energies and MP2 correlation energies in Hartree. A deliberately invalid frozen-orbital count demonstrates error recovery: the example drops the original HF handle, extracts the retained output from the error using the standard conversion traits, and retries MP2 without rerunning HF. Unexpected failures propagate as errors. The example illustrates API ownership and error handling; the independent scientific comparisons remain separate verification procedures.

prepare() constructs reusable input data; it does not compute integrals or solve HF. Each normal run_hf() call builds a new HF execution state, including one-electron integrals and the orthogonalizer. Without an explicit reuse source, AO ERIs are recomputed too. Repeated execution of a prepared calculation does not itself memoize these numerical arrays. Random seeds resolved at preparation are retained for repeated executions.

An optional EriCache can load compatible AO ERIs from a directory or store newly computed ones. It is disposable local storage. A supplied CompactEri through run_hf_with_eri() bypasses AO ERI computation and cache lookup, but the raw tensor API checks its expected element count rather than proving its scientific provenance. The caller must supply ERIs for the prepared geometry, AO ordering, and screening policy. Use the persistence APIs’ compatibility checks when reusing an archive. HF solutions retain their AO ERIs, and cloned solutions share immutable result data; these retained results are distinct from the prepared input data.

Portable .rustiq bundles can be read and written through the core persistence APIs and through the CLI’s run --reuse, run --artifact, artifact inspect, and artifact verify commands. run INPUT --reuse BUNDLE resolves the supplied runfile’s geometry and basis as usual, then uses bundle compatibility to select AO ERIs. run BUNDLE.rustiq reconstructs the archived resolved calculations without the original inputs or local basis store. The Rust API can also reconstruct a calculation from those scientific snapshots. These workflows start fresh HF executions; HF solutions and SCF restart state are not persisted. Neither workflow turns a local cache directory into a portable artifact. See the portable bundle guide, the persistence contract, and the runnable API examples for details. Portable NPY ERI payloads stream directly into the final shared compact storage through an exact-size iterator, avoiding a temporary full-size Vec<f64>.

This distinction matters for performance: compact AO ERI storage still grows as the fourth power of basis dimension, and the MP2 workspace budget covers additional transformation buffers rather than the full HF data or process memory. See MP2 memory policy.

Build profiles and performance investigation

Cargo’s default development and test builds favor development speed. Release builds optimize execution using thin link-time optimization and one codegen unit, and strip symbols. The custom profiling profile retains release optimizations with debug information and symbols:

cargo build --locked --release
cargo build --locked --profile profiling

The profiling executable is under target/profiling/. For optimization specific to the build machine’s CPU, use RUSTFLAGS="-C target-cpu=native" cargo build --locked --release; that binary may require instructions absent on another CPU. In PowerShell, set $env:RUSTFLAGS = "-C target-cpu=native", build, then remove it with Remove-Item Env:RUSTFLAGS. There is no separate native Cargo profile.

Coulomb integrals cache Boys function values by order. When the requested maximum order is nonzero, positive arguments up to 20 use a positive-term series to seed the highest order, followed by downward recurrence; larger arguments retain the gamma-function seed. An order-zero cache uses the error-function expression. This changes numerical evaluation rather than the integral definition or screening policy. The unit tests in boys.rs compare the cache against independent quadrature, including the series boundary. Run cargo test -p rustiq-chem-core math_utils::boys and the affected PySCF comparisons after changing this path.

The eri_timings and mp2_timings benchmarks use Criterion and require bench:

cargo bench --bench mp2_timings --features bench
cargo bench --bench eri_timings --features bench
cargo bench --bench eri_storage_allocation --features bench
cargo bench --bench eri_storage_memory --features bench

The MP2 benchmark compares the dense AO-to-MO transformation and correlation energy evaluation with the production memory-blocked path on deterministic synthetic RHF inputs. Each blocked case is constrained to process occupied orbitals in at least two blocks, even when the requested memory budget could fit one block. RUSTIQ_MP2_MEMORY sets an upper cap; each case may use a smaller budget to force multiple blocks. AO dimensions must be at least 6. It checks that both paths produce matching energies before timing them, and records the selected block size, block count, workspace budget, and estimated temporary workspace. Input and ERI construction remain outside the measured iterations. These synthetic inputs benchmark the contraction code; they are not molecular calculations or scientific accuracy validation. RUSTIQ_MP2_MEMORY, RUSTIQ_MP2_SIZES (comma-separated AO dimensions), and RAYON_NUM_THREADS select the memory budget, AO dimensions, and thread count. The ERI benchmark times repeated integral construction for the checked-in molecular cases. By default it skips the larger ethanol and cholesterol cases; set RUSTIQ_BENCH_HEAVY=1 to include them. RUSTIQ_BENCH_FILTER selects cases by name or geometry path, and RUSTIQ_BENCH_BASIS_STORE selects a basis store. It reports compact ERI storage elements processed per second. This count is the size of the compact representation; Schwarz screening may skip evaluations or leave stored elements zero, so it is not a count of integrals evaluated per second.

The eri_storage_allocation benchmark compares Vec-then-Arc construction with the exact-size-iterator Arc initialization path for identical compact tensor sizes. It measures storage allocation and initialization only, not ERI evaluation. Compare the same Criterion benchmark and compiler settings on both revisions. The Miri workflow targets a sequential unit test for the shared in-place initialization helper, checking its initialization safety independently of Rayon scheduling. Run the same check locally with nightly Rust:

rustup +nightly component add miri rust-src
cargo +nightly miri setup
cargo +nightly miri test -p rustiq-chem-core --lib test_in_place_storage_initialization_miri

The eri_storage_memory benchmark uses a counting global allocator in a separate executable to report peak live heap bytes attributable to each construction. It includes requested allocation sizes, not allocator overhead or process RSS. It compares the same Vec-then-Arc and in-place storage paths.

The standard cases use 30 samples, a 2-second warm-up, and a 5-second measurement target. Heavy ERI cases and MP2 cases with 48 or more AOs use Criterion’s minimum of 10 samples, a 1-second warm-up, and a 2-second target. These are targets: Criterion may take longer to collect the requested samples, especially when each calculation is expensive. Heavy ERI cases remain disabled unless RUSTIQ_BENCH_HEAVY=1 is set. Measurements are saved under target/criterion.

Criterion baselines can be compared only between revisions that both use Criterion and the same benchmark workload. The multiblock MP2 workload has a new Criterion identifier, so its first run establishes a new starting point; do not compare it with the previous single-block mp2/blocked measurements. Legacy timing results also cannot be compared directly with Criterion measurements. MP2 input construction is excluded from measured iterations. Keep CPU architecture, Rayon thread count, build flags, enabled features, MP2 memory budget, power settings, and benchmark environment variables consistent. Save a baseline on the reference revision with the same workload, then compare from the revision under test:

cargo bench --bench mp2_timings --features bench -- --save-baseline before
cargo bench --bench mp2_timings --features bench -- --baseline before

See the MP2 benchmark source and ERI benchmark source. Benchmarks provide performance measurements, not scientific validation.

Generate reproducible reports

From the repository root, the scientific report reuses the canonical PySCF reference cases and their absolute tolerances:

uv run --locked python tools/reference/report.py --output-dir target/scientific-report

It writes full precision results to validation.json and readable tables to validation.md. In addition to the cases run by pytest tools/reference, it includes the frozen-core MP2 variants exercised by those tests at 32 KiB and 512 MiB workspace budgets. Total HF energies and MP2 correlation energies are reported separately. A case fails if either implementation does not converge, an energy comparison exceeds its existing tolerance, or the UHF spin expectation value misses its tolerance. Failed cases remain in the report, and the command returns a nonzero status. The report records software versions, commit and platform metadata so results can be compared across revisions.

Generate Criterion measurements and their report with:

cargo bench --features bench --bench eri_timings
cargo bench --features bench --bench mp2_timings
uv run --locked python tools/benchmarks/report.py

The report is written to target/performance-report/benchmarks.json and benchmarks.md. To produce both reports on GitHub, open Actions → Scientific and performance report → Run workflow on the selected branch or commit. The workflow uploads rustiq-scientific-performance-reports; download that artifact from the completed workflow run to inspect the Markdown and JSON files.

Criterion stores structured estimates in target/criterion; the benchmark executables also write the case dimensions and configuration used by the report generator there. The default runs include the small ERI cases and MP2 AO sizes 12, 24, and 48. To select a smaller local run or opt into the expensive ERI cases, set RUSTIQ_BENCH_FILTER, RUSTIQ_MP2_SIZES, RUSTIQ_MP2_MEMORY, and RUSTIQ_BENCH_HEAVY as described above. The report includes Criterion means and confidence intervals. Its configured MP2 workspace is not measured peak RSS, and synthetic MP2 cases do not model complete molecular calculations. Results are machine-dependent; the on-demand GitHub Actions workflow starts with one small ERI case and AO sizes 12 and 24. To run heavy ERI cases, select Include the heavy ERI cases and choose an ERI filter that matches those cases. The checkbox enables RUSTIQ_BENCH_HEAVY=1; it is disabled by default.

These reports provide numerical regression checks for selected cases. They do not establish general chemical accuracy; see the scientific scope for the limits of HF, MP2, and numerical agreement. Equal numeric convergence thresholds do not mean that RustiQ and PySCF use identical convergence tests.

Independent AO integral validation

The parameterized Cargo tests integral_reference_tests::export_integral_validation::case_N compare production overlap (S), kinetic (T), nuclear attraction (V), and Hcore = T + V matrices with versioned PySCF 2.14.0 fixtures. It compares every matrix entry and every unique unscreened electron-repulsion integral (ERI), in chemists’ notation. No Python installation is needed for Cargo tests. The cases are H2/STO-3G (2 AOs), H2O/STO-3G (7), H2O/6-31G (13), and H2O/cc-pVDZ with spherical 5D (24) and Cartesian 6D (25) functions. The Cartesian cc-pVDZ water case is also rotated by 0.37 radians around the normalized (1, 2, 3) axis with RustiQ’s Geometry::rotate; its independently generated PySCF reference uses the same Rodrigues rotation. This checks the Cartesian integrals with coordinates spanning all three Cartesian axes.

All families use an absolute tolerance of 1e-10 in each family’s units, with no relative acceptance criterion. Measured maxima on these cases are below 9e-14; the threshold leaves margin for floating-point accumulation and platform differences in contracted Hermite/Boys evaluation while remaining below the 1e-8 scale that would require investigation. Relative errors are diagnostic only and are reported for reference magnitudes at least 1e-8. Reports include dimensions, counts, RMS and maximum absolute error, exceedance counts, and the five largest discrepancies with zero-based AO indices and full precision values. For S the units are dimensionless; T, V, Hcore, and ERIs are in atomic units of energy.

The fixtures are generated independently by PySCF/libcint from the same raw checked-in Basis Set Exchange (BSE) JSON exponents and contraction coefficients, not from RustiQ’s normalized primitives. PySCF interprets and normalizes those coefficients independently. This avoids differences in rounded packaged basis sets. Geometry is converted from the checked-in Angstrom XYZ files to Bohr using 0.529177210903 Angstrom/Bohr (CODATA 2018), then supplied explicitly in Bohr. The JSON metadata records source hashes, PySCF version, AO permutation, units, layout, and each numerical array hash. Cargo rejects changed source inputs or array contents; pytest also regenerates every reference array and checks it within 1e-13 absolute units. Regeneration never reads RustiQ integral values.

RustiQ retains atom, BSE shell, and contraction order; PySCF stably sorts shells by angular momentum. Within each p shell RustiQ uses z,y,x versus PySCF x,y,z. The real spherical d order and phases agree: xy, yz, (2zz−xx−yy), xz, (xx−yy), with normalized polynomial combinations. Cartesian order is reversed: RustiQ zz,yz,yy,xz,xy,xx versus PySCF xx,xy,xz,yy,yz,zz. The mapping is constructed from these conventions, never fitted to integral values. PySCF’s Cartesian d functions have radial normalization: multiply its xx,yy,zz functions by sqrt(5/(4π)) and xy,xz,yz by sqrt(15/(4π)) to obtain unit-normalized monomials. These factors follow from angular integrals of squared monomials, 4π/5 and 4π/15, respectively. The reference generator checks the resulting unit overlap diagonals. A separate analytic coefficient test compares the spherical d polynomials with PySCF’s Cartesian-to-spherical transformation, checking phases that the planar water geometry alone cannot distinguish. This positive diagonal scaling has condition number sqrt(3); there is no fitted rotation or phase adjustment. The Cartesian extension changes only the in-memory shell representation, preserving every source exponent and coefficient and leaving fixtures and production defaults unchanged. See the authoritative PySCF basis construction source and integral interface.

Reproduce the independent comparisons and verify the fixtures:

cargo test -p rustiq-chem-core --lib integral_reference_tests
nix develop .#mini-pyscf --command pytest tools/reference
# Equivalent non-Nix workflow:
uv run --locked pytest tools/reference
# Intentionally regenerate versioned NumPy arrays and provenance:
uv run --locked python tools/reference/integrals.py --regenerate
# Write the detailed integral-only machine-readable report:
uv run --locked python tools/reference/integrals.py --report target/integrals.json

NumPy writes individual .npy fixtures; Rust reads them directly through the existing persistence readers backed by npyz. Matrices have two-dimensional shapes and Fortran (column-major) storage. ERIs use lower triangular AO pairs i≥j, followed by lower triangular pairs of pairs p≥q, with pair index i(i+1)/2+j. No full four-index tensor is allocated. The scientific report above includes an independent AO integral section and full precision JSON diagnostics. Harness failures are reported as failures, not omitted.

Three forms of evidence remain distinct: independent integral comparisons check agreement with libcint; internal eightfold compact reconstruction checks validate storage symmetry; end-to-end HF/MP2 comparisons validate selected calculations. The compact reconstruction check is algebraic and does not independently recompute eight permutations. Existing Schwarz screening tests remain separate; this accuracy test passes None to the production threshold interface and does not change default screening. Successful checks establish agreement only for these geometries, contractions, s/p functions, and spherical and Cartesian d functions. They do not establish correctness for higher angular momenta, extreme exponents, arbitrary geometries, or basis completeness.