Instruction:
Make a summary of the following Python 3 code
def matvec(a, b, compression=False):
    acrs = _vector.vector.to_list(a.tt)
    bcrs = _vector.vector.to_list(b)
    ccrs = []
    d = b.d

    def get_core(i):
        acr = _np.reshape(
            acrs[i],
            (a.tt.r[i],
             a.n[i],
             a.m[i],
             a.tt.r[
                 i + 1]),
            order='F')
        acr = acr.transpose([3, 0, 1, 2])  # a(R_{i+1}, R_i, n_i, m_i)
        bcr = bcrs[i].transpose([1, 0, 2])  # b(m_i, r_i, r_{i+1})
        # c(R_{i+1}, R_i, n_i, r_i, r_{i+1})
        ccr = _np.tensordot(acr, bcr, axes=(3, 0))
        ccr = ccr.transpose([1, 3, 2, 0, 4]).reshape(
            (a.tt.r[i] * b.r[i], a.n[i], a.tt.r[i + 1] * b.r[i + 1]), order='F')
        return ccr

    if compression:  # the compression is laaaaazy and one-directioned
        # calculate norm of resulting _vector first
        nrm = _np.array([[1.0]])  # 1 x 1
        v = _np.array([[1.0]])
        for i in xrange(d):
            ccr = get_core(i)
            # print(str(ccr.shape) + " -> "),
            # minimal loss compression
            ccr = _np.tensordot(v, ccr, (1, 0))
            rl, n, rr = ccr.shape
            if i < d - 1:
                u, s, v = _np.linalg.svd(
                    ccr.reshape(
                        (rl * n, rr), order='F'), full_matrices=False)
                newr = min(rl * n, rr)
                ccr = u[:, :newr].reshape((rl, n, newr), order='F')
                v = _np.dot(_np.diag(s[:newr]), v[:newr, :])
            # print(ccr.shape)
            # r x r . r x n x R -> r x n x R
            nrm = _np.tensordot(nrm, ccr, (0, 0))
            # r x n x R . r x n x R -> n x R x n x R
            nrm = _np.tensordot(nrm, _np.conj(ccr), (0, 0))
            nrm = nrm.diagonal(axis1=0, axis2=2)  # n x R x n x R -> R x R x n
            nrm = nrm.sum(axis=2)  # R x R x n -> R x R
        if nrm.size > 1:
            raise Exception('too many numbers in norm')
        # print("Norm calculated:", nrm)
        nrm = _np.sqrt(_np.linalg.norm(nrm))
        # print("Norm predicted:", nrm)
        compression = compression * nrm / _np.sqrt(d - 1)
        v = _np.array([[1.0]])

    for i in xrange(d):
        ccr = get_core(i)
        rl, n, rr = ccr.shape
        if compression:
            ccr = _np.tensordot(v, ccr, (1, 0))  # c(s_i, n_i, r_i, r_{i+1})
            if i < d - 1:
                rl = v.shape[0]
                u, s, v = _np.linalg.svd(
                    ccr.reshape(
                        (rl * n, rr), order='F'), full_matrices=False)
                ss = _np.cumsum(s[::-1])[::-1]
                newr = max(min([r for r in range(ss.size) if ss[
                    r] <= compression] + [min(rl * n, rr)]), 1)
                # print("Rank % 4d replaced by % 4d" % (rr, newr))
                ccr = u[:, :newr].reshape((rl, n, newr), order='F')
                v = _np.dot(_np.diag(s[:newr]), v[:newr, :])
        ccrs.append(ccr)
    result = _vector.vector.from_list(ccrs)
    if compression:
        # print(result)
        print("Norm actual:", result.norm(), " mean rank:", result.rmean())
        # print("Norm very actual:", matvec(a,b).norm())
    return result

Response: Matrix - vector product in TT format.


Instruction:
Can you generate the documentation for the following Python 3 function
def kron(a, b):
    if hasattr(a, '__kron__'):
        return a.__kron__(b)
    if a is None:
        return b
    else:
        raise ValueError(
            'Kron is waiting for two TT-vectors or two TT-matrices')

Response: Kronecker product of two TT - matrices or two TT - vectors


Instruction:
Can you generate the documentation for the following Python 3 function
def dot(a, b):
    if hasattr(a, '__dot__'):
        return a.__dot__(b)
    if a is None:
        return b
    else:
        raise ValueError(
            'Dot is waiting for two TT-vectors or two TT-    matrices')

Response: Dot product of two TT - vectors or two TT - Vocabularys


Instruction:
Make a summary of the following Python 3 code
def zkron(ttA, ttB):
    Al = _matrix.matrix.to_list(ttA)
    Bl = _matrix.matrix.to_list(ttB)
    Hl = [_np.kron(B, A) for (A, B) in zip(Al, Bl)]
    return _matrix.matrix.from_list(Hl)

Response: Do kronecker product between two matrices ttA and ttB.


Instruction:
Write a Python 3 script to
do kronecker product between vectors ttA and ttB.

Response:
def zkronv(ttA, ttB):
    """
    Do kronecker product between vectors ttA and ttB.
    Look about kronecker at: https://en.wikipedia.org/wiki/Kronecker_product
    For details about operation refer: https://arxiv.org/abs/1802.02839
    :param ttA: first TT-vector;
    :param ttB: second TT-vector;
    :return: operation result in z-order
    """
    Al = _vector.vector.to_list(ttA)
    Bl = _vector.vector.to_list(ttB)
    Hl = [_np.kron(B, A) for (A, B) in zip(Al, Bl)]
    return _vector.vector.from_list(Hl)


Instruction:
Here you have a function in Python 3, explain what it does
def zmeshgrid(d):
    lin = xfun(2, d)
    one = ones(2, d)

    xx = zkronv(lin, one)
    yy = zkronv(one, lin)

    return xx, yy

Response: Returns a meshgrid like np. meshgrid but in z - order


Instruction:
Explain what the following Python 3 code does
def zaffine(c0, c1, c2, d):

    xx, yy = zmeshgrid(d)
    Hx, Hy = _vector.vector.to_list(xx), _vector.vector.to_list(yy)

    Hs = _cp.deepcopy(Hx)
    Hs[0][:, :, 0] = c1 * Hx[0][:, :, 0] + c2 * Hy[0][:, :, 0]
    Hs[-1][1, :, :] = c1 * Hx[-1][1, :, :] + (c0 + c2 * Hy[-1][1, :, :])

    d = len(Hs)
    for k in range(1, d - 1):
        Hs[k][1, :, 0] = c1 * Hx[k][1, :, 0] + c2 * Hy[k][1, :, 0]

    return _vector.vector.from_list(Hs)

Response: Generate linear function c0 + c1 ex + c2 ey in z ordering with d cores in QTT


Instruction:
Can you generate a brief explanation for the following Python 3 code
def concatenate(*args):
    tmp = _np.array([[1] + [0] * (len(args) - 1)])
    result = kron(_vector.vector(tmp), args[0])
    for i in range(1, len(args)):
        result += kron(_vector.vector(_np.array([[0] * i +
                                                 [1] + [0] * (len(args) - i - 1)])), args[i])
    return result

Response: Concatenates given TT - vectors.


Instruction:
Write a Python 3 script to
sum TT - vector over specified axes

Response:
def sum(a, axis=-1):
    """Sum TT-vector over specified axes"""
    d = a.d
    crs = _vector.vector.to_list(a.tt if isinstance(a, _matrix.matrix) else a)
    if axis < 0:
        axis = range(a.d)
    elif isinstance(axis, int):
        axis = [axis]
    axis = list(axis)[::-1]
    for ax in axis:
        crs[ax] = _np.sum(crs[ax], axis=1)
        rleft, rright = crs[ax].shape
        if (rleft >= rright or rleft < rright and ax + 1 >= d) and ax > 0:
            crs[ax - 1] = _np.tensordot(crs[ax - 1], crs[ax], axes=(2, 0))
        elif ax + 1 < d:
            crs[ax + 1] = _np.tensordot(crs[ax], crs[ax + 1], axes=(1, 0))
        else:
            return _np.sum(crs[ax])
        crs.pop(ax)
        d -= 1
    return _vector.vector.from_list(crs)


Instruction:
How would you explain what the following Python 3 function does
def ones(n, d=None):
    c = _vector.vector()
    if d is None:
        c.n = _np.array(n, dtype=_np.int32)
        c.d = c.n.size
    else:
        c.n = _np.array([n] * d, dtype=_np.int32)
        c.d = d
    c.r = _np.ones((c.d + 1,), dtype=_np.int32)
    c.get_ps()
    c.core = _np.ones(c.ps[c.d] - 1)
    return c

Response: Creates a TT - vector of all ones


Instruction:
Make a summary of the following Python 3 code
def rand(n, d=None, r=2, samplefunc=_np.random.randn):
    n0 = _np.asanyarray(n, dtype=_np.int32)
    r0 = _np.asanyarray(r, dtype=_np.int32)
    if d is None:
        d = n.size
    if n0.size is 1:
        n0 = _np.ones((d,), dtype=_np.int32) * n0
    if r0.size is 1:
        r0 = _np.ones((d + 1,), dtype=_np.int32) * r0
        r0[0] = 1
        r0[d] = 1
    c = _vector.vector()
    c.d = d
    c.n = n0
    c.r = r0
    c.get_ps()
    c.core = samplefunc(c.ps[d] - 1)
    return c

Response: Generate a random d - dimensional TT - vector with ranks r.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def eye(n, d=None):
    c = _matrix.matrix()
    c.tt = _vector.vector()
    if d is None:
        n0 = _np.asanyarray(n, dtype=_np.int32)
        c.tt.d = n0.size
    else:
        n0 = _np.asanyarray([n] * d, dtype=_np.int32)
        c.tt.d = d
    c.n = n0.copy()
    c.m = n0.copy()
    c.tt.n = (c.n) * (c.m)
    c.tt.r = _np.ones((c.tt.d + 1,), dtype=_np.int32)
    c.tt.get_ps()
    c.tt.alloc_core()
    for i in xrange(c.tt.d):
        c.tt.core[
        c.tt.ps[i] -
        1:c.tt.ps[
              i +
              1] -
          1] = _np.eye(
            c.n[i]).flatten()
    return c

Response: Creates an identity TT - matrix


Instruction:
Can you write a function in Python 3 where it
creates multilevel Toeplitz TT - matrix with D levels.

