Skip to main content

gam_problem/
custom_family_error.rs

1//! Custom-family error type and its String conversions.
2
3use thiserror::Error;
4
5use crate::{IdentifiabilityAudit, MapUniquenessError};
6
7#[derive(Debug, Clone, Error)]
8pub enum CustomFamilyError {
9    #[error("custom-family invalid input in {context}: {reason}")]
10    InvalidInput {
11        context: &'static str,
12        reason: String,
13    },
14    #[error("custom-family optimization error in {context}: {reason}")]
15    Optimization {
16        context: &'static str,
17        reason: String,
18    },
19    #[error("{reason}")]
20    DimensionMismatch { reason: String },
21    #[error("{reason}")]
22    NumericalFailure { reason: String },
23    #[error("{reason}")]
24    ConstraintViolation { reason: String },
25    #[error("{reason}")]
26    UnsupportedConfiguration { reason: String },
27    #[error("{reason}")]
28    BasisDecompositionFailed { reason: String },
29    /// Pre-fit cross-block identifiability audit refused the fit. The
30    /// joint design across `ParameterBlockSpec`s carries a rank
31    /// deficiency that the post-`joint_null_rotation` absorption did
32    /// not resolve: two or more blocks contribute the same direction,
33    /// or a structural >2-way alias was detected without per-pair
34    /// attribution. The full `IdentifiabilityAudit` is held so
35    /// consumers (logs, structured-error sinks, the seed driver's
36    /// classifier) can extract the alias pairs and the summary string
37    /// without reparsing.
38    #[error("identifiability audit refused the fit: {}", audit.summary)]
39    IdentifiabilityFailure { audit: IdentifiabilityAudit },
40    /// MAP estimate uniqueness condition `ker(J^T W J) ∩ ker(S) = {0}` is
41    /// violated.  A null direction of `J^T W J` carries zero penalty
42    /// curvature, so the posterior is flat along that direction and the
43    /// MAP is non-unique.  The structured [`MapUniquenessError`] names the
44    /// dominant block so the caller can add the missing penalty or remove
45    /// the unpenalised direction.
46    #[error("MAP estimate non-unique: {}", error)]
47    MapUniquenessFailure { error: MapUniquenessError },
48}
49
50impl From<String> for CustomFamilyError {
51    fn from(value: String) -> Self {
52        Self::InvalidInput {
53            context: "custom-family string boundary",
54            reason: value,
55        }
56    }
57}
58
59impl From<CustomFamilyError> for String {
60    fn from(value: CustomFamilyError) -> Self {
61        value.to_string()
62    }
63}