.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "_generated/examples/plot_mutual_information.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr__generated_examples_plot_mutual_information.py: ================================= Mutual Information Reduction ================================= ICA finds components that are as statistically independent as possible. This example demonstrates that property directly: we generate 8 truly independent Laplacian sources, mix them linearly, then show how both AMICA and extended Infomax reduce the pairwise mutual information (MI) between the recovered components compared to the original mixed channels. A well-fitted decomposition produces a pairwise MI matrix that is close to zero everywhere off the diagonal -- each IC is approximately independent of every other. On this clean synthetic dataset both algorithms converge to a similar result. AMICA's advantage over Infomax becomes more apparent on real EEG data, where source distributions are complex and non-stationary in ways that a fixed sigmoidal nonlinearity cannot capture. .. GENERATED FROM PYTHON SOURCE LINES 20-112 .. image-sg:: /_generated/examples/images/sphx_glr_plot_mutual_information_001.png :alt: Pairwise mutual information before and after ICA Both methods converge to similar results on simple synthetic data., Mixed channels mean MI = 0.124 nats, AMICA sources mean MI = 0.041 nats, Ext. Infomax sources mean MI = 0.041 nats :srcset: /_generated/examples/images/sphx_glr_plot_mutual_information_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Fitting AMICA ... AMICA T=5000 n_orig=8 M=1 J=3 After sphering: n=8 sldet=-14.7404 Auto chunk_t=131072 (CPU, ~32 MB L3 target) iter 1 lrate=1.000e-01 LL=-3.2987417517 nd=3.212e-01 Starting Newton at iter 50 ... iter 100 lrate=1.000e+00 LL=-3.1859325979 nd=9.877e-04 iter 200 lrate=1.000e+00 LL=-3.1850218178 nd=5.668e-04 Done - 200 iterations final LL=-3.18502182 Fitting extended Infomax ... /home/runner/work/pyamica/pyamica/examples/plot_mutual_information.py:64: RuntimeWarning: The data has not been high-pass filtered. For good ICA performance, it should be high-pass filtered (e.g., with a 1.0 Hz lower bound) before fitting ICA. ica_infomax.fit(raw, picks="eeg") Mean pairwise MI Mixed channels : 0.1243 nats AMICA sources : 0.0413 nats Infomax sources: 0.0413 nats | .. code-block:: Python import numpy as np import matplotlib.pyplot as plt import mne from pyamica import AmicaICA, score_mutual_information from pyamica._mne import _mi_matrix mne.set_log_level("WARNING") # ── Synthetic data ───────────────────────────────────────────────────────────── rng = np.random.default_rng(42) N_CH = 8 T = 5000 SFREQ = 250.0 CH_NAMES = ["Fp1", "Fp2", "F3", "F4", "C3", "C4", "P3", "P4"] # 8 truly independent Laplacian sources, unit variance S = rng.laplace(0, 1, (N_CH, T)) S = S / S.std(axis=1, keepdims=True) # Mix with a random matrix A_true = rng.standard_normal((N_CH, N_CH)) X = A_true @ S X = X / X.std() * 1e-5 # scale to EEG-like amplitude (~10 µV rms) info = mne.create_info(CH_NAMES, sfreq=SFREQ, ch_types="eeg") raw = mne.io.RawArray(X, info, verbose=False) raw.set_montage("standard_1020") # ── Fit AMICA ───────────────────────────────────────────────────────────────── print("Fitting AMICA ...") ica_amica = AmicaICA(max_iter=200, verbose=True) ica_amica.fit(raw, picks="eeg") # ── Fit extended Infomax ─────────────────────────────────────────────────────── print("Fitting extended Infomax ...") ica_infomax = mne.preprocessing.ICA( n_components=N_CH, method="infomax", random_state=0, fit_params=dict(extended=True), ) ica_infomax.fit(raw, picks="eeg") # ── Compute MI matrices ──────────────────────────────────────────────────────── mi_mixed = _mi_matrix(raw.get_data(), n_bins=30) mi_amica = score_mutual_information(ica_amica.get_mne_ica(0), raw) mi_infomax = score_mutual_information(ica_infomax, raw) idx = np.triu_indices(N_CH, k=1) mean_mixed = mi_mixed[idx].mean() mean_amica = mi_amica[idx].mean() mean_infomax = mi_infomax[idx].mean() print(f"\nMean pairwise MI") print(f" Mixed channels : {mean_mixed:.4f} nats") print(f" AMICA sources : {mean_amica:.4f} nats") print(f" Infomax sources: {mean_infomax:.4f} nats") # ── Plot ─────────────────────────────────────────────────────────────────────── labels = [ch[:3] for ch in CH_NAMES] vmax = mi_mixed.max() fig, axes = plt.subplots(1, 3, figsize=(13, 4)) titles = [ f"Mixed channels\nmean MI = {mean_mixed:.3f} nats", f"AMICA sources\nmean MI = {mean_amica:.3f} nats", f"Ext. Infomax sources\nmean MI = {mean_infomax:.3f} nats", ] matrices = [mi_mixed, mi_amica, mi_infomax] for ax, mi, title in zip(axes, matrices, titles): im = ax.imshow(mi, vmin=0, vmax=vmax, cmap="YlOrRd", aspect="auto") ax.set_xticks(range(N_CH)) ax.set_yticks(range(N_CH)) ax.set_xticklabels(labels, fontsize=8) ax.set_yticklabels(labels, fontsize=8) ax.set_title(title, fontsize=10) plt.colorbar(im, ax=ax, label="MI (nats)", fraction=0.046, pad=0.04) fig.suptitle( "Pairwise mutual information before and after ICA\n" "Both methods converge to similar results on simple synthetic data.", fontsize=11, ) plt.tight_layout() plt.show() .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 6.049 seconds) .. _sphx_glr_download__generated_examples_plot_mutual_information.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_mutual_information.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_mutual_information.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_mutual_information.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_