Response:
def Toeplitz(x, d=None, D=None, kind='F'):
    """ Creates multilevel Toeplitz TT-matrix with ``D`` levels.

        Possible _matrix types:

        * 'F' - full Toeplitz _matrix,             size(x) = 2^{d+1}
        * 'C' - circulant _matrix,                 size(x) = 2^d
        * 'L' - lower triangular Toeplitz _matrix, size(x) = 2^d
        * 'U' - upper triangular Toeplitz _matrix, size(x) = 2^d

        Sample calls:

        >>> # one-level Toeplitz _matrix:
        >>> T = tt.Toeplitz(x)
        >>> # one-level circulant _matrix:
        >>> T = tt.Toeplitz(x, kind='C')
        >>> # three-level upper-triangular Toeplitz _matrix:
        >>> T = tt.Toeplitz(x, D=3, kind='U')
        >>> # two-level mixed-type Toeplitz _matrix:
        >>> T = tt.Toeplitz(x, kind=['L', 'U'])
        >>> # two-level mixed-size Toeplitz _matrix:
        >>> T = tt.Toeplitz(x, [3, 4], kind='C')

    """

    # checking for arguments consistency
    def check_kinds(D, kind):
        if D % len(kind) == 0:
            kind.extend(kind * (D // len(kind) - 1))
        if len(kind) != D:
            raise ValueError(
                "Must give proper amount of _matrix kinds (one or D, for example)")

    kind = list(kind)
    if not set(kind).issubset(['F', 'C', 'L', 'U']):
        raise ValueError("Toeplitz _matrix kind must be one of F, C, L, U.")
    if d is None:
        if D is None:
            D = len(kind)
        if x.d % D:
            raise ValueError(
                "x.d must be divisible by D when d is not specified!")
        if len(kind) == 1:
            d = _np.array([x.d // D - (1 if kind[0] == 'F' else 0)]
                          * D, dtype=_np.int32)
            kind = kind * D
        else:
            check_kinds(D, kind)
            if set(kind).issubset(['F']):
                d = _np.array([x.d // D - 1] * D, dtype=_np.int32)
            elif set(kind).issubset(['C', 'L', 'U']):
                d = _np.array([x.d // D] * D, dtype=_np.int32)
            else:
                raise ValueError(
                    "Only similar _matrix kinds (only F or only C, L and U) are accepted when d is not specified!")
    elif d is not None:
        d = _np.asarray(d, dtype=_np.int32).flatten()
        if D is None:
            D = d.size
        elif d.size == 1:
            d = _np.array([d[0]] * D, dtype=_np.int32)
        if D != d.size:
            raise ValueError("D must be equal to len(d)")
        check_kinds(D, kind)
        if _np.sum(d) + _np.sum([(1 if knd == 'F' else 0)
                                 for knd in kind]) != x.d:
            raise ValueError(
                "Dimensions inconsistency: x.d != d_1 + d_2 + ... + d_D")

    # predefined matrices and tensors:
    I = [[1, 0], [0, 1]]
    J = [[0, 1], [0, 0]]
    JT = [[0, 0], [1, 0]]
    H = [[0, 1], [1, 0]]
    S = _np.array([[[0], [1]], [[1], [0]]]).transpose()  # 2 x 2 x 1
    P = _np.zeros((2, 2, 2, 2))
    P[:, :, 0, 0] = I
    P[:, :, 1, 0] = H
    P[:, :, 0, 1] = H
    P[:, :, 1, 1] = I
    P = _np.transpose(P)  # 2 x 2! x 2 x 2 x '1'
    Q = _np.zeros((2, 2, 2, 2))
    Q[:, :, 0, 0] = I
    Q[:, :, 1, 0] = JT
    Q[:, :, 0, 1] = JT
    Q = _np.transpose(Q)  # 2 x 2! x 2 x 2 x '1'
    R = _np.zeros((2, 2, 2, 2))
    R[:, :, 1, 0] = J
    R[:, :, 0, 1] = J
    R[:, :, 1, 1] = I
    R = _np.transpose(R)  # 2 x 2! x 2 x 2 x '1'
    W = _np.zeros([2] * 5)  # 2 x 2! x 2 x 2 x 2
    W[0, :, :, 0, 0] = W[1, :, :, 1, 1] = I
    W[0, :, :, 1, 0] = W[0, :, :, 0, 1] = JT
    W[1, :, :, 1, 0] = W[1, :, :, 0, 1] = J
    W = _np.transpose(W)  # 2 x 2! x 2 x 2 x 2
    V = _np.zeros((2, 2, 2, 2))
    V[0, :, :, 0] = I
    V[0, :, :, 1] = JT
    V[1, :, :, 1] = J
    V = _np.transpose(V)  # '1' x 2! x 2 x 2 x 2

    crs = []
    xcrs = _vector.vector.to_list(x)
    dp = 0  # dimensions passed
    for j in xrange(D):
        currd = d[j]
        xcr = xcrs[dp]
        cr = _np.tensordot(V, xcr, (0, 1))
        cr = cr.transpose(3, 0, 1, 2, 4)  # <r_dp| x 2 x 2 x |2> x |r_{dp+1}>
        cr = cr.reshape((x.r[dp], 2, 2, 2 * x.r[dp + 1]),
                        order='F')  # <r_dp| x 2 x 2 x |2r_{dp+1}>
        dp += 1
        crs.append(cr)
        for i in xrange(1, currd - 1):
            xcr = xcrs[dp]
            # (<2| x 2 x 2 x |2>) x <r_dp| x |r_{dp+1}>
            cr = _np.tensordot(W, xcr, (1, 1))
            # <2| x <r_dp| x 2 x 2 x |2> x |r_{dp+1}>
            cr = cr.transpose([0, 4, 1, 2, 3, 5])
            # <2r_dp| x 2 x 2 x |2r_{dp+1}>
            cr = cr.reshape((2 * x.r[dp], 2, 2, 2 * x.r[dp + 1]), order='F')
            dp += 1
            crs.append(cr)
        if kind[j] == 'F':
            xcr = xcrs[dp]  # r_dp x 2 x r_{dp+1}
            cr = _np.tensordot(W, xcr, (1, 1)).transpose([0, 4, 1, 2, 3, 5])
            # <2r_dp| x 2 x 2 x |2r_{dp+1}>
            cr = cr.reshape((2 * x.r[dp], 2, 2, 2 * x.r[dp + 1]), order='F')
            dp += 1
            xcr = xcrs[dp]  # r_dp x 2 x r_{dp+1}
            # <2| x |1> x <r_dp| x |r_{dp+1}>
            tmp = _np.tensordot(S, xcr, (1, 1))
            # tmp = tmp.transpose([0, 2, 1, 3]) # TODO: figure out WHY THE HELL
            # this spoils everything
            # <2r_dp| x |r_{dp+1}>
            tmp = tmp.reshape((2 * x.r[dp], x.r[dp + 1]), order='F')
            # <2r_{dp-1}| x 2 x 2 x |r_{dp+1}>
            cr = _np.tensordot(cr, tmp, (3, 0))
            dp += 1
            crs.append(cr)
        else:
            dotcore = None
            if kind[j] == 'C':
                dotcore = P
            elif kind[j] == 'L':
                dotcore = Q
            elif kind[j] == 'U':
                dotcore = R
            xcr = xcrs[dp]  # r_dp x 2 x r_{dp+1}
            # <2| x 2 x 2 x |'1'> x <r_dp| x |r_{dp+1}>
            cr = _np.tensordot(dotcore, xcr, (1, 1))
            # <2| x <r_dp| x 2 x 2 x |r_{dp+1}>
            cr = cr.transpose([0, 3, 1, 2, 4])
            cr = cr.reshape((2 * x.r[dp], 2, 2, x.r[dp + 1]), order='F')
            dp += 1
            crs.append(cr)
    return _matrix.matrix.from_list(crs)


Instruction:
Can you tell what is the following Python 3 function doing
def qlaplace_dd(d):
    res = _matrix.matrix()
    d0 = d[::-1]
    D = len(d0)
    I = _np.eye(2)
    J = _np.array([[0, 1], [0, 0]])
    cr = []
    if D is 1:
        for k in xrange(1, d0[0] + 1):
            if k is 1:
                cur_core = _np.zeros((1, 2, 2, 3))
                cur_core[:, :, :, 0] = 2 * I - J - J.T
                cur_core[:, :, :, 1] = -J
                cur_core[:, :, :, 2] = -J.T
            elif k is d0[0]:
                cur_core = _np.zeros((3, 2, 2, 1))
                cur_core[0, :, :, 0] = I
                cur_core[1, :, :, 0] = J.T
                cur_core[2, :, :, 0] = J
            else:
                cur_core = _np.zeros((3, 2, 2, 3))
                cur_core[0, :, :, 0] = I
                cur_core[1, :, :, 1] = J
                cur_core[2, :, :, 2] = J.T
                cur_core[1, :, :, 0] = J.T
                cur_core[2, :, :, 0] = J
            cr.append(cur_core)
    else:
        for k in xrange(D):
            for kappa in xrange(1, d0[k] + 1):
                if kappa is 1:
                    if k is 0:
                        cur_core = _np.zeros((1, 2, 2, 4))
                        cur_core[:, :, :, 0] = 2 * I - J - J.T
                        cur_core[:, :, :, 1] = -J
                        cur_core[:, :, :, 2] = -J.T
                        cur_core[:, :, :, 3] = I
                    elif k is D - 1:
                        cur_core = _np.zeros((2, 2, 2, 3))
                        cur_core[0, :, :, 0] = 2 * I - J - J.T
                        cur_core[0, :, :, 1] = -J
                        cur_core[0, :, :, 2] = -J.T
                        cur_core[1, :, :, 0] = I
                    else:
                        cur_core = _np.zeros((2, 2, 2, 4))
                        cur_core[0, :, :, 0] = 2 * I - J - J.T
                        cur_core[0, :, :, 1] = -J
                        cur_core[0, :, :, 2] = -J.T
                        cur_core[0, :, :, 3] = I
                        cur_core[1, :, :, 0] = I
                elif kappa is d0[k]:
                    if k is D - 1:
                        cur_core = _np.zeros((3, 2, 2, 1))
                        cur_core[0, :, :, 0] = I
                        cur_core[1, :, :, 0] = J.T
                        cur_core[2, :, :, 0] = J
                    else:
                        cur_core = _np.zeros((4, 2, 2, 2))
                        cur_core[3, :, :, 0] = I
                        cur_core[0, :, :, 1] = I
                        cur_core[1, :, :, 1] = J.T
                        cur_core[2, :, :, 1] = J
                else:
                    if k is D - 1:
                        cur_core = _np.zeros((3, 2, 2, 3))
                        cur_core[0, :, :, 0] = I
                        cur_core[1, :, :, 1] = J
                        cur_core[2, :, :, 2] = J.T
                        cur_core[1, :, :, 0] = J.T
                        cur_core[2, :, :, 0] = J
                    else:
                        cur_core = _np.zeros((4, 2, 2, 4))
                        cur_core[0, :, :, 0] = I
                        cur_core[1, :, :, 1] = J
                        cur_core[2, :, :, 2] = J.T
                        cur_core[1, :, :, 0] = J.T
                        cur_core[2, :, :, 0] = J
                        cur_core[3, :, :, 3] = I
                cr.append(cur_core)
    return _matrix.matrix.from_list(cr)

Response: Creates a QTT representation of the Laplace operator


Instruction:
Here you have a function in Python 3, explain what it does
def xfun(n, d=None):
    if isinstance(n, six.integer_types):
        n = [n]
    if d is None:
        n0 = _np.asanyarray(n, dtype=_np.int32)
    else:
        n0 = _np.array(n * d, dtype=_np.int32)
    d = n0.size
    if d == 1:
        return _vector.vector.from_list(
            [_np.reshape(_np.arange(n0[0]), (1, n0[0], 1))])
    cr = []
    cur_core = _np.ones((1, n0[0], 2))
    cur_core[0, :, 0] = _np.arange(n0[0])
    cr.append(cur_core)
    ni = float(n0[0])
    for i in xrange(1, d - 1):
        cur_core = _np.zeros((2, n0[i], 2))
        for j in xrange(n0[i]):
            cur_core[:, j, :] = _np.eye(2)
        cur_core[1, :, 0] = ni * _np.arange(n0[i])
        ni *= n0[i]
        cr.append(cur_core)
    cur_core = _np.ones((2, n0[d - 1], 1))
    cur_core[1, :, 0] = ni * _np.arange(n0[d - 1])
    cr.append(cur_core)
    return _vector.vector.from_list(cr)

Response: Create a QTT - representation of 0 n - > prod


Instruction:
Can you implement a function in Python 3 that
creates a QTT - representation of a uniform grid on an interval [ a b )

Response:
def linspace(n, d=None, a=0.0, b=1.0, right=True, left=True):
    """ Create a QTT-representation of a uniform grid on an interval [a, b] """
    if isinstance(n, six.integer_types):
        n = [n]
    if d is None:
        n0 = _np.asanyarray(n, dtype=_np.int32)
    else:
        n0 = _np.array(n * d, dtype=_np.int32)
    d = n0.size
    t = xfun(n0)
    e = ones(n0)
    N = _np.prod(n0)  # Size
    if left and right:
        h = (b - a) * 1.0 / (N - 1)
        res = a * e + t * h
    elif left and not right:
        h = (b - a) * 1.0 / N
        res = a * e + t * h
    elif right and not left:
        h = (b - a) * 1.0 / N
        res = a * e + (t + e) * h
    else:
        h = (b - a) * 1.0 / (N - 1)
        res = a * e + (t + e) * h
    return res.round(1e-13)


Instruction:
How would you implement a function in Python 3 that
creates TT - vector for the sin function.

Response:
def sin(d, alpha=1.0, phase=0.0):
    """ Create TT-vector for :math:`\\sin(\\alpha n + \\varphi)`."""
    cr = []
    cur_core = _np.zeros([1, 2, 2], dtype=_np.float)
    cur_core[0, 0, :] = [_math.cos(phase), _math.sin(phase)]
    cur_core[0, 1, :] = [_math.cos(alpha + phase), _math.sin(alpha + phase)]
    cr.append(cur_core)
    for i in xrange(1, d - 1):
        cur_core = _np.zeros([2, 2, 2], dtype=_np.float)
        cur_core[0, 0, :] = [1.0, 0.0]
        cur_core[1, 0, :] = [0.0, 1.0]
        cur_core[
        0,
        1,
        :] = [
            _math.cos(
                alpha *
                2 ** i),
            _math.sin(
                alpha *
                2 ** i)]
        cur_core[1,
        1,
        :] = [-_math.sin(alpha * 2 ** i),
              _math.cos(alpha * 2 ** i)]
        cr.append(cur_core)
    cur_core = _np.zeros([2, 2, 1], dtype=_np.float)
    cur_core[0, :, 0] = [0.0, _math.sin(alpha * 2 ** (d - 1))]
    cur_core[1, :, 0] = [1.0, _math.cos(alpha * 2 ** (d - 1))]
    cr.append(cur_core)
    return _vector.vector.from_list(cr)


Instruction:
How would you explain what the following Python 3 function does
def cos(d, alpha=1.0, phase=0.0):
    return sin(d, alpha, phase + _math.pi * 0.5)

Response: Create TT - vector for cos function.


Instruction:
Write a Python 3 function for
creating TT - vector for delta - function.

Response:
def delta(n, d=None, center=0):
    """ Create TT-vector for delta-function :math:`\\delta(x - x_0)`. """
    if isinstance(n, six.integer_types):
        n = [n]
    if d is None:
        n0 = _np.asanyarray(n, dtype=_np.int32)
    else:
        n0 = _np.array(n * d, dtype=_np.int32)
    d = n0.size

    if center < 0:
        cind = [0] * d
    else:
        cind = []
        for i in xrange(d):
            cind.append(center % n0[i])
            center //= n0[i]
        if center > 0:
            cind = [0] * d
    cr = []
    for i in xrange(d):
        cur_core = _np.zeros((1, n0[i], 1))
        cur_core[0, cind[i], 0] = 1
        cr.append(cur_core)
    return _vector.vector.from_list(cr)


Instruction:
Explain what the following Python 3 code does
def stepfun(n, d=None, center=1, direction=1):
    if isinstance(n, six.integer_types):
        n = [n]
    if d is None:
        n0 = _np.asanyarray(n, dtype=_np.int32)
    else:
        n0 = _np.array(n * d, dtype=_np.int32)
    d = n0.size
    N = _np.prod(n0)

    if center >= N and direction < 0 or center <= 0 and direction > 0:
        return ones(n0)

    if center <= 0 and direction < 0 or center >= N and direction > 0:
        raise ValueError(
            "Heaviside function with specified center and direction gives zero tensor!")
    if direction > 0:
        center = N - center
    cind = []
    for i in xrange(d):
        cind.append(center % n0[i])
        center //= n0[i]

    def gen_notx(currcind, currn):
        return [0.0] * (currn - currcind) + [1.0] * currcind

    def gen_notx_rev(currcind, currn):
        return [1.0] * currcind + [0.0] * (currn - currcind)

    def gen_x(currcind, currn):
        result = [0.0] * currn
        result[currn - currcind - 1] = 1.0
        return result

    def gen_x_rev(currcind, currn):
        result = [0.0] * currn
        result[currcind] = 1.0
        return result

    if direction > 0:
        x = gen_x
        notx = gen_notx
    else:
        x = gen_x_rev
        notx = gen_notx_rev

    crs = []
    prevrank = 1
    for i in range(d)[::-1]:
        break_further = max([0] + cind[:i])
        nextrank = 2 if break_further else 1
        one = [1] * n0[i]
        cr = _np.zeros([nextrank, n0[i], prevrank], dtype=_np.float)
        tempx = x(cind[i], n0[i])
        tempnotx = notx(cind[i], n0[i])
        # high-conditional magic
        if not break_further:
            if cind[i]:
                if prevrank > 1:
                    cr[0, :, 0] = one
                    cr[0, :, 1] = tempnotx
                else:
                    cr[0, :, 0] = tempnotx
            else:
                cr[0, :, 0] = one
        else:
            if prevrank > 1:
                cr[0, :, 0] = one
                if cind[i]:
                    cr[0, :, 1] = tempnotx
                    cr[1, :, 1] = tempx
                else:
                    cr[1, :, 1] = tempx
            else:
                if cind[i]:
                    cr[0, :, 0] = tempnotx
                    cr[1, :, 0] = tempx
                else:
                    nextrank = 1
                    cr = cr[:1, :, :]
                    cr[0, :, 0] = tempx
        prevrank = nextrank
        crs.append(cr)
    return _vector.vector.from_list(crs[::-1])

Response: Create TT - vector for Heaviside step function.


Instruction:
Write a Python 3 script to
generate a list of all the available units in a single tree.

Response:
def unit(n, d=None, j=None, tt_instance=True):
    ''' Generates e_j _vector in tt.vector format
    ---------
    Parameters:
        n - modes (either integer or array)
        d - dimensionality (integer)
        j - position of 1 in full-format e_j (integer)
        tt_instance - if True, returns tt.vector;
                      if False, returns tt cores as a list
    '''
    if isinstance(n, int):
        if d is None:
            d = 1
        n = n * _np.ones(d, dtype=_np.int32)
    else:
        d = len(n)
    if j is None:
        j = 0
    rv = []

    j = _ind2sub(n, j)

    for k in xrange(d):
        rv.append(_np.zeros((1, n[k], 1)))
        rv[-1][0, j[k], 0] = 1
    if tt_instance:
        rv = _vector.vector.from_list(rv)
    return rv


Instruction:
Can you tell what is the following Python 3 function doing
def permute(x, order, eps=None, return_cores=False):
    '''
    Permute dimensions (python translation of original matlab code)
       Y = permute(X, ORDER, EPS) permutes the dimensions of the TT-tensor X
       according to ORDER, delivering a result at relative accuracy EPS. This
       function is equivalent to
          Y = tt_tensor(permute(reshape(full(X), X.n'),ORDER), EPS)
       but avoids the conversion to the full format.

     Simon Etter, Summer 2015
     Seminar of Applied Mathematics, ETH Zurich

     TT-Toolbox 2.2, 2009-2012

    This is TT Toolbox, written by Ivan Oseledets et al. Institute of
    Numerical Mathematics, Moscow, Russia webpage:
    http://spring.inm.ras.ru/osel

    For all questions, bugs and suggestions please mail
    ivan.oseledets@gmail.com
    ---------------------------

     This code basically performs insertion sort on the TT dimensions:
      for k = 2:d
         Bubble the kth dimension to the right (according to ORDER) position in the first 1:k dimensions.

     The current code could be optimised at the following places:
      - Instead of initially orthogonalising with respect to the first two vertices,
        orthogonalise directly with respect to the first inversion.
      - When performing the SVD, check on which side of the current position the
        next swap will occur and directly establish the appropriate orthogonality
        (current implementation always assumes we move left).
     Both changes reduce the number of QRs by at most O(d) and are therefore likely
     to produce negligible speedup while rendering the code more complicated.
     '''

    def _reshape(tensor, shape):
        return _np.reshape(tensor, shape, order='F')

    # Parse input
    if eps is None:
        eps = _np.spacing(1)
    cores = _vector.vector.to_list(x)
    d = _cp.deepcopy(x.d)
    n = _cp.deepcopy(x.n)
    r = _cp.deepcopy(x.r)

    idx = _np.empty(len(order))
    idx[order] = _np.arange(len(order))
    eps /= d ** 1.5
    # ^Numerical evidence suggests that eps = eps/d may be sufficient, however I can only prove correctness
    #  for this choice of global-to-local conversion factor.
    assert len(order) > d - 1, 'ORDER must have at least D elements for a D-dimensional tensor'

    # RL-orthogonalise x
    for kk in xrange(d - 1, 1, -1):  ##########################################
        new_shape = [r[kk], n[kk] * r[kk + 1]]
        Q, R = _np.linalg.qr(_reshape(cores[kk], new_shape).T)
        tr = min(new_shape)
        cores[kk] = _reshape(Q.T, [tr, n[kk], r[kk + 1]])
        tmp = _reshape(cores[kk - 1], [r[kk - 1] * n[kk - 1], r[kk]])
        tmp = _np.dot(tmp, R.T)
        cores[kk - 1] = _reshape(tmp, [r[kk - 1], n[kk - 1], tr])
        r[kk] = tr
    k = 0
    while (True):
        # Find next inversion
        nk = k
        while (nk < d - 1) and (idx[nk] < idx[nk + 1]):
            nk += 1
        if (nk == d - 1):
            break

        # Move orthogonal centre there
        for kk in xrange(k, nk - 1):  #############
            new_shape = [r[kk] * n[kk], r[kk + 1]]
            Q, R = _np.linalg.qr(_reshape(cores[kk], new_shape))
            tr = min(new_shape)
            new_shape = [r[kk], n[kk], tr]
            cores[kk] = _reshape(Q, new_shape)
            tmp = _reshape(cores[kk + 1], [r[kk + 1], n[kk + 1] * r[kk + 2]])
            tmp = _np.dot(R, tmp)
            cores[kk + 1] = _reshape(tmp, [tr, n[kk + 1], r[kk + 2]])
            r[kk + 1] = tr
        k = nk

        # Swap dimensions
        tmp = _reshape(cores[k], [r[k] * n[k], r[k + 1]])
        tmp = _np.dot(tmp, _reshape(cores[k + 1], [r[k + 1], n[k + 1] * r[k + 2]]))
        c = _reshape(tmp, [r[k], n[k], n[k + 1], r[k + 2]])
        c = _np.transpose(c, [0, 2, 1, 3])
        tmp = _reshape(c, [r[k] * n[k + 1], n[k] * r[k + 2]])
        U, S, Vt = _np.linalg.svd(tmp, full_matrices=False)
        r[k + 1] = max(_my_chop2(S, _np.linalg.norm(S) * eps), 1)
        lenS = len(S)
        tmp = U[:, :lenS] * S  # multiplication by diagonal matrix
        cores[k] = _reshape(tmp[:, :r[k + 1]], [r[k], n[k + 1], r[k + 1]])
        cores[k + 1] = _reshape(Vt[:r[k + 1], :], [r[k + 1], n[k], r[k + 2]])
        idx[[k, k + 1]] = idx[[k + 1, k]]
        n[[k, k + 1]] = n[[k + 1, k]]
        k = max(k - 1, 0)  ##################
    # Parse output
    if return_cores:
        return cores
    return _vector.vector.from_list(cores)

Response: Permute the dimensions of the TT - tensor X according to the given order and return the resulting vector.


Instruction:
How would you code a function in Python 3 to
find the approximate minimum value of the function on a d - dimensional grid.

Response:
def min_func(fun, bounds_min, bounds_max, d=None, rmax=10,
             n0=64, nswp=10, verb=True, smooth_fun=None):
    """Find (approximate) minimal value of the function on a d-dimensional grid."""
    if d is None:
        d = len(bounds_min)
        a = np.asanyarray(bounds_min).copy()
        b = np.asanyarray(bounds_max).copy()
    else:
        a = np.ones(d) * bounds_min
        b = np.ones(d) * bounds_max

    if smooth_fun is None:
        smooth_fun = lambda p, lam: (math.pi / 2 - np.arctan(p - lam))
    #smooth_fun = lambda p, lam: np.exp(-10*(p - lam))

    # We do not need to store the cores, only the interfaces!
    Rx = [[]] * (d + 1)  # Python list for the interfaces
    Rx[0] = np.ones((1, 1))
    Rx[d] = np.ones((1, 1))
    Jy = [np.empty(0, dtype=np.int)] * (d + 1)
    ry = rmax * np.ones(d + 1, dtype=np.int)
    ry[0] = 1
    ry[d] = 1
    n = n0 * np.ones(d, dtype=np.int)
    fun_evals = 0

    grid = [np.reshape(np.linspace(a[i], b[i], n[i]), (n[i], 1))
            for i in xrange(d)]
    for i in xrange(d - 1):
        #cr1 = y[i]
        ry[i + 1] = min(ry[i + 1], n[i] * ry[i])
        cr1 = np.random.randn(ry[i], n[i], ry[i + 1])
        cr1 = reshape(cr1, (ry[i] * n[i], ry[i + 1]))
        q, r = np.linalg.qr(cr1)
        ind = maxvol(q)
        w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i])
        w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int))
        Jy[i + 1] = np.hstack((w1, w2))
        Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1))
        Jy[i + 1] = Jy[i + 1][ind, :]

        # Jy{i+1} = [kron(ones(n(i),1), Jy{i}), kron((1:n(i))', ones(ry(i),1))];
        # Jy{i+1} = Jy{i+1}(ind,:);
    swp = 0
    dirn = -1
    i = d - 1
    lm = float('Inf')
    while swp < nswp:
        # Right-to-left sweep
        # The idea: compute the current core; compute the function of it;
        # Shift locally or globally? Local shift would be the first try
        # Compute the current core

        if np.size(Jy[i]) == 0:
            w1 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int)
        else:
            w1 = mkron(np.ones((n[i] * ry[i + 1], 1), dtype=np.int), Jy[i])
        w2 = mkron(mkron(np.ones((ry[i + 1], 1), dtype=np.int),
                         grid[i]), np.ones((ry[i], 1), dtype=np.int))
        if np.size(Jy[i + 1]) == 0:
            w3 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int)
        else:
            w3 = mkron(Jy[i + 1], np.ones((ry[i] * n[i], 1), dtype=np.int))

        J = np.hstack((w1, w2, w3))
        # Just add some random indices to J, which is rnr x d, need to make rn (r + r0) x add,
        # i.e., just generate random r, random n and random multiindex

        cry = fun(J)
        fun_evals += cry.size
        cry = reshape(cry, (ry[i], n[i], ry[i + 1]))
        min_cur = np.min(cry.flatten("F"))
        ind_cur = np.argmin(cry.flatten("F"))
        if lm > min_cur:
            lm = min_cur
            x_full = J[ind_cur, :]
            val = fun(x_full)
            if verb:
                print('New record:', val, 'Point:', x_full, 'fevals:', fun_evals)
        cry = smooth_fun(cry, lm)
        if (dirn < 0 and i > 0):
            cry = reshape(cry, (ry[i], n[i] * ry[i + 1]))
            cry = cry.T
            #q, r = np.linalg.qr(cry)
            u, s, v = mysvd(cry, full_matrices=False)
            ry[i] = min(ry[i], rmax)
            q = u[:, :ry[i]]
            ind = rect_maxvol(q)[0]  # maxvol(q)
            ry[i] = ind.size
            w1 = mkron(np.ones((ry[i + 1], 1), dtype=np.int), grid[i])
            if np.size(Jy[i + 1]) == 0:
                w2 = np.zeros((n[i] * ry[i + 1], 0), dtype=np.int)
            else:
                w2 = mkron(Jy[i + 1], np.ones((n[i], 1), dtype=np.int))
            Jy[i] = np.hstack((w1, w2))
            Jy[i] = reshape(Jy[i], (n[i] * ry[i + 1], -1))
            Jy[i] = Jy[i][ind, :]

        if (dirn > 0 and i < d - 1):
            cry = reshape(cry, (ry[i] * n[i], ry[i + 1]))
            q, r = np.linalg.qr(cry)
            #ind = maxvol(q)
            ind = rect_maxvol(q)[0]
            ry[i + 1] = ind.size
            w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i])
            w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int))
            Jy[i + 1] = np.hstack((w1, w2))
            Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1))
            Jy[i + 1] = Jy[i + 1][ind, :]

        i += dirn
        if i == d or i == -1:
            dirn = -dirn
            i += dirn
            swp = swp + 1
    return val, x_full


