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