Skip to main content

A Python package to perform coupled non-negative independent component analysis.

Project description

Repo Docs PyPI license PyPI version Conda (channel only) Code style: ruff uv

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:

  1. NMF initialization — Non-negative Matrix Factorization provides an initial estimate of C and S.
  2. Riemannian mutual information minimization — A linear transformation M is found that minimizes statistical dependence between source components while strictly enforcing non-negativity of MS and M⁻ᵀC via 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 BaseEstimator and TransformerMixin for 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cnica-0.1.2.tar.gz (513.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cnica-0.1.2-py3-none-any.whl (32.0 kB view details)

Uploaded Python 3

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

Hashes for cnica-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9ee390a22521006d5f5658f2d1ed646b100e804c215054653c5e8bbfb7061b81
MD5 36112ea7b62dfd30c564cde1e033037a
BLAKE2b-256 a9e0798616fa67df46dd5e17a2d929774dfd59c60173698ac2f409f6c6f09944

See more details on using hashes here.

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

Hashes for cnica-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 903b2f347fe085d71736946210c8357ee0c50f8792ce3c4e83371e86bc157ea2
MD5 c84a1836dbdd8b0d12d9bf13a32135af
BLAKE2b-256 59d3a87ed8848af5cba8e6068f523bc7431bdf50c06467322685fb38c32e5d91

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page