Skip to main content

gamlss_core/model/
layout.rs

1use std::ops::Range;
2
3use crate::ParameterName;
4
5/// Именованный блок коэффициентов внутри плоского вектора параметров.
6///
7/// Связывает стабильное имя параметра распределения (например, `"mu"`)
8/// с диапазоном позиций в общем beta-векторе.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ParameterSlice {
11    /// Stable distribution parameter name, e.g. `"mu"` or `"sigma"`.
12    pub name: &'static str,
13    /// Coefficient range for this parameter inside the full beta vector.
14    pub range: Range<usize>,
15}
16
17/// Отображение параметров распределения на диапазоны в плоском beta-векторе.
18///
19/// Используется для introspection модели: распаковки коэффициентов,
20/// построения diagnostics и передачи информации внешним оптимизаторам.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ParameterLayout {
23    slices: Vec<ParameterSlice>,
24}
25
26impl ParameterLayout {
27    /// Creates a layout from named slices.
28    #[must_use]
29    pub fn new(slices: Vec<ParameterSlice>) -> Self {
30        Self { slices }
31    }
32
33    /// Number of parameter blocks represented by this layout.
34    #[must_use]
35    pub fn len(&self) -> usize {
36        self.slices.len()
37    }
38
39    /// `true` if this layout has no parameter blocks.
40    #[must_use]
41    pub fn is_empty(&self) -> bool {
42        self.slices.is_empty()
43    }
44
45    /// Minimum coefficient-vector length needed to contain every slice.
46    ///
47    /// For ordinary model layouts this is equal to the model's coefficient
48    /// count. For manually constructed layouts with gaps it returns the
49    /// largest slice end.
50    #[must_use]
51    pub fn ncoefficients(&self) -> usize {
52        self.slices
53            .iter()
54            .map(|slice| slice.range.end)
55            .max()
56            .unwrap_or(0)
57    }
58
59    /// Returns all parameter slices in model order.
60    #[must_use]
61    pub fn slices(&self) -> &[ParameterSlice] {
62        &self.slices
63    }
64
65    /// Visits parameter slices in model order without allocating.
66    pub fn visit_slices(&self, mut visit: impl FnMut(usize, &'static str, Range<usize>)) {
67        for (index, slice) in self.slices.iter().enumerate() {
68            visit(index, slice.name, slice.range.clone());
69        }
70    }
71
72    /// Returns the coefficient range for `name`, if present.
73    #[must_use]
74    pub fn slice(&self, name: &str) -> Option<Range<usize>> {
75        self.slices
76            .iter()
77            .find(|slice| slice.name == name)
78            .map(|slice| slice.range.clone())
79    }
80
81    /// Returns the coefficient range for typed parameter marker `P`, if present.
82    #[must_use]
83    pub fn slice_of<P>(&self) -> Option<Range<usize>>
84    where
85        P: ParameterName,
86    {
87        self.slice(P::NAME)
88    }
89}
90
91/// Коэффициенты одного распакованного параметрического блока.
92///
93/// Возвращается методом [`crate::Gamlss::unpack_theta`] для human-readable
94/// представления плоского beta-вектора.
95#[derive(Debug, Clone, PartialEq)]
96pub struct ParameterCoefficients {
97    /// Stable distribution parameter name.
98    pub name: &'static str,
99    /// Coefficients for this parameter block.
100    pub coefficients: Vec<f64>,
101}
102
103/// Человекочитаемое представление плоского beta-вектора.
104///
105/// Содержит по одному [`ParameterCoefficients`] для каждого параметра
106/// распределения в порядке модели.
107#[derive(Debug, Clone, PartialEq)]
108pub struct UnpackedTheta {
109    /// Parameter blocks in model order.
110    pub blocks: Vec<ParameterCoefficients>,
111}
112
113impl UnpackedTheta {
114    /// Returns an unpacked coefficient block by parameter name.
115    #[must_use]
116    pub fn block(&self, name: &str) -> Option<&ParameterCoefficients> {
117        self.blocks.iter().find(|block| block.name == name)
118    }
119
120    /// Returns an unpacked coefficient block for typed parameter marker `P`.
121    #[must_use]
122    pub fn block_of<P>(&self) -> Option<&ParameterCoefficients>
123    where
124        P: ParameterName,
125    {
126        self.block(P::NAME)
127    }
128
129    /// Returns coefficients by parameter name.
130    #[must_use]
131    pub fn coefficients(&self, name: &str) -> Option<&[f64]> {
132        self.block(name).map(|block| block.coefficients.as_slice())
133    }
134
135    /// Returns coefficients for typed parameter marker `P`.
136    #[must_use]
137    pub fn coefficients_of<P>(&self) -> Option<&[f64]>
138    where
139        P: ParameterName,
140    {
141        self.coefficients(P::NAME)
142    }
143}
144
145/// Training diagnostics for a candidate theta vector.
146///
147/// Содержит значения objective, scaled training negative log-likelihood (без
148/// штрафов), суммарный штраф, норму градиента и число не-finite компонент
149/// градиента.
150#[derive(Debug, Clone, Copy, PartialEq)]
151pub struct TrainingDiagnostics {
152    /// Full objective value: weighted training negative log-likelihood plus penalties.
153    pub objective: f64,
154    /// Training negative log-likelihood before penalties, using the model's objective scale.
155    pub train_nll: f64,
156    /// Total penalty contribution.
157    pub penalty: f64,
158    /// Euclidean norm of the objective gradient.
159    pub gradient_norm: f64,
160    /// Number of non-finite gradient entries.
161    pub nonfinite_gradient_count: usize,
162}