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}