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