// ============================================================ // QuantumOps.td — Operation definitions for the #q quantum dialect // ============================================================ // Covers: alloc, unitary, entangle, measure, reset, concat, extract. // Linear-type discipline enforced via traits + verifier. #ifndef QUANTUM_OPS #define QUANTUM_OPS include "QuantumDialect.td" include "QuantumTypes.td" include "mlir/Interfaces/SideEffectInterfaces.td" // ============================================================ // Traits // ============================================================ // Enforce no-cloning: every !quantum.qubit SSA value must have // exactly one use (consumed by unitary, entangle, or measure). def Quantum_NoCloning : NativeOpTrait<"NoCloning"> { let cppNamespace = "::mlir::quantum"; } // ============================================================ // Allocation Operations // ============================================================ def Quantum_AllocOp : Quantum_Op<"alloc", [ MemoryEffects<[MemAlloc]>, DeclareOpInterfaceMethods, Quantum_NoCloning ]> { let summary = "Allocate a clean qubit or qureg"; let description = [{ Allocates a fresh qubit or register in the |0⟩ state. The result type determines the allocation: !quantum.qubit → single qubit !quantum.qureg → register of N qubits !quantum.qureg → dynamic-size register The allocated resource must be consumed by a unitary, entangle, or measure operation before the function returns. The verifier rejects dangling allocations (no-cloning trait). }]; let arguments = (ins Optional:$size); let results = (outs AnyTypeOf<[Quantum_QubitType, Quantum_QuregType]>:$result); let assemblyFormat = "($size^)? attr-dict `:` type($result)"; let hasVerifier = 1; } def Quantum_AllocWithStateOp : Quantum_Op<"alloc_with_state", [ MemoryEffects<[MemAlloc]>, Quantum_NoCloning ]> { let summary = "Allocate qubits with a specific initial state"; let description = [{ Allocates a qubit or register initialized to a user-specified state vector. The state must be normalized. This mirrors the CUDA-Q RAII allocation with initialisation: qubit q = cudaq::qrt::qubit_alloca(initialState); The verifier checks that the state length matches the allocation size (2^N for N qubits). }]; let arguments = (ins AnyTypeOf<[Quantum_QubitType, Quantum_QuregType]>:$qubits, Attribute:$state // DenseComplexFPElementsAttr ); let results = (outs AnyTypeOf<[Quantum_QubitType, Quantum_QuregType]>:$result); let assemblyFormat = [{ $qubits `with` $state attr-dict `:` type($result) }]; } // ============================================================ // Unitary Operations // ============================================================ def Quantum_UnitaryOp : Quantum_Op<"unitary", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Parametrised multi-axis rotation (exact algebraic angles)"; let description = [{ Applies a parametrised unitary to one or more qubits. The angles are stored as exact algebraic values (rational or symbolic), not floating-point approximations. This enables: - Exact Clifford+T synthesis - Symbolic gradient computation for variational algorithms - Noise-aware compilation with precision guarantees The axis parameter selects the rotation axis: "X" → R_x(θ) = exp(-iθ/2 · σ_x) "Y" → R_y(θ) = exp(-iθ/2 · σ_y) "Z" → R_z(θ) = exp(-iθ/2 · σ_z) "arbitrary" → arbitrary single-qubit unitary Examples: quantum.unitary %q [0.5] axis "Y" // H gate (θ=π/2) quantum.unitary %q [0.25] // T gate (θ=π/4) quantum.unitary %q [0.125, 0.5, 0.0] // U3 gate }]; let arguments = (ins Variadic:$qubits, ArrayAttr:$angles, // e.g. [89/2462, ...] OptionalAttr:$axis // "X","Y","Z","arbitrary" ); let results = (outs Variadic:$results); // linear consumption let assemblyFormat = [{ $qubits `(` $angles `)` (`axis` $axis^)? attr-dict `:` functional-type($qubits, $results) }]; let hasVerifier = 1; // enforce angle domain, no-cloning } // ============================================================ // Entangle Operations (controlled gates) // ============================================================ def Quantum_EntangleOp : Quantum_Op<"entangle", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Multi-qubit controlled operation (CNOT, Toffoli, CPhase, ...)"; let description = [{ Controlled operation acting on control and target qubits. This is the universal controlled gate. The base gate is determined by the number of targets and optional phases: 1 target, no phases → CNOT (X) or controlled-U 1 target, phase π → CZ (Z) 2 targets → Toffoli (CCX) or Fredkin (CSWAP) The adjoint flag negates all angles for parameterised gates and reverses the gate sequence for non-parameterised gates. Linear-type discipline: all input qubits are consumed and replaced by output qubits in the same positions. }]; let arguments = (ins Variadic:$controls, Variadic:$targets, OptionalAttr:$phases, // for controlled-phase UnitAttr:$is_adj ); let results = (outs Variadic:$out_controls, Variadic:$out_targets ); let assemblyFormat = [{ (`adj` $is_adj^)? `[` $controls `]` $targets (`phases` $phases^)? attr-dict `:` functional-type(operands, results) }]; let hasVerifier = 1; } // ============================================================ // Measurement Operations // ============================================================ def Quantum_MeasureOp : Quantum_Op<"measure", [ MemoryEffects<[MemRead, MemWrite]>, Quantum_NoCloning ]> { let summary = "Collapse amplitude vector into classical bits"; let description = [{ Measures the specified qubits in the computational (Z) basis. Returns: - A classical bit (i1) for each measured qubit - The post-measurement qubit state (consumed, cannot be reused without a fresh allocation) The optional registerName attaches metadata for classical control flow (e.g. "c" for the full register, "q0" for a single qubit). This mirrors the CUDA-Q QuakeToLLVM measurement pattern: %r = call %Result* @__quantum__qis__mz(%Qubit* %q) %bit = trunc %r to i1 }]; let arguments = (ins Variadic:$qubits, OptionalAttr:$registerName ); let results = (outs Variadic:$bits, // classical results Variadic:$collapsed // post-measurement state ); let assemblyFormat = [{ $qubits (`->` $registerName^)? attr-dict `:` functional-type($qubits, results) }]; let hasVerifier = 1; } // ============================================================ // Register Operations // ============================================================ def Quantum_ConcatOp : Quantum_Op<"concat", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Concatenate two quregs into one"; let arguments = (ins Quantum_QuregType:$left, Quantum_QuregType:$right ); let results = (outs Quantum_QuregType:$result); let assemblyFormat = [{ $left `,` $right attr-dict `:` type($result) }]; } def Quantum_ExtractRefOp : Quantum_Op<"extract_ref", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Extract a single qubit from a qureg by index"; let arguments = (ins Quantum_QuregType:$source, IntegerAttr:$index ); let results = (outs Quantum_QubitType:$result); let assemblyFormat = [{ $source `[` $index `]` attr-dict `:` type($result) }]; let hasVerifier = 1; // bounds check } def Quantum_SubveqOp : Quantum_Op<"subveq", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Extract a contiguous sub-register"; let arguments = (ins Quantum_QuregType:$source, IntegerAttr:$low, IntegerAttr:$high ); let results = (outs Quantum_QuregType:$result); let assemblyFormat = [{ $source `[` $low `:` $high `]` attr-dict `:` type($result) }]; let hasVerifier = 1; // bounds check, low < high } def Quantum_VeqSizeOp : Quantum_Op<"veq_size", [ Pure ]> { let summary = "Return the size of a qureg"; let arguments = (ins Quantum_QuregType:$source); let results = (outs I64:$size); let assemblyFormat = [{ $source attr-dict `:` type($size) }]; } // ============================================================ // Reset Operation // ============================================================ def Quantum_ResetOp : Quantum_Op<"reset", [ MemoryEffects<[MemWrite]>, Quantum_NoCloning ]> { let summary = "Reset qubit to |0⟩ without measurement"; let arguments = (ins Quantum_QubitType:$target); let results = (outs Quantum_QubitType:$result); let assemblyFormat = [{ $target attr-dict `:` type($result) }]; } // ============================================================ // Exp Pauli (exponentiation of Pauli string) // ============================================================ def Quantum_ExpPauliOp : Quantum_Op<"exp_pauli", [ NoMemoryEffect, Quantum_NoCloning ]> { let summary = "Exponentiation of a Pauli string: exp(-iθ/2 · P)"; let description = [{ Applies exp(-iθ/2 · P) where P is a tensor product of Pauli operators (X, Y, Z, I) on the specified qubits. This is the native gate for: - QAOA cost Hamiltonian evolution - Variational quantum eigensolver (VQE) ansatz - Suzuki-Trotter decomposition of molecular Hamiltonians The pauli string is encoded as a dense integer array: 0 = I, 1 = X, 2 = Y, 3 = Z Example: // e^{-iθ/2 · X⊗Z} on q0, q1 quantum.exp_pauli %q0, %q1 [1, 3] for θ = 0.5 }]; let arguments = (ins Variadic:$qubits, DenseI32ArrayAttr:$pauli, // Pauli string encoding AnyAttr:$theta // angle (rational or float) ); let results = (outs Variadic:$results); let assemblyFormat = [{ $qubits `(` $pauli `)` `for` $theta attr-dict `:` functional-type($qubits, $results) }]; let hasVerifier = 1; // pauli length == qubit count } #endif // QUANTUM_OPS