Skip to main content

prism_q/
error.rs

1//! Error types for PRISM-Q.
2//!
3//! Invalid input returns [`PrismError`]. API misuse (out-of-bounds indices,
4//! wrong-variant accessors) panics, and each such method says so under `# Panics`.
5
6use thiserror::Error;
7
8/// Top-level error type for PRISM-Q operations.
9#[derive(Debug, Error, Clone, PartialEq)]
10#[non_exhaustive]
11pub enum PrismError {
12    /// OpenQASM parse error with source line number.
13    #[error("parse error at line {line}: {message}")]
14    Parse { line: usize, message: String },
15
16    /// Valid OpenQASM that PRISM-Q does not support. A loop keyword names the
17    /// entry point that reads it.
18    #[error("unsupported construct at line {line}: `{construct}`{}", loop_hint(.construct))]
19    UnsupportedConstruct { construct: String, line: usize },
20
21    /// Qubit index exceeds register size.
22    #[error("invalid qubit index {index} (register size: {register_size})")]
23    InvalidQubit { index: usize, register_size: usize },
24
25    /// Classical bit index exceeds register size.
26    #[error("invalid classical bit index {index} (register size: {register_size})")]
27    InvalidClassicalBit { index: usize, register_size: usize },
28
29    /// Gate applied to wrong number of qubits.
30    #[error("gate `{gate}`: expected {expected} qubit(s), got {got}")]
31    GateArity {
32        gate: String,
33        expected: usize,
34        got: usize,
35    },
36
37    /// Backend does not support the requested operation.
38    #[error("backend `{backend}` does not support: {operation}")]
39    BackendUnsupported { backend: String, operation: String },
40
41    /// Invalid gate parameter (e.g., NaN rotation angle).
42    #[error("invalid parameter: {message}")]
43    InvalidParameter { message: String },
44
45    /// Reference to a register name that was never declared.
46    #[error("undefined register `{name}` at line {line}")]
47    UndefinedRegister { name: String, line: usize },
48
49    /// Circuit holds an instruction with no OpenQASM 3.0 spelling.
50    #[error("cannot export instruction {index} to OpenQASM 3.0: {reason}")]
51    ExportUnsupported { index: usize, reason: String },
52
53    /// Incompatible backend for the given circuit.
54    #[error("backend `{backend}` is incompatible: {reason}")]
55    IncompatibleBackend { backend: String, reason: String },
56
57    /// A shot of a dynamic program ran more blocks than its step bound allows.
58    /// `region` names the loop it was in.
59    #[error(
60        "dynamic program stopped after {max_steps} steps in {region}; raise the bound with          `max_steps` if the loop is meant to run that long"
61    )]
62    StepLimit { region: String, max_steps: u64 },
63
64    /// An allocation the run needs is over a memory cap; the numbers are on
65    /// [`ResourceLimit`], boxed so the error stays small on every `Result`.
66    #[error("{0}")]
67    ResourceLimit(Box<ResourceLimit>),
68}
69
70/// Payload of [`PrismError::ResourceLimit`]. `limit` is the cap detected on this
71/// machine or set through `env_var`, when a variable overrides it.
72#[derive(Debug, Error, Clone, PartialEq)]
73#[error(
74    "backend `{backend}`: {operation} needs {required} {resource}, exceeding the cap of \
75     {limit} on this machine{}",
76    override_hint(.env_var)
77)]
78#[non_exhaustive]
79pub struct ResourceLimit {
80    pub backend: String,
81    pub operation: String,
82    pub resource: ResourceKind,
83    pub required: u128,
84    pub limit: u128,
85    pub env_var: Option<&'static str>,
86}
87
88/// Unit of a [`PrismError::ResourceLimit`].
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[non_exhaustive]
91pub enum ResourceKind {
92    /// Qubits of a dense statevector, or of an allocation priced as one.
93    Qubits,
94    /// Complex amplitudes of backend workspace.
95    Amplitudes,
96    /// Stored nonzero entries of a sparse state.
97    Entries,
98    /// Complex elements of a tensor-network intermediate.
99    Elements,
100    DeviceBytes,
101}
102
103impl std::fmt::Display for ResourceKind {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.write_str(match self {
106            ResourceKind::Qubits => "qubits",
107            ResourceKind::Amplitudes => "amplitudes",
108            ResourceKind::Entries => "entries",
109            ResourceKind::Elements => "elements",
110            ResourceKind::DeviceBytes => "bytes of device memory",
111        })
112    }
113}
114
115/// Where a program declined for a loop keyword can go instead.
116fn loop_hint(construct: &str) -> &'static str {
117    match construct {
118        "while" | "break" | "continue" => {
119            "; `openqasm::parse_dynamic` reads it into a control-flow graph"
120        }
121        _ => "",
122    }
123}
124
125fn override_hint(env_var: &Option<&'static str>) -> String {
126    env_var.map_or_else(String::new, |var| format!(" (set {var} to override)"))
127}
128
129pub type Result<T> = std::result::Result<T, PrismError>;