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 toselfdirectly.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:
CompositeBlockBaseThe phase-exact amplitude-amplification iterate.
For a state-preparation unitary
Aand a good-state phase oracleO_good = I - 2 Pi_good, this block implements exactlyQ = A R0 A_dagger O_good,where
ReflectionBlocksuppliesR0 = 2|0...0><0...0| - I. Consequently, the circuit-time child order isO_good,A_dagger,R0,A.The oracle contract is mathematical:
oraclemust 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 rawReflectionBlockabout the good subspace implements2 Pi_good - I = -(I - 2 Pi_good)— the exact negative of a good-state oracle. To use one as an oracle, compose it with agphase(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’sReflectionBlockisR0 = -S0, yielding the exact form above.powerrepeats the iterate: the block implementsQ^power. The defaultpower=1is the single iterateQ;power=0is the identity (empty circuit). This is the only knob a fixed-schedule amplitude-amplification consumer needs — Grover appliesQ^kafter a uniform preparation, and maximum-likelihood amplitude estimation runs several powersQ^{m_k}(includingm_0 = 0) afterA.- Parameters:
state_preparation – Unitary
Apreparing the initial state from|0...0>.oracle – Unitary implementing exactly
I - 2 Pi_goodon the same register asstate_preparation.target_qubits – Optional placement of the complete iterate.
name – Block name.
power – Non-negative number of times to repeat the iterate
Q(default1;0is 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 toselfdirectly.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:
CompositeBlockBaseCanonical QAE circuit for a phase-exact amplification iterate.
For
A|0> = sqrt(1-a)|psi_bad> + sqrt(a)|psi_good>and an oracle implementing exactlyO_good = I - 2 Pi_good, the embeddedAmplitudeAmplificationBlockhas relevant eigenphases+/- 2 theta, wheresin(theta)**2 = a. This block applies QPE to that iterate without adding measurements.Qubits
0 .. n_ancilla-1form 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
Apreparing 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 toselfdirectly.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:
CompositeBlockBasePattern B composite: block-encode an operator
Avia LCU.Decomposes
A = Σ_i c_i U_iinto Pauli strings and assembles the standardPrep† · Select · PrepLCU circuit. The block-encoded operator on the|0…0⟩_ancsubspace isA / λwhereλ = Σ_i |c_i|(stored asself.lambda_norm).Preploads real amplitudes√(|c_i|/λ)on the ancilla register; each LCU phaseφ_i = arg(c_i)is carried by the correspondingSelectBlockentry and lowered through the multi-controlledGPhasedecomposition.- 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 toselfdirectly.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.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 toselfdirectly.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:
SimpleBlockBrickwork 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_layersPCE layers. Each layer has three single-qubit rotation sublayers (Rx, Ry, Rz) interleaved with brickwork Rxx entangling sublayers (nativeRXXgate). The entangling sublayers alternate between even pairs (0,1),(2,3),… and odd pairs (1,2),(3,4),…, matching the even/odd tiling used byBrickworkEntanglingBlock.- 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 toselfdirectly.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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_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, soancilla_postselectionstaysNoneand the block is control-safe — provided the caller readsstate_qubitsrather thann_qubits.validate_amplification_blocksis the documented consumer that still readsn_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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_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, soancilla_postselectionstaysNoneand the block is control-safe — provided the caller readsstate_qubitsrather thann_qubits.validate_amplification_blocksis the documented consumer that still readsn_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:
CompositeBlockBaseCompose a sequence of pre-built sub-blocks into a single circuit.
Pattern B (composite) — populates
selfviaself.add_child(...)inbuild_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 nextbuild()re-entersbuild_vanilla.
- class qarp.blocks.CompositeBlockBase(n_qubits: int, target_qubits: List[int] | None = None, *, name: str | None = None)[source]¶
Bases:
CompositeBlockComposite 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_daggerwould have its raw (still-symbolic) command buffer copied into the parent at C++add_childtime, and the lazy transforms would be silently dropped.
- add_wired_child(child) None[source]¶
Build
childand wire it in — the one-call form of the hand-rolledchild.build(); add_child(child)pattern (a child’s owntarget_qubitsplacement is honoured byflatten). Wires through the baseadd_childso a subclass that defersadd_child(CompositeBlock) is not re-entered. A child is never re-interpreted here: control it withControlledBlock(§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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- class qarp.blocks.ConditionalBlock(cbits: List[int], values: List[bool], then_body: Block, else_body: Block | None = None, name: str | None = None)[source]¶
Bases:
ConditionalBlockClassical-control wrapper: run
then_body(orelse_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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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:
ControlledBlockQuantum-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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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:
SimpleBlockQAOA 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 weightwemitsrzz(w·γ) = exp(-i (w·γ/2) Z⊗Z)and each linear termrz(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 toselfdirectly.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:
CompositeBlockBaseDensity 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
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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 toselfdirectly.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:
CompositeBlockBaseUniform preparation followed by optimal known-count amplification.
For a search register of size
N = 2**n_qubitsandn_marked = t, the block prepares the uniform state and applies the integer number of amplification iterates maximizingsin((2*k + 1)*theta)**2around the first optimum, wheretheta = asin(sqrt(t/N)).The oracle must implement exactly
I - 2 Pi_good. It is deep-copied at construction and is not inferred fromn_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 toselfdirectly.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:
SimpleBlockHardware-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 toselfdirectly.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 toselfdirectly.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
HaarRandomBlockwith 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:
CompositeBlockBaseHadamard test for
⟨ψ|U|ψ⟩using one ancilla qubit.Pattern B (composite). Layout: qubit 0 is the ancilla; qubits
1 .. state.n_qubitshold the state register.The circuit:
Hon ancilla.statepreparation on the state register.C-Uon(ancilla, state)controlled onancilla = |1⟩.(optional)
C-U†on(ancilla, state)controlled onancilla = |0⟩.(optional)
Sdgon ancilla — switches the post-measurement basis so the ancilla expectation gives the imaginary part of⟨ψ|U|ψ⟩.Hon ancilla.(optional)
Measureancilla into cbit 0.
The
unitary(andunitary_dagger, if provided) must be qarpx blocks whose flattened command stream uses gates thatControlledBlockcan 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 toselfdirectly.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 toselfdirectly.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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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 toselfdirectly.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:
CompositeBlockBaseMeasure an interferometric transition state in a QWC Pauli basis.
This wrapper first prepares
InterferometricStateBlock, then rotates state-register qubits into the requested localX,YorZbasis and measures the ancilla and all data qubits.basismaps 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 toselfdirectly.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:
CompositeBlockBasePrepare a measurement-free interferometric transition state.
Qubit 0 is the interferometric ancilla and qubits
1..nare the state register. The branch synthesis is delegated toHadamardTestBlock, 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 toselfdirectly.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- 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 toselfdirectly.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 toselfdirectly.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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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¶
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- class qarp.blocks.MeasureBlock(qubit: int, cbit: int, name: str | None = None)[source]¶
Bases:
MeasureBlockSingle mid-circuit measurement: writes
qubitoutcome tocbit.- 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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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:
SimpleBlockQAOA 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 toselfdirectly.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:
SimpleBlockPermutation implementing multiplication modulo a small integer.
For
n = (modulus - 1).bit_length()this block acts on the complete2**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_QUBITScaps the width at six; measured exact sampling ofOrderFindingBlock(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 of3ntotal qubits dominating. Raising the constant needs new evidence.- Parameters:
multiplier – Integer multiplier. It is normalized modulo
modulusand 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 toselfdirectly.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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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 toselfdirectly.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:
CompositeBlockBaseReference circuit for finding the multiplicative order of a base.
The LSB-indexed register layout is
countingfollowed bywork. The circuit prepares the work register in|1>, computesa**x mod Nby 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 < modulusand 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
ModularMultiplicationBlockand 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 toselfdirectly.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.ParticleNumberProjectorBlock(n_qubits: int, Npart: int, target_qubits: List[int] | None = None, name: str = 'ParticleNumberProjector')[source]¶
Bases:
_LCUProjectorBlockBlock 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 toselfdirectly.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 toselfdirectly.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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- class qarp.blocks.PreparesKnownState[source]¶
Bases:
objectDeclares 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 remaining2^n - 1columns free.AmplitudeAmplificationBlockis the canonical consumer:Q = A·S_0·A†·S_χis correct for any unitaryAwhose zeroth column is|ψ⟩, becauseA S_0 A† = 2|ψ⟩⟨ψ| - Iregardless 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 raisesnb_type_init(): invalid number of bases. This mirrors_BlockMixinand@_attach_mixinin_block.py.isinstance(block, PreparesKnownState)still answers correctly.Declaring is opt-in and orthogonal to the block hierarchy (§13): decorate a
SimpleBlockleaf or aCompositeBlockBasetree alike, wherever the block happens to live. Parameterized ansätze must not declare it —UCCBlock,HEABlock,SPABlock,QAOABlockhave 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 whatAmplitudeEstimationBlockdoes 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float][source]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_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 (bitiisstate_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:
SimpleBlockPattern A leaf: subspace-selective phase rotation.
Implements the diagonal unitary that applies
e^{+iφ}to the firstdimcomputational basis states ande^{-iφ}to the remaining2^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 toselfdirectly.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 toselfdirectly.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:
CompositeBlockBaseQuantum 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 toselfdirectly.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:
SimpleBlockReversible 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
Hon 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 aPreparesKnownStatedeclarer (compareSelectBlock).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 ≈
4NToffolis total across allN = 2**index_qubitsentries, withk − 1clean work qubits (k = index_qubits). This block instead applies one ancilla-freek-controlled X per set bit per nonzero entry (the X-sandwich + nativemcxtechniqueSparseStateBlockuses), and an ancilla-freemcxdecomposes quadratically ink. Measured onqx.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 atk = 5/7. This construction is right only for small tables. Unary iteration, with thek − 1work qubits placed by the caller throughtarget_qubitslike every other composite’s ancillas, is the planned follow-up; the present construction then stays available asancilla_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 elementjis output qubitindex_qubits + j). All tuples must share the same length (the output width); indices absent fromdataare 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 toselfdirectly.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:
objectReturns the optimal angles to build a certain polynomial transformation
- Parameters:
P – polynomial transformation (has to be all even or all odd)
- 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 toselfdirectly.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:
CompositeBlockBasePattern B composite: Quantum Singular Value Transformation of an operator
Adriven 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
BEblock-encodesA / λandΠ_φis aProjectedControlPhaseBlockrotating 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 toselfdirectly.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:
CompositeBlockBasePattern B composite: qubitization walk operator built from a
ReflectionBlockon the ancilla register followed by aBlockEncodingBlock.The walk operator is
W = R · BEwhere:R = 2|0…0⟩⟨0…0| - Ion the LCU-control register (the firstnum_controlsqubits, wherenum_controls = ⌈log₂ N_LCU⌉).BEblock-encodesA / λon the full register.
Iterating
Wrealises 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 toselfdirectly.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 toselfdirectly.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:
SimpleBlockPython-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 putsMeasurecommands into the command stream — visible to QIR / QASM emitters and to anything that inspects the flat command list.Internally it’s a
SimpleBlockcarrying oneMeasurecommand per(qubit, cbit)pair. Semantically equivalent to chaining N single-qubitMeasureBlock(q, c)primitives, but built in one call without the composite-of-N-MeasureBlocksn_qubitsinference foot-gun.Three-role API recap:
block.measure(q, c)— builder method onSimpleBlock; appends a singleMeasurecommand to the leaf’s own buffer. Used when constructing a leaf with measurements inline (e.g. inside this class’s ownbuild_vanilla).MeasureBlock(qubit, cbit)— single-Command qarpx-native typed primitive. Used for tree-level composition (add_child patterns inside e.g.SWAPTestBlock,HadamardTestBlock; body ofConditionalBlock).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 tolist(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".
- class qarp.blocks.ReflectionBlock(n_qubits: int, name: str = 'Reflection')[source]¶
Bases:
SimpleBlockReflection about
|0…0⟩:2|0⟩⟨0| - Ion 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 toselfdirectly.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:
ResetBlockSingle 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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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
- class qarp.blocks.SWAPTestBlock(bra: Block, ket: Block, measure: bool = False, target_qubits: List[int] | None = None, name: str = 'SWAPTest')[source]¶
Bases:
CompositeBlockBaseSWAP test for the overlap
|⟨bra|ket⟩|²using one ancilla qubit.Pattern B (composite). Layout: qubit 0 is the ancilla; qubits
1 .. n_statehold the ket register; qubits1 + n_state .. 1 + 2*n_state - 1hold the bra register.The circuit:
Hon ancilla.ketpreparation on the ket register.brapreparation on the bra register.CSWAP(ancilla, ket[i], bra[i])for eachi.Hon ancilla.(optional)
Measureancilla 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 toselfdirectly.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:
CompositeBlockBaseSelect one unitary
U_iconditioned 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 asU_i.(phase, Block): selected asexp(i phase) U_i.(phase, pauli_string_or_dict): backward-compatible shorthand usingPauliBlock.
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_controlsentries 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 toselfdirectly.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:
SimpleBlockLeaf 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 externalBlockusers) callsuper().__init__positionally; do not reorder.nameis keyword-only: the formern_controls/control_stateslots are gone (controlisation is explicit —ControlledBlock, §13), and a stale five-positional call must fail loudly rather than shiftname.- 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-runningbuild_vanilla()— re-running would double-append commands to the C++ buffer. Composite-style blocks often callchild.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 toselfdirectly.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.targetis 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
selfand carries the same sympy state. The dagger is applied lazily whenflatten()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;BarrierandGPhaseweigh 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):
Pending symbol replacements (Symbol → Symbol) via the C++
Block.replace_symbolsmethod, which returns a fresh block with renamed params.Pending symbol substitutions (Symbol → float) via the C++
Block.set_symbolsmethod (similar — returns a fresh block).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_symbolsscans the canonical (untransformed) command buffer, so it doesn’t see pendingset_symbols/replace_symbolsqueued on the Python side. Replay them here in the same orderflatten()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_qasm2writes — multiple registers, the whole-registermeasure q -> c;, and the qiskit-extendedqelib1.incnames — 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 noGateTypeof its own and lowers whereverMCZdoes. Acceptsmcx(c0, c1, t)ormcx([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 headlinen_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 otherGateType(mirrorsqx.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 theBranch*classical-control markers, perqx.gate_is_physical) — none represent a physical gate applied to the register. ACompositeBlock’s children are included in the sum sinceflatten()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
QarpEnginerun path inqarp/engines/qarp_engine.py):Transpilerlowers gates outsidetarget_gatesetviabuiltin_decompositions.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),GPhasesums, …), combining across gates on other qubits. Atlevel >= 2also commutation-aware: pairs combine across provably-commuting gates on shared qubits (Rz·CX-control·Rzmerges, matrix-verified commutation table, bounded lookahead).fuse_single_qubit_gatescollapses runs of single-qubit gates on each qubit into a singleCustom2×2 matrix — only when the target admits ``Custom`` (native_gatesetdoes; 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.GateSettarget. Defaults toqx.native_gateset()— the gatesQarpSimulatorcan 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
SimpleBlockholding the optimized command sequence. The receiver is not mutated. The returned block is markedbuiltand itstarget_qubitsis the local[0, n_qubits)frame, ready forflatten()/ engine consumption.- Raises:
RuntimeError – If the block has not been built yet — call
.build()first soflatten()is well-defined.ValueError – If
levelis 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
CompositeBlockis drawn as one box per child sub-block so the plot mirrors how the circuit was composed; passdecompose_boxes=Trueto flatten it into primitive gates instead. Leaf blocks always render their gates.
- 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/daggerare applied viaflatten(). 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 within1e-10—ValueErrorotherwise; 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_qasm2(output: str | None = None) str¶
Emit the block’s circuit as OpenQASM 2.0 text.
The narrower of the two languages: symbolic parameters,
GPhaseandMCZhave no representation and raiseCapabilityError(§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.
- unitary_matrix() ndarray¶
Dense
2^n × 2^nunitary of this block, global phase included.Same guards as
statevector(). Exponential inn_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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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:
_LCUProjectorBlockBlock encoding of the projector onto the S(S+1) spin-squared sector.
- class qarp.blocks.SyProjectorBlock(n_qubits: int, My: float, target_qubits: List[int] | None = None, name: str = 'SyProjector')[source]¶
Bases:
_LCUProjectorBlockBlock 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:
SimpleBlockPattern A leaf: amplitude-encode an arbitrary 2^n-vector into a circuit.
Delegates to
self.state_preparation(...)(qarpx C++ Möttönen synthesis) inbuild_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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_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:
SimpleBlockPattern A leaf: synthesize
U(t) = exp(-i H t)for a Hamiltonian H.Builds the time-evolution unitary numerically via
scipy.linalg.expmand delegates toself.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 toselfdirectly.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:
SimpleBlockPattern A leaf: synthesize an arbitrary 2^n × 2^n unitary into a circuit.
Delegates to
self.unitary_synthesis(...)(qarpx C++ Quantum Shannon Decomposition) inbuild_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 toselfdirectly.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:
_LCUProjectorBlockBlock 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:
SimpleBlockSymbol-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 parameterss_kare the only free parameters at run time.Radians convention: a symbol value
s_kcontributesexp(+i s_k c time P)per Pauli termPwith coefficientc— emitted as acommuting_pauli_set_expangle of-2·c·time/steps · s_kradians (the builder realisesexp(-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 toselfdirectly.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 toselfdirectly.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 toselfdirectly.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 sortedsymbolsregistry — 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
- 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;
Noneif none.Nonemeans every ancilla returns to|0⟩with probability 1 — the block is deterministic, and therefore safe underControlledBlockand safe to hand tovalidate_amplification_blocks. A returned condition must fix exactlyancilla_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 aSamplerresult —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 toselfdirectly.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_exactisTrue(the default0.0here 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) iftarget_statevector()is met exactly.Falsemarks a block whose construction is inherently approximate (a fixed-precision discretization, a truncated low-rank/MPS expansion, …) — the conformance suite then checksprepared_statevector()againsttarget_statevector()by infidelity againsterror_boundrather than byatol=1e-10elementwise equality. Most blocks never touch this; override alongsideerror_bound.
- prepared_statevector() Tuple[ndarray, float]¶
What the built circuit actually leaves on
state_qubits.Returns
(state, probability)— the counterpart totarget_statevector(), which the two are asserted equal against.probabilityis that ofancilla_postselection, and is1.0for a deterministic block. Exponential inn_qubits: a validation tool, not a simulation path.
- 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 toselfdirectly.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 (isinstanceis true) without registering, so a gate over the public surface must also walkissubclass(cls, PreparesKnownState).
- qarp.blocks.prepares_known_state(cls: type) type[source]¶
Attach
PreparesKnownStatetoclsand register it.Members the block defines itself are left alone, so a block overrides
state_qubits/ancilla_postselectionsimply by defining them.Raises
ValueErrorwhen 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.