qarp.engines

Execution engines. Public depth: flat. Submodules are private.

class qarp.engines.CudaqEngine(*, backend: str = 'statevector', precision: str = 'fp64', max_bond_dim: int = 0, target: str | None = None, n_shots: int | Shots = 10000, seed: int | None = None)[source]

Bases: Engine

Pure-Python engine wrapping the C++ Transpiler + qx.CudaqSimulator.

provides_amplitudes = True (base default) — noiseless simulator; the device→host statevector transfer is separately capped by _check_statevector_host.

Gradients: "default" resolves to the batched parameter shift, which stays on-device for StateVector expectation values over a QubitOperator (batch_expectation); other targets pull the host statevector per point. "adjoint" is refused — the adjoint is a CPU kernel and this engine never constructs a CPU simulator behind the caller’s back (a GPU adjoint is post-release work); use QarpEngine for it.

Parameters:
  • backend – One of statevector (single GPU, default), statevector-mgpu (multi-GPU + MPI), tensornet, tensornet-mps.

  • precisionfp64 (default) or fp32.

  • max_bond_dim – Bond-dimension cap for tensornet-mps (ignored otherwise).

  • target – Optional NVQIR backend name passed verbatim to the simulator (CUDAQ_DEFAULT_SIMULATOR), overriding backend/precision. Use "qpp" to run on CPU (e.g. for tests/CI without a GPU) or to reach a backend not covered by the backend enum.

  • n_shots – Default shot count used when a primitive has n_shots=None; qarp.EXACT makes exact readout the engine-wide default.

  • seed – Optional RNG seed (passed to the simulator). EXACT readouts involve no RNG and are seed-independent.

gradient_methods: frozenset[str] = frozenset({'default', 'finite-diff', 'parameter-shift', 'spsa'})
class qarp.engines.Engine[source]

Bases: ABC

Common surface implemented by all concrete engines.

Concrete engines own a transpiler + simulator, compile each primitive’s sub_blocks once on build(), then dispatch one or many parameter sets through run() / batch_run() keeping the per-shot loop in C++ (no Python round-trip per shot).

batch_run(primitives: list[Runnable], param_sets: Sequence[Mapping], n_shots: int | Shots | None = None, rebuild: bool = True) list[list[float | complex | dict[tuple[int, ...], float]]][source]

Sweep one set of primitives over multiple parameter dicts.

The inner simulation loop stays entirely in C++ (no Python round-trip per parameter set). Template: _batch_setup then _sweep.

Parameters:
  • primitives – Primitive instances to evaluate.

  • param_sets – One dict per evaluation point.

  • n_shots – Override engine-default shot count for this sweep.

  • rebuild – If False, reuse transpilation from a prior build() call when the primitive structure is unchanged.

Returns:

results_by_set[set_idx][prim_idx] = scalar.

build(primitives: list[Runnable], params: Mapping = {}, rebuild: bool = True) None[source]

Compile all primitives (template method).

Per primitive: prim.build()_validate_primitive → per block: flatten → substitute → _validate_flat_commands_warn_uninitialised_conditions_compile_one_post_compile_check. The reuse path (rebuild=False with compiled circuits present) re-validates — capability-relevant state may have changed — and keeps prior routing maps, so results stay in logical qubit order.

gradient_methods: frozenset[str] = frozenset({})
prepare_structured_qpe(kind: Literal['qpe', 'dosqpe'], unitary, state, n_ancilla: int, primitive: Runnable) StructuredQPEPlan | None[source]

Offer a fast-path plan for canonical / DOS phase estimation.

unitary / state are built blocks. Returns a StructuredQPEPlan when this engine can evaluate the QPE via a structured sampler (matrix exponentiation — the controlled-U ladder is never compiled), or None → the caller builds the generic circuit. Base default: no fast path.

Callers must not probe why a plan was refused — every eligibility rule lives in the engine override (see QarpEngine).

property provides_amplitudes: bool
resolve_gradient_method(prim: Runnable, method: str) str[source]

The concrete method run_gradient(method=...) would use for prim.

resource_modeler() ResourceModeler | None[source]

Modeler for qarp.resources.ResourceEstimator, or None.

Engines pricing gates beyond raw counting (e.g. a digital-Rz T-cost) override this. No in-tree engine does: the base None is the only return, and the hook is the seam a modeling engine re-attaches to.

run(params: Mapping = {}) list[float | complex | dict[tuple[int, ...], float]][source]

Simulate all built primitives and return their scalar results.

Template method: re-validates every primitive per call (noise toggles and initial_state mutations must not slip through), substitutes parameters, and defers the engine-specific simulation to _dispatch_one.

run_gradient(params: Mapping, method: str = 'default', options: Mapping | None = None) list[ndarray][source]

