Skip to main content

cqlib_core/device/
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//! # Device Error Types
14//!
15//! This module defines error types for quantum device operations.
16//! It provides error handling for device topology validation,
17//! layout mapping, and qubit connectivity issues.
18
19use crate::device::{LogicalQubit, PhysicalQubit};
20use std::fmt;
21use thiserror::Error;
22
23#[derive(Error, Debug, Clone, PartialEq, Eq)]
24pub enum DeviceError {
25    /// The device must contain at least one physical qubit.
26    EmptyDevice,
27    InvalidOnlineQubit(PhysicalQubit),
28    QubitNotInDevice(PhysicalQubit),
29    QubitNotInTopology(PhysicalQubit),
30    EdgeNotInTopology(PhysicalQubit, PhysicalQubit),
31    InvalidTopology(TopologyError),
32    /// A non-standard instruction was configured as an atomic native capability.
33    NonStandardNativeInstruction {
34        /// Human-readable instruction identity.
35        instruction: String,
36    },
37    /// A standard gate's arity cannot be represented at the target location.
38    InvalidNativeInstructionArity {
39        /// Human-readable instruction identity.
40        instruction: String,
41        /// Inclusive arity range accepted by the writer.
42        expected: std::ops::RangeInclusive<usize>,
43        /// Number of qubits used by the standard gate.
44        actual: usize,
45    },
46    /// A native instruction error rate is not a finite probability.
47    InvalidNativeInstructionErrorRate {
48        /// Human-readable instruction identity.
49        instruction: String,
50        /// Stable textual representation of the rejected value.
51        value: String,
52    },
53    /// A native instruction duration is negative or non-finite.
54    InvalidNativeInstructionDuration {
55        /// Human-readable instruction identity.
56        instruction: String,
57        /// Stable textual representation of the rejected value.
58        value: String,
59    },
60}
61
62/// Errors raised when a physical circuit does not satisfy device capabilities.
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub enum DeviceValidationError {
65    /// An operation references a physical qubit that is absent or offline.
66    UnusablePhysicalQubit {
67        /// Device name used for diagnostics.
68        device: String,
69        /// Physical qubit that is absent or offline.
70        qubit: PhysicalQubit,
71    },
72    /// A two-qubit instruction has no coupling in the requested direction.
73    MissingDirectedCoupling {
74        /// Device name used for diagnostics.
75        device: String,
76        /// Human-readable instruction identity.
77        instruction: String,
78        /// Requested control-side physical qubit.
79        control: PhysicalQubit,
80        /// Requested target-side physical qubit.
81        target: PhysicalQubit,
82    },
83    /// The device does not support an instruction on the exact ordered qargs.
84    UnsupportedInstruction {
85        /// Device name used for diagnostics.
86        device: String,
87        /// Human-readable instruction identity.
88        instruction: String,
89        /// Ordered physical arguments requested by the operation.
90        qargs: Vec<PhysicalQubit>,
91    },
92    /// A gate-like instruction remains above the device-native abstraction level.
93    UndecomposedInstruction {
94        /// Device name used for diagnostics.
95        device: String,
96        /// Human-readable instruction identity.
97        instruction: String,
98        /// Ordered physical arguments requested by the operation.
99        qargs: Vec<PhysicalQubit>,
100    },
101}
102
103impl fmt::Display for DeviceValidationError {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        match self {
106            Self::UnusablePhysicalQubit { device, qubit } => write!(
107                f,
108                "device '{device}' cannot execute an operation on unusable physical qubit {qubit}"
109            ),
110            Self::UnsupportedInstruction {
111                device,
112                instruction,
113                qargs,
114            } => {
115                let qargs = qargs
116                    .iter()
117                    .map(ToString::to_string)
118                    .collect::<Vec<_>>()
119                    .join(", ");
120                write!(
121                    f,
122                    "device '{device}' does not support instruction {instruction} on ordered physical qargs [{qargs}]"
123                )
124            }
125            Self::MissingDirectedCoupling {
126                device,
127                instruction,
128                control,
129                target,
130            } => write!(
131                f,
132                "device '{device}' has no directed coupling {control} -> {target} required by instruction {instruction}"
133            ),
134            Self::UndecomposedInstruction {
135                device,
136                instruction,
137                qargs,
138            } => {
139                let qargs = qargs
140                    .iter()
141                    .map(ToString::to_string)
142                    .collect::<Vec<_>>()
143                    .join(", ");
144                write!(
145                    f,
146                    "device '{device}' cannot validate undecomposed instruction {instruction} on ordered physical qargs [{qargs}]"
147                )
148            }
149        }
150    }
151}
152
153impl std::error::Error for DeviceValidationError {}
154
155impl fmt::Display for DeviceError {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        match self {
158            Self::EmptyDevice => {
159                write!(f, "Device must contain at least one physical qubit")
160            }
161            Self::InvalidOnlineQubit(q) => {
162                write!(
163                    f,
164                    "Specified online qubit {} does not exist in the device topology",
165                    q
166                )
167            }
168            Self::QubitNotInDevice(q) => {
169                write!(f, "Qubit {} is not registered with the device", q)
170            }
171            Self::QubitNotInTopology(q) => {
172                write!(f, "Qubit {} is not in the device topology", q)
173            }
174            Self::EdgeNotInTopology(control, target) => {
175                write!(
176                    f,
177                    "Edge ({}, {}) is not in the device topology",
178                    control, target
179                )
180            }
181            Self::InvalidTopology(error) => {
182                write!(f, "Invalid device topology: {error}")
183            }
184            Self::NonStandardNativeInstruction { instruction } => write!(
185                f,
186                "instruction {instruction} cannot be stored as a native capability: native instructions must be standard gates"
187            ),
188            Self::InvalidNativeInstructionArity {
189                instruction,
190                expected,
191                actual,
192            } => {
193                let expected = if expected.start() == expected.end() {
194                    expected.start().to_string()
195                } else {
196                    format!("{}..={}", expected.start(), expected.end())
197                };
198                write!(
199                    f,
200                    "instruction {instruction} cannot be stored as a native capability: expected {expected} qubits, got {actual}"
201                )
202            }
203            Self::InvalidNativeInstructionErrorRate { instruction, value } => write!(
204                f,
205                "instruction {instruction} has invalid native error rate {value}: expected a finite value in [0, 1]"
206            ),
207            Self::InvalidNativeInstructionDuration { instruction, value } => write!(
208                f,
209                "instruction {instruction} has invalid native duration {value}: expected a finite non-negative value"
210            ),
211        }
212    }
213}
214
215/// Errors that can occur when creating or operating on a [`crate::device::Layout`].
216#[derive(Error, Debug, Clone, PartialEq, Eq)]
217pub enum LayoutError {
218    /// The number of logical qubits exceeds the number of physical qubits.
219    TooManyLogicalQubits { logical: usize, physical: usize },
220    /// A logical qubit appears more than once in the logical qubit list.
221    DuplicateLogicalQubit(LogicalQubit),
222    /// A physical qubit appears more than once in the physical qubit list.
223    DuplicatePhysicalQubit(PhysicalQubit),
224    /// A logical qubit in `init_map` is not present in the logical qubit list.
225    InvalidLogicalQubit(LogicalQubit),
226    /// A physical qubit is not available to the layout.
227    InvalidPhysicalQubit(PhysicalQubit),
228    /// Tried to bind a logical qubit that is already mapped.
229    LogicalQubitAlreadyBound(LogicalQubit),
230    /// Tried to bind a physical qubit that already carries a logical qubit.
231    PhysicalQubitAlreadyOccupied(PhysicalQubit),
232    /// Tried to unbind a logical qubit that is not mapped.
233    LogicalQubitNotBound(LogicalQubit),
234}
235
236impl fmt::Display for LayoutError {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        match self {
239            Self::TooManyLogicalQubits { logical, physical } => {
240                write!(
241                    f,
242                    "Logical qubits ({}) exceed physical qubits ({})",
243                    logical, physical
244                )
245            }
246            Self::DuplicateLogicalQubit(q) => {
247                write!(f, "Logical qubit {} appears more than once", q)
248            }
249            Self::DuplicatePhysicalQubit(q) => {
250                write!(f, "Physical qubit {} appears more than once", q)
251            }
252            Self::InvalidLogicalQubit(q) => {
253                write!(f, "Logical qubit {} not in logical qubit list", q)
254            }
255            Self::InvalidPhysicalQubit(q) => {
256                write!(f, "Physical qubit {} not in physical qubit list", q)
257            }
258            Self::LogicalQubitAlreadyBound(q) => {
259                write!(f, "Logical qubit {} is already bound", q)
260            }
261            Self::PhysicalQubitAlreadyOccupied(q) => {
262                write!(f, "Physical qubit {} is already occupied", q)
263            }
264            Self::LogicalQubitNotBound(q) => write!(f, "Logical qubit {} is not bound", q),
265        }
266    }
267}
268
269/// Errors that can occur when operating on a Topology.
270#[derive(Error, Debug, Clone, PartialEq, Eq)]
271pub enum TopologyError {
272    /// Tried to operate on a qubit that doesn't exist in the topology.
273    QubitNotFound(PhysicalQubit),
274    /// Tried to operate on a coupling edge that doesn't exist.
275    CouplingNotFound {
276        control: PhysicalQubit,
277        target: PhysicalQubit,
278    },
279    /// Tried to add a duplicate qubit.
280    QubitAlreadyExists(PhysicalQubit),
281    /// Tried to add a duplicate coupling.
282    CouplingAlreadyExists {
283        control: PhysicalQubit,
284        target: PhysicalQubit,
285    },
286    /// Tried to add a coupling from a qubit to itself.
287    SelfCoupling { qubit: PhysicalQubit },
288    /// Tried to remove the same qubit more than once in one operation.
289    DuplicateQubitRemoval(PhysicalQubit),
290    /// Tried to remove the same coupling more than once in one operation.
291    DuplicateCouplingRemoval {
292        control: PhysicalQubit,
293        target: PhysicalQubit,
294    },
295}
296
297impl fmt::Display for TopologyError {
298    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299        match self {
300            TopologyError::QubitNotFound(q) => write!(f, "Qubit {:?} not found", q),
301            TopologyError::CouplingNotFound { control, target } => {
302                write!(f, "Coupling ({:?} -> {:?}) not found", control, target)
303            }
304            TopologyError::QubitAlreadyExists(q) => write!(f, "Qubit {:?} already exists", q),
305            TopologyError::CouplingAlreadyExists { control, target } => {
306                write!(f, "Coupling ({:?} -> {:?}) already exists", control, target)
307            }
308            TopologyError::SelfCoupling { qubit } => {
309                write!(
310                    f,
311                    "Self coupling ({:?} -> {:?}) is not allowed",
312                    qubit, qubit
313                )
314            }
315            TopologyError::DuplicateQubitRemoval(q) => {
316                write!(f, "Qubit {:?} appears more than once in removal request", q)
317            }
318            TopologyError::DuplicateCouplingRemoval { control, target } => {
319                write!(
320                    f,
321                    "Coupling ({:?} -> {:?}) appears more than once in removal request",
322                    control, target
323                )
324            }
325        }
326    }
327}