Source code for mvlearn.decomposition.multiviewica

# BSD 3-Clause License
# Copyright (c) 2020, Hugo RICHARD and Pierre ABLIN
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#  this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Modified from source package https://github.com/hugorichard/multiviewica

import numpy as np

try:
    from multiviewica import multiviewica
except ModuleNotFoundError as error:
    msg = (
        f"ModuleNotFoundError: {error}. multiviewica dependencies"
        + "required for this function. Please consult the mvlearn"
        + "installation instructions at https://github.com/mvlearn/mvlearn"
        + "to correctly install multiviewica dependencies."
    )
    raise ModuleNotFoundError(msg)

from .base import BaseMultiViewICA


[docs]class MultiviewICA(BaseMultiViewICA): r""" Multiview ICA for which views share a common source but separate mixing matrices. Parameters ---------- n_components : int, optional Number of components to extract. If None, n_components is set to the minimum number of features in the dataset. noise : float, default=1.0 Gaussian noise level max_iter : int, default=1000 Maximum number of iterations to perform init : {'permica', 'groupica'} or np array of shape (n_groups, n_components, n_components), default='permica' If permica: initialize with perm ICA, if groupica, initialize with group ica. Else, use the provided array to initialize. preproc: 'pca' or a ViewTransformer-like instance, default='pca' Preprocessing method to use to reduce data. If "pca", performs PCA separately on each view to reduce dimension of each view. Otherwise the dimension reduction is performed using the transform method of the ViewTransformer-like object. This instance also needs an inverse transform method to recover original data from reduced data. multiview_output : bool, optional (default True) If True, the `.transform` method returns one dataset per view. Otherwise, it returns one dataset, of shape (n_samples, n_components) random_state : int, RandomState instance or None, default=None Used to perform a random initialization. If int, random_state is the seed used by the random number generator; If RandomState instance, random_state is the random number generator; If None, the random number generator is the RandomState instance used by np.random. tol : float, default=1e-3 A positive scalar giving the tolerance at which the un-mixing matrices are considered to have converged. verbose : bool, default=False Print information Attributes ---------- pcas_ : ViewTransformer instance The fitted `ViewTransformer` used to reduce the data. The `ViewTransformer` is given by `ViewTransformer(PCA(n_components=n_components))` where n_components is the number of chosen. Only used if n_components is not None. mixing_ : array, shape (n_views, n_components, n_components) The square mixing matrices, linking preprocessed data and the independent components. pca_components_: array, shape (n_components, n_features) Principal axes in feature space, representing the directions of maximum variance in the data. Only used if n_components is not None. components_ : array, shape (n_views, n_components, n_components) The square unmixing matrices individual_components_ : list of array Individual unmixing matrices estimated by least squares. `individual_components_[i]` is an array of shape (n_components, n_features) where n_features is the number of features in the dataset `i`. individual_mixing_ : list of array Individual mixing matrices estimated by least squares. `individual_components_[i]` is an array of shape (n_features, n_components) where n_features is the number of features in the dataset `i`. See also -------- groupica Notes ----- Given each view :math:`X_i` It optimizes: .. math:: l(W) = \frac{1}{T} \sum_{t=1}^T [\sum_k log(cosh(Y_{avg,k,t})) + \sum_i l_i(X_{i,.,t})] where .. math:: l _i(X_{i,.,t}) = - log(|W_i|) + 1/(2 \sigma) ||X_{i,.,t}W_i - Y_{avg,.,t}||^2, :math:`W_i` is the mixing matrix for view :math:`i`, :math:`Y_{avg} = \frac{1}{n} \sum_{i=1}^n X_i W_i`, and :math:`\sigma` is the noise level. References ---------- .. [#1mvica] Hugo Richard, et al. "Modeling shared responses in neuroimaging studies through multiview ICA." In Pre-proceedings of Advances in Neural Information Processing Systems, volume 33, 2020 Examples -------- >>> from mvlearn.datasets import load_UCImultifeature >>> from mvlearn.decomposition import MultiviewICA >>> Xs, _ = load_UCImultifeature() >>> ica = MultiviewICA(n_components=3, max_iter=10) >>> sources = ica.fit_transform(Xs) >>> print(sources.shape) (6, 2000, 3) """ def __init__( self, n_components=None, noise=1.0, max_iter=1000, init="permica", multiview_output=True, random_state=None, tol=1e-3, verbose=False, n_jobs=30, ): super().__init__( n_components=n_components, multiview_output=multiview_output, ) self.n_jobs = n_jobs self.verbose = verbose self.n_components = n_components self.noise = noise self.max_iter = max_iter self.init = init self.tol = tol self.random_state = random_state self.multiview_output = multiview_output def fit_reduced(self, reduced_X): """ Fit the model with reduced data Parameters ---------- reduced_X: np array of shape (n_views, n_samples, n_components) Returns -------- unmixing: np array of shape (n_views, n_components, n_components) such that reduced_X[i].dot(unmixing[i].T) unmixes the data sources: np array of shape (n_samples, n_components) """ _, unmixings_, S = multiviewica( np.swapaxes(reduced_X, 1, 2), noise=self.noise, max_iter=self.max_iter, init=self.init, random_state=self.random_state, tol=self.tol, verbose=self.verbose, ) return unmixings_, S.T def aggregate(self, X_transformed, index=None): """ Aggregate transformed data to form a unique output Parameters ---------- X_transformed : list of array-likes The transformed data. If `multiview_output` is True, it is a list with the estimated individual principal components. If `multiview_output` is False, it is a single array containing the shared principal components. index: int or array-like, default=None The index or list of indices of the fitted views to which the inputted views correspond. If None, there should be as many inputted views as the fitted views and in the same order. Note that the index parameter is not available in all methods of mvlearn yet. Returns ------- Source: np array of shape (n_samples, n_components) """ return np.mean(X_transformed, axis=0)