Chemistry inputs: is the file sound, and what will measuring it cost

Two commands, both of which run in seconds on a laptop and neither of which needs hardware.

qbc chem-audit reads a qubit Hamiltonian and refuses it when its own declared metadata contradicts itself. qbc measure-plan counts what measuring that operator costs.

Neither computes a chemistry result. They tell you whether the input is well formed and what the run costs. Whether the result is worth believing is a different question and this package does not answer it.

qbc chem-audit

The file below ships with the repository, so this output is reproducible:

$ qbc chem-audit tests/fixtures/hamiltonians/h2_2e2o.json
ACCEPT: tests/fixtures/hamiltonians/h2_2e2o.json
  [        pass] reference method is correlated
                 reference_energy_type = 'FCI_sparse', names FCI
  [        pass] exact energy sits below RHF
                 exact -1.136654584383908 vs RHF -1.1174874250696742, correlation -0.019167 Ha
  [        pass] provenance chain reaches a correlated solve
                 provenance 'RDKit -> PySCF -> OpenFermion -> JW -> sparse_FCI' names FCI and the JW mapping
  [        pass] not flagged synthetic
                 metadata.synthetic = False
  [        pass] qubit count matches the active space
                 n_qubits 4 = 2 x 2 orbitals

The five checks

check

fails when

reference method is correlated

reference_energy_type names no post Hartree Fock method. A mean field reference measures the basis set, not the algorithm

exact energy sits below RHF

the reference energy is not below the mean field one. Equality means no correlation energy is present, which is what a placeholder record looks like

provenance chain reaches a correlated solve

the declared chain names no correlated solve, or no qubit mapping. Without a mapping stage, a fermionic operator never became a qubit operator

not flagged synthetic

the file flags itself synthetic

qubit count matches the active space

n_qubits is not 2 x n_orbitals under a mapping that costs one qubit per spin orbital, or a term reaches past the declared width

The fifth one is the reason the tool exists. A generator wrote n_qubits = n_active_orbitals instead of 2 * n_active_orbitals, so a file describing an 8 electron, 8 orbital active space carried an 8 qubit operator where the space needs 16. Nothing downstream noticed, because nothing downstream checks, and the filename said the file was real the whole time.

Three verdicts, not two

verdict

exit

means

ACCEPT

0

every check passed

INCOMPLETE

1

nothing failed, but the file did not declare enough to check

REFUSE

2

at least one check failed

INCOMPLETE is a real answer. A file from somebody else’s pipeline usually declares less than ours does. Calling that a failure would make this a tool for our own files; calling it a pass would be an assurance nobody measured. --strict turns undeclared into a refusal, which is what CI should use.

File formats it reads

Deliberately generic, because a validator that only reads one project’s layout is that project’s internal script.

  • grouped JSON, the layout used by the QubitBoost chemistry corpus (pauli_groups)

  • a flat term list: {"n_qubits": 4, "terms": [{"pauli": "Z0 Z1", "coefficient": 0.17}]}, also accepting [["Z0 Z1", 0.17]] and {"Z0 Z1": 0.17}

  • OpenFermion QubitOperator, or any record whose terms are keyed by ((index, letter), ...)

  • Qiskit SparsePauliOp, with a metadata block passed alongside

Pauli strings are read sparse ("Z0 X3") or dense ("ZIIX", left to right from qubit 0).

from qb_compiler.chem import audit_hamiltonian, load_hamiltonian

verdict = audit_hamiltonian(load_hamiltonian("your_hamiltonian.json"), strict=True)
print(verdict.verdict, verdict.failures())

qbc measure-plan

$ qbc measure-plan beh2_6e6o.json --shots-per-setting 4096
Measurement plan: beh2_6e6o.json
  qubits              : 12
  measurable terms    : 366 of 367
  QWC settings        : 99 (grouping factor 3.70x)
  largest group       : 78 terms
  shots per setting   : 4096
  shots, grouped      : 405,504
  shots, term by term : 1,499,136 (1,093,632 more than grouped)
  identity offset     : -9.534814

The identity term carries the nuclear and frozen core energy. It is a constant, so it is excluded from the bill rather than paid for.

What the number is, and what it is not

It is a structural count: how many measurement settings the operator’s own commutation structure implies, and what that costs at your chosen rate. It carries no variance weighting and no precision claim. Two operators with identical setting counts can need very different budgets to reach the same error bar, because that depends on coefficients and on state dependent variance, neither of which is visible here.

The grouping is textbook greedy qubit-wise commuting. Better groupers exist and finding the minimum is NP-hard, so a good grouper returns fewer settings than this. That makes the number an upper bound on what a good grouper needs, which is a fair basis for a budget and a poor basis for a claim about grouping quality. This package does not compete on grouping quality.

What the spread looks like on a real corpus

Measured across the 14 file corpus this was built against: at 12 qubits the setting count spans 1.79 times between molecules (99 to 177, three files), and at 16 qubits 1.59 times (1196 to 1896, nine files). Qubit count does not predict where a molecule lands in that range, so two jobs of the same width on your desk can differ by nearly a factor of two in what they cost to measure.

from qb_compiler.chem import load_hamiltonian, measurement_plan

plan = measurement_plan(load_hamiltonian("beh2_6e6o.json"), shots_per_setting=4096)
print(plan.n_settings_qwc, plan.shots_qwc)

Both in CI

qbc chem-audit hamiltonians/*.json --strict

or use the action described in github-action.md, which runs the audit, prints the bill, and fails the build on a malformed input.