Skip to content

Code tour

This page shows where each part of NWQLib lives in the source and in what order to read it. Follow one scientific Problem through its configured Method, the Plan it makes, the run that collects data and the Result. Inputs and initialization are defined in separate places, so two methods may target the same Eigenproblem with different trial states.

Before reading code, set up a development environment with Set up, test and build.

The Glossary defines the words that docstrings and these pages use in a narrow sense, such as Plan, Run, Program and preparation record. The terms used only by QHD and its augmented-Lagrangian and box-refinement layers are in the QHD guide.

Conventions gives the sign, phase, qubit-order, unit, normalization and error-quantity conventions shared across families, with the module that states each one.

Find the source and reason for a line of code

A developer reading an unfamiliar line usually needs to know which paper and equation it implements, which assumption makes that equation applicable, and why the code differs from the paper where it does. Look in these places, in this order.

  1. The docstring and nearby comments. A scientific module names the paper, its arXiv version and the equation, theorem or algorithm that each function implements, together with the premises the result needs and the units, normalization and shape of the output. A comment next to a rearranged formula gives the step that connects it to the paper. Where NWQLib departs from the paper, the comment states the departure and its reason. Where the paper itself contains an error, the comment gives the correction and the independent check behind it.
  2. The family guide's source map. Each guide under docs/algorithms/ has a section that lists the implemented steps with their source equations and the functions that own them. Use it to go from an equation to its code. References links every source map, including those of the subroutine pages.
  3. The bibliography. References gives each citation with the paper version whose numbering the docstrings and source maps use. Equation numbers can change between arXiv versions, and occasionally between the PDF and HTML renderings of one version. Where the renderings are known to differ, the citation gives both numbers. Every page, docstring or comment that names a paper gives that paper's identifier in the same file, in the form Ding–Lin arXiv:2211.11973v2, or with a DOI or ISBN when the work has no arXiv version. References is the library's citation catalogue. The mathematics page also ends with the paper versions that its equation and theorem locators use, and References lists each of those papers too.
  4. The constants registry. The contribution policy requires every hardcoded tolerance, cap and default to have an entry in Engineering constants with its value, file, reason and the condition for revisiting it. Search that page for the constant's name or value.
  5. The design rationale. Records, the Run lifecycle, archives and backend adapters follow rules that no paper supplies. Design rationale names the failure each rule prevents, the invariant it keeps and a test that fails when the rule breaks.

To learn one family before changing it, read its guide from the problem definition to the source map, then the cited paper sections, then the owners listed under Follow a family in the order given there. Walk one expectation through the code shows the Plan, Run and Result lifecycle that every family shares. Maintenance lists the checks each kind of change needs.

Reading order

  1. src/nwqlib/scientist.py owns public plan/compare/estimate/prepare/submit/solve and saved-result entry points. search.py ranks a supplied finite set and can rescore its existing Comparison without replanning.
  2. problems/records.py, problems/inputs.py and operators/inputs.py own mathematical inputs, output meaning and admitted physical data. Inputs keep coordinate, phase and normalization information, and each Method configures its own initialization.
  3. core/planning.py defines the shared Plan with its original Problem, Method, output, RNG snapshot, selected construction and reconstruction data. core/analysis.py defines Result and its attached immutable RunData. Reanalysis produces a new Result from that data.
  4. algorithms/protocol.py defines Method planning, execution, analysis and optional verification/archive hooks. algorithms/registry.py resolves explicitly selected trusted factories, and listing the inventory does not load external implementations. Configured Method types supply their schemas.
  5. ir/ and blocks/ carry the actual Program definitions, ordered calls, arguments, ports and selected native bindings. ir/validation.py admits a Program before any consumer reads it, and the docstring of its admission class gives the qubit state, coherence-epoch and correlation model. blocks/selection.py binds each selected definition to trusted native code by exact identity, and blocks/lowering.py consumes that same body. resources/fold.py consumes its applicable analytical laws without building circuits, and an unknown cost stays unknown.
  6. execution.py declares the observation, preparation-record, submission and limit records. _prepared_execution.py owns Run, preparation, submission, collection, shared native caches and cumulative exposure. Backend connections in backends/ consume the selected readout and return the actual native configuration. _run_journal.py stores durable progress.
  7. backends/assessment.py, profiles.py and telemetry.py keep original profile predictions, allocation and measured execution populations associated. Explicit inspection.py operates on actual native circuits, and formula estimates are not optimized circuit inventories.
  8. evidence/ owns scoped error facts, statistical models, criteria and explicit verification relations. Result.assess does not turn a component bound into a total error certificate. Missing evidence remains unknown.
  9. artifacts.py owns requested immutable arrays. saved_evidence.py saves/restores actual Results and offers a metadata-only report reader. _choice_archive.py restores selected input data through known or explicitly supplied Method code, and _run_archive.py restores execution frontiers. Loading does not replan, normalize arrays or perform scientific analysis.
  10. cli.py provides algorithms/card/options/check-method/report. algorithms/authoring.py checks an explicit Method case with its author's independent oracle and wrong-pair witness. tests/_hadamard_method.py, the test suite's reference external Method, implements a real Hadamard expectation using shared selected subroutines and Run.