Instruction:
Can you implement a function in Python 3 that
finds the approximate minimum element in a TT - tensor.

Response:
def min_tens(tens, rmax=10, nswp=10, verb=True, smooth_fun=None):
    """Find (approximate) minimal element in a TT-tensor."""
    if smooth_fun is None:
        smooth_fun = lambda p, lam: (math.pi / 2 - np.arctan(p - lam))
    d = tens.d
    Rx = [[]] * (d + 1)  # Python list for the interfaces
    Rx[0] = np.ones((1, 1))
    Rx[d] = np.ones((1, 1))
    Jy = [np.empty(0, dtype=np.int)] * (d + 1)
    ry = rmax * np.ones(d + 1, dtype=np.int)
    ry[0] = 1
    ry[d] = 1
    n = tens.n
    elements_seen = 0
    phi_left = [np.empty(0)] * (d + 1)
    phi_left[0] = np.array([1])
    phi_right = [np.empty(0)] * (d + 1)
    phi_right[d] = np.array([1])
    cores = tt.tensor.to_list(tens)

    # Fill initial multiindex J randomly.
    grid = [np.reshape(range(n[i]), (n[i], 1)) for i in xrange(d)]
    for i in xrange(d - 1):
        ry[i + 1] = min(ry[i + 1], n[i] * ry[i])
        ind = sorted(np.random.permutation(ry[i] * n[i])[0:ry[i + 1]])
        w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i])
        w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int))
        Jy[i + 1] = np.hstack((w1, w2))
        Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1))
        Jy[i + 1] = Jy[i + 1][ind, :]
        phi_left[i + 1] = np.tensordot(phi_left[i], cores[i], 1)
        phi_left[i + 1] = reshape(phi_left[i + 1], (ry[i] * n[i], -1))
        phi_left[i + 1] = phi_left[i + 1][ind, :]
    swp = 0
    dirn = -1
    i = d - 1
    lm = float('Inf')
    while swp < nswp:
        # Right-to-left sweep
        # The idea: compute the current core; compute the function of it;
        # Shift locally or globally? Local shift would be the first try
        # Compute the current core

        if np.size(Jy[i]) == 0:
            w1 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int)
        else:
            w1 = mkron(np.ones((n[i] * ry[i + 1], 1), dtype=np.int), Jy[i])
        w2 = mkron(mkron(np.ones((ry[i + 1], 1), dtype=np.int),
                         grid[i]), np.ones((ry[i], 1), dtype=np.int))
        if np.size(Jy[i + 1]) == 0:
            w3 = np.zeros((ry[i] * n[i] * ry[i + 1], 0), dtype=np.int)
        else:
            w3 = mkron(Jy[i + 1], np.ones((ry[i] * n[i], 1), dtype=np.int))
        J = np.hstack((w1, w2, w3))

        phi_right[i] = np.tensordot(cores[i], phi_right[i + 1], 1)
        phi_right[i] = reshape(phi_right[i], (-1, n[i] * ry[i + 1]))

        cry = np.tensordot(
            phi_left[i], np.tensordot(
                cores[i], phi_right[
                    i + 1], 1), 1)
        elements_seen += cry.size
        cry = reshape(cry, (ry[i], n[i], ry[i + 1]))
        min_cur = np.min(cry.flatten("F"))
        ind_cur = np.argmin(cry.flatten("F"))
        if lm > min_cur:
            lm = min_cur
            x_full = J[ind_cur, :]
            val = tens[x_full]
            if verb:
                print('New record:', val, 'Point:', x_full, 'elements seen:', elements_seen)
        cry = smooth_fun(cry, lm)
        if dirn < 0 and i > 0:
            cry = reshape(cry, (ry[i], n[i] * ry[i + 1]))
            cry = cry.T
            #q, r = np.linalg.qr(cry)
            u, s, v = mysvd(cry, full_matrices=False)
            ry[i] = min(ry[i], rmax)
            q = u[:, :ry[i]]
            ind = rect_maxvol(q)[0]  # maxvol(q)
            ry[i] = ind.size
            w1 = mkron(np.ones((ry[i + 1], 1), dtype=np.int), grid[i])
            if np.size(Jy[i + 1]) == 0:
                w2 = np.zeros((n[i] * ry[i + 1], 0), dtype=np.int)
            else:
                w2 = mkron(Jy[i + 1], np.ones((n[i], 1), dtype=np.int))
            Jy[i] = np.hstack((w1, w2))
            Jy[i] = reshape(Jy[i], (n[i] * ry[i + 1], -1))
            Jy[i] = Jy[i][ind, :]
            phi_right[i] = np.tensordot(cores[i], phi_right[i + 1], 1)
            phi_right[i] = reshape(phi_right[i], (-1, n[i] * ry[i + 1]))
            phi_right[i] = phi_right[i][:, ind]

        if dirn > 0 and i < d - 1:
            cry = reshape(cry, (ry[i] * n[i], ry[i + 1]))
            q, r = np.linalg.qr(cry)
            #ind = maxvol(q)
            ind = rect_maxvol(q)[0]
            ry[i + 1] = ind.size
            phi_left[i + 1] = np.tensordot(phi_left[i], cores[i], 1)
            phi_left[i + 1] = reshape(phi_left[i + 1], (ry[i] * n[i], -1))
            phi_left[i + 1] = phi_left[i + 1][ind, :]
            w1 = mkron(np.ones((n[i], 1), dtype=np.int), Jy[i])
            w2 = mkron(grid[i], np.ones((ry[i], 1), dtype=np.int))
            Jy[i + 1] = np.hstack((w1, w2))
            Jy[i + 1] = reshape(Jy[i + 1], (ry[i] * n[i], -1))
            Jy[i + 1] = Jy[i + 1][ind, :]

        i += dirn
        if i == d or i == -1:
            dirn = -dirn
            i += dirn
            swp = swp + 1
    return val, x_full


