Picard
Preconditioned ICA for Real Data — A fast and robust Independent Component Analysis implementation in Rust.
Based on the paper:
Pierre Ablin, Jean-François Cardoso, Alexandre Gramfort. "Faster independent component analysis by preconditioning with Hessian approximations" IEEE Transactions on Signal Processing, 2018 https://arxiv.org/abs/1706.08171
Features
- Fast convergence: Uses L-BFGS with Hessian preconditioning for faster convergence than FastICA
- Robust: Works well on real data where ICA assumptions don't perfectly hold
- Orthogonal mode (Picard-O): Enforces orthogonal unmixing matrix for whitened data
- Extended mode: Handles both sub-Gaussian and super-Gaussian sources automatically
- Multiple density functions: Tanh (default), Exp, and Cube for different source distributions
- Warm start options: FastICA or JADE initialization for improved convergence
- Comprehensive output: Access to unmixing matrices, sources, convergence info, and more
Installation
Add to your Cargo.toml:
[]
= "0.1"
= "0.15"
You'll also need a BLAS backend for ndarray-linalg. For example, with OpenBLAS:
[]
= { = "0.16", = ["openblas-system"] }
Quick Start
use ;
use Array2;
// Your data matrix: (n_features × n_samples)
let x: = /* ... */;
// Fit ICA with default settings
let result = fit?;
// Access results
let sources = &result.sources; // Estimated sources (n_components × n_samples)
let unmixing = &result.unmixing; // Unmixing matrix W
let converged = result.converged; // Whether algorithm converged
let n_iter = result.n_iterations; // Number of iterations
// Get the full unmixing matrix (W @ K if whitening was used)
let full_w = result.full_unmixing;
// Get the mixing matrix (pseudo-inverse of full unmixing)
let mixing = result.mixing;
Configuration
Use the builder pattern for fine-grained control:
use ;
let config = builder
.n_components // Number of components to extract
.ortho // Use orthogonal constraint (Picard-O)
.extended // Handle sub and super-Gaussian sources
.whiten // Perform PCA whitening
.centering // Center the data
.density // Density function
.max_iter // Maximum iterations
.tol // Convergence tolerance
.m // L-BFGS memory size
.random_state // For reproducibility
.verbose // Print progress
.build;
let result = fit_with_config?;
Warm Start Options
Picard supports two warm start methods to speed convergence:
JADE (Joint Approximate Diagonalization of Eigenmatrices)
let config = builder
.jade_it // Run 50 JADE iterations first
.random_state
.build;
JADE uses fourth-order cumulants and Jacobi rotations. It's particularly effective for:
- Mixed sub-Gaussian and super-Gaussian sources
- Cases where the sources have distinct kurtosis values
- Providing a robust starting point for difficult separations
FastICA
let config = builder
.fastica_it // Run 10 FastICA iterations first
.random_state
.build;
Note: You can use either jade_it or fastica_it, but not both simultaneously.
Density Functions
Choose the density function based on your source distributions:
use DensityType;
// Tanh (default) - good for super-Gaussian sources (e.g., speech, sparse signals)
let density = tanh;
let density = tanh_with_alpha; // Custom alpha
// Exp - for heavy-tailed super-Gaussian sources
let density = exp;
let density = exp_with_alpha;
// Cube - for sub-Gaussian sources (e.g., uniform distributions)
let density = cube;
Transforming New Data
// Fit on training data
let result = fit?;
// Transform new data using the fitted model
let sources_new = transform?;
Evaluating Separation Quality
When you know the true mixing matrix (e.g., in simulations):
use ;
// Amari distance: 0 = perfect separation
let distance = amari_distance;
// Permute W @ A to visualize how close it is to identity
let wa = result.full_unmixing.dot;
let permuted = permute; // Should be close to identity
Algorithm
Picard combines two key ideas:
- Sparse Hessian approximations for ICA that are cheap to compute and invert
- L-BFGS optimization that refines these approximations using gradient history
This yields an algorithm that:
- Has the same cost per iteration as simple quasi-Newton methods
- Achieves much better convergence on real data
- Is typically 2-5× faster than FastICA
Picard vs Picard-O
- Picard (
ortho: false): Standard ICA, minimizes mutual information - Picard-O (
ortho: true): Adds orthogonality constraint, faster for whitened data
Extended Mode
When extended: true, the algorithm automatically detects and handles both:
- Super-Gaussian sources (positive kurtosis): speech, sparse signals
- Sub-Gaussian sources (negative kurtosis): uniform distributions
Output Structure
Error Handling
use ;
match fit
License
BSD-3-Clause, matching the original Python implementation.