tenferro_runtime/error.rs
1//! Error types for the tenferro runtime crate.
2//!
3//! # Examples
4//!
5//! ```rust
6//! use tenferro_runtime::error::Error;
7//!
8//! let err = Error::InvalidSubscripts("bad label".into());
9//! assert!(err.to_string().contains("bad label"));
10//! ```
11
12use std::sync::atomic::{AtomicUsize, Ordering};
13
14use tenferro_tensor::DType;
15
16static NEXT_CONTEXT_ID: AtomicUsize = AtomicUsize::new(1);
17
18/// Errors produced by einsum, eval, and other tenferro operations.
19///
20/// # Examples
21///
22/// ```rust
23/// use tenferro_runtime::error::Error;
24///
25/// let err = Error::InvalidSubscripts("rank mismatch".into());
26/// ```
27#[derive(Debug, thiserror::Error)]
28pub enum Error {
29 /// Einsum subscript string is invalid or cannot be parsed.
30 #[error("invalid subscripts: {0}")]
31 InvalidSubscripts(String),
32
33 /// Contraction optimization failed (shape mismatch, bad path, etc.).
34 #[error("contraction error: {0}")]
35 ContractionError(String),
36
37 /// A required input tensor is missing from the inputs map.
38 #[error("missing input: {0}")]
39 MissingInput(String),
40
41 /// Reverse-mode gradient requires a scalar output.
42 #[error("grad requires a scalar output, got shape {shape:?}")]
43 NonScalarGrad { shape: Vec<usize> },
44
45 /// Runtime tensor execution failed in the backend layer.
46 #[error(transparent)]
47 TensorRuntime(#[from] tenferro_tensor::Error),
48
49 /// A `TracedTensor` passed to graph-executor input bindings is not a
50 /// placeholder (has attached data).
51 #[error(
52 "binding #{binding_index} is not a placeholder; \
53 only tensors built via input_concrete_shape / input_symbolic_shape \
54 can be bound"
55 )]
56 UnexpectedBinding { binding_index: usize },
57
58 /// A placeholder appearing in the graph has no binding supplied.
59 #[error("placeholder {input_key} has no runtime input binding")]
60 UnboundPlaceholder { input_key: String },
61
62 /// The same placeholder was bound more than once in the `bindings` slice.
63 #[error("placeholder {input_key} was bound more than once")]
64 DuplicateBinding { input_key: String },
65
66 /// A binding tensor's dtype does not match the placeholder's dtype.
67 #[error("binding dtype mismatch for placeholder: expected {expected:?}, got {actual:?}")]
68 PlaceholderDtypeMismatch { expected: DType, actual: DType },
69
70 /// A binding tensor's shape does not match an `input_concrete_shape`
71 /// placeholder's fixed shape.
72 #[error(
73 "binding shape mismatch for concrete-shape placeholder: \
74 expected {expected:?}, got {actual:?}"
75 )]
76 PlaceholderShapeMismatch {
77 expected: Vec<usize>,
78 actual: Vec<usize>,
79 },
80
81 /// A binding tensor's rank does not match an `input_symbolic_shape`
82 /// placeholder's declared rank.
83 #[error(
84 "binding rank mismatch for symbolic-shape placeholder: \
85 expected rank {expected}, got rank {actual}"
86 )]
87 PlaceholderRankMismatch { expected: usize, actual: usize },
88
89 /// Operation attempted to mix tensors from different eager contexts.
90 #[error(
91 "tensors belong to different eager AD contexts ({lhs} vs {rhs}); \
92 detach into the target context before combining them"
93 )]
94 ContextMismatch { lhs: ContextId, rhs: ContextId },
95
96 /// Traced graph construction rejected an invalid operation configuration.
97 #[error("{op}: invalid traced graph build: {message}")]
98 InvalidGraphBuild {
99 /// Public operation name.
100 op: &'static str,
101 /// Validation failure details.
102 message: String,
103 },
104
105 /// An AD transform requires a primitive or extension rule that is not
106 /// registered for the requested operation.
107 #[error("unsupported {transform} AD rule for {op}")]
108 UnsupportedAdRule {
109 /// AD transform that requested the rule, such as `grad` or `backward`.
110 transform: &'static str,
111 /// Operation or extension family identifier that has no applicable rule.
112 op: String,
113 },
114
115 /// Lowering rejected an inconsistent compiled graph.
116 #[error("invalid compiled graph: {message}")]
117 InvalidCompiledGraph {
118 /// Validation failure details.
119 message: String,
120 },
121
122 /// An unexpected internal error.
123 #[error("internal error: {0}")]
124 Internal(String),
125}
126
127/// Opaque identifier for an eager AD runtime, used in [`Error::ContextMismatch`].
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub struct ContextId(usize);
130
131impl ContextId {
132 /// Generate a fresh opaque runtime context identifier.
133 ///
134 /// Runtime implementations use this when constructing a new execution
135 /// context. The value is intentionally opaque and is only useful in error
136 /// reporting and equality checks.
137 pub fn fresh() -> Self {
138 let id = NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed);
139 Self(id)
140 }
141}
142
143impl std::fmt::Display for ContextId {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "ctx@{:x}", self.0)
146 }
147}
148
149/// Result type alias for tenferro operations.
150pub type Result<T> = std::result::Result<T, Error>;