gam_problem/
basis_error.rs1use gam_linalg::faer_ndarray::FaerLinalgError;
8use thiserror::Error;
9
10#[derive(Error, Debug)]
12pub enum BasisError {
13 #[error("Spline degree must be at least 1, but was {0}.")]
14 InvalidDegree(usize),
15
16 #[error(
17 "Spline degree {degree} is too low for derivative order {derivative_order}; need degree >= {minimum_degree}."
18 )]
19 InsufficientDegreeForDerivative {
20 degree: usize,
21 derivative_order: usize,
22 minimum_degree: usize,
23 },
24
25 #[error("Data range is invalid: start ({0}) must be less than or equal to end ({1}).")]
26 InvalidRange(f64, f64),
27
28 #[error(
29 "Data range has zero width (min equals max), which collapses the B-spline knot domain; requested {0} internal knots."
30 )]
31 DegenerateRange(usize),
32
33 #[error(
34 "Penalty order ({order}) must be positive and less than the number of basis functions ({num_basis})."
35 )]
36 InvalidPenaltyOrder { order: usize, num_basis: usize },
37
38 #[error(
39 "Insufficient knots for degree {degree} spline: need at least {required} knots but only {provided} were provided."
40 )]
41 InsufficientKnotsForDegree {
42 degree: usize,
43 required: usize,
44 provided: usize,
45 },
46
47 #[error(
48 "Cannot apply sum-to-zero constraint: requires at least 2 basis functions, but only {found} were provided."
49 )]
50 InsufficientColumnsForConstraint { found: usize },
51
52 #[error(
53 "Constraint matrix must have the same number of rows as the basis: basis has {basisrows}, constraint has {constraintrows}."
54 )]
55 ConstraintMatrixRowMismatch {
56 basisrows: usize,
57 constraintrows: usize,
58 },
59
60 #[error(
61 "Weights dimension mismatch: expected {expected} weights to match basis matrix rows, but got {found}."
62 )]
63 WeightsDimensionMismatch { expected: usize, found: usize },
64
65 #[error("QR decomposition failed while applying constraints: {0}")]
66 LinalgError(#[from] FaerLinalgError),
67
68 #[error(
69 "Failed to identify a constraint nullspace basis at {site}: \
70 coefficient dim {coeff_dim}, cross-rank {cross_rank}, \
71 constraint Frobenius {cross_frobenius:.3e}, \
72 constrained Gram spectrum {gram_spectrum}. \
73 The smooth basis collapses onto the parametric block — typical causes: \
74 (a) the smooth's evaluated kernel underflows after projecting out the \
75 polynomial nullspace, leaving only floating-point noise (Duchon hybrid \
76 in moderate-to-high d with length_scale near pairwise center distances); \
77 (b) the parametric block already spans the smooth's column space \
78 (over-restrictive identifiability constraint); \
79 (c) the smooth has effective rank ≤ parametric-block size on this data."
80 )]
81 ConstraintNullspaceCollapsed {
82 site: &'static str,
83 cross_rank: usize,
84 coeff_dim: usize,
85 cross_frobenius: f64,
86 gram_spectrum: String,
92 },
93
94 #[error(
95 "Knot vector is degenerate: all Greville abscissae are equal, so linear constraint cannot be applied."
96 )]
97 DegenerateKnots,
98
99 #[error(
100 "The provided knot vector is invalid: {0}. It must be non-decreasing and contain only finite values."
101 )]
102 InvalidKnotVector(String),
103
104 #[error("Failed to build sparse basis matrix: {0}")]
105 SparseCreation(String),
106
107 #[error("Dimension mismatch: {0}")]
108 DimensionMismatch(String),
109
110 #[error(
111 "Indefinite penalty matrix in {context}: minimum eigenvalue {min_eigenvalue:.3e} is below tolerance {tolerance:.3e}. {guidance}"
112 )]
113 IndefinitePenalty {
114 context: String,
115 min_eigenvalue: f64,
116 tolerance: f64,
117 guidance: String,
118 },
119
120 #[error("Invalid input: {0}")]
121 InvalidInput(String),
122
123 #[error("{0}")]
124 DenseDerivativeMaterializationRefused(String),
125
126 #[error(
127 "Radial basis derivative is undefined at center collision (r = 0) for {kernel} \
128 with dim = {dim}, m = {m}: {message}. The first/second derivative of the \
129 underlying φ(r) does not have a finite limit as r → 0+, so the design-row \
130 gradient and Hessian have no well-defined value at coincident points."
131 )]
132 DegenerateAtCollision {
133 kernel: &'static str,
134 dim: usize,
135 m: f64,
136 message: &'static str,
137 },
138
139 #[error(
140 "Periodic radial basis derivative is undefined at the wrap branch cut \
141 (signed displacement = ±period/2) for raw delta = {raw}, period = {period}: \
142 the wrapped displacement jumps between ±period/2 and the first derivative \
143 w.r.t. the input has a one-sided discontinuity. Move the evaluation point \
144 off the branch cut or define a one-sided convention."
145 )]
146 PeriodicWrapBranchCut { raw: f64, period: f64 },
147
148 #[error("{0}")]
149 Other(String),
150}