Esempio n. 1
0
def ilc(components, instrument, data, patch_ids=None):
    """ Internal Linear Combination

    Parameters
    ----------
    components: list or tuple of lists
        `Components` of the mixing matrix. They must have no free parameter.
    instrument:
        Object that provides the following as a key or an attribute.

        - **frequency**

        They can be anything that is convertible to a float numpy array.
    data: ndarray or MaskedArray
        Data vector to be separated. Shape ``(n_freq, ..., n_pix)``.
        ``...`` can be also absent.
        Values equal to hp.UNSEEN or, if MaskedArray, masked values are
        neglected during the component separation process.
    patch_ids: array
        It stores the id of the region over which the ILC weights are computed
        independently. It must be broadcast-compatible with data.

    Returns
    -------
    result : dict
	It includes

        - **W**: *(ndarray)* - ILC weights for each component and possibly each
          patch.
        - **freq_cov**: *(ndarray)* - Empirical covariance for each patch
        - **s**: *(ndarray)* - Component maps

    Note
    ----
    * During the component separation, a pixel is masked if at least one of its
      frequencies is masked.
    """
    # Checks
    instrument = standardize_instrument(instrument)
    np.broadcast(data, patch_ids)
    n_freq = data.shape[0]
    assert len(instrument.frequency) == n_freq,\
        "The number of frequencies does not match the number of maps provided"
    n_comp = len(components)

    # Prepare mask and set to zero all the frequencies in the masked pixels:
    # NOTE: mask are good pixels
    mask = ~_intersect_mask(data)

    mm = MixingMatrix(*components)
    A = mm.eval(instrument.frequency)

    data = data.T
    res = OptimizeResult()
    res.s = np.full(data.shape[:-1] + (n_comp, ), hp.UNSEEN)

    def ilc_patch(ids_i, i_patch):
        if not np.any(ids_i):
            return
        data_patch = data[ids_i]  # data_patch is a copy (advanced indexing)
        cov = np.cov(data_patch.reshape(-1, n_freq).T)
        # Perform the inversion of the correlation instead of the covariance.
        # This allows to meaninfully invert covariances that have very noisy
        # channels.
        assert cov.ndim == 2
        cov_regularizer = np.diag(cov)**0.5 * np.diag(cov)[:, np.newaxis]**0.5
        correlation = cov / cov_regularizer
        try:
            inv_freq_cov = np.linalg.inv(correlation) / cov_regularizer
        except np.linalg.LinAlgError:
            np.set_printoptions(precision=2)
            logging.error(
                f"Empirical covariance matrix cannot be reliably inverted.\n"
                f"The domain that failed is {i_patch}.\n"
                f"Covariance matrix diagonal {np.diag(cov)}\n"
                f"Correlation matrix\n{correlation}")
            raise
        res.freq_cov[i_patch] = cov
        res.W[i_patch] = alg.W(A, inv_freq_cov)
        res.s[ids_i] = alg._mv(res.W[i_patch], data_patch)

    if patch_ids is None:
        res.freq_cov = np.full((n_freq, n_freq), hp.UNSEEN)
        res.W = np.full((n_comp, n_freq), hp.UNSEEN)
        ilc_patch(mask, np.s_[:])
    else:
        n_id = patch_ids.max() + 1
        res.freq_cov = np.full((n_id, n_freq, n_freq), hp.UNSEEN)
        res.W = np.full((n_id, n_comp, n_freq), hp.UNSEEN)
        patch_ids_bak = patch_ids.copy().T
        patch_ids_bak[~mask] = -1
        for i in range(n_id):
            ids_i = np.where(patch_ids_bak == i)
            ilc_patch(ids_i, i)

    res.s = res.s.T
    res.components = mm.components

    return res