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
Esempio n. 2
0
def harmonic_ilc_alm(components, instrument, alms, lbins=None, fsky=None):
    """ Internal Linear Combination of alms

    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**

        It can be anything that is convertible to a float numpy array.
    alms: ndarray
        Data vector to be separated. Shape ``(n_freq, ..., lm)``.
        ``...`` can be 1, 3 or absent. The ILC weights are computed
	independently for each of its indices.
    lbins: array
        It stores the edges of the bins that will have the same ILC weights.
	If a multipole is not in a bin but is the alms, an independent bin
	will be assigned to it
    fsky: array
        If provided the output power spectra are corrected for this factor

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

        - **W**: *(ndarray)* - ILC weights for each component and possibly
	  each index of the `...` dimension in the alms.
        - **s**: *(ndarray)* - Alms of the cleaned components
        - **cl_in**: *(ndarray)* - Spectra of the input alm
        - **cl_out**: *(ndarray)* - Spectra of the output alm
        - **fsky**: *(ndarray)* - The input fsky used to correct the cls

    """
    cl_in = np.array([hp.alm2cl(alm) for alm in alms])

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

    cov = _empirical_harmonic_covariance(alms)
    if lbins is not None:
        for lmin, lmax in zip(lbins[:-1], lbins[1:]):
            # Average the covariances in the bin
            lmax = min(lmax, cov.shape[-1])
            dof = 2 * np.arange(lmin, lmax) + 1
            cov[..., lmin:lmax] = ((dof / dof.sum() *
                                    cov[..., lmin:lmax]).sum(-1))[...,
                                                                  np.newaxis]
    cov = _regularized_inverse(cov.swapaxes(-1, -3))
    ilc_filter = np.linalg.inv(A.T @ cov @ A) @ A.T @ cov
    del cov, dof

    res = OptimizeResult()
    res.s = _apply_harmonic_W(ilc_filter, alms)

    # Craft output
    cl_out = np.array([hp.alm2cl(alm) for alm in res.s])
    res.cl_in = cl_in
    res.cl_out = cl_out
    if fsky:
        res.cl_in /= fsky
        res.cl_out /= fsky

    res.fsky = fsky
    res.W = ilc_filter

    return res