Instruction:
Create a Python 3 function to
compute the orthogonal matrix Q_{ \ leq i as defined in [ 1 ].

Response:
def left(X, i):
    """Compute the orthogonal matrix Q_{\leq i} as defined in [1]."""
    if i < 0:
        return np.ones([1, 1])
    answ = np.ones([1, 1])
    cores = tt.tensor.to_list(X)
    for dim in xrange(i+1):
        answ = np.tensordot(answ, cores[dim], 1)
    answ = reshape(answ, (-1, X.r[i+1]))
    return answ


Instruction:
Explain what the following Python 3 code does
def right(X, i):
    if i > X.d-1:
        return np.ones([1, 1])
    answ = np.ones([1, 1])
    cores = tt.tensor.to_list(X)
    for dim in xrange(X.d-1, i-1, -1):
        answ = np.tensordot(cores[dim], answ, 1)
    answ = reshape(answ, (X.r[i], -1))
    return answ.T

Response: Compute the orthogonal matrix Q_{ \ geq i as defined in [ 1 ].


Instruction:
Write a Python 3 function that can
compute the i - th unfolding of a tensor.

Response:
def unfolding(tens, i):
    """Compute the i-th unfolding of a tensor."""
    return reshape(tens.full(), (np.prod(tens.n[0:(i+1)]), -1))


Instruction:
How would you implement a function in Python 3 that
updates the lhs matrix with the new values from the lhs matrix.

Response:
def _update_lhs(lhs, xCore, zCore, new_lhs):
    """ Function to be called from the project()"""
    # TODO: Use intermediate variable to use 5 nested loops instead of 6.
    r_old_x, n, r_x = xCore.shape
    num_obj, r_old_z, n, r_z = zCore.shape
    for idx in range(num_obj):
        for val in range(n):
            for alpha_old_z in range(r_old_z):
                for alpha_z in range(r_z):
                    for alpha_old_x in range(r_old_x):
                        for alpha_x in range(r_x):
                            curr_value = lhs[idx, alpha_old_x, alpha_old_z]
                            curr_value *= xCore[alpha_old_x, val, alpha_x]
                            curr_value *= zCore[idx, alpha_old_z, val, alpha_z]
                            new_lhs[idx, alpha_x, alpha_z] += curr_value


Instruction:
Can you generate the documentation for the following Python 3 function
def _update_rhs(curr_rhs, xCore, zCore, new_rhs):
    # TODO: Use intermediate variable to use 5 nested loops instead of 6.
    r_x, n, r_old_x = xCore.shape
    num_obj, r_z, n, r_old_z = zCore.shape
    for idx in range(num_obj):
        for val in range(n):
            for alpha_old_z in range(r_old_z):
                for alpha_z in range(r_z):
                    for alpha_old_x in range(r_old_x):
                        for alpha_x in range(r_x):
                            curr_value = curr_rhs[idx, alpha_old_z, alpha_old_x]
                            curr_value *= xCore[alpha_x, val, alpha_old_x]
                            curr_value *= zCore[idx, alpha_z, val, alpha_old_z]
                            new_rhs[idx, alpha_z, alpha_x] += curr_value

Response: Update the RHS array of the current RHS array with the new values.


Instruction:
How would you explain what the following Python 3 function does
def project(X, Z, use_jit=False, debug=False):
    zArr = None
    if isinstance(Z, tt.vector):
        zArr = [Z]
    else:
        zArr = Z

    # Get rid of redundant ranks (they cause technical difficulties).
    X = X.round(eps=0)

    numDims, modeSize = X.d, X.n
    coresX = tt.tensor.to_list(X)
    coresZ = [None] * len(zArr)
    for idx in xrange(len(zArr)):
        assert(modeSize == zArr[idx].n).all()
        coresZ[idx] = tt.tensor.to_list(zArr[idx])

    if not use_jit and len(zArr) > 10:
        print('Consider using use_jit=True option to speed up the projection '
              'process.')
    if use_jit:
        for dim in xrange(numDims):
            r1, n, r2 = coresZ[0][dim].shape
            for idx in xrange(len(zArr)):
                if (r1, n, r2) != coresZ[idx][dim].shape:
                    print('Warning: cannot use the jit version when not all '
                          'the ranks in the Z array are equal each other. '
                          'Switching to the non-jit version.')
                    use_jit = False

    if use_jit:
        zCoresDim = [None] * numDims
        for dim in xrange(numDims):
            r1, n, r2 = coresZ[0][dim].shape
            zCoresDim[dim] = np.zeros([len(zArr), r1, n, r2])
            for idx in xrange(len(zArr)):
                if (r1, n, r2) != coresZ[idx][dim].shape:
                    print('Warning: cannot use the jit version when not all '
                          'the ranks in the Z array are equal each other. '
                          'Switching to the non-jit version.')
                    use_jit = False
                zCoresDim[dim][idx, :, :, :] = coresZ[idx][dim]
        # Initialize the cores of the projection_X(sum z[i]).
        coresP = []
        for dim in xrange(numDims):
            r1 = 2 * X.r[dim]
            r2 = 2 * X.r[dim+1]
            if dim == 0:
                r1 = 1
            if dim == numDims - 1:
                r2 = 1
            coresP.append(np.zeros((r1, modeSize[dim], r2)))
        # rhs[dim] is a len(zArr) x zArr[idx] x X.rank_dim.rank_dim ndarray.
        # Right to left orthogonalization of X and preparation of the rhs vectors.
        for dim in xrange(numDims-1, 0, -1):
            # Right to left orthogonalization of the X cores.
            coresX = cores_orthogonalization_step(coresX, dim, left_to_right=False)
            r1, n, r2 = coresX[dim].shape

            # Fill the right orthogonal part of the projection.
            for value in xrange(modeSize[dim]):
                coresP[dim][0:r1, value, 0:r2] = coresX[dim][:, value, :]

        rhs = [None] * (numDims+1)
        for dim in xrange(numDims):
            rhs[dim] = np.zeros([len(zArr), zArr[idx].r[dim], coresX[dim].shape[0]])
        rhs[numDims] = np.ones([len(zArr), 1, 1])

        for dim in xrange(numDims-1, 0, -1):
            _update_rhs(rhs[dim+1], coresX[dim], zCoresDim[dim], rhs[dim])

        if debug:
            assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full()))

        # lsh is a len(zArr) x X.rank_dim x zArr[idx].rank_dim ndarray.
        lhs = np.ones([len(zArr), 1, 1])
        # Left to right sweep.
        for dim in xrange(numDims):
            cc = coresX[dim].copy()
            r1, n, r2 = cc.shape
            if dim < numDims-1:
                # Left to right orthogonalization.
                cc = reshape(cc, (-1, r2))
                cc, rr = np.linalg.qr(cc)
                r2 = cc.shape[1]
                # Warning: since ranks can change here, do not use X.r!
                # Use coresX[dim].shape instead.
                if debug:
                    # Need to do it before the move non orthogonal part rr to
                    # the coresX[dim+1].
                    rightQ = right(tt.tensor.from_list(coresX), dim+1)
                coresX[dim] = reshape(cc, (r1, n, r2)).copy()
                coresX[dim+1] = np.tensordot(rr, coresX[dim+1], 1)

                new_lhs = np.zeros([len(zArr), r2, zArr[idx].r[dim+1]])
                _update_lhs(lhs, coresX[dim], zCoresDim[dim], new_lhs)

                # See the correspondic section in the non-jit version of this
                # code for a less confusing implementation of
                # the transformation below.
                currPCore = np.einsum('ijk,iklm->ijlm', lhs, zCoresDim[dim])
                currPCore = reshape(currPCore, (len(zArr), r1*n, -1))
                currPCore -= np.einsum('ij,kjl->kil', cc, new_lhs)
                currPCore = np.einsum('ijk,ikl', currPCore, rhs[dim+1])
                currPCore = reshape(currPCore, (r1, modeSize[dim], r2))
                if dim == 0:
                    coresP[dim][0:r1, :, 0:r2] += currPCore
                else:
                    coresP[dim][r1:, :, 0:r2] += currPCore
                if debug:
                    explicit_sum = np.zeros((r1, modeSize[dim], r2))
                    for idx in xrange(len(zArr)):
                        leftQm1 = left(tt.tensor.from_list(coresX), dim-1)
                        leftQ = left(tt.tensor.from_list(coresX), dim)

                        first = np.tensordot(leftQm1.T, unfolding(zArr[idx], dim-1), 1)
                        second = reshape(first, (-1, np.prod(modeSize[dim+1:])))
                        if dim < numDims-1:
                            explicit = second.dot(rightQ)
                            orth_cc = reshape(coresX[dim], (-1, coresX[dim].shape[2]))
                            explicit -= orth_cc.dot(leftQ.T.dot(unfolding(zArr[idx], dim)).dot(rightQ))
                        else:
                            explicit = second
                        explicit_sum += reshape(explicit, currPCore.shape)
                    assert(np.allclose(explicit_sum, currPCore))
                lhs = new_lhs

                if dim == 0:
                    coresP[dim][0:r1, :, r2:] = coresX[dim]
                else:
                    coresP[dim][r1:, :, r2:] = coresX[dim]

            if dim == numDims-1:
                coresP[dim][r1:, :, 0:r2] += np.einsum('ijk,iklm->jlm', lhs, zCoresDim[dim])

        if debug:
            assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full()))
        return tt.tensor.from_list(coresP)
    else:
        # Non-jit version of the code.
        # Initialize the cores of the projection_X(sum z[i]).
        coresP = []
        for dim in xrange(numDims):
            r1 = 2 * X.r[dim]
            r2 = 2 * X.r[dim+1]
            if dim == 0:
                r1 = 1
            if dim == numDims - 1:
                r2 = 1
            coresP.append(np.zeros((r1, modeSize[dim], r2)))
        # rhs[idx][dim] is an (Z.rank_dim * X.rank_dim) x 1 vector
        rhs = [[0] * (numDims+1) for _ in xrange(len(zArr))]
        for idx in xrange(len(zArr)):
            rhs[idx][numDims] = np.ones([1, 1])
        # Right to left sweep to orthogonalize the cores and prepare rhs.
        for dim in xrange(numDims-1, 0, -1):
            # Right to left orthogonalization of the X cores.
            coresX = cores_orthogonalization_step(coresX, dim, left_to_right=False)
            r1, n, r2 = coresX[dim].shape

            # Fill the right orthogonal part of the projection.
            coresP[dim][0:r1, :, 0:r2] = coresX[dim]
            # Compute rhs.
            for idx in xrange(len(zArr)):
                coreProd = np.tensordot(coresZ[idx][dim], coresX[dim], axes=(1, 1))
                coreProd = np.transpose(coreProd, (0, 2, 1, 3))
                coreProd = reshape(coreProd, (zArr[idx].r[dim]*r1, zArr[idx].r[dim+1]*r2))
                rhs[idx][dim] = np.dot(coreProd, rhs[idx][dim+1])
        if debug:
            assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full()))

        # lsh[idx] is an X.rank_dim x zArr[idx].rank_dim matrix.
        lhs = [np.ones([1, 1]) for _ in xrange(len(zArr))]
        # Left to right sweep.
        for dim in xrange(numDims - 1):
            if debug:
                rightQ = right(tt.tensor.from_list(coresX), dim+1)
            # Left to right orthogonalization of the X cores.
            coresX = cores_orthogonalization_step(coresX, dim, left_to_right=True)
            r1, n, r2 = coresX[dim].shape
            cc = reshape(coresX[dim], (-1, r2))

            for idx in xrange(len(zArr)):
                currZCore = reshape(coresZ[idx][dim], (zArr[idx].r[dim], -1))
                currPCore = np.dot(lhs[idx], currZCore)

                # TODO: consider using np.einsum.
                coreProd = np.tensordot(coresX[dim], coresZ[idx][dim], axes=(1, 1))
                coreProd = np.transpose(coreProd, (0, 2, 1, 3))
                coreProd = reshape(coreProd, (r1*zArr[idx].r[dim], r2*zArr[idx].r[dim+1]))
                lhs[idx] = reshape(lhs[idx], (1, -1))
                lhs[idx] = np.dot(lhs[idx], coreProd)
                lhs[idx] = reshape(lhs[idx], (r2, zArr[idx].r[dim+1]))

                currPCore = reshape(currPCore, (-1, zArr[idx].r[dim+1]))
                currPCore -= np.dot(cc, lhs[idx])
                rhs[idx][dim+1] = reshape(rhs[idx][dim+1], (zArr[idx].r[dim+1], r2))
                currPCore = np.dot(currPCore, rhs[idx][dim+1])
                currPCore = reshape(currPCore, (r1, modeSize[dim], r2))
                if dim == 0:
                    coresP[dim][0:r1, :, 0:r2] += currPCore
                else:
                    coresP[dim][r1:, :, 0:r2] += currPCore

                if debug:
                    leftQm1 = left(tt.tensor.from_list(coresX), dim-1)
                    leftQ = left(tt.tensor.from_list(coresX), dim)

                    first = np.tensordot(leftQm1.T, unfolding(zArr[idx], dim-1), 1)
                    second = reshape(first, (-1, np.prod(modeSize[dim+1:])))
                    if dim < numDims-1:
                        explicit = second.dot(rightQ)
                        orth_cc = reshape(coresX[dim], (-1, coresX[dim].shape[2]))
                        explicit -= orth_cc.dot(leftQ.T.dot(unfolding(zArr[idx], dim)).dot(rightQ))
                    else:
                        explicit = second
                    explicit = reshape(explicit, currPCore.shape)
                    assert(np.allclose(explicit, currPCore))

            if dim == 0:
                coresP[dim][0:r1, :, r2:] = coresX[dim]
            else:
                coresP[dim][r1:, :, r2:] = coresX[dim]

        for idx in xrange(len(zArr)):
            r1, n, r2 = coresX[numDims-1].shape
            currZCore = reshape(coresZ[idx][numDims-1], (zArr[idx].r[numDims-1], -1))
            currPCore = np.dot(lhs[idx], currZCore)
            currPCore = reshape(currPCore, (r1, n, r2))
            coresP[numDims-1][r1:, :, 0:r2] += currPCore

        if debug:
            assert(np.allclose(X.full(), tt.tensor.from_list(coresX).full()))
        return tt.tensor.from_list(coresP)

