qarp.blocks

Circuit blocks. Public depth: flat — every block is qarp.blocks.<Name>.

Submodules are private implementation and may be reorganised without notice.

class qarp.blocks.AGateBlock(theta: Symbol | float, phi: Symbol | float, target_qubits: List[int] | None = None, name: str = 'A-gate')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.AmplitudeAmplificationBlock(state_preparation: Block, oracle: Block, target_qubits: List[int] | None = None, name: str = 'AmplitudeAmplification', *, power: int = 1)[source]

Bases: CompositeBlockBase

The phase-exact amplitude-amplification iterate.

For a state-preparation unitary A and a good-state phase oracle O_good = I - 2 Pi_good, this block implements exactly

Q = A R0 A_dagger O_good,

where ReflectionBlock supplies R0 = 2|0...0><0...0| - I. Consequently, the circuit-time child order is O_good, A_dagger, R0, A.

The oracle contract is mathematical: oracle must be a unitary block with the stated phase convention. The constructor checks its type and width but deliberately does not build a dense matrix to prove its semantics — that proof is exponential in the register width, so the phase convention is the caller’s promise. In particular, a raw ReflectionBlock about the good subspace implements 2 Pi_good - I = -(I - 2 Pi_good) — the exact negative of a good-state oracle. To use one as an oracle, compose it with a gphase(pi) block to restore the sign. Getting this wrong is invisible in standalone sampling (probabilities are phase-blind) but shifts every controlled eigenphase by one half, which silently corrupts amplitude estimation built on the controlled iterate.

Caller-owned inputs are deep-copied at construction. Building this block therefore does not build, retarget, or otherwise mutate either input.

This convention is Eq. (1) of Brassard, Hoyer, Mosca, and Tapp, Quantum Amplitude Amplification and Estimation, arXiv:quant-ph/0005055. Their zero-state reflection is S0 = I - 2|0><0| and their iterate is -A S0 A^-1 S_chi. OpenQARP’s ReflectionBlock is R0 = -S0, yielding the exact form above.

power repeats the iterate: the block implements Q^power. The default power=1 is the single iterate Q; power=0 is the identity (empty circuit). This is the only knob a fixed-schedule amplitude-amplification consumer needs — Grover applies Q^k after a uniform preparation, and maximum-likelihood amplitude estimation runs several powers Q^{m_k} (including m_0 = 0) after A.

Parameters:
  • state_preparation – Unitary A preparing the initial state from |0...0>.

  • oracle – Unitary implementing exactly I - 2 Pi_good on the same register as state_preparation.

  • target_qubits – Optional placement of the complete iterate.

  • name – Block name.

  • power – Non-negative number of times to repeat the iterate Q (default 1; 0 is the identity). This option is keyword-only.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.AmplitudeEstimationBlock(state_preparation: Block, oracle: Block, n_ancilla: int, target_qubits: list[int] | None = None, name: str = 'AmplitudeEstimation')[source]

Bases: CompositeBlockBase

Canonical QAE circuit for a phase-exact amplification iterate.

For A|0> = sqrt(1-a)|psi_bad> + sqrt(a)|psi_good> and an oracle implementing exactly O_good = I - 2 Pi_good, the embedded AmplitudeAmplificationBlock has relevant eigenphases +/- 2 theta, where sin(theta)**2 = a. This block applies QPE to that iterate without adding measurements.

Qubits 0 .. n_ancilla-1 form the estimation register and the state register follows it. Caller-owned blocks are deep-copied and never built, retargeted, or otherwise mutated.

The construction follows Brassard, Hoyer, Mosca, and Tapp, Quantum Amplitude Amplification and Estimation, arXiv:quant-ph/0005055.

Parameters:
  • state_preparation – Unitary A preparing the initial state.

  • oracle – Good-state phase oracle implementing I - 2 Pi_good.

  • n_ancilla – Positive number of estimation qubits.

  • target_qubits – Optional placement of the complete QAE circuit.

  • name – Block name.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

qarp.blocks.AnyBlock

alias of Block

class qarp.blocks.BlockEncodingBlock(A: ndarray | QubitOperator | None = None, target_qubits: List[int] | None = None, name: str = 'BlockEncoding', *, coefficients: Sequence[complex] | None = None, unitaries: Sequence[Any] | None = None)[source]

Bases: CompositeBlockBase

Pattern B composite: block-encode an operator A via LCU.

Decomposes A = Σ_i c_i U_i into Pauli strings and assembles the standard Prep† · Select · Prep LCU circuit. The block-encoded operator on the |0…0⟩_anc subspace is A / λ where λ = Σ_i |c_i| (stored as self.lambda_norm).

Prep loads real amplitudes √(|c_i|/λ) on the ancilla register; each LCU phase φ_i = arg(c_i) is carried by the corresponding SelectBlock entry and lowered through the multi-controlled GPhase decomposition.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

lambda_factor() float[source]

Compatibility shim: returns self.lambda_norm.

