QURI Parts backend transpiler implementation.
This module provides QuriPartsTranspiler for converting Qamomile QKernels into QURI Parts quantum circuits.
Overview¶
| Function | Description |
|---|---|
emit_controlled_gate | Emit a controlled version of a gate. |
emit_controlled_pauli_evolve | Emit exp(-i * gamma * H) under accumulated controls. |
emit_multi_controlled_gate | Emit one gate under an arbitrary number of accumulated controls. |
evaluate_binop | Evaluate a BinOp and store the result in bindings. |
map_nested_controlled_u_results | Map a nested controlled-U’s result values to physical qubits. |
resolve_controlled_u_call | Resolve a (possibly nested) controlled-U call site to physical qubits. |
resolve_power | Resolve ControlledUOperation.power to a concrete int. |
| Class | Description |
|---|---|
BinOp | Binary arithmetic operation (ADD, SUB, MUL, DIV, FLOORDIV, MOD, POW, MIN). |
CompositeGateOperation | Represents a composite gate (QPE, QFT, etc.) as a single operation. |
ControlledUOperation | Base class for controlled-U operations. |
EmitError | Error during backend code emission. |
EmitPass | Base class for backend-specific emission passes. |
ForOperation | Represents a for loop operation. |
GateOperation | Quantum gate operation. |
GateOperationType | |
InverseBlockOperation | Represent an inverse qkernel/block as a first-class IR operation. |
PauliEvolveOp | Pauli evolution operation: exp(-i * gamma * H). |
QamomileQuriPartsTranspileError | Exception raised for errors in the Qamomile to QURI Parts conversion process. |
QuriPartsEmitPass | QURI Parts-specific emission pass. |
QuriPartsExecutor | QURI Parts quantum executor. |
QuriPartsGateEmitter | GateEmitter implementation for QURI Parts. |
QuriPartsTranspiler | QURI Parts transpiler for qamomile.circuit module. |
ReturnOperation | Explicit return operation marking the end of a block with return values. |
SegmentationPass | Segment a block into a strategy-specific executable program plan. |
StandardEmitPass | Standard emit pass implementation using GateEmitter protocol. |
Transpiler | Base class for backend-specific transpilers. |
Functions¶
emit_controlled_gate [source]¶
def emit_controlled_gate(
emit_pass: 'StandardEmitPass',
circuit: Any,
op: GateOperation,
control_idx: int,
target_indices: list[int],
bindings: dict[str, Any],
) -> NoneEmit a controlled version of a gate.
emit_controlled_pauli_evolve [source]¶
def emit_controlled_pauli_evolve(
emit_pass: 'StandardEmitPass',
circuit: Any,
op: PauliEvolveOp,
control_indices: list[int],
qubit_map: QubitMap,
bindings: dict[str, Any],
) -> NoneEmit exp(-i * gamma * H) under accumulated controls.
Lowers a controlled Pauli evolution by exploiting that each Pauli
term’s evolution exp(-i * theta * P) factors as
U_dagger * RZ(2 * theta) * U where U (the basis change onto
the Z axis followed by the parity CX ladder) is Clifford and thus
control-independent. Controlling a conjugated gate is
U_dagger * C[RZ] * U with U / U_dagger applied
unconditionally: when every control is 0 the central RZ
becomes the identity and U_dagger * I * U = I. Only the central
RZ therefore carries the controls, which keeps the dense
multi-controlled cost to a single rotation per Hamiltonian term
instead of controlling every basis-change and ladder gate.
The basis-change (H / SDG / S) and CX-ladder gates are
emitted uncontrolled through emit_pass._emitter; the central
RZ is routed through :func:_emit_mc_rotation, which dispatches
to emit_crz for a single control and to the backend’s
_emit_irreducible_multi_controlled_gate hook for two or more.
A constant (identity) Hamiltonian term c * I is a global phase
exp(-i * gamma * c) for the uncontrolled evolution (correctly
dropped by :func:emit_pauli_evolve), but under controls it becomes
an observable relative phase on the all-controls-on subspace, so it
MUST be emitted here. It is realized as a P(-gamma * c) on one
control conditioned on the remaining controls (emit_p for a single
control, a controlled / dense P for more), matching Qiskit’s
native PauliEvolutionGate whose SparsePauliOp carries the
constant.
Parameters:
| Name | Type | Description |
|---|---|---|
emit_pass | StandardEmitPass | Active emit pass; provides the value resolver, gate emitter, and the multi-controlled rotation hook. |
circuit | Any | Backend circuit being emitted into. |
op | PauliEvolveOp | The Pauli evolution operation inside the controlled block. |
control_indices | list[int] | Accumulated physical control qubits. Must be non-empty. |
qubit_map | QubitMap | Block-local qubit map the operation’s quantum operands resolve against; updated in place with the evolved-register result addresses. |
bindings | dict[str, Any] | Bindings visible inside the block, used to resolve the Hamiltonian, gamma, and register shape. |
Raises:
EmitError— Ifcontrol_indicesis empty, the observable does not resolve to a Hamiltonian, gamma cannot be resolved, the Hamiltonian is non-Hermitian (a term or the constant has a non-real coefficient), the register size does not match the Hamiltonian, a term qubit cannot be resolved, or (for two or more controls, or a nonzero constant term) the angle is runtime-parametric and the backend’s dense multi-controlled path requires a compile-time-numeric angle.
emit_multi_controlled_gate [source]¶
def emit_multi_controlled_gate(
emit_pass: 'StandardEmitPass',
circuit: Any,
op: GateOperation,
control_indices: list[int],
target_indices: list[int],
bindings: dict[str, Any],
) -> NoneEmit one gate under an arbitrary number of accumulated controls.
Structurally reduces multi-qubit gate types to single-target forms
by absorbing their own control qubits into the control set
(CX -> X, CZ -> Z, CP -> P, TOFFOLI -> X; SWAP
and RZZ via their standard CX conjugations), then emits:
one control: via :func:
emit_controlled_gate(the existing single-control dispatch),two controls on X / Z: via
emit_toffoli(Z conjugated by H),anything else: via the backend’s
_emit_irreducible_multi_controlled_gatehook, whose base implementation raises a descriptiveEmitError.
Parameters:
| Name | Type | Description |
|---|---|---|
emit_pass | StandardEmitPass | Active emit pass. Subclass overrides of _emit_irreducible_multi_controlled_gate are respected for the irreducible tail. |
circuit | Any | Backend circuit being emitted into. |
op | GateOperation | Gate operation whose operands are already resolved to target_indices. |
control_indices | list[int] | Physical control qubits. Must be non-empty. |
target_indices | list[int] | Physical qubits for the gate’s own operands, in operand order. |
bindings | dict[str, Any] | Bindings used to resolve rotation angles. |
Raises:
EmitError— Ifcontrol_indicesis empty, the gate has fewer resolved targets than its type requires, or the reduction bottoms out on a backend without multi-control support.
evaluate_binop [source]¶
def evaluate_binop(emit_pass: 'StandardEmitPass', op: BinOp, bindings: dict[str, Any]) -> NoneEvaluate a BinOp and store the result in bindings.
Tries the shared fold_classical_op first for a clean concrete
fold (which already encapsulates the runtime-parameter guard).
Falls back to creating backend Parameter symbols and doing
symbolic arithmetic when one or both operands are runtime
parameters — that’s the path that lets rx(q, gamma * 2) produce
a circuit with a single Parameter("gamma") * 2 expression rather
than baking in a placeholder.
map_nested_controlled_u_results [source]¶
def map_nested_controlled_u_results(
op: ControlledUOperation,
resolved: ResolvedControlledU,
qubit_map: QubitMap,
) -> NoneMap a nested controlled-U’s result values to physical qubits.
The result layout mirrors the operand layout for every controlled-U
shape: one result per control operand slot followed by one result
per sub-kernel quantum operand. Controlled gates only add a
relative phase — no qubit moves — so each result keeps the physical
slots of its operand. For the control_indices pool form the
whole pool passes through, selected controls included.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ControlledUOperation | The operation whose results need mapping. |
resolved | ResolvedControlledU | Physical resolution returned by :func:resolve_controlled_u_call for this operation. |
qubit_map | QubitMap | Mutable qubit map updated in place. |
resolve_controlled_u_call [source]¶
def resolve_controlled_u_call(
emit_pass: 'StandardEmitPass',
op: ControlledUOperation,
qubit_map: QubitMap,
bindings: dict[str, Any],
) -> ResolvedControlledUResolve a (possibly nested) controlled-U call site to physical qubits.
Handles all three operand layouts:
ConcreteControlledU: one scalar operand per control qubit.SymbolicControlledUwithcontrol_indices is None: a control prefix ofnum_control_argsscalar / vector operands whose flattened qubit count equals the resolvednum_controls.SymbolicControlledUwithcontrol_indicesset: a single control pool whose listed elements act as controls while the rest pass through.
Parameters:
| Name | Type | Description |
|---|---|---|
emit_pass | StandardEmitPass | Active emit pass; provides the value resolver. |
op | ControlledUOperation | The controlled-U operation to resolve. |
qubit_map | QubitMap | Qubit map the operands resolve against (block-local when called from a controlled walker). |
bindings | dict[str, Any] | Bindings visible at the call site, including loop-iteration values during unrolling. |
Returns:
ResolvedControlledU — Physical controls / targets and the
inner-block bindings.
Raises:
EmitError— If the operation has no inner block,num_controlsor acontrol_indicesentry cannot be resolved, indices repeat or fall outside the pool, the flattened control count does not matchnum_controls, or an operand cannot be expanded to physical qubits.
resolve_power [source]¶
def resolve_power(
emit_pass: 'StandardEmitPass',
op: ControlledUOperation,
bindings: dict[str, Any],
) -> intResolve ControlledUOperation.power to a concrete int.
Classes¶
BinOp [source]¶
class BinOp(BinaryOperationBase)Binary arithmetic operation (ADD, SUB, MUL, DIV, FLOORDIV, MOD, POW, MIN).
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
kind: BinOpKind | None = None,
) -> NoneAttributes¶
kind: BinOpKind | Noneoperation_kind: OperationKindsignature: Signature
CompositeGateOperation [source]¶
class CompositeGateOperation(Operation)Represents a composite gate (QPE, QFT, etc.) as a single operation.
CompositeGate allows representing complex multi-gate operations as a single atomic operation in the IR. This enables:
Resource estimation without full implementation
Backend-native conversion (e.g., Qiskit’s QPE)
User-defined complex gates
The operands structure is:
operands[0:num_control_qubits]: Control qubits (if any)
operands[num_control_qubits:num_control_qubits+num_target_qubits]: Target qubits
operands[num_control_qubits+num_target_qubits:]: Parameters
The results structure:
results[0:num_control_qubits]: Control qubits (returned)
results[num_control_qubits:]: Target qubits (returned)
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
gate_type: CompositeGateType = CompositeGateType.CUSTOM,
num_control_qubits: int = 0,
num_target_qubits: int = 0,
custom_name: str = '',
resource_metadata: ResourceMetadata | None = None,
has_implementation: bool = True,
implementation_block: Block | None = None,
composite_gate_instance: Any = None,
strategy_name: str | None = None,
) -> NoneAttributes¶
composite_gate_instance: Anycontrol_qubits: list[‘Value’] Get the control qubit operands.custom_name: strgate_type: CompositeGateTypehas_implementation: boolimplementation: Block | None Get the implementation block, if any.implementation_block: Block | Nonename: str Human-readable name of this composite gate.num_control_qubits: intnum_target_qubits: intoperation_kind: OperationKind Return the operation kind (always QUANTUM).parameters: list[‘Value’] Get the parameter operands (angles, etc.).resource_metadata: ResourceMetadata | Nonesignature: Signature Return the operation signature.strategy_name: str | Nonetarget_qubits: list[‘Value’] Get the target qubit operands.
ControlledUOperation [source]¶
class ControlledUOperation(Operation)Base class for controlled-U operations.
Two concrete subclasses handle distinct operand layouts:
ConcreteControlledU: Fixednum_controls: int, individual qubit operands.SymbolicControlledU: Symbolicnum_controls: Value, vector-based control operands; optionalcontrol_indicesselects a subset of the control vector to act as controls (the rest pass through).
All isinstance(op, ControlledUOperation) checks match every subclass.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: int | Value = 1,
) -> NoneAttributes¶
block: Block | Nonecontrol_operands: list[Value] Get the control qubit values.is_symbolic_num_controls: bool Whether num_controls is symbolic (Value) rather than concrete.num_controls: int | Valueoperation_kind: OperationKindparam_operands: list[Value] Get parameter operands (non-qubit, non-block).power: int | Valuesignature: Signaturetarget_operands: list[Value] Get the target qubit values (arguments to U).
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationEmitError [source]¶
class EmitError(QamomileCompileError)Error during backend code emission.
Constructor¶
def __init__(self, message: str, operation: str | None = None)Attributes¶
operation
EmitPass [source]¶
class EmitPass(Pass[ProgramPlan, ExecutableProgram[T]], Generic[T])Base class for backend-specific emission passes.
Subclasses implement _emit_quantum_segment() to generate backend-specific quantum circuits.
Input: ProgramPlan Output: ExecutableProgram with compiled segments
Constructor¶
def __init__(
self,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
)Initialize with optional parameter bindings.
Parameters:
| Name | Type | Description |
|---|---|---|
bindings | dict[str, Any] | None | Values to bind parameters to. If not provided, parameters must be bound at execution time. |
parameters | list[str] | None | List of parameter names to preserve as backend parameters. |
Attributes¶
bindingsname: strparameters
Methods¶
run¶
def run(self, input: ProgramPlan) -> ExecutableProgram[T]Emit backend code from a program plan.
ForOperation [source]¶
class ForOperation(HasNestedOps, Operation)Represents a for loop operation.
Example:
for i in range(start, stop, step):
bodyConstructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
loop_var: str = '',
loop_var_value: Value | None = None,
operations: list[Operation] = list(),
) -> NoneAttributes¶
loop_var: strloop_var_value: Value | Noneoperation_kind: OperationKindoperations: list[Operation]signature: Signature
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Include loop_var_value so cloning/substitution stays consistent.
Without this override, UUIDRemapper would clone every body
reference to the loop variable to a fresh UUID, but leave
loop_var_value pointing at the un-cloned original — emit-time
UUID-keyed lookups for the loop variable would then miss.
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> Operationreplace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationGateOperation [source]¶
class GateOperation(Operation)Quantum gate operation.
For rotation gates (RX, RY, RZ, P, CP, RZZ), the angle parameter is
stored as the last element of operands. Use the theta
property for typed read access and the rotation / fixed factory
class-methods for type-safe construction.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
gate_type: GateOperationType | None = None,
) -> NoneAttributes¶
gate_type: GateOperationType | Noneoperation_kind: OperationKindqubit_operands: list[Value] Qubit operands (excluding the theta parameter if present).signature: Signaturetheta: Value | None Angle parameter for rotation gates, orNonefor fixed gates.
Methods¶
fixed¶
@classmethod
def fixed(
cls,
gate_type: GateOperationType,
qubits: list[Value],
results: list[Value],
) -> 'GateOperation'Create a fixed gate (H, X, CX, SWAP, …) with no angle parameter.
rotation¶
@classmethod
def rotation(
cls,
gate_type: GateOperationType,
qubits: list[Value],
theta: Value,
results: list[Value],
) -> 'GateOperation'Create a rotation gate (RX, RY, RZ, P, CP, RZZ) with an angle.
GateOperationType [source]¶
class GateOperationType(enum.Enum)Attributes¶
CPCXCZHPRXRYRZRZZSSDGSWAPTTDGTOFFOLIXYZ
InverseBlockOperation [source]¶
class InverseBlockOperation(Operation)Represent an inverse qkernel/block as a first-class IR operation.
The operation stores both the original forward block and a Qamomile-built
inverse implementation block. Emitters may use source_block with a
backend-native inverse/adjoint primitive, then fall back to
implementation_block when native inversion is unavailable.
Operands are ordered as control qubits, target quantum operands, then
classical/object parameters. Results mirror the quantum operand layout:
control results first, then one target result per target operand. Vector
target operands therefore count as one operand/result while contributing
their scalar width to num_target_qubits.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
num_control_qubits: int = 0,
num_target_qubits: int = 0,
custom_name: str = '',
source_block: Block | None = None,
implementation_block: Block | None = None,
) -> NoneAttributes¶
control_qubits: list[‘Value’] Return control quantum operands.custom_name: strimplementation_block: Block | Nonename: str Return a human-readable inverse operation name.num_control_qubits: intnum_target_qubits: intoperation_kind: OperationKind Return the operation kind.parameters: list[‘Value’] Return classical/object parameter operands.signature: Signature Return the operation signature.source_block: Block | Nonetarget_qubits: list[‘Value’] Return target quantum operands.
PauliEvolveOp [source]¶
class PauliEvolveOp(Operation)Pauli evolution operation: exp(-i * gamma * H).
This operation applies the time evolution of a Pauli Hamiltonian to a quantum register.
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
evolved_qubits: Value The evolved quantum register result.gamma: Value The evolution time parameter.observable: Value The Observable parameter operand.operation_kind: OperationKind PauliEvolveOp is QUANTUM - transforms quantum state.qubits: Value The quantum register operand.signature: Signature
QamomileQuriPartsTranspileError [source]¶
class QamomileQuriPartsTranspileError(QamomileCompileError)Exception raised for errors in the Qamomile to QURI Parts conversion process.
QuriPartsEmitPass [source]¶
class QuriPartsEmitPass(StandardEmitPass['qp_c.LinearMappedUnboundParametricQuantumCircuit'])QURI Parts-specific emission pass.
Uses StandardEmitPass with QuriPartsGateEmitter for gate emission. QURI Parts does not support native control flow, so all loops are unrolled.
Constructor¶
def __init__(
self,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
)Initialize the QURI Parts emit pass.
Parameters:
| Name | Type | Description |
|---|---|---|
bindings | dict[str, Any] | None | Parameter bindings for the circuit |
parameters | list[str] | None | List of parameter names to preserve as backend parameters |
QuriPartsExecutor [source]¶
class QuriPartsExecutor(QuantumExecutor['qp_c.LinearMappedUnboundParametricQuantumCircuit'])QURI Parts quantum executor.
Supports both sampling and expectation value estimation. Uses Qulacs backend by default for efficient simulation.
Example:
executor = QuriPartsExecutor() # Uses Qulacs by default
counts = executor.execute(circuit, shots=1000)
# counts: {"00": 512, "11": 512}Constructor¶
def __init__(self, sampler: Any = None, estimator: Any = None, seed: int | None = None)Initialize executor with optional sampler, estimator, and seed.
Parameters:
| Name | Type | Description |
|---|---|---|
sampler | Any | QURI Parts sampler. Defaults to None, meaning a qulacs vector sampler is created lazily on first use. |
estimator | Any | QURI Parts parametric estimator. Defaults to None, meaning a qulacs parametric estimator is created lazily on first use. |
seed | int | None | Optional random seed forwarded to the qulacs vector sampler so that sampling is reproducible. When set, two execute calls with the same seed and circuit return identical shot counts, which unblocks reproducible tutorials and benchmarks. Defaults to None, meaning sampling is non-deterministic. The seed is only applied to the default qulacs sampler; it is ignored when a custom sampler is supplied, since an arbitrary sampler has no standard seed interface. |
Attributes¶
non_parametric_estimator: Any Lazy initialization of non-parametric estimator.parametric_estimator: Any Lazy initialization of parametric estimator for optimization.sampler: Any Lazy initialization of sampler.
Methods¶
bind_parameters¶
def bind_parameters(
self,
circuit: 'qp_c.LinearMappedUnboundParametricQuantumCircuit',
bindings: dict[str, Any],
parameter_metadata: ParameterMetadata,
) -> 'ImmutableBoundParametricQuantumCircuit'Bind parameter values to the QURI Parts circuit.
QURI Parts requires parameter values as a sequence in the order parameters were added to the circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
circuit | 'qp_c.LinearMappedUnboundParametricQuantumCircuit' | The unbound parametric circuit |
bindings | dict[str, Any] | Dictionary of parameter name to value |
parameter_metadata | ParameterMetadata | Metadata about the parameters |
Returns:
'ImmutableBoundParametricQuantumCircuit' — Bound parametric circuit
Raises:
QamomileQuriPartsTranspileError— If a required parameter binding is missing frombindings.
estimate¶
def estimate(
self,
circuit: 'qp_c.LinearMappedUnboundParametricQuantumCircuit',
hamiltonian: 'qm_o.Hamiltonian',
params: Sequence[float] | None = None,
) -> floatEstimate expectation value <psi|H|psi>.
Also accepts a native quri_parts.core.operator.Operator at
runtime for convenience (auto-conversion is skipped in that case).
Parameters:
| Name | Type | Description |
|---|---|---|
circuit | 'qp_c.LinearMappedUnboundParametricQuantumCircuit' | The unbound parametric circuit (state preparation ansatz) |
hamiltonian | 'qm_o.Hamiltonian' | Hamiltonian to measure (qamomile Hamiltonian or native QURI Parts Operator at runtime) |
params | Sequence[float] | None | Parameter values for the parametric circuit |
Returns:
float — Real part of the expectation value
estimate_expectation¶
def estimate_expectation(
self,
circuit: 'qp_c.LinearMappedUnboundParametricQuantumCircuit',
hamiltonian: 'qp_o.Operator',
param_values: Sequence[float],
) -> floatEstimate expectation value of hamiltonian for a circuit.
Handles both unbound parametric circuits (used during optimization)
and already-bound or non-parametric circuits. When the circuit has
already been bound (e.g., by _run_expval in ExecutableProgram),
apply_circuit produces a GeneralCircuitQuantumState instead
of a ParametricCircuitQuantumState, so we dispatch to the
non-parametric estimator automatically.
Parameters:
| Name | Type | Description |
|---|---|---|
circuit | 'qp_c.LinearMappedUnboundParametricQuantumCircuit' | The quantum circuit (unbound parametric or bound/concrete) |
hamiltonian | 'qp_o.Operator' | QURI Parts Operator representing the Hamiltonian |
param_values | Sequence[float] | Sequence of parameter values in order (ignored for bound/non-parametric circuits) |
Returns:
float — Real part of the expectation value
execute¶
def execute(self, circuit: Any, shots: int) -> dict[str, int]Execute circuit and return bitstring counts.
Parameters:
| Name | Type | Description |
|---|---|---|
circuit | Any | The quantum circuit to execute (bound or unbound) |
shots | int | Number of measurement shots |
Returns:
dict[str, int] — Dictionary mapping bitstrings to counts (e.g., {“00”: 512, “11”: 512})
QuriPartsGateEmitter [source]¶
class QuriPartsGateEmitterGateEmitter implementation for QURI Parts.
Emits individual quantum gates to QURI Parts circuits.
QURI Parts parametric circuits accept angles in dictionary form: {parameter: coefficient, CONST: constant_offset}
Constructor¶
def __init__(self) -> NoneInitialize the emitter.
Attributes¶
measurement_mode: MeasurementMode QURI Parts always uses STATIC measurement (sampler handles it).
Methods¶
append_gate¶
def append_gate(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
gate: Any,
qubits: list[int],
) -> NoneAppend a gate to the circuit.
Since circuit_to_gate returns None, this is only called with QURI Parts native gates which can be extended into the circuit.
circuit_to_gate¶
def circuit_to_gate(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
name: str = 'U',
) -> AnyQURI Parts doesn’t support converting circuits to gates.
Returns None to signal that manual decomposition should be used.
combine_symbolic¶
def combine_symbolic(self, kind: BinOpKind, lhs: Any, rhs: Any) -> 'dict[Parameter, float]'Combine two angle operands as a QURI Parts linear-combination dict.
QURI Parts’ Parameter is Rust-backed and exposes no Python
arithmetic operators (param * float raises TypeError).
The only supported representation for parametric angles is the
linear-combination dict consumed by
LinearMappedUnboundParametricQuantumCircuit.add_Parametric*,
which fundamentally cannot express non-linear combinations
(parameter × parameter, parameter ** n, etc.). This override
produces such a dict for every linear case and raises a clear
QamomileQuriPartsTranspileError for non-linear cases instead
of letting the underlying TypeError bubble up.
Parameters:
| Name | Type | Description |
|---|---|---|
kind | BinOpKind | The BinOpKind from the IR. |
lhs | Any | Left operand (numeric, QURI Parts Parameter, or an existing linear-combination dict). |
rhs | Any | Right operand (same shapes). |
Returns:
'dict[Parameter, float]' — A fresh dict[Parameter, float] representing the linear
'dict[Parameter, float]' — combination of the operands.
Raises:
QamomileQuriPartsTranspileError— When the operation cannot be expressed as a linear combination (e.g.param * param,param / param,param ** n,param // n, division by zero in the symbolic path).
create_circuit¶
def create_circuit(
self,
num_qubits: int,
num_clbits: int,
) -> 'LinearMappedUnboundParametricQuantumCircuit'Create a new QURI Parts parametric circuit.
Note: QURI Parts does not support classical bits in circuits. The num_clbits parameter is accepted for interface compatibility but is not used.
create_parameter¶
def create_parameter(self, name: str) -> 'Parameter'Create a QURI Parts parameter and register it with the circuit.
emit_barrier¶
def emit_barrier(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubits: list[int],
) -> NoneNo-op: QURI Parts doesn’t support barrier instructions.
emit_ch¶
def emit_ch(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
) -> NoneEmit controlled-Hadamard gate using decomposition.
Follows the shared CH_DECOMPOSITION recipe from
qamomile.circuit.transpiler.decompositions.
emit_cp¶
def emit_cp(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-Phase gate using decomposition.
Follows the shared CP_DECOMPOSITION recipe from
qamomile.circuit.transpiler.decompositions.
Inlined here because QURI Parts parametric angles use dict representation.
emit_crx¶
def emit_crx(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-RX gate using decomposition.
CRX(ctrl, tgt, θ) = RZ(tgt, π/2) CNOT(ctrl, tgt) RY(tgt, -θ/2) CNOT(ctrl, tgt) RY(tgt, θ/2) RZ(tgt, -π/2)
emit_cry¶
def emit_cry(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-RY gate using decomposition.
Follows the shared CRY_DECOMPOSITION recipe from
qamomile.circuit.transpiler.decompositions.
Inlined here because QURI Parts parametric angles use dict representation.
emit_crz¶
def emit_crz(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-RZ gate using decomposition.
Follows the shared CRZ_DECOMPOSITION recipe from
qamomile.circuit.transpiler.decompositions.
Inlined here because QURI Parts parametric angles use dict representation.
emit_cx¶
def emit_cx(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
) -> NoneEmit CNOT (controlled-X) gate.
emit_cy¶
def emit_cy(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
) -> NoneEmit controlled-Y gate using decomposition.
Follows the shared CY_DECOMPOSITION recipe from
qamomile.circuit.transpiler.decompositions.
emit_cz¶
def emit_cz(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
) -> NoneEmit controlled-Z gate.
emit_h¶
def emit_h(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit Hadamard gate.
emit_measure¶
def emit_measure(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
clbit: int,
) -> NoneNo-op: QURI Parts circuits don’t support measurement gates.
Measurement is handled separately by samplers/estimators.
emit_p¶
def emit_p(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
angle: float | Any,
) -> NoneEmit Phase gate using U1.
P(θ) = U1(θ) = diag(1, e^{iθ}). For non-parametric angles we use the native U1 gate which is mathematically identical to the Phase gate. For parametric angles we fall back to ParametricRZ because QURI Parts does not provide a ParametricU1 gate. RZ differs only by a global phase: P(θ) = e^{iθ/2} · RZ(θ), which is physically irrelevant for single-qubit usage. Controlled-phase (CP) has its own decomposition and does not go through this path.
emit_rx¶
def emit_rx(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
angle: float | Any,
) -> NoneEmit RX rotation gate.
emit_ry¶
def emit_ry(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
angle: float | Any,
) -> NoneEmit RY rotation gate.
emit_rz¶
def emit_rz(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
angle: float | Any,
) -> NoneEmit RZ rotation gate.
emit_rzz¶
def emit_rzz(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit1: int,
qubit2: int,
angle: float | Any,
) -> NoneEmit RZZ gate using ParametricPauliRotation.
emit_s¶
def emit_s(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit S (phase) gate.
emit_sdg¶
def emit_sdg(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit S-dagger (inverse S) gate.
emit_swap¶
def emit_swap(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit1: int,
qubit2: int,
) -> NoneEmit SWAP gate.
emit_t¶
def emit_t(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit T gate.
emit_tdg¶
def emit_tdg(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit T-dagger (inverse T) gate.
emit_toffoli¶
def emit_toffoli(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control1: int,
control2: int,
target: int,
) -> NoneEmit Toffoli (CCX) gate.
emit_x¶
def emit_x(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit Pauli-X gate.
emit_y¶
def emit_y(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit Pauli-Y gate.
emit_z¶
def emit_z(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
qubit: int,
) -> NoneEmit Pauli-Z gate.
gate_controlled¶
def gate_controlled(self, gate: Any, num_controls: int) -> AnyCreate controlled version of a gate.
Returns None since QURI Parts doesn’t support this natively.
gate_inverse¶
def gate_inverse(self, gate: Any) -> AnyReturn the inverse of a concrete QURI Parts circuit.
Parameters:
| Name | Type | Description |
|---|---|---|
gate | Any | Candidate QURI Parts circuit to invert. Runtime parametric circuits are expected to fall back to Qamomile’s gate-by-gate inverse implementation. |
Returns:
Any — Inverted QURI Parts circuit when inverse_circuit can
handle gate; otherwise None so callers can fall back to
Qamomile-level decomposition.
gate_power¶
def gate_power(self, gate: Any, power: int) -> AnyCreate gate raised to a power.
Returns None since QURI Parts doesn’t support this natively.
supports_for_loop¶
def supports_for_loop(self) -> boolQURI Parts does not support native for loops.
supports_gate_inverse¶
def supports_gate_inverse(self) -> boolReport QURI Parts circuit inverse support.
Returns:
bool — True because gate_inverse can invert concrete
bool — QURI Parts circuits. The backend still reports no reusable
bool — gate support, so inverse blocks normally use the
bool — transpiler-level native path rather than the shared
bool — blockvalue_to_gate path.
supports_if_else¶
def supports_if_else(self) -> boolQURI Parts does not support native if/else.
supports_while_loop¶
def supports_while_loop(self) -> boolQURI Parts does not support native while loops.
QuriPartsTranspiler [source]¶
class QuriPartsTranspiler(Transpiler['qp_c.LinearMappedUnboundParametricQuantumCircuit'])QURI Parts transpiler for qamomile.circuit module.
Converts Qamomile QKernels into QURI Parts quantum circuits.
Example:
from qamomile.quri_parts import QuriPartsTranspiler
import qamomile.circuit as qm
@qm.qkernel
def bell_state(q0: qm.Qubit, q1: qm.Qubit) -> tuple[qm.Bit, qm.Bit]:
q0 = qm.h(q0)
q0, q1 = qm.cx(q0, q1)
return qm.measure(q0), qm.measure(q1)
transpiler = QuriPartsTranspiler()
circuit = transpiler.to_circuit(bell_state)Methods¶
executor¶
def executor(
self,
sampler: Any = None,
estimator: Any = None,
seed: int | None = None,
) -> QuriPartsExecutorCreate a QURI Parts executor.
Parameters:
| Name | Type | Description |
|---|---|---|
sampler | Any | Optional custom sampler. Defaults to None, meaning the qulacs vector sampler is used. |
estimator | Any | Optional custom estimator. Defaults to None, meaning the qulacs parametric estimator is used. |
seed | int | None | Optional random seed forwarded to the qulacs vector sampler for reproducible sampling. Defaults to None, meaning sampling is non-deterministic. The seed is ignored when a custom sampler is supplied, since an arbitrary sampler has no standard seed interface. |
Returns:
QuriPartsExecutor — Executor configured for this backend, bound
to the given seed.
ReturnOperation [source]¶
class ReturnOperation(Operation)Explicit return operation marking the end of a block with return values.
This operation represents an explicit return statement in the IR. It takes the values to be returned as operands and produces no results (it is a terminal operation that transfers control flow back to the caller).
operands: [Value, ...] - The values to return (may be empty for void returns) results: [] - Always empty (terminal operation)
Example:
A function that returns two values (a UInt and a Float):
ReturnOperation(
operands=[uint_value, float_value],
results=[],
)
The signature would be:
operands=[ParamHint("return_0", UIntType()), ParamHint("return_1", FloatType())]
results=[]Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Return CLASSICAL as this is a control flow operation without quantum effects.signature: Signature Return the signature with operands for each return value and no results.
SegmentationPass [source]¶
class SegmentationPass(Pass[Block, ProgramPlan])Segment a block into a strategy-specific executable program plan.
This pass:
Materializes return operations (syncs output_values from ReturnOperation)
Splits the operation list into quantum and classical segments
Builds a ProgramPlan via the configured segmentation strategy
Input: Block (typically ANALYZED or AFFINE) Output: ProgramPlan
Constructor¶
def __init__(self, strategy: SegmentationStrategy | None = None) -> NoneAttributes¶
name: str
Methods¶
run¶
def run(self, input: Block) -> ProgramPlanSegment the block into a ProgramPlan.
StandardEmitPass [source]¶
class StandardEmitPass(EmitPass[T], Generic[T])Standard emit pass implementation using GateEmitter protocol.
This class provides the orchestration logic for circuit emission while delegating backend-specific operations to a GateEmitter.
Subclasses (QiskitEmitPass, CudaqEmitPass) override specific methods
to provide native backend support. The thin wrappers here delegate to
module functions in emit_support/ by default.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_emitter | GateEmitter[T] | Backend-specific gate emitter |
bindings | dict[str, Any] | None | Parameter bindings for the circuit |
parameters | list[str] | None | List of parameter names to preserve as backend parameters |
composite_emitters | list[CompositeGateEmitter[T]] | None | Optional list of CompositeGateEmitter for native implementations |
Constructor¶
def __init__(
self,
gate_emitter: GateEmitter[T],
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
composite_emitters: list[CompositeGateEmitter[T]] | None = None,
)Transpiler [source]¶
class Transpiler(ABC, Generic[T])Base class for backend-specific transpilers.
Provides the full compilation pipeline from QKernel to executable program.
Usage:
transpiler = QiskitTranspiler()
Option 1: Full pipeline¶
executable = transpiler.compile(kernel, bindings={“theta”: 0.5}) results = executable.run(transpiler.executor())
Option 2: Step-by-step¶
block = transpiler.to_block(kernel) substituted = transpiler.substitute(block) affine = transpiler.inline(substituted) validated = transpiler.affine_validate(affine) folded = transpiler.constant_fold(validated, bindings={“theta”: 0.5}) analyzed = transpiler.analyze(folded) plan = transpiler.plan(analyzed) executable = transpiler.emit(plan, bindings={“theta”: 0.5})
Option 3: Just get the circuit (no execution)¶
circuit = transpiler.to_circuit(kernel, bindings={“theta”: 0.5})
With configuration (strategy overrides)¶
config = TranspilerConfig.with_strategies({“qft”: “approximate”}) transpiler = QiskitTranspiler(config=config)
Attributes¶
MAX_UNROLL_DEPTH: intconfig: TranspilerConfig Get the transpiler configuration.
Methods¶
affine_validate¶
def affine_validate(self, block: Block) -> BlockPass 1.5: Validate affine type semantics.
This is a safety net to catch affine type violations that may have bypassed frontend checks. Validates that quantum values are used at most once.
analyze¶
def analyze(self, block: Block) -> BlockPass 2: Validate and analyze dependencies.
classical_lowering¶
def classical_lowering(self, block: Block) -> BlockPass 2.25: Lower measurement-derived classical ops.
Identifies CompOp / CondOp / NotOp / BinOp
instances whose operand dataflow traces back to a measurement and
rewrites them to RuntimeClassicalExpr. Compile-time-foldable
and emit-time-foldable (loop-bound, parameter-bound) classical
ops are left unchanged.
Runs after analyze so the measurement-taint analysis has the
full dependency graph available, and before
validate_symbolic_shapes / plan / emit so downstream
passes can rely on the cleaner IR (in particular: future
segmentation work can dispatch on RuntimeClassicalExpr type
instead of the BitType-only heuristic).
constant_fold¶
def constant_fold(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.5: Fold constant expressions.
Evaluates BinOp operations when all operands are constants
or bound parameters. This prevents quantum segment splitting
from parametric expressions like phase * 2.
emit¶
def emit(
self,
separated: ProgramPlan,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> ExecutableProgram[T]Pass 4: Generate backend-specific code.
Parameters:
| Name | Type | Description |
|---|---|---|
separated | ProgramPlan | The separated program to emit |
bindings | dict[str, Any] | None | Parameter values to bind at compile time |
parameters | list[str] | None | Parameter names to preserve as backend parameters |
executor¶
def executor(self, **kwargs: Any = {}) -> QuantumExecutor[T]Create a quantum executor for this backend.
inline¶
def inline(self, block: Block) -> BlockPass 1: Inline all CallBlockOperations.
lower_compile_time_ifs¶
def lower_compile_time_ifs(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.75: Lower compile-time resolvable IfOperations.
Evaluates IfOperation conditions (including expression-derived conditions via CompOp/CondOp/NotOp) and replaces resolved ones with selected-branch operations. Phi outputs are substituted with selected-branch values throughout the block.
This prevents SegmentationPass from seeing classical-only compile-time IfOperations that would otherwise split quantum segments.
partial_eval¶
def partial_eval(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.75: Fold constants and lower compile-time control flow.
plan¶
def plan(self, block: Block) -> ProgramPlanPass 3: Lower and split into a program plan.
Validates C→Q→C pattern with single quantum segment.
resolve_parameter_shapes¶
def resolve_parameter_shapes(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 0.75: Resolve symbolic Vector parameter shape dims.
Qamomile circuits are compile-time fixed-structure. Parameter
Vector[Float] / Vector[UInt] inputs carry symbolic
{name}_dim{i} shape Values so frontend code like
arr.shape[0] returns a usable handle. This pass looks at
bindings and, for every parameter array that has a concrete
binding, substitutes those symbolic dims with constants so that
downstream loop-bound resolution sees fixed lengths.
Parameters without a concrete binding are left as-is; their symbolic dims are harmless as long as no compile-time structure decision depends on them (the library QAOA pattern).
set_config¶
def set_config(self, config: TranspilerConfig) -> NoneSet the transpiler configuration.
Parameters:
| Name | Type | Description |
|---|---|---|
config | TranspilerConfig | Transpiler configuration to use |
slice_borrow_check¶
def slice_borrow_check(self, block: Block) -> BlockPass 1.9: Post-fold slice-view linearity checker.
Runs after :meth:partial_eval has resolved slice bounds to
concrete values. Catches the slice-view linearity violations
that the trace-time frontend check cannot detect on its own —
specifically, slices whose bounds were symbolic at trace
time (so the frontend bulk-borrow tracker had to skip them)
and aliasing scenarios that only become visible once those
bounds are folded to constants:
A view whose newly-concrete coverage overlaps another live view of the same root parent.
A view whose newly-concrete coverage hits a slot that was consumed by a destructive view operation earlier in the block.
A view that reaches the end of the block while still recorded as the owner of the parent’s slots (i.e. it was never used or never released).
Direct element borrows (q[i]) emit no IR operation, so the
IR-level pass cannot observe them; the trace-time validation
in :func:func_to_block._validate_returned_arrays covers that
path.
The pass is a pass-through for the IR — it only raises on violations and leaves the block unchanged on success.
strip_slice_ops¶
def strip_slice_ops(self, block: Block) -> BlockPass 1.95: Remove SliceArrayOperation nodes from the block.
PartialEvaluationPass keeps these declarative ops through
constant folding so :meth:slice_borrow_check can use them
as view-declaration markers. Once the linearity check has run,
segmentation and downstream passes expect a classical-op-free
quantum stream — this pass performs that cleanup.
substitute¶
def substitute(self, block: Block) -> BlockPass 0.5: Apply substitutions (optional).
This pass replaces CallBlockOperation targets and sets strategy names on CompositeGateOperations based on config.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | Block to transform |
Returns:
Block — Block with substitutions applied
to_block¶
def to_block(
self,
kernel: QKernel,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> BlockConvert a QKernel to a Block.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to convert |
bindings | dict[str, Any] | None | Concrete values to bind at trace time (resolves array shapes) |
parameters | list[str] | None | Names to keep as unbound parameters |
When bindings or parameters are provided, uses kernel.build() to properly resolve array shapes from the bound data. Otherwise uses the cached hierarchical block for efficiency.
to_circuit¶
def to_circuit(self, kernel: QKernel, bindings: dict[str, Any] | None = None) -> TCompile and extract just the quantum circuit.
This is a convenience method for when you just want the backend circuit without the full executable.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to compile |
bindings | dict[str, Any] | None | Parameter values to bind |
Returns:
T — Backend-specific quantum circuit
transpile¶
def transpile(
self,
kernel: QKernel,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> ExecutableProgram[T]Full compilation pipeline from QKernel to executable.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to compile |
bindings | dict[str, Any] | None | Parameter values to bind (also resolves array shapes). Names in bindings and parameters must be disjoint — a name is either compile-time bound or runtime symbolic, never both. |
parameters | list[str] | None | Parameter names to preserve as backend parameters |
Returns:
ExecutableProgram[T] — ExecutableProgram ready for execution
Raises:
ValueError— If a name appears in bothbindingsandparameters. A name being in both is ambiguous (placeholder value vs runtime symbol) and used to silently miscompile control-flow predicates that depended on parameter-array elements; rejecting the overlap up front keeps the contract unambiguous.QamomileCompileError— If compilation fails (validation, dependency errors)
Pipeline:
to_block: Convert QKernel to Block
substitute: Apply substitutions (if configured)
resolve_parameter_shapes: Constant-fold symbolic Vector param dims
inline: Inline CallBlockOperations
affine_validate: Validate affine type semantics
partial_eval: Fold constants and lower compile-time control flow
analyze: Validate and analyze dependencies
validate_symbolic_shapes: Reject unresolved parameter shape dims
plan: Build ProgramPlan (segment into C->Q->C steps)
emit: Generate backend-specific code
unroll_recursion¶
def unroll_recursion(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockFixed-point loop of inline ↔ partial_eval for self-recursive kernels.
Each iteration unrolls one layer of self-referential
CallBlockOperation and then folds the base-case
IfOperation via partial_eval. Terminates when no
CallBlockOperation remains (success), when every residual call
is trapped inside an operation-owned block where partial_eval
cannot fold it (control / inverse of a recursive kernel — raises a
targeted error, see below), or when MAX_UNROLL_DEPTH is reached
(genuinely non-terminating top-level recursion — raises).
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | The block to unroll. May be HIERARCHICAL (still containing self-referential CallBlockOperations) or already AFFINE (returned unchanged). |
bindings | dict[str, Any] | None | Compile-time bindings used by partial_eval to fold the base-case condition. Defaults to None, meaning no bindings are applied. |
Returns:
Block — The fully unrolled, AFFINE block once no
CallBlockOperation remains. Returned unchanged when the
input already has no calls.
Raises:
FrontendTransformError— If every remainingCallBlockOperationis trapped inside aControlledUOperation.block/InverseBlockOperationblock (a self-recursive kernel was passed toqmc.control/qmc.inverse), or if a genuinely non-terminating top-level recursion does not converge withinMAX_UNROLL_DEPTHiterations. The two cases carry distinct, cause-specific messages.
validate_symbolic_shapes¶
def validate_symbolic_shapes(self, block: Block) -> BlockPass 2.5: Reject unresolved parameter shape dims in loop bounds.
Runs after analyze so dependency info is complete. Raises
QamomileCompileError with an actionable message when a
gamma_dim0-style symbolic Value reaches a ForOperation
bound without being folded to a constant by
ParameterShapeResolutionPass.