Response: Project a tensor X on the tangent space of tensor Z on the tangent space of tensor X.


Instruction:
Make a summary of the following Python 3 code
def projector_splitting_add(Y, delta, debug=False):
    # Get rid of redundant ranks (they cause technical difficulties).
    delta = delta.round(eps=0)
    numDims = delta.d
    assert(numDims == Y.d)
    modeSize = delta.n
    assert(modeSize == Y.n).all()
    coresDelta = tt.tensor.to_list(delta)
    coresY = tt.tensor.to_list(Y)
    # rhs[dim] is an (delta.rank_dim * Y.rank_dim) x 1 vector
    rhs = [None] * (numDims+1)
    rhs[numDims] = np.ones([1, 1])
    # Right to left sweep to orthogonalize the cores and prepare the rhs.
    for dim in xrange(numDims-1, 0, -1):
        # Right to left orthogonalization of the Y cores.
        coresY = cores_orthogonalization_step(coresY, dim, left_to_right=False)
        r1, n, r2 = coresY[dim].shape

        # rhs computation.
        coreProd = np.tensordot(coresDelta[dim], coresY[dim], axes=(1, 1))
        coreProd = np.transpose(coreProd, (0, 2, 1, 3))
        coreProd = reshape(coreProd, (delta.r[dim]*r1, delta.r[dim+1]*r2))
        rhs[dim] = np.dot(coreProd, rhs[dim+1])
    if debug:
        assert(np.allclose(Y.full(), tt.tensor.from_list(coresY).full()))

    # lsh is an Y.rank_dim x delta.rank_dim matrix.
    lhs = np.ones([1, 1])
    # s is an Y.rank_dim x Y.rank_dim matrix.
    s = np.ones([1, 1])
    # Left to right projector splitting sweep.
    for dim in xrange(numDims):
        # Y^+ (formula 4.10)
        cc = coresDelta[dim].copy()
        r1, n, r2 = coresY[dim].shape
        cc = np.tensordot(lhs, cc, 1)
        rhs[dim+1] = reshape(rhs[dim+1], (delta.r[dim+1], r2))
        cc = reshape(cc, (-1, delta.r[dim+1]))
        cc = np.dot(cc, rhs[dim+1])
        if debug:
            first = np.kron(np.eye(modeSize[dim]), left(tt.tensor.from_list(coresY), dim-1).T)
            second = np.dot(first, unfolding(delta, dim))
            explicit = np.dot(second, right(tt.tensor.from_list(coresY), dim+1))
            assert(np.allclose(explicit, cc))
        cc += reshape(np.tensordot(s, coresY[dim], 1), (-1, Y.r[dim+1]))
        if dim < numDims-1:
            cc, rr = np.linalg.qr(cc)
            # TODO: do we need to use r1 = cc.shape[1] here????
        cc = reshape(cc, coresY[dim].shape)
        coresY[dim] = cc.copy()

        if dim < numDims-1:
            coreProd = np.tensordot(coresY[dim], coresDelta[dim], axes=(1, 1))
            coreProd = np.transpose(coreProd, (0, 2, 1, 3))
            coreProd = reshape(coreProd, (r1*delta.r[dim], r2*delta.r[dim+1]))
            lhs = reshape(lhs, (1, -1))
            lhs = np.dot(lhs, coreProd)
            lhs = reshape(lhs, (r2, delta.r[dim+1]))

        if dim < numDims-1:
            # Y^- (formula 4.7)
            s = rr - np.dot(lhs, rhs[dim+1])
            if debug:
                first = left(tt.tensor.from_list(coresY), dim).T
                second = np.dot(first, unfolding(delta, dim))
                explicit = np.dot(second, right(tt.tensor.from_list(coresY), dim+1))
                assert(np.allclose(explicit, np.dot(lhs, rhs[dim+1])))

    return tt.tensor.from_list(coresY)