The root and owning package exports are lazy. Common scientific coordination dispatches Method behavior and does not reproduce each family's constructor or controller. Saved Source descriptions are inert. Builtin archive loading uses the known implementation inventory, while external Method loading requires an explicit implementation.

Walk one expectation through the code

Use the Hadamard Method, whose complete implementation is tests/_hadamard_method.py. For the state supplied as (1, i) and observable Y, the expected normalized expectation is one. The Method adds one ancilla and uses the existing selected blocks. It does not build a second execution framework.

  1. Admit the input and select the meaning. scientist.plan chooses the default NormalizedExpectation output and calls HadamardPauliExpectation.plan. That method checks one nonzero real Pauli coefficient and matching state dimension before selecting PREP and controlled signed-Pauli blocks. Plan._bind attaches those actual native bindings once. The reconstruction keeps the observable scale alpha=abs(c) and the ordered ancilla Pauli label, and declarations alone cannot recreate the bindings. See Compose blocks.
  2. Read costs or prepare the same construction. scientist.estimate delegates to the shared resource fold, which reads the selected Program and laws without building a circuit. scientist.prepare instead creates a Run and invokes the Method's inherited prepare_static. blocks/lowering.py::_lower_qiskit consumes the original calls, arguments and port order, and AerBackend.prepare lowers that logical circuit and inserts the requested readout. PreparedArtifact records this actual preparation, not a second reference build. See Run on a backend.
  3. Acquire through the Run. scientist.submit returns the Run after starting its selected work. The inherited execute_static submits the prepared item and collects a completed observation before analysis. _prepared_execution.py owns reservations and the association of each observation with its preparation record, and the backend translates the actual native result. Exact readout obtains the ancilla Pauli mean, while positive shots select the original ancilla count population. Run.wait returns the scientific Result once available. See Execution and storage.
  4. Reconstruct and validate the quantity. HadamardPauliExpectation.analyze calls _ancilla_mean on those observations, then computes value = rec.alpha * mean. For A=cP, the controlled action already includes sign(c), so this multiplies the magnitude exactly once. HadamardExpectationResult.validate_plan checks the selected scientific relation, and validate_data joins the mean and result to the actual chunk. Result._attach checks the original Plan and observation identities before attaching RunData. Neither analysis nor its summary reacquires the state.
  5. Save, reload and reuse. saved_evidence.save_result invokes the Method's save_archive hook and stores its Result/RunData. load_result(path, method=HadamardPauliExpectation) requires that explicit trusted implementation. Its load_archive restores the selected inputs and known block bindings, and the common loader checks the original Plan identity. Result.analyze() then interprets the same data again. read_report follows the separate metadata-only path. A persistent Run additionally uses _run_archive.py and _run_journal.py to restore its execution frontier, and a standalone Result is not that frontier. See Save, load and reanalyze results and Continue an interrupted run.

When changing this Method, place the change at the corresponding owner: input/selection in plan, native action in the selected block, data interpretation in analyze, and saved selected data in its archive hooks. Adaptive methods such as ADAPT replace the static execution hook with their controller in src/nwqlib/algorithms/gcim/adapt_acquisition.py, and they still use the same preparation, submission and observation owners. The author check supplies an independent expected value and a wrong-pair falsifier, but it does not certify every input supported by a Method.

Follow a family

Each algorithm guide has a source map from implemented steps to their paper equations and code owners. References lists those maps.

