From c0128a0eb8cbb9b8f6a48ce83bc92a889038f8c1 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 22 Oct 2026 19:21:18 +0200 Subject: [PATCH] Use scale-relative degeneracy check in linear trendline fit Found while testing crates/model/src/trendline.rs:281-313 (linreg) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/model/src/trendline.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/model/src/trendline.rs b/crates/model/src/trendline.rs index f893a764..de57e17b 100644 --- a/crates/model/src/trendline.rs +++ b/crates/model/src/trendline.rs @@ -289,7 +289,7 @@ fn linreg(xs: &[f64], ys: &[f64], intercept: Option) -> Option<(f64, f64)> sxx += x * x; sxy += x * (y - b); } - if sxx.abs() < 1e-18 { + if sxx <= 0.0 { return None; } let m = sxy / sxx; @@ -304,7 +304,7 @@ fn linreg(xs: &[f64], ys: &[f64], intercept: Option) -> Option<(f64, f64)> sxy += x * y; } let den = n * sxx - sx * sx; - if den.abs() < 1e-18 { + if den.abs() <= 1e-12 * n * sxx { return None; } let m = (n * sxy - sx * sy) / den; @@ -547,6 +547,14 @@ mod tests { Trendline { kind, ..Default::default() } } + #[test] + fn linear_fits_tiny_x_values() { + let xs = [1e-10, 2e-10, 3e-10, 4e-10, 5e-10]; + let ys = [1.0, 2.0, 3.0, 4.0, 5.0]; + let fit = line(TrendlineType::Linear).fit(&xs, &ys).expect("fit of tiny x values"); + assert!((fit.y(3e-10).unwrap() - 3.0).abs() < 1e-6); + } + #[test] fn linear_recovers_slope_and_a_fixed_intercept() { let xs = [1.0, 2.0, 3.0, 4.0];