cqlib_core/qis/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
13use crate::circuit::CircuitError;
14use num_complex::Complex64;
15use thiserror::Error;
16
17/// Comprehensive error type for the `qis` (Quantum Information Science) module.
18///
19/// This enum captures errors that can occur during statevector simulation,
20/// density matrix simulation, observables, Hamiltonians, and Pauli operator manipulations.
21#[derive(Debug, Error)]
22pub enum QisError {
23 /// A fermionic mode lies outside the declared mode space.
24 #[error("Fermionic mode {mode} is out of bounds for {num_modes} modes")]
25 ModeOutOfBounds { mode: usize, num_modes: usize },
26
27 /// Fermionic operators must declare the same mode count.
28 #[error("Mode count mismatch: expected {expected}, got {actual}")]
29 ModeMismatch { expected: usize, actual: usize },
30
31 /// Operator coefficients and arithmetic results must be finite.
32 #[error("Operator coefficient must be finite, got {coefficient}")]
33 NonFiniteCoefficient { coefficient: Complex64 },
34
35 /// A fermionic occupation must be either zero or one.
36 #[error("Invalid occupation at mode {index}: expected 0 or 1, got {value}")]
37 InvalidOccupation { index: usize, value: u8 },
38 /// Errors originating from quantum circuit operations, such as unsupported gates
39 /// or symbolic parameters.
40 #[error(transparent)]
41 CircuitError(#[from] CircuitError),
42
43 /// Thrown when the number of qubits of an operator (e.g., Hamiltonian) does not match
44 /// the number of qubits of the quantum state being measured.
45 #[error("Qubit count mismatch: expected {expected}, got {actual}")]
46 QubitMismatch { expected: usize, actual: usize },
47
48 /// Thrown when the dimensions of matrices or vectors do not match expectations
49 /// (e.g., when multiplying matrices of incompatible shapes).
50 #[error("Dimension mismatch: expected {expected}, got {actual}")]
51 DimensionMismatch { expected: usize, actual: usize },
52
53 /// Thrown when a state vector or density matrix has an invalid length
54 /// (e.g., its length is not a power of 2).
55 #[error("Invalid state dimension: length {0} is not a power of 2")]
56 InvalidStateDimension(usize),
57
58 /// Thrown when a probability value is mathematically invalid (e.g., < 0 or > 1).
59 #[error("Invalid probability value: {0}")]
60 InvalidProbability(f64),
61
62 /// Thrown when attempting to access a qubit or amplitude index that is out of bounds.
63 #[error("Index out of bounds: index {index}, max {max}")]
64 IndexOutOfBounds { index: usize, max: usize },
65
66 /// Thrown when an operation is requested but not supported by the simulation backend.
67 #[error("Unsupported simulation operation: {0}")]
68 UnsupportedOperation(String),
69
70 /// Thrown when parsing a Pauli string fails.
71 #[error(transparent)]
72 PauliStringParseError(#[from] PauliStringParseError),
73
74 /// Thrown when an explicitly provided quantum state is not normalized
75 /// (i.e., trace or norm is not 1).
76 #[error("State is not normalized")]
77 NotNormalized,
78
79 /// Thrown when an operator is expected to be Hermitian but is not
80 /// (e.g., a non-Hermitian Hamiltonian or density matrix).
81 #[error("Operator is not Hermitian")]
82 NotHermitian,
83
84 /// Thrown when a density matrix is expected to be positive semidefinite but is not.
85 #[error("Density matrix is not positive semidefinite")]
86 NotPositiveSemidefinite,
87
88 /// Thrown when a parameter value is invalid.
89 #[error("Invalid parameter value: {0}")]
90 InvalidParameterValue(String),
91
92 /// Thrown when a subsystem specification is invalid for entanglement calculations.
93 #[error("Invalid subsystem: {0}")]
94 InvalidSubsystem(String),
95
96 /// Thrown when an operation requires a specific dimension that is not met.
97 #[error("Unsupported dimension: expected {expected}, got {actual}")]
98 UnsupportedDimension { expected: usize, actual: usize },
99
100 /// Thrown when a gate is applied to a [`StabilizerState`] but is not a Clifford gate
101 /// (i.e., it does not map the Pauli group to itself under conjugation).
102 /// Examples: T, T†, Rx(θ) for generic θ.
103 ///
104 /// Use [`crate::qis::Statevector`] for universal (non-Clifford) simulation.
105 #[error("Non-Clifford gate: {0}")]
106 NonCliffordGate(String),
107}
108
109pub(crate) fn validate_coefficient(coefficient: Complex64) -> Result<(), QisError> {
110 if coefficient.re.is_finite() && coefficient.im.is_finite() {
111 Ok(())
112 } else {
113 Err(QisError::NonFiniteCoefficient { coefficient })
114 }
115}
116
117/// Error type for parsing PauliString from a string representation.
118#[derive(Debug, Clone, PartialEq, Eq, Error)]
119pub enum PauliStringParseError {
120 /// The string provided for parsing is empty.
121 #[error("empty string")]
122 EmptyString,
123
124 /// The string contains an invalid or unrecognized character.
125 #[error("invalid character '{0}'")]
126 InvalidCharacter(char),
127
128 /// The string only contains a phase but lacks any Pauli operators.
129 #[error("no Pauli operators specified")]
130 NoOperators,
131}