Family Actual owners and path
Expectation algorithms/expectation.py owns finite Pauli selection, grouped exact readout, parity/count inference and optional readout calibration. evidence/binary.py and statistics.py preserve the selected statistical premises.
Lanczos src/nwqlib/algorithms/lanczos/method.py selects the signed Chebyshev walk and its readout table and analyzes moments, and its sibling readout.py reads signed outcomes from the histograms of the coherent SELECT-basis readout. Its numerical.py builds the raw Chebyshev Gram matrix and pencil from moments and owns the allocation and cutoff rules. src/nwqlib/_projected_eigensolver.py thresholds and solves the pencil, and workflow.py advances explicit sensitivity sampling through the common Run.
GCIM / ADAPT Read src/nwqlib/algorithms/gcim/pencil.py and src/nwqlib/_projected_eigensolver.py for the H/S pencil and its solve, then fixed_basis.py, which selects normalized trial columns and interprets transition measurements. For ADAPT-GCiM continue with adapt_inputs.py (pool and commutators), adapt.py (one query space for every adaptive query), adapt_acquisition.py (basis chains, native composition and the resumable controller drive_adapt), then optimization.py and adapt_verification.py. src/nwqlib/subroutines/fermionic_pool.py and fermionic_circuits.py define the generators and their exact circuits. The Table V test in tests/test_fermionic_pool.py compares every operator of the default spin-adapted pool with Table V of Zheng et al., arXiv:2312.07691v3, including signs and repeated indices.
LCHS src/nwqlib/algorithms/lchs/method.py, and its sibling selection.py and primary_records.py, connect LinearDynamics to selected reconstruction and Result. plan_lchs routes the problem, and select_dense pads A and calls time_independent_terms.py, which applies the PSD rule and asks providers.py for finite nodes and coefficients. parameters.py fixes every SELECT and PREP product once. quantum.py assembles the actual PREP/SELECT/readout Program, and native.py supplies dense, product-formula, structured and QSP leaves. host.py evaluates the selected classical model, solution_error_budget.py propagates component bounds to the physical output, and refinement/verification owners hold the explicit checks.
QPE src/nwqlib/algorithms/qpe/method.py selects the input, time scale and Hadamard-test Program, and its sibling powers.py selects each controlled power with the bounds in src/nwqlib/subroutines/trotterization/error_budget.py. numerical.py holds the four estimator kernels with their paper equations and departures. controller.py advances RWPE through the common Run, and records.py joins stored parameters to the Program. Verification is explicit and keeps its nominal spectral scope.
QLS Read src/nwqlib/subroutines/qsp/phases.py for the Wx convention, phase solving and the sampled sup-norm bound, then inverse.py and shortcut.py in the same package for the two polynomial families, and evolution.py for the projector-phase circuit. src/nwqlib/algorithms/qls/method.py, and its sibling host_planning.py and primary_records.py, select the original LinearSystem, encoding, spectrum and polynomial. quantum.py emits the named encoding/projector/phase/query/readout calls, numerical/norm-search owners supply explicitly chosen classical models, and verification.py states the explicit allowances.
QHD Start with the module docstring of src/nwqlib/algorithms/qhd/method.py, which maps the Method to Leng et al.'s Algorithm 1 (arXiv:2303.01471v1) and states each departure with its reason. Then read src/nwqlib/algorithms/qhd/schedules.py, grid.py, kinetic.py and potential.py for the discretized Hamiltonian, with objective.py preprocessing the objective for the one-hot compiler, then initial_state.py for the start vectors and their structured preparation, compiler.py for step order and the phase ledger, binary.py for the binary register, its QFT kinetic step and diagonal syntheses, and split_step.py for the classical split-step flavor. resources.py, evolution_bounds.py and circuit_errors.py hold the rotation law of the emitted circuit, the budgeted T estimate, the per-circuit error sources and their bounds. method.py, its sibling records.py and the decoding owner connect an ordered box objective to the selected finite model, its one-hot or binary circuit, the observed candidate and the most probable point. Explicit verification keeps grid and continuum/global-optimum claims distinct. The two layers that run a sequence of QHD Plans share _outer.py, which owns their inner planning streams, remaining limits, counts and point rules. constrained.py and constrained_records.py hold the augmented-Lagrangian layer for a ConstrainedOptimization, including support preprocessing, inequality representation selection, slack caps and error records, projected readout and the planning-only round-0 entry, and refinement.py and refinement_records.py hold box refinement with its search model and stall split. _durable.py holds the durable directory of both layers, with its outer record committed by write-then-rename, its controller lock, the backend of its inner Runs with a saved noise model, and the reopening of inner Runs by resume_augmented_lagrangian and resume_box_refinement.
Circuit subroutines src/nwqlib/subroutines/_multiplexors.py owns the uniformly controlled rotations, phase diagonals and dependency-projected multiplexors that the other constructions reuse, together with their CX laws. src/nwqlib/subroutines/state_preparation/direct.py builds PREP from them, src/nwqlib/subroutines/lcu/core.py composes PREP, SELECT and PREP†, and src/nwqlib/subroutines/block_encoding/core.py plans a multiplexed Pauli, banded or dense-dilation encoding and then builds exactly that plan. controlled in src/nwqlib/subroutines/qiskit_compat.py adds every control. It synthesizes a directly controlled UnitaryGate from its whole controlled matrix. Any other gate takes the route that the construction selects (dense control route). On the gate-wise route controlled replaces each dense unitary in the definition by its exact synthesis before Qiskit controls the gates, and on the whole-matrix route, which "auto" takes for one control, it synthesizes the controlled matrix of each dense unitary. src/nwqlib/subroutines/_dense_synthesis.py provides both syntheses, CX and one-qubit gates that equal the matrix to rounding.

