Skip to main content

gam_models/transformation_normal/
error.rs

1// ---------------------------------------------------------------------------
2// Typed errors
3// ---------------------------------------------------------------------------
4
5/// Typed errors emitted by the transformation-normal family pipeline.
6///
7/// Each variant carries a pre-formatted `reason` so `Display` is
8/// byte-equivalent to the original `format!(...)` strings the module used
9/// before the typed-error migration. The category split lets callers
10/// pattern-match on the failure kind (e.g. distinguish a degenerate
11/// covariate design from a non-finite intermediate) without parsing text.
12///
13/// Public/trait boundaries (e.g. `CustomFamily::evaluate`) still return
14/// `Result<_, String>`; the `From<TransformationNormalError> for String`
15/// impl below provides the shim so every typed error flushes through `?`
16/// or `.into()` at the boundary without per-callsite `.map_err`.
17#[derive(Debug, Clone)]
18pub enum TransformationNormalError {
19    /// Shape/length/dimension/contract violations on inputs to a routine
20    /// (e.g. response/covariate row mismatch, beta length mismatch,
21    /// wrong number of blocks, malformed configuration parameters).
22    InvalidInput { reason: String },
23    /// A required covariate design or weight configuration cannot support
24    /// the routine — empty design, zero total weight, residual variance
25    /// not representable, warm-start coefficients all non-finite.
26    DesignDegenerate { reason: String },
27    /// A numeric intermediate (response transform, derivative,
28    /// log-likelihood, weight, offset, gradient component, calibration
29    /// quantity) came out non-finite or non-positive where positive
30    /// finite is required.
31    NonFinite { reason: String },
32    /// The fitted monotone transform's derivative dropped to or below
33    /// zero, or the response endpoint ordering required by the latent
34    /// score (lower < h < upper) was not satisfied at evaluation time.
35    MonotonicityViolated { reason: String },
36    /// A numerical step that maps through the standard-normal CDF
37    /// (endpoint mass, log-difference, PIT probability, derivative
38    /// ratio) underflowed or became non-representable at the requested
39    /// arguments.
40    NumericalFailure { reason: String },
41    /// A prediction evaluated the transform at a response/covariate point
42    /// whose transformed value falls outside the certified positivity
43    /// domain `[lower, upper]` by more than the boundary-roundoff floor.
44    /// Under the direct-α cutover (gam#2306) monotonicity is certified only
45    /// on the fitted rows (via the factored Khatri-Rao cone) and the
46    /// persisted domain certificate; extrapolation past that support is a
47    /// typed refusal, never a clamped/fabricated tail quantile.
48    OutsideCertifiedDomain { reason: String },
49}
50
51impl_reason_error_boilerplate! {
52    TransformationNormalError {
53        InvalidInput,
54        DesignDegenerate,
55        NonFinite,
56        MonotonicityViolated,
57        NumericalFailure,
58        OutsideCertifiedDomain,
59    }
60}
61
62impl From<crate::block_layout::block_count::BlockCountMismatch> for TransformationNormalError {
63    fn from(
64        err: crate::block_layout::block_count::BlockCountMismatch,
65    ) -> TransformationNormalError {
66        TransformationNormalError::InvalidInput {
67            reason: err.message(),
68        }
69    }
70}