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