Follow the shared machinery

The layers below have no paper source. Design rationale indexes the failure each mechanism prevents, its owner and the test that witnesses it.

  • Durable Run. The module docstring of src/nwqlib/_prepared_execution.py states the lifecycle rules and the crash windows of a static acquisition. Then read src/nwqlib/_run_journal.py for atomic transitions and the file lock, src/nwqlib/_run_archive.py for the cache write, commit and delete order, and Execution and storage. The module docstring also covers prepare(plan, settings="all"), which prepares every experiment of a static Plan before acquisition, and PreparationNotRebuilt, which a Run raises instead of building again a local preparation that holds a charge but no preparation record.
  • Backends. The module docstring of src/nwqlib/backends/connection.py states the adapter contract that submit_detached and refresh_submissions rely on. Then follow one detached adapter end to end, for example src/nwqlib/backends/slurm.py with src/nwqlib/backends/nwqsim.py, through launch, reconciliation, refresh and result decoding. Backend adapter contract lists the provider rules.
  • Error evidence. A Method's Plan carries an error model listing its required error sources, and Result.facts supplies any evaluated bound. Result.assess combines them by the triangle inequality and a union bound and passes only on witnessed proved or certified support. An explicit verify produces facts that preparation records support, bound to the options it was given. src/nwqlib/evidence/error_model.py owns these rules.

io/streaming.py writes the supported selected primitive recipes directly as bounded QASM. io/materialization.py explicitly verifies and imports that text after source-derived expansion admission. Neither operation changes the original selected experiment.

Byte and work budgets

Many numerical functions take max_bytes, and some also take max_work or max_products. Before allocating, such a function computes from the input sizes alone the bytes of the arrays it will hold at the same time (its peak live arrays) and the number of scalar operations in its dominant steps. It compares these numbers with the limits and raises ValueError, naming the operation, the requirement and the limit, when either is exceeded. operators/access.py holds the shared checks _check_bytes and _check_products, and many modules make the same comparison inline.

The reason is that array sizes in this library grow as 2**q in the qubit count q, as D² or D³ in a dense dimension D, or polynomially in a degree or step count. A small change to an input can turn a computation of seconds into one that exhausts memory partway through. Checking the requirement first turns that failure into an immediate error, raised before the operation allocates anything and, for checks made during planning, before any preparation or submission. Work that runs after acquisition but whose size the Plan alone fixes, such as the projected solve of Lanczos or FixedGCIM and the updates of the RWPE controller, is checked during planning, so an undersized limit fails before any acquisition. The caller can then raise the limit knowingly or choose a compact representation. The contribution policy states the general rule that admission precedes the work it governs, and Input cost controls give the size law of each input operation.

The units are the same across the library. max_bytes counts bytes. Its default 10_000_000_000 (decimal 10 GB) is defined once in _limits.py and sized so that ordinary research inputs pass, as its registry entry explains. max_work and max_products count scalar operations according to a law written next to the check, for example 8D³ for a dense SVD of dimension D or D² for a dense matrix-vector product. Their defaults, most of them 1_000_000_000, are registered in Engineering constants.