class qarp.blocks.BrickworkEntanglingBlock(n_qubits: int, circular: bool, use_cz: bool, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.BrickworkPCEBlock(n_qubits: int, n_layers: int, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

Brickwork Pauli Correlation Encoding (PCE) ansatz block.

The ansatz used by the original PCE-algorithm paper (qarp.algorithms.PCE): Sciorilli, Borges, Patti, García-Martín, Camilo, Anandkumar & Aolita, “Towards large-scale quantum optimization solvers with few qubits”, Nat. Commun. 16, 476 (2025), https://doi.org/10.1038/s41467-024-55346-z.

Constructs a parameterized quantum circuit by stacking n_layers PCE layers. Each layer has three single-qubit rotation sublayers (Rx, Ry, Rz) interleaved with brickwork Rxx entangling sublayers (native RXX gate). The entangling sublayers alternate between even pairs (0,1),(2,3),… and odd pairs (1,2),(3,4),…, matching the even/odd tiling used by BrickworkEntanglingBlock.

Parameters:
  • n_qubits – Number of qubits in the circuit.

  • n_layers – Number of PCE layers to stack.

  • target_qubits – Specific qubits to apply the block to. If None, uses all qubits.

  • name – Optional custom name for the block.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.CSFStateBlock(n_spatial_orbitals: int, core_orbitals: List[int], open_shell_orbitals: List[int], coupling_path: List[int | float | Rational] | None = None, Ms: int | float | Rational | None = None, mapping: Mapping | None = None, target_qubits: List[int] | None = None, name: str = 'CSF', *, S: int | float | Rational | None = None)[source]

Bases: CompositeBlockBase

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The mapped, normalized CI coefficients — same convention as MultiONVStateBlock.target_statevector, computed classically from onv_coefficients rather than read back from the built circuit.

class qarp.blocks.CVOQRAMStateBlock(dataset: dict[tuple, float], target_qubits=None, name: str = 'CVOQRAM')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla()[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: tuple[int, ...]

The memory register — the ancillas carry no part of the state.

target_statevector() ndarray[source]

The dataset amplitudes, indexed LSB over the memory register.

The ancillas are restored to |0⟩ deterministically, so ancilla_postselection stays None and the block is control-safe — provided the caller reads state_qubits rather than n_qubits. validate_amplification_blocks is the documented consumer that still reads n_qubits (a deferred contract limit), so this block does not yet fit amplitude amplification without padding the oracle.

class qarp.blocks.CVQRAMStateBlock(dataset: dict[tuple, float], target_qubits=None, name: str = 'CVQRAM')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla()[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: tuple[int, ...]

The memory register — the ancillas carry no part of the state.

target_statevector() ndarray[source]

The dataset amplitudes, indexed LSB over the memory register.

The ancillas are restored to |0⟩ deterministically, so ancilla_postselection stays None and the block is control-safe — provided the caller reads state_qubits rather than n_qubits. validate_amplification_blocks is the documented consumer that still reads n_qubits (a deferred contract limit), so this block does not yet fit amplitude amplification without padding the oracle.

class qarp.blocks.CompositeBlock(blocks: Sequence[Block], n_qubits: int | None = None, target_qubits: List[int] | None = None, *, name: str = 'CompositeBlock')[source]

Bases: CompositeBlockBase

Compose a sequence of pre-built sub-blocks into a single circuit.

Pattern B (composite) — populates self via self.add_child(...) in build_vanilla().

add_child(child: Block) None[source]

Append a child and schedule it for wiring at the next build().

Idempotent under rebuild: a composite built once, then given another child, wires only that child when built again. Wiring is deferred — this method clears the Python built flag, so flatten() raises until the next build() re-enters build_vanilla.

build_vanilla() None[source]

Wire the not-yet-wired children (all of them on the first build).

add_wired_child auto-materialises any pending Python-level lazy ops on the child, so a child created via set_symbols / dagger is folded into a concrete SimpleBlock at composition time.

class qarp.blocks.CompositeBlockBase(n_qubits: int, target_qubits: List[int] | None = None, *, name: str | None = None)[source]

Bases: CompositeBlock

Composite of children — populate by calling self.add_child(...).

add_child(child)[source]

Add a built child block; auto-materialise pending Python-level ops.

Without this override, a child holding _pending_substitutions / _pending_replacements / _is_dagger would have its raw (still-symbolic) command buffer copied into the parent at C++ add_child time, and the lazy transforms would be silently dropped.

add_wired_child(child) None[source]

Build child and wire it in — the one-call form of the hand-rolled child.build(); add_child(child) pattern (a child’s own target_qubits placement is honoured by flatten). Wires through the base add_child so a subclass that defers add_child (CompositeBlock) is not re-entered. A child is never re-interpreted here: control it with ControlledBlock (§13).

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

gphase(self, arg: qarpx.Param, /) qarpx.Block
CompositeBlockBase.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.ComputationalBasisStateBlock(basis_state: List[int], target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

|b⟩ for the requested bit string.

class qarp.blocks.ConditionalBlock(cbits: List[int], values: List[bool], then_body: Block, else_body: Block | None = None, name: str | None = None)[source]

Bases: ConditionalBlock

Classical-control wrapper: run then_body (or else_body) based on cbits.

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

gphase(self, arg: qarpx.Param, /) qarpx.Block
ConditionalBlock.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.ControlledBlock(inner: Block, num_controls: int = 1, ctrl_state: List[bool] | None = None, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: ControlledBlock

Quantum-controlled wrapper around any inner Block.

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

property control_state: List[bool]

Control values, LSB-first per control qubit (§6).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

gphase(self, arg: qarpx.Param, /) qarpx.Block
ControlledBlock.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

property n_controls: int

Number of control qubits this block applies (lowest indices).

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.CostOperatorBlock(n_qubits: int, problem: Graph, linear_terms: dict | None = None, symbol_idx: int = 0, target_qubits=None, use_rzz: bool = True, name: str | None = None)[source]

Bases: SimpleBlock

QAOA cost operator for a graph-encoded cost Hamiltonian.

Edges contribute ZZ rotations; linear terms contribute Z rotations. The symbol γ is in radians: each edge of weight w emits rzz(w·γ) = exp(-i (w·γ/2) Z⊗Z) and each linear term rz(c·γ).

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.DOSQPEBlock(eigenstate: Block, unitary: Block, n_ancilla: int, n_state: int, measure: bool = False, target_qubits: List[int] | None = None, name: str = 'DOSQPE')[source]

Bases: CompositeBlockBase

Density Of States Quantum Phase Estimation (DOSQPE) circuit block.

Composes:

ancilla Hadamards · eigenstate prep · CNOT purification entanglement · controlled-U^(2^i) ladder (on state register) · inverse QFT · (optional) ancilla measurements.

Total qubits: n_ancilla + 2 * n_state. The registers are laid out as:

[0 .. n_ancilla-1] — ancilla (time/frequency) [n_ancilla .. n_ancilla+n_state-1] — state [n_ancilla+n_state .. n_q-1] — purification (traced out)

References

arXiv:2510.14744

build_vanilla()[source]

Build the DOSQPE circuit as a qarpx CompositeBlock.

Returns:

The assembled circuit.

Return type:

qx.CompositeBlock

class qarp.blocks.DickeStateBlock(n_qubits: int, hamming_weight: int, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

Equal, real-positive superposition of the C(n, k) weight-k basis states.

class qarp.blocks.GHZLikeStateBlock(basis_state: List[int], dephase: bool = False, target_qubits: List[int] | None = None, name=None)[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla()[source]

Build a GHZ-like state by applying H on the first qubit with basis_state=1, then cascading CNOTs from that qubit to all other qubits with basis_state=1.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

(|0…0⟩ + p·|mask⟩)/√2, with p = -i when dephasing.

Sdg = diag(1, -i) (§2.2) follows the Hadamard, so the marked branch picks up -i, not +i. An empty mask leaves |0…0⟩ unentangled.

class qarp.blocks.GivensBlock(theta: Symbol | float, target_qubits: list[int] | None = None, name: str = 'Givens')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.GroverBlock(oracle: Block, n_marked: int = 1, target_qubits: list[int] | None = None, name: str = 'Grover')[source]

Bases: CompositeBlockBase

Uniform preparation followed by optimal known-count amplification.

For a search register of size N = 2**n_qubits and n_marked = t, the block prepares the uniform state and applies the integer number of amplification iterates maximizing sin((2*k + 1)*theta)**2 around the first optimum, where theta = asin(sqrt(t/N)).

The oracle must implement exactly I - 2 Pi_good. It is deep-copied at construction and is not inferred from n_marked.

The uniform search construction follows Grover, arXiv:quant-ph/9605043; the known-multiple-solution iteration analysis follows Boyer, Brassard, Hoyer, and Tapp, arXiv:quant-ph/9605034.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.HEABlock(n_qubits: int, n_layers: int, real: bool, linear: bool, circular: bool, use_cz: bool, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

Hardware-Efficient Ansatz (HEA) block with configurable entanglement patterns.

HEABlock constructs a parameterized quantum circuit by stacking multiple HEA layers, each consisting of single-qubit rotations followed by entangling gates. The architecture supports both linear and brickwork entanglement topologies, with options for real-valued (Ry-only) or complex-valued (Ry-Rz) rotations. This structure provides an expressive ansatz suitable for variational quantum algorithms while maintaining compatibility with near-term quantum hardware constraints.

Parameters:
  • n_qubits – Number of qubits in the circuit.

  • n_layers – Number of HEA layers to stack.

  • real – If True, uses only Ry rotations (real ansatz); if False, includes Rz rotations (complex).

  • linear – If True, uses linear entanglement; if False, uses brickwork entanglement.

  • circular – If True, applies entanglement with periodic boundary conditions.

  • use_cz – If True, uses CZ gates for entanglement; if False, uses CNOT gates.

  • target_qubits – Specific qubits to apply the block to. If None, uses all qubits.

  • name – Optional custom name for the block.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.HaarRandomBlock(n_qubits: int, t_design: int | None = None, depth: int | None = None, seed: int | None = None, real: bool = False, target_qubits: List[int] | None = None, name: str = 'HaarRandomUnitary')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

get_haar_state() ndarray[source]

Get the Haar-random state prepared by this circuit acting on |0...0⟩.

Computes U|0...0⟩ where U is the unitary implemented by the built circuit. This is equivalent to extracting the first column of the unitary matrix.

When real=True, the returned vector is real-valued (cast to complex dtype for compatibility).

Returns:

A normalised complex state vector of length 2 ** n_qubits.

Raises:

RuntimeError – If the block has not been built yet.

reseed(seed: int) HaarRandomBlock[source]

Return a new block with the same configuration but a different seed.

Parameters:

seed – New random seed.

Returns:

A new HaarRandomBlock with the updated seed.

class qarp.blocks.HadamardTestBlock(state: Block, unitary: Block, unitary_dagger: Block | None = None, estimate_imaginary: bool = False, measure: bool = False, target_qubits: List[int] | None = None, name: str = 'HadamardTest')[source]

Bases: CompositeBlockBase

Hadamard test for ⟨ψ|U|ψ⟩ using one ancilla qubit.

Pattern B (composite). Layout: qubit 0 is the ancilla; qubits 1 .. state.n_qubits hold the state register.

The circuit:

  1. H on ancilla.

  2. state preparation on the state register.

  3. C-U on (ancilla, state) controlled on ancilla = |1⟩.

  4. (optional) C-U† on (ancilla, state) controlled on ancilla = |0⟩.

  5. (optional) Sdg on ancilla — switches the post-measurement basis so the ancilla expectation gives the imaginary part of ⟨ψ|U|ψ⟩.

  6. H on ancilla.

  7. (optional) Measure ancilla into cbit 0.

The unitary (and unitary_dagger, if provided) must be qarpx blocks whose flattened command stream uses gates that ControlledBlock can lift to single-controlled form (X, Y, Z, H, S, Sdg, T, Tdg, Rx, Ry, Rz, P, U, CX, SWAP, GPhase, Barrier). Other gates would need to be decomposed first.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.HnBlock(n_qubits: int, target_qubits: List[int] | None = None, name: str = 'Hn')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.HypergraphStateBlock(hypergraph: Hypergraph | None = None, n_qubits: int | None = None, edges: List[Tuple[int, ...]] | None = None, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

2^{-n/2} (-1)^{e(x)}, with e(x) the number of hyperedges whose qubits are all set in x.

The order-1 case is included: a single-vertex edge is “fully set” iff that bit is 1, which is exactly the Z the build emits.

class qarp.blocks.IdentityBlock(n_qubits: int, target_qubits: List[int] | None = None, name: str = 'Identity')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.InterferometricMeasurementBlock(bra: Block, ket: Block, basis: Dict[int, str] | None = None, estimate_imaginary: bool = False, name: str = 'InterferometricMeasurement')[source]

Bases: CompositeBlockBase

Measure an interferometric transition state in a QWC Pauli basis.

This wrapper first prepares InterferometricStateBlock, then rotates state-register qubits into the requested local X, Y or Z basis and measures the ancilla and all data qubits. basis maps state-register qubits (indexed from zero, excluding the ancilla) to Pauli letters; omitted qubits remain in the computational Z basis.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.InterferometricStateBlock(bra: Block, ket: Block, estimate_imaginary: bool = False, name: str = 'InterferometricState')[source]

Bases: CompositeBlockBase

Prepare a measurement-free interferometric transition state.

Qubit 0 is the interferometric ancilla and qubits 1..n are the state register. The branch synthesis is delegated to HadamardTestBlock, including its branch-sharing optimization for similar state-preparation circuits. No basis rotations or measurements are appended, so the block can be embedded in a larger circuit or measured by a caller-specific primitive.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.LayerBlock(gate_type: GateType, n_qubits: int, qubit_indices: List[int] | None = None, overlapping: int = 0, periodic_boundary: bool = False, parameters: List[float | Symbol] | None = None, target_qubits: List[int] | None = None, name: str = 'LayerBlock')[source]

Bases: SimpleBlock

property arity: int

Return the arity of the gate type.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.LinearEntanglingBlock(n_qubits: int, circular: bool, use_cz: bool, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.LowRankStateBlock(n_qubits: int, amplitudes: List[complex] | Dict[Tuple[int, ...], complex], cut: int | None = None, max_schmidt_rank: int | None = None, target_qubits: List[int] | None = None, name: str = 'LowRankState')[source]

Bases: CompositeBlockBase

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float
property is_exact: bool
prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The un-truncated, normalized input amplitudes — the ideal state this block approximates, per error_bound.

class qarp.blocks.MPSStateBlock(tensors: List[NDArray], target_qubits: List[int] | None = None, name: str = 'MPSState')[source]

Bases: CompositeBlockBase

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]
target_statevector() ndarray[source]

The state the original (pre-canonicalization) tensors represent, normalized — canonicalization is a gauge transformation of the internal circuit construction, not of what’s being prepared.

class qarp.blocks.MappedONVStateBlock(occupation_number_vector: list[int], mapping: Mapping | None = None, target_qubits: List[int] | None = None, name=None)[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla()[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

|b⟩ for the ONV encoded under mapping.

class qarp.blocks.MeasureBlock(qubit: int, cbit: int, name: str | None = None)[source]

Bases: MeasureBlock

Single mid-circuit measurement: writes qubit outcome to cbit.

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

gphase(self, arg: qarpx.Param, /) qarpx.Block
MeasureBlock.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.MixedOperatorBlock(n_qubits: int, symbol_idx: int = 0, target_qubits=None, name: str | None = None)[source]

Bases: SimpleBlock

QAOA mixer operator: rx(β) = exp(-i (β/2) X) on every qubit.

The symbol β is in radians.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.ModularMultiplicationBlock(multiplier: int, modulus: int, target_qubits: List[int] | None = None, name: str = 'ModularMultiplication')[source]

Bases: SimpleBlock

Permutation implementing multiplication modulo a small integer.

For n = (modulus - 1).bit_length() this block acts on the complete 2**n-dimensional Hilbert space as

\[\begin{split}|x\rangle \mapsto \begin{cases} |m x \bmod N\rangle, & x < N,\\ |x\rangle, & x \geq N. \end{cases}\end{split}\]

The reference synthesizer enumerates basis labels and is exponential in the work-register width. It is intended for exact small-integer examples, not cryptographic-scale factoring. MAX_REFERENCE_WORK_QUBITS caps the width at six; measured exact sampling of OrderFindingBlock(2, N) on the default engine takes 2 s at six work qubits (N=63), 31 s at seven (N=127) and 155 s at eight (N=255), the statevector of 3n total qubits dominating. Raising the constant needs new evidence.

Parameters:
  • multiplier – Integer multiplier. It is normalized modulo modulus and must be coprime to it.

  • modulus – Integer modulus greater than one.

  • target_qubits – Qubits occupied when embedded in a parent block.

  • name – Block name.

MAX_REFERENCE_WORK_QUBITS = 6
build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.MultiONVStateBlock(onv_coefficients: Dict[Tuple[int, ...], complex], mapping: Mapping | None = None, target_qubits: List[int] | None = None, name: str = 'MultiONV')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The normalized, mapping-encoded CI coefficients, LSB-first (§1).

class qarp.blocks.OrbitalRotationBlock(u: NDArray, dagger: bool = False, target_qubits: List[int] | None = None, name: str = 'OrbitalRotation')[source]

Bases: CompositeBlockBase

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.OrderFindingBlock(base: int, modulus: int, n_counting_qubits: int | None = None, target_qubits: List[int] | None = None, name: str = 'OrderFinding')[source]

Bases: CompositeBlockBase

Reference circuit for finding the multiplicative order of a base.

The LSB-indexed register layout is counting followed by work. The circuit prepares the work register in |1>, computes a**x mod N by controlled modular multiplications, and applies an inverse QFT to the counting register. Measurement is deliberately left to a sampler.

Parameters:
  • base – Integer satisfying 1 < base < modulus and coprime to it.

  • modulus – Integer modulus greater than one.

  • n_counting_qubits – Counting-register width. Defaults to twice the work width and cannot be smaller than that value.

  • target_qubits – Qubits occupied when embedded in a parent block.

  • name – Block name.

Note

Modular arithmetic is synthesized by ModularMultiplicationBlock and therefore has the same six-work-qubit reference limit and exponential gate cost.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

property counting_qubits: list[int]

Local counting-register qubits, ordered least-significant first.

property work_qubits: list[int]

Local work-register qubits, ordered least-significant first.

class qarp.blocks.ParticleNumberProjectorBlock(n_qubits: int, Npart: int, target_qubits: List[int] | None = None, name: str = 'ParticleNumberProjector')[source]

Bases: _LCUProjectorBlock

Block encoding of the projector onto a fixed particle-number sector.

class qarp.blocks.PauliBlock(pauli_string: Dict[int, str] | str, coefficient: complex | None = None, phase: float | None = None, n_qubits: int | None = None, change_basis: bool = False, measure: bool = False, target_qubits: List[int] | None = None, name: str = 'Pauli')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.PhaseShiftBlock(phase: float, target_qubits: List[int] | None = None, name: str = 'PS')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.PiecewiseLinearStateBlock(n_domain_qubits: int, breakpoints: List[int], slopes: List[float], intercepts: List[float], target_qubits: List[int] | None = None, name: str = 'PiecewiseLinear')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None
property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]
target_statevector() ndarray[source]

2^{-n/2} Σₓ |x⟩(cos(θ(x)/2)|0⟩ + sin(θ(x)/2)|1⟩) on (domain..., flag), LSB: index x + 2^n·f.

class qarp.blocks.PreparesKnownState[source]

Bases: object

Declares the state a block leaves behind when applied to |0…0⟩.

A primitive is specified by its whole unitary; a block that prepares a state is specified by a single column of it, U|0…0⟩, with the remaining 2^n - 1 columns free. AmplitudeAmplificationBlock is the canonical consumer: Q = A·S_0·A†·S_χ is correct for any unitary A whose zeroth column is |ψ⟩, because A S_0 A† = 2|ψ⟩⟨ψ| - I regardless of the rest. So no unitary oracle exists for such a block — §18’s exact-equality comparison has nothing to compare against — and this column is the contract instead.

Declare it with the prepares_known_state() decorator, which attaches these members rather than inheriting them: every block class has exactly one base, its nanobind C++ counterpart, and a second base raises nb_type_init(): invalid number of bases. This mirrors _BlockMixin and @_attach_mixin in _block.py. isinstance(block, PreparesKnownState) still answers correctly.

Declaring is opt-in and orthogonal to the block hierarchy (§13): decorate a SimpleBlock leaf or a CompositeBlockBase tree alike, wherever the block happens to live. Parameterized ansätze must not declare it — UCCBlock, HEABlock, SPABlock, QAOABlock have no fixed column, since their output depends on symbol values and on the reference state they are applied to.

Two rules keep the declaration honest:

  • target_statevector() is derived from the block’s mathematical definition, never read back from its own commands. A declaration computed from the circuit compares the implementation with itself and answers the §18 reviewer check with no.

  • It carries global phase. Standalone a prep’s global phase is unobservable, but it becomes a physical relative phase the moment the block sits under ControlledBlock — which is exactly what AmplitudeEstimationBlock does to it (§13).

The declaration says nothing about the other columns, so it neither implies nor requires that the block be applied first; it is a statement about one input, not about circuit position.

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float][source]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The declared state on state_qubits.

2**len(state_qubits) amplitudes, LSB-indexed (bit i is state_qubits[i]), unit norm, global phase included.

class qarp.blocks.ProjectedControlPhaseBlock(phase: float, dim: int, n_qubits: int, target_qubits: List[int] | None = None, name: str = 'PCP')[source]

Bases: SimpleBlock

Pattern A leaf: subspace-selective phase rotation.

Implements the diagonal unitary that applies e^{+iφ} to the first dim computational basis states and e^{-iφ} to the remaining 2^n_qubits dim. Useful as a projection-like operator in QSVT-style constructions.

Delegates to self.diagonal_unitary(...) (qarpx Shende-Bullock-Markov synthesis).

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QAOABlock(n_qubits: int, n_layers: int, problem: Graph, linear_terms: dict | None = None, use_rzz: bool = True, target_qubits: List[int] | None = None, name='QAOA')[source]

Bases: CompositeBlock

class qarp.blocks.QFTBlock(n_qubits: int, target_qubits: List[int] | None = None, name: str = 'QFT')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QPEBlock(eigenstate: Block, unitary: Block, n_ancilla: int, n_state: int, measure: bool = True, target_qubits: List[int] | None = None, name: str = 'QPE')[source]

Bases: CompositeBlockBase

Quantum Phase Estimation (QPE) circuit block — Pattern B composite.

Composes: ancilla Hadamards · state prep · controlled-U^(2^i) ladder · inverse QFT · (optional) ancilla measurements.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QROMBlock(index_qubits: int, data: Dict[int, Tuple[int, ...]], target_qubits: List[int] | None = None, name: str = 'QROM')[source]

Bases: SimpleBlock

Reversible classical data loading: |l⟩|0...0⟩ -> |l⟩|data[l]⟩.

The “Q” in QROM — a read-only lookup table addressed by a quantum index register, XOR-loading each entry’s bits into a separate output register. Applied to an index register already in superposition (e.g. after H on every index qubit), it entangles the two: sum_l |l⟩|0⟩ -> sum_l |l⟩|data[l]⟩. Valid for any index register state, not only |0…0⟩ — a general oracle, not a PreparesKnownState declarer (compare SelectBlock).

Deviation from the literature this is named after (Babbush et al., PRX 8, 041015 (2018) §III.D; Low, Kliuchnikov & Schaeffer, arXiv:1812.00954, “QROAM”): those constructions use unary iteration to amortize the address decode to ≈ 4N Toffolis total across all N = 2**index_qubits entries, with k 1 clean work qubits (k = index_qubits). This block instead applies one ancilla-free k-controlled X per set bit per nonzero entry (the X-sandwich + native mcx technique SparseStateBlock uses), and an ancilla-free mcx decomposes quadratically in k. Measured on qx.clifford_t_rz_gateset(): 312 CNOTs for 8 entries × 3 bits (k = 3), 16 400 for 32 × 5 (k = 5), 179 944 for 128 × 4 (k = 7) — against ≈ 130 / 510 Toffolis for unary iteration at k = 5 / 7. This construction is right only for small tables. Unary iteration, with the k 1 work qubits placed by the caller through target_qubits like every other composite’s ancillas, is the planned follow-up; the present construction then stays available as ancilla_free=True.

Parameters:
  • index_qubits – width of the index (address) register, qubits 0..index_qubits-1; at least 1.

  • data – dict mapping each populated index (0 <= l < 2**index_qubits) to its output bit-tuple (LSB-first, §1 — tuple element j is output qubit index_qubits + j). All tuples must share the same length (the output width); indices absent from data are implicitly all-zero.

  • target_qubits – standard Block kwargs.

  • name – standard Block kwargs.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QSPAngleFinder(P)[source]

Bases: object

Returns the optimal angles to build a certain polynomial transformation

Parameters:

P – polynomial transformation (has to be all even or all odd)

QSP()[source]
QSVT()[source]
S(phi)[source]
U_phi(phi, a)[source]
W(a)[source]
rotation_matrix(pauli_matrix, theta)[source]
class qarp.blocks.QSPBlock(a, P_angles, target_qubits: List[int] | None = None, name: str = 'QSP')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QSVTBlock(A: ndarray | QubitOperator, P_angles: List[float], target_qubits: List[int] | None = None, name: str = 'QSVT')[source]

Bases: CompositeBlockBase

Pattern B composite: Quantum Singular Value Transformation of an operator A driven by a sequence of projector-controlled-phase angles.

For a polynomial of degree d = len(P_angles) - 1 (per the QSVT convention), the assembled circuit is:

Even d:

Π_φ₀ · BE† · Π_φ₁ · BE · Π_φ₂ · BE† · … · Π_φ_d

Odd d:

Π_φ₀ · BE · Π_φ₁ · BE† · Π_φ₂ · BE · … · BE · Π_φ_d

where BE block-encodes A / λ and Π_φ is a ProjectedControlPhaseBlock rotating the LCU-control subspace.

Inputs are validated to be square / Hermitian / real-block-encoding (the standard QSVT preconditions).

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.QubitizationBlock(A: ndarray | QubitOperator, operator_name: str = 'Operator', target_qubits: List[int] | None = None, name: str = 'Qubitization')[source]

Bases: CompositeBlockBase

Pattern B composite: qubitization walk operator built from a ReflectionBlock on the ancilla register followed by a BlockEncodingBlock.

The walk operator is W = R · BE where:

  • R = 2|0…0⟩⟨0…0| - I on the LCU-control register (the first num_controls qubits, where num_controls = ⌈log₂ N_LCU⌉).

  • BE block-encodes A / λ on the full register.

Iterating W realises a quantum walk whose spectrum encodes the eigenphases of the block-encoded operator, the foundation of QSP / QSVT.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.RSPBlock(theta: Symbol | float, target_qubits: List[int] | None = None, name: str = 'RSP')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.ReadoutBlock(n_qubits: int, qubits: Sequence[int] | None = None, cbits: Sequence[int] | None = None, target_qubits: List[int] | None = None, name: str = 'Readouts')[source]

Bases: SimpleBlock

Python-layer convenience wrapper that reads out a sequence of qubits.

For users composing circuits with the Python block API and CompositeBlock, this is the ergonomic “exit point” that puts Measure commands into the command stream — visible to QIR / QASM emitters and to anything that inspects the flat command list.

Internally it’s a SimpleBlock carrying one Measure command per (qubit, cbit) pair. Semantically equivalent to chaining N single-qubit MeasureBlock(q, c) primitives, but built in one call without the composite-of-N-MeasureBlocks n_qubits inference foot-gun.

Three-role API recap:

  • block.measure(q, c) — builder method on SimpleBlock; appends a single Measure command to the leaf’s own buffer. Used when constructing a leaf with measurements inline (e.g. inside this class’s own build_vanilla).

  • MeasureBlock(qubit, cbit) — single-Command qarpx-native typed primitive. Used for tree-level composition (add_child patterns inside e.g. SWAPTestBlock, HadamardTestBlock; body of ConditionalBlock).

  • ReadoutBlock(n_qubits, qubits=..., cbits=...) — Python-layer collection wrapper for “read out these qubits in one call.”

Parameters:
  • n_qubits – Size of the qubit register this block operates on.

  • qubits – Sequence of qubit indices to read out. Defaults to range(n_qubits) (read out the full register).

  • cbits – Classical-bit targets, same length as qubits. Defaults to list(qubits) — i.e. cbit_i = qubit_i.

  • target_qubits – Optional parent-space remap for the qubits this block covers, forwarded to Block.

  • name – Display name; defaults to "Readouts".

build_vanilla() None[source]

Emit one Measure command per (qubit, cbit) pair.

class qarp.blocks.ReflectionBlock(n_qubits: int, name: str = 'Reflection')[source]

Bases: SimpleBlock

Reflection about |0…0⟩: 2|0⟩⟨0| - I on n qubits.

Built as -1 · X^n · MCZ · X^n: the X-sandwich turns the MCZ’s “phase −1 on |11…1⟩” into “phase −1 on |00…0⟩”, and the global GPhase(π) flips the overall sign so the |0⟩ subspace gets +1 (and everything else gets −1 — i.e. 2|0⟩⟨0| I).

Parameters:
  • n_qubits – number of qubits of the circuit block

  • name – name of the block

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.ResetBlock(qubit: int, name: str | None = None)[source]

Bases: ResetBlock

Single mid-circuit reset on qubit.

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

gphase(self, arg: qarpx.Param, /) qarpx.Block
ResetBlock.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.SPABlock(n_qubits: int, n_layers: int, real: bool, linear: bool, circular: bool, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: CompositeBlock

get_entangler_block(i, j, layer_index)[source]
class qarp.blocks.SWAPTestBlock(bra: Block, ket: Block, measure: bool = False, target_qubits: List[int] | None = None, name: str = 'SWAPTest')[source]

Bases: CompositeBlockBase

SWAP test for the overlap |⟨bra|ket⟩|² using one ancilla qubit.

Pattern B (composite). Layout: qubit 0 is the ancilla; qubits 1 .. n_state hold the ket register; qubits 1 + n_state .. 1 + 2*n_state - 1 hold the bra register.

The circuit:

  1. H on ancilla.

  2. ket preparation on the ket register.

  3. bra preparation on the bra register.

  4. CSWAP(ancilla, ket[i], bra[i]) for each i.

  5. H on ancilla.

  6. (optional) Measure ancilla into cbit 0.

The resulting ancilla expectation P(0) - P(1) = |⟨bra|ket⟩|².

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.SelectBlock(unitaries: Sequence[Any], num_controls: int, target_qubits: List[int] | None = None, name: str = 'Select')[source]

Bases: CompositeBlockBase

Select one unitary U_i conditioned on a control register.

The control qubits live at local indices [0..num_controls) and the selected unitary acts on local target qubits [num_controls..num_controls + target_size).

Accepted entries in unitaries:

  • Block: selected directly as U_i.

  • (phase, Block): selected as exp(i phase) U_i.

  • (phase, pauli_string_or_dict): backward-compatible shorthand using PauliBlock.

Target-register sizing: Blocks and Pauli strings have exact widths and must all agree; sparse Pauli dicts act as identity on unlisted qubits and only need to fit the register. A dict-only list is sized by the highest addressed qubit.

If fewer than 2**num_controls entries are supplied, the missing selector states implement the identity, matching the previous behavior.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.SimpleBlock(n_qubits: int, target_qubits: List[int] | None = None, *, name: str | None = None)[source]

Bases: SimpleBlock

Leaf block — populate by calling self.h(q), self.cx(c, t), etc.

The positional constructor order (n_qubits, target_qubits) is stable API — subclasses (including external Block users) call super().__init__ positionally; do not reorder. name is keyword-only: the former n_controls / control_state slots are gone (controlisation is explicit — ControlledBlock, §13), and a stale five-positional call must fail loudly rather than shift name.

build() Self

Build the block: run build_vanilla(), mark built, finalise.

Returns self so callers can chain my_block.build().flatten().

Idempotent: re-calling build() on an already-built block returns self without re-running build_vanilla() — re-running would double-append commands to the C++ buffer. Composite-style blocks often call child.build() even when the user already built the child, so the guard prevents duplicates.

build_vanilla() None

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

can_emit_to(target: str) str | None

None if this block can cross to target, else the reason it cannot.

Pre-flight form of to_<target>() — evaluates the emitter’s declared gate set and capabilities without importing the SDK, so it answers on machines where the SDK is absent. target is an emitter target_name: “qiskit” | “qulacs” | “pytket” | “pennylane” | “qasm3” | “qasm2” | “qir”.

ccz(c0, c1, t)

Doubly-controlled Z: sugar for mcz([c0, c1, t]) (§5).

cp(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cp(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cry(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
cry(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
crz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
crz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
cu(self, arg0: int, arg1: int, arg2: qarpx.Param, arg3: qarpx.Param, arg4: qarpx.Param, arg5: qarpx.Param, /) qarpx.Block
dagger() Self

Return a deep copy with the dagger flag toggled.

The returned block is the SAME Python class as self and carries the same sympy state. The dagger is applied lazily when flatten() is called.

This wraps the standard Python pattern (deepcopy + flip flag) rather than calling the C++ Block::dagger() so subclass identity is preserved across the dagger.

depth() int

Circuit depth of the built block: the longest dependency path through the flattened command stream, computed on the wire-dependency DAG (qx.CircuitDAG). Gates that can act simultaneously on disjoint qubits share a time step; Barrier and GPhase weigh 0.

Raises:

RuntimeError – If the block has not been built yet.

flatten()

Return the flat [qx.Command, ...] after applying pending ops.

Lazily applies (in order):
  1. Pending symbol replacements (Symbol → Symbol) via the C++ Block.replace_symbols method, which returns a fresh block with renamed params.

  2. Pending symbol substitutions (Symbol → float) via the C++ Block.set_symbols method (similar — returns a fresh block).

  3. Pending dagger flag — applied as a per-command Python-level reverse + dagger of the resulting flat stream.

The original C++ command buffer (self) is left unmodified — this is a read-only view on top of that canonical command buffer. Each pending op produces a transient C++ block whose commands feed the next op.

free_symbols() List[str]

Free symbol names with pending lazy ops replayed.

The C++ free_symbols scans the canonical (untransformed) command buffer, so it doesn’t see pending set_symbols / replace_symbols queued on the Python side. Replay them here in the same order flatten() applies them: all renames first, then all substitutions.

static from_pennylane(tape: Any) SimpleBlock[source]

Build a SimpleBlock from a pennylane.tape.QuantumScript.

static from_pytket(circuit: Any) SimpleBlock[source]

Build a SimpleBlock from a pytket.Circuit.

static from_qasm2(qasm: str) SimpleBlock[source]

Build a SimpleBlock from OpenQASM 2.0 text (inverse of to_qasm2).

Accepts more than to_qasm2 writes — multiple registers, the whole-register measure q -> c;, and the qiskit-extended qelib1.inc names — so foreign files parse too (§12.2).

static from_qasm3(qasm: str) SimpleBlock[source]

Build a SimpleBlock from OpenQASM 3.0 text (inverse of to_qasm3).

static from_qiskit(circuit: Any) SimpleBlock[source]

Build a SimpleBlock from a qiskit.QuantumCircuit.

static from_qulacs(circuit: Any) SimpleBlock[source]

Build a SimpleBlock from a qulacs.QuantumCircuit.

gphase(self, arg: qarpx.Param, /) qarpx.Block
SimpleBlock.is_built -> bool
mark_built() None

Declare an externally populated block built (absorb / cutting reconstruction paths) without running the build lifecycle.

mcx(*qubits)

Multi-controlled X; the last qubit is the target (§5).

Emitted as H(t) · MCZ(qubits) · H(t) — exact, no phase — so it needs no GateType of its own and lowers wherever MCZ does. Accepts mcx(c0, c1, t) or mcx([c0, c1, t]).

mcz(self, arg: collections.abc.Sequence[int], /) qarpx.Block
n_1q_gates() int

Number of 1-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(1); same exclusions and build precondition.

n_2q_gates() int

Number of 2-qubit gates in the flattened circuit.

Shorthand for n_nqb_gates(2); same exclusions and build precondition.

n_gates() int

Total physical gate count over all arities (qx.n_physical_gates).

Same exclusions and build precondition as n_nqb_gates; equals the resource vector’s headline n_gates.

n_gates_of_type(gate: GateType) int

Number of flattened commands of the given GateType (qx.n_gates_of_type).

Unfiltered: unlike n_nqb_gates, Barrier/Measure/ Reset/GPhase/branch markers are counted like any other GateType (mirrors qx.CircuitDAG.count_ops()).

Raises:

RuntimeError – If the block has not been built yet.

n_nqb_gates(k: int) int

Number of k-qubit gates in the flattened circuit (qx.n_nqb_gates).

Excludes non-gate commands (Barrier, Measure, Reset, GPhase, and the Branch* classical-control markers, per qx.gate_is_physical) — none represent a physical gate applied to the register. A CompositeBlock’s children are included in the sum since flatten() already recurses into them.

Raises:

RuntimeError – If the block has not been built yet.

optimize(target_gateset: GateSet | None = None, level: int = 1) SimpleBlock

Lower + peephole-optimize the block, returning a new SimpleBlock.

Pipeline (matches the QarpEngine run path in qarp/engines/qarp_engine.py):

  1. Transpiler lowers gates outside target_gateset via builtin_decompositions.

  2. Wire-adjacent cancellation on the circuit DAG (level >= 1): inverse pairs and same-axis rotation merges (H·H, Rz(a)·Rz(b) Rz(a+b), S·Sdg, Rz(0), GPhase sums, …), combining across gates on other qubits. At level >= 2 also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rz merges, matrix-verified commutation table, bounded lookahead).

  3. fuse_single_qubit_gates collapses runs of single-qubit gates on each qubit into a single Custom 2×2 matrix — only when the target admits ``Custom`` (native_gateset does; the SDK and hardware targets do not). The output never leaves the target (§16 rebase totality, Transpiler.optimize_in_target).

Parameters:
  • target_gateset – Optional qx.GateSet target. Defaults to qx.native_gateset() — the gates QarpSimulator can dispatch in a single csim kernel sweep, avoiding decomposition of natively-runnable gates.

  • level – Optimization level 0-2 (qx.OptLevel). 0 = transpile only; 1 = wire-adjacent cancellation + fusion (default, the engine pipeline’s level); 2 = + commutation-aware cancellation (opt-in). Surviving gate order is preserved at every level.

Returns:

A new SimpleBlock holding the optimized command sequence. The receiver is not mutated. The returned block is marked built and its target_qubits is the local [0, n_qubits) frame, ready for flatten() / engine consumption.

Raises:
  • RuntimeError – If the block has not been built yet — call .build() first so flatten() is well-defined.

  • ValueError – If level is not 0, 1, or 2.

  • CapabilityError – If a gate cannot be rebased onto target_gateset.

p(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
p(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
parameter_map(values: Iterable[float]) Dict[Symbol, float]

Map a positional parameter vector onto symbols — the one blessed vector→map conversion. Length-checked; use this instead of hand-zipping against a symbol list.

plot(*args: Any, **kwargs: Any)

Plot the block via CircuitAdapter.

A built CompositeBlock is drawn as one box per child sub-block so the plot mirrors how the circuit was composed; pass decompose_boxes=True to flatten it into primitive gates instead. Leaf blocks always render their gates.

refresh_symbols(postfix: str) Self

Append postfix to every symbol name; return a new block.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) Self

Schedule a symbol → symbol rename; return a new block.

rx(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rx(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rxx(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rxx(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
ry(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
ry(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
ryy(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
ryy(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
rz(self, arg0: int, arg1: qarpx.Param, /) qarpx.Block
rz(self, arg: collections.abc.Sequence[tuple[int, qarpx.Param]], /) qarpx.Block
rzz(self, arg0: int, arg1: int, arg2: qarpx.Param, /) qarpx.Block
rzz(self, arg: collections.abc.Sequence[tuple[int, int, qarpx.Param]], /) qarpx.Block
set_symbols(symbol_parameter_map: Dict[Symbol, float]) Self

Schedule a symbol → float substitution; return a new block.

The substitution is applied lazily in flatten().

statevector(initial_state: ndarray | None = None) ndarray

Exact statevector of this block applied to initial_state (default |0…0⟩).

A mathematical view — no engine, no noise. Pending set_symbols / replace_symbols / dagger are applied via flatten(). Terminal measurements are tolerated; a true mid-circuit operation (Reset, conditioned gate, measure-then-reuse) is rejected by the C++ simulator (the evolution is not a single statevector).

Parameters:

initial_state – Optional LSB-indexed amplitudes (any 1-D complex-convertible array, length 2**n_qubits, unit norm within 1e-10ValueError otherwise; never renormalised). The returned statevector feeds back in unchanged, so step → snapshot → re-seed loops are O(2^n) per step.

property symbols: Tuple[Symbol, ...] | None

Canonically ordered free-parameter registry of a built block.

Always sorted by string representation (_sorted_symbols); every positional parameter vector in the public API aligns to this order. Never use it for symbol↔operator pairing — pairing lives in dedicated structures (e.g. TrotterAnsatzBlock.symbol_qop_pairs).

to_pennylane() Any

Export the block’s circuit to a pennylane.tape.QuantumScript.

to_pytket() Any

Export the block’s circuit to a pytket.Circuit.

to_qasm2(output: str | None = None) str

Emit the block’s circuit as OpenQASM 2.0 text.

The narrower of the two languages: symbolic parameters, GPhase and MCZ have no representation and raise CapabilityError (§12.2). to_qasm3() carries all three.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 2.0 program string.

to_qasm3(output: str | None = None) str

Emit the block’s circuit as OpenQASM 3.0 text.

Parameters:

output – If given, also writes the QASM to that file.

Returns:

The OpenQASM 3.0 program string.

to_qir(output: str | None = None) str

Emit the block’s circuit as QIR (LLVM IR) text.

Parameters:

output – If given, also writes the QIR to that file.

Returns:

The QIR module as a string.

to_qiskit() Any

Export the block’s circuit to a qiskit.QuantumCircuit.

to_qulacs() Any

Export the block’s circuit to a qulacs.QuantumCircuit.

u(self, arg0: int, arg1: qarpx.Param, arg2: qarpx.Param, arg3: qarpx.Param, /) qarpx.Block
unitary_matrix() ndarray

Dense 2^n × 2^n unitary of this block, global phase included.

Same guards as statevector(). Exponential in n_qubits — an exploration/validation tool, not a simulation path.

class qarp.blocks.SlaterDeterminantBlock(orbital_coefficients: NDArray, target_qubits: List[int] | None = None, name: str = 'SlaterDeterminant')[source]

Bases: CompositeBlockBase

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

Amplitude of |onv⟩ is det(Q[occupied_rows, :]) — the standard Slater-determinant-to-CI-coefficient minor formula. Cauchy-Binet (Σ_S det(Q[S,:])² = det(QᵀQ) = 1 for orthonormal columns) makes this unit-norm without an explicit renormalization.

class qarp.blocks.SparseStateBlock(n_qubits: int, amplitudes: Dict[Tuple[int, ...], complex], target_qubits: List[int] | None = None, name: str = 'SparseState')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The normalized input amplitudes, addressed LSB-first (§1).

class qarp.blocks.SpinSquaredProjectorBlock(n_qubits: int, S: float, Ms: float = 0.0, n_alpha: int | None = None, n_beta: int | None = None, n_gamma: int | None = None, target_qubits: List[int] | None = None, name: str = 'S2Projector')[source]

Bases: _LCUProjectorBlock

Block encoding of the projector onto the S(S+1) spin-squared sector.

property eigenvalue: float
class qarp.blocks.SyProjectorBlock(n_qubits: int, My: float, target_qubits: List[int] | None = None, name: str = 'SyProjector')[source]

Bases: _LCUProjectorBlock

Block encoding of the projector onto a fixed Sy eigenvalue.

class qarp.blocks.SynthesizedStateBlock(n_qubits: int, amplitudes: List[complex] | Dict[tuple, complex], target_qubits: List[int] | None = None, name: str = 'SynthStateBlock')[source]

Bases: SimpleBlock

Pattern A leaf: amplitude-encode an arbitrary 2^n-vector into a circuit.

Delegates to self.state_preparation(...) (qarpx C++ Möttönen synthesis) in build_vanilla(). Inputs are normalized; an all-zero amplitude vector is rejected at construction.

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The normalized input amplitudes.

Definitional, not an independent oracle: this block’s target is its input, so the conformance check here only pins the synthesis to the vector it was handed. What proves the Möttönen synthesis itself correct lives in the synthesis tests (§18).

class qarp.blocks.SynthesizedTimeEvolutionBlock(operator: QubitOperator, n_qubits: int, time: float | None = None, target_qubits: List[int] | None = None, name: str = 'SynthTimeEvoBlock')[source]

Bases: SimpleBlock

Pattern A leaf: synthesize U(t) = exp(-i H t) for a Hamiltonian H.

Builds the time-evolution unitary numerically via scipy.linalg.expm and delegates to self.unitary_synthesis(...) (qarpx Quantum Shannon Decomposition) for the circuit synthesis.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

set_time(time_value: float) SynthesizedTimeEvolutionBlock[source]
class qarp.blocks.SynthesizedUnitaryBlock(unitary_matrix: ndarray, target_qubits: List[int] | None = None, name: str = 'SynthUnitaryBlock')[source]

Bases: SimpleBlock

Pattern A leaf: synthesize an arbitrary 2^n × 2^n unitary into a circuit.

Delegates to self.unitary_synthesis(...) (qarpx C++ Quantum Shannon Decomposition) in build_vanilla().

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

class qarp.blocks.SzProjectorBlock(n_qubits: int, Ms: float, target_qubits: List[int] | None = None, name: str = 'SzProjector')[source]

Bases: _LCUProjectorBlock

Block encoding of the projector onto a fixed Sz eigenvalue.

class qarp.blocks.TrotterAnsatzBlock(n_qubits: int, qubit_exponents: List[QubitOperator], symbols: List[Symbol], steps: int = 1, time: float | Symbol = 1.0, order: int = 1, imaginary: bool = False, grouping: GroupingStrategy | None = None, target_qubits=None, name='TrotterAnsatz')[source]

Bases: SimpleBlock

Symbol-per-term Trotterised ansatz: ∏_k exp(-i s_k Q_k).

Each (s_k, Q_k) pair contributes one factor in the ansatz. Per-term Pauli coefficients absorb into the symbol’s effective angle, so the ansatz parameters s_k are the only free parameters at run time.

Radians convention: a symbol value s_k contributes exp(+i s_k c time P) per Pauli term P with coefficient c — emitted as a commuting_pauli_set_exp angle of -2·c·time/steps · s_k radians (the builder realises exp(-i/2 · Σ angle·P)).

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

replace_symbols(new_parameters: Dict[Symbol, Symbol]) SimpleBlock[source]

Schedule a symbol → symbol rename; return a new block.

set_time(time_value: float) SimpleBlock[source]
class qarp.blocks.TrotterBlock(n_qubits: int, operator: QubitOperator | None, steps: int = 1, time: float | Symbol | None = None, order: int = 1, imaginary: bool = False, composition: Literal['suzuki', 'yoshida'] = 'suzuki', grouping: GroupingStrategy | None = None, target_qubits: List[int] | None = None, name: str | None = None)[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

set_time(time_value: float) SimpleBlock[source]

Substitute the symbolic time with a concrete value (radians).

class qarp.blocks.UCCBlock(occupation_number_vector: list[int], singles: bool = True, doubles: bool = True, paired_doubles: bool = False, generalised: bool = False, spin_conserving: bool = True, mapping: Mapping | None = None, order: int = 1, time: float = 1.0, steps: int = 1, grouping: GroupingStrategy | None = None, symbol_postfix: str = '', target_qubits: List[int] | None = None, name: str = 'UCC')[source]

Bases: CompositeBlockBase

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

replace_symbols(new_parameters: dict) UCCBlock[source]

Schedule a symbol → symbol rename; return a new block.

property symbol_qop_pairs

(symbol, encoded generator), generation-ordered.

Mirrors TrotterAnsatzBlock.symbol_qop_pairs. Use this — never the sorted symbols registry — when positional symbol↔operator correspondence matters.

Type:

Pairing surface

class qarp.blocks.UPCCDBlock(basis_state: list[int], t2: ndarray | None = None, threshold: float = 0.0001, target_qubits: List[int] | None = None, name: str = 'UPCCD')[source]

Bases: CompositeBlockBase

build_vanilla() None[source]

Build the UPCCD composite by adding one GivensBlock per allowed paired-double, then a final alpha→beta CX layer.

class qarp.blocks.UniformSuperpositionBlock(M: int, n_qubits: int | None = None, target_qubits: List[int] | None = None, name: str = 'UniformSuperposition')[source]

Bases: SimpleBlock

property ancilla_postselection: PostSelection | None

Condition under which the prepared state appears; None if none.

None means every ancilla returns to |0⟩ with probability 1 — the block is deterministic, and therefore safe under ControlledBlock and safe to hand to validate_amplification_blocks. A returned condition must fix exactly ancilla_qubits: the state then exists only on that branch, the caller must condition on it, and the block is not control-safe. Pass it straight to a Sampler result — PostSelection.apply() speaks that currency already.

property ancilla_qubits: Tuple[int, ...]

The complement of state_qubits, ascending.

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

declares_known_state = True
property error_bound: float

Upper bound on 1 - |⟨target|prepared⟩|² (infidelity).

Meaningless — and unchecked — when is_exact is True (the default 0.0 here is never read). An approximate block overrides both together; the bound should be computable from the block’s own construction (a discarded Schmidt weight, a discretization precision, …), not fitted after the fact.

property is_exact: bool

True (the default) if target_statevector() is met exactly.

False marks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checks prepared_statevector() against target_statevector() by infidelity against error_bound rather than by atol=1e-10 elementwise equality. Most blocks never touch this; override alongside error_bound.

prepared_statevector() Tuple[ndarray, float]

What the built circuit actually leaves on state_qubits.

Returns (state, probability) — the counterpart to target_statevector(), which the two are asserted equal against. probability is that of ancilla_postselection, and is 1.0 for a deterministic block. Exponential in n_qubits: a validation tool, not a simulation path.

property state_qubits: Tuple[int, ...]

Block-local qubit indices carrying the prepared state, ascending.

Defaults to the whole register. Override when the block sizes itself larger than the state it prepares, as the QRAM blocks do.

target_statevector() ndarray[source]

The uniform distribution over {0, ..., M-1} — definitional, like SynthesizedStateBlock’s: the target is the input.

class qarp.blocks.XnBlock(n_qubits: int, target_qubits: List[int] | None = None, name: str = 'Xn')[source]

Bases: SimpleBlock

build_vanilla() None[source]

Override to populate the block.

For SimpleBlock subclasses: call self.h(0), self.cx(0, 1), etc. — gate methods inherited from the C++ side append commands to self directly.

For CompositeBlockBase subclasses: call self.add_child(sub_block).

For wrapper blocks (ControlledBlock, ConditionalBlock): typically no override is needed; the wrapper logic happens at construction.

Default is a no-op so wrapper blocks (whose build_vanilla is empty) and ad-hoc SimpleBlock instances populated externally both work.

qarp.blocks.declaring_blocks() Dict[str, type][source]

Every block class that has declared the contract, by name.

Registration happens at class-definition time, so a block appears here once its module is imported — which qarp.blocks.__init__ does for the whole public surface (§15). Only decorated classes appear: an undecorated subclass inherits the declaration (isinstance is true) without registering, so a gate over the public surface must also walk issubclass(cls, PreparesKnownState).

qarp.blocks.prepares_known_state(cls: type) type[source]

Attach PreparesKnownState to cls and register it.

Members the block defines itself are left alone, so a block overrides state_qubits / ancilla_postselection simply by defining them.

Raises ValueError when a different class with the same __name__ is already registered: declaring_blocks() is keyed by bare name, and a silent overwrite would drop the earlier block from the conformance gate.