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