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