Resource estimation module for Qamomile circuits.
This module provides comprehensive resource estimation for quantum circuits, including:
Qubit counting: Algebraic qubit count with SymPy
Gate counting: Breakdown by gate type (single/two-qubit, T gates, Clifford)
Algorithmic estimates: Theoretical bounds for QAOA, QPE, Hamiltonian simulation
All estimates are expressed as SymPy symbolic expressions, allowing dependency on problem size parameters.
Usage:
Basic circuit analysis (method API — recommended):
>>> estimate = my_circuit.estimate_resources()
>>> print(estimate.qubits) # e.g., “n + 3”
>>> print(estimate
Function API (also supported):
>>> from qamomile.circuit.estimator import estimate_resources
>>> estimate = estimate_resources(my_circuit.block)
>>> print(estimate.qubits) # e.g., “n + 3”
>>> print(estimate
Algorithmic estimates: >>> from qamomile.circuit.estimator.algorithmic import estimate_qaoa >>> import sympy as sp >>> n, p = sp.symbols(‘n p’, positive=True, integer=True) >>> est = estimate_qaoa(n, p, num_edges=n*(n-1)/2) >>> print(est.gates.total)
References:
Based on “Quantum algorithms: A survey of applications and end-to-end complexities” (arXiv:2310.03011v2)
Overview¶
| Function | Description |
|---|---|
count_gates | Count gates in a quantum circuit. |
estimate_resources | Estimate all resources for a quantum circuit. |
| Class | Description |
|---|---|
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
Functions¶
count_gates [source]¶
def count_gates(
block: Block | list[Operation],
bindings: dict[str, Any] | None = None,
) -> GateCountCount gates in a quantum circuit.
This function analyzes operations and returns algebraic gate counts using SymPy expressions. Counts may contain symbols for parametric problem sizes (e.g., loop bounds, array dimensions).
Supports:
GateOperation: Single gate counts
ForOperation: Multiplies inner count by iterations
IfOperation: Takes maximum of branches
CallBlockOperation: Recursively counts called blocks
ControlledUOperation: Counts as a single opaque gate
PauliEvolveOp: Counts decomposition gates (requires bindings)
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | Block or list of Operations to analyze |
bindings | dict[str, Any] | None | Optional parameter bindings. Required for PauliEvolveOp gate counting (must contain the Hamiltonian). |
Returns:
GateCount — GateCount with total, single_qubit, two_qubit, t_gates, clifford_gates
Example:
>>> from qamomile.circuit.estimator import count_gates
>>> count = count_gates(my_circuit.block)
>>> print(count.total) # e.g., "2*n + 5"
>>> print(count.t_gates) # e.g., "n"estimate_resources [source]¶
def estimate_resources(
block: Block | list[Operation],
*,
bindings: dict[str, Any] | None = None,
) -> ResourceEstimateEstimate all resources for a quantum circuit.
This is the main entry point for comprehensive resource estimation. Combines qubit counting and gate counting.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | Block or list of Operations to analyze |
bindings | dict[str, Any] | None | Optional concrete parameter bindings (scalars and dicts). |
Returns:
ResourceEstimate — ResourceEstimate with qubits, gates, and parameters
Example:
>>> import qamomile.circuit as qm
>>> from qamomile.circuit.estimator import estimate_resources
>>>
>>> @qm.qkernel
>>> def bell_state() -> qm.Vector[qm.Qubit]:
... q = qm.qubit_array(2)
... q[0] = qm.h(q[0])
... q[0], q[1] = qm.cx(q[0], q[1])
... return q
>>>
>>> est = estimate_resources(bell_state.block)
>>> print(est.qubits) # 2
>>> print(est.gates.total) # 2
>>> print(est.gates.two_qubit) # 1Example with parametric size:
qm.qkernel def ghz_state(n: qm.UInt) -> qm.Vector[qm.Qubit]: ... q = qm.qubit_array(n) ... q[0] = qm.h(q[0]) ... for i in qm.range(n - 1): ... q[i], q[i+1] = qm.cx(q[i], q[i+1]) ... return q
est = estimate_resources(ghz_state.block) print(est.qubits) # n print(est.gates.total) # n print(est.gates.two_qubit) # n - 1
Substitute concrete value¶
concrete = est.substitute(n=100) print(concrete.qubits) # 100 print(concrete
.gates .total) # 100
Classes¶
ResourceEstimate [source]¶
class ResourceEstimateComprehensive resource estimate for a quantum circuit.
All metrics are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
qubits: sp.Expr,
gates: GateCount,
parameters: dict[str, sp.Symbol] = dict(),
) -> NoneAttributes¶
gates: GateCountparameters: dict[str, sp.Symbol]qubits: sp.Expr
Methods¶
simplify¶
def simplify(self) -> ResourceEstimateSimplify all SymPy expressions.
Returns:
ResourceEstimate — New ResourceEstimate with simplified expressions
substitute¶
def substitute(self, **values: int | float = {}) -> ResourceEstimateSubstitute concrete values for parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
**values | int | float | Parameter name -> concrete value mappings |
Returns:
ResourceEstimate — New ResourceEstimate with substituted values
Example:
>>> est = estimate_resources(circuit)
>>> concrete = est.substitute(n=100, p=3)
>>> print(concrete.qubits) # 100 (instead of 'n')to_dict¶
def to_dict(self) -> dict[str, Any]Convert to a dictionary for serialization.
Returns:
dict[str, Any] — Dictionary with all metrics as strings (for JSON/YAML export)
Example:
>>> est = estimate_resources(circuit)
>>> data = est.to_dict()
>>> import json
>>> print(json.dumps(data, indent=2))qamomile.circuit.estimator.algorithmic¶
Algorithmic resource estimators based on theoretical complexity formulas.
These estimators provide resource bounds based on published complexity results from quantum algorithms literature, particularly from:
arXiv:2310.03011v2 - “Quantum algorithms: A survey of applications and end-to-end complexities”
Unlike the circuit-based estimators (gate_counter, qubits_counter), these provide theoretical estimates based on algorithm parameters without needing the actual circuit implementation.
Overview¶
| Function | Description |
|---|---|
estimate_qaoa | Estimate resources for QAOA circuit. |
estimate_qdrift | Estimate resources for qDRIFT Hamiltonian simulation. |
estimate_qpe | Estimate resources for Quantum Phase Estimation. |
estimate_qsvt | Estimate resources for QSVT-based Hamiltonian simulation. |
estimate_trotter | Estimate resources for Trotter/Suzuki formula Hamiltonian simulation. |
Functions¶
estimate_qaoa [source]¶
def estimate_qaoa(
n: sp.Expr | int,
p: sp.Expr | int,
num_edges: sp.Expr | int,
mixer_type: str = 'x',
) -> ResourceEstimateEstimate resources for QAOA circuit.
QAOA alternates between cost and mixer unitaries for p layers. For Ising/QUBO problems:
Cost layer: RZZ gates on edges (controlled-Z rotations)
Mixer layer: RX gates on all qubits
Based on standard QAOA formulation (Farhi et al. 2014).
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits (problem size) |
p | sp.Expr | int | Number of QAOA layers |
num_edges | sp.Expr | int | Number of edges in problem graph (for cost Hamiltonian) |
mixer_type | str | Type of mixer (“x” for standard X-mixer, future: “xy”, “grover”) |
Returns:
ResourceEstimate — ResourceEstimate with:
qubits: n
gates.total: 2pn + pnum_edges (RX + RZZ gates)
gates.single_qubit: 2pn (RX gates)
gates.two_qubit: pnum_edges (RZZ gates)
Example:
>>> import sympy as sp
>>> n, p = sp.symbols('n p', positive=True, integer=True)
>>> # Complete graph K_n has n*(n-1)/2 edges
>>> edges = n * (n - 1) / 2
>>> est = estimate_qaoa(n, p, edges)
>>> print(est.qubits) # n
>>> print(est.gates.total) # n*p*(n + 3)/2
>>>
>>> # MaxCut on K_10 with p=3
>>> concrete = est.substitute(n=10, p=3)
>>> print(concrete.gates.total) # 195References:
Farhi et al. “A Quantum Approximate Optimization Algorithm” arXiv:1411.4028
Section 20 of arXiv:2310.03011v2 for variational algorithms
estimate_qdrift [source]¶
def estimate_qdrift(
L: sp.Expr | int,
hamiltonian_1norm: sp.Expr | float,
time: sp.Expr | float,
error: sp.Expr | float,
) -> ResourceEstimateEstimate resources for qDRIFT Hamiltonian simulation.
qDRIFT is a randomized algorithm that samples Hamiltonian terms proportionally to their coefficients. Simpler than Trotter but requires more samples.
Parameters:
| Name | Type | Description |
|---|---|---|
L | sp.Expr | int | Number of terms in Hamiltonian |
hamiltonian_1norm | sp.Expr | float | ||H||_1 = Σ|coefficients| |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
Returns:
ResourceEstimate — ResourceEstimate for qDRIFT
Complexity (Section 11.2, arXiv:2310.03011v2): Number of samples: N = O(||H||_1^2 * t^2 / ε)
Note quadratic dependence on time (bad) but linear in error (good). Best for small t or when simplicity is valued over gate count.
Example:
>>> import sympy as sp
>>> L, h1, t, eps = sp.symbols('L h1 t eps', positive=True)
>>>
>>> est = estimate_qdrift(L, h1, t, eps)
>>> print(est.gates.total) # O(h1^2 * t^2 / ε)
>>>
>>> # Compare to Trotter (order 2): O((h1*t)^1.5 / √ε)
>>> # qDRIFT worse for large t, better for small εReferences:
Section 11.2 of arXiv:2310.03011v2
Campbell arXiv:1811.08017: qDRIFT algorithm
estimate_qpe [source]¶
def estimate_qpe(
n_system: sp.Expr | int,
precision: sp.Expr | int,
hamiltonian_norm: sp.Expr | float | None = None,
method: str = 'qubitization',
) -> ResourceEstimateEstimate resources for Quantum Phase Estimation.
QPE estimates the eigenvalue of a unitary operator U = e^(iHt). Two main approaches:
Trotter-based: Approximate e^(iHt) using Trotter formulas
Qubitization: Use block-encoding with quantum walk operator
Parameters:
| Name | Type | Description |
|---|---|---|
n_system | sp.Expr | int | Number of system qubits (qubits in the state being analyzed) |
precision | sp.Expr | int | Number of bits of precision in phase estimate (ε = 2^(-precision)) |
hamiltonian_norm | sp.Expr | float | None | Normalization ||H|| for block-encoding (required for qubitization) |
method | str | “qubitization” (recommended) or “trotter” |
Returns:
ResourceEstimate — ResourceEstimate with qubit and gate counts
For qubitization method (Section 13, arXiv:2310.03011): qubits: n_system + precision + O(log n_system) ancillas calls to block-encoding: O(||H|| * 2^precision) gates per call: depends on Hamiltonian structure
For Trotter method:
qubits: n_system + precision depth: O(2^precision * Trotter_depth)
Example:
>>> import sympy as sp
>>> n = sp.Symbol('n', positive=True, integer=True)
>>> m = sp.Symbol('m', positive=True, integer=True) # precision bits
>>> alpha = sp.Symbol('alpha', positive=True) # ||H||
>>>
>>> est = estimate_qpe(n, m, hamiltonian_norm=alpha)
>>> print(est.qubits) # n + m + O(log n)
>>> print(est.gates.total) # O(alpha * 2^m)
>>>
>>> # Concrete example: 100 qubits, 10 bits precision
>>> concrete = est.substitute(n=100, m=10, alpha=50)
>>> print(concrete.qubits) # ~117 (100 + 10 + log2(100))References:
Nielsen & Chuang, Section 5.2: Original QPE
Section 13 of arXiv:2310.03011v2: Modern variants
Low & Chuang arXiv:1610.06546: Qubitization-based QPE
estimate_qsvt [source]¶
def estimate_qsvt(
n: sp.Expr | int,
hamiltonian_norm: sp.Expr | float,
time: sp.Expr | float,
error: sp.Expr | float,
) -> ResourceEstimateEstimate resources for QSVT-based Hamiltonian simulation.
Quantum Singular Value Transformation (QSVT) provides near-optimal Hamiltonian simulation with complexity linear in time and logarithmic in error.
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits |
hamiltonian_norm | sp.Expr | float | α where ||H|| ≤ α (block-encoding normalization) |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
Returns:
ResourceEstimate — ResourceEstimate for QSVT simulation
Complexity (Section 11.4, arXiv:2310.03011v2): Calls to block-encoding: O(α*t + log(1/ε) / log(log(1/ε)))
This is nearly optimal: Ω(α*t + log(1/ε)) lower bound known.
Example:
>>> import sympy as sp
>>> n, alpha, t, eps = sp.symbols('n alpha t eps', positive=True)
>>>
>>> est = estimate_qsvt(n, alpha, t, eps)
>>> print(est.gates.total) # O(α*t + log(1/ε)/log(log(1/ε)))
>>>
>>> # Much better than Trotter for small error!
>>> trotter = estimate_trotter(n, L=alpha, time=t, error=eps)
>>> # QSVT: O(αt + log 1/ε), Trotter: O(α t^1.5 / √ε)References:
Section 11.4 of arXiv:2310.03011v2
Low & Chuang arXiv:1610.06546: QSVT framework
Gilyen et al. arXiv:1806.01838: QSP/QSVT
estimate_trotter [source]¶
def estimate_trotter(
n: sp.Expr | int,
L: sp.Expr | int,
time: sp.Expr | float,
error: sp.Expr | float,
order: int = 2,
hamiltonian_1norm: sp.Expr | float | None = None,
) -> ResourceEstimateEstimate resources for Trotter/Suzuki formula Hamiltonian simulation.
Product formula methods approximate e^(iHt) by decomposing H into a sum of L terms and using the formula: e^(iHt) ≈ (e^(iH_1 Δt) ... e^(iH_L Δt))^r
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits |
L | sp.Expr | int | Number of terms in Hamiltonian decomposition |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
order | int | Trotter order (2, 4, 6, ...). Higher order = fewer steps but more complex |
hamiltonian_1norm | sp.Expr | float | None | ||H||_1 = Σ|coefficients| (if None, assumes ||H||_1 ~ L) |
Returns:
ResourceEstimate — ResourceEstimate for Trotter simulation
Complexity (pth-order formula, Section 11.1): Number of steps: r = O((||H||_1 * t)^(1+1/p) / ε^(1/p)) Total gates: O(r * L * n) where n is gates per Hamiltonian term
Example:
>>> import sympy as sp
>>> n, L, t, eps = sp.symbols('n L t eps', positive=True)
>>>
>>> # Second-order Trotter
>>> est2 = estimate_trotter(n, L, t, eps, order=2)
>>> print(est2.gates.total) # O(L * (||H||_1 * t)^1.5 / eps^0.5)
>>>
>>> # Fourth-order Trotter (fewer steps, better scaling)
>>> est4 = estimate_trotter(n, L, t, eps, order=4)
>>> print(est4.gates.total) # O(L * (||H||_1 * t)^1.25 / eps^0.25)
>>>
>>> # Concrete: 100 qubits, 1000 terms, time=10, error=0.001
>>> concrete = est2.substitute(n=100, L=1000, t=10, eps=0.001)References:
Section 11.1 of arXiv:2310.03011v2
Childs et al. arXiv:1912.08854: Improved Trotter bounds
qamomile.circuit.estimator.algorithmic.hamiltonian_simulation¶
Theoretical resource estimates for Hamiltonian simulation.
Provides estimates for multiple simulation methods:
Product formulas (Trotter/Suzuki)
qDRIFT
QSVT/QSP-based methods
Based on Section 11 of arXiv:2310.03011v2.
Overview¶
| Function | Description |
|---|---|
estimate_qdrift | Estimate resources for qDRIFT Hamiltonian simulation. |
estimate_qsvt | Estimate resources for QSVT-based Hamiltonian simulation. |
estimate_trotter | Estimate resources for Trotter/Suzuki formula Hamiltonian simulation. |
| Class | Description |
|---|---|
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
Functions¶
estimate_qdrift [source]¶
def estimate_qdrift(
L: sp.Expr | int,
hamiltonian_1norm: sp.Expr | float,
time: sp.Expr | float,
error: sp.Expr | float,
) -> ResourceEstimateEstimate resources for qDRIFT Hamiltonian simulation.
qDRIFT is a randomized algorithm that samples Hamiltonian terms proportionally to their coefficients. Simpler than Trotter but requires more samples.
Parameters:
| Name | Type | Description |
|---|---|---|
L | sp.Expr | int | Number of terms in Hamiltonian |
hamiltonian_1norm | sp.Expr | float | ||H||_1 = Σ|coefficients| |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
Returns:
ResourceEstimate — ResourceEstimate for qDRIFT
Complexity (Section 11.2, arXiv:2310.03011v2): Number of samples: N = O(||H||_1^2 * t^2 / ε)
Note quadratic dependence on time (bad) but linear in error (good). Best for small t or when simplicity is valued over gate count.
Example:
>>> import sympy as sp
>>> L, h1, t, eps = sp.symbols('L h1 t eps', positive=True)
>>>
>>> est = estimate_qdrift(L, h1, t, eps)
>>> print(est.gates.total) # O(h1^2 * t^2 / ε)
>>>
>>> # Compare to Trotter (order 2): O((h1*t)^1.5 / √ε)
>>> # qDRIFT worse for large t, better for small εReferences:
Section 11.2 of arXiv:2310.03011v2
Campbell arXiv:1811.08017: qDRIFT algorithm
estimate_qsvt [source]¶
def estimate_qsvt(
n: sp.Expr | int,
hamiltonian_norm: sp.Expr | float,
time: sp.Expr | float,
error: sp.Expr | float,
) -> ResourceEstimateEstimate resources for QSVT-based Hamiltonian simulation.
Quantum Singular Value Transformation (QSVT) provides near-optimal Hamiltonian simulation with complexity linear in time and logarithmic in error.
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits |
hamiltonian_norm | sp.Expr | float | α where ||H|| ≤ α (block-encoding normalization) |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
Returns:
ResourceEstimate — ResourceEstimate for QSVT simulation
Complexity (Section 11.4, arXiv:2310.03011v2): Calls to block-encoding: O(α*t + log(1/ε) / log(log(1/ε)))
This is nearly optimal: Ω(α*t + log(1/ε)) lower bound known.
Example:
>>> import sympy as sp
>>> n, alpha, t, eps = sp.symbols('n alpha t eps', positive=True)
>>>
>>> est = estimate_qsvt(n, alpha, t, eps)
>>> print(est.gates.total) # O(α*t + log(1/ε)/log(log(1/ε)))
>>>
>>> # Much better than Trotter for small error!
>>> trotter = estimate_trotter(n, L=alpha, time=t, error=eps)
>>> # QSVT: O(αt + log 1/ε), Trotter: O(α t^1.5 / √ε)References:
Section 11.4 of arXiv:2310.03011v2
Low & Chuang arXiv:1610.06546: QSVT framework
Gilyen et al. arXiv:1806.01838: QSP/QSVT
estimate_trotter [source]¶
def estimate_trotter(
n: sp.Expr | int,
L: sp.Expr | int,
time: sp.Expr | float,
error: sp.Expr | float,
order: int = 2,
hamiltonian_1norm: sp.Expr | float | None = None,
) -> ResourceEstimateEstimate resources for Trotter/Suzuki formula Hamiltonian simulation.
Product formula methods approximate e^(iHt) by decomposing H into a sum of L terms and using the formula: e^(iHt) ≈ (e^(iH_1 Δt) ... e^(iH_L Δt))^r
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits |
L | sp.Expr | int | Number of terms in Hamiltonian decomposition |
time | sp.Expr | float | Evolution time t |
error | sp.Expr | float | Target error ε |
order | int | Trotter order (2, 4, 6, ...). Higher order = fewer steps but more complex |
hamiltonian_1norm | sp.Expr | float | None | ||H||_1 = Σ|coefficients| (if None, assumes ||H||_1 ~ L) |
Returns:
ResourceEstimate — ResourceEstimate for Trotter simulation
Complexity (pth-order formula, Section 11.1): Number of steps: r = O((||H||_1 * t)^(1+1/p) / ε^(1/p)) Total gates: O(r * L * n) where n is gates per Hamiltonian term
Example:
>>> import sympy as sp
>>> n, L, t, eps = sp.symbols('n L t eps', positive=True)
>>>
>>> # Second-order Trotter
>>> est2 = estimate_trotter(n, L, t, eps, order=2)
>>> print(est2.gates.total) # O(L * (||H||_1 * t)^1.5 / eps^0.5)
>>>
>>> # Fourth-order Trotter (fewer steps, better scaling)
>>> est4 = estimate_trotter(n, L, t, eps, order=4)
>>> print(est4.gates.total) # O(L * (||H||_1 * t)^1.25 / eps^0.25)
>>>
>>> # Concrete: 100 qubits, 1000 terms, time=10, error=0.001
>>> concrete = est2.substitute(n=100, L=1000, t=10, eps=0.001)References:
Section 11.1 of arXiv:2310.03011v2
Childs et al. arXiv:1912.08854: Improved Trotter bounds
Classes¶
ResourceEstimate [source]¶
class ResourceEstimateComprehensive resource estimate for a quantum circuit.
All metrics are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
qubits: sp.Expr,
gates: GateCount,
parameters: dict[str, sp.Symbol] = dict(),
) -> NoneAttributes¶
gates: GateCountparameters: dict[str, sp.Symbol]qubits: sp.Expr
Methods¶
simplify¶
def simplify(self) -> ResourceEstimateSimplify all SymPy expressions.
Returns:
ResourceEstimate — New ResourceEstimate with simplified expressions
substitute¶
def substitute(self, **values: int | float = {}) -> ResourceEstimateSubstitute concrete values for parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
**values | int | float | Parameter name -> concrete value mappings |
Returns:
ResourceEstimate — New ResourceEstimate with substituted values
Example:
>>> est = estimate_resources(circuit)
>>> concrete = est.substitute(n=100, p=3)
>>> print(concrete.qubits) # 100 (instead of 'n')to_dict¶
def to_dict(self) -> dict[str, Any]Convert to a dictionary for serialization.
Returns:
dict[str, Any] — Dictionary with all metrics as strings (for JSON/YAML export)
Example:
>>> est = estimate_resources(circuit)
>>> data = est.to_dict()
>>> import json
>>> print(json.dumps(data, indent=2))qamomile.circuit.estimator.algorithmic.qaoa¶
Theoretical resource estimates for QAOA (Quantum Approximate Optimization Algorithm).
Based on Section 4 and Section 20 of arXiv:2310.03011v2.
Overview¶
| Function | Description |
|---|---|
estimate_qaoa | Estimate resources for QAOA circuit. |
estimate_qaoa_ising | Estimate resources for QAOA on Ising model. |
| Class | Description |
|---|---|
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
Functions¶
estimate_qaoa [source]¶
def estimate_qaoa(
n: sp.Expr | int,
p: sp.Expr | int,
num_edges: sp.Expr | int,
mixer_type: str = 'x',
) -> ResourceEstimateEstimate resources for QAOA circuit.
QAOA alternates between cost and mixer unitaries for p layers. For Ising/QUBO problems:
Cost layer: RZZ gates on edges (controlled-Z rotations)
Mixer layer: RX gates on all qubits
Based on standard QAOA formulation (Farhi et al. 2014).
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits (problem size) |
p | sp.Expr | int | Number of QAOA layers |
num_edges | sp.Expr | int | Number of edges in problem graph (for cost Hamiltonian) |
mixer_type | str | Type of mixer (“x” for standard X-mixer, future: “xy”, “grover”) |
Returns:
ResourceEstimate — ResourceEstimate with:
qubits: n
gates.total: 2pn + pnum_edges (RX + RZZ gates)
gates.single_qubit: 2pn (RX gates)
gates.two_qubit: pnum_edges (RZZ gates)
Example:
>>> import sympy as sp
>>> n, p = sp.symbols('n p', positive=True, integer=True)
>>> # Complete graph K_n has n*(n-1)/2 edges
>>> edges = n * (n - 1) / 2
>>> est = estimate_qaoa(n, p, edges)
>>> print(est.qubits) # n
>>> print(est.gates.total) # n*p*(n + 3)/2
>>>
>>> # MaxCut on K_10 with p=3
>>> concrete = est.substitute(n=10, p=3)
>>> print(concrete.gates.total) # 195References:
Farhi et al. “A Quantum Approximate Optimization Algorithm” arXiv:1411.4028
Section 20 of arXiv:2310.03011v2 for variational algorithms
estimate_qaoa_ising [source]¶
def estimate_qaoa_ising(
n: sp.Expr | int,
p: sp.Expr | int,
quadratic_terms: sp.Expr | int,
linear_terms: sp.Expr | int | None = None,
) -> ResourceEstimateEstimate resources for QAOA on Ising model.
Ising Hamiltonian: H = Σ J_ij Z_i Z_j + Σ h_i Z_i
This is a convenience wrapper around estimate_qaoa() that accepts the number of quadratic and linear terms directly.
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | int | Number of qubits |
p | sp.Expr | int | Number of QAOA layers |
quadratic_terms | sp.Expr | int | Number of J_ij terms (edges in interaction graph) |
linear_terms | sp.Expr | int | None | Number of h_i terms (defaults to n if None) |
Returns:
ResourceEstimate — ResourceEstimate for QAOA
Example:
>>> # 3-regular graph with n vertices: 3n/2 edges
>>> import sympy as sp
>>> n, p = sp.symbols('n p', positive=True, integer=True)
>>> est = estimate_qaoa_ising(n, p, quadratic_terms=3*n/2)
>>> print(est.gates.total) # 2*n*p + 3*n*p/2Classes¶
ResourceEstimate [source]¶
class ResourceEstimateComprehensive resource estimate for a quantum circuit.
All metrics are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
qubits: sp.Expr,
gates: GateCount,
parameters: dict[str, sp.Symbol] = dict(),
) -> NoneAttributes¶
gates: GateCountparameters: dict[str, sp.Symbol]qubits: sp.Expr
Methods¶
simplify¶
def simplify(self) -> ResourceEstimateSimplify all SymPy expressions.
Returns:
ResourceEstimate — New ResourceEstimate with simplified expressions
substitute¶
def substitute(self, **values: int | float = {}) -> ResourceEstimateSubstitute concrete values for parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
**values | int | float | Parameter name -> concrete value mappings |
Returns:
ResourceEstimate — New ResourceEstimate with substituted values
Example:
>>> est = estimate_resources(circuit)
>>> concrete = est.substitute(n=100, p=3)
>>> print(concrete.qubits) # 100 (instead of 'n')to_dict¶
def to_dict(self) -> dict[str, Any]Convert to a dictionary for serialization.
Returns:
dict[str, Any] — Dictionary with all metrics as strings (for JSON/YAML export)
Example:
>>> est = estimate_resources(circuit)
>>> data = est.to_dict()
>>> import json
>>> print(json.dumps(data, indent=2))qamomile.circuit.estimator.algorithmic.qpe¶
Theoretical resource estimates for QPE (Quantum Phase Estimation).
Based on Section 13 of arXiv:2310.03011v2.
Overview¶
| Function | Description |
|---|---|
estimate_eigenvalue_filtering | Estimate resources for eigenstate filtering (QSVT-based). |
estimate_qpe | Estimate resources for Quantum Phase Estimation. |
| Class | Description |
|---|---|
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
Functions¶
estimate_eigenvalue_filtering [source]¶
def estimate_eigenvalue_filtering(
n_system: sp.Expr | int,
target_overlap: sp.Expr | float,
gap: sp.Expr | float | None = None,
) -> ResourceEstimateEstimate resources for eigenstate filtering (QSVT-based).
Uses quantum singular value transformation to filter eigenstates based on their eigenvalues.
Based on Section 2.1 (Fermi-Hubbard) and general eigenstate preparation methods in arXiv:2310.03011v2.
Parameters:
| Name | Type | Description |
|---|---|---|
n_system | sp.Expr | int | Number of system qubits |
target_overlap | sp.Expr | float | Desired overlap γ with target eigenstate |
gap | sp.Expr | float | None | Spectral gap Δ (if known, improves estimates) |
Returns:
ResourceEstimate — ResourceEstimate
O(1/γ) calls to block-encoding if no gap known:
O(1/√γΔ) if gap Δ is known
Example:
>>> import sympy as sp
>>> n = sp.Symbol('n', positive=True, integer=True)
>>> gamma = sp.Symbol('gamma', positive=True) # overlap
>>>
>>> est = estimate_eigenvalue_filtering(n, gamma)
>>> print(est.gates.total) # O(n/gamma)References:
Lin & Tong arXiv:1910.14596: Eigenstate filtering via QSVT
estimate_qpe [source]¶
def estimate_qpe(
n_system: sp.Expr | int,
precision: sp.Expr | int,
hamiltonian_norm: sp.Expr | float | None = None,
method: str = 'qubitization',
) -> ResourceEstimateEstimate resources for Quantum Phase Estimation.
QPE estimates the eigenvalue of a unitary operator U = e^(iHt). Two main approaches:
Trotter-based: Approximate e^(iHt) using Trotter formulas
Qubitization: Use block-encoding with quantum walk operator
Parameters:
| Name | Type | Description |
|---|---|---|
n_system | sp.Expr | int | Number of system qubits (qubits in the state being analyzed) |
precision | sp.Expr | int | Number of bits of precision in phase estimate (ε = 2^(-precision)) |
hamiltonian_norm | sp.Expr | float | None | Normalization ||H|| for block-encoding (required for qubitization) |
method | str | “qubitization” (recommended) or “trotter” |
Returns:
ResourceEstimate — ResourceEstimate with qubit and gate counts
For qubitization method (Section 13, arXiv:2310.03011): qubits: n_system + precision + O(log n_system) ancillas calls to block-encoding: O(||H|| * 2^precision) gates per call: depends on Hamiltonian structure
For Trotter method:
qubits: n_system + precision depth: O(2^precision * Trotter_depth)
Example:
>>> import sympy as sp
>>> n = sp.Symbol('n', positive=True, integer=True)
>>> m = sp.Symbol('m', positive=True, integer=True) # precision bits
>>> alpha = sp.Symbol('alpha', positive=True) # ||H||
>>>
>>> est = estimate_qpe(n, m, hamiltonian_norm=alpha)
>>> print(est.qubits) # n + m + O(log n)
>>> print(est.gates.total) # O(alpha * 2^m)
>>>
>>> # Concrete example: 100 qubits, 10 bits precision
>>> concrete = est.substitute(n=100, m=10, alpha=50)
>>> print(concrete.qubits) # ~117 (100 + 10 + log2(100))References:
Nielsen & Chuang, Section 5.2: Original QPE
Section 13 of arXiv:2310.03011v2: Modern variants
Low & Chuang arXiv:1610.06546: Qubitization-based QPE
Classes¶
ResourceEstimate [source]¶
class ResourceEstimateComprehensive resource estimate for a quantum circuit.
All metrics are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
qubits: sp.Expr,
gates: GateCount,
parameters: dict[str, sp.Symbol] = dict(),
) -> NoneAttributes¶
gates: GateCountparameters: dict[str, sp.Symbol]qubits: sp.Expr
Methods¶
simplify¶
def simplify(self) -> ResourceEstimateSimplify all SymPy expressions.
Returns:
ResourceEstimate — New ResourceEstimate with simplified expressions
substitute¶
def substitute(self, **values: int | float = {}) -> ResourceEstimateSubstitute concrete values for parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
**values | int | float | Parameter name -> concrete value mappings |
Returns:
ResourceEstimate — New ResourceEstimate with substituted values
Example:
>>> est = estimate_resources(circuit)
>>> concrete = est.substitute(n=100, p=3)
>>> print(concrete.qubits) # 100 (instead of 'n')to_dict¶
def to_dict(self) -> dict[str, Any]Convert to a dictionary for serialization.
Returns:
dict[str, Any] — Dictionary with all metrics as strings (for JSON/YAML export)
Example:
>>> est = estimate_resources(circuit)
>>> data = est.to_dict()
>>> import json
>>> print(json.dumps(data, indent=2))qamomile.circuit.estimator.gate_counter¶
Gate counting for quantum circuits.
This module provides algebraic gate counting using SymPy expressions, allowing resource estimates to depend on problem size parameters.
Overview¶
| Function | Description |
|---|---|
build_for_items_scope | Build child resolver for a ForItemsOperation body. |
build_for_loop_scope | Build child resolver and resolved bounds for a ForOperation. |
build_if_scopes | Build child resolvers for both branches of an IfOperation. |
build_while_scope | Build child resolver and trip-count symbol for a WhileOperation. |
classify_controlled_u | Classify a ControlledUOperation (opaque gate, total=1). |
classify_gate | Classify a single GateOperation into a GateCount. |
classify_pauli_evolve | Gate count for PauliEvolve decomposition (Pauli gadget method). |
count_gates | Count gates in a quantum circuit. |
extract_gate_count_from_metadata | Extract GateCount from ResourceMetadata. |
qft_iqft_gate_count | Gate count for QFT or IQFT on n qubits. |
resolve_composite_gate | Resolve a CompositeGateOperation to its resource source. |
resolve_controlled_u | Resolve a ControlledUOperation to (num_controls, num_targets). |
resolve_for_items_cardinality | Return the symbolic cardinality |dict_name| for a ForItems loop. |
symbolic_iterations | Compute the number of iterations of range(start, stop, step). |
| Class | Description |
|---|---|
Block | Unified block representation for all pipeline stages. |
CallBlockOperation | |
CompositeGateOperation | Represents a composite gate (QPE, QFT, etc.) as a single operation. |
ControlledUOperation | Base class for controlled-U operations. |
ExprResolver | Single source of truth for converting IR Values to SymPy expressions. |
ForItemsOperation | Represents iteration over dict/iterable items. |
ForOperation | Represents a for loop operation. |
GateCount | Gate count breakdown for a quantum circuit. |
GateOperation | Quantum gate operation. |
HasNestedOps | Mixin for operations that contain nested operation lists. |
IfOperation | Represents an if-else conditional operation. |
Operation | |
PauliEvolveOp | Pauli evolution operation: exp(-i * gamma * H). |
WhileOperation | Represents a while loop operation. |
Functions¶
build_for_items_scope [source]¶
def build_for_items_scope(op: ForItemsOperation, resolver: ExprResolver) -> ExprResolverBuild child resolver for a ForItemsOperation body.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForItemsOperation | The for-items loop operation. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
ExprResolver — Child resolver scoped to the loop body.
build_for_loop_scope [source]¶
def build_for_loop_scope(
op: ForOperation,
resolver: ExprResolver,
) -> tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol]Build child resolver and resolved bounds for a ForOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForOperation | The for-loop operation. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol] — tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol]:
(child_resolver, start, stop, step, loop_symbol).
The child resolver has block = local block over
op.operations, loop variable mapped to loop_symbol,
and parent blocks propagated.
build_if_scopes [source]¶
def build_if_scopes(op: Any, resolver: ExprResolver) -> tuple[ExprResolver, ExprResolver]Build child resolvers for both branches of an IfOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | Any | An IfOperation with true_operations and false_operations attributes. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, ExprResolver] — tuple[ExprResolver, ExprResolver]: (true_resolver, false_resolver).
build_while_scope [source]¶
def build_while_scope(op: Any, resolver: ExprResolver) -> tuple[ExprResolver, sp.Symbol]Build child resolver and trip-count symbol for a WhileOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | Any | A WhileOperation with an operations attribute. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, sp.Symbol] — tuple[ExprResolver, sp.Symbol]: (child_resolver, trip_count_symbol)
where trip_count_symbol is |while|.
classify_controlled_u [source]¶
def classify_controlled_u(nc: int | sp.Expr, num_targets: int) -> GateCountClassify a ControlledUOperation (opaque gate, total=1).
power is NOT multiplied — each ControlledUOperation call counts
as exactly one gate regardless of power. Exponential oracle counts
arise from loop structure (e.g. for _rep in range(2**k)).
Parameters:
| Name | Type | Description |
|---|---|---|
nc | int | sp.Expr | Number of control qubits (concrete or symbolic). |
num_targets | int | Number of target qubits. |
Returns:
GateCount — Gate count with total=1 and two_qubit / multi_qubit
flags set based on the total qubit count (nc + num_targets).
classify_gate [source]¶
def classify_gate(op: GateOperation, num_controls: int | sp.Expr = 0) -> GateCountClassify a single GateOperation into a GateCount.
Handles both concrete and symbolic num_controls. The result always
has total=1.
classify_pauli_evolve [source]¶
def classify_pauli_evolve(hamiltonian: Any) -> GateCountGate count for PauliEvolve decomposition (Pauli gadget method).
For each Hamiltonian term with k qubits (x_count X, y_count Y):
Basis change (pre): x_count H + y_count (Sdg + H) = x_count + 2*y_count single-qubit
CNOT ladder: 2*(k-1) CX gates (k >= 2)
RZ gate: 1 rotation gate
Basis undo (post): x_count H + y_count (H + S) = x_count + 2y_count single-qubit Total single-qubit per term: 2x_count + 4y_count + 1 (RZ) Total two-qubit per term: 2max(0, k-1) (CX ladder)
Parameters:
| Name | Type | Description |
|---|---|---|
hamiltonian | Any | A concrete Hamiltonian with known terms. |
Returns:
GateCount — Total gate counts for the full Pauli evolution.
count_gates [source]¶
def count_gates(
block: Block | list[Operation],
bindings: dict[str, Any] | None = None,
) -> GateCountCount gates in a quantum circuit.
This function analyzes operations and returns algebraic gate counts using SymPy expressions. Counts may contain symbols for parametric problem sizes (e.g., loop bounds, array dimensions).
Supports:
GateOperation: Single gate counts
ForOperation: Multiplies inner count by iterations
IfOperation: Takes maximum of branches
CallBlockOperation: Recursively counts called blocks
ControlledUOperation: Counts as a single opaque gate
PauliEvolveOp: Counts decomposition gates (requires bindings)
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | Block or list of Operations to analyze |
bindings | dict[str, Any] | None | Optional parameter bindings. Required for PauliEvolveOp gate counting (must contain the Hamiltonian). |
Returns:
GateCount — GateCount with total, single_qubit, two_qubit, t_gates, clifford_gates
Example:
>>> from qamomile.circuit.estimator import count_gates
>>> count = count_gates(my_circuit.block)
>>> print(count.total) # e.g., "2*n + 5"
>>> print(count.t_gates) # e.g., "n"extract_gate_count_from_metadata [source]¶
def extract_gate_count_from_metadata(meta: ResourceMetadata) -> GateCountExtract GateCount from ResourceMetadata.
Emits UserWarning when total_gates is set but sub-categories
have None gaps and the known sub-total is less than total_gates.
This warning behaviour is required by test_metadata_warnings.py.
Parameters:
| Name | Type | Description |
|---|---|---|
meta | ResourceMetadata | Metadata to extract gate counts from. Fields left as None are treated as 0. |
Returns:
GateCount — Extracted gate counts as sp.Integer values.
qft_iqft_gate_count [source]¶
def qft_iqft_gate_count(n: sp.Expr) -> GateCountGate count for QFT or IQFT on n qubits.
QFT = n H + n(n-1)/2 CP + n//2 SWAP
Parameters:
| Name | Type | Description |
|---|---|---|
n | sp.Expr | Number of qubits (may be symbolic). |
Returns:
GateCount — Symbolic gate counts for the standard QFT decomposition.
resolve_composite_gate [source]¶
def resolve_composite_gate(op: CompositeGateOperation, resolver: ExprResolver) -> CompositeGateResolutionResolve a CompositeGateOperation to its resource source.
Priority (deterministic, not fallback):
resource_metadata— always preferred when presentimplementation(has_implementation + Block)Known formula (QFT / IQFT)
Error — no resource info available
Parameters:
| Name | Type | Description |
|---|---|---|
op | CompositeGateOperation | The operation to resolve. |
resolver | ExprResolver | Resolver for the current scope. |
Returns:
CompositeGateResolution — Exactly one of its four branches is populated.
resolve_controlled_u [source]¶
def resolve_controlled_u(op: ControlledUOperation, resolver: ExprResolver) -> tuple[int | sp.Expr, int]Resolve a ControlledUOperation to (num_controls, num_targets).
num_controls may be int or sp.Expr (symbolic).
num_targets is always a concrete int.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ControlledUOperation | The operation to resolve. |
resolver | ExprResolver | Resolver for the current scope. |
Returns:
tuple[int | sp.Expr, int] — tuple[int | sp.Expr, int]: (num_controls, num_targets).
resolve_for_items_cardinality [source]¶
def resolve_for_items_cardinality(op: ForItemsOperation) -> sp.ExprReturn the symbolic cardinality |dict_name| for a ForItems loop.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForItemsOperation | The for-items loop operation. |
Returns:
sp.Expr — sp.Expr: A positive integer symbol |dict_name|.
symbolic_iterations [source]¶
def symbolic_iterations(start: sp.Expr, stop: sp.Expr, step: sp.Expr) -> sp.ExprCompute the number of iterations of range(start, stop, step).
Uses the discrete formula Max(0, ceiling((stop - start) / step))
which exactly matches Python range() semantics for any integer
step (positive, negative, or symbolic).
Parameters:
| Name | Type | Description |
|---|---|---|
start | sp.Expr | Symbolic start bound. |
stop | sp.Expr | Symbolic stop bound. |
step | sp.Expr | Symbolic step bound. |
Returns:
sp.Expr — sp.Expr: Symbolic iteration count Max(0, ceiling((stop - start) / step)).
Raises:
ValueError— If step is a concrete zero.
Classes¶
Block [source]¶
class BlockUnified block representation for all pipeline stages.
Replaces the older traced and callable IR wrappers with a single structure.
The kind field indicates which pipeline stage this block is at.
Constructor¶
def __init__(
self,
name: str = '',
label_args: list[str] = list(),
input_values: list[Value] = list(),
output_values: list[Value] = list(),
output_names: list[str] = list(),
operations: list['Operation'] = list(),
kind: BlockKind = BlockKind.HIERARCHICAL,
parameters: dict[str, Value] = dict(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]parameters: dict[str, Value]
Methods¶
call¶
def call(self, **kwargs: Value = {}) -> 'CallBlockOperation'Create a CallBlockOperation against this block.
is_affine¶
def is_affine(self) -> boolCheck if block contains no CallBlockOperations.
unbound_parameters¶
def unbound_parameters(self) -> list[str]Return list of unbound parameter names.
CallBlockOperation [source]¶
class CallBlockOperation(Operation)Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
block: Block | None = None,
) -> NoneAttributes¶
block: Block | Noneoperation_kind: OperationKindsignature: Signature
Methods¶
is_self_reference_to¶
def is_self_reference_to(self, block: Block) -> boolReturn True if this call points to the given block (self-ref).
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.
Three concrete subclasses handle distinct operand layouts:
ConcreteControlledU: Fixednum_controls: int, individual qubit operands.SymbolicControlledU: Symbolicnum_controls: Value, vector-based control operands.IndexSpecControlledU: Single vector with explicit index lists selecting which elements are controls/targets.
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,
) -> NoneAttributes¶
block: Block | Nonecontrol_operands: list[Value] Get the control qubit values.has_index_spec: bool Whether target/control positions are specified via index lists.is_symbolic_num_controls: bool Whether num_controls is symbolic (Value) rather than concrete.operation_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]) -> OperationExprResolver [source]¶
class ExprResolverSingle source of truth for converting IR Values to SymPy expressions.
Resolution strategy (deterministic, single path):
Already sp.Basic → return as-is
Not a Value (int, float, bool) → direct conversion
UUID in context (call_context / BinOp) → return mapped expression
Constant value → sp.Integer / sp.Float
Unbound parameter → sp.Symbol (symbolic) or raise (concrete)
Loop variable (name-based) → return mapped symbol
BinOp/CompOp result → trace in block operations
Search parent blocks → trace in ancestors
Fallback → sp.Symbol (symbolic) or raise (concrete)
Constructor¶
def __init__(
self,
block: Any = None,
context: dict[str, sp.Expr] | None = None,
loop_var_names: dict[str, sp.Symbol] | None = None,
parent_blocks: list[Any] | None = None,
)Initialise an ExprResolver.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Any | The current block (Block or _LocalBlock) whose operations are searched for BinOp/CompOp traces. |
context | dict[str, sp.Expr] | None | UUID → resolved expression mapping for values passed across scope boundaries (e.g. call arguments, composite-gate operands). |
loop_var_names | dict[str, sp.Symbol] | None | Value name → SymPy symbol mapping for loop variables in scope. |
parent_blocks | list[Any] | None | Ancestor blocks to search when tracing fails in the current block. |
Attributes¶
block: Any The current block being resolved against.context: dict[str, sp.Expr] Copy of the UUID → expression context mapping.loop_var_names: dict[str, sp.Symbol] Copy of the loop variable name → symbol mapping.
Methods¶
call_child_scope¶
def call_child_scope(self, call_op: Any) -> ExprResolverCreate a child resolver for a CallBlockOperation.
Maps formal parameter UUIDs → resolved actual arguments,
including array shape dimension UUIDs (critical for
resolving e.g. kernel.shape[0] inside the callee).
Parent blocks are intentionally reset — the callee only sees
its own scope plus values propagated through call_context.
Parameters:
| Name | Type | Description |
|---|---|---|
call_op | Any | A CallBlockOperation whose block field is the called Block and operands are actuals. |
Returns:
ExprResolver — A new resolver scoped to the callee block with
formal→actual bindings in context and empty parent blocks.
child_scope¶
def child_scope(
self,
inner_block: Any,
extra_context: dict[str, sp.Expr] | None = None,
extra_loop_vars: dict[str, sp.Symbol] | None = None,
) -> ExprResolverCreate a child resolver for an inner scope (loop body, branch).
Propagates parent_blocks so values from outer scopes remain
traceable. For callee scopes (CallBlockOperation), use
:meth:call_child_scope instead — callees get a fresh scope.
Parameters:
| Name | Type | Description |
|---|---|---|
inner_block | Any | The block for the child scope. |
extra_context | dict[str, sp.Expr] | None | Additional UUID → expression mappings to merge into the child context. |
extra_loop_vars | dict[str, sp.Symbol] | None | Additional loop variable name → symbol mappings. |
Returns:
ExprResolver — A new resolver scoped to inner_block with
parent blocks propagated from the current resolver.
resolve¶
def resolve(self, v: Any) -> sp.ExprConvert IR Value to SymPy expression (symbolic mode).
Unbound parameters become sp.Symbol. Never raises for valid IR.
Parameters:
| Name | Type | Description |
|---|---|---|
v | Any | IR Value, primitive Python type, or sp.Basic. |
Returns:
sp.Expr — sp.Expr: Resolved SymPy expression.
resolve_concrete¶
def resolve_concrete(self, v: Any) -> intConvert IR Value to concrete int.
Parameters:
| Name | Type | Description |
|---|---|---|
v | Any | IR Value, primitive Python type, or sp.Basic. |
Returns:
int — The resolved concrete integer.
Raises:
UnresolvedValueError— If the value is symbolic.
ForItemsOperation [source]¶
class ForItemsOperation(HasNestedOps, Operation)Represents iteration over dict/iterable items.
Example:
for (i, j), Jij in qmc.items(ising):
bodyConstructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
key_vars: list[str] = list(),
value_var: str = '',
key_is_vector: bool = False,
key_var_values: tuple[Value, ...] | None = None,
value_var_value: Value | None = None,
operations: list[Operation] = list(),
) -> NoneAttributes¶
key_is_vector: boolkey_var_values: tuple[Value, ...] | Nonekey_vars: list[str]operation_kind: OperationKindoperations: list[Operation]signature: Signaturevalue_var: strvalue_var_value: Value | None
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Include the per-key/value Value fields for cloning/substitution.
Same rationale as ForOperation.all_input_values: keep the IR
identity fields in lockstep with body references so UUID-keyed
lookups stay valid after inline cloning.
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]) -> OperationForOperation [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]) -> OperationGateCount [source]¶
class GateCountGate count breakdown for a quantum circuit.
All counts are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
total: sp.Expr,
single_qubit: sp.Expr,
two_qubit: sp.Expr,
multi_qubit: sp.Expr,
t_gates: sp.Expr,
clifford_gates: sp.Expr,
rotation_gates: sp.Expr,
oracle_calls: dict[str, sp.Expr] = dict(),
oracle_queries: dict[str, sp.Expr] = dict(),
) -> NoneAttributes¶
clifford_gates: sp.Exprmulti_qubit: sp.Exproracle_calls: dict[str, sp.Expr]oracle_queries: dict[str, sp.Expr]rotation_gates: sp.Exprsingle_qubit: sp.Exprt_gates: sp.Exprtotal: sp.Exprtwo_qubit: sp.Expr
Methods¶
max¶
def max(self, other: GateCount) -> GateCountElement-wise maximum of two gate counts.
Parameters:
| Name | Type | Description |
|---|---|---|
other | GateCount | The gate count to compare against. |
Returns:
GateCount — New instance where each field is sp.Max(self.field, other.field). Oracle dicts are merged key-wise with
sp.Max for common keys.
simplify¶
def simplify(self) -> GateCountSimplify all SymPy expressions.
Returns:
GateCount — New instance with sp.simplify and
_strip_nonneg_max applied to every field.
zero¶
@staticmethod
def zero() -> GateCountReturn a zero gate count.
Returns:
GateCount — Instance with all fields set to sp.Integer(0)
and empty oracle dicts.
GateOperation [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.
HasNestedOps [source]¶
class HasNestedOpsMixin for operations that contain nested operation lists.
Subclasses implement nested_op_lists() and rebuild_nested()
so that generic passes can recurse into control flow without
isinstance chains.
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]Return all nested operation lists in this control flow op.
rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> OperationReturn a copy with nested operation lists replaced.
new_lists must have the same length/order as nested_op_lists().
IfOperation [source]¶
class IfOperation(HasNestedOps, Operation)Represents an if-else conditional operation.
Example:
if condition:
true_body
else:
false_bodyConstructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
true_operations: list[Operation] = list(),
false_operations: list[Operation] = list(),
phi_ops: list[PhiOp] = list(),
) -> NoneAttributes¶
condition: Valuefalse_operations: list[Operation]operation_kind: OperationKindphi_ops: list[PhiOp]signature: Signaturetrue_operations: list[Operation]
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> OperationOperation [source]¶
class Operation(abc.ABC)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operands: list[Value]operation_kind: OperationKind Return the kind of this operation for classical/quantum classification.results: list[Value]signature: Signature
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Return all input Values including subclass-specific fields.
Generic passes should use this instead of accessing operands
directly to ensure no Value is missed. Subclasses override this
to include extra Value fields (e.g. ControlledUOperation.power).
replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationReturn a copy with all Values substituted via mapping.
Handles operands, results, and subclass-specific Value
fields. Subclasses override to handle their extra fields.
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
WhileOperation [source]¶
class WhileOperation(HasNestedOps, Operation)Represents a while loop operation.
Only measurement-backed conditions are supported: the condition must
be a Bit value produced by qmc.measure(). Non-measurement
conditions (classical variables, constants, comparisons) are rejected
by ValidateWhileContractPass before reaching backend emit.
Example::
bit = qmc.measure(q)
while bit:
q = qmc.h(q)
bit = qmc.measure(q)Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
operations: list[Operation] = list(),
max_iterations: int | None = None,
) -> NoneAttributes¶
max_iterations: int | Noneoperation_kind: OperationKindoperations: list[Operation]signature: Signature
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> Operationqamomile.circuit.estimator.qubits_counter¶
Qubit resource counting for Block.
This module counts the number of qubits required by a quantum circuit, using ExprResolver for value resolution and shared engine helpers for control flow scoping.
Overview¶
| Function | Description |
|---|---|
build_for_items_scope | Build child resolver for a ForItemsOperation body. |
build_for_loop_scope | Build child resolver and resolved bounds for a ForOperation. |
build_if_scopes | Build child resolvers for both branches of an IfOperation. |
build_while_scope | Build child resolver and trip-count symbol for a WhileOperation. |
qubits_counter | Count the number of qubits required by a Block. |
resolve_for_items_cardinality | Return the symbolic cardinality |dict_name| for a ForItems loop. |
symbolic_iterations | Compute the number of iterations of range(start, stop, step). |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
Block | Unified block representation for all pipeline stages. |
CallBlockOperation | |
CompositeGateOperation | Represents a composite gate (QPE, QFT, etc.) as a single operation. |
ControlledUOperation | Base class for controlled-U operations. |
ExprResolver | Single source of truth for converting IR Values to SymPy expressions. |
ForItemsOperation | Represents iteration over dict/iterable items. |
ForOperation | Represents a for loop operation. |
HasNestedOps | Mixin for operations that contain nested operation lists. |
IfOperation | Represents an if-else conditional operation. |
Operation | |
PauliEvolveOp | Pauli evolution operation: exp(-i * gamma * H). |
QInitOperation | Initialize the qubit |
QubitType | Type representing a quantum bit (qubit). |
WhileOperation | Represents a while loop operation. |
Functions¶
build_for_items_scope [source]¶
def build_for_items_scope(op: ForItemsOperation, resolver: ExprResolver) -> ExprResolverBuild child resolver for a ForItemsOperation body.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForItemsOperation | The for-items loop operation. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
ExprResolver — Child resolver scoped to the loop body.
build_for_loop_scope [source]¶
def build_for_loop_scope(
op: ForOperation,
resolver: ExprResolver,
) -> tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol]Build child resolver and resolved bounds for a ForOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForOperation | The for-loop operation. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol] — tuple[ExprResolver, sp.Expr, sp.Expr, sp.Expr, sp.Symbol]:
(child_resolver, start, stop, step, loop_symbol).
The child resolver has block = local block over
op.operations, loop variable mapped to loop_symbol,
and parent blocks propagated.
build_if_scopes [source]¶
def build_if_scopes(op: Any, resolver: ExprResolver) -> tuple[ExprResolver, ExprResolver]Build child resolvers for both branches of an IfOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | Any | An IfOperation with true_operations and false_operations attributes. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, ExprResolver] — tuple[ExprResolver, ExprResolver]: (true_resolver, false_resolver).
build_while_scope [source]¶
def build_while_scope(op: Any, resolver: ExprResolver) -> tuple[ExprResolver, sp.Symbol]Build child resolver and trip-count symbol for a WhileOperation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | Any | A WhileOperation with an operations attribute. |
resolver | ExprResolver | Resolver for the enclosing scope. |
Returns:
tuple[ExprResolver, sp.Symbol] — tuple[ExprResolver, sp.Symbol]: (child_resolver, trip_count_symbol)
where trip_count_symbol is |while|.
qubits_counter [source]¶
def qubits_counter(block: Block | list[Operation]) -> sp.ExprCount the number of qubits required by a Block.
This function analyzes the operations in a Block and counts the total number of qubits that need to be allocated. It handles:
QInitOperation: Counts single qubits and qubit arrays
ForOperation/WhileOperation: Counts inner resources (assumes uncomputation)
IfOperation: Takes the maximum of both branches
CallBlockOperation: Recursively counts called blocks
ControlledUOperation: Recursively counts the unitary block
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | The Block to analyze. |
Returns:
sp.Expr — The qubit count as a sympy expression. May contain symbols
sp.Expr — for parametric dimensions (e.g., if array sizes are parameters).
Example:
>>> from qamomile.circuit.ir.block import Block
>>> block = some_block()
>>> count = qubits_counter(block)
>>> print(count) # e.g., "n + 3" for parametric nresolve_for_items_cardinality [source]¶
def resolve_for_items_cardinality(op: ForItemsOperation) -> sp.ExprReturn the symbolic cardinality |dict_name| for a ForItems loop.
Parameters:
| Name | Type | Description |
|---|---|---|
op | ForItemsOperation | The for-items loop operation. |
Returns:
sp.Expr — sp.Expr: A positive integer symbol |dict_name|.
symbolic_iterations [source]¶
def symbolic_iterations(start: sp.Expr, stop: sp.Expr, step: sp.Expr) -> sp.ExprCompute the number of iterations of range(start, stop, step).
Uses the discrete formula Max(0, ceiling((stop - start) / step))
which exactly matches Python range() semantics for any integer
step (positive, negative, or symbolic).
Parameters:
| Name | Type | Description |
|---|---|---|
start | sp.Expr | Symbolic start bound. |
stop | sp.Expr | Symbolic stop bound. |
step | sp.Expr | Symbolic step bound. |
Returns:
sp.Expr — sp.Expr: Symbolic iteration count Max(0, ceiling((stop - start) / step)).
Raises:
ValueError— If step is a concrete zero.
Classes¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
Constructor¶
def __init__(
self,
type: T,
name: str,
version: int = 0,
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
parent_array: ArrayValue | None = None,
element_indices: tuple[Value, ...] = (),
shape: tuple[Value, ...] = tuple(),
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]type: Tuuid: str
Methods¶
next_version¶
def next_version(self) -> ArrayValue[T]Block [source]¶
class BlockUnified block representation for all pipeline stages.
Replaces the older traced and callable IR wrappers with a single structure.
The kind field indicates which pipeline stage this block is at.
Constructor¶
def __init__(
self,
name: str = '',
label_args: list[str] = list(),
input_values: list[Value] = list(),
output_values: list[Value] = list(),
output_names: list[str] = list(),
operations: list['Operation'] = list(),
kind: BlockKind = BlockKind.HIERARCHICAL,
parameters: dict[str, Value] = dict(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]parameters: dict[str, Value]
Methods¶
call¶
def call(self, **kwargs: Value = {}) -> 'CallBlockOperation'Create a CallBlockOperation against this block.
is_affine¶
def is_affine(self) -> boolCheck if block contains no CallBlockOperations.
unbound_parameters¶
def unbound_parameters(self) -> list[str]Return list of unbound parameter names.
CallBlockOperation [source]¶
class CallBlockOperation(Operation)Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
block: Block | None = None,
) -> NoneAttributes¶
block: Block | Noneoperation_kind: OperationKindsignature: Signature
Methods¶
is_self_reference_to¶
def is_self_reference_to(self, block: Block) -> boolReturn True if this call points to the given block (self-ref).
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.
Three concrete subclasses handle distinct operand layouts:
ConcreteControlledU: Fixednum_controls: int, individual qubit operands.SymbolicControlledU: Symbolicnum_controls: Value, vector-based control operands.IndexSpecControlledU: Single vector with explicit index lists selecting which elements are controls/targets.
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,
) -> NoneAttributes¶
block: Block | Nonecontrol_operands: list[Value] Get the control qubit values.has_index_spec: bool Whether target/control positions are specified via index lists.is_symbolic_num_controls: bool Whether num_controls is symbolic (Value) rather than concrete.operation_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]) -> OperationExprResolver [source]¶
class ExprResolverSingle source of truth for converting IR Values to SymPy expressions.
Resolution strategy (deterministic, single path):
Already sp.Basic → return as-is
Not a Value (int, float, bool) → direct conversion
UUID in context (call_context / BinOp) → return mapped expression
Constant value → sp.Integer / sp.Float
Unbound parameter → sp.Symbol (symbolic) or raise (concrete)
Loop variable (name-based) → return mapped symbol
BinOp/CompOp result → trace in block operations
Search parent blocks → trace in ancestors
Fallback → sp.Symbol (symbolic) or raise (concrete)
Constructor¶
def __init__(
self,
block: Any = None,
context: dict[str, sp.Expr] | None = None,
loop_var_names: dict[str, sp.Symbol] | None = None,
parent_blocks: list[Any] | None = None,
)Initialise an ExprResolver.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Any | The current block (Block or _LocalBlock) whose operations are searched for BinOp/CompOp traces. |
context | dict[str, sp.Expr] | None | UUID → resolved expression mapping for values passed across scope boundaries (e.g. call arguments, composite-gate operands). |
loop_var_names | dict[str, sp.Symbol] | None | Value name → SymPy symbol mapping for loop variables in scope. |
parent_blocks | list[Any] | None | Ancestor blocks to search when tracing fails in the current block. |
Attributes¶
block: Any The current block being resolved against.context: dict[str, sp.Expr] Copy of the UUID → expression context mapping.loop_var_names: dict[str, sp.Symbol] Copy of the loop variable name → symbol mapping.
Methods¶
call_child_scope¶
def call_child_scope(self, call_op: Any) -> ExprResolverCreate a child resolver for a CallBlockOperation.
Maps formal parameter UUIDs → resolved actual arguments,
including array shape dimension UUIDs (critical for
resolving e.g. kernel.shape[0] inside the callee).
Parent blocks are intentionally reset — the callee only sees
its own scope plus values propagated through call_context.
Parameters:
| Name | Type | Description |
|---|---|---|
call_op | Any | A CallBlockOperation whose block field is the called Block and operands are actuals. |
Returns:
ExprResolver — A new resolver scoped to the callee block with
formal→actual bindings in context and empty parent blocks.
child_scope¶
def child_scope(
self,
inner_block: Any,
extra_context: dict[str, sp.Expr] | None = None,
extra_loop_vars: dict[str, sp.Symbol] | None = None,
) -> ExprResolverCreate a child resolver for an inner scope (loop body, branch).
Propagates parent_blocks so values from outer scopes remain
traceable. For callee scopes (CallBlockOperation), use
:meth:call_child_scope instead — callees get a fresh scope.
Parameters:
| Name | Type | Description |
|---|---|---|
inner_block | Any | The block for the child scope. |
extra_context | dict[str, sp.Expr] | None | Additional UUID → expression mappings to merge into the child context. |
extra_loop_vars | dict[str, sp.Symbol] | None | Additional loop variable name → symbol mappings. |
Returns:
ExprResolver — A new resolver scoped to inner_block with
parent blocks propagated from the current resolver.
resolve¶
def resolve(self, v: Any) -> sp.ExprConvert IR Value to SymPy expression (symbolic mode).
Unbound parameters become sp.Symbol. Never raises for valid IR.
Parameters:
| Name | Type | Description |
|---|---|---|
v | Any | IR Value, primitive Python type, or sp.Basic. |
Returns:
sp.Expr — sp.Expr: Resolved SymPy expression.
resolve_concrete¶
def resolve_concrete(self, v: Any) -> intConvert IR Value to concrete int.
Parameters:
| Name | Type | Description |
|---|---|---|
v | Any | IR Value, primitive Python type, or sp.Basic. |
Returns:
int — The resolved concrete integer.
Raises:
UnresolvedValueError— If the value is symbolic.
ForItemsOperation [source]¶
class ForItemsOperation(HasNestedOps, Operation)Represents iteration over dict/iterable items.
Example:
for (i, j), Jij in qmc.items(ising):
bodyConstructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
key_vars: list[str] = list(),
value_var: str = '',
key_is_vector: bool = False,
key_var_values: tuple[Value, ...] | None = None,
value_var_value: Value | None = None,
operations: list[Operation] = list(),
) -> NoneAttributes¶
key_is_vector: boolkey_var_values: tuple[Value, ...] | Nonekey_vars: list[str]operation_kind: OperationKindoperations: list[Operation]signature: Signaturevalue_var: strvalue_var_value: Value | None
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Include the per-key/value Value fields for cloning/substitution.
Same rationale as ForOperation.all_input_values: keep the IR
identity fields in lockstep with body references so UUID-keyed
lookups stay valid after inline cloning.
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]) -> OperationForOperation [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]) -> OperationHasNestedOps [source]¶
class HasNestedOpsMixin for operations that contain nested operation lists.
Subclasses implement nested_op_lists() and rebuild_nested()
so that generic passes can recurse into control flow without
isinstance chains.
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]Return all nested operation lists in this control flow op.
rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> OperationReturn a copy with nested operation lists replaced.
new_lists must have the same length/order as nested_op_lists().
IfOperation [source]¶
class IfOperation(HasNestedOps, Operation)Represents an if-else conditional operation.
Example:
if condition:
true_body
else:
false_bodyConstructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
true_operations: list[Operation] = list(),
false_operations: list[Operation] = list(),
phi_ops: list[PhiOp] = list(),
) -> NoneAttributes¶
condition: Valuefalse_operations: list[Operation]operation_kind: OperationKindphi_ops: list[PhiOp]signature: Signaturetrue_operations: list[Operation]
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> OperationOperation [source]¶
class Operation(abc.ABC)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operands: list[Value]operation_kind: OperationKind Return the kind of this operation for classical/quantum classification.results: list[Value]signature: Signature
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Return all input Values including subclass-specific fields.
Generic passes should use this instead of accessing operands
directly to ensure no Value is missed. Subclasses override this
to include extra Value fields (e.g. ControlledUOperation.power).
replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationReturn a copy with all Values substituted via mapping.
Handles operands, results, and subclass-specific Value
fields. Subclasses override to handle their extra fields.
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
QInitOperation [source]¶
class QInitOperation(Operation)Initialize the qubit
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
QubitType [source]¶
class QubitType(QuantumTypeMixin, ValueType)Type representing a quantum bit (qubit).
WhileOperation [source]¶
class WhileOperation(HasNestedOps, Operation)Represents a while loop operation.
Only measurement-backed conditions are supported: the condition must
be a Bit value produced by qmc.measure(). Non-measurement
conditions (classical variables, constants, comparisons) are rejected
by ValidateWhileContractPass before reaching backend emit.
Example::
bit = qmc.measure(q)
while bit:
q = qmc.h(q)
bit = qmc.measure(q)Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
operations: list[Operation] = list(),
max_iterations: int | None = None,
) -> NoneAttributes¶
max_iterations: int | Noneoperation_kind: OperationKindoperations: list[Operation]signature: Signature
Methods¶
nested_op_lists¶
def nested_op_lists(self) -> list[list[Operation]]rebuild_nested¶
def rebuild_nested(self, new_lists: list[list[Operation]]) -> Operationqamomile.circuit.estimator.resource_estimator¶
Unified resource estimation interface.
This module combines all resource metrics (qubits, gates) into a single interface for comprehensive circuit analysis.
Overview¶
| Function | Description |
|---|---|
count_gates | Count gates in a quantum circuit. |
estimate_resources | Estimate all resources for a quantum circuit. |
qubits_counter | Count the number of qubits required by a Block. |
| Class | Description |
|---|---|
Block | Unified block representation for all pipeline stages. |
Operation | |
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
Functions¶
count_gates [source]¶
def count_gates(
block: Block | list[Operation],
bindings: dict[str, Any] | None = None,
) -> GateCountCount gates in a quantum circuit.
This function analyzes operations and returns algebraic gate counts using SymPy expressions. Counts may contain symbols for parametric problem sizes (e.g., loop bounds, array dimensions).
Supports:
GateOperation: Single gate counts
ForOperation: Multiplies inner count by iterations
IfOperation: Takes maximum of branches
CallBlockOperation: Recursively counts called blocks
ControlledUOperation: Counts as a single opaque gate
PauliEvolveOp: Counts decomposition gates (requires bindings)
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | Block or list of Operations to analyze |
bindings | dict[str, Any] | None | Optional parameter bindings. Required for PauliEvolveOp gate counting (must contain the Hamiltonian). |
Returns:
GateCount — GateCount with total, single_qubit, two_qubit, t_gates, clifford_gates
Example:
>>> from qamomile.circuit.estimator import count_gates
>>> count = count_gates(my_circuit.block)
>>> print(count.total) # e.g., "2*n + 5"
>>> print(count.t_gates) # e.g., "n"estimate_resources [source]¶
def estimate_resources(
block: Block | list[Operation],
*,
bindings: dict[str, Any] | None = None,
) -> ResourceEstimateEstimate all resources for a quantum circuit.
This is the main entry point for comprehensive resource estimation. Combines qubit counting and gate counting.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | Block or list of Operations to analyze |
bindings | dict[str, Any] | None | Optional concrete parameter bindings (scalars and dicts). |
Returns:
ResourceEstimate — ResourceEstimate with qubits, gates, and parameters
Example:
>>> import qamomile.circuit as qm
>>> from qamomile.circuit.estimator import estimate_resources
>>>
>>> @qm.qkernel
>>> def bell_state() -> qm.Vector[qm.Qubit]:
... q = qm.qubit_array(2)
... q[0] = qm.h(q[0])
... q[0], q[1] = qm.cx(q[0], q[1])
... return q
>>>
>>> est = estimate_resources(bell_state.block)
>>> print(est.qubits) # 2
>>> print(est.gates.total) # 2
>>> print(est.gates.two_qubit) # 1Example with parametric size:
qm.qkernel def ghz_state(n: qm.UInt) -> qm.Vector[qm.Qubit]: ... q = qm.qubit_array(n) ... q[0] = qm.h(q[0]) ... for i in qm.range(n - 1): ... q[i], q[i+1] = qm.cx(q[i], q[i+1]) ... return q
est = estimate_resources(ghz_state.block) print(est.qubits) # n print(est.gates.total) # n print(est.gates.two_qubit) # n - 1
Substitute concrete value¶
concrete = est.substitute(n=100) print(concrete.qubits) # 100 print(concrete
.gates .total) # 100
qubits_counter [source]¶
def qubits_counter(block: Block | list[Operation]) -> sp.ExprCount the number of qubits required by a Block.
This function analyzes the operations in a Block and counts the total number of qubits that need to be allocated. It handles:
QInitOperation: Counts single qubits and qubit arrays
ForOperation/WhileOperation: Counts inner resources (assumes uncomputation)
IfOperation: Takes the maximum of both branches
CallBlockOperation: Recursively counts called blocks
ControlledUOperation: Recursively counts the unitary block
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | list[Operation] | The Block to analyze. |
Returns:
sp.Expr — The qubit count as a sympy expression. May contain symbols
sp.Expr — for parametric dimensions (e.g., if array sizes are parameters).
Example:
>>> from qamomile.circuit.ir.block import Block
>>> block = some_block()
>>> count = qubits_counter(block)
>>> print(count) # e.g., "n + 3" for parametric nClasses¶
Block [source]¶
class BlockUnified block representation for all pipeline stages.
Replaces the older traced and callable IR wrappers with a single structure.
The kind field indicates which pipeline stage this block is at.
Constructor¶
def __init__(
self,
name: str = '',
label_args: list[str] = list(),
input_values: list[Value] = list(),
output_values: list[Value] = list(),
output_names: list[str] = list(),
operations: list['Operation'] = list(),
kind: BlockKind = BlockKind.HIERARCHICAL,
parameters: dict[str, Value] = dict(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]parameters: dict[str, Value]
Methods¶
call¶
def call(self, **kwargs: Value = {}) -> 'CallBlockOperation'Create a CallBlockOperation against this block.
is_affine¶
def is_affine(self) -> boolCheck if block contains no CallBlockOperations.
unbound_parameters¶
def unbound_parameters(self) -> list[str]Return list of unbound parameter names.
Operation [source]¶
class Operation(abc.ABC)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operands: list[Value]operation_kind: OperationKind Return the kind of this operation for classical/quantum classification.results: list[Value]signature: Signature
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]Return all input Values including subclass-specific fields.
Generic passes should use this instead of accessing operands
directly to ensure no Value is missed. Subclasses override this
to include extra Value fields (e.g. ControlledUOperation.power).
replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationReturn a copy with all Values substituted via mapping.
Handles operands, results, and subclass-specific Value
fields. Subclasses override to handle their extra fields.
ResourceEstimate [source]¶
class ResourceEstimateComprehensive resource estimate for a quantum circuit.
All metrics are SymPy expressions that may contain symbols for parametric problem sizes.
Constructor¶
def __init__(
self,
qubits: sp.Expr,
gates: GateCount,
parameters: dict[str, sp.Symbol] = dict(),
) -> NoneAttributes¶
gates: GateCountparameters: dict[str, sp.Symbol]qubits: sp.Expr
Methods¶
simplify¶
def simplify(self) -> ResourceEstimateSimplify all SymPy expressions.
Returns:
ResourceEstimate — New ResourceEstimate with simplified expressions
substitute¶
def substitute(self, **values: int | float = {}) -> ResourceEstimateSubstitute concrete values for parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
**values | int | float | Parameter name -> concrete value mappings |
Returns:
ResourceEstimate — New ResourceEstimate with substituted values
Example:
>>> est = estimate_resources(circuit)
>>> concrete = est.substitute(n=100, p=3)
>>> print(concrete.qubits) # 100 (instead of 'n')to_dict¶
def to_dict(self) -> dict[str, Any]Convert to a dictionary for serialization.
Returns:
dict[str, Any] — Dictionary with all metrics as strings (for JSON/YAML export)
Example:
>>> est = estimate_resources(circuit)
>>> data = est.to_dict()
>>> import json
>>> print(json.dumps(data, indent=2))