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