qamomile.circuit.frontend.ast_transform¶
Overview¶
| Function | Description |
|---|---|
collect_quantum_rebind_violations | Analyze func for forbidden quantum rebind patterns. |
emit_if | Builder function for if-else conditional with Phi function merging. |
for_items | Builder function to create a for-items loop in Qamomile frontend. |
for_loop | Builder function to create a for loop in Qamomile frontend. |
should_trace_for_loop | Decide whether a qmc.range body must be traced. |
transform_control_flow | |
while_loop | Create a while loop whose condition is a measurement result. |
| Class | Description |
|---|---|
ControlFlowTransformer | |
QuantumRebindAnalyzer | Detects forbidden quantum variable reassignment at the AST level. |
RebindSourceKind | Discriminator for the source of a detected rebind violation. |
RebindViolation | A detected forbidden quantum variable rebinding. |
VariableCollector | Collect variables used and mutated within a block. |
Functions¶
collect_quantum_rebind_violations [source]¶
def collect_quantum_rebind_violations(func: Callable, quantum_param_names: set[str]) -> list[RebindViolation]Analyze func for forbidden quantum rebind patterns.
Returns a (possibly empty) list of violations. Never raises on
analysis failure – returns [] instead.
emit_if [source]¶
def emit_if(
cond_func: Callable,
true_func: Callable,
false_func: Callable,
variables: list,
) -> AnyBuilder function for if-else conditional with Phi function merging.
This function is called from AST-transformed code. The AST transformer converts: if condition: true_body else: false_body
Into:
def _cond_N(vars): return condition def _body_N(vars): true_body; return vars def _body_N+1(vars): false_body; return vars result = emit_if(_cond_N, _body_N, _body_N+1, [var_list])
Parameters:
| Name | Type | Description |
|---|---|---|
cond_func | typing.Callable | Function returning the condition (Bit or bool-like Handle) |
true_func | typing.Callable | Function executing true branch, returns updated variables |
false_func | typing.Callable | Function executing false branch, returns updated variables |
variables | list | List of variables used in the branches |
Returns:
typing.Any — Merged variable values after conditional execution (using Phi functions)
Example:
@qkernel
def my_kernel(q: Qubit) -> Qubit:
result = measure(q)
if result:
q = z(q)
return qfor_items [source]¶
def for_items(
d: Dict,
key_var_names: list[str],
value_var_name: str,
) -> Generator[tuple[Any, Any], None, None]Builder function to create a for-items loop in Qamomile frontend.
This context manager creates a ForItemsOperation that iterates over dictionary (key, value) pairs. The operation is always unrolled at transpile time since quantum backends cannot natively iterate over classical data structures.
Parameters:
| Name | Type | Description |
|---|---|---|
d | Dict | Dict handle to iterate over |
key_var_names | list[str] | Names of key unpacking variables (e.g., [“i”, “j”] for tuple keys) |
value_var_name | str | Name of value variable (e.g., “Jij”) |
Yields:
tuple[typing.Any, typing.Any] — Tuple of (key_handles, value_handle) for use in loop body
Example:
@qkernel
def ising_cost(
q: Vector[Qubit],
ising: Dict[Tuple[UInt, UInt], Float],
gamma: Float,
) -> Vector[Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qfor_loop [source]¶
def for_loop(
start,
stop,
step = 1,
var_name: str = '_loop_idx',
) -> Generator[UInt, None, None]Builder function to create a for loop in Qamomile frontend.
Parameters:
| Name | Type | Description |
|---|---|---|
start | `` | Loop start value (can be Handle or int) |
stop | `` | Loop stop value (can be Handle or int) |
step | `` | Loop step value (default=1) |
var_name | str | Name of the loop variable (default=“_loop_idx”) |
Yields:
UInt — The loop iteration variable (can be used as array index)
Example:
@QKernel
def my_kernel(qubits: Array[Qubit, Literal[3]]) -> Array[Qubit, Literal[3]]:
for i in qm.range(3):
qubits[i] = h(qubits[i])
return qubits
@QKernel
def my_kernel2(qubits: Array[Qubit, Literal[5]]) -> Array[Qubit, Literal[5]]:
for i in qm.range(1, 4): # i = 1, 2, 3
qubits[i] = h(qubits[i])
return qubitsshould_trace_for_loop [source]¶
def should_trace_for_loop(start: Any, stop: Any, step: Any) -> boolDecide whether a qmc.range body must be traced.
The frontend executes loop bodies once to capture a ForOperation.
When all bounds are concrete and Python’s range would execute
zero times, tracing the body would incorrectly leak borrow /
destructive-consume state into the enclosing scope. Symbolic or
invalid bounds stay conservative and trace the body so the normal
compiler validation path reports any errors.
Parameters:
| Name | Type | Description |
|---|---|---|
start | typing.Any | Loop start bound. |
stop | typing.Any | Loop stop bound. |
step | typing.Any | Loop step bound. |
Returns:
bool — False only for statically-known zero-trip loops; True
bool — otherwise.
transform_control_flow [source]¶
def transform_control_flow(func: Callable)while_loop [source]¶
def while_loop(cond: Callable) -> Generator[WhileLoop, None, None]Create a while loop whose condition is a measurement result.
The condition must be a Bit produced by qmc.measure().
Non-measurement conditions (classical variables, constants,
comparisons) are accepted at build time but will be rejected by
ValidateWhileContractPass during transpilation.
Parameters:
| Name | Type | Description |
|---|---|---|
cond | typing.Callable | A callable (lambda) that returns the loop condition. Must return a Bit handle originating from qmc.measure(). |
Yields:
WhileLoop — A marker object for the while loop context.
Example::
@qm.qkernel
def repeat_until_zero() -> qm.Bit:
q = qm.qubit("q")
q = qm.h(q)
bit = qm.measure(q)
while bit:
q = qm.qubit("q2")
q = qm.h(q)
bit = qm.measure(q)
return bitClasses¶
ControlFlowTransformer [source]¶
class ControlFlowTransformer(ast.NodeTransformer)Constructor¶
def __init__(
self,
global_names: set[str] | None = None,
param_names: set[str] | None = None,
namespace: dict[str, Any] | None = None,
) -> NoneAttributes¶
counter: intfor_func_nameif_func_nametype_registry: dict[str, ast.AST]while_func_name
Methods¶
visit_AnnAssign¶
def visit_AnnAssign(self, node: ast.AnnAssign) -> AnyDetect annotated assignments such as a: int = 0 and register
the type information.
visit_For¶
def visit_For(self, node: ast.For) -> Anyvisit_FunctionDef¶
def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDefProcess the function body with definition tracking.
Collects parameter names as the initial set of defined variables
and delegates to _visit_body_with_tracking for sequential
statement processing.
visit_If¶
def visit_If(self, node: ast.If) -> Anyvisit_While¶
def visit_While(self, node: ast.While) -> AnyQuantumRebindAnalyzer [source]¶
class QuantumRebindAnalyzer(ast.NodeVisitor)Detects forbidden quantum variable reassignment at the AST level.
Forbidden patterns (target is an existing quantum variable):
a = bwherebis quantum with a different origina = f(b, ...)wherebis quantum with a different origina = qm.qubit("...")silently discardingaa = some_opaque_call()where the call has no known quantum sourcea = b = exprchained assignment touching a quantum namea: qm.Qubit = ...(annotated form of the above)
Allowed patterns:
a = f(a, ...)(self-update)new = f(b, ...)(new binding — target was not quantum before)alias = q(new alias — target was not quantum before)a, b = f(a, b)/a, b = (g(b), h(a))(1-to-1 quantum permutation)
The analyzer is a single-pass ast.NodeVisitor and does not
model Python control flow precisely. To keep compile-time-if
dead-branch rebinds — which the IR’s CompileTimeIfLoweringPass
will later resolve by selecting one branch and discarding the
other — from being rejected at decoration time, visit_If /
visit_For / visit_While route every branch through
:meth:_visit_branch_scope, which snapshots quantum_vars
before and restores it after each body / orelse, AND
truncates any violations recorded inside the branch back to the
pre-branch length. Top-level (non-branch-internal) rebinds are
flagged as usual; branch-internal rebinds are deliberately not
reported at decoration time. The companion gap on the IR side
(AffineValidationPass does not detect silent-discard
patterns) is tracked in LIMITATIONS.md.
Constructor¶
def __init__(self, quantum_param_names: set[str]) -> NoneInitialize the analyzer with the kernel’s quantum parameter names.
Parameters:
| Name | Type | Description |
|---|---|---|
quantum_param_names | set[str] | Names of kernel parameters whose annotated type is a quantum handle (Qubit / Vector[Qubit]). Each is seeded into quantum_vars as its own origin. |
Attributes¶
quantum_vars: dict[str, str]violations: list[RebindViolation]
Methods¶
visit_AnnAssign¶
def visit_AnnAssign(self, node: ast.AnnAssign) -> NoneDispatch q: qm.Qubit = expr through the single-assign path.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.AnnAssign | The annotated assignment statement. Annotation-only forms (q: qm.Qubit with no RHS) are ignored — there is nothing to rebind. |
visit_Assign¶
def visit_Assign(self, node: ast.Assign) -> NoneDispatch a = expr / a, b = expr / a = b = expr.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.Assign | The assignment statement. |
visit_Call¶
def visit_Call(self, node: ast.Call) -> NoneApply consume effects when a classical-returning call is seen.
_check_single_assign / _check_tuple_assign already
invoke :meth:_consume_quantum_args when a classical-returning
call appears on the RHS of an assignment. This visitor handles
the cases where the same call appears outside an assignment:
as a bare expression statement (qm.measure(q)), inside an
if / while condition, inside a for iterable, or
nested inside any other expression visited via
generic_visit. Without this hook, those forms would leave
q in quantum_vars and trip a false-positive
FRESH_ALLOCATION violation on a later rebind of q.
Re-visiting from inside a covered assignment path is benign:
_consume_quantum_args is idempotent — by the time
visit_Call runs as part of generic_visit after the
assignment dispatch, the relevant origins are already gone
from quantum_vars and the second call is a no-op.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.Call | The call expression. |
visit_For¶
def visit_For(self, node: ast.For) -> NoneVisit a for loop’s body and else with branch-local scope.
node.iter is walked first (before the branch-scope
snapshot) so that any consume effect inside the iterable
expression is reflected in the outer analyzer state. The
loop target itself is not modeled — Qamomile’s frontend
rewrites qmc.range(...) loops via the control-flow
transformer, so the iterator variable is a classical index
and never quantum.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.For | The for statement. |
visit_If¶
def visit_If(self, node: ast.If) -> NoneVisit if/else body with branch-local scope.
node.test is walked first (before the branch-scope
snapshot) so that any consume effect inside the condition
(e.g. if qm.measure(q):) is reflected in the outer
analyzer state, not silently rolled back when the branch
scope restores. See :meth:_visit_branch_scope for the
snapshot-restore protocol applied to body and orelse.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.If | The if statement. |
visit_While¶
def visit_While(self, node: ast.While) -> NoneVisit a while loop’s body and else with branch-local scope.
Same protocol as :meth:visit_If. node.test is walked
before the branch-scope snapshot.
Parameters:
| Name | Type | Description |
|---|---|---|
node | ast.While | The while statement. |
RebindSourceKind [source]¶
class RebindSourceKind(enum.StrEnum)Discriminator for the source of a detected rebind violation.
Each value classifies why the analyzer believes an existing quantum binding is being silently discarded, and lets downstream error-message formatting render a domain-appropriate explanation instead of forcing a generic “different quantum variable” sentence onto, e.g., a fresh allocation.
Members:
DIRECT_ALIAS: q = other_q or q = qs[i].
QUANTUM_ARG: q = f(other_q, ...) where other_q has a
different origin than q.
FRESH_ALLOCATION: q = qm.qubit(...) /
qm.qubit_array(...) — the original quantum state is
silently discarded in favor of a freshly allocated one.
UNKNOWN_CALL: q = some_func(...) where the call references
no known quantum variable and is not a recognized quantum
constructor; conservatively treated as a rebind because
the original q is not threaded through the RHS.
CHAINED_ASSIGNMENT: q1 = q2 = expr where at least one
target is an existing quantum variable; chained binding
semantics are too ambiguous to verify self-update.
Attributes¶
CHAINED_ASSIGNMENTDIRECT_ALIASFRESH_ALLOCATIONQUANTUM_ARGUNKNOWN_CALL
RebindViolation [source]¶
class RebindViolationA detected forbidden quantum variable rebinding.
Constructor¶
def __init__(
self,
target_name: str,
source_name: str | None,
source_kind: RebindSourceKind,
func_name: str | None,
lineno: int,
source_expr: str | None = None,
) -> NoneAttributes¶
func_name: str | Nonelineno: intsource_expr: str | Nonesource_kind: RebindSourceKindsource_name: str | Nonetarget_name: str
VariableCollector [source]¶
class VariableCollector(ast.NodeVisitor)Collect variables used and mutated within a block.
Excludes:
Function names in calls (func in Call)
Global base objects in attribute accesses (value in Attribute)
Global variables (modules, builtins, etc.)
Constructor¶
def __init__(self, global_names: set[str] | None = None)Attributes¶
incoming_vars: set[str] Variables that must come from an outer scope (first use is Load).load_vars: set[str] Variables referenced in Load context (actually read).locally_defined_vars: set[str] Variables first defined (Store) within this scope.store_vars: set[str] Variables assigned in Store context.vars
Methods¶
visit_Assign¶
def visit_Assign(self, node: ast.Assign)Visit the RHS first to match Python’s evaluation order.
q1 = qm.h(q1) → RHS q1 (Load) is first → first_context is “Load”
cond2 = qm.measure(q2) → RHS q2 (Load) first, LHS cond2 (Store) after
visit_Attribute¶
def visit_Attribute(self, node: ast.Attribute)Record the base name of an attribute access.
Global names such as module names (qm.h) are excluded as before,
while user variables (qs.shape) are treated as Load.
visit_AugAssign¶
def visit_AugAssign(self, node: ast.AugAssign)AugAssign (e.g. x += 1) is an implicit Read-before-Write.
Visit the RHS first and record Name targets as both Load and Store. first_context is “Load” (the existing value is read first).
visit_Call¶
def visit_Call(self, node: ast.Call)Exclude the function name of a call.
visit_FunctionDef¶
def visit_FunctionDef(self, node: ast.FunctionDef)Skip traversal of inner function definitions.
visit_Name¶
def visit_Name(self, node: ast.Name)Collect variable names (only those not in the exclude list).
qamomile.circuit.frontend.composite_gate¶
Frontend interface for composite gates.
Overview¶
| Function | Description |
|---|---|
composite_gate | Decorator to create a CompositeGate from a qkernel function or as a stub. |
get_current_tracer | |
trace | Context manager to set the current tracer. |
| Class | Description |
|---|---|
Block | Unified block representation for all pipeline stages. |
BlockKind | Classification of block structure for pipeline stages. |
CompositeGate | Base class for user-facing composite gate definitions. |
CompositeGateOperation | Represents a composite gate (QPE, QFT, etc.) as a single operation. |
CompositeGateType | Registry of known composite gate types. |
DecompositionStrategy | Protocol for defining decomposition strategies. |
Qubit | |
ResourceMetadata | Resource estimation metadata for composite gates. |
Tracer | |
Value | A typed SSA value in the IR. |
Vector | 1-dimensional array type. |
Functions¶
composite_gate [source]¶
def composite_gate(
func: Callable | None = None,
*,
stub: bool = False,
name: str = '',
num_qubits: int | None = None,
num_controls: int = 0,
resource_metadata: ResourceMetadata | None = None,
gate_type: CompositeGateType = CompositeGateType.CUSTOM,
) -> _WrappedCompositeGate | _StubCompositeGate | Callable[[Callable], _WrappedCompositeGate | _StubCompositeGate]Decorator to create a CompositeGate from a qkernel function or as a stub.
Usage with qkernel (implementation provided):
composite_gate [source](name=“my_qft”)
qkernel [source]
def my_qft(q0: Qubit, q1: Qubit) -> tuple[Qubit, Qubit]:
q0 = h(q0)
q0, q1 = cp(q0, q1, pi/2)
q1 = h(q1)
return q0, q1
# Usage:
q0, q1 = my_qft(q0, q1)Usage as stub (no implementation, for resource estimation):
composite_gate [source](
stub=True, name=“oracle”, num_qubits=5,
resource_metadata=ResourceMetadata(query_complexity=100, t_gates=10),
)
def oracle():
pass
# Usage:
results = oracle(*qubits)
metadata = oracle.get_resource_metadata()Parameters:
| Name | Type | Description |
|---|---|---|
func | Callable | None | The qkernel function (when used without arguments) |
stub | bool | If True, create a stub gate with no implementation |
name | str | Name for the composite gate |
num_qubits | int | None | Number of target qubits (required for stub) |
num_controls | int | Number of control qubits (default: 0) |
resource_metadata | ResourceMetadata | None | ResourceMetadata for resource estimation (stub mode) |
gate_type | CompositeGateType | The type of composite gate (default: CUSTOM) |
Returns:
_WrappedCompositeGate | _StubCompositeGate | Callable[[Callable], _WrappedCompositeGate | _StubCompositeGate] — A CompositeGate instance that can be called like a gate function.
get_current_tracer [source]¶
def get_current_tracer() -> Tracertrace [source]¶
def trace(tracer: Tracer | None = None) -> Generator[Tracer, None, None]Context manager to set the current tracer.
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(),
param_slots: tuple[ParamSlot, ...] = tuple(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]param_slots: tuple[ParamSlot, ...]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.
BlockKind [source]¶
class BlockKind(Enum)Classification of block structure for pipeline stages.
Attributes¶
AFFINEANALYZEDHIERARCHICALTRACED
CompositeGate [source]¶
class CompositeGate(abc.ABC)Base class for user-facing composite gate definitions.
Subclasses can define composite gates in two ways:
Using _decompose() (recommended for users): Define the gate decomposition using frontend syntax (same as
qkernel[source]).class QFT(CompositeGate): def __init__(self, num_qubits: int): self._num_qubits = num_qubits @property def num_target_qubits(self) -> int: return self._num_qubits def _decompose(self, qubits: Vector[Qubit]) -> Vector[Qubit]: # Use frontend syntax: qm.h(), qm.cp(), qm.range(), etc. n = self._num_qubits for j in qmc.range(n - 1, -1, -1): qubits[j] = qmc.h(qubits[j]) for k in qmc.range(j - 1, -1, -1): angle = math.pi / (2 ** (j - k)) qubits[j], qubits[k] = qmc.cp(qubits[j], qubits[k], angle) return qubits def _resources(self) -> ResourceMetadata: return ResourceMetadata(t_gates=0)Using get_implementation() (advanced): Return a pre-built Block directly.
Example usage:
# Factory function pattern
def qft(qubits: Vector[Qubit]) -> Vector[Qubit]:
n = _get_size(qubits)
return QFT(n)(qubits)
# Direct class usage
result = QFT(3)(*qubit_list)Attributes¶
custom_name: strgate_type: CompositeGateTypenum_control_qubits: int Number of control qubits (default: 0).num_target_qubits: int Number of target qubits this gate operates on.
Methods¶
build_decomposition¶
def build_decomposition(self, *qubits: Qubit = (), **params: Any = {}) -> Block | NoneBuild the decomposition circuit dynamically.
Override this method to provide a decomposition that depends on runtime arguments (e.g., QPE needs the unitary Block).
This method is called by InlinePass when inlining composite gates that have dynamic implementations.
Parameters:
| Name | Type | Description |
|---|---|---|
*qubits | Qubit | The qubits passed to the gate |
**params | Any | Additional parameters (e.g., unitary for QPE) |
Returns:
Block | None — Block containing the decomposition, or None if not available.
Example:
class QPE(CompositeGate):
def build_decomposition(self, *qubits, **params):
unitary = params.get("unitary")
# Build QPE circuit using the unitary
return self._build_qpe_impl(qubits, unitary)get_implementation¶
def get_implementation(self) -> Block | NoneGet the implementation Block, if any.
Return None for stub gates (used in resource estimation). Override in subclasses to provide implementation.
Note: If _decompose() is defined, it takes precedence over this method.
get_resource_metadata¶
def get_resource_metadata(self) -> ResourceMetadata | NoneGet resource estimation metadata.
Returns _resources() if defined, otherwise None. Override _resources() to provide resource hints.
get_resources_for_strategy¶
def get_resources_for_strategy(self, strategy_name: str | None = None) -> ResourceMetadata | NoneGet resource metadata for a specific strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
strategy_name | str | None | Strategy to query, or None for default |
Returns:
ResourceMetadata | None — ResourceMetadata for the strategy, or None if not available
get_strategy¶
@classmethod
def get_strategy(cls, name: str | None = None) -> 'DecompositionStrategy | None'Get a registered decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | None | Strategy name, or None for default strategy |
Returns:
'DecompositionStrategy | None' — DecompositionStrategy instance, or None if not found
list_strategies¶
@classmethod
def list_strategies(cls) -> list[str]List all registered strategy names.
Returns:
list[str] — List of strategy names
register_strategy¶
@classmethod
def register_strategy(cls, name: str, strategy: 'DecompositionStrategy') -> NoneRegister a decomposition strategy for this gate type.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy identifier (e.g., “standard”, “approximate”) |
strategy | 'DecompositionStrategy' | DecompositionStrategy instance |
Example:
QFT.register_strategy("approximate", ApproximateQFTStrategy(k=3))set_default_strategy¶
@classmethod
def set_default_strategy(cls, name: str) -> NoneSet the default decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy name to use as default |
Raises:
ValueError— If strategy is not registered
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.
CompositeGateType [source]¶
class CompositeGateType(enum.Enum)Registry of known composite gate types.
Attributes¶
CUSTOMIQFTQFTQPE
DecompositionStrategy [source]¶
class DecompositionStrategy(Protocol)Protocol for defining decomposition strategies.
A decomposition strategy provides:
A unique name for identification
A decompose method that performs the actual decomposition
Resource estimation for the decomposition
Strategies allow the same composite gate to have multiple implementations with different trade-offs (e.g., precision vs. gate count).
Attributes¶
name: str Unique identifier for this strategy.
Methods¶
decompose¶
def decompose(self, qubits: tuple['Qubit', ...]) -> tuple['Qubit', ...]Perform the decomposition.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | tuple['Qubit', ...] | Input qubits to decompose |
Returns:
tuple['Qubit', ...] — Output qubits after decomposition
resources¶
def resources(self, num_qubits: int) -> 'ResourceMetadata'Return resource estimates for this decomposition.
Parameters:
| Name | Type | Description |
|---|---|---|
num_qubits | int | Number of qubits the gate operates on |
Returns:
'ResourceMetadata' — ResourceMetadata with gate counts, depth estimates, etc.
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
ResourceMetadata [source]¶
class ResourceMetadataResource estimation metadata for composite gates.
Gate count fields mirror GateCount categories.
None semantics:
Fields left as None mean “unknown/unspecified”. During extraction, gate_counter treats None as 0, which may undercount resources if the true value is nonzero. To ensure accurate resource estimates, set all relevant fields explicitly.
When total_gates is set but some of single_qubit_gates, two_qubit_gates, or multi_qubit_gates are None, the extractor emits a UserWarning if the known sub-total is less than total_gates, indicating potentially missing gate category data.
Constructor¶
def __init__(
self,
query_complexity: int | None = None,
t_gates: int | None = None,
ancilla_qubits: int = 0,
total_gates: int | None = None,
single_qubit_gates: int | None = None,
two_qubit_gates: int | None = None,
multi_qubit_gates: int | None = None,
clifford_gates: int | None = None,
rotation_gates: int | None = None,
custom_metadata: dict[str, Any] = dict(),
) -> NoneAttributes¶
ancilla_qubits: intclifford_gates: int | Nonecustom_metadata: dict[str, Any]multi_qubit_gates: int | Nonequery_complexity: int | Nonerotation_gates: int | Nonesingle_qubit_gates: int | Nonet_gates: int | Nonetotal_gates: int | Nonetwo_qubit_gates: int | None
Tracer [source]¶
class TracerConstructor¶
def __init__(self, _operations: list[Operation] = list()) -> NoneAttributes¶
operations: list[Operation]
Methods¶
add_operation¶
def add_operation(self, op) -> NoneValue [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
qamomile.circuit.frontend.constructors¶
Overview¶
| Function | Description |
|---|---|
bit | Create a Bit handle from a boolean/int literal or declare a named Bit parameter. |
float_ | Create a Float handle from a float literal or declare a named Float parameter. |
get_current_tracer | |
qubit | Create a new qubit and emit a QInitOperation. |
qubit_array | Create a new qubit array (vector/matrix/tensor) and emit QInitOperations. |
uint | Create a UInt handle from an integer literal or a named parameter. |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
QInitOperation | Initialize the qubit |
Value | A typed SSA value in the IR. |
Functions¶
bit [source]¶
def bit(arg: bool | str | int) -> BitCreate a Bit handle from a boolean/int literal or declare a named Bit parameter.
float_ [source]¶
def float_(arg: float | str) -> FloatCreate a Float handle from a float literal or declare a named Float parameter.
get_current_tracer [source]¶
def get_current_tracer() -> Tracerqubit [source]¶
def qubit(name: str) -> QubitCreate a new qubit and emit a QInitOperation.
qubit_array [source]¶
def qubit_array(
shape: UInt | int | tuple[UInt | int, ...],
name: str,
) -> Vector[Qubit] | Matrix[Qubit] | Tensor[Qubit]Create a new qubit array (vector/matrix/tensor) and emit QInitOperations.
uint [source]¶
def uint(arg: int | str) -> UIntCreate a UInt handle from an integer literal or a named parameter.
Parameters:
| Name | Type | Description |
|---|---|---|
arg | int | str | An integer literal to bake in as a compile-time constant, or a str naming a symbolic UInt parameter. A bool is rejected: True / False are not valid integer values here even though bool subclasses int. (Sign is not validated here -- a negative literal is accepted and baked in as-is.) |
Returns:
UInt — A constant-valued handle for an int argument, or a named
symbolic handle for a str argument.
Raises:
TypeError— Ifargis neither a plainintnor astr(in particular, if it is abool).
Classes¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]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
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.decomposition¶
Decomposition strategy framework for composite gates.
This module provides the infrastructure for defining multiple decomposition patterns for composite gates, enabling flexible gate synthesis strategies.
Example:
class StandardQFTStrategy:
@property
def name(self) -> str:
return "standard"
def decompose(self, qubits: tuple[Qubit, ...]) -> tuple[Qubit, ...]:
# Full precision QFT implementation
...
def resources(self, num_qubits: int) -> ResourceMetadata:
return ResourceMetadata(...)
class ApproximateQFTStrategy:
def __init__(self, truncation_depth: int = 3):
self._k = truncation_depth
@property
def name(self) -> str:
return f"approximate_k{self._k}"
def decompose(self, qubits: tuple[Qubit, ...]) -> tuple[Qubit, ...]:
# Truncated rotations QFT
...
# Register strategies
QFT.register_strategy("standard", StandardQFTStrategy())
QFT.register_strategy("approximate", ApproximateQFTStrategy(truncation_depth=3))Overview¶
| Function | Description |
|---|---|
get_global_registry | Get the global strategy registry. |
get_strategy | Get a strategy from the global registry. |
register_strategy | Register a strategy in the global registry. |
| Class | Description |
|---|---|
DecompositionConfig | Configuration for decomposition strategy selection. |
DecompositionStrategy | Protocol for defining decomposition strategies. |
Qubit | |
ResourceMetadata | Resource estimation metadata for composite gates. |
StrategyRegistry | Registry for managing decomposition strategies. |
Functions¶
get_global_registry [source]¶
def get_global_registry() -> StrategyRegistryGet the global strategy registry.
Returns:
StrategyRegistry — The global StrategyRegistry instance
get_strategy [source]¶
def get_strategy(
gate_name: str,
strategy_name: str | None = None,
) -> DecompositionStrategy | NoneGet a strategy from the global registry.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name |
strategy_name | str | None | The strategy name (uses “standard” if None) |
Returns:
DecompositionStrategy | None — The strategy instance, or None if not found
register_strategy [source]¶
def register_strategy(gate_name: str, strategy_name: str, strategy: DecompositionStrategy) -> NoneRegister a strategy in the global registry.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name (e.g., “qft”, “iqft”) |
strategy_name | str | The strategy name (e.g., “standard”, “approximate”) |
strategy | DecompositionStrategy | The strategy instance |
Classes¶
DecompositionConfig [source]¶
class DecompositionConfigConfiguration for decomposition strategy selection.
This configuration is passed to the transpiler to control which decomposition strategies are used for composite gates.
Constructor¶
def __init__(
self,
strategy_overrides: dict[str, str] = dict(),
strategy_params: dict[str, dict[str, Any]] = dict(),
default_strategy: str = 'standard',
) -> NoneAttributes¶
default_strategy: strstrategy_overrides: dict[str, str]strategy_params: dict[str, dict[str, Any]]
Methods¶
get_strategy_for_gate¶
def get_strategy_for_gate(self, gate_name: str) -> strGet the strategy name for a specific gate.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name (e.g., “qft”, “iqft”) |
Returns:
str — Strategy name to use
get_strategy_params¶
def get_strategy_params(self, strategy_name: str) -> dict[str, Any]Get parameters for a specific strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
strategy_name | str | The strategy name |
Returns:
dict[str, Any] — Dictionary of parameters
DecompositionStrategy [source]¶
class DecompositionStrategy(Protocol)Protocol for defining decomposition strategies.
A decomposition strategy provides:
A unique name for identification
A decompose method that performs the actual decomposition
Resource estimation for the decomposition
Strategies allow the same composite gate to have multiple implementations with different trade-offs (e.g., precision vs. gate count).
Attributes¶
name: str Unique identifier for this strategy.
Methods¶
decompose¶
def decompose(self, qubits: tuple['Qubit', ...]) -> tuple['Qubit', ...]Perform the decomposition.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | tuple['Qubit', ...] | Input qubits to decompose |
Returns:
tuple['Qubit', ...] — Output qubits after decomposition
resources¶
def resources(self, num_qubits: int) -> 'ResourceMetadata'Return resource estimates for this decomposition.
Parameters:
| Name | Type | Description |
|---|---|---|
num_qubits | int | Number of qubits the gate operates on |
Returns:
'ResourceMetadata' — ResourceMetadata with gate counts, depth estimates, etc.
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
ResourceMetadata [source]¶
class ResourceMetadataResource estimation metadata for composite gates.
Gate count fields mirror GateCount categories.
None semantics:
Fields left as None mean “unknown/unspecified”. During extraction, gate_counter treats None as 0, which may undercount resources if the true value is nonzero. To ensure accurate resource estimates, set all relevant fields explicitly.
When total_gates is set but some of single_qubit_gates, two_qubit_gates, or multi_qubit_gates are None, the extractor emits a UserWarning if the known sub-total is less than total_gates, indicating potentially missing gate category data.
Constructor¶
def __init__(
self,
query_complexity: int | None = None,
t_gates: int | None = None,
ancilla_qubits: int = 0,
total_gates: int | None = None,
single_qubit_gates: int | None = None,
two_qubit_gates: int | None = None,
multi_qubit_gates: int | None = None,
clifford_gates: int | None = None,
rotation_gates: int | None = None,
custom_metadata: dict[str, Any] = dict(),
) -> NoneAttributes¶
ancilla_qubits: intclifford_gates: int | Nonecustom_metadata: dict[str, Any]multi_qubit_gates: int | Nonequery_complexity: int | Nonerotation_gates: int | Nonesingle_qubit_gates: int | Nonet_gates: int | Nonetotal_gates: int | Nonetwo_qubit_gates: int | None
StrategyRegistry [source]¶
class StrategyRegistryRegistry for managing decomposition strategies.
This class provides a centralized registry for strategies, allowing them to be looked up by name across the transpiler pipeline.
Example:
registry = StrategyRegistry()
registry.register("qft", "standard", StandardQFTStrategy())
registry.register("qft", "approximate", ApproximateQFTStrategy())
strategy = registry.get("qft", "standard")Constructor¶
def __init__(self) -> NoneInitialize empty registry.
Methods¶
get¶
def get(
self,
gate_name: str,
strategy_name: str | None = None,
) -> DecompositionStrategy | NoneGet a strategy for a gate.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name |
strategy_name | str | None | The strategy name (uses “standard” if None) |
Returns:
DecompositionStrategy | None — The strategy instance, or None if not found
list_gates¶
def list_gates(self) -> list[str]List all gates with registered strategies.
Returns:
list[str] — List of gate names
list_strategies¶
def list_strategies(self, gate_name: str) -> list[str]List available strategies for a gate.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name |
Returns:
list[str] — List of strategy names
register¶
def register(
self,
gate_name: str,
strategy_name: str,
strategy: DecompositionStrategy,
) -> NoneRegister a strategy for a gate.
Parameters:
| Name | Type | Description |
|---|---|---|
gate_name | str | The gate name (e.g., “qft”, “iqft”) |
strategy_name | str | The strategy name (e.g., “standard”, “approximate”) |
strategy | DecompositionStrategy | The strategy instance |
qamomile.circuit.frontend.func_to_block¶
Overview¶
| Function | Description |
|---|---|
build_param_slots | Build a ParamSlot tuple for the classical arguments of a kernel. |
create_dummy_handle | Create a dummy Handle instance based on ValueType. |
create_dummy_input | Create a dummy input based on parameter type annotation. |
func_to_block | Convert a function to a hierarchical Block. |
get_current_tracer | |
handle_type_map | Map Handle type to ValueType. |
is_array_type | Check if type is a Vector, Matrix, or Tensor subclass. |
is_dict_type | Check if type is a Dict handle type. |
is_tuple_type | Check if type is a Tuple handle type. |
trace | Context manager to set the current tracer. |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
Bit | |
BitType | Type representing a classical bit. |
Block | Unified block representation for all pipeline stages. |
BlockKind | Classification of block structure for pipeline stages. |
Dict | Dict handle for qkernel functions. |
DictType | Type representing a dictionary mapping keys to values. |
DictValue | A dictionary value stored as stable ordered entries. |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
Observable | Handle representing a Hamiltonian observable parameter. |
ObservableType | Type representing a Hamiltonian observable parameter. |
ParamKind | Lifecycle classification for a classical kernel argument. |
ParamSlot | Metadata for a single classical kernel argument. |
QInitOperation | Initialize the qubit |
Qubit | |
ReturnOperation | Explicit return operation marking the end of a block with return values. |
Tracer | |
Tuple | Tuple handle for qkernel functions. |
TupleType | Type representing a tuple of values. |
TupleValue | A tuple of IR values for structured data. |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
ValueType | Base class for all value types in the IR. |
Constants¶
TYPE_MAPPING:dict[Any, Any]={int: UIntType, float: FloatType, bool: BitType}
Functions¶
build_param_slots [source]¶
def build_param_slots(
signature: inspect.Signature,
input_types: dict[str, Any],
*,
parameters: list[str] | None = None,
kwargs: dict[str, Any] | None = None,
qubit_sizes: dict[str, int] | None = None,
bind_defaults: bool,
) -> tuple[ParamSlot, ...]Build a ParamSlot tuple for the classical arguments of a kernel.
Mirrors the argument-classification logic in
QKernel._create_traced_block so the resulting slot list reflects
the same decisions that drive symbolic-vs-bound input creation. Only
classical (non-quantum, non-Tuple, non-Dict) arguments are included;
pure-quantum arguments live in Block.input_values instead.
Parameters:
| Name | Type | Description |
|---|---|---|
signature | inspect.Signature | The kernel function’s signature. |
input_types | dict[str, Any] | Resolved frontend type annotations keyed by argument name (typically QKernel.input_types or the equivalent computed in func_to_block). |
parameters | list[str] | None | Names explicitly requested as runtime parameters via parameters=[...]. None is treated as an empty list. |
kwargs | dict[str, Any] | None | Concrete values supplied via bindings / direct kwargs. None is treated as an empty dict. |
qubit_sizes | dict[str, int] | None | Optional mapping from Vector[Qubit] parameter names to their integer sizes; these are quantum inputs and are not included in the slot list. |
bind_defaults | bool | When True, Python signature defaults are treated as COMPILE_TIME_BOUND with bound_value=default. When False (e.g., the func_to_block path that does not bake in defaults), defaulted arguments stay RUNTIME_PARAMETER and the default appears only in ParamSlot.default. |
Returns:
tuple[ParamSlot, ...] — tuple[ParamSlot, ...]: One slot per classical argument, in the
order they appear in signature.parameters.
create_dummy_handle [source]¶
def create_dummy_handle(value_type: ValueType, name: str = 'dummy', emit_init: bool = True) -> HandleCreate a dummy Handle instance based on ValueType.
Parameters:
| Name | Type | Description |
|---|---|---|
value_type | ValueType | The IR type for the value. |
name | str | Name for the value. |
emit_init | bool | If True, emit QInitOperation for qubit types (requires active tracer). |
Used for creating input parameters during tracing.
create_dummy_input [source]¶
def create_dummy_input(
param_type: Any,
name: str = 'param',
emit_init: bool = True,
*,
shape: tuple[int, ...] | None = None,
) -> HandleCreate a dummy input based on parameter type annotation.
Parameters:
| Name | Type | Description |
|---|---|---|
param_type | Any | The type annotation for the parameter. |
name | str | Name for the value. |
emit_init | bool | If True, emit QInitOperation for qubit arrays (default: True). Set to False when creating a nested Block’s internal dummy inputs, or when the dummy will receive its qubits from a caller’s CallBlockOperation. |
shape | tuple[int, ...] | None | Optional concrete shape for array types. When provided, the dummy array’s shape Values carry compile-time constants instead of symbolic placeholders. Used by call-time sub-kernel specialization so that shape-dependent stdlib helpers (qft / iqft / qpe) resolve get_size to a concrete integer and emit the correct gate sequence. Ignored for non-array types. Default: None (symbolic shape). |
Returns:
Handle — A frontend Handle wrapping a dummy Value or ArrayValue
suitable for use as a function-parameter input during
tracing.
Raises:
TypeError— Ifparam_typeis not a supported parameter type, or if a Tuple/array annotation is missing its element type(s).
func_to_block [source]¶
def func_to_block(func: Callable) -> BlockConvert a function to a hierarchical Block.
Example:
def my_func(a: UInt, b: UInt) -> tuple[UInt]:
c = a + b
return (c, )
block = func_to_block(my_func)get_current_tracer [source]¶
def get_current_tracer() -> Tracerhandle_type_map [source]¶
def handle_type_map(handle_type: type[Handle] | type) -> ValueTypeMap Handle type to ValueType.
is_array_type [source]¶
def is_array_type(t: Any) -> boolCheck if type is a Vector, Matrix, or Tensor subclass.
is_dict_type [source]¶
def is_dict_type(t: Any) -> boolCheck if type is a Dict handle type.
is_tuple_type [source]¶
def is_tuple_type(t: Any) -> boolCheck if type is a Tuple handle type.
trace [source]¶
def trace(tracer: Tracer | None = None) -> Generator[Tracer, None, None]Context manager to set the current tracer.
Classes¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
BitType [source]¶
class BitType(ClassicalTypeMixin, ValueType)Type representing a classical bit.
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(),
param_slots: tuple[ParamSlot, ...] = tuple(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]param_slots: tuple[ParamSlot, ...]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.
BlockKind [source]¶
class BlockKind(Enum)Classification of block structure for pipeline stages.
Attributes¶
AFFINEANALYZEDHIERARCHICALTRACED
Dict [source]¶
class Dict(Handle, Generic[K, V])Dict handle for qkernel functions.
Represents a dictionary mapping keys to values, commonly used for Ising coefficients like {(i, j): Jij}.
Example:
@qmc.qkernel
def ising_cost(
q: qmc.Vector[qmc.Qubit],
ising: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qConstructor¶
def __init__(
self,
value: DictValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_entries: list[tuple[Handle, Handle]] = list(),
_size: UInt | None = None,
_key_type: type | None = None,
) -> NoneAttributes¶
size: UInt Return the number of entries as a UInt handle.value: DictValue
Methods¶
items¶
def items(self) -> DictItemsIterator[K, V]Return an iterator over (key, value) pairs.
DictType [source]¶
class DictType(ValueType)Type representing a dictionary mapping keys to values.
Unlike simple types, DictType stores the key and value types, so equality and hashing depend on those types. When key_type and value_type are None, represents a generic Dict type.
Quantum/classical classification is derived from key/value types.
Constructor¶
def __init__(
self,
key_type: ValueType | None = None,
value_type: ValueType | None = None,
) -> NoneAttributes¶
key_type: ValueType | Nonevalue_type: ValueType | None
Methods¶
is_classical¶
def is_classical(self) -> boolis_quantum¶
def is_quantum(self) -> boollabel¶
def label(self) -> strDictValue [source]¶
class DictValue(_MetadataValueMixin)A dictionary value stored as stable ordered entries.
Constructor¶
def __init__(
self,
name: str,
entries: tuple[tuple[TupleValue | Value, Value], ...] = tuple(),
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
) -> NoneAttributes¶
entries: tuple[tuple[TupleValue | Value, Value], ...]logical_id: strmetadata: ValueMetadataname: strtype: DictTypeuuid: str
Methods¶
is_constant¶
def is_constant(self) -> boolnext_version¶
def next_version(self) -> DictValueFloat [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
Observable [source]¶
class Observable(Handle)Handle representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1) + 0.5 * qm_o.X(0)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
# Use Hamiltonian from bindings
return qm.expval(q, H)
# Pass via bindings
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneObservableType [source]¶
class ObservableType(ObjectTypeMixin, ValueType)Type representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example usage:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
return qm.expval(q, H)
# H is passed as binding
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(self) -> NoneParamKind [source]¶
class ParamKind(enum.Enum)Lifecycle classification for a classical kernel argument.
Values:
RUNTIME_PARAMETER: The argument is intended to be bound at
execution time by the backend (or, more generally, by the
outer caller in a hybrid loop). It survives the
compilation pipeline as a symbolic parameter.
COMPILE_TIME_BOUND: The argument was provided as a binding
(or via a Python default) and is folded into the IR by
resolve_parameter_shapes / partial_eval. No
symbolic counterpart remains in the emitted circuit.
Attributes¶
COMPILE_TIME_BOUNDRUNTIME_PARAMETER
ParamSlot [source]¶
class ParamSlotMetadata for a single classical kernel argument.
A ParamSlot describes one position in the kernel’s classical
parameter contract — its declared type, whether it is a runtime
parameter or a compile-time-bound value, the Python default (if
any), the actually-bound value (when kind is
COMPILE_TIME_BOUND), and any outer-DSL hints. Slots are
immutable; pipeline passes that need to update a slot must clone
via dataclasses.replace.
The slot is identified by name, which matches the kernel’s
Python parameter name and the corresponding entry in
Block.label_args. A slot’s name MUST never overlap between
RUNTIME_PARAMETER and COMPILE_TIME_BOUND instances within
one Block (this mirrors the project-level bindings /
parameters disjointness rule).
Constructor¶
def __init__(
self,
name: str,
type: 'ValueType',
kind: ParamKind,
ndim: int = 0,
default: Any = None,
bound_value: Any = None,
differentiable: bool = False,
) -> NoneAttributes¶
bound_value: Anydefault: Anydifferentiable: boolkind: ParamKindname: strndim: inttype: ‘ValueType’
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
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
ReturnOperation [source]¶
class ReturnOperation(Operation)Explicit return operation marking the end of a block with return values.
This operation represents an explicit return statement in the IR. It takes the values to be returned as operands and produces no results (it is a terminal operation that transfers control flow back to the caller).
operands: [Value, ...] - The values to return (may be empty for void returns) results: [] - Always empty (terminal operation)
Example:
A function that returns two values (a UInt and a Float):
ReturnOperation(
operands=[uint_value, float_value],
results=[],
)
The signature would be:
operands=[ParamHint("return_0", UIntType()), ParamHint("return_1", FloatType())]
results=[]Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Return CLASSICAL as this is a control flow operation without quantum effects.signature: Signature Return the signature with operands for each return value and no results.
Tracer [source]¶
class TracerConstructor¶
def __init__(self, _operations: list[Operation] = list()) -> NoneAttributes¶
operations: list[Operation]
Methods¶
add_operation¶
def add_operation(self, op) -> NoneTuple [source]¶
class Tuple(Handle, Generic[K, V])Tuple handle for qkernel functions.
Represents a tuple of values, commonly used for multi-index keys like (i, j) in Ising models.
Example:
@qmc.qkernel
def my_kernel(idx: qmc.Tuple[qmc.UInt, qmc.UInt]) -> qmc.UInt:
i, j = idx
return i + jConstructor¶
def __init__(
self,
value: TupleValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_elements: tuple[Handle, ...] = tuple(),
) -> NoneAttributes¶
value: TupleValue
TupleType [source]¶
class TupleType(ValueType)Type representing a tuple of values.
Unlike simple types, TupleType stores the types of its elements, so equality and hashing depend on the element types.
Quantum/classical classification is derived from element types: quantum if any element is quantum, classical if all are classical.
Constructor¶
def __init__(self, element_types: tuple[ValueType, ...]) -> NoneAttributes¶
element_types: tuple[ValueType, ...]
Methods¶
is_classical¶
def is_classical(self) -> boolis_quantum¶
def is_quantum(self) -> boollabel¶
def label(self) -> strTupleValue [source]¶
class TupleValue(_MetadataValueMixin)A tuple of IR values for structured data.
Constructor¶
def __init__(
self,
name: str,
elements: tuple[Value, ...] = tuple(),
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
) -> NoneAttributes¶
elements: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strtype: ‘TupleType’uuid: str
Methods¶
is_constant¶
def is_constant(self) -> boolnext_version¶
def next_version(self) -> TupleValueUInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
ValueType [source]¶
class ValueType(abc.ABC)Base class for all value types in the IR.
Type instances are compared by class - all instances of the same type class are considered equal. This allows using type instances as dictionary keys where all QubitType() instances match.
Methods¶
is_classical¶
def is_classical(self) -> boolis_object¶
def is_object(self) -> boolis_quantum¶
def is_quantum(self) -> boollabel¶
def label(self) -> strqamomile.circuit.frontend.handle¶
Overview¶
| Function | Description |
|---|---|
get_size | Return the size of a Vector handle as a Python integer. |
| Class | Description |
|---|---|
Bit | |
Dict | Dict handle for qkernel functions. |
Float | Floating-point handle with arithmetic operations. |
Handle | |
Matrix | 2-dimensional array type. |
Observable | Handle representing a Hamiltonian observable parameter. |
QFixed | |
Qubit | |
Tensor | N-dimensional array type (3 or more dimensions). |
Tuple | Tuple handle for qkernel functions. |
UInt | Unsigned integer handle with arithmetic operations. |
Vector | 1-dimensional array type. |
VectorView | Strided view over a parent Vector, backed by a sliced ArrayValue. |
Functions¶
get_size [source]¶
def get_size(arr: Vector[_H]) -> intReturn the size of a Vector handle as a Python integer.
Resolves the leading axis of arr.shape through two forms a
Vector shape entry can take:
A plain Python
int(built-in bound shape; this is what you get fromqmc.qubit_array(N, ...)for literalN).A
UInthandle whose underlyingValuecarries a compile-time constant (set byuint(literal),_create_bound_input, or partial evaluation).
A UInt handle whose underlying Value is not a constant is
treated as an unresolved symbolic dimension and raises
ValueError even when the handle has the dataclass-default
init_value=0. Falling back to init_value for that case
would silently turn a runtime-symbolic Vector[Float] parameter
into a “size 0” array, hiding programming errors. Callers that
need to handle symbolic shapes (e.g., to gracefully no-op when the
size is unknown) must catch the ValueError themselves.
Parameters:
| Name | Type | Description |
|---|---|---|
arr | Vector[Handle] | Vector handle whose first axis size is requested. |
Returns:
int — The first-axis size as a plain Python int.
Raises:
TypeError— If arr is not a 1-DVectorhandle (Vectoror itsVectorViewsubclass) — e.g., a scalarQubitwas passed where aVectoris required, a higher-rankMatrix/Tensorwas passed (this helper only resolves a 1-D first-axis size), or an unrelatedshape-bearing object such as a numpy array. This is a clearer signal than the bareAttributeErrorthatarr.shapewould otherwise raise, and it guards the stdlib / composite callers that resolve a register size through this helper.ValueError— If the shape cannot be resolved to a concrete integer — e.g., the Vector is a runtime-parametric handle without compile-time bindings, or carries aUIntdimension whose underlyingValuehas not been promoted to a constant.
Classes¶
Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
Dict [source]¶
class Dict(Handle, Generic[K, V])Dict handle for qkernel functions.
Represents a dictionary mapping keys to values, commonly used for Ising coefficients like {(i, j): Jij}.
Example:
@qmc.qkernel
def ising_cost(
q: qmc.Vector[qmc.Qubit],
ising: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qConstructor¶
def __init__(
self,
value: DictValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_entries: list[tuple[Handle, Handle]] = list(),
_size: UInt | None = None,
_key_type: type | None = None,
) -> NoneAttributes¶
size: UInt Return the number of entries as a UInt handle.value: DictValue
Methods¶
items¶
def items(self) -> DictItemsIterator[K, V]Return an iterator over (key, value) pairs.
Float [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
Matrix [source]¶
class Matrix(ArrayBase[T])2-dimensional array type.
Example:
import qamomile as qm
# Create a 3x4 matrix of qubits
matrix: qm.Matrix[qm.Qubit] = qm.Matrix(shape=(3, 4))
# Access elements (always requires 2 indices)
q = matrix[0, 1]
q = qm.h(q)
matrix[0, 1] = qConstructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, int | UInt] = (0, 0),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
Observable [source]¶
class Observable(Handle)Handle representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1) + 0.5 * qm_o.X(0)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
# Use Hamiltonian from bindings
return qm.expval(q, H)
# Pass via bindings
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneQFixed [source]¶
class QFixed(Handle)Constructor¶
def __init__(
self,
value: Value[QFixedType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QFixedType]
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
Tensor [source]¶
class Tensor(ArrayBase[T])N-dimensional array type (3 or more dimensions).
Example:
import qamomile as qm
# Create a 2x3x4 tensor of qubits
tensor: qm.Tensor[qm.Qubit] = qm.Tensor(shape=(2, 3, 4))
# Access elements (requires all indices)
q = tensor[0, 1, 2]
q = qm.h(q)
tensor[0, 1, 2] = qConstructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
Tuple [source]¶
class Tuple(Handle, Generic[K, V])Tuple handle for qkernel functions.
Represents a tuple of values, commonly used for multi-index keys like (i, j) in Ising models.
Example:
@qmc.qkernel
def my_kernel(idx: qmc.Tuple[qmc.UInt, qmc.UInt]) -> qmc.UInt:
i, j = idx
return i + jConstructor¶
def __init__(
self,
value: TupleValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_elements: tuple[Handle, ...] = tuple(),
) -> NoneAttributes¶
value: TupleValue
UInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
VectorView [source]¶
class VectorView(Vector[T])Strided view over a parent Vector, backed by a sliced ArrayValue.
A VectorView is produced by slicing a Vector (q[1::2],
q[a:b], etc.). It is a thin Vector subclass whose value
is a fresh ArrayValue with slice_of / slice_start /
slice_step metadata pointing back to the parent’s ArrayValue.
Element accesses go through Vector._get_element unchanged — the
IR element carries parent_array = sliced_av, and the emit-time
resolver walks the slice_of chain to produce the physical qubit
index. No affine translation happens in the view itself.
Because the sliced ArrayValue is a first-class IR Value,
the view can be passed as an operand of CallBlockOperation to
another @qkernel without the inline-trace special-case path
that earlier iterations required. Passing views through
expval / measure likewise operates on the sliced qubit
subset, not the root parent as a whole.
Linearity:
Slicing bulk-borrows the covered parent slots whenever
start, step and length are compile-time int
constants. While the view is live, accessing the corresponding
parent slot directly (q[0] after evens = q[0::2]) raises
QubitConsumedError. Under the strict-return policy the
view’s ownership is cleared only by two operations:
slice-assigning it back into the parent (
parent[a:b:c] = view) — this is the only path that fully releases the borrow without destroying the qubits;destructively consuming it (
measure(view)/cast(view, ...)/expval(view, H)) — the physical slots become consumed markers, no return needed.
Every other consume (broadcast gates h(view),
pauli_evolve(view, H, gamma), sub-kernel calls
f(view), controlled-U index_spec) only transfers
ownership to a freshly-wrapped VectorView and that new
view still must be returned via slice assignment. A view
left bulk-borrowing at the parent’s consume point raises
UnreturnedBorrowError.
Symbolic slices (q[lo:hi] with lo/hi UInt) cannot
enumerate their covered slots at trace time and therefore skip
the bulk-borrow here; SliceBorrowCheckPass picks them up
post-fold after bindings resolve the bounds to concrete values.
Example:
@qmc.qkernel
def alternating_h(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
evens = q[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])
q[0::2] = evens # explicit return before the parent is used
return qMethods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the view and release its parent slice-borrows.
Validates that every view-local borrow has been returned,
then dispatches on operation_name to keep the parent’s
slice-borrow record consistent with the new strict-return
semantics:
Destructive (
measure/cast): leaveselfparked in the parent’s borrow table as a destroyed-slot breadcrumb.super().consume()flipsself._consumed = Trueandself._consumed_by = operation_name, which is what :func:_is_destroyed_slot_ownerreads to reject subsequent access at the same slot.Releasing (
slice assignment): drop every parent entry thatselfcurrently owns. The caller (the slice- assignment frontend path) also emits aReleaseSliceViewOperationso the IR-level checker sees the release. This branch is reserved for explicit borrow- return paths.Transfer (every other op — broadcast gates, rotation, phase, ControlledU, sub-kernel call argument consumption, etc.): rebind the parent’s borrow entry from
selfto the new view handle returned here. The new view inheritsself._slice_covered_indicesso it can be slice-assigned back to the parent later — strict-return requires that eventualparent[a:b:c] = new_view.
Operations that produce a fresh sliced ArrayValue (e.g.
:func:qamomile.circuit.frontend.operation.pauli_evolve.pauli_evolve,
:class:QKernel.__call__ for callees that return a sliced
array) cannot simply use the auto-returned new_view
because the new view they build wraps a different Value
than this consume’s return. Those op implementations call
:meth:_transfer_borrow_to after building their result so
the parent’s borrow table tracks the right handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this view (used in error messages and for dispatch). |
Returns:
typing.Self — A fresh view handle with the same backing state; under
typing.Self — transfer the parent’s borrow table now points at this
typing.Self — handle, under release / destruction the parent’s record
typing.Self — for the covered slots is finalised.
Raises:
QubitConsumedError— If any covered slot was already destroyed by a prior destructive view consume on an overlapping view that has since gone out of scope.
qamomile.circuit.frontend.handle.array¶
Overview¶
| Function | Description |
|---|---|
get_current_tracer | |
is_plain_int | Return True if value is a Python int but not a bool. |
| Class | Description |
|---|---|
AffineTypeError | Base class for affine type violations. |
ArrayBase | Base class for array types (Vector, Matrix, Tensor). |
ArrayValue | An array of typed IR values. |
BinOpKind | |
Bit | |
BitType | Type representing a classical bit. |
CInitOperation | Initialize the classical values (const, arguments etc) |
ConsumeMode | Classify how a VectorView.consume call resolves slice borrows. |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
Handle | |
Matrix | 2-dimensional array type. |
QInitOperation | Initialize the qubit |
Qubit | |
QubitBorrowConflictError | Qubit slot inaccessible because another live handle borrows it. |
QubitConsumedError | Qubit handle used after being consumed by a previous operation. |
QubitType | Type representing a quantum bit (qubit). |
ReleaseSliceViewOperation | Mark a slice view’s borrow as explicitly returned to its parent. |
SliceArrayOperation | Construct a strided view of an ArrayValue. |
Tensor | N-dimensional array type (3 or more dimensions). |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
UnreturnedBorrowError | Borrowed array element not returned before array use. |
Value | A typed SSA value in the IR. |
Vector | 1-dimensional array type. |
VectorView | Strided view over a parent Vector, backed by a sliced ArrayValue. |
Functions¶
get_current_tracer [source]¶
def get_current_tracer() -> Traceris_plain_int [source]¶
def is_plain_int(value: object) -> boolReturn True if value is a Python int but not a bool.
bool is a subclass of int in Python, so isinstance(True, int)
is True. This helper distinguishes a genuine integer from a boolean,
which matters wherever a boolean must be rejected in an integer slot — for
example, validating decoded wire data or a register width.
Parameters:
| Name | Type | Description |
|---|---|---|
value | object | The value to test. |
Returns:
bool — True when value is an int and not a bool.
Classes¶
AffineTypeError [source]¶
class AffineTypeError(QamomileCompileError)Base class for affine type violations.
Affine types enforce that quantum resources (qubits) are used at most once. This prevents common errors such as reusing a consumed qubit or aliasing.
Constructor¶
def __init__(
self,
message: str,
handle_name: str | None = None,
operation_name: str | None = None,
first_use_location: str | None = None,
)Attributes¶
first_use_locationhandle_nameoperation_name
ArrayBase [source]¶
class ArrayBase(Handle, Generic[T])Base class for array types (Vector, Matrix, Tensor).
Provides common functionality for array indexing and element access.
Constructor¶
def __init__(
self,
value: ArrayValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
element_type: Type[T]shape: tuple[int | UInt, ...] Return the shape of the array.value: ArrayValue
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the array, enforcing borrow-return contract for quantum arrays.
For quantum arrays, all borrowed elements must be returned before the array can be consumed. This ensures that no unreturned borrows are silently discarded by operations like qkernel calls or controlled gates.
When any slot of the array has already been physically consumed
by an earlier destructive view operation (measure(q[1::2])
then measure(q)), this raises QubitConsumedError rather
than silently re-consuming those slots.
create¶
@classmethod
def create(
cls,
shape: tuple[int | UInt, ...],
name: str,
el_type: Type[T],
) -> 'ArrayBase[T]'Create an ArrayValue for the given shape and name.
validate_all_returned¶
def validate_all_returned(self) -> NoneValidate all borrowed elements have been returned.
Strict-return policy: an active slice view that is still
registered as the owner of any parent slot is treated as an
unreturned borrow even if the view itself has no outstanding
element borrows. The caller must perform an explicit slice
assignment (parent[a:b:c] = view) to release the view’s
bulk-borrow before consuming the parent. Destructively
consumed views (parked in the dict with view._consumed set
and view._consumed_by classified as
:attr:ConsumeMode.DESTRUCTIVE) record physically-destroyed
slots and are not outstanding borrows; they survive
end-of-block so a later whole-array consume can detect and
reject the destroyed slots.
Raises:
UnreturnedBorrowError— If any elements are still borrowed, either directly or by a slice view that has not been explicitly returned via slice assignment.
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]BinOpKind [source]¶
class BinOpKind(enum.Enum)Attributes¶
ADDDIVFLOORDIVMINMODMULPOWSUB
Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
BitType [source]¶
class BitType(ClassicalTypeMixin, ValueType)Type representing a classical bit.
CInitOperation [source]¶
class CInitOperation(Operation)Initialize the classical values (const, arguments etc)
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
ConsumeMode [source]¶
class ConsumeMode(enum.Enum)Classify how a VectorView.consume call resolves slice borrows.
ArrayBase.consume and VectorView.consume accept a free-form
operation_name string used both for error messages and for
dispatching how the parent’s bulk-borrow table is updated. The
string itself is purely cosmetic; the dispatch logic only cares
about which of three resolution modes applies, captured by this
enum:
DESTRUCTIVE: the consume physically destroys the qubits (measure/cast). The consumed view stays parked in the parent’s borrow table so any later access to the same slot is surfaced as a use-after-destroy.RELEASING: the consume returns the borrow to the parent cleanly (slice assignment) — the entries are dropped and the parent regains free access to the covered slots.TRANSFER: the consume hands ownership forward to a freshly builtVectorView(broadcast gates,pauli_evolve,QKernel.__call__, controlled-Uindex_spec). The covered slots stay borrowed under the new handle.
Attributes¶
DESTRUCTIVERELEASINGTRANSFER
Float [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
Matrix [source]¶
class Matrix(ArrayBase[T])2-dimensional array type.
Example:
import qamomile as qm
# Create a 3x4 matrix of qubits
matrix: qm.Matrix[qm.Qubit] = qm.Matrix(shape=(3, 4))
# Access elements (always requires 2 indices)
q = matrix[0, 1]
q = qm.h(q)
matrix[0, 1] = qConstructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, int | UInt] = (0, 0),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
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
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
QubitBorrowConflictError [source]¶
class QubitBorrowConflictError(AffineTypeError)Qubit slot inaccessible because another live handle borrows it.
Raised when a qubit slot cannot be accessed because another live
handle currently borrows it — a slice view that has not been
returned, an outstanding element borrow, or any future borrow form
Qamomile may add. Unlike :class:QubitConsumedError, the slot is
not destroyed: releasing the borrowing handle (slice assignment,
element write-back, etc.) restores access.
Example of incorrect code (overlapping slice views)::
a = q[0:3] # q[0..2] now borrowed by ``a``
b = q[2:5] # ERROR: q[2] is still borrowed by ``a``Correct code::
a = q[0:3]
q[0:3] = a # return ``a`` first
b = q[2:5] # now safeExample of incorrect code (element borrow not returned before borrowing a neighbour)::
q0 = qubits[0]
q0 = qmc.h(q0)
q1 = qubits[1] # ERROR: q0 is still borrowedCorrect code::
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0 # return the element first
q1 = qubits[1] # now safeQubitConsumedError [source]¶
class QubitConsumedError(AffineTypeError)Qubit handle used after being consumed by a previous operation.
Each qubit handle can only be used once. After a gate operation, you must reassign the result to use the new handle.
Example of incorrect code:
q1 = qm.h(q) q2 = qm.x(q) # ERROR: q was already consumed by h()
Correct code:
q = qm.h(q) # Reassign to capture new handle q = qm.x(q) # Use the reassigned handle
QubitType [source]¶
class QubitType(QuantumTypeMixin, ValueType)Type representing a quantum bit (qubit).
ReleaseSliceViewOperation [source]¶
class ReleaseSliceViewOperation(Operation)Mark a slice view’s borrow as explicitly returned to its parent.
Emitted by :meth:Vector.__setitem__ when used with a slice index
(qs[a:b] = qmc.h(qs[a:b])). This op tells the post-fold
linearity checker
(:class:~qamomile.circuit.transpiler.passes.slice_borrow_check.SliceBorrowCheckPass)
that the view referenced in operands[0] no longer owns its
covered parent slots, mirroring the frontend’s
VectorView.consume(operation_name="slice assignment") borrow
release.
Like :class:SliceArrayOperation, this op is a declarative
classical-side marker that does not survive into the emit stream:
:class:~qamomile.circuit.transpiler.passes.strip_slice_ops.StripSliceArrayOpsPass
removes both :class:SliceArrayOperation and
:class:ReleaseSliceViewOperation after
:class:SliceBorrowCheckPass has observed them. Reaching emit
is a compiler-internal invariant violation and is rejected with a
RuntimeError from :mod:standard_emit.
Within a control-flow body (ForOperation / WhileOperation
/ IfOperation), this op only releases view borrows that were
created within the same body. Releasing a borrow that the
enclosing block has registered (an “outer-snapshot” borrow) is
rejected by SliceBorrowCheckPass with
SliceBorrowViolationError — the loop-merge semantics of the
pass cannot propagate entry deletions out of the body, so the only
way to keep the static check consistent is to forbid that pattern.
Example:
``qs[1:3] = qmc.h(qs[1:3])`` emits, after the broadcast loop::
ReleaseSliceViewOperation(
operands=[qmc_h_result_view], # slice_of=qs_value
results=[],
)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Release is classical — it updates borrow tracking metadata only.signature: Signature Return the type signature of this release operation.
SliceArrayOperation [source]¶
class SliceArrayOperation(Operation)Construct a strided view of an ArrayValue.
The op itself performs no quantum action — it records that the
result ArrayValue is a strided view of the operand parent
with the given start / step. The result’s
slice_of / slice_start / slice_step fields carry the
affine map used by the emit-time resolver.
SliceArrayOperation is classified as :attr:OperationKind.CLASSICAL
because slicing is pure index selection — no new quantum operation
is introduced. The pipeline keeps this op through
PartialEvaluationPass (which invokes
ConstantFoldingPass(..., strip_slice_ops=False)) so the
post-fold :class:~qamomile.circuit.transpiler.passes.slice_borrow_check.SliceBorrowCheckPass
can use it as a view-declaration marker; once that check has run,
StripSliceArrayOpsPass removes every SliceArrayOperation
/ ReleaseSliceViewOperation so segmentation
(:mod:~qamomile.circuit.transpiler.passes.separate) and the
downstream emit stage only see a pure quantum-op stream. By the
time :mod:~qamomile.circuit.transpiler.passes.separate runs the
op has therefore been stripped — reaching emit is a compiler-
internal invariant violation.
Example:
``q[1::2]`` on a ``Vector[Qubit]`` emits::
SliceArrayOperation(
operands=[q_value, uint_1, uint_2],
results=[sliced_value], # slice_of=q_value, slice_start=uint_1, slice_step=uint_2
)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Slice is classical — it selects indices without quantum action.signature: Signature Return the type signature of this slice operation.
Tensor [source]¶
class Tensor(ArrayBase[T])N-dimensional array type (3 or more dimensions).
Example:
import qamomile as qm
# Create a 2x3x4 tensor of qubits
tensor: qm.Tensor[qm.Qubit] = qm.Tensor(shape=(2, 3, 4))
# Access elements (requires all indices)
q = tensor[0, 1, 2]
q = qm.h(q)
tensor[0, 1, 2] = qConstructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
UInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
UnreturnedBorrowError [source]¶
class UnreturnedBorrowError(AffineTypeError)Borrowed array element not returned before array use.
When you borrow an element from a qubit array, you must return it (write it back) before using other elements or the array itself.
Example of incorrect code:
q0 = qubits[0] q0 = qmc.h(q0) q1 = qubits[1] # ERROR: q0 not returned yet
Correct code:
q0 = qubits[0] q0 = qmc.h(q0) qubits[0] = q0 # Return the borrowed element q1 = qubits[1] # Now safe to borrow another
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
VectorView [source]¶
class VectorView(Vector[T])Strided view over a parent Vector, backed by a sliced ArrayValue.
A VectorView is produced by slicing a Vector (q[1::2],
q[a:b], etc.). It is a thin Vector subclass whose value
is a fresh ArrayValue with slice_of / slice_start /
slice_step metadata pointing back to the parent’s ArrayValue.
Element accesses go through Vector._get_element unchanged — the
IR element carries parent_array = sliced_av, and the emit-time
resolver walks the slice_of chain to produce the physical qubit
index. No affine translation happens in the view itself.
Because the sliced ArrayValue is a first-class IR Value,
the view can be passed as an operand of CallBlockOperation to
another @qkernel without the inline-trace special-case path
that earlier iterations required. Passing views through
expval / measure likewise operates on the sliced qubit
subset, not the root parent as a whole.
Linearity:
Slicing bulk-borrows the covered parent slots whenever
start, step and length are compile-time int
constants. While the view is live, accessing the corresponding
parent slot directly (q[0] after evens = q[0::2]) raises
QubitConsumedError. Under the strict-return policy the
view’s ownership is cleared only by two operations:
slice-assigning it back into the parent (
parent[a:b:c] = view) — this is the only path that fully releases the borrow without destroying the qubits;destructively consuming it (
measure(view)/cast(view, ...)/expval(view, H)) — the physical slots become consumed markers, no return needed.
Every other consume (broadcast gates h(view),
pauli_evolve(view, H, gamma), sub-kernel calls
f(view), controlled-U index_spec) only transfers
ownership to a freshly-wrapped VectorView and that new
view still must be returned via slice assignment. A view
left bulk-borrowing at the parent’s consume point raises
UnreturnedBorrowError.
Symbolic slices (q[lo:hi] with lo/hi UInt) cannot
enumerate their covered slots at trace time and therefore skip
the bulk-borrow here; SliceBorrowCheckPass picks them up
post-fold after bindings resolve the bounds to concrete values.
Example:
@qmc.qkernel
def alternating_h(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
evens = q[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])
q[0::2] = evens # explicit return before the parent is used
return qMethods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the view and release its parent slice-borrows.
Validates that every view-local borrow has been returned,
then dispatches on operation_name to keep the parent’s
slice-borrow record consistent with the new strict-return
semantics:
Destructive (
measure/cast): leaveselfparked in the parent’s borrow table as a destroyed-slot breadcrumb.super().consume()flipsself._consumed = Trueandself._consumed_by = operation_name, which is what :func:_is_destroyed_slot_ownerreads to reject subsequent access at the same slot.Releasing (
slice assignment): drop every parent entry thatselfcurrently owns. The caller (the slice- assignment frontend path) also emits aReleaseSliceViewOperationso the IR-level checker sees the release. This branch is reserved for explicit borrow- return paths.Transfer (every other op — broadcast gates, rotation, phase, ControlledU, sub-kernel call argument consumption, etc.): rebind the parent’s borrow entry from
selfto the new view handle returned here. The new view inheritsself._slice_covered_indicesso it can be slice-assigned back to the parent later — strict-return requires that eventualparent[a:b:c] = new_view.
Operations that produce a fresh sliced ArrayValue (e.g.
:func:qamomile.circuit.frontend.operation.pauli_evolve.pauli_evolve,
:class:QKernel.__call__ for callees that return a sliced
array) cannot simply use the auto-returned new_view
because the new view they build wraps a different Value
than this consume’s return. Those op implementations call
:meth:_transfer_borrow_to after building their result so
the parent’s borrow table tracks the right handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this view (used in error messages and for dispatch). |
Returns:
typing.Self — A fresh view handle with the same backing state; under
typing.Self — transfer the parent’s borrow table now points at this
typing.Self — handle, under release / destruction the parent’s record
typing.Self — for the covered slots is finalised.
Raises:
QubitConsumedError— If any covered slot was already destroyed by a prior destructive view consume on an overlapping view that has since gone out of scope.
qamomile.circuit.frontend.handle.containers¶
Container types for qkernel: Tuple and Dict handles.
Overview¶
| Class | Description |
|---|---|
Dict | Dict handle for qkernel functions. |
DictItemsIterator | Iterator for Dict.items() that yields (key, value) pairs. |
DictValue | A dictionary value stored as stable ordered entries. |
Handle | |
Tuple | Tuple handle for qkernel functions. |
TupleValue | A tuple of IR values for structured data. |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
Classes¶
Dict [source]¶
class Dict(Handle, Generic[K, V])Dict handle for qkernel functions.
Represents a dictionary mapping keys to values, commonly used for Ising coefficients like {(i, j): Jij}.
Example:
@qmc.qkernel
def ising_cost(
q: qmc.Vector[qmc.Qubit],
ising: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qConstructor¶
def __init__(
self,
value: DictValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_entries: list[tuple[Handle, Handle]] = list(),
_size: UInt | None = None,
_key_type: type | None = None,
) -> NoneAttributes¶
size: UInt Return the number of entries as a UInt handle.value: DictValue
Methods¶
items¶
def items(self) -> DictItemsIterator[K, V]Return an iterator over (key, value) pairs.
DictItemsIterator [source]¶
class DictItemsIterator(Generic[K, V])Iterator for Dict.items() that yields (key, value) pairs.
This is used internally for iterating over Dict entries in qkernel.
Constructor¶
def __init__(self, dict_handle: 'Dict[K, V]', _index: int = 0) -> NoneAttributes¶
dict_handle: ‘Dict[K, V]’
DictValue [source]¶
class DictValue(_MetadataValueMixin)A dictionary value stored as stable ordered entries.
Constructor¶
def __init__(
self,
name: str,
entries: tuple[tuple[TupleValue | Value, Value], ...] = tuple(),
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
) -> NoneAttributes¶
entries: tuple[tuple[TupleValue | Value, Value], ...]logical_id: strmetadata: ValueMetadataname: strtype: DictTypeuuid: str
Methods¶
is_constant¶
def is_constant(self) -> boolnext_version¶
def next_version(self) -> DictValueHandle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
Tuple [source]¶
class Tuple(Handle, Generic[K, V])Tuple handle for qkernel functions.
Represents a tuple of values, commonly used for multi-index keys like (i, j) in Ising models.
Example:
@qmc.qkernel
def my_kernel(idx: qmc.Tuple[qmc.UInt, qmc.UInt]) -> qmc.UInt:
i, j = idx
return i + jConstructor¶
def __init__(
self,
value: TupleValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_elements: tuple[Handle, ...] = tuple(),
) -> NoneAttributes¶
value: TupleValue
TupleValue [source]¶
class TupleValue(_MetadataValueMixin)A tuple of IR values for structured data.
Constructor¶
def __init__(
self,
name: str,
elements: tuple[Value, ...] = tuple(),
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
) -> NoneAttributes¶
elements: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strtype: ‘TupleType’uuid: str
Methods¶
is_constant¶
def is_constant(self) -> boolnext_version¶
def next_version(self) -> TupleValueUInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.handle.hamiltonian¶
Observable handle for Hamiltonian parameters.
This module provides the Observable handle class that represents a reference to a Hamiltonian observable provided via bindings during transpilation. Unlike HamiltonianExpr in previous versions, this is a pure parameter handle with no arithmetic operations.
Overview¶
| Class | Description |
|---|---|
Handle | |
Observable | Handle representing a Hamiltonian observable parameter. |
Classes¶
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
Observable [source]¶
class Observable(Handle)Handle representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1) + 0.5 * qm_o.X(0)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
# Use Hamiltonian from bindings
return qm.expval(q, H)
# Pass via bindings
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> Noneqamomile.circuit.frontend.handle.handle¶
Overview¶
| Function | Description |
|---|---|
evaluate_binop_values | Evaluate a binary arithmetic operation on two concrete values. |
get_current_tracer |
| Class | Description |
|---|---|
ArithmeticMixin | Mixin providing arithmetic operations for numeric Handle types. |
ArrayBase | Base class for array types (Vector, Matrix, Tensor). |
BinOp | Binary arithmetic operation (ADD, SUB, MUL, DIV, FLOORDIV, MOD, POW, MIN). |
BinOpKind | |
CompOp | Comparison operation (EQ, NEQ, LT, LE, GT, GE). |
CompOpKind | |
CondOp | Conditional logical operation (AND, OR). |
CondOpKind | |
Handle | |
NotOp | |
QubitConsumedError | Qubit handle used after being consumed by a previous operation. |
UInt | Unsigned integer handle with arithmetic operations. |
Value | A typed SSA value in the IR. |
Functions¶
evaluate_binop_values [source]¶
def evaluate_binop_values(
kind: BinOpKind | None,
left: float | int,
right: float | int,
) -> float | int | NoneEvaluate a binary arithmetic operation on two concrete values.
Parameters:
| Name | Type | Description |
|---|---|---|
kind | BinOpKind | None | The BinOpKind to apply. |
left | float | int | Left operand (numeric). |
right | float | int | Right operand (numeric). |
Returns:
float | int | None — The result, or None on division by zero, unknown kind, or
float | int | None — arithmetic error.
get_current_tracer [source]¶
def get_current_tracer() -> TracerClasses¶
ArithmeticMixin [source]¶
class ArithmeticMixinMixin providing arithmetic operations for numeric Handle types.
Requires:
value: Value attribute
_make_result(): Method to create result Handle of same type
_coerce(): Method to convert Python literals to Handle
Attributes¶
value: Value
ArrayBase [source]¶
class ArrayBase(Handle, Generic[T])Base class for array types (Vector, Matrix, Tensor).
Provides common functionality for array indexing and element access.
Constructor¶
def __init__(
self,
value: ArrayValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
element_type: Type[T]shape: tuple[int | UInt, ...] Return the shape of the array.value: ArrayValue
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the array, enforcing borrow-return contract for quantum arrays.
For quantum arrays, all borrowed elements must be returned before the array can be consumed. This ensures that no unreturned borrows are silently discarded by operations like qkernel calls or controlled gates.
When any slot of the array has already been physically consumed
by an earlier destructive view operation (measure(q[1::2])
then measure(q)), this raises QubitConsumedError rather
than silently re-consuming those slots.
create¶
@classmethod
def create(
cls,
shape: tuple[int | UInt, ...],
name: str,
el_type: Type[T],
) -> 'ArrayBase[T]'Create an ArrayValue for the given shape and name.
validate_all_returned¶
def validate_all_returned(self) -> NoneValidate all borrowed elements have been returned.
Strict-return policy: an active slice view that is still
registered as the owner of any parent slot is treated as an
unreturned borrow even if the view itself has no outstanding
element borrows. The caller must perform an explicit slice
assignment (parent[a:b:c] = view) to release the view’s
bulk-borrow before consuming the parent. Destructively
consumed views (parked in the dict with view._consumed set
and view._consumed_by classified as
:attr:ConsumeMode.DESTRUCTIVE) record physically-destroyed
slots and are not outstanding borrows; they survive
end-of-block so a later whole-array consume can detect and
reject the destroyed slots.
Raises:
UnreturnedBorrowError— If any elements are still borrowed, either directly or by a slice view that has not been explicitly returned via slice assignment.
BinOp [source]¶
class BinOp(BinaryOperationBase)Binary arithmetic operation (ADD, SUB, MUL, DIV, FLOORDIV, MOD, POW, MIN).
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
kind: BinOpKind | None = None,
) -> NoneAttributes¶
kind: BinOpKind | Noneoperation_kind: OperationKindsignature: Signature
BinOpKind [source]¶
class BinOpKind(enum.Enum)Attributes¶
ADDDIVFLOORDIVMINMODMULPOWSUB
CompOp [source]¶
class CompOp(BinaryOperationBase)Comparison operation (EQ, NEQ, LT, LE, GT, GE).
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
kind: CompOpKind | None = None,
) -> NoneAttributes¶
kind: CompOpKind | Noneoperation_kind: OperationKindsignature: Signature
CompOpKind [source]¶
class CompOpKind(enum.Enum)Attributes¶
EQGEGTLELTNEQ
CondOp [source]¶
class CondOp(BinaryOperationBase)Conditional logical operation (AND, OR).
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
kind: CondOpKind | None = None,
) -> NoneAttributes¶
kind: CondOpKind | Noneoperation_kind: OperationKindsignature: Signature
CondOpKind [source]¶
class CondOpKind(enum.Enum)Attributes¶
ANDOR
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
NotOp [source]¶
class NotOp(Operation)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
input: Valueoperation_kind: OperationKindoutput: Valuesignature: Signature
QubitConsumedError [source]¶
class QubitConsumedError(AffineTypeError)Qubit handle used after being consumed by a previous operation.
Each qubit handle can only be used once. After a gate operation, you must reassign the result to use the new handle.
Example of incorrect code:
q1 = qm.h(q) q2 = qm.x(q) # ERROR: q was already consumed by h()
Correct code:
q = qm.h(q) # Reassign to capture new handle q = qm.x(q) # Use the reassigned handle
UInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.handle.primitives¶
Overview¶
| Class | Description |
|---|---|
ArithmeticMixin | Mixin providing arithmetic operations for numeric Handle types. |
BinOpKind | |
Bit | |
BitType | Type representing a classical bit. |
CompOpKind | |
CondOpKind | |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
Handle | |
QFixed | |
Qubit | |
QubitType | Type representing a quantum bit (qubit). |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
Classes¶
ArithmeticMixin [source]¶
class ArithmeticMixinMixin providing arithmetic operations for numeric Handle types.
Requires:
value: Value attribute
_make_result(): Method to create result Handle of same type
_coerce(): Method to convert Python literals to Handle
Attributes¶
value: Value
BinOpKind [source]¶
class BinOpKind(enum.Enum)Attributes¶
ADDDIVFLOORDIVMINMODMULPOWSUB
Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
BitType [source]¶
class BitType(ClassicalTypeMixin, ValueType)Type representing a classical bit.
CompOpKind [source]¶
class CompOpKind(enum.Enum)Attributes¶
EQGEGTLELTNEQ
CondOpKind [source]¶
class CondOpKind(enum.Enum)Attributes¶
ANDOR
Float [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
QFixed [source]¶
class QFixed(Handle)Constructor¶
def __init__(
self,
value: Value[QFixedType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QFixedType]
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
QubitType [source]¶
class QubitType(QuantumTypeMixin, ValueType)Type representing a quantum bit (qubit).
UInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.handle.utils¶
Utility helpers for handle types.
Overview¶
| Function | Description |
|---|---|
get_size | Return the size of a Vector handle as a Python integer. |
Functions¶
get_size [source]¶
def get_size(arr: Vector[_H]) -> intReturn the size of a Vector handle as a Python integer.
Resolves the leading axis of arr.shape through two forms a
Vector shape entry can take:
A plain Python
int(built-in bound shape; this is what you get fromqmc.qubit_array(N, ...)for literalN).A
UInthandle whose underlyingValuecarries a compile-time constant (set byuint(literal),_create_bound_input, or partial evaluation).
A UInt handle whose underlying Value is not a constant is
treated as an unresolved symbolic dimension and raises
ValueError even when the handle has the dataclass-default
init_value=0. Falling back to init_value for that case
would silently turn a runtime-symbolic Vector[Float] parameter
into a “size 0” array, hiding programming errors. Callers that
need to handle symbolic shapes (e.g., to gracefully no-op when the
size is unknown) must catch the ValueError themselves.
Parameters:
| Name | Type | Description |
|---|---|---|
arr | Vector[Handle] | Vector handle whose first axis size is requested. |
Returns:
int — The first-axis size as a plain Python int.
Raises:
TypeError— If arr is not a 1-DVectorhandle (Vectoror itsVectorViewsubclass) — e.g., a scalarQubitwas passed where aVectoris required, a higher-rankMatrix/Tensorwas passed (this helper only resolves a 1-D first-axis size), or an unrelatedshape-bearing object such as a numpy array. This is a clearer signal than the bareAttributeErrorthatarr.shapewould otherwise raise, and it guards the stdlib / composite callers that resolve a register size through this helper.ValueError— If the shape cannot be resolved to a concrete integer — e.g., the Vector is a runtime-parametric handle without compile-time bindings, or carries aUIntdimension whose underlyingValuehas not been promoted to a constant.
Classes¶
Handle [source]¶
class Handle(abc.ABC)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
id: strindices: tuple[‘UInt’, ...]name: str | Noneparent: ‘ArrayBase | None’value: Value
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfMark this handle as consumed and return a fresh handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this handle, used for error messages. |
Returns:
typing.Self — A new handle pointing to the same underlying value.
Raises:
QubitConsumedError— If this handle was already consumed and is a quantum type.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
qamomile.circuit.frontend.operation¶
qamomile.circuit.frontend.operation.cast¶
Cast operation for type conversions over the same quantum resources.
Overview¶
| Function | Description |
|---|---|
cast | Cast a quantum value to a different type without allocating new qubits. |
get_current_tracer |
| Class | Description |
|---|---|
CastOperation | Type cast operation for creating aliases over the same quantum resources. |
QFixed | |
QFixedType | Quantum fixed-point type. |
Qubit | |
Value | A typed SSA value in the IR. |
Vector | 1-dimensional array type. |
VectorView | Strided view over a parent Vector, backed by a sliced ArrayValue. |
Functions¶
cast [source]¶
def cast(source: Vector[Qubit], target_type: type, *, int_bits: int = 0) -> QFixedCast a quantum value to a different type without allocating new qubits.
The cast performs a move: the source handle is consumed and cannot be reused after the cast. The returned handle references the same physical qubits.
Parameters:
| Name | Type | Description |
|---|---|---|
source | Vector[Qubit] | The value to cast (currently supports Vector[Qubit]) |
target_type | type | The target type class (currently supports QFixed) |
int_bits | int | For QFixed, number of integer bits (default: 0 = all fractional) |
Returns:
QFixed — A new handle of the target type referencing the same qubits.
Example:
@qmc.qkernel
def my_circuit():
phase_register = qmc.qubit_array(5, name="phase")
# ... apply some operations ...
# Cast the qubit array to QFixed for measurement
phase_qfixed = qmc.cast(phase_register, qmc.QFixed, int_bits=0)
phase_value = qmc.measure(phase_qfixed)
return phase_valueRaises:
TypeError— If the source type or target type is not supportedValueError— If int_bits is negative or larger than the number of qubits
get_current_tracer [source]¶
def get_current_tracer() -> TracerClasses¶
CastOperation [source]¶
class CastOperation(Operation)Type cast operation for creating aliases over the same quantum resources.
This operation does NOT allocate new qubits. It creates a new Value that references the same underlying quantum resources with a different type.
Use cases:
Vector[Qubit] -> QFixed (after QPE, for phase measurement)
Vector[Qubit] -> QUInt (for quantum arithmetic)
QUInt -> QFixed (reinterpret bits with different encoding)
QFixed -> QUInt (reinterpret bits with different encoding)
operands: [source_value] - The value being cast results: [cast_result] - The new value with target type (same physical qubits)
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
source_type: ValueType | None = None,
target_type: ValueType | None = None,
qubit_mapping: list[str] = list(),
) -> NoneAttributes¶
num_qubits: int Number of qubits involved in the cast.operation_kind: OperationKind Cast stays in the same segment as its source (QUANTUM for quantum types).qubit_mapping: list[str]signature: Signature Return the type signature of this cast operation.source_type: ValueType | Nonetarget_type: ValueType | None
QFixed [source]¶
class QFixed(Handle)Constructor¶
def __init__(
self,
value: Value[QFixedType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QFixedType]
QFixedType [source]¶
class QFixedType(QuantumTypeMixin, ValueType)Quantum fixed-point type.
Represents a quantum register encoding a fixed-point number with specified integer and fractional bits.
Constructor¶
def __init__(
self,
integer_bits: int | Value[UIntType] = 0,
fractional_bits: int | Value[UIntType] = 0,
) -> NoneAttributes¶
fractional_bits: int | Value[UIntType]integer_bits: int | Value[UIntType]
Methods¶
label¶
def label(self) -> strQubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
VectorView [source]¶
class VectorView(Vector[T])Strided view over a parent Vector, backed by a sliced ArrayValue.
A VectorView is produced by slicing a Vector (q[1::2],
q[a:b], etc.). It is a thin Vector subclass whose value
is a fresh ArrayValue with slice_of / slice_start /
slice_step metadata pointing back to the parent’s ArrayValue.
Element accesses go through Vector._get_element unchanged — the
IR element carries parent_array = sliced_av, and the emit-time
resolver walks the slice_of chain to produce the physical qubit
index. No affine translation happens in the view itself.
Because the sliced ArrayValue is a first-class IR Value,
the view can be passed as an operand of CallBlockOperation to
another @qkernel without the inline-trace special-case path
that earlier iterations required. Passing views through
expval / measure likewise operates on the sliced qubit
subset, not the root parent as a whole.
Linearity:
Slicing bulk-borrows the covered parent slots whenever
start, step and length are compile-time int
constants. While the view is live, accessing the corresponding
parent slot directly (q[0] after evens = q[0::2]) raises
QubitConsumedError. Under the strict-return policy the
view’s ownership is cleared only by two operations:
slice-assigning it back into the parent (
parent[a:b:c] = view) — this is the only path that fully releases the borrow without destroying the qubits;destructively consuming it (
measure(view)/cast(view, ...)/expval(view, H)) — the physical slots become consumed markers, no return needed.
Every other consume (broadcast gates h(view),
pauli_evolve(view, H, gamma), sub-kernel calls
f(view), controlled-U index_spec) only transfers
ownership to a freshly-wrapped VectorView and that new
view still must be returned via slice assignment. A view
left bulk-borrowing at the parent’s consume point raises
UnreturnedBorrowError.
Symbolic slices (q[lo:hi] with lo/hi UInt) cannot
enumerate their covered slots at trace time and therefore skip
the bulk-borrow here; SliceBorrowCheckPass picks them up
post-fold after bindings resolve the bounds to concrete values.
Example:
@qmc.qkernel
def alternating_h(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
evens = q[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])
q[0::2] = evens # explicit return before the parent is used
return qMethods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the view and release its parent slice-borrows.
Validates that every view-local borrow has been returned,
then dispatches on operation_name to keep the parent’s
slice-borrow record consistent with the new strict-return
semantics:
Destructive (
measure/cast): leaveselfparked in the parent’s borrow table as a destroyed-slot breadcrumb.super().consume()flipsself._consumed = Trueandself._consumed_by = operation_name, which is what :func:_is_destroyed_slot_ownerreads to reject subsequent access at the same slot.Releasing (
slice assignment): drop every parent entry thatselfcurrently owns. The caller (the slice- assignment frontend path) also emits aReleaseSliceViewOperationso the IR-level checker sees the release. This branch is reserved for explicit borrow- return paths.Transfer (every other op — broadcast gates, rotation, phase, ControlledU, sub-kernel call argument consumption, etc.): rebind the parent’s borrow entry from
selfto the new view handle returned here. The new view inheritsself._slice_covered_indicesso it can be slice-assigned back to the parent later — strict-return requires that eventualparent[a:b:c] = new_view.
Operations that produce a fresh sliced ArrayValue (e.g.
:func:qamomile.circuit.frontend.operation.pauli_evolve.pauli_evolve,
:class:QKernel.__call__ for callees that return a sliced
array) cannot simply use the auto-returned new_view
because the new view they build wraps a different Value
than this consume’s return. Those op implementations call
:meth:_transfer_borrow_to after building their result so
the parent’s borrow table tracks the right handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this view (used in error messages and for dispatch). |
Returns:
typing.Self — A fresh view handle with the same backing state; under
typing.Self — transfer the parent’s borrow table now points at this
typing.Self — handle, under release / destruction the parent’s record
typing.Self — for the covered slots is finalised.
Raises:
QubitConsumedError— If any covered slot was already destroyed by a prior destructive view consume on an overlapping view that has since gone out of scope.
qamomile.circuit.frontend.operation.control¶
Controlled gate operations.
Overview¶
| Function | Description |
|---|---|
control | Create a controlled version of a quantum gate. |
get_current_tracer |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
ConcreteControlledU | Controlled-U with concrete (int) number of controls. |
ControlledGate | Wrapper for controlled version of a QKernel. |
ControlledUOperation | Base class for controlled-U operations. |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
QKernel | Decorator class for Qamomile quantum kernels. |
Qubit | |
SymbolicControlledU | Controlled-U with symbolic (Value) number of controls. |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
Functions¶
control [source]¶
def control(
qkernel: QKernel | Callable[..., Any],
num_controls: int | UInt = 1,
) -> ControlledGateCreate a controlled version of a quantum gate.
Accepts a @qmc.qkernel-decorated function, a qkernel-backed
CompositeGate created by the function-form decorator, or a plain
built-in gate callable (qmc.rx, qmc.h, qmc.cp, ...). When
given a plain callable, a thin @qkernel wrapper is synthesized
automatically by inspecting the callable’s signature, so users no
longer need to write a one-line wrapper just to control a primitive
gate.
Parameters:
| Name | Type | Description |
|---|---|---|
qkernel | QKernel | Callable[..., Any] | A QKernel defining the gate to control, a qkernel-backed CompositeGate, or a built-in gate callable whose parameters are annotated with Qubit, Float / float, or UInt / int (possibly inside a Union such as Union[Qubit, Vector[Qubit]]). |
num_controls | int | UInt | Number of control qubits (default: 1). Can be int (concrete) or UInt (symbolic). |
Returns:
ControlledGate — A ControlledGate that can be called with
ControlledGate — (*controls, *targets, **params).
Raises:
TypeError— Ifqkernelis a callable that cannot be auto-wrapped (missing annotations, unsupported types, or no qubit parameters).ValueError— Ifnum_controlsis a concreteintless than 1.
Example:
Built-in gates can be controlled directly, with no wrapper::
crx = qmc.control(qmc.rx)
ctrl_out, tgt_out = crx(ctrl, target, angle=0.5)
cch = qmc.control(qmc.h, num_controls=2)
c0, c1, tgt = cch(ctrl0, ctrl1, target)
``@qmc.qkernel`` arguments are still supported for cases that need
custom logic::
@qmc.qkernel
def rx_then_h(q: Qubit, theta: float) -> Qubit:
q = qmc.rx(q, theta)
q = qmc.h(q)
return q
ctrl_out, tgt_out = qmc.control(rx_then_h)(ctrl, target, theta=0.5)
Qkernel-backed composite gates can also be controlled directly::
controlled_gate = qmc.control(my_composite_gate)
ctrl_out, tgt0_out, tgt1_out = controlled_gate(ctrl, tgt0, tgt1)get_current_tracer [source]¶
def get_current_tracer() -> TracerClasses¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]ConcreteControlledU [source]¶
class ConcreteControlledU(ControlledUOperation)Controlled-U with concrete (int) number of controls.
Operand layout: [ctrl_0, ..., ctrl_n, tgt_0, ..., tgt_m, params...]
Result layout: [ctrl_0', ..., ctrl_n', tgt_0', ..., tgt_m']
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: int = 1,
) -> NoneAttributes¶
control_operands: list[Value]num_controls: intparam_operands: list[Value]signature: Signaturetarget_operands: list[Value]
ControlledGate [source]¶
class ControlledGateWrapper for controlled version of a QKernel.
Created by calling control(qkernel). The resulting object
can be called like a gate function.
Example:
@qmc.qkernel
def phase_gate(q: Qubit, theta: float) -> Qubit:
return qmc.p(q, theta)
controlled_phase = qmc.control(phase_gate)
ctrl_out, tgt_out = controlled_phase(ctrl, target, theta=0.5)
# Double-controlled
cc_phase = qmc.control(phase_gate, num_controls=2)
c0, c1, tgt = cc_phase(ctrl0, ctrl1, target, theta=0.5)Constructor¶
def __init__(self, qkernel: 'QKernel', num_controls: int | UInt = 1) -> NoneWrap a QKernel as a controlled operation.
Parameters:
| Name | Type | Description |
|---|---|---|
qkernel | QKernel | The kernel to control. Built-in gate callables are not accepted directly here -- :func:control synthesizes a wrapper QKernel for them before instantiating ControlledGate -- so by this point qkernel must expose a dict input_types attribute and an inspect.Signature signature attribute. |
num_controls | int | UInt | Number of control qubits. A concrete int must be >= 1; a symbolic UInt defers validation to emit time. Defaults to 1. A bool is rejected: it is not a valid control count even though bool subclasses int. |
Raises:
TypeError— Ifnum_controlsis abool, or ifqkerneldoes not expose a dictinput_types/ aninspect.Signaturesignature.ValueError— If a concreteintnum_controlsis < 1.
ControlledUOperation [source]¶
class ControlledUOperation(Operation)Base class for controlled-U operations.
Two concrete subclasses handle distinct operand layouts:
ConcreteControlledU: Fixednum_controls: int, individual qubit operands.SymbolicControlledU: Symbolicnum_controls: Value, vector-based control operands; optionalcontrol_indicesselects a subset of the control vector to act as controls (the rest pass through).
All isinstance(op, ControlledUOperation) checks match every subclass.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: int | Value = 1,
) -> NoneAttributes¶
block: Block | Nonecontrol_operands: list[Value] Get the control qubit values.is_symbolic_num_controls: bool Whether num_controls is symbolic (Value) rather than concrete.num_controls: int | Valueoperation_kind: OperationKindparam_operands: list[Value] Get parameter operands (non-qubit, non-block).power: int | Valuesignature: Signaturetarget_operands: list[Value] Get the target qubit values (arguments to U).
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationFloat [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
QKernel [source]¶
class QKernel(Generic[P, R])Decorator class for Qamomile quantum kernels.
Constructor¶
def __init__(self, func: Callable[P, R]) -> NoneAttributes¶
block: Block Compile the function to a hierarchical Block if not already compiled.funcinput_typesnameoutput_typesraw_funcsignature
Methods¶
build¶
def build(self, parameters: list[str] | None = None, **kwargs: Any = {}) -> BlockBuild a traced Block by tracing this kernel.
Parameters:
| Name | Type | Description |
|---|---|---|
parameters | list[str] | None | List of argument names to keep as unbound parameters. - None (default): Auto-detect parameters (non-Qubit args without value/default) - []: No parameters (all non-Qubit args must have value/default) - [“name”]: Explicit parameter list Only float, int, UInt, and their arrays are allowed as parameters. |
**kwargs | Any | Concrete values for non-parameter arguments. |
Returns:
Block — The traced block ready for transpilation, estimation,
or visualization.
Raises:
TypeError— If a non-parameterizable type is specified as parameter.ValueError— If required arguments are missing.
Example:
@qm.qkernel
def circuit(q: Qubit, theta: float) -> Qubit:
q = qm.rx(q, theta)
return q
# Auto-detect theta as parameter
block = circuit.build()
# Explicit parameter list
block = circuit.build(parameters=["theta"])
# theta bound to concrete value
block = circuit.build(theta=0.5)
# Transpile with binding
transpiler = QiskitTranspiler()
result = transpiler.emit(graph, binding={"theta": 0.5})draw¶
def draw(
self,
inline: bool = False,
fold_loops: bool = True,
expand_composite: bool = False,
inline_depth: int | None = None,
**kwargs: Any = {},
) -> AnyVisualize the circuit using Matplotlib.
This method builds the computation graph and creates a static visualization. Parameters are auto-detected: non-Qubit arguments without concrete values are shown as symbolic parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
inline | bool | If True, expand CallBlockOperation contents (inlining). If False (default), show CallBlockOperation as boxes. |
fold_loops | bool | If True (default), display ForOperation as blocks instead of unrolling. If False, expand loops and show all iterations. |
expand_composite | bool | If True, expand CompositeGateOperation (QFT, IQFT, etc.). If False (default), show as boxes. Independent of inline. |
inline_depth | int | None | Maximum nesting depth for inline expansion. None means unlimited (default). 0 means no inlining, 1 means top-level only, etc. Only affects CallBlock/ControlledU, not CompositeGate. |
**kwargs | Any | Concrete values for arguments. Arguments not provided here (and without defaults) will be shown as symbolic parameters. |
Returns:
Any — matplotlib.figure.Figure object.
Raises:
ImportError— If matplotlib is not installed.
Example:
import qamomile.circuit as qm
@qm.qkernel
def inner(q: qm.Qubit) -> qm.Qubit:
return qm.x(q)
@qm.qkernel
def circuit(q: qm.Qubit, theta: float) -> qm.Qubit:
q = inner(q)
q = qm.h(q)
q = qm.rx(q, theta)
return q
# Draw with auto-detected symbolic parameter (theta)
fig = circuit.draw()
# Draw with bound parameter
fig = circuit.draw(theta=0.5)
# Draw with blocks as boxes (default)
fig = circuit.draw()
# Draw with blocks expanded (inlined)
fig = circuit.draw(inline=True)
# Draw with loops folded (shown as blocks)
fig = circuit.draw(fold_loops=True)
# Draw with composite gates expanded
fig = circuit.draw(expand_composite=True)estimate_resources¶
def estimate_resources(self, *, bindings: dict[str, Any] | None = None) -> ResourceEstimateEstimate all resources for this kernel’s circuit.
Convenience method that delegates to the module-level
estimate_resources function, eliminating the need to
access .block directly.
Parameters:
| Name | Type | Description |
|---|---|---|
bindings | dict[str, Any] | None | Optional concrete parameter bindings (scalars and dicts). Dict values trigger |key| cardinality substitution. |
Returns:
ResourceEstimate — ResourceEstimate with qubits, gates, and parameters.
Example:
>>> @qm.qkernel
... def bell() -> 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 = bell.estimate_resources()
>>> print(est.qubits) # 2Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
SymbolicControlledU [source]¶
class SymbolicControlledU(ControlledUOperation)Controlled-U with symbolic (Value) number of controls.
Operand layout: [ctrl_arg_0, ..., ctrl_arg_{k-1}, tgt_0, ..., tgt_m, params...]
Result layout: [ctrl_arg_0', ..., ctrl_arg_{k-1}', tgt_0', ..., tgt_m']
The number of control arguments k is recorded in
num_control_args; the default k = 1 corresponds to the
historical single-pool form (operands[0] is a
Vector[Qubit] / VectorView whose length equals
num_controls, or whose control_indices-selected subset
does). When k > 1 the control prefix is a heterogeneous
sequence of scalar Qubit values and ArrayValues whose
total qubit count is num_controls; the emit pass walks them
in order to recover the per-physical-qubit control set.
When control_indices is None the entire control prefix
is used as active controls (one-arg form: len(ctrl_vector) == num_controls; multi-arg form: the qubit-count sum of the
prefix args equals num_controls). When non-None, the
listed indices select exactly num_controls slots from a
single-arg pool to act as controls; combining
control_indices with the multi-arg control prefix is
rejected at frontend time.
Each control_indices entry is stored as a Value of
UIntType regardless of whether the frontend passed an
int literal or a UInt handle, so all downstream
value-substitution passes see a uniform shape.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: Value = (lambda: Value(type=(UIntType()), name=''))(),
control_indices: tuple[Value, ...] | None = None,
num_control_args: int = 1,
) -> NoneAttributes¶
control_indices: tuple[Value, ...] | Nonecontrol_operands: list[Value]is_symbolic_num_controls: boolnum_control_args: intnum_controls: Valueparam_operands: list[Value]signature: Signaturetarget_operands: list[Value]
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationUInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.operation.control_flow¶
Overview¶
| Function | Description |
|---|---|
emit_if | Builder function for if-else conditional with Phi function merging. |
for_items | Builder function to create a for-items loop in Qamomile frontend. |
for_loop | Builder function to create a for loop in Qamomile frontend. |
get_current_tracer | |
is_array_type | Check if type is a Vector, Matrix, or Tensor subclass. |
items | Iterate over dictionary key-value pairs. |
range | Symbolic range for use in qkernel for-loops. |
should_trace_for_loop | Decide whether a qmc.range body must be traced. |
trace | Context manager to set the current tracer. |
while_loop | Create a while loop whose condition is a measurement result. |
| Class | Description |
|---|---|
ArrayBase | Base class for array types (Vector, Matrix, Tensor). |
ArrayValue | An array of typed IR values. |
Bit | |
BitType | Type representing a classical bit. |
Dict | Dict handle for qkernel functions. |
DictItemsIterator | Iterator for Dict.items() that yields (key, value) pairs. |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
ForItemsOperation | Represents iteration over dict/iterable items. |
ForOperation | Represents a for loop operation. |
IfOperation | Represents an if-else conditional operation. |
Observable | Handle representing a Hamiltonian observable parameter. |
PhiOp | SSA Phi function: merge point after conditional branch. |
QFixed | |
Qubit | |
Tracer | |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
Vector | 1-dimensional array type. |
WhileLoop | |
WhileOperation | Represents a while loop operation. |
Functions¶
emit_if [source]¶
def emit_if(
cond_func: Callable,
true_func: Callable,
false_func: Callable,
variables: list,
) -> AnyBuilder function for if-else conditional with Phi function merging.
This function is called from AST-transformed code. The AST transformer converts: if condition: true_body else: false_body
Into:
def _cond_N(vars): return condition def _body_N(vars): true_body; return vars def _body_N+1(vars): false_body; return vars result = emit_if(_cond_N, _body_N, _body_N+1, [var_list])
Parameters:
| Name | Type | Description |
|---|---|---|
cond_func | typing.Callable | Function returning the condition (Bit or bool-like Handle) |
true_func | typing.Callable | Function executing true branch, returns updated variables |
false_func | typing.Callable | Function executing false branch, returns updated variables |
variables | list | List of variables used in the branches |
Returns:
typing.Any — Merged variable values after conditional execution (using Phi functions)
Example:
@qkernel
def my_kernel(q: Qubit) -> Qubit:
result = measure(q)
if result:
q = z(q)
return qfor_items [source]¶
def for_items(
d: Dict,
key_var_names: list[str],
value_var_name: str,
) -> Generator[tuple[Any, Any], None, None]Builder function to create a for-items loop in Qamomile frontend.
This context manager creates a ForItemsOperation that iterates over dictionary (key, value) pairs. The operation is always unrolled at transpile time since quantum backends cannot natively iterate over classical data structures.
Parameters:
| Name | Type | Description |
|---|---|---|
d | Dict | Dict handle to iterate over |
key_var_names | list[str] | Names of key unpacking variables (e.g., [“i”, “j”] for tuple keys) |
value_var_name | str | Name of value variable (e.g., “Jij”) |
Yields:
tuple[typing.Any, typing.Any] — Tuple of (key_handles, value_handle) for use in loop body
Example:
@qkernel
def ising_cost(
q: Vector[Qubit],
ising: Dict[Tuple[UInt, UInt], Float],
gamma: Float,
) -> Vector[Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qfor_loop [source]¶
def for_loop(
start,
stop,
step = 1,
var_name: str = '_loop_idx',
) -> Generator[UInt, None, None]Builder function to create a for loop in Qamomile frontend.
Parameters:
| Name | Type | Description |
|---|---|---|
start | `` | Loop start value (can be Handle or int) |
stop | `` | Loop stop value (can be Handle or int) |
step | `` | Loop step value (default=1) |
var_name | str | Name of the loop variable (default=“_loop_idx”) |
Yields:
UInt — The loop iteration variable (can be used as array index)
Example:
@QKernel
def my_kernel(qubits: Array[Qubit, Literal[3]]) -> Array[Qubit, Literal[3]]:
for i in qm.range(3):
qubits[i] = h(qubits[i])
return qubits
@QKernel
def my_kernel2(qubits: Array[Qubit, Literal[5]]) -> Array[Qubit, Literal[5]]:
for i in qm.range(1, 4): # i = 1, 2, 3
qubits[i] = h(qubits[i])
return qubitsget_current_tracer [source]¶
def get_current_tracer() -> Traceris_array_type [source]¶
def is_array_type(t: Any) -> boolCheck if type is a Vector, Matrix, or Tensor subclass.
items [source]¶
def items(d: Dict) -> DictItemsIteratorIterate over dictionary key-value pairs.
This function returns an iterator over (key, value) pairs from a Dict. Used for iterating over Ising coefficients and similar data structures.
Example:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)Parameters:
| Name | Type | Description |
|---|---|---|
d | Dict | A Dict handle to iterate over |
Returns:
DictItemsIterator — DictItemsIterator yielding (key, value) pairs
range [source]¶
def range(
stop_or_start: int | UInt,
stop: int | UInt | None = None,
step: int | UInt = 1,
) -> Iterator[UInt]Symbolic range for use in qkernel for-loops.
This function accepts UInt (symbolic) values and is transformed by the AST transformer into for_loop() calls.
Example:
for i in qmc.range(n): # 0 to n-1
for i in qmc.range(start, stop): # start to stop-1
for i in qmc.range(start, stop, step):should_trace_for_loop [source]¶
def should_trace_for_loop(start: Any, stop: Any, step: Any) -> boolDecide whether a qmc.range body must be traced.
The frontend executes loop bodies once to capture a ForOperation.
When all bounds are concrete and Python’s range would execute
zero times, tracing the body would incorrectly leak borrow /
destructive-consume state into the enclosing scope. Symbolic or
invalid bounds stay conservative and trace the body so the normal
compiler validation path reports any errors.
Parameters:
| Name | Type | Description |
|---|---|---|
start | typing.Any | Loop start bound. |
stop | typing.Any | Loop stop bound. |
step | typing.Any | Loop step bound. |
Returns:
bool — False only for statically-known zero-trip loops; True
bool — otherwise.
trace [source]¶
def trace(tracer: Tracer | None = None) -> Generator[Tracer, None, None]Context manager to set the current tracer.
while_loop [source]¶
def while_loop(cond: Callable) -> Generator[WhileLoop, None, None]Create a while loop whose condition is a measurement result.
The condition must be a Bit produced by qmc.measure().
Non-measurement conditions (classical variables, constants,
comparisons) are accepted at build time but will be rejected by
ValidateWhileContractPass during transpilation.
Parameters:
| Name | Type | Description |
|---|---|---|
cond | typing.Callable | A callable (lambda) that returns the loop condition. Must return a Bit handle originating from qmc.measure(). |
Yields:
WhileLoop — A marker object for the while loop context.
Example::
@qm.qkernel
def repeat_until_zero() -> qm.Bit:
q = qm.qubit("q")
q = qm.h(q)
bit = qm.measure(q)
while bit:
q = qm.qubit("q2")
q = qm.h(q)
bit = qm.measure(q)
return bitClasses¶
ArrayBase [source]¶
class ArrayBase(Handle, Generic[T])Base class for array types (Vector, Matrix, Tensor).
Provides common functionality for array indexing and element access.
Constructor¶
def __init__(
self,
value: ArrayValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
element_type: Type[T]shape: tuple[int | UInt, ...] Return the shape of the array.value: ArrayValue
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the array, enforcing borrow-return contract for quantum arrays.
For quantum arrays, all borrowed elements must be returned before the array can be consumed. This ensures that no unreturned borrows are silently discarded by operations like qkernel calls or controlled gates.
When any slot of the array has already been physically consumed
by an earlier destructive view operation (measure(q[1::2])
then measure(q)), this raises QubitConsumedError rather
than silently re-consuming those slots.
create¶
@classmethod
def create(
cls,
shape: tuple[int | UInt, ...],
name: str,
el_type: Type[T],
) -> 'ArrayBase[T]'Create an ArrayValue for the given shape and name.
validate_all_returned¶
def validate_all_returned(self) -> NoneValidate all borrowed elements have been returned.
Strict-return policy: an active slice view that is still
registered as the owner of any parent slot is treated as an
unreturned borrow even if the view itself has no outstanding
element borrows. The caller must perform an explicit slice
assignment (parent[a:b:c] = view) to release the view’s
bulk-borrow before consuming the parent. Destructively
consumed views (parked in the dict with view._consumed set
and view._consumed_by classified as
:attr:ConsumeMode.DESTRUCTIVE) record physically-destroyed
slots and are not outstanding borrows; they survive
end-of-block so a later whole-array consume can detect and
reject the destroyed slots.
Raises:
UnreturnedBorrowError— If any elements are still borrowed, either directly or by a slice view that has not been explicitly returned via slice assignment.
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
BitType [source]¶
class BitType(ClassicalTypeMixin, ValueType)Type representing a classical bit.
Dict [source]¶
class Dict(Handle, Generic[K, V])Dict handle for qkernel functions.
Represents a dictionary mapping keys to values, commonly used for Ising coefficients like {(i, j): Jij}.
Example:
@qmc.qkernel
def ising_cost(
q: qmc.Vector[qmc.Qubit],
ising: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qConstructor¶
def __init__(
self,
value: DictValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_entries: list[tuple[Handle, Handle]] = list(),
_size: UInt | None = None,
_key_type: type | None = None,
) -> NoneAttributes¶
size: UInt Return the number of entries as a UInt handle.value: DictValue
Methods¶
items¶
def items(self) -> DictItemsIterator[K, V]Return an iterator over (key, value) pairs.
DictItemsIterator [source]¶
class DictItemsIterator(Generic[K, V])Iterator for Dict.items() that yields (key, value) pairs.
This is used internally for iterating over Dict entries in qkernel.
Constructor¶
def __init__(self, dict_handle: 'Dict[K, V]', _index: int = 0) -> NoneAttributes¶
dict_handle: ‘Dict[K, V]’
Float [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
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]) -> OperationIfOperation [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]]) -> OperationObservable [source]¶
class Observable(Handle)Handle representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1) + 0.5 * qm_o.X(0)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
# Use Hamiltonian from bindings
return qm.expval(q, H)
# Pass via bindings
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NonePhiOp [source]¶
class PhiOp(Operation)SSA Phi function: merge point after conditional branch.
This operation selects one of two values based on a condition. Used to merge values from different branches of an if-else statement.
Example:
if condition:
x = x + 1 # true_value
else:
x = x + 2 # false_value
# x is now PhiOp(condition, true_value, false_value)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
condition: Valuefalse_value: Valueoperation_kind: OperationKindoutput: Valuesignature: Signaturetrue_value: Value
QFixed [source]¶
class QFixed(Handle)Constructor¶
def __init__(
self,
value: Value[QFixedType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QFixedType]
Qubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
Tracer [source]¶
class TracerConstructor¶
def __init__(self, _operations: list[Operation] = list()) -> NoneAttributes¶
operations: list[Operation]
Methods¶
add_operation¶
def add_operation(self, op) -> NoneUInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
WhileLoop [source]¶
class WhileLoopWhileOperation [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.frontend.operation.expval¶
Expectation value operation for computing <psi|H|psi>.
This module provides the expval() function for computing the expectation
value of a Hamiltonian observable with respect to a quantum state.
Overview¶
| Function | Description |
|---|---|
expval | Compute the expectation value of an observable on a quantum state. |
get_current_tracer | |
resolve_root_qubit_address | Resolve an array-element value to its root (array_uuid, index). |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
ExpvalOp | Expectation value operation. |
FloatType | Type representing a floating-point number. |
Value | A typed SSA value in the IR. |
Functions¶
expval [source]¶
def expval(
qubits: Qubit | Vector[Qubit] | tuple[Qubit, ...],
hamiltonian: Observable,
) -> FloatCompute the expectation value of an observable on a quantum state.
This function computes <psi|H|psi> where psi is the quantum
state represented by qubits and H is the Hamiltonian
observable.
The quantum state is consumed by this operation: expval
classifies as :attr:ConsumeMode.DESTRUCTIVE, the same category as
measure / cast. Conceptually an Estimator runs many shots
of the state to estimate the expectation, so the qubits cannot be
reused afterwards. Any attempt to access the same qubits / view
slots after expval is rejected as use-after-destroy, both at
trace time and post-fold in the IR.
Parameters:
| Name | Type | Description |
|---|---|---|
qubits | Qubit | Vector[Qubit] | tuple[Qubit, ...] | The quantum register holding the prepared state. A single Qubit handle is accepted for 1-qubit observables. When a Vector is passed all previously-borrowed elements must have been returned (the strict-return policy is enforced by consume here). When a slice view (VectorView) is passed its covered parent slots become consumed-slot markers so the parent cannot reuse them later. |
hamiltonian | Observable | The Observable parameter representing the Hamiltonian. The actual qamomile.observable.Hamiltonian is provided via transpile(..., bindings={...}). |
Returns:
Float — A scalar handle holding the expectation value, suitable
for use as the kernel return value or as an operand to
further classical operations.
Raises:
QubitConsumedError— Ifqubitswas already consumed (e.g. measured / cast earlier in the kernel), or if any covered slot of a passed view was destroyed by a prior destructive view operation.UnreturnedBorrowError— Ifqubitsis aVectorwith outstanding element or slice-view borrows that have not been returned.
Example:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1) + 0.5 * (qm_o.X(0) + qm_o.X(1))
@qm.qkernel
def vqe_step(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
# Ansatz
q[0] = qm.ry(q[0], theta)
q[0], q[1] = qm.cx(q[0], q[1])
# Expectation value -> Float (q is consumed here)
return qm.expval(q, H)
# Pass Hamiltonian via bindings
executable = transpiler.transpile(vqe_step, bindings={"H": H})get_current_tracer [source]¶
def get_current_tracer() -> Tracerresolve_root_qubit_address [source]¶
def resolve_root_qubit_address(value: 'Value') -> tuple[str, int] | NoneResolve an array-element value to its root (array_uuid, index).
Walks the parent_array / slice_of chain and composes the nested
affine slice maps, so view[i] resolves to
(root_uuid, start + step * i) for the composed (start, step). The
returned pair is the build-stable identity of the physical qubit slot: the
root array’s QInitOperation always registers it as
QubitAddress(root_uuid, index), so this address resolves even when the
element’s own (per-version) UUID was never registered.
The transpiler’s resource allocator uses the same walk to resolve gate and measurement operands; this shared helper keeps both call sites consistent.
Parameters:
| Name | Type | Description |
|---|---|---|
value | Value | The value to resolve. Expected to be an array element (parent_array set with a single constant element_indices entry). |
Returns:
tuple[str, int] | None — tuple[str, int] | None: (root_array_uuid, composed_index) when
value is an array element with a constant index whose entire
slice_of chain has constant slice_start / slice_step.
None when value is not an array element, when its index is
non-constant, or when any slice bound in the chain is non-constant
(those cases are deferred to the emit-time resolver, which has
bindings available). Also None for a negative constant index
or a chain frame with negative slice_start / non-positive
slice_step — composing those would silently address a wrong
root slot, so they are refused rather than guessed (the frontend
rejects them at trace time; this guard covers programmatically
constructed IR).
Classes¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]ExpvalOp [source]¶
class ExpvalOp(Operation)Expectation value operation.
This operation computes the expectation value <psi|H|psi> where psi is the quantum state and H is the Hamiltonian observable.
The operation bridges quantum and classical computation:
Input: quantum state (qubits) + Observable reference
Output: classical Float (expectation value)
Example IR:
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
hamiltonian: Value Alias for observable (deprecated, use observable instead).observable: Value The Observable parameter operand.operation_kind: OperationKind ExpvalOp is HYBRID - bridges quantum state to classical value.output: Value The expectation value result.qubits: Value The quantum register operand.signature: Signature
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
qamomile.circuit.frontend.operation.inverse¶
Frontend helpers for applying inverse quantum operations.
Overview¶
| Function | Description |
|---|---|
get_current_tracer | |
inverse | Create an inverse operation wrapper. |
| Class | Description |
|---|---|
ArrayBase | Base class for array types (Vector, Matrix, Tensor). |
ArrayValue | An array of typed IR values. |
Block | Unified block representation for all pipeline stages. |
BlockKind | Classification of block structure for pipeline stages. |
CallBlockOperation | |
CompositeGate | Base class for user-facing composite gate definitions. |
CompositeGateOperation | Represents a composite gate (QPE, QFT, etc.) as a single operation. |
CompositeGateType | Registry of known composite gate types. |
ConcreteControlledU | Controlled-U with concrete (int) number of controls. |
ControlledUOperation | Base class for controlled-U operations. |
FloatType | Type representing a floating-point number. |
ForItemsOperation | Represents iteration over dict/iterable items. |
ForOperation | Represents a for loop operation. |
GateOperation | Quantum gate operation. |
GateOperationType | |
IfOperation | Represents an if-else conditional operation. |
InverseBlockOperation | Represent an inverse qkernel/block as a first-class IR operation. |
InverseGate | Callable wrapper that applies a QKernel’s inverse. |
MeasureOperation | |
MeasureQFixedOperation | Measure a quantum fixed-point number. |
MeasureVectorOperation | Measure a vector of qubits. |
Operation | |
OperationKind | Classification of operations for classical/quantum separation. |
PauliEvolveOp | Pauli evolution operation: exp(-i * gamma * H). |
QInitOperation | Initialize the qubit |
QKernel | Decorator class for Qamomile quantum kernels. |
ResourceMetadata | Resource estimation metadata for composite gates. |
ReturnOperation | Explicit return operation marking the end of a block with return values. |
SymbolicControlledU | Controlled-U with symbolic (Value) number of controls. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
ValueBase | Protocol for IR values with typed metadata. |
ValueSubstitutor | Substitute IR values in operations using a UUID-keyed mapping. |
VectorView | Strided view over a parent Vector, backed by a sliced ArrayValue. |
WhileOperation | Represents a while loop operation. |
Functions¶
get_current_tracer [source]¶
def get_current_tracer() -> Tracerinverse [source]¶
def inverse(target: QKernel | Callable[..., Any]) -> AnyCreate an inverse operation wrapper.
Native Qamomile gate functions are first synthesized into tiny
QKernel objects, then inverted with the same block walker used for
user-defined kernels. Known QFT/IQFT functions map directly to their
counterpart so backend-native composite emission remains available.
Parameters:
| Name | Type | Description |
|---|---|---|
target | QKernel | Callable[..., Any] | Native gate function, QKernel, or supported stdlib function to invert. |
Returns:
Any — A callable inverse wrapper, or the opposite QFT/IQFT function.
Raises:
TypeError— Iftargetcannot be interpreted as a gate-like callable, or if aCompositeGateinstance is passed directly.NotImplementedError— If an inverted kernel uses unsupported operations such asif/while/for itemscontrol flow,QInit, or aForOperationwhose bounds are not compile-time constants when the inverse wrapper is traced.
Example:
>>> import qamomile.circuit as qmc
>>> @qmc.qkernel
... def layer(q: qmc.Qubit, angle: qmc.Float) -> qmc.Qubit:
... q = qmc.h(q)
... q = qmc.rz(q, angle)
... return q
>>> @qmc.qkernel
... def circuit(angle: qmc.Float) -> qmc.Qubit:
... q = qmc.qubit("q")
... q = layer(q, angle)
... q = qmc.inverse(layer)(q, angle)
... return qClasses¶
ArrayBase [source]¶
class ArrayBase(Handle, Generic[T])Base class for array types (Vector, Matrix, Tensor).
Provides common functionality for array indexing and element access.
Constructor¶
def __init__(
self,
value: ArrayValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
element_type: Type[T]shape: tuple[int | UInt, ...] Return the shape of the array.value: ArrayValue
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the array, enforcing borrow-return contract for quantum arrays.
For quantum arrays, all borrowed elements must be returned before the array can be consumed. This ensures that no unreturned borrows are silently discarded by operations like qkernel calls or controlled gates.
When any slot of the array has already been physically consumed
by an earlier destructive view operation (measure(q[1::2])
then measure(q)), this raises QubitConsumedError rather
than silently re-consuming those slots.
create¶
@classmethod
def create(
cls,
shape: tuple[int | UInt, ...],
name: str,
el_type: Type[T],
) -> 'ArrayBase[T]'Create an ArrayValue for the given shape and name.
validate_all_returned¶
def validate_all_returned(self) -> NoneValidate all borrowed elements have been returned.
Strict-return policy: an active slice view that is still
registered as the owner of any parent slot is treated as an
unreturned borrow even if the view itself has no outstanding
element borrows. The caller must perform an explicit slice
assignment (parent[a:b:c] = view) to release the view’s
bulk-borrow before consuming the parent. Destructively
consumed views (parked in the dict with view._consumed set
and view._consumed_by classified as
:attr:ConsumeMode.DESTRUCTIVE) record physically-destroyed
slots and are not outstanding borrows; they survive
end-of-block so a later whole-array consume can detect and
reject the destroyed slots.
Raises:
UnreturnedBorrowError— If any elements are still borrowed, either directly or by a slice view that has not been explicitly returned via slice assignment.
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
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(),
param_slots: tuple[ParamSlot, ...] = tuple(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]param_slots: tuple[ParamSlot, ...]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.
BlockKind [source]¶
class BlockKind(Enum)Classification of block structure for pipeline stages.
Attributes¶
AFFINEANALYZEDHIERARCHICALTRACED
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).
CompositeGate [source]¶
class CompositeGate(abc.ABC)Base class for user-facing composite gate definitions.
Subclasses can define composite gates in two ways:
Using _decompose() (recommended for users): Define the gate decomposition using frontend syntax (same as
qkernel[source]).class QFT(CompositeGate): def __init__(self, num_qubits: int): self._num_qubits = num_qubits @property def num_target_qubits(self) -> int: return self._num_qubits def _decompose(self, qubits: Vector[Qubit]) -> Vector[Qubit]: # Use frontend syntax: qm.h(), qm.cp(), qm.range(), etc. n = self._num_qubits for j in qmc.range(n - 1, -1, -1): qubits[j] = qmc.h(qubits[j]) for k in qmc.range(j - 1, -1, -1): angle = math.pi / (2 ** (j - k)) qubits[j], qubits[k] = qmc.cp(qubits[j], qubits[k], angle) return qubits def _resources(self) -> ResourceMetadata: return ResourceMetadata(t_gates=0)Using get_implementation() (advanced): Return a pre-built Block directly.
Example usage:
# Factory function pattern
def qft(qubits: Vector[Qubit]) -> Vector[Qubit]:
n = _get_size(qubits)
return QFT(n)(qubits)
# Direct class usage
result = QFT(3)(*qubit_list)Attributes¶
custom_name: strgate_type: CompositeGateTypenum_control_qubits: int Number of control qubits (default: 0).num_target_qubits: int Number of target qubits this gate operates on.
Methods¶
build_decomposition¶
def build_decomposition(self, *qubits: Qubit = (), **params: Any = {}) -> Block | NoneBuild the decomposition circuit dynamically.
Override this method to provide a decomposition that depends on runtime arguments (e.g., QPE needs the unitary Block).
This method is called by InlinePass when inlining composite gates that have dynamic implementations.
Parameters:
| Name | Type | Description |
|---|---|---|
*qubits | Qubit | The qubits passed to the gate |
**params | Any | Additional parameters (e.g., unitary for QPE) |
Returns:
Block | None — Block containing the decomposition, or None if not available.
Example:
class QPE(CompositeGate):
def build_decomposition(self, *qubits, **params):
unitary = params.get("unitary")
# Build QPE circuit using the unitary
return self._build_qpe_impl(qubits, unitary)get_implementation¶
def get_implementation(self) -> Block | NoneGet the implementation Block, if any.
Return None for stub gates (used in resource estimation). Override in subclasses to provide implementation.
Note: If _decompose() is defined, it takes precedence over this method.
get_resource_metadata¶
def get_resource_metadata(self) -> ResourceMetadata | NoneGet resource estimation metadata.
Returns _resources() if defined, otherwise None. Override _resources() to provide resource hints.
get_resources_for_strategy¶
def get_resources_for_strategy(self, strategy_name: str | None = None) -> ResourceMetadata | NoneGet resource metadata for a specific strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
strategy_name | str | None | Strategy to query, or None for default |
Returns:
ResourceMetadata | None — ResourceMetadata for the strategy, or None if not available
get_strategy¶
@classmethod
def get_strategy(cls, name: str | None = None) -> 'DecompositionStrategy | None'Get a registered decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | None | Strategy name, or None for default strategy |
Returns:
'DecompositionStrategy | None' — DecompositionStrategy instance, or None if not found
list_strategies¶
@classmethod
def list_strategies(cls) -> list[str]List all registered strategy names.
Returns:
list[str] — List of strategy names
register_strategy¶
@classmethod
def register_strategy(cls, name: str, strategy: 'DecompositionStrategy') -> NoneRegister a decomposition strategy for this gate type.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy identifier (e.g., “standard”, “approximate”) |
strategy | 'DecompositionStrategy' | DecompositionStrategy instance |
Example:
QFT.register_strategy("approximate", ApproximateQFTStrategy(k=3))set_default_strategy¶
@classmethod
def set_default_strategy(cls, name: str) -> NoneSet the default decomposition strategy.
Parameters:
| Name | Type | Description |
|---|---|---|
name | str | Strategy name to use as default |
Raises:
ValueError— If strategy is not registered
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.
CompositeGateType [source]¶
class CompositeGateType(enum.Enum)Registry of known composite gate types.
Attributes¶
CUSTOMIQFTQFTQPE
ConcreteControlledU [source]¶
class ConcreteControlledU(ControlledUOperation)Controlled-U with concrete (int) number of controls.
Operand layout: [ctrl_0, ..., ctrl_n, tgt_0, ..., tgt_m, params...]
Result layout: [ctrl_0', ..., ctrl_n', tgt_0', ..., tgt_m']
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: int = 1,
) -> NoneAttributes¶
control_operands: list[Value]num_controls: intparam_operands: list[Value]signature: Signaturetarget_operands: list[Value]
ControlledUOperation [source]¶
class ControlledUOperation(Operation)Base class for controlled-U operations.
Two concrete subclasses handle distinct operand layouts:
ConcreteControlledU: Fixednum_controls: int, individual qubit operands.SymbolicControlledU: Symbolicnum_controls: Value, vector-based control operands; optionalcontrol_indicesselects a subset of the control vector to act as controls (the rest pass through).
All isinstance(op, ControlledUOperation) checks match every subclass.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: int | Value = 1,
) -> NoneAttributes¶
block: Block | Nonecontrol_operands: list[Value] Get the control qubit values.is_symbolic_num_controls: bool Whether num_controls is symbolic (Value) rather than concrete.num_controls: int | Valueoperation_kind: OperationKindparam_operands: list[Value] Get parameter operands (non-qubit, non-block).power: int | Valuesignature: Signaturetarget_operands: list[Value] Get the target qubit values (arguments to U).
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationFloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
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]) -> OperationGateOperation [source]¶
class GateOperation(Operation)Quantum gate operation.
For rotation gates (RX, RY, RZ, P, CP, RZZ), the angle parameter is
stored as the last element of operands. Use the theta
property for typed read access and the rotation / fixed factory
class-methods for type-safe construction.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
gate_type: GateOperationType | None = None,
) -> NoneAttributes¶
gate_type: GateOperationType | Noneoperation_kind: OperationKindqubit_operands: list[Value] Qubit operands (excluding the theta parameter if present).signature: Signaturetheta: Value | None Angle parameter for rotation gates, orNonefor fixed gates.
Methods¶
fixed¶
@classmethod
def fixed(
cls,
gate_type: GateOperationType,
qubits: list[Value],
results: list[Value],
) -> 'GateOperation'Create a fixed gate (H, X, CX, SWAP, …) with no angle parameter.
rotation¶
@classmethod
def rotation(
cls,
gate_type: GateOperationType,
qubits: list[Value],
theta: Value,
results: list[Value],
) -> 'GateOperation'Create a rotation gate (RX, RY, RZ, P, CP, RZZ) with an angle.
GateOperationType [source]¶
class GateOperationType(enum.Enum)Attributes¶
CPCXCZHPRXRYRZRZZSSDGSWAPTTDGTOFFOLIXYZ
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]]) -> OperationInverseBlockOperation [source]¶
class InverseBlockOperation(Operation)Represent an inverse qkernel/block as a first-class IR operation.
The operation stores both the original forward block and a Qamomile-built
inverse implementation block. Emitters may use source_block with a
backend-native inverse/adjoint primitive, then fall back to
implementation_block when native inversion is unavailable.
Operands are ordered as control qubits, target quantum operands, then
classical/object parameters. Results mirror the quantum operand layout:
control results first, then one target result per target operand. Vector
target operands therefore count as one operand/result while contributing
their scalar width to num_target_qubits.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
num_control_qubits: int = 0,
num_target_qubits: int = 0,
custom_name: str = '',
source_block: Block | None = None,
implementation_block: Block | None = None,
) -> NoneAttributes¶
control_qubits: list[‘Value’] Return control quantum operands.custom_name: strimplementation_block: Block | Nonename: str Return a human-readable inverse operation name.num_control_qubits: intnum_target_qubits: intoperation_kind: OperationKind Return the operation kind.parameters: list[‘Value’] Return classical/object parameter operands.signature: Signature Return the operation signature.source_block: Block | Nonetarget_qubits: list[‘Value’] Return target quantum operands.
InverseGate [source]¶
class InverseGateCallable wrapper that applies a QKernel’s inverse.
Parameters:
| Name | Type | Description |
|---|---|---|
qkernel | QKernel | Kernel whose inverse should be emitted. |
Constructor¶
def __init__(self, qkernel: QKernel) -> NoneInitialize the inverse wrapper.
Parameters:
| Name | Type | Description |
|---|---|---|
qkernel | QKernel | Kernel whose inverse should be emitted. |
MeasureOperation [source]¶
class MeasureOperation(Operation)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
MeasureQFixedOperation [source]¶
class MeasureQFixedOperation(Operation)Measure a quantum fixed-point number.
This operation measures all qubits in a QFixed register and produces a Float result. During transpilation, this is lowered to individual MeasureOperations plus a DecodeQFixedOperation.
operands: [QFixed value (contains qubit_values in params)] results: [Float value]
Encoding:
For QPE phase (int_bits=0): float_value = 0.b0b1b2... = b00.5 + b10.25 + b2*0.125 + ...
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
num_bits: int = 0,
int_bits: int = 0,
) -> NoneAttributes¶
int_bits: intnum_bits: intoperation_kind: OperationKindsignature: Signature
MeasureVectorOperation [source]¶
class MeasureVectorOperation(Operation)Measure a vector of qubits.
Takes a Vector[Qubit] (ArrayValue) and produces a Vector[Bit] (ArrayValue). This operation measures all qubits in the vector as a single operation.
operands: [ArrayValue of qubits] results: [ArrayValue of bits]
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
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.
OperationKind [source]¶
class OperationKind(enum.Enum)Classification of operations for classical/quantum separation.
This enum is used to categorize operations during compilation to determine which parts run on classical hardware vs quantum hardware.
Values:
QUANTUM: Pure quantum operations (gates, qubit allocation) CLASSICAL: Pure classical operations (arithmetic, comparisons) HYBRID: Operations that bridge classical and quantum (measurement, encode/decode) CONTROL: Control flow structures (for, while, if)
Attributes¶
CLASSICALCONTROLHYBRIDQUANTUM
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
QKernel [source]¶
class QKernel(Generic[P, R])Decorator class for Qamomile quantum kernels.
Constructor¶
def __init__(self, func: Callable[P, R]) -> NoneAttributes¶
block: Block Compile the function to a hierarchical Block if not already compiled.funcinput_typesnameoutput_typesraw_funcsignature
Methods¶
build¶
def build(self, parameters: list[str] | None = None, **kwargs: Any = {}) -> BlockBuild a traced Block by tracing this kernel.
Parameters:
| Name | Type | Description |
|---|---|---|
parameters | list[str] | None | List of argument names to keep as unbound parameters. - None (default): Auto-detect parameters (non-Qubit args without value/default) - []: No parameters (all non-Qubit args must have value/default) - [“name”]: Explicit parameter list Only float, int, UInt, and their arrays are allowed as parameters. |
**kwargs | Any | Concrete values for non-parameter arguments. |
Returns:
Block — The traced block ready for transpilation, estimation,
or visualization.
Raises:
TypeError— If a non-parameterizable type is specified as parameter.ValueError— If required arguments are missing.
Example:
@qm.qkernel
def circuit(q: Qubit, theta: float) -> Qubit:
q = qm.rx(q, theta)
return q
# Auto-detect theta as parameter
block = circuit.build()
# Explicit parameter list
block = circuit.build(parameters=["theta"])
# theta bound to concrete value
block = circuit.build(theta=0.5)
# Transpile with binding
transpiler = QiskitTranspiler()
result = transpiler.emit(graph, binding={"theta": 0.5})draw¶
def draw(
self,
inline: bool = False,
fold_loops: bool = True,
expand_composite: bool = False,
inline_depth: int | None = None,
**kwargs: Any = {},
) -> AnyVisualize the circuit using Matplotlib.
This method builds the computation graph and creates a static visualization. Parameters are auto-detected: non-Qubit arguments without concrete values are shown as symbolic parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
inline | bool | If True, expand CallBlockOperation contents (inlining). If False (default), show CallBlockOperation as boxes. |
fold_loops | bool | If True (default), display ForOperation as blocks instead of unrolling. If False, expand loops and show all iterations. |
expand_composite | bool | If True, expand CompositeGateOperation (QFT, IQFT, etc.). If False (default), show as boxes. Independent of inline. |
inline_depth | int | None | Maximum nesting depth for inline expansion. None means unlimited (default). 0 means no inlining, 1 means top-level only, etc. Only affects CallBlock/ControlledU, not CompositeGate. |
**kwargs | Any | Concrete values for arguments. Arguments not provided here (and without defaults) will be shown as symbolic parameters. |
Returns:
Any — matplotlib.figure.Figure object.
Raises:
ImportError— If matplotlib is not installed.
Example:
import qamomile.circuit as qm
@qm.qkernel
def inner(q: qm.Qubit) -> qm.Qubit:
return qm.x(q)
@qm.qkernel
def circuit(q: qm.Qubit, theta: float) -> qm.Qubit:
q = inner(q)
q = qm.h(q)
q = qm.rx(q, theta)
return q
# Draw with auto-detected symbolic parameter (theta)
fig = circuit.draw()
# Draw with bound parameter
fig = circuit.draw(theta=0.5)
# Draw with blocks as boxes (default)
fig = circuit.draw()
# Draw with blocks expanded (inlined)
fig = circuit.draw(inline=True)
# Draw with loops folded (shown as blocks)
fig = circuit.draw(fold_loops=True)
# Draw with composite gates expanded
fig = circuit.draw(expand_composite=True)estimate_resources¶
def estimate_resources(self, *, bindings: dict[str, Any] | None = None) -> ResourceEstimateEstimate all resources for this kernel’s circuit.
Convenience method that delegates to the module-level
estimate_resources function, eliminating the need to
access .block directly.
Parameters:
| Name | Type | Description |
|---|---|---|
bindings | dict[str, Any] | None | Optional concrete parameter bindings (scalars and dicts). Dict values trigger |key| cardinality substitution. |
Returns:
ResourceEstimate — ResourceEstimate with qubits, gates, and parameters.
Example:
>>> @qm.qkernel
... def bell() -> 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 = bell.estimate_resources()
>>> print(est.qubits) # 2ResourceMetadata [source]¶
class ResourceMetadataResource estimation metadata for composite gates.
Gate count fields mirror GateCount categories.
None semantics:
Fields left as None mean “unknown/unspecified”. During extraction, gate_counter treats None as 0, which may undercount resources if the true value is nonzero. To ensure accurate resource estimates, set all relevant fields explicitly.
When total_gates is set but some of single_qubit_gates, two_qubit_gates, or multi_qubit_gates are None, the extractor emits a UserWarning if the known sub-total is less than total_gates, indicating potentially missing gate category data.
Constructor¶
def __init__(
self,
query_complexity: int | None = None,
t_gates: int | None = None,
ancilla_qubits: int = 0,
total_gates: int | None = None,
single_qubit_gates: int | None = None,
two_qubit_gates: int | None = None,
multi_qubit_gates: int | None = None,
clifford_gates: int | None = None,
rotation_gates: int | None = None,
custom_metadata: dict[str, Any] = dict(),
) -> NoneAttributes¶
ancilla_qubits: intclifford_gates: int | Nonecustom_metadata: dict[str, Any]multi_qubit_gates: int | Nonequery_complexity: int | Nonerotation_gates: int | Nonesingle_qubit_gates: int | Nonet_gates: int | Nonetotal_gates: int | Nonetwo_qubit_gates: int | None
ReturnOperation [source]¶
class ReturnOperation(Operation)Explicit return operation marking the end of a block with return values.
This operation represents an explicit return statement in the IR. It takes the values to be returned as operands and produces no results (it is a terminal operation that transfers control flow back to the caller).
operands: [Value, ...] - The values to return (may be empty for void returns) results: [] - Always empty (terminal operation)
Example:
A function that returns two values (a UInt and a Float):
ReturnOperation(
operands=[uint_value, float_value],
results=[],
)
The signature would be:
operands=[ParamHint("return_0", UIntType()), ParamHint("return_1", FloatType())]
results=[]Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Return CLASSICAL as this is a control flow operation without quantum effects.signature: Signature Return the signature with operands for each return value and no results.
SymbolicControlledU [source]¶
class SymbolicControlledU(ControlledUOperation)Controlled-U with symbolic (Value) number of controls.
Operand layout: [ctrl_arg_0, ..., ctrl_arg_{k-1}, tgt_0, ..., tgt_m, params...]
Result layout: [ctrl_arg_0', ..., ctrl_arg_{k-1}', tgt_0', ..., tgt_m']
The number of control arguments k is recorded in
num_control_args; the default k = 1 corresponds to the
historical single-pool form (operands[0] is a
Vector[Qubit] / VectorView whose length equals
num_controls, or whose control_indices-selected subset
does). When k > 1 the control prefix is a heterogeneous
sequence of scalar Qubit values and ArrayValues whose
total qubit count is num_controls; the emit pass walks them
in order to recover the per-physical-qubit control set.
When control_indices is None the entire control prefix
is used as active controls (one-arg form: len(ctrl_vector) == num_controls; multi-arg form: the qubit-count sum of the
prefix args equals num_controls). When non-None, the
listed indices select exactly num_controls slots from a
single-arg pool to act as controls; combining
control_indices with the multi-arg control prefix is
rejected at frontend time.
Each control_indices entry is stored as a Value of
UIntType regardless of whether the frontend passed an
int literal or a UInt handle, so all downstream
value-substitution passes see a uniform shape.
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
power: int | Value = 1,
block: Block | None = None,
num_controls: Value = (lambda: Value(type=(UIntType()), name=''))(),
control_indices: tuple[Value, ...] | None = None,
num_control_args: int = 1,
) -> NoneAttributes¶
control_indices: tuple[Value, ...] | Nonecontrol_operands: list[Value]is_symbolic_num_controls: boolnum_control_args: intnum_controls: Valueparam_operands: list[Value]signature: Signaturetarget_operands: list[Value]
Methods¶
all_input_values¶
def all_input_values(self) -> list[ValueBase]replace_values¶
def replace_values(self, mapping: dict[str, ValueBase]) -> OperationUIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
ValueBase [source]¶
class ValueBase(Protocol)Protocol for IR values with typed metadata.
Attributes are declared as read-only properties to match frozen dataclass fields in concrete implementations (Value, ArrayValue, etc.).
Attributes¶
logical_id: strmetadata: ValueMetadataname: struuid: str
Methods¶
get_const¶
def get_const(self) -> int | float | bool | Noneis_constant¶
def is_constant(self) -> boolis_parameter¶
def is_parameter(self) -> boolnext_version¶
def next_version(self) -> ValueBaseparameter_name¶
def parameter_name(self) -> str | NoneValueSubstitutor [source]¶
class ValueSubstitutorSubstitute IR values in operations using a UUID-keyed mapping.
Parameters:
| Name | Type | Description |
|---|---|---|
value_map | Mapping[str, ValueBase] | Mapping from original value UUIDs to replacement values. |
transitive | bool | Whether substitutions should chase chains such as A -> B -> C to the terminal value. Defaults to False. |
Constructor¶
def __init__(self, value_map: Mapping[str, ValueBase], transitive: bool = False)Initialize the substitutor.
Parameters:
| Name | Type | Description |
|---|---|---|
value_map | Mapping[str, ValueBase] | Mapping from original value UUIDs to replacement values. |
transitive | bool | Whether substitutions should chase chains to their terminal value. Defaults to False. |
Methods¶
substitute_operation¶
def substitute_operation(self, op: Operation) -> OperationSubstitute values in an operation.
Parameters:
| Name | Type | Description |
|---|---|---|
op | Operation | Operation whose operands, results, and subclass-specific value fields should be substituted. |
Returns:
Operation — Operation with all mapped value references replaced.
substitute_value¶
def substitute_value(self, value: ValueBase) -> ValueBaseSubstitute a single value.
Parameters:
| Name | Type | Description |
|---|---|---|
value | ValueBase | Value to replace or rebuild. |
Returns:
ValueBase — Replacement value, rebuilt value with substituted
ValueBase — metadata, or the original value when nothing maps.
VectorView [source]¶
class VectorView(Vector[T])Strided view over a parent Vector, backed by a sliced ArrayValue.
A VectorView is produced by slicing a Vector (q[1::2],
q[a:b], etc.). It is a thin Vector subclass whose value
is a fresh ArrayValue with slice_of / slice_start /
slice_step metadata pointing back to the parent’s ArrayValue.
Element accesses go through Vector._get_element unchanged — the
IR element carries parent_array = sliced_av, and the emit-time
resolver walks the slice_of chain to produce the physical qubit
index. No affine translation happens in the view itself.
Because the sliced ArrayValue is a first-class IR Value,
the view can be passed as an operand of CallBlockOperation to
another @qkernel without the inline-trace special-case path
that earlier iterations required. Passing views through
expval / measure likewise operates on the sliced qubit
subset, not the root parent as a whole.
Linearity:
Slicing bulk-borrows the covered parent slots whenever
start, step and length are compile-time int
constants. While the view is live, accessing the corresponding
parent slot directly (q[0] after evens = q[0::2]) raises
QubitConsumedError. Under the strict-return policy the
view’s ownership is cleared only by two operations:
slice-assigning it back into the parent (
parent[a:b:c] = view) — this is the only path that fully releases the borrow without destroying the qubits;destructively consuming it (
measure(view)/cast(view, ...)/expval(view, H)) — the physical slots become consumed markers, no return needed.
Every other consume (broadcast gates h(view),
pauli_evolve(view, H, gamma), sub-kernel calls
f(view), controlled-U index_spec) only transfers
ownership to a freshly-wrapped VectorView and that new
view still must be returned via slice assignment. A view
left bulk-borrowing at the parent’s consume point raises
UnreturnedBorrowError.
Symbolic slices (q[lo:hi] with lo/hi UInt) cannot
enumerate their covered slots at trace time and therefore skip
the bulk-borrow here; SliceBorrowCheckPass picks them up
post-fold after bindings resolve the bounds to concrete values.
Example:
@qmc.qkernel
def alternating_h(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
evens = q[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])
q[0::2] = evens # explicit return before the parent is used
return qMethods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the view and release its parent slice-borrows.
Validates that every view-local borrow has been returned,
then dispatches on operation_name to keep the parent’s
slice-borrow record consistent with the new strict-return
semantics:
Destructive (
measure/cast): leaveselfparked in the parent’s borrow table as a destroyed-slot breadcrumb.super().consume()flipsself._consumed = Trueandself._consumed_by = operation_name, which is what :func:_is_destroyed_slot_ownerreads to reject subsequent access at the same slot.Releasing (
slice assignment): drop every parent entry thatselfcurrently owns. The caller (the slice- assignment frontend path) also emits aReleaseSliceViewOperationso the IR-level checker sees the release. This branch is reserved for explicit borrow- return paths.Transfer (every other op — broadcast gates, rotation, phase, ControlledU, sub-kernel call argument consumption, etc.): rebind the parent’s borrow entry from
selfto the new view handle returned here. The new view inheritsself._slice_covered_indicesso it can be slice-assigned back to the parent later — strict-return requires that eventualparent[a:b:c] = new_view.
Operations that produce a fresh sliced ArrayValue (e.g.
:func:qamomile.circuit.frontend.operation.pauli_evolve.pauli_evolve,
:class:QKernel.__call__ for callees that return a sliced
array) cannot simply use the auto-returned new_view
because the new view they build wraps a different Value
than this consume’s return. Those op implementations call
:meth:_transfer_borrow_to after building their result so
the parent’s borrow table tracks the right handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this view (used in error messages and for dispatch). |
Returns:
typing.Self — A fresh view handle with the same backing state; under
typing.Self — transfer the parent’s borrow table now points at this
typing.Self — handle, under release / destruction the parent’s record
typing.Self — for the covered slots is finalised.
Raises:
QubitConsumedError— If any covered slot was already destroyed by a prior destructive view consume on an overlapping view that has since gone out of scope.
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.frontend.operation.measurement¶
Measurement operations for quantum circuits.
Overview¶
| Function | Description |
|---|---|
get_current_tracer | |
measure | Measure a qubit or QFixed in the computational basis. |
| Class | Description |
|---|---|
ArrayValue | An array of typed IR values. |
IRMeasureOperation | |
MeasureQFixedOperation | Measure a quantum fixed-point number. |
MeasureVectorOperation | Measure a vector of qubits. |
UIntType | Type representing an unsigned integer. |
Value | A typed SSA value in the IR. |
VectorClass | 1-dimensional array type. |
Functions¶
get_current_tracer [source]¶
def get_current_tracer() -> Tracermeasure [source]¶
def measure(target: Union[Qubit, QFixed, Vector[Qubit]]) -> Union[Bit, Float, Vector[Bit]]Measure a qubit or QFixed in the computational basis.
Performs a projective measurement in the Z-basis. The quantum resource is consumed by this operation and cannot be used afterwards.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, QFixed, Vector[Qubit]] | The quantum resource to measure. - Qubit: Returns a classical Bit - QFixed: Returns a Float (decoded from measured bits) |
Returns:
Union[Bit, Float, Vector[Bit]] — Bit for Qubit input, Float for QFixed input.
Example:
@qkernel
def measure_qubit(q: Qubit) -> Bit:
q = h(q)
return measure(q)
@qkernel
def measure_qfixed(qf: QFixed) -> Float:
# After QPE, qf holds phase bits
return measure(qf)Classes¶
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]MeasureOperation [source]¶
class MeasureOperation(Operation)Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
MeasureQFixedOperation [source]¶
class MeasureQFixedOperation(Operation)Measure a quantum fixed-point number.
This operation measures all qubits in a QFixed register and produces a Float result. During transpilation, this is lowered to individual MeasureOperations plus a DecodeQFixedOperation.
operands: [QFixed value (contains qubit_values in params)] results: [Float value]
Encoding:
For QPE phase (int_bits=0): float_value = 0.b0b1b2... = b00.5 + b10.25 + b2*0.125 + ...
Constructor¶
def __init__(
self,
operands: list[Value] = list(),
results: list[Value] = list(),
num_bits: int = 0,
int_bits: int = 0,
) -> NoneAttributes¶
int_bits: intnum_bits: intoperation_kind: OperationKindsignature: Signature
MeasureVectorOperation [source]¶
class MeasureVectorOperation(Operation)Measure a vector of qubits.
Takes a Vector[Qubit] (ArrayValue) and produces a Vector[Bit] (ArrayValue). This operation measures all qubits in the vector as a single operation.
operands: [ArrayValue of qubits] results: [ArrayValue of bits]
Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKindsignature: Signature
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
qamomile.circuit.frontend.operation.pauli_evolve¶
Pauli evolution operation for applying exp(-i * gamma * H).
This module provides the pauli_evolve() function for applying the
time evolution operator of a Pauli Hamiltonian to a quantum state.
Overview¶
| Function | Description |
|---|---|
get_current_tracer | |
pauli_evolve | Apply exp(-i * gamma * H) to a qubit register. |
| Class | Description |
|---|---|
PauliEvolveOp | Pauli evolution operation: exp(-i * gamma * H). |
VectorView | Strided view over a parent Vector, backed by a sliced ArrayValue. |
Functions¶
get_current_tracer [source]¶
def get_current_tracer() -> Tracerpauli_evolve [source]¶
def pauli_evolve(q: Vector[Qubit], hamiltonian: Observable, gamma: Float) -> Vector[Qubit]Apply exp(-i * gamma * H) to a qubit register.
Implements Hamiltonian time evolution using the Pauli gadget technique. The actual Hamiltonian is provided via bindings at transpile time.
Each backend can use native implementations:
Qiskit: PauliEvolutionGate
QuriParts: PauliRotation gates
Others: fallback decomposition (basis change + CNOT ladder + RZ)
Parameters:
| Name | Type | Description |
|---|---|---|
q | Vector[Qubit] | The quantum register to evolve. |
hamiltonian | Observable | Observable parameter referencing the Hamiltonian. The actual qamomile.observable.Hamiltonian is provided via bindings. |
gamma | Float | Evolution time / variational parameter. |
Returns:
Vector[Qubit] — Vector[Qubit]: The evolved qubit register.
Example:
import qamomile.circuit as qmc
import qamomile.observable as qm_o
H = 0.5 * qm_o.X(0) * qm_o.Z(1) + qm_o.Z(0)
@qmc.qkernel
def cost_layer(
q: qmc.Vector[qmc.Qubit],
H: qmc.Observable,
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
q = qmc.pauli_evolve(q, H, gamma)
return q
transpiler = QiskitTranspiler()
exe = transpiler.transpile(cost_layer, bindings={"H": H, "gamma": 0.5})Classes¶
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
VectorView [source]¶
class VectorView(Vector[T])Strided view over a parent Vector, backed by a sliced ArrayValue.
A VectorView is produced by slicing a Vector (q[1::2],
q[a:b], etc.). It is a thin Vector subclass whose value
is a fresh ArrayValue with slice_of / slice_start /
slice_step metadata pointing back to the parent’s ArrayValue.
Element accesses go through Vector._get_element unchanged — the
IR element carries parent_array = sliced_av, and the emit-time
resolver walks the slice_of chain to produce the physical qubit
index. No affine translation happens in the view itself.
Because the sliced ArrayValue is a first-class IR Value,
the view can be passed as an operand of CallBlockOperation to
another @qkernel without the inline-trace special-case path
that earlier iterations required. Passing views through
expval / measure likewise operates on the sliced qubit
subset, not the root parent as a whole.
Linearity:
Slicing bulk-borrows the covered parent slots whenever
start, step and length are compile-time int
constants. While the view is live, accessing the corresponding
parent slot directly (q[0] after evens = q[0::2]) raises
QubitConsumedError. Under the strict-return policy the
view’s ownership is cleared only by two operations:
slice-assigning it back into the parent (
parent[a:b:c] = view) — this is the only path that fully releases the borrow without destroying the qubits;destructively consuming it (
measure(view)/cast(view, ...)/expval(view, H)) — the physical slots become consumed markers, no return needed.
Every other consume (broadcast gates h(view),
pauli_evolve(view, H, gamma), sub-kernel calls
f(view), controlled-U index_spec) only transfers
ownership to a freshly-wrapped VectorView and that new
view still must be returned via slice assignment. A view
left bulk-borrowing at the parent’s consume point raises
UnreturnedBorrowError.
Symbolic slices (q[lo:hi] with lo/hi UInt) cannot
enumerate their covered slots at trace time and therefore skip
the bulk-borrow here; SliceBorrowCheckPass picks them up
post-fold after bindings resolve the bounds to concrete values.
Example:
@qmc.qkernel
def alternating_h(q: qmc.Vector[qmc.Qubit]) -> qmc.Vector[qmc.Qubit]:
evens = q[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])
q[0::2] = evens # explicit return before the parent is used
return qMethods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the view and release its parent slice-borrows.
Validates that every view-local borrow has been returned,
then dispatches on operation_name to keep the parent’s
slice-borrow record consistent with the new strict-return
semantics:
Destructive (
measure/cast): leaveselfparked in the parent’s borrow table as a destroyed-slot breadcrumb.super().consume()flipsself._consumed = Trueandself._consumed_by = operation_name, which is what :func:_is_destroyed_slot_ownerreads to reject subsequent access at the same slot.Releasing (
slice assignment): drop every parent entry thatselfcurrently owns. The caller (the slice- assignment frontend path) also emits aReleaseSliceViewOperationso the IR-level checker sees the release. This branch is reserved for explicit borrow- return paths.Transfer (every other op — broadcast gates, rotation, phase, ControlledU, sub-kernel call argument consumption, etc.): rebind the parent’s borrow entry from
selfto the new view handle returned here. The new view inheritsself._slice_covered_indicesso it can be slice-assigned back to the parent later — strict-return requires that eventualparent[a:b:c] = new_view.
Operations that produce a fresh sliced ArrayValue (e.g.
:func:qamomile.circuit.frontend.operation.pauli_evolve.pauli_evolve,
:class:QKernel.__call__ for callees that return a sliced
array) cannot simply use the auto-returned new_view
because the new view they build wraps a different Value
than this consume’s return. Those op implementations call
:meth:_transfer_borrow_to after building their result so
the parent’s borrow table tracks the right handle.
Parameters:
| Name | Type | Description |
|---|---|---|
operation_name | str | Name of the operation consuming this view (used in error messages and for dispatch). |
Returns:
typing.Self — A fresh view handle with the same backing state; under
typing.Self — transfer the parent’s borrow table now points at this
typing.Self — handle, under release / destruction the parent’s record
typing.Self — for the covered slots is finalised.
Raises:
QubitConsumedError— If any covered slot was already destroyed by a prior destructive view consume on an overlapping view that has since gone out of scope.
qamomile.circuit.frontend.operation.qubit_gates¶
Overview¶
| Function | Description |
|---|---|
ccx | Toffoli (CCX) gate: flips target when both controls are |1>. |
cp | Controlled-Phase gate. |
cx | CNOT (Controlled-X) gate. |
cz | CZ (Controlled-Z) gate. |
get_current_tracer | |
h | Hadamard gate. |
p | Phase gate: P(theta)|1> = e^{i*theta}|1>. |
rx | Rotation around X-axis: RX(angle) = exp(-i * angle/2 * X). |
ry | Rotation around Y-axis: RY(angle) = exp(-i * angle/2 * Y). |
rz | Rotation around Z-axis: RZ(angle) = exp(-i * angle/2 * Z). |
rzz | RZZ gate: exp(-i * angle/2 * Z ⊗ Z). |
s | S gate (square root of Z). |
sdg | S-dagger gate (inverse of S gate). |
swap | SWAP gate: exchanges two qubits. |
t | T gate (fourth root of Z). |
tdg | T-dagger gate (inverse of T gate). |
x | Pauli-X gate (NOT gate). |
y | Pauli-Y gate. |
z | Pauli-Z gate. |
| Class | Description |
|---|---|
FloatType | Type representing a floating-point number. |
GateOperationType | |
IRGateOperation | Quantum gate operation. |
QubitAliasError | Same qubit used multiple times in one operation. |
Value | A typed SSA value in the IR. |
VectorClass | 1-dimensional array type. |
Functions¶
ccx [source]¶
def ccx(control1: Qubit, control2: Qubit, target: Qubit) -> tuple[Qubit, Qubit, Qubit]Toffoli (CCX) gate: flips target when both controls are |1>.
Parameters:
| Name | Type | Description |
|---|---|---|
control1 | Qubit | First control qubit. |
control2 | Qubit | Second control qubit. |
target | Qubit | Target qubit. |
Returns:
tuple[Qubit, Qubit, Qubit] — Tuple of (control1_out, control2_out, target_out) after CCX.
cp [source]¶
def cp(control: Qubit, target: Qubit, theta: float | Float) -> tuple[Qubit, Qubit]Controlled-Phase gate.
cx [source]¶
def cx(control: Qubit, target: Qubit) -> tuple[Qubit, Qubit]CNOT (Controlled-X) gate.
cz [source]¶
def cz(control: Qubit, target: Qubit) -> tuple[Qubit, Qubit]CZ (Controlled-Z) gate.
get_current_tracer [source]¶
def get_current_tracer() -> Tracerh [source]¶
def h(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]Hadamard gate.
Applied to a single Qubit it returns the transformed qubit. Applied
to a Vector[Qubit] it broadcasts the gate over every element via a
transpile-time loop, equivalent to
for i in qmc.range(n): qs[i] = h(qs[i]).
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit] to apply H to. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
p [source]¶
def p(
target: Union[Qubit, Vector[Qubit]],
theta: float | Float,
) -> Union[Qubit, Vector[Qubit]]Phase gate: P(theta)|1> = e^{i*theta}|1>.
Broadcasts the same theta over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit] to apply the phase to. |
theta | float | Float | Phase angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
rx [source]¶
def rx(
target: Union[Qubit, Vector[Qubit]],
angle: float | Float,
) -> Union[Qubit, Vector[Qubit]]Rotation around X-axis: RX(angle) = exp(-i * angle/2 * X).
Broadcasts the same angle over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
angle | float | Float | Rotation angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
ry [source]¶
def ry(
target: Union[Qubit, Vector[Qubit]],
angle: float | Float,
) -> Union[Qubit, Vector[Qubit]]Rotation around Y-axis: RY(angle) = exp(-i * angle/2 * Y).
Broadcasts the same angle over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
angle | float | Float | Rotation angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
rz [source]¶
def rz(
target: Union[Qubit, Vector[Qubit]],
angle: float | Float,
) -> Union[Qubit, Vector[Qubit]]Rotation around Z-axis: RZ(angle) = exp(-i * angle/2 * Z).
Broadcasts the same angle over every qubit when called with a
Vector[Qubit].
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
angle | float | Float | Rotation angle in radians. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
rzz [source]¶
def rzz(qubit_0: Qubit, qubit_1: Qubit, angle: float | Float) -> tuple[Qubit, Qubit]RZZ gate: exp(-i * angle/2 * Z ⊗ Z).
The RZZ gate applies a rotation around the ZZ axis on two qubits.
Parameters:
| Name | Type | Description |
|---|---|---|
qubit_0 | Qubit | First qubit. |
qubit_1 | Qubit | Second qubit. |
angle | float | Float | Rotation angle in radians. |
Returns:
tuple[Qubit, Qubit] — Tuple of (qubit_0_out, qubit_1_out) after RZZ.
s [source]¶
def s(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]S gate (square root of Z).
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
sdg [source]¶
def sdg(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]S-dagger gate (inverse of S gate).
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
swap [source]¶
def swap(qubit_0: Qubit, qubit_1: Qubit) -> tuple[Qubit, Qubit]SWAP gate: exchanges two qubits.
The SWAP gate swaps the states of two qubits.
Parameters:
| Name | Type | Description |
|---|---|---|
qubit_0 | Qubit | First qubit. |
qubit_1 | Qubit | Second qubit. |
Returns:
tuple[Qubit, Qubit] — Tuple of (qubit_0_out, qubit_1_out) after SWAP.
t [source]¶
def t(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]T gate (fourth root of Z).
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
tdg [source]¶
def tdg(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]T-dagger gate (inverse of T gate).
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
x [source]¶
def x(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]Pauli-X gate (NOT gate).
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
y [source]¶
def y(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]Pauli-Y gate.
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
z [source]¶
def z(target: Union[Qubit, Vector[Qubit]]) -> Union[Qubit, Vector[Qubit]]Pauli-Z gate.
Broadcasts over a Vector[Qubit] when applied to one.
Parameters:
| Name | Type | Description |
|---|---|---|
target | Union[Qubit, Vector[Qubit]] | A single Qubit or a Vector[Qubit]. |
Returns:
Union[Qubit, Vector[Qubit]] — A Qubit for scalar input, a Vector[Qubit] for array input.
Raises:
TypeError— Iftargetis neither aQubitnor aVector[Qubit].
Classes¶
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
GateOperationType [source]¶
class GateOperationType(enum.Enum)Attributes¶
CPCXCZHPRXRYRZRZZSSDGSWAPTTDGTOFFOLIXYZ
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.
QubitAliasError [source]¶
class QubitAliasError(AffineTypeError)Same qubit used multiple times in one operation.
Operations like cx() require distinct qubits for control and target. Using the same qubit in both positions is physically impossible and indicates a programming error.
Example of incorrect code:
q1, q2 = qm.cx(q, q) # ERROR: same qubit as control and target
Correct code:
q1, q2 = qm.cx(control, target) # Use distinct qubits
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
qamomile.circuit.frontend.param_validation¶
Validate bound qkernel argument handles against declared parameter types.
These predicates and validators classify a kernel parameter’s declared
annotation (scalar Qubit vs. Vector[Qubit], quantum vs. classical)
and check that the handle bound to it at a call site matches that
declaration. They are shared by the plain qkernel call path
(QKernel.__call__ in :mod:qamomile.circuit.frontend.qkernel) and the
controlled-gate call path (ControlledGate in
:mod:qamomile.circuit.frontend.operation.control). Keeping them in this
neutral module means neither of those modules has to import the other just
to reach these checks.
Overview¶
| Function | Description |
|---|---|
is_array_type | Check if type is a Vector, Matrix, or Tensor subclass. |
| Class | Description |
|---|---|
Bit | |
BitType | Type representing a classical bit. |
Float | Floating-point handle with arithmetic operations. |
FloatType | Type representing a floating-point number. |
ObservableType | Type representing a Hamiltonian observable parameter. |
Qubit | |
UInt | Unsigned integer handle with arithmetic operations. |
UIntType | Type representing an unsigned integer. |
Functions¶
is_array_type [source]¶
def is_array_type(t: Any) -> boolCheck if type is a Vector, Matrix, or Tensor subclass.
Classes¶
Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
BitType [source]¶
class BitType(ClassicalTypeMixin, ValueType)Type representing a classical bit.
Float [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FloatType [source]¶
class FloatType(ClassicalTypeMixin, ValueType)Type representing a floating-point number.
ObservableType [source]¶
class ObservableType(ObjectTypeMixin, ValueType)Type representing a Hamiltonian observable parameter.
This is a reference type - the actual qamomile.observable.Hamiltonian is provided via bindings during transpilation. It cannot be constructed or manipulated within qkernels.
Example usage:
import qamomile.circuit as qm
import qamomile.observable as qm_o
# Build Hamiltonian in Python
H = qm_o.Z(0) * qm_o.Z(1)
@qm.qkernel
def vqe(q: qm.Vector[qm.Qubit], H: qm.Observable) -> qm.Float:
return qm.expval(q, H)
# H is passed as binding
executable = transpiler.transpile(vqe, bindings={"H": H})Constructor¶
def __init__(self) -> NoneQubit [source]¶
class Qubit(Handle)Constructor¶
def __init__(
self,
value: Value[QubitType],
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
) -> NoneAttributes¶
value: Value[QubitType]
UInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
UIntType [source]¶
class UIntType(ClassicalTypeMixin, ValueType)Type representing an unsigned integer.
qamomile.circuit.frontend.qkernel¶
Overview¶
| Function | Description |
|---|---|
bit | Create a Bit handle from a boolean/int literal or declare a named Bit parameter. |
build_param_slots | Build a ParamSlot tuple for the classical arguments of a kernel. |
collect_quantum_rebind_violations | Analyze func for forbidden quantum rebind patterns. |
create_dummy_input | Create a dummy input based on parameter type annotation. |
float_ | Create a Float handle from a float literal or declare a named Float parameter. |
func_to_block | Convert a function to a hierarchical Block. |
get_current_tracer | |
handle_type_map | Map Handle type to ValueType. |
is_array_type | Check if type is a Vector, Matrix, or Tensor subclass. |
is_dict_type | Check if type is a Dict handle type. |
is_tuple_type | Check if type is a Tuple handle type. |
qkernel | Decorator to define a Qamomile quantum kernel. |
qubit_array | Create a new qubit array (vector/matrix/tensor) and emit QInitOperations. |
trace | Context manager to set the current tracer. |
transform_control_flow | |
uint | Create a UInt handle from an integer literal or a named parameter. |
| Class | Description |
|---|---|
ArrayBase | Base class for array types (Vector, Matrix, Tensor). |
ArrayValue | An array of typed IR values. |
Bit | |
Block | Unified block representation for all pipeline stages. |
BlockKind | Classification of block structure for pipeline stages. |
CallBlockOperation | |
Dict | Dict handle for qkernel functions. |
DictValue | A dictionary value stored as stable ordered entries. |
Float | Floating-point handle with arithmetic operations. |
FrontendTransformError | Error during frontend AST-to-builder lowering. |
QKernel | Decorator class for Qamomile quantum kernels. |
QubitRebindError | Quantum variable reassigned from a different quantum source. |
RebindSourceKind | Discriminator for the source of a detected rebind violation. |
RebindViolation | A detected forbidden quantum variable rebinding. |
ResourceEstimate | Comprehensive resource estimate for a quantum circuit. |
ReturnOperation | Explicit return operation marking the end of a block with return values. |
Tracer | |
UInt | Unsigned integer handle with arithmetic operations. |
Value | A typed SSA value in the IR. |
Vector | 1-dimensional array type. |
Functions¶
bit [source]¶
def bit(arg: bool | str | int) -> BitCreate a Bit handle from a boolean/int literal or declare a named Bit parameter.
build_param_slots [source]¶
def build_param_slots(
signature: inspect.Signature,
input_types: dict[str, Any],
*,
parameters: list[str] | None = None,
kwargs: dict[str, Any] | None = None,
qubit_sizes: dict[str, int] | None = None,
bind_defaults: bool,
) -> tuple[ParamSlot, ...]Build a ParamSlot tuple for the classical arguments of a kernel.
Mirrors the argument-classification logic in
QKernel._create_traced_block so the resulting slot list reflects
the same decisions that drive symbolic-vs-bound input creation. Only
classical (non-quantum, non-Tuple, non-Dict) arguments are included;
pure-quantum arguments live in Block.input_values instead.
Parameters:
| Name | Type | Description |
|---|---|---|
signature | inspect.Signature | The kernel function’s signature. |
input_types | dict[str, Any] | Resolved frontend type annotations keyed by argument name (typically QKernel.input_types or the equivalent computed in func_to_block). |
parameters | list[str] | None | Names explicitly requested as runtime parameters via parameters=[...]. None is treated as an empty list. |
kwargs | dict[str, Any] | None | Concrete values supplied via bindings / direct kwargs. None is treated as an empty dict. |
qubit_sizes | dict[str, int] | None | Optional mapping from Vector[Qubit] parameter names to their integer sizes; these are quantum inputs and are not included in the slot list. |
bind_defaults | bool | When True, Python signature defaults are treated as COMPILE_TIME_BOUND with bound_value=default. When False (e.g., the func_to_block path that does not bake in defaults), defaulted arguments stay RUNTIME_PARAMETER and the default appears only in ParamSlot.default. |
Returns:
tuple[ParamSlot, ...] — tuple[ParamSlot, ...]: One slot per classical argument, in the
order they appear in signature.parameters.
collect_quantum_rebind_violations [source]¶
def collect_quantum_rebind_violations(func: Callable, quantum_param_names: set[str]) -> list[RebindViolation]Analyze func for forbidden quantum rebind patterns.
Returns a (possibly empty) list of violations. Never raises on
analysis failure – returns [] instead.
create_dummy_input [source]¶
def create_dummy_input(
param_type: Any,
name: str = 'param',
emit_init: bool = True,
*,
shape: tuple[int, ...] | None = None,
) -> HandleCreate a dummy input based on parameter type annotation.
Parameters:
| Name | Type | Description |
|---|---|---|
param_type | Any | The type annotation for the parameter. |
name | str | Name for the value. |
emit_init | bool | If True, emit QInitOperation for qubit arrays (default: True). Set to False when creating a nested Block’s internal dummy inputs, or when the dummy will receive its qubits from a caller’s CallBlockOperation. |
shape | tuple[int, ...] | None | Optional concrete shape for array types. When provided, the dummy array’s shape Values carry compile-time constants instead of symbolic placeholders. Used by call-time sub-kernel specialization so that shape-dependent stdlib helpers (qft / iqft / qpe) resolve get_size to a concrete integer and emit the correct gate sequence. Ignored for non-array types. Default: None (symbolic shape). |
Returns:
Handle — A frontend Handle wrapping a dummy Value or ArrayValue
suitable for use as a function-parameter input during
tracing.
Raises:
TypeError— Ifparam_typeis not a supported parameter type, or if a Tuple/array annotation is missing its element type(s).
float_ [source]¶
def float_(arg: float | str) -> FloatCreate a Float handle from a float literal or declare a named Float parameter.
func_to_block [source]¶
def func_to_block(func: Callable) -> BlockConvert a function to a hierarchical Block.
Example:
def my_func(a: UInt, b: UInt) -> tuple[UInt]:
c = a + b
return (c, )
block = func_to_block(my_func)get_current_tracer [source]¶
def get_current_tracer() -> Tracerhandle_type_map [source]¶
def handle_type_map(handle_type: type[Handle] | type) -> ValueTypeMap Handle type to ValueType.
is_array_type [source]¶
def is_array_type(t: Any) -> boolCheck if type is a Vector, Matrix, or Tensor subclass.
is_dict_type [source]¶
def is_dict_type(t: Any) -> boolCheck if type is a Dict handle type.
is_tuple_type [source]¶
def is_tuple_type(t: Any) -> boolCheck if type is a Tuple handle type.
qkernel [source]¶
def qkernel(func: Callable[P, R]) -> QKernel[P, R]Decorator to define a Qamomile quantum kernel.
Parameters:
| Name | Type | Description |
|---|---|---|
func | Callable[P, R] | The function to decorate. |
Returns:
QKernel[P, R] — An instance of QKernel wrapping the function.
qubit_array [source]¶
def qubit_array(
shape: UInt | int | tuple[UInt | int, ...],
name: str,
) -> Vector[Qubit] | Matrix[Qubit] | Tensor[Qubit]Create a new qubit array (vector/matrix/tensor) and emit QInitOperations.
trace [source]¶
def trace(tracer: Tracer | None = None) -> Generator[Tracer, None, None]Context manager to set the current tracer.
transform_control_flow [source]¶
def transform_control_flow(func: Callable)uint [source]¶
def uint(arg: int | str) -> UIntCreate a UInt handle from an integer literal or a named parameter.
Parameters:
| Name | Type | Description |
|---|---|---|
arg | int | str | An integer literal to bake in as a compile-time constant, or a str naming a symbolic UInt parameter. A bool is rejected: True / False are not valid integer values here even though bool subclasses int. (Sign is not validated here -- a negative literal is accepted and baked in as-is.) |
Returns:
UInt — A constant-valued handle for an int argument, or a named
symbolic handle for a str argument.
Raises:
TypeError— Ifargis neither a plainintnor astr(in particular, if it is abool).
Classes¶
ArrayBase [source]¶
class ArrayBase(Handle, Generic[T])Base class for array types (Vector, Matrix, Tensor).
Provides common functionality for array indexing and element access.
Constructor¶
def __init__(
self,
value: ArrayValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt, ...] = tuple(),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
element_type: Type[T]shape: tuple[int | UInt, ...] Return the shape of the array.value: ArrayValue
Methods¶
consume¶
def consume(self, operation_name: str = 'unknown') -> SelfConsume the array, enforcing borrow-return contract for quantum arrays.
For quantum arrays, all borrowed elements must be returned before the array can be consumed. This ensures that no unreturned borrows are silently discarded by operations like qkernel calls or controlled gates.
When any slot of the array has already been physically consumed
by an earlier destructive view operation (measure(q[1::2])
then measure(q)), this raises QubitConsumedError rather
than silently re-consuming those slots.
create¶
@classmethod
def create(
cls,
shape: tuple[int | UInt, ...],
name: str,
el_type: Type[T],
) -> 'ArrayBase[T]'Create an ArrayValue for the given shape and name.
validate_all_returned¶
def validate_all_returned(self) -> NoneValidate all borrowed elements have been returned.
Strict-return policy: an active slice view that is still
registered as the owner of any parent slot is treated as an
unreturned borrow even if the view itself has no outstanding
element borrows. The caller must perform an explicit slice
assignment (parent[a:b:c] = view) to release the view’s
bulk-borrow before consuming the parent. Destructively
consumed views (parked in the dict with view._consumed set
and view._consumed_by classified as
:attr:ConsumeMode.DESTRUCTIVE) record physically-destroyed
slots and are not outstanding borrows; they survive
end-of-block so a later whole-array consume can detect and
reject the destroyed slots.
Raises:
UnreturnedBorrowError— If any elements are still borrowed, either directly or by a slice view that has not been explicitly returned via slice assignment.
ArrayValue [source]¶
class ArrayValue(Value[T])An array of typed IR values.
When slice_of is set, this array is a strided view over another
array. Element accesses on a sliced ArrayValue resolve to
physical slots on the root parent via the affine map
parent_index = slice_start + slice_step * view_local_index,
applied recursively along slice_of chains. The emit-time
resolver walks this chain to produce the final qubit index; passes
that substitute or clone values must treat slice_of /
slice_start / slice_step as Value references that need to
track through the same mapping as parent_array.
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(),
slice_of: 'ArrayValue | None' = None,
slice_start: 'Value | None' = None,
slice_step: 'Value | None' = None,
) -> NoneAttributes¶
logical_id: strmetadata: ValueMetadataname: strshape: tuple[Value, ...]slice_of: ‘ArrayValue | None’slice_start: ‘Value | None’slice_step: ‘Value | None’type: Tuuid: str
Methods¶
is_slice¶
def is_slice(self) -> boolReturn True if this array is a strided view of another array.
Returns:
bool — True iff slice_of is non-None.
next_version¶
def next_version(self) -> ArrayValue[T]Bit [source]¶
class Bit(Handle)Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: bool = False,
) -> NoneAttributes¶
init_value: bool
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(),
param_slots: tuple[ParamSlot, ...] = tuple(),
) -> NoneAttributes¶
input_values: list[Value]kind: BlockKindlabel_args: list[str]name: stroperations: list[‘Operation’]output_names: list[str]output_values: list[Value]param_slots: tuple[ParamSlot, ...]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.
BlockKind [source]¶
class BlockKind(Enum)Classification of block structure for pipeline stages.
Attributes¶
AFFINEANALYZEDHIERARCHICALTRACED
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).
Dict [source]¶
class Dict(Handle, Generic[K, V])Dict handle for qkernel functions.
Represents a dictionary mapping keys to values, commonly used for Ising coefficients like {(i, j): Jij}.
Example:
@qmc.qkernel
def ising_cost(
q: qmc.Vector[qmc.Qubit],
ising: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
gamma: qmc.Float,
) -> qmc.Vector[qmc.Qubit]:
for (i, j), Jij in qmc.items(ising):
q[i], q[j] = qmc.rzz(q[i], q[j], gamma * Jij)
return qConstructor¶
def __init__(
self,
value: DictValue,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_entries: list[tuple[Handle, Handle]] = list(),
_size: UInt | None = None,
_key_type: type | None = None,
) -> NoneAttributes¶
size: UInt Return the number of entries as a UInt handle.value: DictValue
Methods¶
items¶
def items(self) -> DictItemsIterator[K, V]Return an iterator over (key, value) pairs.
DictValue [source]¶
class DictValue(_MetadataValueMixin)A dictionary value stored as stable ordered entries.
Constructor¶
def __init__(
self,
name: str,
entries: tuple[tuple[TupleValue | Value, Value], ...] = tuple(),
metadata: ValueMetadata = ValueMetadata(),
uuid: str = (lambda: str(uuid.uuid4()))(),
logical_id: str = (lambda: str(uuid.uuid4()))(),
) -> NoneAttributes¶
entries: tuple[tuple[TupleValue | Value, Value], ...]logical_id: strmetadata: ValueMetadataname: strtype: DictTypeuuid: str
Methods¶
is_constant¶
def is_constant(self) -> boolnext_version¶
def next_version(self) -> DictValueFloat [source]¶
class Float(ArithmeticMixin, Handle)Floating-point handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: float = 0.0,
) -> NoneAttributes¶
init_value: float
FrontendTransformError [source]¶
class FrontendTransformError(QamomileCompileError)Error during frontend AST-to-builder lowering.
QKernel [source]¶
class QKernel(Generic[P, R])Decorator class for Qamomile quantum kernels.
Constructor¶
def __init__(self, func: Callable[P, R]) -> NoneAttributes¶
block: Block Compile the function to a hierarchical Block if not already compiled.funcinput_typesnameoutput_typesraw_funcsignature
Methods¶
build¶
def build(self, parameters: list[str] | None = None, **kwargs: Any = {}) -> BlockBuild a traced Block by tracing this kernel.
Parameters:
| Name | Type | Description |
|---|---|---|
parameters | list[str] | None | List of argument names to keep as unbound parameters. - None (default): Auto-detect parameters (non-Qubit args without value/default) - []: No parameters (all non-Qubit args must have value/default) - [“name”]: Explicit parameter list Only float, int, UInt, and their arrays are allowed as parameters. |
**kwargs | Any | Concrete values for non-parameter arguments. |
Returns:
Block — The traced block ready for transpilation, estimation,
or visualization.
Raises:
TypeError— If a non-parameterizable type is specified as parameter.ValueError— If required arguments are missing.
Example:
@qm.qkernel
def circuit(q: Qubit, theta: float) -> Qubit:
q = qm.rx(q, theta)
return q
# Auto-detect theta as parameter
block = circuit.build()
# Explicit parameter list
block = circuit.build(parameters=["theta"])
# theta bound to concrete value
block = circuit.build(theta=0.5)
# Transpile with binding
transpiler = QiskitTranspiler()
result = transpiler.emit(graph, binding={"theta": 0.5})draw¶
def draw(
self,
inline: bool = False,
fold_loops: bool = True,
expand_composite: bool = False,
inline_depth: int | None = None,
**kwargs: Any = {},
) -> AnyVisualize the circuit using Matplotlib.
This method builds the computation graph and creates a static visualization. Parameters are auto-detected: non-Qubit arguments without concrete values are shown as symbolic parameters.
Parameters:
| Name | Type | Description |
|---|---|---|
inline | bool | If True, expand CallBlockOperation contents (inlining). If False (default), show CallBlockOperation as boxes. |
fold_loops | bool | If True (default), display ForOperation as blocks instead of unrolling. If False, expand loops and show all iterations. |
expand_composite | bool | If True, expand CompositeGateOperation (QFT, IQFT, etc.). If False (default), show as boxes. Independent of inline. |
inline_depth | int | None | Maximum nesting depth for inline expansion. None means unlimited (default). 0 means no inlining, 1 means top-level only, etc. Only affects CallBlock/ControlledU, not CompositeGate. |
**kwargs | Any | Concrete values for arguments. Arguments not provided here (and without defaults) will be shown as symbolic parameters. |
Returns:
Any — matplotlib.figure.Figure object.
Raises:
ImportError— If matplotlib is not installed.
Example:
import qamomile.circuit as qm
@qm.qkernel
def inner(q: qm.Qubit) -> qm.Qubit:
return qm.x(q)
@qm.qkernel
def circuit(q: qm.Qubit, theta: float) -> qm.Qubit:
q = inner(q)
q = qm.h(q)
q = qm.rx(q, theta)
return q
# Draw with auto-detected symbolic parameter (theta)
fig = circuit.draw()
# Draw with bound parameter
fig = circuit.draw(theta=0.5)
# Draw with blocks as boxes (default)
fig = circuit.draw()
# Draw with blocks expanded (inlined)
fig = circuit.draw(inline=True)
# Draw with loops folded (shown as blocks)
fig = circuit.draw(fold_loops=True)
# Draw with composite gates expanded
fig = circuit.draw(expand_composite=True)estimate_resources¶
def estimate_resources(self, *, bindings: dict[str, Any] | None = None) -> ResourceEstimateEstimate all resources for this kernel’s circuit.
Convenience method that delegates to the module-level
estimate_resources function, eliminating the need to
access .block directly.
Parameters:
| Name | Type | Description |
|---|---|---|
bindings | dict[str, Any] | None | Optional concrete parameter bindings (scalars and dicts). Dict values trigger |key| cardinality substitution. |
Returns:
ResourceEstimate — ResourceEstimate with qubits, gates, and parameters.
Example:
>>> @qm.qkernel
... def bell() -> 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 = bell.estimate_resources()
>>> print(est.qubits) # 2QubitRebindError [source]¶
class QubitRebindError(AffineTypeError)Quantum variable reassigned from a different quantum source.
When a quantum variable is reassigned, the RHS must consume the same variable (self-update pattern). Reassigning from a different quantum variable would silently discard the original quantum state.
The check runs at qkernel decoration time as a static AST analysis
(see qamomile.circuit.frontend.ast_transform.collect_quantum_rebind_violations)
and raises immediately — the wrapped QKernel object is never
constructed when a violation is present. The check is run
unconditionally for every decorated kernel: kernel-level quantum
parameters (Qubit / Vector[Qubit]) seed origins from the
signature, and the analyzer’s recognition of internal quantum
constructors (qubit(...) / qubit_array(...)) seeds further
origins from inside the body so kernels that derive all of their
quantum state from internal allocations are also covered.
Branch-internal rebinds (assignments inside an if / for /
while body) are NOT flagged at decoration time: compile-time
conditional branches legitimately rebind quantum names (the
compile-time-if lowering pass selects one branch and discards the
other), and the single-pass AST analyzer cannot distinguish
compile-time from runtime branches. To keep those compile-time
patterns working, branch-internal violations are suppressed.
This is a known coverage gap. AffineValidationPass in the IR
layer only enforces “consumed at most once” and does NOT detect
“never consumed” / “silent discard” patterns, so a genuine runtime
if cond: q = qm.qubit("fresh") that discards a parameter is
currently not raised by either layer. A dedicated IR-level
silent-discard pass (or a flow-sensitive frontend analyzer) would
be needed to close it; this is tracked as follow-up. Top-level
(non-branch-internal) bypasses continue to raise at decoration
time.
Example of incorrect code:
a = qm.h(b) # ERROR: ‘a’ was quantum, now overwritten from ‘b’ a = b # ERROR: ‘a’ was quantum, now overwritten from ‘b’
Correct patterns:
a = qm.h(a) # Self-update (OK) new = qm.h(b) # New binding (OK, ‘new’ wasn’t quantum before)
RebindSourceKind [source]¶
class RebindSourceKind(enum.StrEnum)Discriminator for the source of a detected rebind violation.
Each value classifies why the analyzer believes an existing quantum binding is being silently discarded, and lets downstream error-message formatting render a domain-appropriate explanation instead of forcing a generic “different quantum variable” sentence onto, e.g., a fresh allocation.
Members:
DIRECT_ALIAS: q = other_q or q = qs[i].
QUANTUM_ARG: q = f(other_q, ...) where other_q has a
different origin than q.
FRESH_ALLOCATION: q = qm.qubit(...) /
qm.qubit_array(...) — the original quantum state is
silently discarded in favor of a freshly allocated one.
UNKNOWN_CALL: q = some_func(...) where the call references
no known quantum variable and is not a recognized quantum
constructor; conservatively treated as a rebind because
the original q is not threaded through the RHS.
CHAINED_ASSIGNMENT: q1 = q2 = expr where at least one
target is an existing quantum variable; chained binding
semantics are too ambiguous to verify self-update.
Attributes¶
CHAINED_ASSIGNMENTDIRECT_ALIASFRESH_ALLOCATIONQUANTUM_ARGUNKNOWN_CALL
RebindViolation [source]¶
class RebindViolationA detected forbidden quantum variable rebinding.
Constructor¶
def __init__(
self,
target_name: str,
source_name: str | None,
source_kind: RebindSourceKind,
func_name: str | None,
lineno: int,
source_expr: str | None = None,
) -> NoneAttributes¶
func_name: str | Nonelineno: intsource_expr: str | Nonesource_kind: RebindSourceKindsource_name: str | Nonetarget_name: str
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))ReturnOperation [source]¶
class ReturnOperation(Operation)Explicit return operation marking the end of a block with return values.
This operation represents an explicit return statement in the IR. It takes the values to be returned as operands and produces no results (it is a terminal operation that transfers control flow back to the caller).
operands: [Value, ...] - The values to return (may be empty for void returns) results: [] - Always empty (terminal operation)
Example:
A function that returns two values (a UInt and a Float):
ReturnOperation(
operands=[uint_value, float_value],
results=[],
)
The signature would be:
operands=[ParamHint("return_0", UIntType()), ParamHint("return_1", FloatType())]
results=[]Constructor¶
def __init__(self, operands: list[Value] = list(), results: list[Value] = list()) -> NoneAttributes¶
operation_kind: OperationKind Return CLASSICAL as this is a control flow operation without quantum effects.signature: Signature Return the signature with operands for each return value and no results.
Tracer [source]¶
class TracerConstructor¶
def __init__(self, _operations: list[Operation] = list()) -> NoneAttributes¶
operations: list[Operation]
Methods¶
add_operation¶
def add_operation(self, op) -> NoneUInt [source]¶
class UInt(ArithmeticMixin, Handle)Unsigned integer handle with arithmetic operations.
Constructor¶
def __init__(
self,
value: Value,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
init_value: int = 0,
) -> NoneAttributes¶
init_value: int
Value [source]¶
class Value(_MetadataValueMixin, Generic[T])A typed SSA value in the IR.
The name field is display-only: it labels the value for
visualization and error messages and has no role in identity. Identity
is carried by uuid (per-version) and logical_id (across
versions).
An empty string (name="") is the anonymous marker used by
auto-generated tmp values (arithmetic results, comparison results,
coerced constants). Name-based readers must guard with truthiness
(if value.name and value.name in bindings: ...) so anonymous values
never collide on a shared empty key. User-supplied parameter names and
array names continue to be non-empty.
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, ...] = (),
) -> NoneAttributes¶
element_indices: tuple[Value, ...]logical_id: strmetadata: ValueMetadataname: strparent_array: ArrayValue | Nonetype: Tuuid: strversion: int
Methods¶
is_array_element¶
def is_array_element(self) -> boolnext_version¶
def next_version(self) -> Value[T]Create a new Value with incremented version and fresh UUID.
Metadata is intentionally preserved across versions so that
parameter bindings and constant annotations remain accessible
after the value is updated (e.g. by a gate application or a
classical operation). The logical_id also stays the same:
it identifies the same logical variable across SSA versions,
independently of backend resource allocation. This applies to
every Value regardless of its type (Qubit, Float,
Bit, ...) -- it is not specific to qubits.
Vector [source]¶
class Vector(ArrayBase[T])1-dimensional array type.
Example:
import qamomile.circuit as qmc
# Create a vector of 3 qubits
qubits: qmc.Vector[qmc.Qubit] = qmc.qubit_array(3, name="qubits")
# Access elements
q0 = qubits[0]
q0 = qmc.h(q0)
qubits[0] = q0
# Apply H gate to all qubits (CORRECT)
n = qubits.shape[0]
for i in qmc.range(n):
qubits[i] = qmc.h(qubits[i])
# Slicing returns a VectorView over a subset of the parent vector.
# The view shares borrow tracking with the parent; element access
# on the view transparently indexes the parent.
evens = qubits[0::2]
for i in qmc.range(evens.shape[0]):
evens[i] = qmc.h(evens[i])Constructor¶
def __init__(
self,
value: ArrayValue = None,
parent: 'ArrayBase | None' = None,
indices: tuple['UInt', ...] = (),
name: str | None = None,
id: str = (lambda: str(uuid.uuid4()))(),
_consumed: bool = False,
_consumed_by: str | None = None,
_shape: tuple[int | UInt] = (0,),
_borrowed_indices: dict[tuple[str, ...], 'tuple[UInt, ...] | ArrayBase[T]'] = dict(),
) -> NoneAttributes¶
value: ArrayValue
qamomile.circuit.frontend.tracer¶
Overview¶
| Function | Description |
|---|---|
get_current_tracer | |
trace | Context manager to set the current tracer. |
Functions¶
get_current_tracer [source]¶
def get_current_tracer() -> Tracertrace [source]¶
def trace(tracer: Tracer | None = None) -> Generator[Tracer, None, None]Context manager to set the current tracer.
Classes¶
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.
Tracer [source]¶
class TracerConstructor¶
def __init__(self, _operations: list[Operation] = list()) -> NoneAttributes¶
operations: list[Operation]
Methods¶
add_operation¶
def add_operation(self, op) -> None