Skip to main content

burn_train/metric/processor/
error.rs

1use burn_core::tensor::TensorReadError;
2use burn_std::ExecutionError;
3
4use crate::metric::{MetricName, store::Split};
5
6/// A metric that could not process an event.
7#[derive(Debug)]
8pub struct MetricError {
9    /// The name of the metric that failed.
10    pub metric: MetricName,
11    /// The split during which the error occurred.
12    pub split: Split,
13    /// The source error.
14    pub source: TensorReadError,
15}
16
17impl core::fmt::Display for MetricError {
18    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        match &self.split {
20            Split::Test(Some(name)) => write!(f, "test/{name}/{}: {}", self.metric, self.source),
21            split => write!(f, "{split}/{}: {}", self.metric, self.source),
22        }
23    }
24}
25
26impl core::error::Error for MetricError {
27    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
28        Some(&self.source)
29    }
30}
31
32/// A failure an event processor reported.
33#[derive(Debug)]
34pub enum EventProcessorFailure {
35    /// Error while evaluating metric.
36    Metric(MetricError),
37    /// An event could not be synced.
38    Sync {
39        /// The split the event belonged to.
40        split: Split,
41        /// The source error.
42        source: ExecutionError,
43    },
44}
45
46impl EventProcessorFailure {
47    /// Whether the failure comes from a poisoned device.
48    pub fn is_device_poisoned(&self) -> bool {
49        match self {
50            Self::Metric(error) => {
51                matches!(&error.source, TensorReadError::Execution(err) if err.is_device_poisoned())
52            }
53            Self::Sync { source, .. } => source.is_device_poisoned(),
54        }
55    }
56}
57
58impl core::fmt::Display for EventProcessorFailure {
59    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60        match self {
61            Self::Metric(error) => write!(f, "{error}"),
62            Self::Sync {
63                split: Split::Test(Some(name)),
64                source,
65            } => write!(f, "test/{name}: the event could not be synced: {source}"),
66            Self::Sync { split, source } => {
67                write!(f, "{split}: the event could not be synced: {source}")
68            }
69        }
70    }
71}
72
73impl core::error::Error for EventProcessorFailure {
74    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
75        match self {
76            Self::Metric(error) => Some(error),
77            Self::Sync { source, .. } => Some(source),
78        }
79    }
80}
81
82impl From<MetricError> for EventProcessorFailure {
83    fn from(error: MetricError) -> Self {
84        Self::Metric(error)
85    }
86}
87
88/// Every failure an event processor reported at once, oldest first.
89#[derive(Debug)]
90pub struct EventProcessorError {
91    failures: Vec<EventProcessorFailure>,
92}
93
94impl EventProcessorError {
95    /// `Ok` when no metric failed, otherwise every metric failure as one error.
96    pub(crate) fn from_errors(errors: Vec<MetricError>) -> Result<(), Self> {
97        match errors.is_empty() {
98            true => Ok(()),
99            false => Err(Self {
100                failures: errors
101                    .into_iter()
102                    .map(EventProcessorFailure::Metric)
103                    .collect(),
104            }),
105        }
106    }
107
108    /// An event of `split` could not be synced.
109    pub(crate) fn sync(split: Split, source: ExecutionError) -> Self {
110        Self {
111            failures: vec![EventProcessorFailure::Sync { split, source }],
112        }
113    }
114
115    /// Append `other`'s failures after this one's.
116    pub(crate) fn merge(&mut self, other: Self) {
117        self.failures.extend(other.failures);
118    }
119
120    /// Every failure, oldest first.
121    pub fn failures(&self) -> &[EventProcessorFailure] {
122        &self.failures
123    }
124
125    /// Every failure, oldest first.
126    pub fn into_failures(self) -> Vec<EventProcessorFailure> {
127        self.failures
128    }
129
130    /// Whether any failure comes from a poisoned device, on which every later read fails too.
131    pub fn is_device_poisoned(&self) -> bool {
132        self.failures
133            .iter()
134            .any(EventProcessorFailure::is_device_poisoned)
135    }
136}
137
138impl core::fmt::Display for EventProcessorError {
139    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140        match self.failures.as_slice() {
141            [failure] => write!(f, "Event processing failed: {failure}"),
142            failures => {
143                write!(f, "Event processing failed {} times:", failures.len())?;
144                for failure in failures {
145                    write!(f, "\n  {failure}")?;
146                }
147                Ok(())
148            }
149        }
150    }
151}
152
153impl core::error::Error for EventProcessorError {}