1use crate::{activation::Activation, NeuralResult};
15use scirs2_core::ndarray::{Array1, Array2, Axis, ScalarOperand};
16use scirs2_core::random::{thread_rng, Normal};
17use sklears_core::types::FloatBounds;
18use std::f64::consts::PI;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26pub enum CouplingType {
27 Additive,
29 Affine,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub enum MaskType {
37 Checkerboard,
39 Channelwise,
41 Alternating,
43}
44
45#[derive(Debug, Clone)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48pub struct CouplingLayerConfig {
49 pub input_dim: usize,
51 pub hidden_dims: Vec<usize>,
53 pub coupling_type: CouplingType,
55 pub mask_type: MaskType,
57 pub reverse_mask: bool,
59 pub activation: String,
61}
62
63impl Default for CouplingLayerConfig {
64 fn default() -> Self {
65 Self {
66 input_dim: 784,
67 hidden_dims: vec![256, 256],
68 coupling_type: CouplingType::Affine,
69 mask_type: MaskType::Checkerboard,
70 reverse_mask: false,
71 activation: "relu".to_string(),
72 }
73 }
74}
75
76#[derive(Debug)]
82#[allow(dead_code)] pub struct AffineCouplingLayer<T: FloatBounds> {
84 input_dim: usize,
86 scale_weights: Vec<Array2<T>>,
88 scale_biases: Vec<Array1<T>>,
90 translation_weights: Vec<Array2<T>>,
92 translation_biases: Vec<Array1<T>>,
94 mask: Array1<bool>,
96 activation: Activation,
98 is_affine: bool,
100 cached_input: Option<Array2<T>>,
102 cached_scale: Option<Array2<T>>,
104}
105
106impl<T: FloatBounds> AffineCouplingLayer<T> {
107 pub fn new(config: CouplingLayerConfig) -> Self {
109 let mut rng = thread_rng();
110 let split_point = config.input_dim / 2;
111
112 let mut mask = Array1::from_elem(config.input_dim, false);
114 match config.mask_type {
115 MaskType::Checkerboard | MaskType::Channelwise => {
116 for i in (if config.reverse_mask { split_point } else { 0 })
117 ..(if config.reverse_mask {
118 config.input_dim
119 } else {
120 split_point
121 })
122 {
123 mask[i] = true;
124 }
125 }
126 MaskType::Alternating => {
127 for i in 0..config.input_dim {
128 if config.reverse_mask {
129 mask[i] = i % 2 == 1;
130 } else {
131 mask[i] = i % 2 == 0;
132 }
133 }
134 }
135 }
136
137 let n_masked = mask.iter().filter(|&&x| x).count();
139 let n_unmasked = config.input_dim - n_masked;
140
141 let mut scale_weights = Vec::new();
143 let mut scale_biases = Vec::new();
144
145 let mut prev_dim = n_masked;
146 for &hidden_dim in &config.hidden_dims {
147 let std = T::from((2.0 / prev_dim as f64).sqrt()).unwrap_or_else(|| T::zero());
148 let w = Array2::from_shape_fn((prev_dim, hidden_dim), |_| {
149 T::from(
150 rng.sample::<f64, _>(
151 Normal::new(0.0, 1.0).expect("standard normal should be valid"),
152 ) * std.to_f64().unwrap_or(0.0),
153 )
154 .expect("value should be present")
155 });
156 let b = Array1::zeros(hidden_dim);
157 scale_weights.push(w);
158 scale_biases.push(b);
159 prev_dim = hidden_dim;
160 }
161
162 let w = Array2::from_shape_fn((prev_dim, n_unmasked), |_| {
164 T::from(
165 rng.sample::<f64, _>(Normal::new(0.0, 1.0).expect("valid distribution params"))
166 * 0.01,
167 )
168 .unwrap_or_else(|| T::zero())
169 });
170 let b = Array1::zeros(n_unmasked);
171 scale_weights.push(w);
172 scale_biases.push(b);
173
174 let mut translation_weights = Vec::new();
176 let mut translation_biases = Vec::new();
177
178 let mut prev_dim = n_masked;
179 for &hidden_dim in &config.hidden_dims {
180 let std = T::from((2.0 / prev_dim as f64).sqrt()).unwrap_or_else(|| T::zero());
181 let w = Array2::from_shape_fn((prev_dim, hidden_dim), |_| {
182 T::from(
183 rng.sample::<f64, _>(
184 Normal::new(0.0, 1.0).expect("standard normal should be valid"),
185 ) * std.to_f64().unwrap_or(0.0),
186 )
187 .expect("value should be present")
188 });
189 let b = Array1::zeros(hidden_dim);
190 translation_weights.push(w);
191 translation_biases.push(b);
192 prev_dim = hidden_dim;
193 }
194
195 let w = Array2::from_shape_fn((prev_dim, n_unmasked), |_| {
197 T::from(
198 rng.sample::<f64, _>(Normal::new(0.0, 1.0).expect("valid distribution params"))
199 * 0.01,
200 )
201 .unwrap_or_else(|| T::zero())
202 });
203 let b = Array1::zeros(n_unmasked);
204 translation_weights.push(w);
205 translation_biases.push(b);
206
207 let activation = match config.activation.as_str() {
208 "relu" => Activation::Relu,
209 "tanh" => Activation::Tanh,
210 "sigmoid" | "logistic" => Activation::Logistic,
211 "elu" => Activation::Elu,
212 _ => Activation::Relu,
213 };
214
215 Self {
216 input_dim: config.input_dim,
217 scale_weights,
218 scale_biases,
219 translation_weights,
220 translation_biases,
221 mask,
222 activation,
223 is_affine: matches!(config.coupling_type, CouplingType::Affine),
224 cached_input: None,
225 cached_scale: None,
226 }
227 }
228
229 pub fn forward(&mut self, x: &Array2<T>) -> NeuralResult<(Array2<T>, T)> {
231 let batch_size = x.nrows();
232
233 let masked_indices: Vec<_> = self
235 .mask
236 .iter()
237 .enumerate()
238 .filter(|(_, &m)| m)
239 .map(|(i, _)| i)
240 .collect();
241 let unmasked_indices: Vec<_> = self
242 .mask
243 .iter()
244 .enumerate()
245 .filter(|(_, &m)| !m)
246 .map(|(i, _)| i)
247 .collect();
248
249 let x_masked = Array2::from_shape_fn((batch_size, masked_indices.len()), |(i, j)| {
250 x[[i, masked_indices[j]]]
251 });
252 let x_unmasked = Array2::from_shape_fn((batch_size, unmasked_indices.len()), |(i, j)| {
253 x[[i, unmasked_indices[j]]]
254 });
255
256 let scale = self.compute_scale(&x_masked)?;
258 let translation = self.compute_translation(&x_masked)?;
259
260 let y_unmasked = if self.is_affine {
262 let exp_scale = scale.mapv(|s| s.exp());
264 &x_unmasked * &exp_scale + &translation
265 } else {
266 &x_unmasked + &translation
268 };
269
270 let mut y = x.clone();
272 for (i, &idx) in unmasked_indices.iter().enumerate() {
273 for j in 0..batch_size {
274 y[[j, idx]] = y_unmasked[[j, i]];
275 }
276 }
277
278 let log_det = if self.is_affine {
280 scale.sum_axis(Axis(1)).sum()
282 } else {
283 T::zero()
285 };
286
287 self.cached_input = Some(x.clone());
289 self.cached_scale = Some(scale);
290
291 Ok((y, log_det))
292 }
293
294 pub fn inverse(&self, y: &Array2<T>) -> NeuralResult<Array2<T>> {
296 let batch_size = y.nrows();
297
298 let masked_indices: Vec<_> = self
300 .mask
301 .iter()
302 .enumerate()
303 .filter(|(_, &m)| m)
304 .map(|(i, _)| i)
305 .collect();
306 let unmasked_indices: Vec<_> = self
307 .mask
308 .iter()
309 .enumerate()
310 .filter(|(_, &m)| !m)
311 .map(|(i, _)| i)
312 .collect();
313
314 let y_masked = Array2::from_shape_fn((batch_size, masked_indices.len()), |(i, j)| {
315 y[[i, masked_indices[j]]]
316 });
317 let y_unmasked = Array2::from_shape_fn((batch_size, unmasked_indices.len()), |(i, j)| {
318 y[[i, unmasked_indices[j]]]
319 });
320
321 let scale = self.compute_scale(&y_masked)?;
323 let translation = self.compute_translation(&y_masked)?;
324
325 let x_unmasked = if self.is_affine {
327 let exp_neg_scale = scale.mapv(|s| (-s).exp());
329 (&y_unmasked - &translation) * &exp_neg_scale
330 } else {
331 &y_unmasked - &translation
333 };
334
335 let mut x = y.clone();
337 for (i, &idx) in unmasked_indices.iter().enumerate() {
338 for j in 0..batch_size {
339 x[[j, idx]] = x_unmasked[[j, i]];
340 }
341 }
342
343 Ok(x)
344 }
345
346 fn compute_scale(&self, x: &Array2<T>) -> NeuralResult<Array2<T>> {
348 let mut h = x.clone();
349
350 for (i, (w, b)) in self
352 .scale_weights
353 .iter()
354 .zip(self.scale_biases.iter())
355 .enumerate()
356 {
357 h = h.dot(w) + b;
358
359 if i < self.scale_weights.len() - 1 {
361 h.mapv_inplace(|x| {
362 let x_f64 = x.to_f64().unwrap_or(0.0);
363 T::from(self.activation.forward(x_f64)).unwrap_or_else(|| T::zero())
364 });
365 }
366 }
367
368 h.mapv_inplace(|s| {
370 let s_f64 = s.to_f64().unwrap_or(0.0);
371 T::from(s_f64.clamp(-10.0, 10.0)).unwrap_or_else(|| T::zero())
372 });
373
374 Ok(h)
375 }
376
377 fn compute_translation(&self, x: &Array2<T>) -> NeuralResult<Array2<T>> {
379 let mut h = x.clone();
380
381 for (i, (w, b)) in self
383 .translation_weights
384 .iter()
385 .zip(self.translation_biases.iter())
386 .enumerate()
387 {
388 h = h.dot(w) + b;
389
390 if i < self.translation_weights.len() - 1 {
392 h.mapv_inplace(|x| {
393 let x_f64 = x.to_f64().unwrap_or(0.0);
394 T::from(self.activation.forward(x_f64)).unwrap_or_else(|| T::zero())
395 });
396 }
397 }
398
399 Ok(h)
400 }
401
402 pub fn num_parameters(&self) -> usize {
404 let scale_params: usize = self.scale_weights.iter().map(|w| w.len()).sum::<usize>()
405 + self.scale_biases.iter().map(|b| b.len()).sum::<usize>();
406
407 let translation_params: usize = self
408 .translation_weights
409 .iter()
410 .map(|w| w.len())
411 .sum::<usize>()
412 + self
413 .translation_biases
414 .iter()
415 .map(|b| b.len())
416 .sum::<usize>();
417
418 scale_params + translation_params
419 }
420}
421
422#[derive(Debug, Clone)]
424#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
425pub struct NormalizingFlowConfig {
426 pub input_dim: usize,
428 pub n_flows: usize,
430 pub hidden_dims: Vec<usize>,
432 pub coupling_type: CouplingType,
434 pub base_mean: f64,
436 pub base_std: f64,
438 pub learning_rate: f64,
440 pub n_iterations: usize,
442 pub batch_size: usize,
444}
445
446impl Default for NormalizingFlowConfig {
447 fn default() -> Self {
448 Self {
449 input_dim: 784,
450 n_flows: 8,
451 hidden_dims: vec![256, 256],
452 coupling_type: CouplingType::Affine,
453 base_mean: 0.0,
454 base_std: 1.0,
455 learning_rate: 0.001,
456 n_iterations: 1000,
457 batch_size: 128,
458 }
459 }
460}
461
462pub struct NormalizingFlow<T: FloatBounds> {
467 coupling_layers: Vec<AffineCouplingLayer<T>>,
469 base_mean: T,
471 base_std: T,
472 input_dim: usize,
474 config: NormalizingFlowConfig,
476}
477
478impl<T: FloatBounds + ScalarOperand> NormalizingFlow<T> {
479 pub fn new(config: NormalizingFlowConfig) -> Self {
481 let mut coupling_layers = Vec::new();
482
483 for i in 0..config.n_flows {
484 let layer_config = CouplingLayerConfig {
485 input_dim: config.input_dim,
486 hidden_dims: config.hidden_dims.clone(),
487 coupling_type: config.coupling_type,
488 mask_type: MaskType::Checkerboard,
489 reverse_mask: i % 2 == 1, activation: "relu".to_string(),
491 };
492 coupling_layers.push(AffineCouplingLayer::new(layer_config));
493 }
494
495 Self {
496 coupling_layers,
497 base_mean: T::from(config.base_mean).unwrap_or_else(|| T::zero()),
498 base_std: T::from(config.base_std).unwrap_or_else(|| T::zero()),
499 input_dim: config.input_dim,
500 config,
501 }
502 }
503
504 pub fn forward(&mut self, x: &Array2<T>) -> NeuralResult<(Array2<T>, T)> {
506 let mut z = x.clone();
507 let mut log_det_sum = T::zero();
508
509 for layer in &mut self.coupling_layers {
511 let (z_new, log_det) = layer.forward(&z)?;
512 z = z_new;
513 log_det_sum += log_det;
514 }
515
516 Ok((z, log_det_sum))
517 }
518
519 pub fn inverse(&self, z: &Array2<T>) -> NeuralResult<Array2<T>> {
521 let mut x = z.clone();
522
523 for layer in self.coupling_layers.iter().rev() {
525 x = layer.inverse(&x)?;
526 }
527
528 Ok(x)
529 }
530
531 pub fn sample(&self, n_samples: usize) -> NeuralResult<Array2<T>> {
533 let mut rng = thread_rng();
534 let normal = Normal::new(
535 self.base_mean.to_f64().unwrap_or(0.0),
536 self.base_std.to_f64().unwrap_or(0.0),
537 )
538 .expect("value should be present");
539
540 let z = Array2::from_shape_fn((n_samples, self.input_dim), |_| {
542 T::from(rng.sample::<f64, _>(normal)).unwrap_or_else(|| T::zero())
543 });
544
545 self.inverse(&z)
547 }
548
549 pub fn log_likelihood(&mut self, x: &Array2<T>) -> NeuralResult<T> {
551 let (z, log_det) = self.forward(x)?;
552
553 let z_normalized = (&z - self.base_mean) / self.base_std;
555 let log_prob_base = z_normalized.mapv(|zi| {
556 let zi_f64 = zi.to_f64().unwrap_or(0.0);
557 T::from(-0.5 * zi_f64 * zi_f64 - 0.5 * (2.0 * PI).ln()).unwrap_or_else(|| T::zero())
558 });
559
560 let log_prob_sum = log_prob_base.sum();
561
562 let log_likelihood = log_prob_sum + log_det;
564
565 Ok(log_likelihood)
566 }
567
568 pub fn num_parameters(&self) -> usize {
570 self.coupling_layers
571 .iter()
572 .map(|layer| layer.num_parameters())
573 .sum()
574 }
575
576 pub fn config(&self) -> &NormalizingFlowConfig {
578 &self.config
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use super::*;
585 use approx::assert_relative_eq;
586
587 #[test]
588 fn test_coupling_layer_creation() {
589 let config = CouplingLayerConfig {
590 input_dim: 10,
591 hidden_dims: vec![32, 32],
592 coupling_type: CouplingType::Affine,
593 mask_type: MaskType::Checkerboard,
594 reverse_mask: false,
595 activation: "relu".to_string(),
596 };
597
598 let layer: AffineCouplingLayer<f64> = AffineCouplingLayer::new(config);
599 assert_eq!(layer.input_dim, 10);
600 assert!(layer.num_parameters() > 0);
601 }
602
603 #[test]
604 fn test_coupling_layer_forward_backward() {
605 let config = CouplingLayerConfig {
606 input_dim: 4,
607 hidden_dims: vec![8],
608 coupling_type: CouplingType::Affine,
609 mask_type: MaskType::Checkerboard,
610 reverse_mask: false,
611 activation: "relu".to_string(),
612 };
613
614 let mut layer: AffineCouplingLayer<f64> = AffineCouplingLayer::new(config);
615 let x = Array2::from_shape_vec((2, 4), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
616 .expect("array shape mismatch");
617
618 let (y, _log_det) = layer.forward(&x).expect("forward pass should succeed");
619 let x_reconstructed = layer.inverse(&y).expect("operation should succeed");
620
621 for i in 0..x.nrows() {
623 for j in 0..x.ncols() {
624 assert_relative_eq!(x[[i, j]], x_reconstructed[[i, j]], epsilon = 1e-5);
625 }
626 }
627 }
628
629 #[test]
630 fn test_normalizing_flow_creation() {
631 let config = NormalizingFlowConfig {
632 input_dim: 10,
633 n_flows: 4,
634 hidden_dims: vec![32],
635 coupling_type: CouplingType::Affine,
636 base_mean: 0.0,
637 base_std: 1.0,
638 learning_rate: 0.001,
639 n_iterations: 100,
640 batch_size: 32,
641 };
642
643 let flow: NormalizingFlow<f64> = NormalizingFlow::new(config);
644 assert_eq!(flow.input_dim, 10);
645 assert_eq!(flow.coupling_layers.len(), 4);
646 assert!(flow.num_parameters() > 0);
647 }
648
649 #[test]
650 fn test_normalizing_flow_invertibility() {
651 let config = NormalizingFlowConfig {
652 input_dim: 8,
653 n_flows: 3,
654 hidden_dims: vec![16],
655 coupling_type: CouplingType::Affine,
656 ..Default::default()
657 };
658
659 let mut flow: NormalizingFlow<f64> = NormalizingFlow::new(config);
660 let x = Array2::from_shape_fn((5, 8), |(i, j)| {
661 (i as f64 + 1.0) * 0.1 + (j as f64 + 1.0) * 0.01
662 });
663
664 let (z, _log_det) = flow.forward(&x).expect("forward pass should succeed");
665 let x_reconstructed = flow.inverse(&z).expect("operation should succeed");
666
667 for i in 0..x.nrows() {
669 for j in 0..x.ncols() {
670 assert_relative_eq!(x[[i, j]], x_reconstructed[[i, j]], epsilon = 1e-4);
671 }
672 }
673 }
674
675 #[test]
676 fn test_normalizing_flow_sampling() {
677 let config = NormalizingFlowConfig {
678 input_dim: 5,
679 n_flows: 2,
680 hidden_dims: vec![10],
681 coupling_type: CouplingType::Additive,
682 ..Default::default()
683 };
684
685 let flow: NormalizingFlow<f64> = NormalizingFlow::new(config);
686 let samples = flow.sample(10).expect("sampling should succeed");
687
688 assert_eq!(samples.nrows(), 10);
689 assert_eq!(samples.ncols(), 5);
690 }
691
692 #[test]
693 fn test_normalizing_flow_log_likelihood() {
694 let config = NormalizingFlowConfig {
695 input_dim: 6,
696 n_flows: 2,
697 hidden_dims: vec![12],
698 coupling_type: CouplingType::Affine,
699 ..Default::default()
700 };
701
702 let mut flow: NormalizingFlow<f64> = NormalizingFlow::new(config);
703 let x = Array2::from_shape_fn((3, 6), |(i, j)| (i as f64 + j as f64) * 0.1);
704
705 let log_likelihood = flow.log_likelihood(&x).expect("operation should succeed");
706 assert!(log_likelihood.is_finite());
708 }
709
710 #[test]
711 fn test_additive_coupling() {
712 let config = CouplingLayerConfig {
713 input_dim: 4,
714 hidden_dims: vec![8],
715 coupling_type: CouplingType::Additive,
716 mask_type: MaskType::Checkerboard,
717 reverse_mask: false,
718 activation: "relu".to_string(),
719 };
720
721 let mut layer: AffineCouplingLayer<f64> = AffineCouplingLayer::new(config);
722 let x =
723 Array2::from_shape_vec((1, 4), vec![1.0, 2.0, 3.0, 4.0]).expect("array shape mismatch");
724
725 let (y, log_det) = layer.forward(&x).expect("forward pass should succeed");
726
727 assert_relative_eq!(log_det, 0.0, epsilon = 1e-10);
729
730 let x_reconstructed = layer.inverse(&y).expect("operation should succeed");
732 for i in 0..x.len() {
733 assert_relative_eq!(x[[0, i]], x_reconstructed[[0, i]], epsilon = 1e-5);
734 }
735 }
736
737 #[test]
738 fn test_mask_alternation() {
739 let config1 = CouplingLayerConfig {
740 input_dim: 6,
741 hidden_dims: vec![8],
742 coupling_type: CouplingType::Affine,
743 mask_type: MaskType::Alternating,
744 reverse_mask: false,
745 activation: "relu".to_string(),
746 };
747
748 let config2 = CouplingLayerConfig {
749 input_dim: 6,
750 hidden_dims: vec![8],
751 coupling_type: CouplingType::Affine,
752 mask_type: MaskType::Alternating,
753 reverse_mask: true,
754 activation: "relu".to_string(),
755 };
756
757 let layer1: AffineCouplingLayer<f64> = AffineCouplingLayer::new(config1);
758 let layer2: AffineCouplingLayer<f64> = AffineCouplingLayer::new(config2);
759
760 let mask1_true = layer1.mask.iter().filter(|&&x| x).count();
762 let mask2_true = layer2.mask.iter().filter(|&&x| x).count();
763
764 assert!((2..=4).contains(&mask1_true));
766 assert!((2..=4).contains(&mask2_true));
767 }
768}