Skip to main content

axon_encoder/
error.rs

1use std::fmt;
2
3/// Stable error type returned by fallible encoder constructors.
4#[non_exhaustive]
5#[derive(Debug, Clone, PartialEq)]
6pub enum EncoderError {
7    /// A rate parameter must be finite.
8    NonFiniteRate { parameter: &'static str },
9    /// `base_rate` must not exceed `max_rate`.
10    RateOrder,
11    /// Numeric range bounds must be finite and strictly increasing.
12    InvalidRange { parameter: &'static str },
13    /// Count parameters must be positive.
14    CountMustBePositive { parameter: &'static str },
15    /// A threshold/tuning-width style parameter must be finite and positive.
16    NonPositiveOrNonFinite { parameter: &'static str },
17    /// A parameter must be finite and non-negative (`>= 0`).
18    NonNegativeFinite { parameter: &'static str },
19    /// Channel/neuron count exceeds the `u16` channel-ID range used when emitting spikes.
20    NumChannelsTooLarge,
21    /// Temporal history depth is too small for the encoder window.
22    HistoryDepthTooSmall { minimum: usize },
23    /// A deserialized state vector has inconsistent lengths.
24    StateLengthMismatch {
25        left: &'static str,
26        right: &'static str,
27    },
28    /// A deserialized history channel contains more samples than allowed by history_depth.
29    HistoryLengthExceedsDepth { channel: usize },
30    /// Cycle/window parameters must be non-zero.
31    WindowMustBePositive { parameter: &'static str },
32}
33
34impl fmt::Display for EncoderError {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::NonFiniteRate { parameter } => write!(f, "{parameter} must be finite"),
38            Self::RateOrder => write!(f, "base_rate must be less than or equal to max_rate"),
39            Self::InvalidRange { parameter } => write!(
40                f,
41                "{parameter} must be finite and min must be less than max"
42            ),
43            Self::CountMustBePositive { parameter } => {
44                write!(f, "{parameter} must be greater than 0")
45            }
46            Self::NonPositiveOrNonFinite { parameter } => {
47                write!(f, "{parameter} must be finite and greater than 0")
48            }
49            Self::NonNegativeFinite { parameter } => {
50                write!(f, "{parameter} must be finite and non-negative")
51            }
52            Self::NumChannelsTooLarge => write!(
53                f,
54                "num_channels exceeds u16::MAX as usize + 1 (max addressable spike channels)"
55            ),
56            Self::HistoryDepthTooSmall { minimum } => {
57                write!(f, "history_depth must be at least {minimum}")
58            }
59            Self::StateLengthMismatch { left, right } => {
60                write!(f, "mismatched {left} and {right} lengths")
61            }
62            Self::HistoryLengthExceedsDepth { channel } => {
63                write!(f, "history channel {channel} length exceeds history_depth")
64            }
65            Self::WindowMustBePositive { parameter } => {
66                write!(f, "{parameter} must be greater than 0")
67            }
68        }
69    }
70}
71
72impl std::error::Error for EncoderError {}
73
74pub(crate) const MAX_SPIKE_CHANNELS: usize = u16::MAX as usize + 1;
75
76pub(crate) fn validate_range(
77    parameter: &'static str,
78    range: (f32, f32),
79) -> Result<(), EncoderError> {
80    if range.0.is_finite() && range.1.is_finite() && range.0 < range.1 {
81        Ok(())
82    } else {
83        Err(EncoderError::InvalidRange { parameter })
84    }
85}
86
87/// Like [`validate_range`], but also rejects spans that overflow f32 arithmetic
88/// (e.g. `(f32::MIN, f32::MAX)` → span is `+inf`).
89///
90/// Use for encoders that normalize with f32 division (`RateEncoder`,
91/// `PopulationEncoder`). Latency/Phase normalize in f64 and may accept wider
92/// finite bounds.
93pub(crate) fn validate_range_f32_span(
94    parameter: &'static str,
95    range: (f32, f32),
96) -> Result<(), EncoderError> {
97    validate_range(parameter, range)?;
98    if (range.1 - range.0).is_finite() {
99        Ok(())
100    } else {
101        Err(EncoderError::InvalidRange { parameter })
102    }
103}
104
105pub(crate) fn validate_channel_count(num_channels: usize) -> Result<(), EncoderError> {
106    if num_channels <= MAX_SPIKE_CHANNELS {
107        Ok(())
108    } else {
109        Err(EncoderError::NumChannelsTooLarge)
110    }
111}
112
113/// Validates that `value` is finite and `>= 0`.
114pub(crate) fn validate_non_negative_finite(
115    parameter: &'static str,
116    value: f32,
117) -> Result<(), EncoderError> {
118    if value.is_finite() && value >= 0.0 {
119        Ok(())
120    } else {
121        Err(EncoderError::NonNegativeFinite { parameter })
122    }
123}