A Python package to perform coupled non-negative independent component analysis.
Project description
CNICA
Coupled Non-negative Independent Component Analysis for blind source separation of non-negative signals, with particular application to spectroscopic data.
Overview
CNICA decomposes an observed data matrix D of shape (n_channels, n_samples)
into two non-negative factor matrices:
X \approx C^T S
where C (shape: n_components × n_channels) is the mixing matrix (e.g.
concentration profiles) and S (shape: n_components × n_samples) is the
source matrix (e.g. pure component spectra).
The decomposition proceeds in two stages:
- NMF initialization — Non-negative Matrix Factorization provides an
initial estimate of
CandS. - Riemannian mutual information minimization — A linear transformation
Mis found that minimizes statistical dependence between source components while strictly enforcing non-negativity ofMSandM⁻ᵀCvia log-barrier penalties on a Riemannian manifold. This recovers components that are physically distinct rather than merely mathematically orthogonal, which is the key advantage over standard NMF.
CNICA is particularly well-suited to spectroscopic datasets where:
- Components are non-negative by physical constraint (e.g. absorbance, emission intensity).
- Pure component signals are expected to be statistically independent.
- Derivatives of the spectra or concentration profiles carry additional discriminative information (e.g. sharp peaks vs. broad backgrounds).
Installation
pip install cnica
Requires Python 3.10+ and depends on numpy, scipy, and scikit-learn.
Quick start
import numpy as np
from cnica import CNICA
from cnica.models import NMFParams, MIOParams
# Observed mixture matrix: 30 channels, 200 wavenumbers
D = np.abs(np.random.randn(30, 200))
model = CNICA(
n_components=3,
nmf_params=NMFParams(beta_loss='frobenius'),
mio_params=MIOParams(params=(True, True, False))
)
S = model.fit_transform(D) # Pure component spectra: (3, 200)
C = model.C_ # Concentration profiles: (3, 30)
Example usage
A complete worked example using synthetic spectroscopic data with three known pure components (Gaussian peaks, cosine wave, linear background) is available in the documentation.
import numpy as np
from cnica import CNICA
from cnica.models import NMFParams, MIOParams
# Ground truth sources
n_wave, n_channels, n_components = 1000, 100, 3
x = np.linspace(0, 20, n_wave)
s1 = np.exp(-(x-4)**2/0.5) + np.exp(-(x-10)**2/0.8) + np.exp(-(x-15)**2/0.4)
s2 = np.cos(x) + 1.2
s3 = 0.1 * x + 0.5
S_true = np.vstack([s1, s2, s3])
# Random non-negative mixing matrix and noisy observations
np.random.seed(42)
C_true = np.random.gamma(shape=2.0, scale=1.0, size=(n_channels, n_components))
D_noisy = np.random.poisson(C_true @ S_true * 100) / 100
# Fit CNICA
model = CNICA(
n_components=n_components,
nmf_params=NMFParams(beta_loss='frobenius', max_iter=10000, tol=1e-9),
mio_params=MIOParams(params=(True, True, False), lam=10.0)
)
S_est = model.fit_transform(D_noisy)
# Check convergence
print(model.mio_result_.success) # True if converged
print(model.mio_result_.message) # Convergence message
# Apply to new data (estimate concentrations given known spectra)
D_new = np.abs(np.random.randn(10, n_wave))
C_new = model.transform(D_new) # shape: (3, 10)
Features
- Similar to scikit-learn API — implements
BaseEstimatorandTransformerMixinfor use in sklearn pipelines. - Two-stage optimization — NMF initialization followed by Riemannian manifold optimization with log-barrier constraints.
- Unsupervised filter tuning — Parks-McClellan FIR filters are tuned automatically via Mean Squared Residual Autocorrelation (MSRAC) to compute signal derivatives for the mutual information objective.
- Convex hull reduction — barrier constraints are enforced only on convex hull vertices of the constraint matrices, reducing cost from O(samples) to O(vertices).
- Sparsity regularization — optional Safe-Plateau sparsity norm rewards peaky components (e.g. sharp Raman bands) while leaving dense components (e.g. broad fluorescence) unaffected.
Status
This package is actively developed and used by the author. Please feel free to open an issue or pull request for bug reports, feature requests, or suggestions.
Documentation
See the documentation for the full API reference and worked examples.
License
This is free software. See LICENSE.
Related work
Publication eminent...
Contact
The author can be reached at ivancic91@gmail.com.
Credits
This package was created using Cookiecutter with the usnistgov/cookiecutter-nist-python template.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cnica-0.1.2.tar.gz.
File metadata
- Download URL: cnica-0.1.2.tar.gz
- Upload date:
- Size: 513.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ee390a22521006d5f5658f2d1ed646b100e804c215054653c5e8bbfb7061b81
|
|
| MD5 |
36112ea7b62dfd30c564cde1e033037a
|
|
| BLAKE2b-256 |
a9e0798616fa67df46dd5e17a2d929774dfd59c60173698ac2f409f6c6f09944
|
File details
Details for the file cnica-0.1.2-py3-none-any.whl.
File metadata
- Download URL: cnica-0.1.2-py3-none-any.whl
- Upload date:
- Size: 32.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
903b2f347fe085d71736946210c8357ee0c50f8792ce3c4e83371e86bc157ea2
|
|
| MD5 |
c84a1836dbdd8b0d12d9bf13a32135af
|
|
| BLAKE2b-256 |
59d3a87ed8848af5cba8e6068f523bc7431bdf50c06467322685fb38c32e5d91
|