def recon_bsgda(y, S, L: SparseTensor, mu: float = 0.01, reg_order: int = 1, **kwargs): N = L.size(-1) M = len(S) assert N >= M > 0 if y.ndim == 1: y = y.view(-1, 1) if y.shape[0] != M: raise RuntimeError( f"y is expected to have a shape ({M},num_signal) or ({M},), not {y.shape}" ) dt, dv, density, on_gpu = get_ddd(L) xp, xcipy, xsplin = get_array_module(on_gpu) L = to_xcipy(L) Ht = construct_sampling_matrix(N, S, dtype=dt, device=dv, layout="csr").T HtH = construct_hth(N, S, dtype=dt, device=dv, layout="csr") B = HtH + mu * L**reg_order Hty = Ht @ xp.asarray(y) if xp == np: x_hat = xsplin.spsolve(B, Hty, **kwargs) else: num_sig = Hty.shape[-1] x_hat = xp.empty((N, num_sig), dtype=Hty.dtype) for j in range(num_sig): x_hat[:, j] = xsplin.spsolve(B, Hty[:, j], **kwargs) return torch.as_tensor(x_hat, device=dv)
def cheby_op_basis(L, coeff, lam_max=2.0, return_ts=False): assert coeff.ndim == 1 K = len(coeff) if isinstance(L, SparseTensor): dt, dv, density, on_gpu = get_ddd(L) xp, xcipy, _ = get_array_module(on_gpu) elif isinstance(L, spmatrix): import scipy xcipy = scipy xp = np else: raise TypeError L = to_xcipy(L) N = L.shape[-1] coeff = xp.asarray(coeff) Im = xcipy.sparse.eye(N, dtype=L.dtype, format="csr") Ln = L * (2 / lam_max) - Im Tl_old = Im Tl_cur = Ln Hl = 0.5 * coeff[0] * Tl_old + coeff[1] * Tl_cur for k in range(2, K): Tl_new = 2 * Ln * Tl_cur - Tl_old Hl = Hl + coeff[k] * Tl_new Tl_old = Tl_cur Tl_cur = Tl_new if return_ts: result = SparseTensor.from_scipy(Hl) if xp == np else from_cpx(Hl) else: result = Hl return result
def construct_hth(N, S, D=None, dtype=None, device=None, layout="csr", return_ts=False): M = len(S) row = torch.as_tensor(S, device=device) col = row.clone() data = torch.ones(M, dtype=dtype, device=device) if D is not None: data = data * torch.as_tensor(D, dtype=dtype, device=device) spa = SparseTensor(row=row, col=col, value=data, sparse_sizes=(N, N)) HtH = spa if return_ts else to_xcipy(spa, layout) return HtH
def construct_sampling_matrix(N, S, dtype=None, device=None, layout="csr", return_ts=False): r""" Construct the sampling matrix :math:`\mathbf{H} \in\{0,1\}^{M \times N}` defined as follows. .. math:: \mathbf{H}_{i j}= \begin{cases}1, & j=\mathcal{S}_{i} \\ 0, & \text { otherwise } \end{cases} Parameters ---------- N: int The total number of nodes S: list, array, torch.Tensor The 1-D list of sampled nodes. dtype: torch.dtype, optional The dtype layout: str The memory layout of the generated sparse matrix. One of ("csc", "csr", "coo"). device: torch.device, str, optional If `True` and `cupy` is installed, use `cupy`as backend; otherwise `scipy`. return_ts: bool, If False, return `scipy.sparse.spmatrix` of device is GPU else 'cupyx.scipy.sparse.spmatrix' Returns ------- H: spmatrix The :obj:`(M,N)` scipy sparse matrix with :obj:`layout` format """ M = len(S) row = torch.arange(M, device=device) col = torch.as_tensor(S, device=device) data = torch.ones(M, dtype=dtype, device=device) spa = SparseTensor(row=row, col=col, value=data, sparse_sizes=(M, N)) H = spa if return_ts else to_xcipy(spa, layout) return H
def recon_rsbs(y, S, L: SparseTensor, cum_coh, mu: float = 0.01, reg_order: int = 1, **kwargs): N = L.size(-1) M = len(S) assert M > 0 if y.shape[0] != M: raise RuntimeError( f"y is expected to have a shape ({M},num_signal) or ({M},), not {y.shape}" ) dt, dv, density, on_gpu = get_ddd(L) xp, xcipy, xsplin = get_array_module(on_gpu) yp = to_xp(y).astype("d") L = to_xcipy(L, layout="csr").astype("d") H = construct_sampling_matrix(N, S, torch.double, dv) Psinv = construct_dia(S, cum_coh, ps=True, inverse=True, dtype=torch.double, device=dv) Bl = H.T * Psinv B = Bl * H + mu * L**reg_order HPy = Bl @ yp if xp == np: x_hat = xsplin.spsolve(B, HPy, **kwargs) else: num_sig = yp.shape[-1] x_hat = xp.empty((N, num_sig), dtype=yp.dtype) for j in range(num_sig): x_hat[:, j] = xsplin.spsolve(B, HPy[:, j], **kwargs) return torch.as_tensor(x_hat, device=dv)
def construct_dia( S, diag_data, ps=True, inverse=False, dtype=None, device=None, layout="csr", return_ts=False, ): N = len(diag_data) M = len(S) row = torch.arange(M, device=device) if ps else torch.arange(N, device=device) col = row.clone() shape = (M, M) if ps else (N, N) data = torch.as_tensor(diag_data, dtype=dtype, device=device) data = data**-1 if inverse else data data = data[S] if ps else data spa = SparseTensor(row=row, col=col, value=data, sparse_sizes=shape) P = spa if return_ts else to_xcipy(spa, layout) return P
def recon_fastssss(y, S, T, order, sd=0.5): """A primary implementation of reconstruction method associated with "FastSSS" sampling algorithm. Parameters ---------- y: Tensor The measurements on sampling set :obj:S:. If the localization operator :obj:`T` has a density greater than the threshold :obj:`sd`, :obj:`y` has a shape of either :obj:`(M,)`,:obj:`(M,1)`,or :obj:`(M,C)`; otherwise :obj:`y` could only be either :obj:`(M,)` or :obj:`(M,1)`. S: List A list consisting of all indices of sampled nodes. T: Tensor, SparseTensor The localization operator order: int sd: float The threshold of :obj:`T`'s density that controls when we use a dense or sparse linear solver. Returns ------- Tensor The signal recovered from the measurements :obj:`y` . """ T_k = matrix_power(T, order) dt, dv, density, on_gpu = get_ddd(T_k) if density > sd: T_k = T_k.to_dense() tmp = torch.linalg.solve(T_k[np.ix_(S, S)], torch.as_tensor(y)) x_hat = T_k[:, S] @ tmp else: T_k = to_xcipy(T_k) xp, xcipy, xsplin = get_array_module(on_gpu) tmp = xsplin.spsolve(T_k[xp.ix_(S, S)], xp.asarray(y)) x_hat = T_k[:, S] * tmp return torch.as_tensor(x_hat, device=dv)
def test_to_xcipy(device, layout): spa = SparseTensor.from_dense(torch.rand(3, 3, device=device)) xcia = to_xcipy(spa, layout=layout) assert xcia.shape == (3, 3) assert xcia.format == layout