Per-primitive gradients — one array per built primitive.

Contract (docs/contracts/qarp_conventions.md §17):

  • result[i] has shape (len(params),) with columns in the insertion order of params — for every method.

  • dtype is float64, or complex128 when the primitive’s value is complex by target (TRANSITION_AMPLITUDE, an expectation value over a qx.Block operator, a complex HadamardTest).

  • The differentiated objective per target: EXPECTATION_VALUE and TRANSITION_AMPLITUDE — the value run() returns (Re and Im separately); a StateVector OVERLAP — |⟨bra|ket⟩|² although run() returns the amplitude (gradient_kind == "squared_overlap"); every other primitive — the value run() returns.

method: "default" (this engine’s policy — adjoint where eligible on QarpEngine, parameter shift otherwise; may change between releases), "adjoint", "parameter-shift", "finite-diff" (options: fd_eps, fd_order 1|2), "spsa" (options: spsa_c0, num_spsa, spsa_seed). An unknown name is a ValueError; a method this engine does not declare in gradient_methods is a CapabilityError.

supports_initial_state: bool = False
class qarp.engines.QarpEngine(device: Device | None = None, *, n_qubits: int | None = None, architecture: Architecture | None = None, noise_model=None, gate_set: GateSet | None = None, directedness: bool = False, n_shots: int | Shots = 10000, seed: int | None = None)[source]

Bases: Engine

Pure Python engine wrapping C++ Transpiler + QarpSimulator.

Pipeline:
build(primitives) — calls primitive.build(), flattens, then runs

the device-aware compilation pipeline (qx.compile_for_device): check_fits → rebase → route → re-rebase (whichever stages are configured on the device).

run(params) — dispatches per primitive on consumes:

AMPLITUDES → primitive.run_from_amplitudes; COUNTS → csim sampling (an ExactResult under n_shots=qarp.EXACT), reindexed back to logical-qubit order when the device routed, then primitive.run(results).

batch_run(…) — sweeps one set of primitives over multiple

parameter dicts; simulation loop stays in C++.

Parameters:
  • device – A pre-built qx.Device. Mutually exclusive with field-style kwargs.

  • n_qubits/architecture/noise_model/gate_set/directedness – Field shorthand — assembles an effective Device internally (provided in lieu of device=…).

  • n_shots – Default shot count used when a primitive has n_shots=None. qarp.EXACT makes exact readout the engine-wide default.

  • seed – Optional RNG seed (passed to QarpSimulator). EXACT readouts involve no RNG and are seed-independent / bit-reproducible.

gradient_methods: frozenset[str] = frozenset({'adjoint', 'default', 'finite-diff', 'parameter-shift', 'spsa'})
property noise_model

The device’s noise model (C++ qx.NoiseModel), or None.

qx.Device copies the model at construction, so toggling the object originally passed in has no effect afterwards — toggle this one instead: engine.noise_model.enabled = False (the disable-noise-for-gradients workflow).

prepare_structured_qpe(kind, unitary, state, n_ancilla: int, primitive: Runnable) StructuredQPEPlan | None[source]

Structured QPE / DOS-QPE fast path (matrix exponentiation in C++).

Eligibility — any miss falls back to the generic circuit (None):

  1. EXACT readout (primitive or engine-wide): the structured C++ sampler has no analytic branch.

  2. Enabled noise model: simulate_*_structured applies raw commands only — the fast path would silently drop the noise.

  3. Routed device: the fast path bypasses the routing/l2p pipeline.

  4. Parametric U after transpile: the ladder needs a concrete matrix.

  5. Seeded primitive: sample() cannot thread initial_state (the C++ signature has no such parameter) — the generic path can.

The fast path never runs build()/_validate_primitive — the primitive is consulted only for shot resolution at sample() time.

property provides_amplitudes: bool

a disabled noise model restores amplitude capability.

Type:

Dynamic

supports_initial_state: bool = True
class qarp.engines.Runnable(*args, **kwargs)[source]

Bases: Protocol

Members engines actually touch on an executable item.

target / operator are typed Any: their concrete types (Target, QubitOperator) live above this layer, so engines match target.name by string and duck-type the rest — importing them here would recreate the engines⇄algorithms cycle this protocol removes. Consumes is importable: it lives in qarp._types, below both layers.

build() Runnable[source]
compiled_circuits: list[list]
consumes: Consumes
gradient_kind: str
initial_state: Any
n_shots: int | Shots | None
operator: Any
run(results: list) Any[source]
run_from_amplitudes(compiled_circuits: list, simulator=None) Any[source]
sub_blocks: list
supported_targets: frozenset
supports_backprop_gradient: bool
supports_exact: bool
target: Any