Response: Compute Y + delta via the projector splitting scheme.


Instruction:
Given the following Python 3 function, write the documentation
def tt_qr(X, left_to_right=True):
    # Get rid of redundant ranks (they cause technical difficulties).
    X = X.round(eps=0)
    numDims = X.d
    coresX = tt.tensor.to_list(X)

    if left_to_right:
        # Left to right orthogonalization of the X cores.
        for dim in xrange(0, numDims-1):
            coresX = cores_orthogonalization_step(
                coresX, dim, left_to_right=left_to_right)
        last_core = coresX[numDims-1]
        r1, n, r2 = last_core.shape
        last_core, rr = np.linalg.qr(reshape(last_core, (-1, r2)))
        coresX[numDims-1] = reshape(last_core, (r1, n, -1))
    else:
        # Right to left orthogonalization of X cores
        for dim in xrange(numDims-1, 0, -1):
            coresX = cores_orthogonalization_step(
                coresX, dim, left_to_right=left_to_right)
        last_core = coresX[0]
        r1, n, r2 = last_core.shape
        last_core, rr = np.linalg.qr(
            np.transpose(reshape(last_core, (r1, -1)))
        )
        coresX[0] = reshape(
            np.transpose(last_core),
            (-1, n, r2))
        rr = np.transpose(rr)

    return tt.tensor.from_list(coresX), rr

