left_svd_pair ============= .. py:function:: t3toolbox.backend.linalg.left_svd_pair(G0_i_a_j, G1_j_b_k, min_rank = None, max_rank = None, rtol = None, atol = None) .. code-block:: python def left_svd_pair( G0_i_a_j: NDArray, # shape=(..., ni, na, nj) G1_j_b_k: NDArray, # shape=(..., nj, nb, nk) min_rank: int = None, # 1 <= min_rank <= max_rank <= minimum(ni*na, nj) max_rank: int = None, # 1 <= min_rank <= max_rank <= minimum(ni*na, nj) rtol: float = None, # removes singular values satisfying sigma < maximum(atol, rtol*sigma1) atol: float = None, # removes singular values satisfying sigma < maximum(atol, rtol*sigma1) ) -> typ.Tuple[ NDArray, # new_G0, shape=(..., ni, na, r) NDArray, # new_G1, shape=(..., r, nb, nj) NDArray, # ss, shape=(.., r) ]: Compute (truncated) singular value decomposition of G0, pushing non-orthogonal remainder onto G1. Orthogonalizes ``G0`` via its left unfolding (so ``new_G0`` is left-orthonormal) and absorbs the ``ss @ Vt`` remainder into the shared bond of ``G1``, leaving the contracted product over the shared index unchanged. Truncation args behave as in :py:func:`truncated_svd`. .. rubric:: Examples >>> import numpy as np >>> import t3toolbox.backend.linalg as linalg >>> np.random.seed(0) >>> G0 = np.random.randn(2, 3, 4) # (ni, na, nj) >>> G1 = np.random.randn(4, 5, 6) # (nj, nb, nk) -- shared bond nj=4 >>> new_G0, new_G1, ss = linalg.left_svd_pair(G0, G1) >>> print(new_G0.shape, new_G1.shape, ss.shape) (2, 3, 4) (4, 5, 6) (4,) >>> before = np.einsum('iaj,jbk->iabk', G0, G1) >>> after = np.einsum('iax,xbk->iabk', new_G0, new_G1) >>> print(np.allclose(before, after)) # product across the shared bond preserved True >>> Um = new_G0.reshape(2 * 3, -1) >>> print(np.allclose(Um.T @ Um, np.eye(Um.shape[1]))) # new_G0 is left-orthonormal True