1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! # Independent Component Analysis (ICA)
//!
//! `linfa-ica` aims to provide pure Rust implementations of ICA algorithms.
//!
//! ICA separates mutivariate signals into their additive, independent subcomponents.
//! ICA is primarily used for separating superimposed signals and not for dimensionality
//! reduction.
//!
//! Input data is whitened (remove underlying correlation) before modeling.
//!
//! ## The Big Picture
//!
//! `linfa-ica` is a crate in the [`linfa`](https://crates.io/crates/linfa) ecosystem,
//! an effort to create a toolkit for classical Machine Learning implemented in pure Rust,
//! akin to Python's `scikit-learn`.
//!
//! ## Current state
//!
//! `linfa-ica` currently provides an implementation of the following methods:
//!
//! - Fast Independent Component Analysis (Fast ICA)
//!
//! ## Example
//!
//! Here's an example of ICA unmixing the mixture of two signals
//!
//! ```
//! use linfa::{
//!     dataset::DatasetBase,
//!     traits::{Fit, Predict},
//! };
//! use linfa_ica::fast_ica::{FastIca, GFunc};
//! use ndarray::{array, concatenate};
//! use ndarray::{Array, Array2, Axis};
//! use ndarray_npy::write_npy;
//! use ndarray_rand::{rand::SeedableRng, rand_distr::Uniform, RandomExt};
//! use rand_xoshiro::Xoshiro256Plus;
//!
//! let nsamples = 2000;
//! // Creating a sine wave signal
//! let source1 = Array::linspace(0., 8., nsamples).mapv(|x| (2f64 * x).sin());
//! // Creating a sawtooth signal
//! let source2 = Array::linspace(0., 8., nsamples).mapv(|x| {
//!     let tmp = (4f64 * x).sin();
//!     if tmp > 0. {
//!         return 1.;
//!     }
//!     -1.
//! });
//!
//! // Column concatenating both the signals
//! let mut sources_original = concatenate![
//!     Axis(1),
//!     source1.insert_axis(Axis(1)),
//!     source2.insert_axis(Axis(1))
//! ];
//!
//! // Adding random noise to the signals
//! let mut rng = Xoshiro256Plus::seed_from_u64(42);
//! sources_original +=
//!     &Array::random_using((2000, 2), Uniform::new(0.0, 1.0), &mut rng).mapv(|x| x * 0.2);
//!
//! // Mixing the two signals
//! let mixing = array![[1., 1.], [0.5, 2.]];
//! // Shape of the data is (2000 x 2)
//! // This data will be unmixed by ICA to recover back the original signals
//! let sources_mixed = sources_original.dot(&mixing.t());
//!
//! // Fitting the model
//! // We set the G function used in the approximation of neg-entropy as logcosh
//! // with its alpha value as 1
//! // `ncomponents` is not set, it will be automatically be assigned 2 from
//! // the input
//! let ica = FastIca::params().gfunc(GFunc::Logcosh(1.0));
//! let ica = ica.fit(&DatasetBase::from(sources_mixed.view())).unwrap();
//!
//! // Here we unmix the data to recover back the original signals
//! let sources_ica = ica.predict(&sources_mixed);
//! ```

#[macro_use]
extern crate ndarray;

pub mod error;
pub mod fast_ica;
pub mod hyperparams;