Response: This function computes the orthogonalized matrix of a TT tensor X and returns X_orth R - orthogonal tensor and right ( lower triangular matrix of the TT tensor X.


Instruction:
Implement a function in Python 3 to
translate full - format index into tt. vector one s.

Response:
def ind2sub(siz, idx):
    '''
    Translates full-format index into tt.vector one's.
    ----------
    Parameters:
        siz - tt.vector modes
        idx - full-vector index
    Note: not vectorized.
    '''
    n = len(siz)
    subs = _np.empty((n))
    k = _np.cumprod(siz[:-1])
    k = _np.concatenate((_np.ones(1), k))
    for i in xrange(n - 1, -1, -1):
        subs[i] = _np.floor(idx / k[i])
        idx = idx % k[i]
    return subs.astype(_np.int32)


Instruction:
Make a summary of the following Python 3 code
def ksl(A, y0, tau, verb=1, scheme='symm', space=8, rmax=2000, use_normest=1):

    y0 = y0.round(1e-14)    # This will fix ranks
                            # to be no more than maximal reasonable.
                            # Fortran part doesn't  handle excessive ranks
    ry = y0.r.copy()
    if scheme is 'symm':
        tp = 2
    else:
        tp = 1

    usenrm = int(use_normest)

    # Check for dtype
    y = tt.vector()
    if np.iscomplex(A.tt.core).any() or np.iscomplex(y0.core).any():
        dyn_tt.dyn_tt.ztt_ksl(
            y0.d,
            A.n,
            A.m,
            A.tt.r,
            A.tt.core + 0j,
            y0.core + 0j,
            ry,
            tau,
            rmax,
            0,
            10,
            verb,
            tp,
            space,
            usenrm
        )
        y.core = dyn_tt.dyn_tt.zresult_core.copy()
    else:
        A.tt.core = np.real(A.tt.core)
        y0.core = np.real(y0.core)
        dyn_tt.dyn_tt.tt_ksl(
            y0.d,
            A.n,
            A.m,
            A.tt.r,
            A.tt.core,
            y0.core,
            ry,
            tau,
            rmax,
            0,
            10,
            verb,
            tp,
            space,
            usenrm
            )
        y.core = dyn_tt.dyn_tt.dresult_core.copy()
    dyn_tt.dyn_tt.deallocate_result()
    y.d = y0.d
    y.n = A.n.copy()
    y.r = ry
    y.get_ps()
    return y

Response:
This function performs one step of dynamical tensor - train approximation based on projector splitting integrator
       .


Instruction:
How would you explain what the following Python 3 function does
def amen_solve(A, f, x0, eps, kickrank=4, nswp=20, local_prec='n',
               local_iters=2, local_restart=40, trunc_norm=1, max_full_size=50, verb=1):
    """ Approximate linear system solution in the tensor-train (TT) format
        using Alternating minimal energy (AMEN approach)

    :References: Sergey Dolgov, Dmitry. Savostyanov

                 Paper 1: http://arxiv.org/abs/1301.6068
                 Paper 2: http://arxiv.org/abs/1304.1222

    :param A: Matrix in the TT-format
    :type A: matrix
    :param f: Right-hand side in the TT-format
    :type f: tensor
    :param x0: TT-tensor of initial guess.
    :type x0: tensor
    :param eps: Accuracy.
    :type eps: float
    :param kickrank: compression rank of the residual Z, i.e. enrichment size [4]
    :param nswp: maximal number of sweeps [50]
    :param local_prec: local preconditioner: '' (no prec.), 'ljacobi', 'cjacobi', 'rjacobi' ['']
    :param local_iters: dimension of local gmres [40]
    :param local_restart: dimension of local gmres [40]
    :param trunc_norm: truncate in either Frob. ('fro'), or residual norm ('residual') ['residual']
    :param max_full_size: maximal size of the local matrix for the full solver [50]
    :param verb: 0 -- no info output, 1 -- print info output

    :Example:

        >>> import tt
        >>> import tt.amen #Needed, not imported automatically
        >>> a = tt.qlaplace_dd([8, 8, 8]) #3D-Laplacian
        >>> rhs = tt.ones(2, 3 * 8) #Right-hand side of all ones
        >>> x = tt.amen.amen_solve(a, rhs, rhs, 1e-8)
        amen_solve: swp=1, max_dx= 9.766E-01, max_res= 3.269E+00, max_rank=5
        amen_solve: swp=2, max_dx= 4.293E-01, max_res= 8.335E+00, max_rank=9
        amen_solve: swp=3, max_dx= 1.135E-01, max_res= 5.341E+00, max_rank=13
        amen_solve: swp=4, max_dx= 9.032E-03, max_res= 5.908E-01, max_rank=17
        amen_solve: swp=5, max_dx= 9.500E-04, max_res= 7.636E-02, max_rank=21
        amen_solve: swp=6, max_dx= 4.002E-05, max_res= 5.573E-03, max_rank=25
        amen_solve: swp=7, max_dx= 4.949E-06, max_res= 8.418E-04, max_rank=29
        amen_solve: swp=8, max_dx= 9.618E-07, max_res= 2.599E-04, max_rank=33
        amen_solve: swp=9, max_dx= 2.792E-07, max_res= 6.336E-05, max_rank=37
        amen_solve: swp=10, max_dx= 4.730E-08, max_res= 1.663E-05, max_rank=41
        amen_solve: swp=11, max_dx= 1.508E-08, max_res= 5.463E-06, max_rank=45
        amen_solve: swp=12, max_dx= 3.771E-09, max_res= 1.847E-06, max_rank=49
        amen_solve: swp=13, max_dx= 7.797E-10, max_res= 6.203E-07, max_rank=53
        amen_solve: swp=14, max_dx= 1.747E-10, max_res= 2.058E-07, max_rank=57
        amen_solve: swp=15, max_dx= 8.150E-11, max_res= 8.555E-08, max_rank=61
        amen_solve: swp=16, max_dx= 2.399E-11, max_res= 4.215E-08, max_rank=65
        amen_solve: swp=17, max_dx= 7.871E-12, max_res= 1.341E-08, max_rank=69
        amen_solve: swp=18, max_dx= 3.053E-12, max_res= 6.982E-09, max_rank=73
        >>> print (tt.matvec(a, x) - rhs).norm() / rhs.norm()
        5.5152374305127345e-09
    """
    m = A.m.copy()
    rx0 = x0.r.copy()
    psx0 = x0.ps.copy()
    if A.is_complex or f.is_complex:
        amen_f90.amen_f90.ztt_amen_wrapper(f.d, A.n, m,
                                           A.tt.r, A.tt.ps, A.tt.core,
                                           f.r, f.ps, f.core,
                                           rx0, psx0, x0.core,
                                           eps, kickrank, nswp, local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec)
    else:
        if x0.is_complex:
            x0 = x0.real()
            rx0 = x0.r.copy()
            psx0 = x0.ps.copy()
        amen_f90.amen_f90.dtt_amen_wrapper(f.d, A.n, m,
                                           A.tt.r, A.tt.ps, A.tt.core,
                                           f.r, f.ps, f.core,
                                           rx0, psx0, x0.core,
                                           eps, kickrank, nswp, local_iters, local_restart, trunc_norm, max_full_size, verb, local_prec)
    x = tt.tensor()
    x.d = f.d
    x.n = m.copy()
    x.r = rx0
    if A.is_complex or f.is_complex:
        x.core = amen_f90.amen_f90.zcore.copy()
    else:
        x.core = amen_f90.amen_f90.core.copy()
    amen_f90.amen_f90.deallocate_result()
    x.get_ps()
    return tt.vector.from_list(tt.tensor.to_list(x))

Response: Proximate linear system solution in the tensor - train.


Instruction:
Explain what the following Python 3 code does
def T(self):
        mycrs = matrix.to_list(self)
        trans_crs = []
        for cr in mycrs:
            trans_crs.append(_np.transpose(cr, [0, 2, 1, 3]))
        return matrix.from_list(trans_crs)

Response: Transposed TT - matrix


Instruction:
Create a Python 3 function to
return real part of a matrix.

Response:
def real(self):
        """Return real part of a matrix."""
        return matrix(self.tt.real(), n=self.n, m=self.m)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def imag(self):
        return matrix(self.tt.imag(), n=self.n, m=self.m)

Response: Return imaginary part of a matrix.


Instruction:
Can you generate the documentation for the following Python 3 function
def c2r(self):
        return matrix(a=self.tt.__complex_op('M'), n=_np.concatenate(
            (self.n, [2])), m=_np.concatenate((self.m, [2])))

Response: Return real matrix from complex one suitable for solving complex linear system with real solver.


Instruction:
Write a Python 3 function for
computing an approximation to a TT - matrix in with accuracy eps

Response:
def round(self, eps=1e-14, rmax=100000):
        """ Computes an approximation to a
            TT-matrix in with accuracy EPS
        """
        c = matrix()
        c.tt = self.tt.round(eps, rmax)
        c.n = self.n.copy()
        c.m = self.m.copy()
        return c


Instruction:
Write a Python 3 script to
create a copy of the TT - matrix

Response:
def copy(self):
        """ Creates a copy of the TT-matrix """
        c = matrix()
        c.tt = self.tt.copy()
        c.n = self.n.copy()
        c.m = self.m.copy()
        return c


Instruction:
Can you generate a brief explanation for the following Python 3 code
def full(self):
        N = self.n.prod()
        M = self.m.prod()
        a = self.tt.full()
        d = self.tt.d
        sz = _np.vstack((self.n, self.m)).flatten('F')
        a = a.reshape(sz, order='F')
        # Design a permutation
        prm = _np.arange(2 * d)
        prm = prm.reshape((d, 2), order='F')
        prm = prm.transpose()
        prm = prm.flatten('F')
        # Get the inverse permutation
        iprm = [0] * (2 * d)
        for i in xrange(2 * d):
            iprm[prm[i]] = i
        a = a.transpose(iprm).reshape(N, M, order='F')
        a = a.reshape(N, M)
        return a

Response: Transforms a TT - matrix into a full matrix


Instruction:
Implement a Python 3 function for
approximating the matrix-by-vector via the AMEn iteration [y,z]=amen_mv(A, x, tol, varargin) Attempts to approximate the y = A*x with accuracy TOL using the AMEn+ALS iteration. Matrix A has to be given in the TT-format, right-hand side x should be given in the TT-format also. Options are provided in form 'PropertyName1',PropertyValue1,'PropertyName2',PropertyValue2 and so on. The parameters are set to default (in brackets in the following) The list of option names and default values are: o y0 - initial approximation to Ax [rand rank-2] o nswp - maximal number of sweeps [20] o verb - verbosity level, 0-silent, 1-sweep info, 2-block info [1] o kickrank - compression rank of the error, i.e. enrichment size [3] o init_qr - perform QR of the input (save some time in ts, etc) [true] o renorm - Orthog. and truncation methods: direct (svd,qr) or gram (apply svd to the gram matrix, faster for m>>n) [direct] o fkick - Perform solution enrichment during forward sweeps [false] (rather questionable yet; false makes error higher, but "better structured": it does not explode in e.g. subsequent matvecs) o z0 - initial approximation to the error Ax-y [rand rank-kickrank] ******** For description of adaptive ALS please see Sergey V. Dolgov, Dmitry V. Savostyanov, Alternating minimal energy methods for linear systems in higher dimensions. Part I: SPD systems, http://arxiv.org/abs/1301.6068, Part II: Faster algorithm and application to nonsymmetric systems, http://arxiv.org/abs/1304.1222 Use {sergey.v.dolgov, dmitry.savostyanov}@gmail.com for feedback ********

Response:
def amen_mv(A, x, tol, y=None, z=None, nswp=20, kickrank=4,
            kickrank2=0, verb=True, init_qr=True, renorm='direct', fkick=False):
    '''
       Approximate the matrix-by-vector via the AMEn iteration
       [y,z]=amen_mv(A, x, tol, varargin)
       Attempts to approximate the y = A*x
       with accuracy TOL using the AMEn+ALS iteration.
       Matrix A has to be given in the TT-format, right-hand side x should be
       given in the TT-format also.

       Options are provided in form
       'PropertyName1',PropertyValue1,'PropertyName2',PropertyValue2 and so
       on. The parameters are set to default (in brackets in the following)
       The list of option names and default values are:
           o y0 - initial approximation to Ax [rand rank-2]
           o nswp - maximal number of sweeps [20]
           o verb - verbosity level, 0-silent, 1-sweep info, 2-block info [1]
           o kickrank - compression rank of the error,
             i.e. enrichment size [3]
           o init_qr - perform QR of the input (save some time in ts, etc) [true]
           o renorm - Orthog. and truncation methods: direct (svd,qr) or gram
             (apply svd to the gram matrix, faster for m>>n) [direct]
           o fkick - Perform solution enrichment during forward sweeps [false]
             (rather questionable yet; false makes error higher, but "better
             structured": it does not explode in e.g. subsequent matvecs)
           o z0 - initial approximation to the error Ax-y [rand rank-kickrank]

    ********
       For description of adaptive ALS please see
       Sergey V. Dolgov, Dmitry V. Savostyanov,
       Alternating minimal energy methods for linear systems in higher dimensions.
       Part I: SPD systems, http://arxiv.org/abs/1301.6068,
       Part II: Faster algorithm and application to nonsymmetric systems, http://arxiv.org/abs/1304.1222

       Use {sergey.v.dolgov, dmitry.savostyanov}@gmail.com for feedback
    ********
    '''

    if renorm is 'gram':
        print("Not implemented yet. Renorm is switched to 'direct'")
        renorm = 'direct'

    if isinstance(x, _tt.vector):
        d = x.d
        m = x.n
        rx = x.r
        x = _tt.vector.to_list(x)
        vectype = 1  # tt_tensor
    elif isinstance(x, list):
        d = len(x)
        m = _np.zeros(d)
        rx = _np.ones(d + 1, dtype=_np.int32)
        for i in xrange(d):
            [_, m[i], rx[i + 1]] = x[i].shape
        vectype = 0  # cell
    else:
        raise Exception('x: use tt.tensor or list of cores as numpy.arrays')

    if isinstance(A, _tt.matrix):
        n = A.n
        ra = A.tt.r
        A = _tt.matrix.to_list(A)
        # prepare A for fast ALS-mv
        for i in xrange(d):
            A[i] = _reshape(A[i], (ra[i] * n[i], m[i] * ra[i + 1]))
        atype = 1  # tt_matrix
    # Alternative: A is a cell of cell: sparse canonical format
    elif isinstance(A, list):
        n = _np.zeros(d)
        for i in xrange(d):
            n[i] = A[i][0].shape[0]
        ra = len(A[0])
        atype = 0  # cell
    else:
        raise Exception('A: use tt.matrix or list of cores as numpy.arrays')

    if y is None:
        y = _tt.rand(n, d, 2)
        y = _tt.vector.to_list(y)
    else:
        if isinstance(y, _tt.vector):
            y = _tt.vector.to_list(y)

    ry = _np.ones(d + 1, dtype=_np.int32)
    for i in range(d):
        ry[i + 1] = y[i].shape[2]

    if (kickrank + kickrank2 > 0):
        if z is None:
            z = _tt.rand(n, d, kickrank + kickrank2)
            rz = z.r
            z = _tt.vector.to_list(z)
        else:
            if isinstance(z, _tt.vector):
                z = _tt.vector.to_list(z)
            rz = _np.ones(d + 1, dtype=_np.int32)
            for i in range(d):
                rz[i + 1] = z[i].shape[2]

        phizax = [None] * (d + 1)  # cell(d+1,1);
        if (atype == 1):
            phizax[0] = _np.ones((1, 1, 1))  # 1
            phizax[d] = _np.ones((1, 1, 1))  # 1
        else:
            phizax[0] = _np.ones((1, ra))  # 33
            phizax[d] = _np.ones((1, ra))
        phizy = [None] * (d + 1)
        phizy[0] = _np.ones((1))  # , 1))
        phizy[d] = _np.ones((1))  # , 1))

    phiyax = [None] * (d + 1)
    if (atype == 1):
        phiyax[0] = _np.ones((1, 1, 1))  # 1
        phiyax[d] = _np.ones((1, 1, 1))  # 1
    else:
        phiyax[0] = _np.ones((1, ra))  # 3
        phiyax[d] = _np.ones((1, ra))

    nrms = _np.ones(d)

    # Initial ort
    for i in range(d - 1):
        if init_qr:
            cr = _reshape(y[i], (ry[i] * n[i], ry[i + 1]))
            if (renorm is 'gram') and (ry[i] * n[i] > 5 * ry[i + 1]):
                [cr, s, R] = _svdgram(cr)
            else:
                [cr, R] = _np.linalg.qr(cr)
            nrmr = _np.linalg.norm(R)  # , 'fro')
            if (nrmr > 0):
                R = R / nrmr
            cr2 = _reshape(y[i + 1], (ry[i + 1], n[i + 1] * ry[i + 2]))
            cr2 = _np.dot(R, cr2)
            ry[i + 1] = cr.shape[1]
            y[i] = _reshape(cr, (ry[i], n[i], ry[i + 1]))
            y[i + 1] = _reshape(cr2, (ry[i + 1], n[i + 1], ry[i + 2]))

        [phiyax[i + 1], nrms[i]
         ] = _compute_next_Phi(phiyax[i], y[i], x[i], 'lr', A[i])

        if (kickrank + kickrank2 > 0):
            cr = _reshape(z[i], (rz[i] * n[i], rz[i + 1]))
            if (renorm == 'gram') and (rz[i] * n[i] > 5 * rz[i + 1]):
                [cr, s, R] = _svdgram(cr)
            else:
                [cr, R] = _np.linalg.qr(cr)
            nrmr = _np.linalg.norm(R)  # , 'fro')
            if (nrmr > 0):
                R = R / nrmr
            cr2 = _reshape(z[i + 1], (rz[i + 1], n[i + 1] * rz[i + 2]))
            cr2 = _np.dot(R, cr2)
            rz[i + 1] = cr.shape[1]
            z[i] = _reshape(cr, (rz[i], n[i], rz[i + 1]))
            z[i + 1] = _reshape(cr2, (rz[i + 1], n[i + 1], rz[i + 2]))
            phizax[
                i +
                1] = _compute_next_Phi(
                phizax[i],
                z[i],
                x[i],
                'lr',
                A[i],
                nrms[i],
                return_norm=False)
            phizy[
                i +
                1] = _compute_next_Phi(
                phizy[i],
                z[i],
                y[i],
                'lr',
                return_norm=False)

    i = d - 1
    direct = -1
    swp = 1
    max_dx = 0

    while swp <= nswp:
        # Project the MatVec generating vector
        crx = _reshape(x[i], (rx[i] * m[i] * rx[i + 1], 1))
        cry = _bfun3(phiyax[i], A[i], phiyax[i + 1], crx)
        nrms[i] = _np.linalg.norm(cry)  # , 'fro')
        # The main goal is to keep y[i] of norm 1
        if (nrms[i] > 0):
            cry = cry / nrms[i]
        else:
            nrms[i] = 1
        y[i] = _reshape(y[i], (ry[i] * n[i] * ry[i + 1], 1))
        dx = _np.linalg.norm(cry - y[i])
        max_dx = max(max_dx, dx)

        # Truncation and enrichment
        if ((direct > 0) and (i < d - 1)):  # ?? i<d
            cry = _reshape(cry, (ry[i] * n[i], ry[i + 1]))
            if (renorm == 'gram'):
                [u, s, v] = _svdgram(cry, tol / d**0.5)
                v = v.T
                r = u.shape[1]
            else:
                [u, s, vt] = _np.linalg.svd(cry, full_matrices=False)
                #s = diag(s)
                r = _my_chop2(s, tol * _np.linalg.norm(s) / d**0.5)
                u = u[:, :r]
                # ????? s - matrix or vector
                v = _np.dot(_tconj(vt[:r, :]), _np.diag(s[:r]))

            # Prepare enrichment, if needed
            if (kickrank + kickrank2 > 0):
                cry = _np.dot(u, v.T)
                cry = _reshape(cry, (ry[i] * n[i], ry[i + 1]))
                # For updating z
                crz = _bfun3(phizax[i], A[i], phizax[i + 1], crx)
                crz = _reshape(crz, (rz[i] * n[i], rz[i + 1]))
                ys = _np.dot(cry, phizy[i + 1])
                yz = _reshape(ys, (ry[i], n[i] * rz[i + 1]))
                yz = _np.dot(phizy[i], yz)
                yz = _reshape(yz, (rz[i] * n[i], rz[i + 1]))
                crz = crz / nrms[i] - yz
                nrmz = _np.linalg.norm(crz)  # , 'fro')
                if (kickrank2 > 0):
                    [crz, _, _] = _np.linalg.svd(crz, full_matrices=False)
                    crz = crz[:, : min(crz.shape[1], kickrank)]
                    crz = _np.hstack(
                        (crz, _np.random.randn(
                            rz[i] * n[i], kickrank2)))
                # For adding into solution
                if fkick:
                    crs = _bfun3(phiyax[i], A[i], phizax[i + 1], crx)
                    crs = _reshape(crs, (ry[i] * n[i], rz[i + 1]))
                    crs = crs / nrms[i] - ys
                    u = _np.hstack((u, crs))
                    if (renorm == 'gram') and (
                            ry[i] * n[i] > 5 * (ry[i + 1] + rz[i + 1])):
                        [u, s, R] = _svdgram(u)
                    else:
                        [u, R] = _np.linalg.qr(u)
                    v = _np.hstack((v, _np.zeros((ry[i + 1], rz[i + 1]))))
                    v = _np.dot(v, R.T)
                    r = u.shape[1]
            y[i] = _reshape(u, (ry[i], n[i], r))

            cr2 = _reshape(y[i + 1], (ry[i + 1], n[i + 1] * ry[i + 2]))
            v = _reshape(v, (ry[i + 1], r))
            cr2 = _np.dot(v.T, cr2)
            y[i + 1] = _reshape(cr2, (r, n[i + 1], ry[i + 2]))

            ry[i + 1] = r

            [phiyax[i + 1], nrms[i]
             ] = _compute_next_Phi(phiyax[i], y[i], x[i], 'lr', A[i])

            if (kickrank + kickrank2 > 0):
                if (renorm == 'gram') and (rz[i] * n[i] > 5 * rz[i + 1]):
                    [crz, s, R] = _svdgram(crz)
                else:
                    [crz, R] = _np.linalg.qr(crz)
                rz[i + 1] = crz.shape[1]
                z[i] = _reshape(crz, (rz[i], n[i], rz[i + 1]))
                # z[i+1] will be recomputed from scratch in the next step

                phizax[
                    i +
                    1] = _compute_next_Phi(
                    phizax[i],
                    z[i],
                    x[i],
                    'lr',
                    A[i],
                    nrms[i],
                    return_norm=False)
                phizy[
                    i +
                    1] = _compute_next_Phi(
                    phizy[i],
                    z[i],
                    y[i],
                    'lr',
                    return_norm=False)

        elif ((direct < 0) and (i > 0)):
            cry = _reshape(cry, (ry[i], n[i] * ry[i + 1]))
            if (renorm == 'gram'):
                [v, s, u] = _svdgram(cry.T, tol / d**0.5)
                u = u.T
                r = v.shape[1]
            else:
                #[v, s, u] = _np.linalg.svd(cry.T, full_matrices=False)
                [u, s, vt] = _np.linalg.svd(cry, full_matrices=False)
                #s = diag(s);
                r = _my_chop2(s, tol * _np.linalg.norm(s) / d**0.5)
                v = _tconj(vt[:r, :])

                #v = vt[:r, :]
                #v = _np.dot(v[:, :r], _np.diag(s[:r]))
                u = _np.dot(u[:, :r], _np.diag(s[:r]))  # ??????????????????

            # Prepare enrichment, if needed
            if (kickrank + kickrank2 > 0):
                cry = _np.dot(u, v.T)  # .T)
                cry = _reshape(cry, (ry[i], n[i] * ry[i + 1]))
                # For updating z
                crz = _bfun3(phizax[i], A[i], phizax[i + 1], crx)
                crz = _reshape(crz, (rz[i], n[i] * rz[i + 1]))
                ys = _np.dot(phizy[i], cry)
                yz = _reshape(ys, (rz[i] * n[i], ry[i + 1]))
                yz = _np.dot(yz, phizy[i + 1])
                yz = _reshape(yz, (rz[i], n[i] * rz[i + 1]))
                crz = crz / nrms[i] - yz
                nrmz = _np.linalg.norm(crz)  # , 'fro')
                if (kickrank2 > 0):
                    [_, _, crz] = _np.linalg.svd(crz, full_matrices=False)
                    crz = crz[:, : min(crz.shape[1], kickrank)]
                    crz = _tconj(crz)
                    crz = _np.vstack(
                        (crz, _np.random.randn(kickrank2, n[i] * rz[i + 1])))
                # For adding into solution
                crs = _bfun3(phizax[i], A[i], phiyax[i + 1], crx)
                crs = _reshape(crs, (rz[i], n[i] * ry[i + 1]))
                crs = crs / nrms[i] - ys
                v = _np.hstack((v, crs.T))  # .T
                #v = v.T
                if (renorm == 'gram') and (
                        n[i] * ry[i + 1] > 5 * (ry[i] + rz[i])):
                    [v, s, R] = _svdgram(v)
                else:
                    [v, R] = _np.linalg.qr(v)
                u = _np.hstack((u, _np.zeros((ry[i], rz[i]))))
                u = _np.dot(u, R.T)
                r = v.shape[1]

            cr2 = _reshape(y[i - 1], (ry[i - 1] * n[i - 1], ry[i]))
            cr2 = _np.dot(cr2, u)
            y[i - 1] = _reshape(cr2, (ry[i - 1], n[i - 1], r))
            y[i] = _reshape(v.T, (r, n[i], ry[i + 1]))

            ry[i] = r

            [phiyax[i], nrms[i]] = _compute_next_Phi(
                phiyax[i + 1], y[i], x[i], 'rl', A[i])

            if (kickrank + kickrank2 > 0):
                if (renorm == 'gram') and (n[i] * rz[i + 1] > 5 * rz[i]):
                    [crz, s, R] = _svdgram(crz.T)
                else:
                    [crz, R] = _np.linalg.qr(crz.T)
                rz[i] = crz.shape[1]
                z[i] = _reshape(crz.T, (rz[i], n[i], rz[i + 1]))
                # don't update z[i-1], it will be recomputed from scratch

                phizax[i] = _compute_next_Phi(
                    phizax[
                        i + 1],
                    z[i],
                    x[i],
                    'rl',
                    A[i],
                    nrms[i],
                    return_norm=False)
                phizy[i] = _compute_next_Phi(
                    phizy[i + 1], z[i], y[i], 'rl', return_norm=False)

        if (verb > 1):
            print('amen-mv: swp=[%d,%d], dx=%.3e, r=%d, |y|=%.3e, |z|=%.3e' % (swp, i, dx, r, _np.linalg.norm(cry), nrmz))

        # Stopping or reversing
        if ((direct > 0) and (i == d - 1)) or ((direct < 0) and (i == 0)):
            if (verb > 0):
                print('amen-mv: swp=%d{%d}, max_dx=%.3e, max_r=%d' % (swp, (1 - direct) // 2, max_dx, max(ry)))
            if ((max_dx < tol) or (swp == nswp)) and (direct > 0):
                break
            else:
                # We are at the terminal block
                y[i] = _reshape(cry, (ry[i], n[i], ry[i + 1]))
                if (direct > 0):
                    swp = swp + 1
            max_dx = 0
            direct = -direct
        else:
            i = i + direct
    # if (direct>0)
    y[d - 1] = _reshape(cry, (ry[d - 1], n[d - 1], ry[d]))
    # else
    #     y{1} = reshape(cry, ry(1), n(1), ry(2));
    # end;

    # Distribute norms equally...
    nrms = _np.exp(sum(_np.log(nrms)) / d)
    # ... and plug them into y
    for i in xrange(d):
        y[i] = _np.dot(y[i], nrms)

    if (vectype == 1):
        y = _tt.vector.from_list(y)
        if kickrank == 0:
            z = None
        else:
            z = _tt.vector.from_list(z)

    return y, z


Instruction:
Create a Python 3 function to
compute the next recurrent Phi.

Response:
def _compute_next_Phi(Phi_prev, x, y, direction, A=None,
                      extnrm=None, return_norm=True):
    '''
    Performs the recurrent Phi (or Psi) matrix computation
    Phi = Phi_prev * (x'Ay).
    If direction is 'lr', computes Psi
    if direction is 'rl', computes Phi
    A can be empty, then only x'y is computed.

        Phi1: rx1, ry1, ra1, or {rx1, ry1}_ra, or rx1, ry1
        Phi2: ry2, ra2, rx2, or {ry2, rx2}_ra, or ry2, rx2
    '''

    [rx1, n, rx2] = x.shape
    [ry1, m, ry2] = y.shape

    if A is not None:
        if isinstance(A, list):  # ?????????????????????????????????
            # A is a canonical block
            ra = len(A)
        else:
            # Just full format
            [ra1, ra2] = A.shape
            ra1 = ra1 // n
            ra2 = ra2 // m
    # ?????????????????????????????????????
    else:
        [ra1, ra2] = [1, 1]

    if isinstance(Phi_prev, list):
        Phi = [None] * ra
        if return_norm:
            nrm = 0
        if (direction == 'lr'):
            # lr: Phi1
            x = _reshape(x, (rx1, n * rx2))
            y = _reshape(y, (ry1 * m, ry2))
            for i in xrange(ra):
                Phi[i] = _np.dot(_tconj(x), Phi_prev[i])
                Phi[i] = _reshape(Phi[i], (n, rx2 * ry1))
                Phi[i] = Phi[i].T
                Phi[i] = _np.dot(Phi[i], A[i])
                Phi[i] = _reshape(Phi[i], (rx2, ry1 * m))
                Phi[i] = _np.dot(Phi[i], y)
                if return_norm:
                    nrm = max(nrm, _np.linalg.norm(Phi[i]))  # , 'fro'))
        else:
            # rl: Phi2
            y = _reshape(y, (ry1, m * ry2))
            x = _reshape(x, (rx1 * n, rx2))
            for i in xrange(ra):
                Phi[i] = _np.dot(Phi_prev[i], x.T)
                Phi[i] = _reshape(Phi[i], (ry2 * rx1, n))
                Phi[i] = _np.dot(Phi[i], A[i])
                Phi[i] = Phi[i].T
                Phi[i] = _reshape(Phi[i], (m * ry2, rx1))
                Phi[i] = _np.dot(y, Phi[i])
                if return_norm:
                    nrm = max(nrm, _np.linalg.norm(Phi[i]))  # , 'fro'))
        if return_norm:
            # Extract the scale to prevent overload
            if (nrm > 0):
                for i in xrange(ra):
                    Phi[i] = Phi[i] / nrm
            else:
                nrm = 1
        elif extnrm is not None:
            # Override the normalization
            for i in xrange(ra):
                Phi[i] = Phi[i] / extnrm
    else:
        if (direction == 'lr'):
            # lr: Phi1
            x = _reshape(x, (rx1, n * rx2))
            Phi = _reshape(Phi_prev, (rx1, ry1 * ra1))
            Phi = _np.dot(_tconj(x), Phi)
            if A is not None:
                Phi = _reshape(Phi, (n * rx2 * ry1, ra1))
                Phi = Phi.T
                Phi = _reshape(Phi, (ra1 * n, rx2 * ry1))
                Phi = _np.dot(A.T, Phi)
                Phi = _reshape(Phi, (m, ra2 * rx2 * ry1))
            else:
                Phi = _reshape(Phi, (n, rx2 * ry1))
            Phi = Phi.T
            Phi = _reshape(Phi, (ra2 * rx2, ry1 * m))
            y = _reshape(y, (ry1 * m, ry2))
            Phi = _np.dot(Phi, y)
            if A is not None:
                Phi = _reshape(Phi, (ra2, rx2 * ry2))
                Phi = Phi.T
                Phi = _reshape(Phi, (rx2, ry2, ra2))
            else:
                Phi = _reshape(Phi, (rx2, ry2))
        else:
            # rl: Phi2
            y = _reshape(y, (ry1 * m, ry2))
            Phi = _reshape(Phi_prev, (ry2, ra2 * rx2))
            Phi = _np.dot(y, Phi)
            if A is not None:
                Phi = _reshape(Phi, (ry1, m * ra2 * rx2))
                Phi = Phi.T
                Phi = _reshape(Phi, (m * ra2, rx2 * ry1))
                Phi = _np.dot(A, Phi)
                Phi = _reshape(Phi, (ra1 * n * rx2, ry1))
                Phi = Phi.T
            Phi = _reshape(Phi, (ry1 * ra1, n * rx2))
            x = _reshape(x, (rx1, n * rx2))
            Phi = _np.dot(Phi, _tconj(x))
            if A is not None:
                Phi = _reshape(Phi, (ry1, ra1, rx1))
            else:
                Phi = _reshape(Phi, (ry1, rx1))

        if return_norm:
            # Extract the scale to prevent overload
            nrm = _np.linalg.norm(Phi)  # , 'fro')
            if (nrm > 0):
                Phi = Phi / nrm
            else:
                nrm = 1
        elif extnrm is not None:
            # Override the normalization by the external one
            Phi = Phi / extnrm

    if return_norm:
        return Phi, nrm
    else:
        return Phi