def test_pinv(self): """test pinv""" # construct non-operator tensor train cores = [self.cores[i][:, :, 0:1, :] for i in range(self.order)] t = TT(cores) # compute pseudoinverse t_pinv = TT.pinv(t, self.order - 1) # matricize tensor trains t = t.full().reshape([np.prod(self.row_dims[:-1]), self.row_dims[-1]]) t_pinv = t_pinv.full().reshape( [np.prod(self.row_dims[:-1]), self.row_dims[-1]]).transpose() # compute relative errors rel_err_1 = np.linalg.norm(t.dot(t_pinv).dot(t) - t) / np.linalg.norm(t) rel_err_2 = np.linalg.norm(t_pinv.dot(t).dot(t_pinv) - t_pinv) / np.linalg.norm(t_pinv) rel_err_3 = np.linalg.norm((t.dot(t_pinv)).transpose() - t.dot(t_pinv)) / np.linalg.norm( t.dot(t_pinv)) rel_err_4 = np.linalg.norm((t_pinv.dot(t)).transpose() - t_pinv.dot(t)) / np.linalg.norm( t_pinv.dot(t)) # check if relative errors are smaller than tolerance self.assertLess(rel_err_1, self.tol) self.assertLess(rel_err_2, self.tol) self.assertLess(rel_err_3, self.tol) self.assertLess(rel_err_4, self.tol)
def mandy_fm(x, y, psi, threshold=0, add_one=True): """Multidimensional Approximation of Nonlinear Dynamics (MANDy) Function-major approach for construction of the tensor train xi. See [1]_ for details. Parameters ---------- x: ndarray snapshot matrix of size d x m (e.g., coordinates) y: ndarray corresponding snapshot matrix of size d x m (e.g., derivatives) psi: list of lambda functions list of basis functions threshold: float, optional threshold for SVDs, default is 0 add_one: bool, optional whether to add the basis function 1 to the cores or not, default is True Returns ------- xi: instance of TT class tensor train of coefficients for chosen basis functions References ---------- .. [1] P. Gelß, S. Klus, J. Eisert, C. Schütte, "Multidimensional Approximation of Nonlinear Dynamical Systems", arXiv:1809.02448, 2018 """ # parameters d = x.shape[0] m = x.shape[1] p = len(psi) # define cores as empty arrays cores = [np.zeros([1, d + add_one, 1, m]) ] + [np.zeros([m, d + add_one, 1, m]) for _ in range(1, p)] # insert elements of first core if add_one is True: for j in range(m): cores[0][0, 0, 0, j] = 1 cores[0][0, 1:, 0, j] = np.array([psi[0](x[k, j]) for k in range(d)]) else: for j in range(m): cores[0][0, :, 0, j] = np.array([psi[0](x[k, j]) for k in range(d)]) # insert elements of subsequent cores for i in range(1, p): if add_one is True: for j in range(m): cores[i][j, 0, 0, j] = 1 cores[i][j, 1:, 0, j] = np.array([psi[i](x[k, j]) for k in range(d)]) else: for j in range(m): cores[0][0, :, 0, j] = np.array([psi[0](x[k, j]) for k in range(d)]) # append core containing unit vectors cores.append(np.eye(m).reshape(m, m, 1, 1)) # construct tensor train xi = TT(cores) # compute pseudoinverse of xi xi = xi.pinv(p, threshold=threshold, ortho_r=False) # multiply last core with y xi.cores[p] = ( xi.cores[p].reshape([xi.ranks[p], m]) @ y.transpose()).reshape( xi.ranks[p], d, 1, 1) # set new row dimension xi.row_dims[p] = d return xi