Skip to main content

cqlib_core/circuit/
error.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! # Circuit Error Types
14//!
15//! This module defines error types for quantum circuit operations.
16//! It provides comprehensive error handling for parameter evaluation,
17//! circuit validation, and symbolic computation errors.
18
19use symb_anafis::DiffError;
20use thiserror::Error;
21
22/// A unified error type for operations involving symbolic parameters.
23#[derive(Debug, Error)]
24pub enum ParameterError {
25    /// Wraps symbolic errors from the underlying math engine (parsing, syntax, diff, simplify).
26    #[error(transparent)]
27    SymbolicError(#[from] DiffError),
28
29    /// Indicates that a symbolic variable required for evaluation was missing.
30    #[error("Symbol not found: {0}")]
31    UndefinedSymbol(String),
32
33    /// Indicates an arithmetic error where a division or modulo operation by zero occurred.
34    #[error("Division by zero")]
35    DivisionByZero,
36
37    /// Indicates that a mathematical function was called with an argument outside its defined domain.
38    #[error("Domain error: {0}")]
39    DomainError(String),
40
41    /// Indicates that a calculation resulted in NaN (Not a Number).
42    #[error("Calculation resulted in NaN: {0}")]
43    NaN(String),
44
45    /// Indicates an error occurred during parsing.
46    #[error("Parse error: {0}")]
47    ParseError(String),
48}
49
50/// Errors that can occur during the numerical evaluation of symbolic parameters.
51///
52/// This enum captures failure modes when resolving a symbolic [`Parameter`](crate::circuit::Parameter)
53/// expression into a concrete floating-point value. These errors typically
54/// arise while binding a parameterized quantum circuit.
55#[derive(Debug, Error)]
56pub enum EvalError {
57    /// Indicates that a symbolic variable required for evaluation was missing from the provided bindings.
58    ///
59    /// # Example
60    /// evaluating `theta + phi` with bindings `{ "theta": 1.0 }` will raise `UndefinedSymbol("phi")`.
61    #[error("Symbol not found: {0}")]
62    UndefinedSymbol(String),
63
64    /// Indicates an arithmetic error where a division or modulo operation by zero occurred.
65    ///
66    /// This includes both explicit division by zero (e.g., `x / 0.0`) and runtime zeros (e.g., `x / y` where `y = 0.0`).
67    #[error("Division by zero")]
68    DivisionByZero,
69
70    /// Indicates that a mathematical function was called with an argument outside its defined domain.
71    ///
72    /// Common causes include:
73    /// - `sqrt(x)` where `x < 0`
74    /// - `ln(x)` where `x <= 0`
75    /// - `asin(x)` or `acos(x)` where `|x| > 1`
76    #[error("Domain error: {0}")]
77    DomainError(String),
78
79    /// Indicates that an intermediate or final calculation resulted in `NaN` (Not a Number).
80    ///
81    /// This serves as a catch-all for undefined numerical behaviors not covered by other variants.
82    #[error("Calculation resulted in NaN: {0}")]
83    NaN(String),
84
85    /// Indicates that the expression tree exceeded the maximum allowed recursion depth during evaluation.
86    ///
87    /// This typically occurs with deeply nested expressions (e.g., `((((x+1)+1)+1)...)`).
88    /// Consider simplifying the expression or increasing the depth limit.
89    #[error("Maximum recursion depth exceeded during evaluation (depth limit: {0})")]
90    MaxRecursionDepthExceeded(usize),
91}
92
93/// Errors that can occur during symbolic differentiation.
94#[derive(Debug, Error)]
95pub enum DerivativeError {
96    /// Indicates that an expression is not differentiable with respect to a given variable.
97    #[error("Cannot differentiate expression: {0}")]
98    NonDifferentiable(String),
99}
100
101/// A comprehensive error type for operations involving Quantum Circuits.
102///
103/// This enum aggregates errors from circuit construction, validation, and manipulation.
104/// It serves as the primary error type for the `Circuit` struct and its related methods.
105#[derive(Debug, Error)]
106pub enum CircuitError {
107    /// Thrown when attempting to add qubits that share the same unique identifier (index).
108    ///
109    /// Qubit indices within a circuit must be unique to ensure unambiguous addressing.
110    #[error("Duplicate qubits found in circuit definition")]
111    DuplicateQubits,
112
113    /// Thrown when an operation references a qubit that is not part of the circuit.
114    ///
115    /// All qubits used in operations (gates, measurements) must be explicitly added to the circuit
116    /// via `add_qubits` or `new` before use.
117    #[error("Qubit {0} not found in circuit")]
118    QubitNotFound(u32),
119
120    /// Thrown when the number of qubits provided for an operation does not match its definition.
121    ///
122    /// For example, applying a 2-qubit CNOT gate to 3 qubits, or a 1-qubit Custom Unitary to 2 qubits.
123    #[error("Qubit count mismatch: expected {expected}, got {actual}")]
124    QubitCountMismatch {
125        /// Required qubit count.
126        expected: usize,
127        /// Supplied qubit count.
128        actual: usize,
129    },
130
131    /// Thrown when the number of parameters supplied to a fixed-arity
132    /// instruction does not match its definition.
133    #[error("Parameter count mismatch: expected {expected}, got {actual}")]
134    ParameterCountMismatch {
135        /// Required parameter count.
136        expected: usize,
137        /// Supplied parameter count.
138        actual: usize,
139    },
140
141    /// Wraps errors raised while simplifying or evaluating symbolic parameters.
142    #[error("Invalid parameter: {0}")]
143    InvalidParameter(#[from] ParameterError),
144
145    /// Thrown when an operation is requested to provide a unitary matrix, but none exists.
146    ///
147    /// This typically happens when calling `.matrix()` on non-unitary instructions such as:
148    /// - `Measure`
149    /// - `Barrier`
150    /// - `Reset`
151    #[error("Instruction has no matrix representation")]
152    NoMatrixRepresentation,
153
154    /// Wraps errors that occur during the evaluation of circuit parameters.
155    ///
156    /// This variant propagates [`EvalError`]s up the stack when operations like matrix generation
157    /// or gate inversion fail due to invalid parameter bindings.
158    #[error("Failed to evaluate parameters: {0}")]
159    ParamEvaluationFailed(#[from] EvalError),
160
161    /// Thrown when inversion is requested for a non-unitary operation or circuit.
162    #[error("Operation is irreversible")]
163    IrreversibleOperation,
164
165    /// Thrown when an instruction cannot be promoted to a controlled operation.
166    #[error("Operation cannot be controlled: {0}")]
167    InvalidControlOperation(String),
168
169    /// Thrown when a numeric result is requested from an operation whose
170    /// circuit-local symbolic parameters have not been resolved.
171    #[error("Symbolic parameter cannot be evaluated in this context")]
172    SymbolicParameterError,
173
174    /// Thrown when a parameter cannot be resolved during circuit decomposition.
175    ///
176    /// This occurs when a sub-circuit or control flow body contains symbolic parameters
177    /// that are not bound to concrete values and cannot be evaluated.
178    #[error("Unresolved parameter in decomposition: {0}")]
179    UnresolvedParameter(String),
180
181    /// Thrown when a parameter index is out of range.
182    ///
183    /// This indicates internal data corruption or an inconsistency between the
184    /// circuit's parameter table and the indices stored in operations.
185    #[error("Parameter index {0} out of range")]
186    InvalidParameterIndex(u32),
187
188    /// Thrown when an operation index is outside the circuit operation list.
189    ///
190    /// `index` is the requested zero-based index and `len` is the operation
191    /// count at the time of the request.
192    #[error("Operation index {index} out of bounds for circuit with {len} operations")]
193    OperationIndexOutOfBounds {
194        /// Requested zero-based operation index.
195        index: usize,
196        /// Number of operations in the circuit.
197        len: usize,
198    },
199
200    /// Thrown when deleting an operation would remove a classical value that
201    /// is still read by another operation.
202    #[error("classical value {index} is still used at {context}")]
203    ClassicalValueStillInUse {
204        /// Circuit-local value index.
205        index: u32,
206        /// Description of the remaining use site.
207        context: String,
208    },
209
210    /// Thrown when a gate parameter has an invalid value (NaN or Infinity).
211    ///
212    /// This occurs when appending or evaluating a gate with a non-finite fixed
213    /// parameter. The tuple stores the parameter position and rejected value.
214    #[error("Invalid parameter value at index {0}: {1}")]
215    InvalidParameterValue(usize, f64),
216
217    /// Thrown when the control flow graph (CFG) has an invalid or inconsistent structure.
218    ///
219    /// This error indicates that the DAG representation of the circuit is malformed,
220    /// which can occur when:
221    /// - A Branch terminator lacks required TrueBranch or FalseBranch edges
222    /// - A Jump terminator references a non-existent block
223    /// - The entry block is not set
224    /// - Edges reference nodes that don't exist in the graph
225    ///
226    /// # Examples
227    ///
228    /// This error is returned when converting an invalid `CircuitDag` back to a `Circuit`
229    /// if the DAG structure was manually modified and left in an inconsistent state.
230    #[error("Invalid control flow graph structure: {0}")]
231    InvalidControlFlow(String),
232
233    /// Thrown when the circuit dependency DAG has an invalid or inconsistent structure.
234    #[error("Invalid circuit DAG structure: {0}")]
235    InvalidDag(String),
236
237    /// Thrown when a classical handle was created by a different circuit.
238    #[error("{kind} {index} belongs to another circuit")]
239    ForeignClassicalHandle {
240        /// Human-readable handle kind.
241        kind: &'static str,
242        /// Circuit-local handle index.
243        index: u32,
244    },
245
246    /// Thrown when a classical value is read before it is available at a use site.
247    #[error("classical value {index} is not available at {context}")]
248    UndefinedClassicalValue {
249        /// Circuit-local value index.
250        index: u32,
251        /// Description of the invalid use site.
252        context: String,
253    },
254
255    /// Thrown when validation finds multiple producers for one immutable value.
256    #[error("classical value {index} has more than one definition: {first} and {second}")]
257    DuplicateClassicalValueDefinition {
258        /// Circuit-local value index.
259        index: u32,
260        /// Description of the first definition site.
261        first: String,
262        /// Description of the conflicting definition site.
263        second: String,
264    },
265
266    /// Thrown when a classical value is used outside its defining control-flow region.
267    #[error("classical value {index} escapes its defining control-flow region at {context}")]
268    ClassicalValueOutOfScope {
269        /// Circuit-local value index.
270        index: u32,
271        /// Description of the invalid use site.
272        context: String,
273    },
274
275    /// Thrown when `break` or `continue` is followed by another operation in the same body.
276    #[error("{operation} must be the final operation in {context}")]
277    NonTerminalControlTransfer {
278        /// Control-transfer operation name.
279        operation: &'static str,
280        /// Description of the containing body.
281        context: String,
282    },
283
284    /// Thrown when `Circuit::depth(recurse=false)` is called on a circuit that
285    /// contains any classical control-flow operation (`if`/`while`/`for`/
286    /// `switch`/`break`/`continue`) at any nesting depth. Pass `recurse = true`
287    /// to obtain an estimated depth that unfolds control flow.
288    #[error("circuit contains control-flow operations; pass recurse=true for an estimated depth")]
289    ControlFlowPresent,
290
291    /// Catch-all for an invalid operation that has no more specific error variant.
292    #[error("Invalid Operation: {0}")]
293    InvalidOperation(String),
294}
295
296/// Legacy compilation error placeholder.
297#[derive(Debug, Error)]
298pub enum CompileError {
299    /// Unclassified compilation failure.
300    #[error("error")]
301    Error,
302}