variational_regression/
lib.rs1pub mod distribution;
6pub mod error;
7pub mod linear;
8pub mod logistic;
9mod math;
10
11pub use distribution::{ScalarDistribution, GammaDistribution, GaussianDistribution, BernoulliDistribution};
12pub use linear::{VariationalLinearRegression, LinearTrainConfig};
13pub use logistic::{VariationalLogisticRegression, LogisticTrainConfig};
14pub use error::RegressionError;
15
16use serde::{Serialize, Deserialize};
17use nalgebra::{DMatrix, DVector};
18type DenseMatrix = DMatrix<f64>;
19type DenseVector = DVector<f64>;
20
21pub trait VariationalRegression<D: ScalarDistribution> {
26
27 fn predict(&self, features: &[f64]) -> Result<D, RegressionError>;
35
36 fn weights(&self) -> &[f64];
40
41 fn bias(&self) -> Option<f64>;
45}
46
47pub trait Features {
51
52 fn into_matrix(self, bias: bool) -> DenseMatrix;
61}
62
63impl Features for Vec<Vec<f64>> {
64 fn into_matrix(self, bias: bool) -> DenseMatrix {
65 (&self).into_matrix(bias)
66 }
67}
68
69impl Features for &Vec<Vec<f64>> {
70 fn into_matrix(self, bias: bool) -> DenseMatrix {
71 let temp: Vec<_> = self.iter().map(|row| row.as_slice()).collect();
72 temp.as_slice().into_matrix(bias)
73 }
74}
75
76impl Features for &[&[f64]] {
77 fn into_matrix(self, bias: bool) -> DenseMatrix {
78 let offset = if bias { 1 } else { 0 };
79 let n = self.len();
80 let d = self[0].len() + offset;
81 let mut x = DenseMatrix::zeros(n, d);
82 for i in 0..n {
83 if bias {
84 x[(i, 0)] = 1.0;
85 }
86 for j in offset..d {
87 x[(i, j)] = self[i][j - offset];
88 }
89 }
90 return x;
91 }
92}
93
94pub trait RealLabels {
98
99 fn into_vector(self) -> DenseVector;
103}
104
105impl RealLabels for Vec<f64> {
106 fn into_vector(self) -> DenseVector {
107 DenseVector::from_vec(self)
108 }
109}
110
111impl RealLabels for &Vec<f64> {
112 fn into_vector(self) -> DenseVector {
113 self.as_slice().into_vector()
114 }
115}
116
117impl RealLabels for &[f64] {
118 fn into_vector(self) -> DenseVector {
119 DenseVector::from_column_slice(self)
120 }
121}
122
123pub trait BinaryLabels {
127
128 fn into_vector(self) -> DenseVector;
132}
133
134impl BinaryLabels for Vec<bool> {
135 fn into_vector(self) -> DenseVector {
136 self.as_slice().into_vector()
137 }
138}
139
140impl BinaryLabels for &Vec<bool> {
141 fn into_vector(self) -> DenseVector {
142 self.as_slice().into_vector()
143 }
144}
145
146impl BinaryLabels for &[bool] {
147 fn into_vector(self) -> DenseVector {
148 let iter = self.iter().map(|&label| if label { 1.0 } else { 0.0 });
149 DenseVector::from_iterator(self.len(), iter)
150 }
151}
152
153pub (crate) fn design_vector(features: &[f64], bias: bool) -> DenseVector {
154 let offset = if bias { 1 } else { 0 };
155 let d = features.len() + offset;
156 let mut x = DenseVector::zeros(d);
157 if bias {
158 x[0] = 1.0;
159 }
160 for j in offset..d {
161 x[j] = features[j - offset];
162 }
163 return x;
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub (crate) struct Stats {
168 pub mean: f64,
169 pub sd: f64
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub (crate) struct Standardizer {
174 stats: Vec<Stats>
175}
176
177impl Standardizer {
178
179 pub fn fit(features: &DenseMatrix) -> Standardizer {
180 let stats = (0..features.ncols()).map(|j| {
181 let mut sum = 0.0;
182 for i in 0..features.nrows() {
183 sum += features[(i, j)];
184 }
185 let mean = sum / features.nrows() as f64;
186 let mut sse = 0.0;
187 for i in 0..features.nrows() {
188 let val = features[(i, j)];
189 sse += (val - mean) * (val - mean);
190 }
191 let sd = (sse / (features.nrows() - 1) as f64).sqrt();
192 Stats { mean, sd }
193 }).collect();
194 Standardizer { stats }
195 }
196
197 pub fn transform_matrix(&self, features: &mut DenseMatrix) {
198 for j in 0..features.ncols() {
199 let stats = &self.stats[j];
200 if stats.sd > 0.0 {
201 for i in 0..features.nrows() {
202 let val = features[(i, j)];
203 features[(i, j)] = (val - stats.mean) / stats.sd;
204 }
205 }
206 }
207 }
208
209 pub fn transform_vector(&self, features: &mut DenseVector) {
210 for j in 0..features.len() {
211 let stats = &self.stats[j];
212 if stats.sd > 0.0 {
213 let val = features[j];
214 features[j] = (val - stats.mean) / stats.sd;
215 }
216 }
217 }
218}
219
220pub (crate) fn get_weights<'a>(includes_bias: bool, params: &'a DenseVector) -> &'a [f64] {
221 let offset = if includes_bias { 1 } else { 0 };
222 ¶ms.as_slice()[offset..]
223}
224
225pub (crate) fn get_bias(includes_bias: bool, params: &DenseVector) -> Option<f64> {
226 if includes_bias {
227 Some( params[0])
228 } else {
229 None
230 }
231}