Source code for teneva.core

"""Package teneva, module core: operations with individual TT-cores.

This module contains functions to work directly with individual TT-cores.

"""
import numpy as np
import teneva


def core_dot(G, R, ltr=True):
    """Multiply TT-core G with matrix R."""
    # TODO: Add docs and demo.
    r1, n, r2 = G.shape
    G = teneva._reshape(G, (r1*n, r2) if ltr else (r1, n*r2))
    R = np.array([[R]]) if isinstance(R, (int, float)) else R
    G = G @ R if ltr else R @ G
    G = teneva._reshape(G, (r1, n, G.shape[1]) if ltr else (G.shape[0], n, r2))
    return G


def core_dot_inv(G, R, ltr=True):
    """Multiply TT-core G with inverted matrix R."""
    # TODO: Add docs and demo.
    r1, n, r2 = G.shape
    G = teneva._reshape(G, (r1*n, r2) if ltr else (r1, n*r2))
    G = np.linalg.solve(R.T, G.T).T if ltr else np.linalg.solve(R, G)
    return teneva._reshape(G, (r1, n, r2))


def core_dot_maxvol(G, R, ind=None, ltr=True):
    """Multiply TT-core G with matrix R and leaves the most important rows."""
    # TODO: Add docs and demo.
    r1, n, r2 = G.shape
    G = core_dot(G, R, ltr)
    G = teneva._reshape(G, (r1, n*G.shape[-1]) if ltr else (G.shape[0]*n, r2))
    ind = teneva._maxvol(G.T if ltr else G)[0] if ind is None else ind
    G = G[:, ind] if ltr else G[ind, :]
    return G, ind


def core_qr_rand(G, m, ltr=True, seed=None):
    """Add random rows to TT-core G and return the new TT-core from Q-factor."""
    # TODO: Add docs and demo.
    r1, n, r2 = G.shape

    rand = teneva._rand(seed)
    rnd = rand.normal(size=(r1*n if ltr else n*r2, m))

    G = teneva._reshape(G, (r1*n, r2) if ltr else (r1, n*r2))
    G = G if ltr else G.T
    G = np.hstack((G, rnd))
    G, _ = np.linalg.qr(G)
    G = G if ltr else G.T
    G = teneva._reshape(G, (r1, n, G.shape[1]) if ltr else (G.shape[0], n, r2))
    return G


[docs]def core_qtt_to_tt(Q_list): """Transform the list of QTT-cores into a TT-core. Args: Q_list (list of np.ndarray): list of QTT-cores of the shapes [[q_0, 2, q_1], [q_1, 2, q_2], ...[q_(q-1), 2, q_q]] and length q. Returns: np.ndarray: TT-core in the form of 3-dimensional array of the shape q_0 x 2^q x q_q. """ G = Q_list[0].copy() for Q in Q_list[1:]: r1 = G.shape[0] r2 = Q.shape[-1] G = np.tensordot(G, Q, 1) G = teneva._reshape(G, (r1, -1, r2)) return G
[docs]def core_stab(G, p0=0, thr=1.E-100): """Scaling for the passed TT-core, i.e., G -> (Q, p), G = 2^p * Q. Args: G (np.ndarray): TT-core in the form of 3-dimensional array. p0 (int): optional initial value of the power-factor (it will be added to returned value p). thr (float): threshold value for applying scaling (if the maximum modulo element in the TT-core is less than this value, then scaling will not be performed). Returns: (np.ndarray, int): scaled TT-core (Q) and power-factor (p), such that G = 2^p * Q. """ v_max = np.max(np.abs(G)) if v_max <= thr: return G, p0 p = int(np.floor(np.log2(v_max))) Q = G / 2.**p return Q, p0 + p
[docs]def core_tt_to_qtt(G, e=0., r=1.E+12): """Transform the TT-core into a list of QTT-cores. Args: G (np.ndarray): TT-core in the form of 3-dimensional array of the shape r1xnxr2. The mode size should be a power of two, i.e., n=2^d. e (float): desired approximation accuracy. r (int): maximum rank for the SVD decomposition. Returns: list: list of QTT-cores (np.ndarrays) of the shape [q1, 2, q2], which approximates the given TT-core G. """ r1, n, r2 = G.shape d = int(np.log2(n)) if 2**d != n: raise ValueError('Invalid mode size (it should be a power of two)') A = teneva._reshape(G, (-1, r2)) A, V0 = teneva.matrix_svd(A, e, r) Y = [] for i in range(d-1): As = A.shape[0] // 2 q = A.shape[1] A = np.hstack([A[:As], A[As:]]) A, V = teneva.matrix_svd(A, e, r) Y.append(teneva._reshape(V, (-1, 2, q), order='C')) Y.append(teneva._reshape(A, (r1, 2, -1))) Y[0] = np.einsum('ijk,kl', Y[0], V0) return Y[::-1]