Skip to main content

gam_models/fit_orchestration/
deviation.rs

1//! Canonical routing from formula-level link-wiggle declarations to the
2//! marginal-slope deviation blocks consumed by the model families.
3//!
4//! Formula materialization and every application frontend use this module.
5//! Keeping the cubic-runtime constraint and penalty defaults here prevents the
6//! CLI and library fit paths from accepting different models.
7
8use super::*;
9
10fn deviation_block_config_from_formula_linkwiggle(
11    wiggle: &LinkWiggleFormulaSpec,
12) -> Result<DeviationBlockConfig, String> {
13    // The score-warp / link-deviation runtime is a cubic I-spline: its span
14    // tables, C2-continuous construction, and derivative operators are all
15    // structurally cubic. The formula parser remains general because other
16    // wiggle consumers support arbitrary degrees, so enforce this constraint
17    // at the routing boundary shared by every frontend.
18    if wiggle.degree != 3 {
19        return Err(format!(
20            "linkwiggle() degree must be 3 when routed into the score-warp / \
21             link-deviation block: that runtime is a cubic I-spline and only \
22             supports cubic splines; got degree={}",
23            wiggle.degree
24        ));
25    }
26    let defaults = WigglePenaltyConfig::cubic_triple_operator_default();
27    Ok(DeviationBlockConfig {
28        degree: wiggle.degree,
29        num_internal_knots: wiggle.num_internal_knots,
30        penalty_order: *wiggle.penalty_orders.iter().max().unwrap_or(&2),
31        penalty_orders: wiggle.penalty_orders.clone(),
32        double_penalty: wiggle.double_penalty,
33        monotonicity_eps: defaults.monotonicity_eps,
34    })
35}
36
37#[derive(Debug)]
38pub struct MarginalSlopeDeviationRouting {
39    pub score_warp: Option<DeviationBlockConfig>,
40    pub link_dev: Option<DeviationBlockConfig>,
41}
42
43pub fn route_marginal_slope_deviation_blocks(
44    main_linkwiggle: Option<&LinkWiggleFormulaSpec>,
45    logslope_linkwiggle: Option<&LinkWiggleFormulaSpec>,
46) -> Result<MarginalSlopeDeviationRouting, String> {
47    Ok(MarginalSlopeDeviationRouting {
48        score_warp: logslope_linkwiggle
49            .map(deviation_block_config_from_formula_linkwiggle)
50            .transpose()?,
51        link_dev: main_linkwiggle
52            .map(deviation_block_config_from_formula_linkwiggle)
53            .transpose()?,
54    })
55}