blprs/
error.rs

1use thiserror::Error;
2
3/// Unified error type for `blprs` operations.
4#[derive(Debug, Error)]
5pub enum BlpError {
6    /// Raised when provided arrays or matrices have incompatible dimensions.
7    #[error("dimension mismatch in {context}: expected {expected} but found {found}")]
8    DimensionMismatch {
9        /// Human-readable context describing the operation.
10        context: &'static str,
11        /// The required dimension, often the model-implied value.
12        expected: usize,
13        /// The dimension that was actually supplied.
14        found: usize,
15    },
16
17    /// Raised when the supplied market ids are not grouped contiguously.
18    #[error("market identifiers must appear in contiguous blocks; market `{market_id}` is split")]
19    NonContiguousMarket { market_id: String },
20
21    /// Raised when product shares are missing or non-positive.
22    #[error("product share at index {index} must be positive, found {share}")]
23    NonPositiveShare { index: usize, share: f64 },
24
25    /// Raised when the outside good share becomes non-positive.
26    #[error("outside share for market `{market_id}` must be positive, found {share}")]
27    NonPositiveOutsideShare { market_id: String, share: f64 },
28
29    /// Raised when a normalization or weight vector is invalid.
30    #[error("weights must be strictly positive and sum to one (slack {slack})")]
31    InvalidWeights { slack: f64 },
32
33    /// Raised when linear algebra operations encounter a singular system.
34    #[error("matrix in {context} is singular")]
35    SingularMatrix { context: &'static str },
36
37    /// Raised when the contraction mapping fails to meet the tolerance.
38    #[error(
39        "BLP contraction did not converge after {iterations} iterations; best max gap {max_gap}"
40    )]
41    ContractionDidNotConverge {
42        /// Number of iterations performed before termination.
43        iterations: usize,
44        /// Maximum absolute change in the last iteration.
45        max_gap: f64,
46    },
47
48    /// Raised when numerical routines produce NaN.
49    #[error("encountered NaN during {context}")]
50    NumericalError { context: &'static str },
51
52    /// Raised when a required component has not been provided to a builder or solver.
53    #[error("{component} must be provided before solving the problem")]
54    MissingComponent { component: &'static str },
55}
56
57impl BlpError {
58    /// Helper to format a [`DimensionMismatch`](BlpError::DimensionMismatch) error.
59    pub fn dimension_mismatch(context: &'static str, expected: usize, found: usize) -> Self {
60        Self::DimensionMismatch {
61            context,
62            expected,
63            found,
64        }
65    }
66
67    /// Helper to raise when a matrix factorization fails due to singularity.
68    pub fn singular(context: &'static str) -> Self {
69        Self::SingularMatrix { context }
70    }
71
72    /// Helper for bubbling up missing component errors from builders.
73    pub fn missing_component(component: &'static str) -> Self {
74        Self::MissingComponent { component }
75    }
76}
77
78/// Type alias for results returned by this crate.
79pub type Result<T> = std::result::Result<T, BlpError>;