From 6cc4a3bddfbe5bef5ff78c3465a452a1859a15e9 Mon Sep 17 00:00:00 2001 From: Tigran Saluev Date: Fri, 18 Oct 2013 12:58:55 +0400 Subject: [PATCH 1/2] Initial refactoring --- tt/core/matrix.py | 323 +++++++++++++++++++ tt/core/tensor.py | 471 ++++++++++++++++++++++++++++ tt/core/tt.py | 778 +--------------------------------------------- 3 files changed, 796 insertions(+), 776 deletions(-) create mode 100644 tt/core/matrix.py create mode 100644 tt/core/tensor.py diff --git a/tt/core/matrix.py b/tt/core/matrix.py new file mode 100644 index 0000000..09b2e68 --- /dev/null +++ b/tt/core/matrix.py @@ -0,0 +1,323 @@ +import numpy as np +from numpy import prod, reshape, nonzero, size, sqrt +import math +from math import sqrt +from numbers import Number +import tt_f90 +import core_f90 + +from tensor import tensor + +#################################################################################################### +############################################# ############################################# +############################################ matrix ############################################ +############################################# ############################################# +#################################################################################################### + +class matrix: + def __init__(self,a=None,eps=1e-14, n=None, m=None): + if a is None: + self.n = 0 #Only two additional fields + self.m = 0 + self.tt = tensor() + return + if isinstance(a,tensor): #Convert from a tt-tensor + if ( n is None or m is None): + n1 = np.sqrt(a.n).astype(np.int32) + m1 = np.sqrt(a.n).astype(np.int32) + else: + n1 = np.array(n,dtype=np.int32) + m1 = np.array(m,dtype=np.int32) + self.n = n1 + self.m = m1 + self.tt = tensor() + self.tt.core = a.core.copy() + self.tt.ps = a.ps.copy() + self.tt.r = a.r.copy() + self.tt.n = a.n.copy() + self.tt.d = self.tt.n.size + return + try: + c = np.asarray(a,dtype=np.float64) + d = c.ndim/2 + p = c.shape + self.n = np.array(p[0:d],dtype=np.int32) + self.m = np.array(p[d:2*d],dtype=np.int32) + prm = np.arange(2*d) + prm = prm.reshape((d,2),order='F') + prm = prm.transpose() + prm = prm.flatten('F') + sz = self.n * self.m + b = c.transpose(prm).reshape(sz,order='F') + self.tt=tensor(b,eps) + return + except ValueError: + pass + + @staticmethod + def from_list(a): + d = len(a) #Number of cores + res = matrix() + n = np.zeros(d,dtype=np.int32) + r = np.zeros(d+1,dtype=np.int32) + m = np.zeros(d,dtype=np.int32) + cr = np.array([]) + for i in xrange(d): + cr = np.concatenate((cr,a[i].flatten('F'))) + r[i] = a[i].shape[0] + r[i+1] = a[i].shape[3] + n[i] = a[i].shape[1] + m[i] = a[i].shape[2] + res.n = n + res.m = m + tt = tensor() + tt.n = n * m + tt.core = cr + tt.r = r + tt.d = d + tt.get_ps() + res.tt = tt + return res + + @staticmethod + def to_list(ttmat): + tt = ttmat.tt + d = tt.d + r = tt.r + n = ttmat.n + m = ttmat.m + ps = tt.ps + core = tt.core + res = [] + for i in xrange(d): + cur_core = core[ps[i]-1:ps[i+1]-1] + cur_core = cur_core.reshape((r[i],n[i],m[i],r[i+1]),order='F') + res.append(cur_core) + return res + + def write(self, fname): + self.tt.write(fname) + + def __repr__(self): + res = "This is a %d-dimensional matrix \n" % self.tt.d + r = self.tt.r + d = self.tt.d + n = self.n + m = self.m + for i in range(d): + res = res + ("r(%d)=%d, n(%d)=%d, m(%d)=%d \n" % (i, r[i],i,n[i],i,m[i])) + res = res + ("r(%d)=%d \n" % (d,r[d])) + return res + + @property + def is_complex(self): + return self.tt.is_complex + + def real(self): + """Return real part of a matrix.""" + return matrix(self.tt.real(), n=self.n, m=self.m) + + def imag(self): + """Return imaginary part of a matrix.""" + return matrix(self.tt.imag(), n=self.n, m=self.m) + + def c2r(self): + """Get real matrix from complex one suitable for solving complex linear system with real solver. + + For matrix :math:`M(i_1,j_1,\\ldots,i_d,j_d) = \\Re M + i\\Im M` returns (d+1)-dimensional matrix + :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` of form + :math:`\\begin{bmatrix}\\Re M & -\\Im M \\\\ \\Im M & \\Re M \\end{bmatrix}`. This function + is useful for solving complex linear system :math:`\\mathcal{A}X = B` with real solver by + transforming it into + + .. math:: + \\begin{bmatrix}\\Re\\mathcal{A} & -\\Im\\mathcal{A} \\\\ + \\Im\\mathcal{A} & \\Re\\mathcal{A} \\end{bmatrix} + \\begin{bmatrix}\\Re X \\\\ \\Im X\\end{bmatrix} = + \\begin{bmatrix}\\Re B \\\\ \\Im B\\end{bmatrix}. + + """ + return matrix(a=self.tt.__complex_op('M'), n=np.concatenate((self.n, [2])), m=np.concatenate((self.m, [2]))) + + def r2c(self): + """Get complex matrix from real one made by ``matrix.c2r()``. + + For matrix :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` returns complex matrix + + .. math:: + M(i_1,j_1,\\ldots,i_d,j_d) = \\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,0,0) + i\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,1,0). + + """ + tmp = self.tt.copy() + newcore = np.array(tmp.core, dtype=np.complex) + cr = newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] + cr = cr.reshape((tmp.r[-2], tmp.n[-1], tmp.r[-1]), order='F') + cr[:, 1, :] *= 1j + cr[:, 2:, :] = 0.0 + newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] = cr.flatten('F') + tmp.core = newcore + return matrix(sum(tmp, axis=tmp.d-1), n=self.n, m=self.m) + + def __getitem__(self, index): + if len(index) == 2: + if isinstance(index[0], int) and index[1] == slice(None): + # row requested + row = index[0] + mycrs = matrix.to_list(self) + crs = [] + for i in xrange(self.tt.d): + crs.append(mycrs[i][:, row % self.n[i], :, :].copy()) + row /= self.n[i] + return tensor.from_list(crs) + elif isinstance(index[1], int) and index[0] == slice(None): + # col requested + col = index[1] + mycrs = matrix.to_list(self) + crs = [] + for i in xrange(self.tt.d): + crs.append(mycrs[i][:, :, col % self.m[i], :].copy()) + col /= self.m[i] + return tensor.from_list(crs) + elif isinstance(index[0], int) and isinstance(index[1], int): + # element requested + pass + else: + # complicated submatrix requested + pass + + def __add__(self,other): + if other is None: + return self + c = matrix() + c.tt = self.tt + other.tt + c.n = np.asanyarray(self.n,dtype=np.int32).copy() + c.m = np.asanyarray(self.m,dtype=np.int32).copy() + return c + + def __radd__(self, other): + if other is None: + return self + return other + self + + def __sub__(self,other): + c = matrix() + c.tt = self.tt-other.tt + c.n = np.asanyarray(self.n,dtype=np.int32).copy() + c.m = np.asanyarray(self.m,dtype=np.int32).copy() + return c + + def __neg__(self): + return (-1)*self + + def __matmul__(self,other): + c = matrix() + c.n = self.n.copy() + c.m = other.m.copy() + tt = tensor() + tt.d = self.tt.d + tt.n = c.n * c.m + if self.is_complex or other.is_complex: + tt.r = core_f90.core.zmat_mat(self.n,self.m,other.m,np.array(self.tt.core, dtype=np.complex),np.array(other.tt.core, dtype=np.complex),self.tt.r,other.tt.r) + tt.core = core_f90.core.zresult_core.copy() + else: + tt.r = core_f90.core.dmat_mat(self.n,self.m,other.m,np.real(self.tt.core),np.real(other.tt.core),self.tt.r,other.tt.r) + tt.core = core_f90.core.result_core.copy() + core_f90.core.dealloc() + tt.get_ps() + c.tt = tt + return c + + def __rmul__(self,other): + if hasattr(other,'__matmul__'): + return other.__matmul__(self) + else: + c = matrix() + c.tt = other * self.tt + c.n = self.n + c.m = self.m + return c + + def __mul__(self,other): + if hasattr(other,'__matmul__'): + return self.__matmul__(other) + else: + c = matrix() + c.tt = self.tt * other + c.n = self.n + c.m = self.m + return c + + def __kron__(self,other): + """ Kronecker product of two TT-matrices """ + if other is None: + return self + a = self + b = other + c = matrix() + c.n = np.concatenate((a.n,b.n)) + c.m = np.concatenate((a.m,b.m)) + c.tt = kron(a.tt,b.tt) + return c + + def norm(self): + return self.tt.norm() + + def round(self,eps): + """ Computes an approximation to a + TT-matrix in with accuracy EPS + """ + c = matrix() + c.tt = self.tt.round(eps) + c.n = self.n.copy() + c.m = self.m.copy() + return c + + 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 + + def __diag__(self): + """ Computes the diagonal of the TT-matrix""" + c = tensor() + c.n = self.n.copy() + c.r = self.tt.r.copy() + c.d = self.tt.d #Number are NOT referenced + c.get_ps() + c.alloc_core() + #Actually copy the data + for i in xrange(c.d): + cur_core1 = np.zeros((c.r[i],c.n[i],c.r[i+1])) + cur_core = self.tt.core[self.tt.ps[i]-1:self.tt.ps[i+1]-1] + cur_core = cur_core.reshape(c.r[i],self.n[i],self.m[i],c.r[i+1],order='F') + for j in xrange(c.n[i]): + cur_core1[:,j,:] = cur_core[:,j,j,:] + c.core[c.ps[i]-1:c.ps[i+1]-1] = cur_core1.flatten('F') + return c + + def full(self): + """ Transforms a TT-matrix into a full matrix""" + 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 + + def rmean(self): + return self.tt.rmean() diff --git a/tt/core/tensor.py b/tt/core/tensor.py new file mode 100644 index 0000000..75aae9f --- /dev/null +++ b/tt/core/tensor.py @@ -0,0 +1,471 @@ +import numpy as np +from numpy import prod, reshape, nonzero, size, sqrt +import math +from math import sqrt +from numbers import Number +import tt_f90 +import core_f90 + + + +#################################################################################################### +############################################# ############################################# +############################################ tensor ############################################ +############################################# ############################################# +#################################################################################################### + +#The main class for working with TT-tensors +class tensor: + """Construct new TT-tensor. + + When called with no arguments, creates dummy object which can be filled from outside. + + When ``a`` is specified, computes approximate decomposition of array ``a`` with accuracy ``eps``: + + :param a: A tensor to approximate. + :type a: ndarray + + >>> a = numpy.sin(numpy.arange(2 ** 10)).reshape([2] * 10, order='F') + >>> a = tt.tensor(a) + >>> a.r + array([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], dtype=int32) + >>> # now let's try different accuracy + >>> b = numpy.random.rand(2, 2, 2, 2, 2, 2, 2, 2, 2, 2) + >>> btt = tt.tensor(b, 1E-14) + >>> btt.r + array([ 1, 2, 4, 8, 16, 32, 16, 8, 4, 2, 1], dtype=int32) + >>> btt = tt.tensor(b, 1E-1) + >>> btt.r + array([ 1, 2, 4, 8, 14, 20, 14, 8, 4, 2, 1], dtype=int32) + + Attributes: + + d : int + Dimensionality of the tensor. + n : ndarray of shape (d,) + Mode sizes of the tensor: if :math:`n_i=\\texttt{n[i-1]}`, then the tensor has shape :math:`n_1\\times\ldots\\times n_d`. + r : ndarray of shape (d+1,) + TT-ranks of current TT decomposition of the tensor. + core : ndarray + Flatten (Fortran-ordered) TT cores stored sequentially in a one-dimensional array. + To get a list of three-dimensional cores, use ``tt.tensor.to_list(my_tensor)``. + """ + def __init__(self,a=None,eps=1e-14): + + if a is None: + self.core = 0 + self.d = 0 + self.n = 0 + self.r = 0 + self.ps = 0 + return + self.d = a.ndim + self.n = np.array(a.shape,dtype=np.int32) + r = np.zeros((self.d+1,),dtype=np.int32) + ps = np.zeros((self.d+1,),dtype=np.int32) + + if ( np.iscomplex(a).any() ): + self.r, self.ps = tt_f90.tt_f90.zfull_to_tt(a.flatten('F'),self.n,self.d,eps) + self.core = tt_f90.tt_f90.zcore.copy() + else: + self.r,self.ps = tt_f90.tt_f90.dfull_to_tt(np.real(a).flatten('F'),self.n,self.d,eps) + self.core = tt_f90.tt_f90.core.copy() + + tt_f90.tt_f90.tt_dealloc() + + @staticmethod + def from_list(a,order='F'): + """Generate TT-tensor object from given TT cores. + + :param a: List of TT cores. + :type a: list + :returns: tensor -- TT-tensor constructed from the given cores. + + """ + d = len(a) #Number of cores + res = tensor() + n = np.zeros(d,dtype=np.int32) + r = np.zeros(d+1,dtype=np.int32) + cr = np.array([]) + for i in xrange(d): + cr = np.concatenate((cr,a[i].flatten(order))) + r[i] = a[i].shape[0] + r[i+1] = a[i].shape[2] + n[i] = a[i].shape[1] + res.d = d + res.n = n + res.r = r + res.core = cr + res.get_ps() + return res + + @staticmethod + def to_list(tt): + """Return list of TT cores a TT decomposition consists of. + + :param tt: TT-tensor. + :type a: tensor + :returns: list -- list of ``tt.d`` three-dimensional cores, ``i``-th core is an ndarray of shape ``(tt.r[i], tt.n[i], tt.r[i+1])``. + """ + d = tt.d + r = tt.r + n = tt.n + ps = tt.ps + core = tt.core + res = [] + for i in xrange(d): + cur_core = core[ps[i]-1:ps[i+1]-1] + cur_core = cur_core.reshape((r[i],n[i],r[i+1]),order='F') + res.append(cur_core) + return res + + @property + def is_complex(self): + return np.iscomplexobj(self.core) + + def _matrix__complex_op(self, op): + return self.__complex_op(op) + + def __complex_op(self, op): + crs = tensor.to_list(self) + newcrs = [] + cr = crs[0] + rl, n, rr = cr.shape + newcr = np.zeros((rl, n, rr * 2), dtype=np.float) + newcr[:, :, :rr] = np.real(cr) + newcr[:, :, rr:] = np.imag(cr) + newcrs.append(newcr) + for i in xrange(1, self.d - 1): + cr = crs[i] + rl, n, rr = cr.shape + newcr = np.zeros((rl * 2, n, rr * 2), dtype=np.float) + newcr[:rl, :, :rr] = newcr[rl:, :, rr:] = np.real(cr) + newcr[:rl, :, rr:] = np.imag(cr) + newcr[rl:, :, :rr] = -np.imag(cr) + newcrs.append(newcr) + cr = crs[-1] + rl, n, rr = cr.shape + if op in ['R', 'r', 'Re']: + # get real part + newcr = np.zeros((rl * 2, n, rr), dtype=np.float) + newcr[:rl, :, :] = np.real(cr) + newcr[rl:, :, :] = -np.imag(cr) + elif op in ['I', 'i', 'Im']: + # get imaginary part + newcr = np.zeros((rl * 2, n, rr), dtype=np.float) + newcr[:rl, :, :] = np.imag(cr) + newcr[rl:, :, :] = np.real(cr) + elif op in ['A', 'B', 'all', 'both']: + # get both parts (increase dimensionality) + newcr = np.zeros((rl * 2, n, 2 * rr), dtype=np.float) + newcr[:rl, :, :rr] = np.real(cr) + newcr[rl:, :, :rr] = -np.imag(cr) + newcr[:rl, :, rr:] = np.imag(cr) + newcr[rl:, :, rr:] = np.real(cr) + newcrs.append(newcr) + newcr = np.zeros((rr * 2, 2, 1), dtype=np.float) + newcr[:rr, 0, :] = newcr[rr:, 1, :] = 1.0 + elif op in ['M']: + # get matrix modificated for real-arithm. solver + newcr = np.zeros((rl * 2, n, 2 * rr), dtype=np.float) + newcr[:rl, :, :rr] = np.real(cr) + newcr[rl:, :, :rr] = -np.imag(cr) + newcr[:rl, :, rr:] = np.imag(cr) + newcr[rl:, :, rr:] = np.real(cr) + newcrs.append(newcr) + newcr = np.zeros((rr * 2, 4, 1), dtype=np.float) + newcr[:rr, [0, 3], :] = 1.0 + newcr[rr:, 1, :] = 1.0 + newcr[rr:, 2, :] = -1.0 + else: + raise ValueError("Unexpected parameter " + op + " at tt.tensor.__complex_op") + newcrs.append(newcr) + return tensor.from_list(newcrs) + + def real(self): + """Get real part of a TT-tensor.""" + return self.__complex_op('Re') + + def imag(self): + """Get imaginary part of a TT-tensor.""" + return self.__complex_op('Im') + + def c2r(self): + """Get real tensor from complex one suitable for solving complex linear system with real solver. + + For tensor :math:`X(i_1,\\ldots,i_d) = \\Re X + i\\Im X` returns (d+1)-dimensional tensor + of form :math:`[\\Re X\\ \\Im X]`. This function is useful for solving complex linear system + :math:`\\mathcal{A}X = B` with real solver by transforming it into + + .. math:: + \\begin{bmatrix}\\Re\\mathcal{A} & -\\Im\\mathcal{A} \\\\ + \\Im\\mathcal{A} & \\Re\\mathcal{A} \\end{bmatrix} + \\begin{bmatrix}\\Re X \\\\ \\Im X\\end{bmatrix} = + \\begin{bmatrix}\\Re B \\\\ \\Im B\\end{bmatrix}. + + """ + return self.__complex_op('both') + + def r2c(self): + """Get complex tensor from real one made by ``tensor.c2r()``. + + For tensor :math:`\\tilde{X}(i_1,\\ldots,i_d,i_{d+1})` returns complex tensor + + .. math:: + X(i_1,\\ldots,i_d) = \\tilde{X}(i_1,\\ldots,i_d,0) + i\\tilde{X}(i_1,\\ldots,i_d,1). + + >>> a = tt.rand(2,10,5) + 1j * tt.rand(2,10,5) + >>> (a.c2r().r2c() - a).norm() / a.norm() + 7.310562016615692e-16 + + """ + tmp = self.copy() + newcore = np.array(tmp.core, dtype=np.complex) + cr = newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] + cr = cr.reshape((tmp.r[-2], tmp.n[-1], tmp.r[-1]), order='F') + cr[:, 1, :] *= 1j + newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] = cr.flatten('F') + tmp.core = newcore + return sum(tmp, axis=tmp.d-1) + + #Print statement + def __repr__(self): + res = "This is a %d-dimensional tensor \n" % self.d + r = self.r + d = self.d + n = self.n + for i in range(0,d): + res = res + ("r(%d)=%d, n(%d)=%d \n" % (i, r[i],i,n[i])) + res = res + ("r(%d)=%d \n" % (d,r[d])) + return res + + def write(self,fname): + if np.iscomplexobj(self.core): + tt_f90.tt_f90.ztt_write_wrapper(self.n,self.r,self.ps,self.core,fname) + else: + tt_f90.tt_f90.dtt_write_wrapper(self.n,self.r,self.ps,np.real(self.core),fname) + + def full(self): + """Returns full array (uncompressed). + + .. warning:: + TT compression allows to keep in memory tensors much larger than ones PC can handle in + raw format. Therefore this function is quite unsafe; use it at your own risk. + + :returns: numpy.ndarray -- full tensor. + + """ + #Generate correct size vector + sz = self.n.copy() + if self.r[0] > 1: + sz = np.concatenate(([self.r[0]],sz)) + if self.r[self.d] > 1: + sz = np.concatenate(([self.r[self.d]],sz)) + #a = np.zeros(sz,order='F') + if ( np.iscomplex(self.core).any() ): + a = tt_f90.tt_f90.ztt_to_full(self.n,self.r,self.ps,self.core,np.prod(sz)) + else: + a = tt_f90.tt_f90.dtt_to_full(self.n,self.r,self.ps,np.real(self.core),np.prod(sz)) + a = a.reshape(sz,order='F') + #import ipdb; ipdb.set_trace() + return a + + def __add__(self,other): + if other is None: + return self + c = tensor() + c.r = np.zeros((self.d+1,),dtype=np.int32) + c.ps = np.zeros((self.d+1,),dtype=np.int32) + c.n = self.n + c.d = self.d + if ( np.iscomplex(self.core).any() or np.iscomplex(other.core).any()): + c.r,c.ps = tt_f90.tt_f90.ztt_add(self.n,self.r,other.r,self.ps,other.ps,self.core+0j,other.core+0j) + c.core = tt_f90.tt_f90.zcore.copy() + else: + #This could be a real fix in the case we fell to the real world + c.r,c.ps = tt_f90.tt_f90.dtt_add(self.n,self.r,other.r,self.ps,other.ps,np.real(self.core),np.real(other.core)) + c.core = tt_f90.tt_f90.core.copy() + tt_f90.tt_f90.tt_dealloc() + return c + + def __radd__(self,other): + if other is None: + return self + return other + self + + + #@profile + def round(self,eps): + """Applies TT rounding procedure to the TT-tensor and **returns rounded tensor**. + + :param eps: Rounding accuracy. + :type eps: float + :returns: tensor -- rounded TT-tensor. + + Usage example: + + >>> a = tt.ones(2, 10) + >>> b = a + a + >>> print b.r + array([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], dtype=int32) + >>> b = b.round(1E-14) + >>> print b.r + array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32) + + """ + c=tensor() + c.n=self.n + c.d=self.d + c.r=self.r.copy() + c.ps=self.ps.copy() + if ( np.iscomplex(self.core).any() ): + tt_f90.tt_f90.ztt_compr2(c.n,c.r,c.ps,self.core,eps) + c.core = tt_f90.tt_f90.zcore.copy() + else: + tt_f90.tt_f90.dtt_compr2(c.n,c.r,c.ps,self.core,eps) + c.core=tt_f90.tt_f90.core.copy() + tt_f90.tt_f90.tt_dealloc() + return c + + #@profile + def norm(self): + if ( np.iscomplex(self.core).any() ): + nrm = tt_f90.tt_f90.ztt_nrm(self.n,self.r,self.ps,self.core) + else: + nrm=tt_f90.tt_f90.dtt_nrm(self.n,self.r,self.ps,np.real(self.core)) + return nrm + + def __rmul__(self,other): + c = tensor() + c.d = self.d + c.n = self.n + if isinstance(other,Number): + c.r = self.r.copy() + c.ps = self.ps.copy() + c.core = self.core.copy() + new_core = c.core[c.ps[0]-1:c.ps[1]-1] + new_core = new_core * other + c.core = np.array(c.core,dtype=new_core.dtype) + c.core[c.ps[0]-1:c.ps[1]-1] = new_core + else: + c =_hdm(self,other) + return c + + def __mul__(self,other): + c = tensor() + c.d = self.d + c.n = self.n + if isinstance(other,Number): + c.r = self.r.copy() + c.ps = self.ps.copy() + c.core = self.core.copy() + new_core = c.core[c.ps[0]-1:c.ps[1]-1] + new_core = new_core * other + c.core = np.array(c.core,dtype=new_core.dtype) + c.core[c.ps[0]-1:c.ps[1]-1] = new_core + else: + c = _hdm(other,self) + return c + + def __sub__(self,other): + c = self + (-1) * other + return c + + def __kron__(self,other): + if other is None: + return self + a = self + b = other + c = tensor() + c.d = a.d + b.d + c.n = np.concatenate((a.n,b.n)) + c.r = np.concatenate((a.r[0:a.d],b.r[0:b.d+1])) + c.get_ps() + c.core = np.concatenate((a.core,b.core)) + return c + + def __dot__(self,other): + r1 = self.r + r2 = other.r + d = self.d + if ( np.iscomplex(self.core).any() or np.iscomplex(other.core).any()): + dt = np.zeros(r1[0]*r2[0]*r1[d]*r2[d],dtype=np.complex) + dt = tt_f90.tt_f90.ztt_dotprod(self.n,r1,r2,self.ps,other.ps,self.core+0j,other.core+0j,dt.size) + else: + dt = np.zeros(r1[0]*r2[0]*r1[d]*r2[d]) + dt = tt_f90.tt_f90.dtt_dotprod(self.n,r1,r2,self.ps,other.ps,np.real(self.core),np.real(other.core),dt.size) + if dt.size is 1: + dt = dt[0] + return dt + + def __col__(self,k): + c = tensor() + d = self.d + r = self.r.copy() + n = self.n.copy() + ps = self.ps.copy() + core = self.core.copy() + last_core = self.core[ps[d-1]-1:ps[d]-1] + last_core = last_core.reshape((r[d-1]*n[d-1],r[d]),order='F') + last_core = last_core[:,k] + try: + r[d] = len(k) + except: + r[d] = 1 + ps[d] = ps[d-1] + r[d-1]*n[d-1]*r[d] + core[ps[d-1]-1:ps[d]-1] = last_core.flatten('F') + c.d = d + c.n = n + c.r = r + c.ps = ps + c.core = core + from tt import matrix + return matrix.from_list(res) + + def __diag__(self): + cl = tensor.to_list(self) + d = self.d + r = self.r + n = self.n + res = [] + dtype = self.core.dtype + for i in xrange(d): + cur_core = cl[i] + res_core = np.zeros((r[i], n[i], n[i], r[i+1]), dtype = dtype) + for s1 in xrange(r[i]): + for s2 in xrange(r[i+1]): + res_core[s1, :, :, s2] = np.diag(cur_core[s1, :, s2].reshape(n[i], order='F')) + res.append(res_core) + from tt import matrix + return matrix.from_list(res) + + def __neg__(self): + return self*(-1) + + def get_ps(self): + self.ps = np.cumsum(np.concatenate(([1],self.n*self.r[0:self.d]*self.r[1:self.d+1]))).astype(np.int32) + + def alloc_core(self): + self.core = np.zeros((self.ps[self.d]-1,),dtype=np.float) + + def copy(self): + c = tensor() + c.core = self.core.copy() + c.d = self.d + c.n = self.n.copy() + c.r = self.r.copy() + c.ps = self.ps.copy() + return c + + def rmean(self): + """ Calculates the mean rank of a TT-tensor.""" + if not np.all(self.n): + return 0 + # Solving quadratic equation ar^2 + br + c = 0; + a = np.sum(self.n[1:-1]) + b = self.n[0] + self.n[-1] + c = - np.sum(self.n * self.r[1:] * self.r[:-1]) + D = b ** 2 - 4 * a * c + r = 0.5 * (-b + sqrt(D)) / a + return r + diff --git a/tt/core/tt.py b/tt/core/tt.py index ec200ce..11a7b5f 100644 --- a/tt/core/tt.py +++ b/tt/core/tt.py @@ -9,782 +9,8 @@ import tt_f90 import core_f90 - - -#################################################################################################### -############################################# ############################################# -############################################ tensor ############################################ -############################################# ############################################# -#################################################################################################### - -#The main class for working with TT-tensors -class tensor: - """Construct new TT-tensor. - - When called with no arguments, creates dummy object which can be filled from outside. - - When ``a`` is specified, computes approximate decomposition of array ``a`` with accuracy ``eps``: - - :param a: A tensor to approximate. - :type a: ndarray - - >>> a = numpy.sin(numpy.arange(2 ** 10)).reshape([2] * 10, order='F') - >>> a = tt.tensor(a) - >>> a.r - array([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], dtype=int32) - >>> # now let's try different accuracy - >>> b = numpy.random.rand(2, 2, 2, 2, 2, 2, 2, 2, 2, 2) - >>> btt = tt.tensor(b, 1E-14) - >>> btt.r - array([ 1, 2, 4, 8, 16, 32, 16, 8, 4, 2, 1], dtype=int32) - >>> btt = tt.tensor(b, 1E-1) - >>> btt.r - array([ 1, 2, 4, 8, 14, 20, 14, 8, 4, 2, 1], dtype=int32) - - Attributes: - - d : int - Dimensionality of the tensor. - n : ndarray of shape (d,) - Mode sizes of the tensor: if :math:`n_i=\\texttt{n[i-1]}`, then the tensor has shape :math:`n_1\\times\ldots\\times n_d`. - r : ndarray of shape (d+1,) - TT-ranks of current TT decomposition of the tensor. - core : ndarray - Flatten (Fortran-ordered) TT cores stored sequentially in a one-dimensional array. - To get a list of three-dimensional cores, use ``tt.tensor.to_list(my_tensor)``. - """ - def __init__(self,a=None,eps=1e-14): - - if a is None: - self.core = 0 - self.d = 0 - self.n = 0 - self.r = 0 - self.ps = 0 - return - self.d = a.ndim - self.n = np.array(a.shape,dtype=np.int32) - r = np.zeros((self.d+1,),dtype=np.int32) - ps = np.zeros((self.d+1,),dtype=np.int32) - - if ( np.iscomplex(a).any() ): - self.r, self.ps = tt_f90.tt_f90.zfull_to_tt(a.flatten('F'),self.n,self.d,eps) - self.core = tt_f90.tt_f90.zcore.copy() - else: - self.r,self.ps = tt_f90.tt_f90.dfull_to_tt(np.real(a).flatten('F'),self.n,self.d,eps) - self.core = tt_f90.tt_f90.core.copy() - - tt_f90.tt_f90.tt_dealloc() - - @staticmethod - def from_list(a,order='F'): - """Generate TT-tensor object from given TT cores. - - :param a: List of TT cores. - :type a: list - :returns: tensor -- TT-tensor constructed from the given cores. - - """ - d = len(a) #Number of cores - res = tensor() - n = np.zeros(d,dtype=np.int32) - r = np.zeros(d+1,dtype=np.int32) - cr = np.array([]) - for i in xrange(d): - cr = np.concatenate((cr,a[i].flatten(order))) - r[i] = a[i].shape[0] - r[i+1] = a[i].shape[2] - n[i] = a[i].shape[1] - res.d = d - res.n = n - res.r = r - res.core = cr - res.get_ps() - return res - - @staticmethod - def to_list(tt): - """Return list of TT cores a TT decomposition consists of. - - :param tt: TT-tensor. - :type a: tensor - :returns: list -- list of ``tt.d`` three-dimensional cores, ``i``-th core is an ndarray of shape ``(tt.r[i], tt.n[i], tt.r[i+1])``. - """ - d = tt.d - r = tt.r - n = tt.n - ps = tt.ps - core = tt.core - res = [] - for i in xrange(d): - cur_core = core[ps[i]-1:ps[i+1]-1] - cur_core = cur_core.reshape((r[i],n[i],r[i+1]),order='F') - res.append(cur_core) - return res - - @property - def is_complex(self): - return np.iscomplexobj(self.core) - - def _matrix__complex_op(self, op): - return self.__complex_op(op) - - def __complex_op(self, op): - crs = tensor.to_list(self) - newcrs = [] - cr = crs[0] - rl, n, rr = cr.shape - newcr = np.zeros((rl, n, rr * 2), dtype=np.float) - newcr[:, :, :rr] = np.real(cr) - newcr[:, :, rr:] = np.imag(cr) - newcrs.append(newcr) - for i in xrange(1, self.d - 1): - cr = crs[i] - rl, n, rr = cr.shape - newcr = np.zeros((rl * 2, n, rr * 2), dtype=np.float) - newcr[:rl, :, :rr] = newcr[rl:, :, rr:] = np.real(cr) - newcr[:rl, :, rr:] = np.imag(cr) - newcr[rl:, :, :rr] = -np.imag(cr) - newcrs.append(newcr) - cr = crs[-1] - rl, n, rr = cr.shape - if op in ['R', 'r', 'Re']: - # get real part - newcr = np.zeros((rl * 2, n, rr), dtype=np.float) - newcr[:rl, :, :] = np.real(cr) - newcr[rl:, :, :] = -np.imag(cr) - elif op in ['I', 'i', 'Im']: - # get imaginary part - newcr = np.zeros((rl * 2, n, rr), dtype=np.float) - newcr[:rl, :, :] = np.imag(cr) - newcr[rl:, :, :] = np.real(cr) - elif op in ['A', 'B', 'all', 'both']: - # get both parts (increase dimensionality) - newcr = np.zeros((rl * 2, n, 2 * rr), dtype=np.float) - newcr[:rl, :, :rr] = np.real(cr) - newcr[rl:, :, :rr] = -np.imag(cr) - newcr[:rl, :, rr:] = np.imag(cr) - newcr[rl:, :, rr:] = np.real(cr) - newcrs.append(newcr) - newcr = np.zeros((rr * 2, 2, 1), dtype=np.float) - newcr[:rr, 0, :] = newcr[rr:, 1, :] = 1.0 - elif op in ['M']: - # get matrix modificated for real-arithm. solver - newcr = np.zeros((rl * 2, n, 2 * rr), dtype=np.float) - newcr[:rl, :, :rr] = np.real(cr) - newcr[rl:, :, :rr] = -np.imag(cr) - newcr[:rl, :, rr:] = np.imag(cr) - newcr[rl:, :, rr:] = np.real(cr) - newcrs.append(newcr) - newcr = np.zeros((rr * 2, 4, 1), dtype=np.float) - newcr[:rr, [0, 3], :] = 1.0 - newcr[rr:, 1, :] = 1.0 - newcr[rr:, 2, :] = -1.0 - else: - raise ValueError("Unexpected parameter " + op + " at tt.tensor.__complex_op") - newcrs.append(newcr) - return tensor.from_list(newcrs) - - def real(self): - """Get real part of a TT-tensor.""" - return self.__complex_op('Re') - - def imag(self): - """Get imaginary part of a TT-tensor.""" - return self.__complex_op('Im') - - def c2r(self): - """Get real tensor from complex one suitable for solving complex linear system with real solver. - - For tensor :math:`X(i_1,\\ldots,i_d) = \\Re X + i\\Im X` returns (d+1)-dimensional tensor - of form :math:`[\\Re X\\ \\Im X]`. This function is useful for solving complex linear system - :math:`\\mathcal{A}X = B` with real solver by transforming it into - - .. math:: - \\begin{bmatrix}\\Re\\mathcal{A} & -\\Im\\mathcal{A} \\\\ - \\Im\\mathcal{A} & \\Re\\mathcal{A} \\end{bmatrix} - \\begin{bmatrix}\\Re X \\\\ \\Im X\\end{bmatrix} = - \\begin{bmatrix}\\Re B \\\\ \\Im B\\end{bmatrix}. - - """ - return self.__complex_op('both') - - def r2c(self): - """Get complex tensor from real one made by ``tensor.c2r()``. - - For tensor :math:`\\tilde{X}(i_1,\\ldots,i_d,i_{d+1})` returns complex tensor - - .. math:: - X(i_1,\\ldots,i_d) = \\tilde{X}(i_1,\\ldots,i_d,0) + i\\tilde{X}(i_1,\\ldots,i_d,1). - - >>> a = tt.rand(2,10,5) + 1j * tt.rand(2,10,5) - >>> (a.c2r().r2c() - a).norm() / a.norm() - 7.310562016615692e-16 - - """ - tmp = self.copy() - newcore = np.array(tmp.core, dtype=np.complex) - cr = newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] - cr = cr.reshape((tmp.r[-2], tmp.n[-1], tmp.r[-1]), order='F') - cr[:, 1, :] *= 1j - newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] = cr.flatten('F') - tmp.core = newcore - return sum(tmp, axis=tmp.d-1) - - #Print statement - def __repr__(self): - res = "This is a %d-dimensional tensor \n" % self.d - r = self.r - d = self.d - n = self.n - for i in range(0,d): - res = res + ("r(%d)=%d, n(%d)=%d \n" % (i, r[i],i,n[i])) - res = res + ("r(%d)=%d \n" % (d,r[d])) - return res - - def write(self,fname): - if np.iscomplexobj(self.core): - tt_f90.tt_f90.ztt_write_wrapper(self.n,self.r,self.ps,self.core,fname) - else: - tt_f90.tt_f90.dtt_write_wrapper(self.n,self.r,self.ps,np.real(self.core),fname) - - def full(self): - """Returns full array (uncompressed). - - .. warning:: - TT compression allows to keep in memory tensors much larger than ones PC can handle in - raw format. Therefore this function is quite unsafe; use it at your own risk. - - :returns: numpy.ndarray -- full tensor. - - """ - #Generate correct size vector - sz = self.n.copy() - if self.r[0] > 1: - sz = np.concatenate(([self.r[0]],sz)) - if self.r[self.d] > 1: - sz = np.concatenate(([self.r[self.d]],sz)) - #a = np.zeros(sz,order='F') - if ( np.iscomplex(self.core).any() ): - a = tt_f90.tt_f90.ztt_to_full(self.n,self.r,self.ps,self.core,np.prod(sz)) - else: - a = tt_f90.tt_f90.dtt_to_full(self.n,self.r,self.ps,np.real(self.core),np.prod(sz)) - a = a.reshape(sz,order='F') - #import ipdb; ipdb.set_trace() - return a - - def __add__(self,other): - if other is None: - return self - c = tensor() - c.r = np.zeros((self.d+1,),dtype=np.int32) - c.ps = np.zeros((self.d+1,),dtype=np.int32) - c.n = self.n - c.d = self.d - if ( np.iscomplex(self.core).any() or np.iscomplex(other.core).any()): - c.r,c.ps = tt_f90.tt_f90.ztt_add(self.n,self.r,other.r,self.ps,other.ps,self.core+0j,other.core+0j) - c.core = tt_f90.tt_f90.zcore.copy() - else: - #This could be a real fix in the case we fell to the real world - c.r,c.ps = tt_f90.tt_f90.dtt_add(self.n,self.r,other.r,self.ps,other.ps,np.real(self.core),np.real(other.core)) - c.core = tt_f90.tt_f90.core.copy() - tt_f90.tt_f90.tt_dealloc() - return c - - def __radd__(self,other): - if other is None: - return self - return other + self - - - #@profile - def round(self,eps): - """Applies TT rounding procedure to the TT-tensor and **returns rounded tensor**. - - :param eps: Rounding accuracy. - :type eps: float - :returns: tensor -- rounded TT-tensor. - - Usage example: - - >>> a = tt.ones(2, 10) - >>> b = a + a - >>> print b.r - array([1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], dtype=int32) - >>> b = b.round(1E-14) - >>> print b.r - array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32) - - """ - c=tensor() - c.n=self.n - c.d=self.d - c.r=self.r.copy() - c.ps=self.ps.copy() - if ( np.iscomplex(self.core).any() ): - tt_f90.tt_f90.ztt_compr2(c.n,c.r,c.ps,self.core,eps) - c.core = tt_f90.tt_f90.zcore.copy() - else: - tt_f90.tt_f90.dtt_compr2(c.n,c.r,c.ps,self.core,eps) - c.core=tt_f90.tt_f90.core.copy() - tt_f90.tt_f90.tt_dealloc() - return c - - #@profile - def norm(self): - if ( np.iscomplex(self.core).any() ): - nrm = tt_f90.tt_f90.ztt_nrm(self.n,self.r,self.ps,self.core) - else: - nrm=tt_f90.tt_f90.dtt_nrm(self.n,self.r,self.ps,np.real(self.core)) - return nrm - - def __rmul__(self,other): - c = tensor() - c.d = self.d - c.n = self.n - if isinstance(other,Number): - c.r = self.r.copy() - c.ps = self.ps.copy() - c.core = self.core.copy() - new_core = c.core[c.ps[0]-1:c.ps[1]-1] - new_core = new_core * other - c.core = np.array(c.core,dtype=new_core.dtype) - c.core[c.ps[0]-1:c.ps[1]-1] = new_core - else: - c =_hdm(self,other) - return c - - def __mul__(self,other): - c = tensor() - c.d = self.d - c.n = self.n - if isinstance(other,Number): - c.r = self.r.copy() - c.ps = self.ps.copy() - c.core = self.core.copy() - new_core = c.core[c.ps[0]-1:c.ps[1]-1] - new_core = new_core * other - c.core = np.array(c.core,dtype=new_core.dtype) - c.core[c.ps[0]-1:c.ps[1]-1] = new_core - else: - c = _hdm(other,self) - return c - - def __sub__(self,other): - c = self + (-1) * other - return c - - def __kron__(self,other): - if other is None: - return self - a = self - b = other - c = tensor() - c.d = a.d + b.d - c.n = np.concatenate((a.n,b.n)) - c.r = np.concatenate((a.r[0:a.d],b.r[0:b.d+1])) - c.get_ps() - c.core = np.concatenate((a.core,b.core)) - return c - - def __dot__(self,other): - r1 = self.r - r2 = other.r - d = self.d - if ( np.iscomplex(self.core).any() or np.iscomplex(other.core).any()): - dt = np.zeros(r1[0]*r2[0]*r1[d]*r2[d],dtype=np.complex) - dt = tt_f90.tt_f90.ztt_dotprod(self.n,r1,r2,self.ps,other.ps,self.core+0j,other.core+0j,dt.size) - else: - dt = np.zeros(r1[0]*r2[0]*r1[d]*r2[d]) - dt = tt_f90.tt_f90.dtt_dotprod(self.n,r1,r2,self.ps,other.ps,np.real(self.core),np.real(other.core),dt.size) - if dt.size is 1: - dt = dt[0] - return dt - - def __col__(self,k): - c = tensor() - d = self.d - r = self.r.copy() - n = self.n.copy() - ps = self.ps.copy() - core = self.core.copy() - last_core = self.core[ps[d-1]-1:ps[d]-1] - last_core = last_core.reshape((r[d-1]*n[d-1],r[d]),order='F') - last_core = last_core[:,k] - try: - r[d] = len(k) - except: - r[d] = 1 - ps[d] = ps[d-1] + r[d-1]*n[d-1]*r[d] - core[ps[d-1]-1:ps[d]-1] = last_core.flatten('F') - c.d = d - c.n = n - c.r = r - c.ps = ps - c.core = core - return c - - def __diag__(self): - cl = tensor.to_list(self) - d = self.d - r = self.r - n = self.n - res = [] - dtype = self.core.dtype - for i in xrange(d): - cur_core = cl[i] - res_core = np.zeros((r[i], n[i], n[i], r[i+1]), dtype = dtype) - for s1 in xrange(r[i]): - for s2 in xrange(r[i+1]): - res_core[s1, :, :, s2] = np.diag(cur_core[s1, :, s2].reshape(n[i], order='F')) - res.append(res_core) - return matrix.from_list(res) - - def __neg__(self): - return self*(-1) - - def get_ps(self): - self.ps = np.cumsum(np.concatenate(([1],self.n*self.r[0:self.d]*self.r[1:self.d+1]))).astype(np.int32) - - def alloc_core(self): - self.core = np.zeros((self.ps[self.d]-1,),dtype=np.float) - - def copy(self): - c = tensor() - c.core = self.core.copy() - c.d = self.d - c.n = self.n.copy() - c.r = self.r.copy() - c.ps = self.ps.copy() - return c - - def rmean(self): - """ Calculates the mean rank of a TT-tensor.""" - if not np.all(self.n): - return 0 - # Solving quadratic equation ar^2 + br + c = 0; - a = np.sum(self.n[1:-1]) - b = self.n[0] + self.n[-1] - c = - np.sum(self.n * self.r[1:] * self.r[:-1]) - D = b ** 2 - 4 * a * c - r = 0.5 * (-b + sqrt(D)) / a - return r - - -#################################################################################################### -############################################# ############################################# -############################################ matrix ############################################ -############################################# ############################################# -#################################################################################################### - -class matrix: - def __init__(self,a=None,eps=1e-14, n=None, m=None): - if a is None: - self.n = 0 #Only two additional fields - self.m = 0 - self.tt = tensor() - return - if isinstance(a,tensor): #Convert from a tt-tensor - if ( n is None or m is None): - n1 = np.sqrt(a.n).astype(np.int32) - m1 = np.sqrt(a.n).astype(np.int32) - else: - n1 = np.array(n,dtype=np.int32) - m1 = np.array(m,dtype=np.int32) - self.n = n1 - self.m = m1 - self.tt = tensor() - self.tt.core = a.core.copy() - self.tt.ps = a.ps.copy() - self.tt.r = a.r.copy() - self.tt.n = a.n.copy() - self.tt.d = self.tt.n.size - return - try: - c = np.asarray(a,dtype=np.float64) - d = c.ndim/2 - p = c.shape - self.n = np.array(p[0:d],dtype=np.int32) - self.m = np.array(p[d:2*d],dtype=np.int32) - prm = np.arange(2*d) - prm = prm.reshape((d,2),order='F') - prm = prm.transpose() - prm = prm.flatten('F') - sz = self.n * self.m - b = c.transpose(prm).reshape(sz,order='F') - self.tt=tensor(b,eps) - return - except ValueError: - pass - - @staticmethod - def from_list(a): - d = len(a) #Number of cores - res = matrix() - n = np.zeros(d,dtype=np.int32) - r = np.zeros(d+1,dtype=np.int32) - m = np.zeros(d,dtype=np.int32) - cr = np.array([]) - for i in xrange(d): - cr = np.concatenate((cr,a[i].flatten('F'))) - r[i] = a[i].shape[0] - r[i+1] = a[i].shape[3] - n[i] = a[i].shape[1] - m[i] = a[i].shape[2] - res.n = n - res.m = m - tt = tensor() - tt.n = n * m - tt.core = cr - tt.r = r - tt.d = d - tt.get_ps() - res.tt = tt - return res - - @staticmethod - def to_list(ttmat): - tt = ttmat.tt - d = tt.d - r = tt.r - n = ttmat.n - m = ttmat.m - ps = tt.ps - core = tt.core - res = [] - for i in xrange(d): - cur_core = core[ps[i]-1:ps[i+1]-1] - cur_core = cur_core.reshape((r[i],n[i],m[i],r[i+1]),order='F') - res.append(cur_core) - return res - - def write(self, fname): - self.tt.write(fname) - - def __repr__(self): - res = "This is a %d-dimensional matrix \n" % self.tt.d - r = self.tt.r - d = self.tt.d - n = self.n - m = self.m - for i in range(d): - res = res + ("r(%d)=%d, n(%d)=%d, m(%d)=%d \n" % (i, r[i],i,n[i],i,m[i])) - res = res + ("r(%d)=%d \n" % (d,r[d])) - return res - - @property - def is_complex(self): - return self.tt.is_complex - - def real(self): - """Return real part of a matrix.""" - return matrix(self.tt.real(), n=self.n, m=self.m) - - def imag(self): - """Return imaginary part of a matrix.""" - return matrix(self.tt.imag(), n=self.n, m=self.m) - - def c2r(self): - """Get real matrix from complex one suitable for solving complex linear system with real solver. - - For matrix :math:`M(i_1,j_1,\\ldots,i_d,j_d) = \\Re M + i\\Im M` returns (d+1)-dimensional matrix - :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` of form - :math:`\\begin{bmatrix}\\Re M & -\\Im M \\\\ \\Im M & \\Re M \\end{bmatrix}`. This function - is useful for solving complex linear system :math:`\\mathcal{A}X = B` with real solver by - transforming it into - - .. math:: - \\begin{bmatrix}\\Re\\mathcal{A} & -\\Im\\mathcal{A} \\\\ - \\Im\\mathcal{A} & \\Re\\mathcal{A} \\end{bmatrix} - \\begin{bmatrix}\\Re X \\\\ \\Im X\\end{bmatrix} = - \\begin{bmatrix}\\Re B \\\\ \\Im B\\end{bmatrix}. - - """ - return matrix(a=self.tt.__complex_op('M'), n=np.concatenate((self.n, [2])), m=np.concatenate((self.m, [2]))) - - def r2c(self): - """Get complex matrix from real one made by ``matrix.c2r()``. - - For matrix :math:`\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,i_{d+1},j_{d+1})` returns complex matrix - - .. math:: - M(i_1,j_1,\\ldots,i_d,j_d) = \\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,0,0) + i\\tilde{M}(i_1,j_1,\\ldots,i_d,j_d,1,0). - - """ - tmp = self.tt.copy() - newcore = np.array(tmp.core, dtype=np.complex) - cr = newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] - cr = cr.reshape((tmp.r[-2], tmp.n[-1], tmp.r[-1]), order='F') - cr[:, 1, :] *= 1j - cr[:, 2:, :] = 0.0 - newcore[tmp.ps[-2]-1:tmp.ps[-1]-1] = cr.flatten('F') - tmp.core = newcore - return matrix(sum(tmp, axis=tmp.d-1), n=self.n, m=self.m) - - def __getitem__(self, index): - if len(index) == 2: - if isinstance(index[0], int) and index[1] == slice(None): - # row requested - row = index[0] - mycrs = matrix.to_list(self) - crs = [] - for i in xrange(self.tt.d): - crs.append(mycrs[i][:, row % self.n[i], :, :].copy()) - row /= self.n[i] - return tensor.from_list(crs) - elif isinstance(index[1], int) and index[0] == slice(None): - # col requested - col = index[1] - mycrs = matrix.to_list(self) - crs = [] - for i in xrange(self.tt.d): - crs.append(mycrs[i][:, :, col % self.m[i], :].copy()) - col /= self.m[i] - return tensor.from_list(crs) - elif isinstance(index[0], int) and isinstance(index[1], int): - # element requested - pass - else: - # complicated submatrix requested - pass - - def __add__(self,other): - if other is None: - return self - c = matrix() - c.tt = self.tt + other.tt - c.n = np.asanyarray(self.n,dtype=np.int32).copy() - c.m = np.asanyarray(self.m,dtype=np.int32).copy() - return c - - def __radd__(self, other): - if other is None: - return self - return other + self - - def __sub__(self,other): - c = matrix() - c.tt = self.tt-other.tt - c.n = np.asanyarray(self.n,dtype=np.int32).copy() - c.m = np.asanyarray(self.m,dtype=np.int32).copy() - return c - - def __neg__(self): - return (-1)*self - - def __matmul__(self,other): - c = matrix() - c.n = self.n.copy() - c.m = other.m.copy() - tt = tensor() - tt.d = self.tt.d - tt.n = c.n * c.m - if self.is_complex or other.is_complex: - tt.r = core_f90.core.zmat_mat(self.n,self.m,other.m,np.array(self.tt.core, dtype=np.complex),np.array(other.tt.core, dtype=np.complex),self.tt.r,other.tt.r) - tt.core = core_f90.core.zresult_core.copy() - else: - tt.r = core_f90.core.dmat_mat(self.n,self.m,other.m,np.real(self.tt.core),np.real(other.tt.core),self.tt.r,other.tt.r) - tt.core = core_f90.core.result_core.copy() - core_f90.core.dealloc() - tt.get_ps() - c.tt = tt - return c - - def __rmul__(self,other): - if hasattr(other,'__matmul__'): - return other.__matmul__(self) - else: - c = matrix() - c.tt = other * self.tt - c.n = self.n - c.m = self.m - return c - - def __mul__(self,other): - if hasattr(other,'__matmul__'): - return self.__matmul__(other) - else: - c = matrix() - c.tt = self.tt * other - c.n = self.n - c.m = self.m - return c - - def __kron__(self,other): - """ Kronecker product of two TT-matrices """ - if other is None: - return self - a = self - b = other - c = matrix() - c.n = np.concatenate((a.n,b.n)) - c.m = np.concatenate((a.m,b.m)) - c.tt = kron(a.tt,b.tt) - return c - - def norm(self): - return self.tt.norm() - - def round(self,eps): - """ Computes an approximation to a - TT-matrix in with accuracy EPS - """ - c = matrix() - c.tt = self.tt.round(eps) - c.n = self.n.copy() - c.m = self.m.copy() - return c - - 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 - - def __diag__(self): - """ Computes the diagonal of the TT-matrix""" - c = tensor() - c.n = self.n.copy() - c.r = self.tt.r.copy() - c.d = self.tt.d #Number are NOT referenced - c.get_ps() - c.alloc_core() - #Actually copy the data - for i in xrange(c.d): - cur_core1 = np.zeros((c.r[i],c.n[i],c.r[i+1])) - cur_core = self.tt.core[self.tt.ps[i]-1:self.tt.ps[i+1]-1] - cur_core = cur_core.reshape(c.r[i],self.n[i],self.m[i],c.r[i+1],order='F') - for j in xrange(c.n[i]): - cur_core1[:,j,:] = cur_core[:,j,j,:] - c.core[c.ps[i]-1:c.ps[i+1]-1] = cur_core1.flatten('F') - return c - - def full(self): - """ Transforms a TT-matrix into a full matrix""" - 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 - - def rmean(self): - return self.tt.rmean() - +from tensor import tensor +from matrix import matrix #Some binary operations (put aside to wrap something in future) #TT-matrix by a TT-vector product From 9d9f4db430f120f117f72fcf48cf085a0d0a101a Mon Sep 17 00:00:00 2001 From: Saluev Date: Fri, 18 Oct 2013 13:26:22 +0400 Subject: [PATCH 2/2] Seems to work --- tt/__init__.py | 4 ++-- tt/core/__init__.py | 4 +++- tt/core/matrix.py | 2 +- tt/core/tensor.py | 17 ++++++++++++++++- tt/core/{tt.py => utils.py} | 16 ---------------- 5 files changed, 22 insertions(+), 21 deletions(-) rename tt/core/{tt.py => utils.py} (97%) diff --git a/tt/__init__.py b/tt/__init__.py index bcd6619..3eed062 100644 --- a/tt/__init__.py +++ b/tt/__init__.py @@ -1,5 +1,5 @@ try: - from core.tt import * + from core import * except: import ctypes try: @@ -9,7 +9,7 @@ ctypes.CDLL("liblapack.so", ctypes.RTLD_GLOBAL) except: print "Did not find MKL or LAPACK library" - from core.tt import * + from core import * from multifuncrs import multifuncrs from multifuncrs2 import multifuncrs2 diff --git a/tt/core/__init__.py b/tt/core/__init__.py index eeb0c35..7354724 100644 --- a/tt/core/__init__.py +++ b/tt/core/__init__.py @@ -1,3 +1,5 @@ """TT core module """ - +from tensor import * +from matrix import * +from utils import * diff --git a/tt/core/matrix.py b/tt/core/matrix.py index 09b2e68..8d234f0 100644 --- a/tt/core/matrix.py +++ b/tt/core/matrix.py @@ -256,7 +256,7 @@ def __kron__(self,other): c = matrix() c.n = np.concatenate((a.n,b.n)) c.m = np.concatenate((a.m,b.m)) - c.tt = kron(a.tt,b.tt) + c.tt = a.tt.__kron__(b.tt) return c def norm(self): diff --git a/tt/core/tensor.py b/tt/core/tensor.py index 75aae9f..a64b42c 100644 --- a/tt/core/tensor.py +++ b/tt/core/tensor.py @@ -348,7 +348,7 @@ def __rmul__(self,other): c.core = np.array(c.core,dtype=new_core.dtype) c.core[c.ps[0]-1:c.ps[1]-1] = new_core else: - c =_hdm(self,other) + c = _hdm(self,other) return c def __mul__(self,other): @@ -469,3 +469,18 @@ def rmean(self): r = 0.5 * (-b + sqrt(D)) / a return r +def _hdm (a,b): + c = tensor() + c.d = a.d + c.n = a.n + c.r = np.zeros((a.d+1,1),dtype=np.int32) + c.ps = np.zeros((a.d+1,1),dtype=np.int32) + if np.iscomplexobj(a.core) or np.iscomplexobj(b.core): + c.r,c.ps = tt_f90.tt_f90.ztt_hdm(a.n,a.r,b.r,a.ps,b.ps,a.core,b.core) + c.core = tt_f90.tt_f90.zcore.copy() + else: + c.r,c.ps = tt_f90.tt_f90.dtt_hdm(a.n,a.r,b.r,a.ps,b.ps,a.core,b.core) + c.core = tt_f90.tt_f90.core.copy() + tt_f90.tt_f90.tt_dealloc() + return c + diff --git a/tt/core/tt.py b/tt/core/utils.py similarity index 97% rename from tt/core/tt.py rename to tt/core/utils.py index 11a7b5f..f7f7e5b 100644 --- a/tt/core/tt.py +++ b/tt/core/utils.py @@ -173,22 +173,6 @@ def concatenate(*args): return result - -def _hdm (a,b): - c = tensor() - c.d = a.d - c.n = a.n - c.r = np.zeros((a.d+1,1),dtype=np.int32) - c.ps = np.zeros((a.d+1,1),dtype=np.int32) - if np.iscomplexobj(a.core) or np.iscomplexobj(b.core): - c.r,c.ps = tt_f90.tt_f90.ztt_hdm(a.n,a.r,b.r,a.ps,b.ps,a.core,b.core) - c.core = tt_f90.tt_f90.zcore.copy() - else: - c.r,c.ps = tt_f90.tt_f90.dtt_hdm(a.n,a.r,b.r,a.ps,b.ps,a.core,b.core) - c.core = tt_f90.tt_f90.core.copy() - tt_f90.tt_f90.tt_dealloc() - return c - def sum(a, axis=-1): """Sum TT-tensor over specified axes""" d = a.d