1use super::functions::*;
6pub struct GrowthFunction {
8 pub vc_dim: usize,
10}
11impl GrowthFunction {
12 pub fn new(vc_dim: usize) -> Self {
14 Self { vc_dim }
15 }
16 pub fn evaluate(&self, m: usize) -> usize {
18 VCDimension::new(self.vc_dim).sauer_shelah_bound(m)
19 }
20}
21#[allow(dead_code)]
23#[derive(Debug, Clone)]
24pub struct CrossValidation {
25 pub n_folds: usize,
26 pub n_samples: usize,
27 pub shuffle: bool,
28 pub stratified: bool,
29}
30#[allow(dead_code)]
31impl CrossValidation {
32 pub fn new(k: usize, n: usize) -> Self {
33 CrossValidation {
34 n_folds: k,
35 n_samples: n,
36 shuffle: true,
37 stratified: false,
38 }
39 }
40 pub fn k_fold_5(n: usize) -> Self {
41 CrossValidation::new(5, n)
42 }
43 pub fn loocv(n: usize) -> Self {
44 CrossValidation::new(n, n)
45 }
46 pub fn fold_size(&self) -> usize {
47 (self.n_samples + self.n_folds - 1) / self.n_folds
48 }
49 pub fn train_size(&self) -> usize {
50 self.n_samples - self.fold_size()
51 }
52 pub fn n_train_test_splits(&self) -> usize {
53 self.n_folds
54 }
55}
56pub struct EarlyStoppingReg {
58 pub max_iters: usize,
60 pub step_size: f64,
62}
63impl EarlyStoppingReg {
64 pub fn new(max_iters: usize, step_size: f64) -> Self {
66 Self {
67 max_iters,
68 step_size,
69 }
70 }
71 pub fn effective_lambda(&self) -> f64 {
73 1.0 / (self.step_size * self.max_iters as f64)
74 }
75}
76pub struct AdaBoost {
78 pub rounds: usize,
80 pub alphas: Vec<f64>,
82 pub weak_accuracies: Vec<f64>,
84}
85impl AdaBoost {
86 pub fn new(rounds: usize) -> Self {
88 Self {
89 rounds,
90 alphas: Vec::new(),
91 weak_accuracies: Vec::new(),
92 }
93 }
94 pub fn compute_alpha(weak_error: f64) -> f64 {
96 0.5 * ((1.0 - weak_error) / weak_error).ln()
97 }
98 pub fn training_error_bound(gammas: &[f64]) -> f64 {
100 let sum_gamma_sq: f64 = gammas.iter().map(|g| g * g).sum();
101 (-2.0 * sum_gamma_sq).exp()
102 }
103 pub fn add_round(&mut self, weak_accuracy: f64) {
105 let weak_error = 1.0 - weak_accuracy;
106 let alpha = Self::compute_alpha(weak_error);
107 self.alphas.push(alpha);
108 self.weak_accuracies.push(weak_accuracy);
109 }
110}
111pub struct OnlineGradientDescent {
113 pub weights: Vec<f64>,
115 pub eta: f64,
117 pub d: f64,
119 pub g: f64,
121 pub t: usize,
123}
124impl OnlineGradientDescent {
125 pub fn new(dim: usize, eta: f64, d: f64, g: f64) -> Self {
127 Self {
128 weights: vec![0.0; dim],
129 eta,
130 d,
131 g,
132 t: 0,
133 }
134 }
135 pub fn update(&mut self, gradient: &[f64]) {
137 for (wi, &gi) in self.weights.iter_mut().zip(gradient.iter()) {
138 *wi -= self.eta * gi;
139 }
140 let norm: f64 = self.weights.iter().map(|wi| wi * wi).sum::<f64>().sqrt();
141 if norm > self.d {
142 let scale = self.d / norm;
143 for wi in self.weights.iter_mut() {
144 *wi *= scale;
145 }
146 }
147 self.t += 1;
148 }
149 pub fn regret_bound(&self) -> f64 {
151 self.d * self.g * (2.0 * self.t as f64).sqrt()
152 }
153}
154#[allow(dead_code)]
156#[derive(Debug, Clone)]
157pub struct SVMClassifier {
158 pub kernel_type: SVMKernel,
159 pub c_regularization: f64,
160 pub n_support_vectors: usize,
161}
162#[allow(dead_code)]
163impl SVMClassifier {
164 pub fn new(kernel: SVMKernel, c: f64) -> Self {
165 SVMClassifier {
166 kernel_type: kernel,
167 c_regularization: c,
168 n_support_vectors: 0,
169 }
170 }
171 pub fn linear(c: f64) -> Self {
172 SVMClassifier::new(SVMKernel::Linear, c)
173 }
174 pub fn rbf(gamma: f64, c: f64) -> Self {
175 SVMClassifier::new(SVMKernel::RBF(gamma), c)
176 }
177 pub fn kernel_value(&self, x: &[f64], xp: &[f64]) -> f64 {
178 match &self.kernel_type {
179 SVMKernel::Linear => dot(x, xp),
180 SVMKernel::Polynomial(d) => (dot(x, xp) + 1.0).powi(*d as i32),
181 SVMKernel::RBF(gamma) => {
182 let sq_dist = x
183 .iter()
184 .zip(xp.iter())
185 .map(|(a, b)| (a - b).powi(2))
186 .sum::<f64>();
187 (-gamma * sq_dist).exp()
188 }
189 SVMKernel::Sigmoid => (dot(x, xp)).tanh(),
190 }
191 }
192 pub fn sparsity_ratio(&self, n_training: usize) -> f64 {
193 if n_training == 0 {
194 return 0.0;
195 }
196 self.n_support_vectors as f64 / n_training as f64
197 }
198}
199pub struct FisherInformation {
201 pub theta: f64,
203}
204impl FisherInformation {
205 pub fn new(theta: f64) -> Self {
207 Self { theta }
208 }
209 pub fn estimate(log_density: impl Fn(f64, f64) -> f64, theta: f64, samples: &[f64]) -> f64 {
213 let h = 1e-5;
214 let n = samples.len() as f64;
215 samples
216 .iter()
217 .map(|&x| {
218 let score = (log_density(x, theta + h) - log_density(x, theta - h)) / (2.0 * h);
219 score * score
220 })
221 .sum::<f64>()
222 / n
223 }
224 pub fn cramer_rao_bound(&self, fisher_val: f64) -> f64 {
226 if fisher_val > 0.0 {
227 1.0 / fisher_val
228 } else {
229 f64::INFINITY
230 }
231 }
232}
233pub struct RademacherComplexity {
238 pub n: usize,
240 pub class_size: usize,
242}
243impl RademacherComplexity {
244 pub fn new(n: usize, class_size: usize) -> Self {
246 Self { n, class_size }
247 }
248 pub fn upper_bound(&self) -> f64 {
250 (2.0 * (self.class_size as f64).ln() / self.n as f64).sqrt()
251 }
252 pub fn generalization_bound(&self, empirical_loss: f64, delta: f64) -> f64 {
254 let rn = self.upper_bound();
255 let confidence_term = ((2.0 / delta).ln() / (2.0 * self.n as f64)).sqrt();
256 empirical_loss + 2.0 * rn + confidence_term
257 }
258}
259pub struct UniformConvergence {
261 pub eps: f64,
263 pub delta: f64,
265}
266impl UniformConvergence {
267 pub fn new(eps: f64, delta: f64) -> Self {
269 Self { eps, delta }
270 }
271 pub fn required_samples(&self, class_size: usize) -> usize {
273 let log_h = (class_size as f64).ln();
274 let log_delta = (1.0 / self.delta).ln();
275 ((2.0 * (log_h + log_delta)) / (self.eps * self.eps)).ceil() as usize
276 }
277}
278pub struct BiasVarianceTradeoff {
280 pub bias_squared: f64,
282 pub variance: f64,
284 pub noise: f64,
286}
287impl BiasVarianceTradeoff {
288 pub fn new(bias_squared: f64, variance: f64, noise: f64) -> Self {
290 Self {
291 bias_squared,
292 variance,
293 noise,
294 }
295 }
296 pub fn total_mse(&self) -> f64 {
298 self.bias_squared + self.variance + self.noise
299 }
300}
301pub struct MutualInformation;
303impl MutualInformation {
304 pub fn compute(joint: &[Vec<f64>]) -> f64 {
308 if joint.is_empty() {
309 return 0.0;
310 }
311 let _n_rows = joint.len();
312 let n_cols = joint[0].len();
313 let px: Vec<f64> = joint.iter().map(|row| row.iter().sum::<f64>()).collect();
314 let py: Vec<f64> = (0..n_cols)
315 .map(|j| {
316 joint
317 .iter()
318 .map(|row| row.get(j).copied().unwrap_or(0.0))
319 .sum()
320 })
321 .collect();
322 let h_x: f64 = px.iter().filter(|&&p| p > 0.0).map(|&p| -p * p.ln()).sum();
323 let h_y: f64 = py.iter().filter(|&&p| p > 0.0).map(|&p| -p * p.ln()).sum();
324 let h_xy: f64 = joint
325 .iter()
326 .flat_map(|row| row.iter())
327 .filter(|&&p| p > 0.0)
328 .map(|&p| -p * p.ln())
329 .sum();
330 (h_x + h_y - h_xy).max(0.0)
331 }
332 pub fn data_processing_inequality(joint_xy: &[Vec<f64>], joint_xz: &[Vec<f64>]) -> bool {
336 let i_xy = Self::compute(joint_xy);
337 let i_xz = Self::compute(joint_xz);
338 i_xz <= i_xy + 1e-9
339 }
340}
341#[allow(dead_code)]
343#[derive(Debug, Clone)]
344pub struct GradientBoosting {
345 pub n_estimators: usize,
346 pub learning_rate: f64,
347 pub max_depth: usize,
348 pub loss: GBLoss,
349}
350#[allow(dead_code)]
351impl GradientBoosting {
352 pub fn new(n: usize, lr: f64, depth: usize, loss: GBLoss) -> Self {
353 GradientBoosting {
354 n_estimators: n,
355 learning_rate: lr,
356 max_depth: depth,
357 loss,
358 }
359 }
360 pub fn xgboost_style(n: usize) -> Self {
361 GradientBoosting::new(n, 0.1, 6, GBLoss::MSE)
362 }
363 pub fn effective_shrinkage(&self) -> f64 {
364 self.learning_rate
365 }
366 pub fn n_leaves_upper_bound(&self) -> usize {
367 2usize.pow(self.max_depth as u32)
368 }
369 pub fn is_regularized(&self) -> bool {
370 self.learning_rate < 1.0
371 }
372}
373pub struct KernelMatrix {
375 pub entries: Vec<Vec<f64>>,
377 pub n: usize,
379}
380impl KernelMatrix {
381 pub fn compute(kernel: &KernelFunction, data: &[Vec<f64>]) -> Self {
383 let n = data.len();
384 let entries: Vec<Vec<f64>> = (0..n)
385 .map(|i| {
386 (0..n)
387 .map(|j| kernel.evaluate(&data[i], &data[j]))
388 .collect()
389 })
390 .collect();
391 Self { entries, n }
392 }
393 pub fn trace(&self) -> f64 {
395 (0..self.n).map(|i| self.entries[i][i]).sum()
396 }
397}
398#[allow(dead_code)]
399#[derive(Debug, Clone, PartialEq)]
400pub enum SVMKernel {
401 Linear,
402 Polynomial(u32),
403 RBF(f64),
404 Sigmoid,
405}
406pub struct KLDivergence;
408impl KLDivergence {
409 pub fn compute(p: &[f64], q: &[f64]) -> f64 {
411 p.iter()
412 .zip(q.iter())
413 .filter(|(&pi, _)| pi > 0.0)
414 .map(|(&pi, &qi)| {
415 if qi == 0.0 {
416 f64::INFINITY
417 } else {
418 pi * (pi / qi).ln()
419 }
420 })
421 .sum()
422 }
423 pub fn is_nonneg(p: &[f64], q: &[f64]) -> bool {
425 Self::compute(p, q) >= -1e-9
426 }
427}
428pub struct VCDimension {
430 pub dim: usize,
432}
433impl VCDimension {
434 pub fn new(dim: usize) -> Self {
436 Self { dim }
437 }
438 pub fn sauer_shelah_bound(&self, m: usize) -> usize {
440 let d = self.dim;
441 let mut bound = 0usize;
442 let mut binom = 1usize;
443 for i in 0..=d.min(m) {
444 if i > 0 {
445 binom = binom * (m - i + 1) / i;
446 }
447 bound = bound.saturating_add(binom);
448 }
449 bound
450 }
451 pub fn can_shatter(&self, m: usize) -> bool {
456 m <= self.dim
457 }
458 pub fn fundamental_theorem_pac(&self) -> bool {
460 self.dim < usize::MAX
461 }
462}
463pub enum KernelType {
465 Linear,
467 Polynomial { degree: u32, offset: f64 },
469 Rbf { sigma: f64 },
471 Laplace { sigma: f64 },
473}
474pub struct RegretBound {
476 pub t: usize,
478 pub d: f64,
480 pub g: f64,
482}
483impl RegretBound {
484 pub fn new(t: usize, d: f64, g: f64) -> Self {
486 Self { t, d, g }
487 }
488 pub fn ogd_bound(&self) -> f64 {
490 self.d * self.g * (2.0 * self.t as f64).sqrt()
491 }
492}
493pub struct SampleComplexity {
498 pub eps: f64,
500 pub delta: f64,
502 pub vc_dim: usize,
504}
505impl SampleComplexity {
506 pub fn new(eps: f64, delta: f64, vc_dim: usize) -> Self {
508 Self { eps, delta, vc_dim }
509 }
510 pub fn compute(&self) -> usize {
512 let d = self.vc_dim as f64;
513 let numerator = d * (d / self.eps + 1.0).ln() + (2.0 / self.delta).ln();
514 (numerator / self.eps).ceil() as usize
515 }
516}
517pub struct KernelSVMTrainer {
521 pub n: usize,
523 pub alphas: Vec<f64>,
525 pub labels: Vec<f64>,
527 pub bias: f64,
529 pub c: f64,
531}
532impl KernelSVMTrainer {
533 pub fn new(n: usize, labels: Vec<f64>, c: f64) -> Self {
535 Self {
536 n,
537 alphas: vec![0.0; n],
538 labels,
539 bias: 0.0,
540 c,
541 }
542 }
543 pub fn decision(&self, kernel_row: &[f64]) -> f64 {
545 self.alphas
546 .iter()
547 .zip(self.labels.iter())
548 .zip(kernel_row.iter())
549 .map(|((a, y), k)| a * y * k)
550 .sum::<f64>()
551 + self.bias
552 }
553 pub fn smo_step(&mut self, i: usize, j: usize, k: &[Vec<f64>]) -> bool {
557 if i == j {
558 return false;
559 }
560 let yi = self.labels[i];
561 let yj = self.labels[j];
562 let fi = self.decision(&k[i]);
563 let fj = self.decision(&k[j]);
564 let ei = fi - yi;
565 let ej = fj - yj;
566 let eta = k[i][i] + k[j][j] - 2.0 * k[i][j];
567 if eta <= 0.0 {
568 return false;
569 }
570 let alpha_j_old = self.alphas[j];
571 let alpha_i_old = self.alphas[i];
572 let (l, h) = if (yi - yj).abs() < 1e-9 {
573 let sum = alpha_i_old + alpha_j_old;
574 ((sum - self.c).max(0.0), sum.min(self.c))
575 } else {
576 let diff = alpha_j_old - alpha_i_old;
577 ((-diff).max(0.0), (self.c - diff).min(self.c))
578 };
579 let alpha_j_new = (alpha_j_old + yj * (ei - ej) / eta).clamp(l, h);
580 if (alpha_j_new - alpha_j_old).abs() < 1e-5 {
581 return false;
582 }
583 let alpha_i_new = alpha_i_old + yi * yj * (alpha_j_old - alpha_j_new);
584 let b1 = self.bias
585 - ei
586 - yi * (alpha_i_new - alpha_i_old) * k[i][i]
587 - yj * (alpha_j_new - alpha_j_old) * k[i][j];
588 let b2 = self.bias
589 - ej
590 - yi * (alpha_i_new - alpha_i_old) * k[i][j]
591 - yj * (alpha_j_new - alpha_j_old) * k[j][j];
592 self.bias = (b1 + b2) / 2.0;
593 self.alphas[i] = alpha_i_new;
594 self.alphas[j] = alpha_j_new;
595 true
596 }
597 pub fn generalization_bound(radius: f64, margin: f64, n: usize) -> f64 {
599 (radius * radius) / (margin * margin * n as f64)
600 }
601}
602pub struct PACLearner {
604 pub eps: f64,
606 pub delta: f64,
608}
609impl PACLearner {
610 pub fn new(eps: f64, delta: f64) -> Self {
612 Self { eps, delta }
613 }
614 pub fn required_samples(&self, vc_dim: usize) -> usize {
616 SampleComplexity::new(self.eps, self.delta, vc_dim).compute()
617 }
618}
619pub struct ELBO {
623 pub kl_term: f64,
625 pub reconstruction_term: f64,
627}
628impl ELBO {
629 pub fn new(reconstruction_term: f64, kl_term: f64) -> Self {
631 Self {
632 kl_term,
633 reconstruction_term,
634 }
635 }
636 pub fn value(&self) -> f64 {
638 self.reconstruction_term - self.kl_term
639 }
640 pub fn compute(q: &[f64], p_joint: &[f64]) -> f64 {
644 q.iter()
645 .zip(p_joint.iter())
646 .filter(|(&qi, _)| qi > 0.0)
647 .map(|(&qi, &pi)| {
648 if pi == 0.0 {
649 f64::NEG_INFINITY
650 } else {
651 qi * (pi / qi).ln()
652 }
653 })
654 .sum()
655 }
656}
657pub struct TikhonovReg {
659 pub lambda: f64,
661}
662impl TikhonovReg {
663 pub fn new(lambda: f64) -> Self {
665 Self { lambda }
666 }
667 pub fn solve(&self, x: &[Vec<f64>], y: &[f64]) -> Vec<f64> {
670 let d = if x.is_empty() { 0 } else { x[0].len() };
671 let n = x.len();
672 let mut xtx = vec![vec![0.0f64; d]; d];
673 for i in 0..d {
674 xtx[i][i] = self.lambda;
675 }
676 for row in x {
677 for i in 0..d {
678 for j in 0..d {
679 xtx[i][j] += row[i] * row[j];
680 }
681 }
682 }
683 let mut xty = vec![0.0f64; d];
684 for (row, &yi) in x.iter().zip(y.iter()) {
685 for j in 0..d {
686 xty[j] += row[j] * yi;
687 }
688 }
689 gauss_solve(&xtx, &xty, d, n)
690 }
691 pub fn penalty(&self, w: &[f64]) -> f64 {
693 self.lambda * w.iter().map(|wi| wi * wi).sum::<f64>()
694 }
695}
696pub struct CausalBackdoor {
701 pub cond_probs: Vec<f64>,
703 pub stratum_probs: Vec<f64>,
705}
706impl CausalBackdoor {
707 pub fn new(cond_probs: Vec<f64>, stratum_probs: Vec<f64>) -> Self {
709 Self {
710 cond_probs,
711 stratum_probs,
712 }
713 }
714 pub fn adjust(&self) -> f64 {
718 self.cond_probs
719 .iter()
720 .zip(self.stratum_probs.iter())
721 .map(|(p, q)| p * q)
722 .sum()
723 }
724 pub fn confounding_bias(&self, observational: f64) -> f64 {
726 (observational - self.adjust()).abs()
727 }
728 pub fn is_valid(&self) -> bool {
730 let sum: f64 = self.stratum_probs.iter().sum();
731 (sum - 1.0).abs() < 1e-6
732 }
733}
734pub struct DoubleRademacher {
736 pub rademacher: RademacherComplexity,
738}
739impl DoubleRademacher {
740 pub fn new(n: usize, class_size: usize) -> Self {
742 Self {
743 rademacher: RademacherComplexity::new(n, class_size),
744 }
745 }
746 pub fn two_sided_bound(&self) -> f64 {
748 2.0 * self.rademacher.upper_bound()
749 }
750}
751pub struct Perceptron {
753 pub weights: Vec<f64>,
755 pub bias: f64,
757 pub mistakes: usize,
759}
760impl Perceptron {
761 pub fn new(dim: usize) -> Self {
763 Self {
764 weights: vec![0.0; dim],
765 bias: 0.0,
766 mistakes: 0,
767 }
768 }
769 pub fn predict(&self, x: &[f64]) -> f64 {
771 let score = dot(&self.weights, x) + self.bias;
772 if score >= 0.0 {
773 1.0
774 } else {
775 -1.0
776 }
777 }
778 pub fn update(&mut self, x: &[f64], label: f64) -> bool {
780 let pred = self.predict(x);
781 if (pred * label) <= 0.0 {
782 for (wi, &xi) in self.weights.iter_mut().zip(x.iter()) {
783 *wi += label * xi;
784 }
785 self.bias += label;
786 self.mistakes += 1;
787 true
788 } else {
789 false
790 }
791 }
792 pub fn mistake_bound(radius: f64, margin: f64) -> usize {
794 ((radius / margin).powi(2)).ceil() as usize
795 }
796}
797pub struct GaussianComplexity {
799 pub n: usize,
801 pub class_size: usize,
803}
804impl GaussianComplexity {
805 pub fn new(n: usize, class_size: usize) -> Self {
807 Self { n, class_size }
808 }
809 pub fn upper_bound(&self) -> f64 {
811 (2.0 * (self.class_size as f64).ln() / self.n as f64).sqrt()
812 }
813}
814#[allow(dead_code)]
815#[derive(Debug, Clone, PartialEq)]
816pub enum GBLoss {
817 MSE,
818 MAE,
819 LogLoss,
820 Huber(f64),
821}
822pub struct LassoReg {
824 pub lambda: f64,
826}
827impl LassoReg {
828 pub fn new(lambda: f64) -> Self {
830 Self { lambda }
831 }
832 pub fn soft_threshold(&self, w: &[f64]) -> Vec<f64> {
834 w.iter()
835 .map(|&wi| {
836 let abs_wi = wi.abs();
837 if abs_wi <= self.lambda {
838 0.0
839 } else {
840 wi.signum() * (abs_wi - self.lambda)
841 }
842 })
843 .collect()
844 }
845 pub fn penalty(&self, w: &[f64]) -> f64 {
847 self.lambda * w.iter().map(|wi| wi.abs()).sum::<f64>()
848 }
849}
850pub struct UCBBandit {
854 pub n: usize,
856 pub counts: Vec<usize>,
858 pub means: Vec<f64>,
860 pub t: usize,
862}
863impl UCBBandit {
864 pub fn new(n: usize) -> Self {
866 Self {
867 n,
868 counts: vec![0; n],
869 means: vec![0.0; n],
870 t: 0,
871 }
872 }
873 pub fn select(&self) -> usize {
878 if let Some(i) = self.counts.iter().position(|&c| c == 0) {
879 return i;
880 }
881 let ln_t = (self.t as f64).ln();
882 (0..self.n)
883 .max_by(|&i, &j| {
884 let ucb_i = self.means[i] + (2.0 * ln_t / self.counts[i] as f64).sqrt();
885 let ucb_j = self.means[j] + (2.0 * ln_t / self.counts[j] as f64).sqrt();
886 ucb_i
887 .partial_cmp(&ucb_j)
888 .unwrap_or(std::cmp::Ordering::Equal)
889 })
890 .unwrap_or(0)
891 }
892 pub fn update(&mut self, arm: usize, reward: f64) {
894 self.counts[arm] += 1;
895 let n = self.counts[arm] as f64;
896 self.means[arm] += (reward - self.means[arm]) / n;
897 self.t += 1;
898 }
899 pub fn regret_bound_upper(&self) -> f64 {
901 let t = self.t as f64;
902 let n = self.n as f64;
903 (n * t * t.ln()).sqrt()
904 }
905}
906pub struct PACBayesGeneralization {
910 pub kl_qp: f64,
912 pub n: usize,
914 pub delta: f64,
916}
917impl PACBayesGeneralization {
918 pub fn new(kl_qp: f64, n: usize, delta: f64) -> Self {
920 Self { kl_qp, n, delta }
921 }
922 pub fn mcallester_bound(&self, empirical_loss: f64) -> f64 {
924 let penalty =
925 ((self.kl_qp + (self.n as f64 / self.delta).ln()) / (2.0 * self.n as f64)).sqrt();
926 empirical_loss + penalty
927 }
928 pub fn catoni_bound(&self, empirical_loss: f64, lambda: f64) -> f64 {
931 let scale = 1.0 / (1.0 - lambda / 2.0);
932 let penalty = self.kl_qp / (2.0 * lambda * self.n as f64);
933 scale * (empirical_loss + penalty)
934 }
935 pub fn optimal_lambda(&self, empirical_loss: f64) -> f64 {
937 let ratio = self.n as f64 * empirical_loss / (self.kl_qp.max(1e-9));
938 1.0 / (1.0 + ratio).sqrt()
939 }
940}
941pub struct ExponentialWeightsAlgorithm {
946 pub n: usize,
948 pub eta: f64,
950 pub weights: Vec<f64>,
952 pub rounds: usize,
954}
955impl ExponentialWeightsAlgorithm {
956 pub fn new(n: usize, eta: f64) -> Self {
958 Self {
959 n,
960 eta,
961 weights: vec![1.0; n],
962 rounds: 0,
963 }
964 }
965 pub fn distribution(&self) -> Vec<f64> {
967 let total: f64 = self.weights.iter().sum();
968 self.weights.iter().map(|w| w / total).collect()
969 }
970 pub fn update(&mut self, losses: &[f64]) {
972 for (w, &l) in self.weights.iter_mut().zip(losses.iter()) {
973 *w *= (-self.eta * l).exp();
974 }
975 self.rounds += 1;
976 }
977 pub fn regret_bound(&self) -> f64 {
979 let t = self.rounds as f64;
980 (self.n as f64).ln() / self.eta + self.eta * t / 2.0
981 }
982 pub fn optimal_eta(n: usize, t: usize) -> f64 {
984 (2.0 * (n as f64).ln() / t as f64).sqrt()
985 }
986}
987pub struct FeatureMap {
989 pub feature_dim: usize,
991}
992impl FeatureMap {
993 pub fn new(feature_dim: usize) -> Self {
995 Self { feature_dim }
996 }
997 pub fn inner_product(&self, x: &[f64], y: &[f64]) -> f64 {
1000 dot(x, y)
1001 }
1002}
1003pub struct KernelSVM {
1005 pub alphas: Vec<f64>,
1007 pub labels: Vec<f64>,
1009 pub bias: f64,
1011 pub c: f64,
1013}
1014impl KernelSVM {
1015 pub fn new(n: usize, c: f64) -> Self {
1017 Self {
1018 alphas: vec![0.0; n],
1019 labels: vec![1.0; n],
1020 bias: 0.0,
1021 c,
1022 }
1023 }
1024 pub fn predict(&self, kernel_vals: &[f64]) -> f64 {
1026 let sum: f64 = self
1027 .alphas
1028 .iter()
1029 .zip(self.labels.iter())
1030 .zip(kernel_vals.iter())
1031 .map(|((a, y), k)| a * y * k)
1032 .sum();
1033 sum + self.bias
1034 }
1035}
1036#[allow(dead_code)]
1038#[derive(Debug, Clone)]
1039pub struct GaussianProcess {
1040 pub mean: f64,
1041 pub length_scale: f64,
1042 pub signal_variance: f64,
1043 pub noise_variance: f64,
1044}
1045#[allow(dead_code)]
1046impl GaussianProcess {
1047 pub fn new(mean: f64, length_scale: f64, signal_var: f64, noise_var: f64) -> Self {
1048 GaussianProcess {
1049 mean,
1050 length_scale,
1051 signal_variance: signal_var,
1052 noise_variance: noise_var,
1053 }
1054 }
1055 pub fn default_rbf() -> Self {
1056 GaussianProcess::new(0.0, 1.0, 1.0, 0.01)
1057 }
1058 pub fn rbf_kernel(&self, x: f64, xp: f64) -> f64 {
1060 let d = x - xp;
1061 self.signal_variance * (-d * d / (2.0 * self.length_scale.powi(2))).exp()
1062 }
1063 pub fn predictive_variance(&self, x: f64, train_x: &[f64]) -> f64 {
1065 let k_star_star = self.rbf_kernel(x, x);
1066 let k_noise: Vec<f64> = train_x.iter().map(|&xi| self.rbf_kernel(x, xi)).collect();
1067 let contrib: f64 = k_noise.iter().map(|&k| k * k).sum::<f64>()
1068 / (self.signal_variance + self.noise_variance).max(1e-10);
1069 (k_star_star - contrib).max(self.noise_variance)
1070 }
1071 pub fn log_marginal_likelihood_approx(&self, n: usize) -> f64 {
1072 -(n as f64) / 2.0 * (2.0 * std::f64::consts::PI * self.signal_variance).ln()
1073 }
1074}
1075pub struct KernelFunction {
1077 pub kernel_type: KernelType,
1079}
1080impl KernelFunction {
1081 pub fn new(kernel_type: KernelType) -> Self {
1083 Self { kernel_type }
1084 }
1085 pub fn evaluate(&self, x: &[f64], y: &[f64]) -> f64 {
1087 match &self.kernel_type {
1088 KernelType::Linear => dot(x, y),
1089 KernelType::Polynomial { degree, offset } => (dot(x, y) + offset).powi(*degree as i32),
1090 KernelType::Rbf { sigma } => {
1091 let diff_sq: f64 = x.iter().zip(y).map(|(a, b)| (a - b).powi(2)).sum();
1092 (-diff_sq / (2.0 * sigma * sigma)).exp()
1093 }
1094 KernelType::Laplace { sigma } => {
1095 let diff_norm: f64 = x.iter().zip(y).map(|(a, b)| (a - b).abs()).sum();
1096 (-diff_norm / sigma).exp()
1097 }
1098 }
1099 }
1100 pub fn is_positive_definite(&self, points: &[Vec<f64>]) -> bool {
1103 let n = points.len();
1104 let mut k: Vec<Vec<f64>> = (0..n)
1105 .map(|i| {
1106 (0..n)
1107 .map(|j| self.evaluate(&points[i], &points[j]))
1108 .collect()
1109 })
1110 .collect();
1111 for i in 0..n {
1112 for j in 0..i {
1113 let mut sum = k[i][j];
1114 for l in 0..j {
1115 sum -= k[i][l] * k[j][l];
1116 }
1117 k[i][j] = sum / k[j][j];
1118 }
1119 let mut diag = k[i][i];
1120 for l in 0..i {
1121 diag -= k[i][l] * k[i][l];
1122 }
1123 if diag < -1e-9 {
1124 return false;
1125 }
1126 k[i][i] = diag.max(0.0).sqrt();
1127 }
1128 true
1129 }
1130}