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}