These limits cover only part of the cost. The byte count includes the arrays whose sizes the code knows before it allocates them and any other allocation that a law names. It excludes other workspace inside NumPy, SciPy, LAPACK and Qiskit, other Python object overhead and the rest of the process, so passing the check does not guarantee that the process fits in memory. For example, the energy-shift relation check charges NumPy's sort buffers, and the circulant classification of a Pauli block-encoding plan charges its Python integers, Fraction wrappers and dictionary entries, as their entries in Engineering constants state. The work count is a planning quantity, not measured CPU time. Each limit applies to one operation. A Method that repeats an operation, for example once per iteration or query, accounts for the repetition separately, and a computation derived from another object scales its limit with that object, as the resource fold's work ceiling does.

Planning combines the requirements of phases whose selected dimensions are already known before starting those phases. Successive memory phases use their largest live-byte envelope, including data held across phases. A refusal distinguishes a complete known requirement from a conservative candidate or a lower bound from interrupted counting. Later phases can need more when their populations depend on coefficients, selected ranks, spectra or observations. operators/access.py::refuse_known_need words such a refusal, and QLS and QPE call it before their first dense phase, and QHD passes the same wording through its own admission owner before it evaluates the initial state.

Comments next to the checks map each term of a formula to the arrays it counts, for example 16 * n for n complex128 entries or 8 * n for n float64 entries. When you add an array to such a function, add its bytes to the formula. When you add a function whose memory or work grows with its input, add a check before the first large allocation and register any new default.

Conventions and validation

  • Contribution policy states the rules for mathematical domains, numerical tolerances, and the scientific and resource distinctions.
  • Conventions collects the sign, phase, qubit-order, unit, normalization and error-quantity conventions shared across families, and each family's own frames and recoveries.
  • Maintenance explains the checks for each kind of change, example and notebook changes, and delivery procedures.
  • Algorithm guides map calculations to their source equations and describe current method controls and limitations. References catalogues the papers and their versions.
  • Design rationale indexes the mechanisms that have no paper source.
  • ENGINEERING_CONSTANTS.md records concrete operation bounds and their reasons.
  • ROADMAP.md describes current capabilities and separately identified open work.
  • docs/scripts/golden_snapshot.py and docs/scripts/mutation_probes.py support scoped behavior comparisons and discriminating mutations. Passing them does not replace independent scientific evidence.

Input relations

For the relations in Conventions and derivations, the named owner writes each one out in its docstring or an adjacent comment.

Relation Justification Owner
Coordinate index k = sum_q b_q 2**q, qubit 0 rightmost Qiskit little-endian order operators/inputs.py (ORDER)
Stored row (x, z) means i**popcount(x & z) X**x Z**z, so both bits set is Y NWQLib encoding operators/_pauli.py (PauliTerms)
SparsePauliOp coefficient of the canonical label is coeff * (-i)**phase Qiskit phase convention operators/inputs.py (operator_input)
Dense matrix to Pauli coefficients c_P = tr(P M)/D by 2×2 block recursion Standard identity operators/_pauli.py (pauli_coefficients)
Pauli product phase and the action of a word on a basis state Standard Pauli algebra in the stored encoding PauliTerms.product, apply_terms
Commuting test Even symplectic product PauliTerms.group
Qubit-wise-commuting test Equal single-qubit Paulis on the shared support PauliTerms.group
a_j† = (prod_{k<j} Z_k)(X_j - iY_j)/2 Jordan–Wigner transformation operators/_fermion.py (_ladder_image)
Normal ordering of dΓ(B)²/2 Standard anticommutator algebra operators/df.py (_contract)
Spin orbital (p, σ) on mode 2p+σ NWQLib convention operators/df.py (_rows)
Pauli L1 norm and spectral enclosure Triangle inequality, unit-norm Pauli words operators/refinement.py (_pauli_values)
Sparse spectral enclosure and norm bound Gershgorin circle theorem operators/refinement.py (_sparse_values)

Search ranking

Step Contract Code owner
Strict dominance, frontier and incomparable rows Read the ranking search.SearchSelection
Objective values read from stored Plans, folds and forecasts Objectives search._objective_value
Association of a selection with its Comparison, and rescoring without new work Rescore a comparison search._comparison_identity, search.SearchSelection.validate_comparison
Size controls checked before evaluation Finite search frontier envelope search.scan, search._frontier_size