QURI Parts backend transpiler implementation.
This module provides QuriPartsTranspiler for converting Qamomile QKernels into QURI Parts quantum circuits.
Overview¶
| Class | Description |
|---|---|
EmitPass | Base class for backend-specific emission passes. |
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. |
SeparatePass | Separate a block into quantum and classical segments. |
StandardEmitPass | Standard emit pass implementation using GateEmitter protocol. |
Transpiler | Base class for backend-specific transpilers. |
Classes¶
EmitPass [source]¶
class EmitPass(Pass[SimplifiedProgram, ExecutableProgram[T]], Generic[T])Base class for backend-specific emission passes.
Subclasses implement _emit_quantum_segment() to generate backend-specific quantum circuits.
Input: SimplifiedProgram (enforces C→Q→C pattern) 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: SimplifiedProgram) -> ExecutableProgram[T]Emit backend code from simplified program.
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)Initialize executor with optional sampler and estimator.
Parameters:
| Name | Type | Description |
|---|---|---|
sampler | Any | QURI Parts sampler (defaults to qulacs vector sampler) |
estimator | Any | QURI Parts parametric estimator (defaults to qulacs) |
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¶
noop_measurement: bool
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.
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.
CH(ctrl, tgt) = RY(tgt, π/4) CNOT(ctrl, tgt) RY(tgt, -π/4)
emit_cp¶
def emit_cp(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-Phase gate using decomposition.
CP(ctrl, tgt, θ) = RZ(tgt, θ/2) CNOT(ctrl, tgt) RZ(tgt, -θ/2) CNOT(ctrl, tgt) RZ(ctrl, θ/2)
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.
CRY(ctrl, tgt, θ) = RY(tgt, θ/2) CNOT(ctrl, tgt) RY(tgt, -θ/2) CNOT(ctrl, tgt)
emit_crz¶
def emit_crz(
self,
circuit: 'LinearMappedUnboundParametricQuantumCircuit',
control: int,
target: int,
angle: float | Any,
) -> NoneEmit controlled-RZ gate using decomposition.
CRZ(ctrl, tgt, θ) = RZ(tgt, θ/2) CNOT(ctrl, tgt) RZ(tgt, -θ/2) CNOT(ctrl, tgt)
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.
CY(ctrl, tgt) = S†(tgt) CNOT(ctrl, tgt) S(tgt)
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_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_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) -> QuriPartsExecutorCreate a QURI Parts executor.
Parameters:
| Name | Type | Description |
|---|---|---|
sampler | Any | Optional custom sampler (defaults to qulacs vector sampler) |
estimator | Any | Optional custom estimator (defaults to qulacs parametric estimator) |
Returns:
QuriPartsExecutor — QuriPartsExecutor configured for this backend
SeparatePass [source]¶
class SeparatePass(Pass[Block, SimplifiedProgram])Separate a block into quantum and classical segments.
This pass:
Materializes return operations (syncs output_values from ReturnOperation)
Splits the operation list into quantum and classical segments
Validates single quantum segment (enforces C→Q→C pattern)
Input: Block (typically ANALYZED or AFFINE) Output: SimplifiedProgram with single quantum segment and optional prep/post
Attributes¶
name: str
Methods¶
run¶
def run(self, input: Block) -> SimplifiedProgramSeparate the block into segments.
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.
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) separated = transpiler.separate(analyzed) executable = transpiler.emit(separated, 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¶
config: 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.
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: SimplifiedProgram,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> ExecutableProgram[T]Pass 4: Generate backend-specific code.
Parameters:
| Name | Type | Description |
|---|---|---|
separated | SimplifiedProgram | 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 SeparatePass from seeing classical-only compile-time IfOperations that would otherwise split quantum segments.
separate¶
def separate(self, block: Block) -> SimplifiedProgramPass 3: Lower and split into quantum and classical segments.
Validates C→Q→C pattern with single quantum segment.
set_config¶
def set_config(self, config: TranspilerConfig) -> NoneSet the transpiler configuration.
Parameters:
| Name | Type | Description |
|---|---|---|
config | TranspilerConfig | Transpiler configuration to use |
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 block_value 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) |
parameters | list[str] | None | Parameter names to preserve as backend parameters |
Returns:
ExecutableProgram[T] — ExecutableProgram ready for execution
Raises:
QamomileCompileError— If compilation fails (validation, dependency errors)
Pipeline:
to_block: Convert QKernel to Block
substitute: Apply substitutions (if configured)
inline: Inline CallBlockOperations
affine_validate: Validate affine type semantics
constant_fold: Fold constant expressions 5.5. lower_compile_time_ifs: Lower compile-time IfOperations 5.8. validate_while_contract: Validate while conditions are measurement-backed
analyze: Validate and analyze dependencies
separate: Split into quantum/classical segments
emit: Generate backend-specific code
validate_while_contract¶
def validate_while_contract(self, block: Block) -> BlockPass 1.8: Validate WhileOperation conditions are measurement-backed.
Rejects while patterns whose condition is not a measurement result
(Bit from qmc.measure()). This prevents late ValueError
at emit time for unsupported while forms.