def testBilinearFormTwoMat(self): # Test bilinear_form_two_mat. shape_list = (((2, 2), (3, 4)), ((2, 3, 4), (2, 2, 2))) rank_list = (1, 2) for tensor_shape in shape_list: for rank in rank_list: A = initializers.random_matrix(tensor_shape, tt_rank=rank, dtype=self.dtype) B = initializers.random_matrix(tensor_shape, tt_rank=rank, dtype=self.dtype) B = ops.transpose(B) x = initializers.random_matrix((tensor_shape[0], None), tt_rank=rank, dtype=self.dtype) y = initializers.random_matrix((tensor_shape[0], None), tt_rank=rank, dtype=self.dtype) res_actual = ops.bilinear_form_two_mat(x, A, B, y) vars = [ res_actual, ops.full(x), ops.full(A), ops.full(B), ops.full(y) ] res_actual_val, x_val, A_val, B_val, y_val = self.evaluate( vars) res_desired = x_val.T.dot(A_val).dot(B_val).dot(y_val) self.assertAllClose(res_actual_val, np.squeeze(res_desired), atol=1e-5, rtol=1e-5)
def testBilinearFormBatch(self): # Test bilinear form for batch of tensors. shape_list = (((2, 2), (3, 4)), ((2, 3, 4), (2, 2, 2))) rank_list = (1, 2) for tensor_shape in shape_list: for rank in rank_list: A = initializers.random_matrix(tensor_shape, tt_rank=rank, dtype=self.dtype) b = initializers.random_matrix_batch((tensor_shape[0], None), tt_rank=rank, batch_size=5, dtype=self.dtype) c = initializers.random_matrix_batch((tensor_shape[1], None), tt_rank=rank, batch_size=5, dtype=self.dtype) res_actual = ops.bilinear_form(A, b, c) vars = [res_actual, ops.full(A), ops.full(b), ops.full(c)] res_actual_val, A_val, b_val, c_val = self.evaluate(vars) res_desired = np.diag(b_val[:, :, 0].dot(A_val).dot(c_val[:, :, 0].T)) self.assertAllClose(res_actual_val, np.squeeze(res_desired), atol=1e-5, rtol=1e-5)
def testOrthogonalizeLeftToRight(self): shape = (2, 4, 3, 3) tt_ranks = (1, 5, 2, 17, 1) updated_tt_ranks = (1, 2, 2, 6, 1) tens = initializers.random_tensor_batch(shape, tt_rank=tt_ranks, batch_size=2) orthogonal = decompositions.orthogonalize_tt_cores(tens) with self.test_session() as sess: tens_val, orthogonal_val = sess.run( [ops.full(tens), ops.full(orthogonal)]) self.assertAllClose(tens_val, orthogonal_val, atol=1e-5, rtol=1e-5) dynamic_tt_ranks = shapes.tt_ranks(orthogonal).eval() self.assertAllEqual(updated_tt_ranks, dynamic_tt_ranks) # Check that the TT-cores are orthogonal. for core_idx in range(4 - 1): core_shape = (updated_tt_ranks[core_idx] * shape[core_idx], updated_tt_ranks[core_idx + 1]) for i in range(2): core = tf.reshape(orthogonal.tt_cores[core_idx][i], core_shape) should_be_eye = tf.matmul(tf.transpose(core), core) should_be_eye_val = sess.run(should_be_eye) self.assertAllClose(np.eye(updated_tt_ranks[core_idx + 1]), should_be_eye_val)
def testReduceSumBatchMultipleWeighted(self): # Multiple weighted sums of a batch of TT-tensors. def desired(tt_batch, coef): res = coef[0] * tt_batch[0] for i in range(1, tt_batch.batch_size): res += coef[i] * tt_batch[i] return res with self.test_session() as sess: tt_batch = initializers.random_tensor_batch((4, 3, 5), tt_rank=2, batch_size=3) coef = [[1., 0.1], [0.9, -0.2], [0.3, 0.3]] coef = np.array(coef).astype(np.float32) res_actual = ops.full( approximate.reduce_sum_batch(tt_batch, 6, coef)) res_desired_1 = ops.full(desired(tt_batch, coef[:, 0])) res_desired_2 = ops.full(desired(tt_batch, coef[:, 1])) res_desired = tf.stack((res_desired_1, res_desired_2)) res_desired_val, res_actual_val = sess.run( [res_desired, res_actual]) self.assertAllClose(res_desired_val, res_actual_val, atol=1e-5, rtol=1e-5)
def _TestSingleGradient(self, func, x, desired): actual1 = ops.full(autodiff.gradients(func, x, runtime_check=False)) actual2 = ops.full(autodiff.gradients(func, x, runtime_check=True)) desired_v, actual1_v, actual2_v = self.evaluate([desired, actual1, actual2]) self.assertAllClose(desired_v, actual1_v, rtol=1e-4) self.assertAllClose(desired_v, actual2_v, rtol=1e-4)
def testPairwiseFlatInnerVectorsWithMatrix(self): # Test pairwise_flat_inner of a batch of TT vectors with providing a matrix, # so we should compute # res[i, j] = tt_vectors[i] ^ T * matrix * tt_vectors[j] tt_vectors_1 = initializers.random_matrix_batch(((2, 3), None), batch_size=2, dtype=self.dtype) tt_vectors_2 = initializers.random_matrix_batch(((2, 3), None), batch_size=3, dtype=self.dtype) matrix = initializers.random_matrix(((2, 3), (2, 3)), dtype=self.dtype) res_actual = batch_ops.pairwise_flat_inner(tt_vectors_1, tt_vectors_2, matrix) full_vectors_1 = tf.reshape(ops.full(tt_vectors_1), (2, 6)) full_vectors_2 = tf.reshape(ops.full(tt_vectors_2), (3, 6)) with self.test_session() as sess: res = sess.run( (res_actual, full_vectors_1, full_vectors_2, ops.full(matrix))) res_actual_val, vectors_1_val, vectors_2_val, matrix_val = res res_desired_val = np.zeros((2, 3)) for i in range(2): for j in range(3): curr_val = np.dot(vectors_1_val[i], matrix_val) curr_val = np.dot(curr_val, vectors_2_val[j]) res_desired_val[i, j] = curr_val self.assertAllClose(res_desired_val, res_actual_val)
def testHessianVectorProduct(self): w = initializers.random_matrix(([5] * 3, None), dtype=self.dtype) A = initializers.random_matrix(([5] * 3, [5] * 3), dtype=self.dtype) x = initializers.random_matrix(([5] * 3, None), dtype=self.dtype) z = initializers.random_matrix(([5] * 3, None), dtype=self.dtype) projected_vector = riemannian.project(z, x) def func1(x): return 0.5 * ops.flat_inner(x, w) ** 2 # Grad: <x, w> w # Hessian: w w.T # Hessian by vector: w <w, P_x z> desired1 = riemannian.project(w * ops.flat_inner(projected_vector, w), x) desired1 = ops.full(desired1) self._TestSingleHessianByVector(func1, x, z, desired1) def func2(x): return ops.bilinear_form(A, x, x) # Hessian of <x, Ax> is A + A.T hessian_by_vector = ops.matmul(ops.transpose(A) + A, projected_vector) desired2 = ops.full(riemannian.project(hessian_by_vector, x)) self._TestSingleHessianByVector(func1, x, z, desired1) def func3(x): # A function which is not invariant to different representations of the # same tensor, i.e. it does not even have a Riemannian gradient or # hessian. return tf.add_n([tf.reduce_sum(c) for c in x.tt_cores]) ** 2 with self.assertRaises(tf.errors.InvalidArgumentError): actual3 = ops.full(autodiff.hessian_vector_product(func3, x, z)) self.evaluate(actual3)
def testOrthogonalizeRightToLeft(self): shape = (2, 4, 3, 3) tt_ranks = (1, 5, 2, 17, 1) updated_tt_ranks = (1, 5, 2, 3, 1) tens = initializers.random_tensor(shape, tt_rank=tt_ranks, dtype=self.dtype) orthogonal = decompositions.orthogonalize_tt_cores(tens, left_to_right=False) with self.test_session() as sess: tens_val, orthogonal_val = sess.run( [ops.full(tens), ops.full(orthogonal)]) self.assertAllClose(tens_val, orthogonal_val, atol=1e-5, rtol=1e-5) dynamic_tt_ranks = shapes.tt_ranks(orthogonal).eval() self.assertAllEqual(updated_tt_ranks, dynamic_tt_ranks) # Check that the TT-cores are orthogonal. for core_idx in range(1, 4): core = orthogonal.tt_cores[core_idx] core = tf.reshape( core, (updated_tt_ranks[core_idx], shape[core_idx] * updated_tt_ranks[core_idx + 1])) should_be_eye = tf.matmul(core, tf.transpose(core)) should_be_eye_val = sess.run(should_be_eye) self.assertAllClose(np.eye(updated_tt_ranks[core_idx]), should_be_eye_val)
def testMultiplyUnknownSizeBatchAndBatch(self): c1 = tf.placeholder(tf.float32, [None, 1, 3, 2]) c2 = tf.placeholder(tf.float32, [None, 2, 3, 1]) tt_b = initializers.random_tensor_batch((3, 3), tt_rank=2, batch_size=8) tt_a = TensorTrainBatch([c1, c2]) res_ab = ops.full(ops.multiply(tt_a, tt_b)) res_ba = ops.full(ops.multiply(tt_b, tt_a)) res_desired = ops.full(tt_a) * ops.full(tt_b) to_run = [res_ab, res_ba, res_desired] feed_dict = { c1: np.random.rand(8, 1, 3, 2), c2: np.random.rand(8, 2, 3, 1) } feed_dict_err = { c1: np.random.rand(1, 1, 3, 2), c2: np.random.rand(1, 2, 3, 1) } with self.test_session() as sess: ab_full, ba_full, des_full = sess.run(to_run, feed_dict=feed_dict) self.assertAllClose(ab_full, des_full) self.assertAllClose(ba_full, des_full) with self.assertRaises(tf.errors.InvalidArgumentError): sess.run(to_run, feed_dict=feed_dict_err)
def testTTTensorSimple(self): # Test that a tensor of ones and of zeros can be converted into TT with # TT-rank 1. shape = (2, 1, 4, 3) tens_arr = (np.zeros(shape).astype(np.float32), np.ones(shape).astype(np.float32)) for tens in tens_arr: tf_tens = tf.constant(tens) tt_tens = decompositions.to_tt_tensor(tf_tens, max_tt_rank=1) with self.test_session(): self.assertAllClose(tens, ops.full(tt_tens).eval()) dynamic_tt_ranks = shapes.tt_ranks(tt_tens).eval() static_tt_ranks = tt_tens.get_tt_ranks().as_list() self.assertAllEqual(dynamic_tt_ranks, static_tt_ranks) # Try to decompose the same tensor with unknown shape. tf_tens_pl = tf.placeholder(tf.float32, (None, None, None, None)) tt_tens = decompositions.to_tt_tensor(tf_tens_pl, max_tt_rank=1) tt_val = ops.full(tt_tens).eval({tf_tens_pl: tens}) self.assertAllClose(tens, tt_val) dynamic_tt_ranks = shapes.tt_ranks(tt_tens).eval( {tf_tens_pl: tens}) self.assertAllEqual(dynamic_tt_ranks, static_tt_ranks)
def testProjectOnItself(self): # Projection of X into the tangent space of itself is X: P_x(x) = x. tens = initializers.random_tensor((2, 3, 4), dtype=self.dtype) proj = riemannian.project_sum(tens, tens) with self.test_session() as sess: actual_val, desired_val = sess.run((ops.full(proj), ops.full(tens))) self.assertAllClose(desired_val, actual_val)
def testSlogDet(self): # Tests the slog_determinant function # TODO: use kron and -1 * kron matrices, when mul is implemented # the current version is platform-dependent tf.compat.v1.set_random_seed(5) # negative derminant initializer = initializers.random_matrix(((2, 3), (2, 3)), tt_rank=1, dtype=self.dtype) kron_neg = variables.get_variable('kron_neg', initializer=initializer) tf.compat.v1.set_random_seed(1) # positive determinant initializer = initializers.random_matrix(((2, 3), (2, 3)), tt_rank=1, dtype=self.dtype) kron_pos = variables.get_variable('kron_pos', initializer=initializer) init_op = tf.compat.v1.global_variables_initializer() # negative derminant self.evaluate(init_op) desired_sign, desired_det = np.linalg.slogdet( self.evaluate(ops.full(kron_neg))) actual_sign, actual_det = self.evaluate(kr.slog_determinant(kron_neg)) self.assertEqual(desired_sign, actual_sign) self.assertAllClose(desired_det, actual_det) # positive determinant desired_sign, desired_det = np.linalg.slogdet( self.evaluate(ops.full(kron_pos))) actual_sign, actual_det = self.evaluate(kr.slog_determinant(kron_pos)) self.assertEqual(desired_sign, actual_sign) self.assertAllClose(desired_det, actual_det)
def testTensorIndexing(self): tens = initializers.random_tensor((3, 3, 4), dtype=self.dtype) with self.test_session() as sess: desired = ops.full(tens)[:, :, :] actual = ops.full(tens[:, :, :]) desired, actual = sess.run([desired, actual]) self.assertAllClose(desired, actual) desired = ops.full(tens)[1, :, :] actual = ops.full(tens[1, :, :]) desired, actual = sess.run([desired, actual]) self.assertAllClose(desired, actual) desired = ops.full(tens)[1:2, 1, :] actual = ops.full(tens[1:2, 1, :]) desired, actual = sess.run([desired, actual]) self.assertAllClose(desired, actual) desired = ops.full(tens)[0:3, :, 3] actual = ops.full(tens[0:3, :, 3]) desired, actual = sess.run([desired, actual]) self.assertAllClose(desired, actual) desired = ops.full(tens)[1, :, 3] actual = ops.full(tens[1, :, 3]) desired, actual = sess.run([desired, actual]) self.assertAllClose(desired, actual) # Wrong number of dims. with self.assertRaises(ValueError): tens[1, :, 3, :] with self.assertRaises(ValueError): tens[1, 1]
def testTTMatTimesTTMatBroadcasting(self): # Multiply a batch of TT-matrices by another batch of TT-matrices with # broadcasting. left_shape = (2, 3) sum_shape = (4, 3) right_shape = (4, 4) with self.test_session() as sess: tt_mat_1 = initializers.random_matrix_batch((left_shape, sum_shape), tt_rank=3, batch_size=3, dtype=self.dtype) tt_mat_2 = initializers.random_matrix_batch((sum_shape, right_shape), dtype=self.dtype) # TT-batch by one element TT-batch res_actual = ops.matmul(tt_mat_1, tt_mat_2) res_actual = ops.full(res_actual) # TT by TT-batch. res_actual2 = ops.matmul(ops.transpose(tt_mat_2[0]), ops.transpose(tt_mat_1)) res_actual2 = ops.full(ops.transpose(res_actual2)) res_desired = tf.einsum('oij,jk->oik', ops.full(tt_mat_1), ops.full(tt_mat_2[0])) to_run = [res_actual, res_actual2, res_desired] res_actual_val, res_actual2_val, res_desired_val = sess.run(to_run) self.assertAllClose(res_actual_val, res_desired_val, atol=1e-5, rtol=1e-5) self.assertAllClose(res_actual2_val, res_desired_val, atol=1e-5, rtol=1e-5)
def testMultiplyTwoBatchesUnknownSize(self): c1 = tf.placeholder(self.dtype, [None, 1, 3, 2]) c2 = tf.placeholder(self.dtype, [None, 2, 3, 1]) c3 = tf.placeholder(self.dtype, [None, 1, 3, 2]) c4 = tf.placeholder(self.dtype, [None, 2, 3, 1]) tt_a = TensorTrainBatch([c1, c2]) tt_b = TensorTrainBatch([c3, c4]) res_ab = ops.full(ops.multiply(tt_a, tt_b)) res_ba = ops.full(ops.multiply(tt_b, tt_a)) res_desired = ops.full(tt_a) * ops.full(tt_b) to_run = [res_ab, res_ba, res_desired] feed_dict = {c1:np.random.rand(7, 1, 3, 2), c2:np.random.rand(7, 2, 3, 1), c3:np.random.rand(7, 1, 3, 2), c4:np.random.rand(7, 2, 3, 1)} feed_dict_err = {c1:np.random.rand(7, 1, 3, 2), c2:np.random.rand(7, 2, 3, 1), c3:np.random.rand(1, 1, 3, 2), c4:np.random.rand(1, 2, 3, 1)} with self.test_session() as sess: ab_full, ba_full, des_full = sess.run(to_run, feed_dict=feed_dict) self.assertAllClose(ab_full, des_full) self.assertAllClose(ba_full, des_full) with self.assertRaises(tf.errors.InvalidArgumentError): sess.run(to_run, feed_dict=feed_dict_err)
def testTranspose(self): # Transpose a batch of TT-matrices. with self.test_session() as sess: tt = initializers.random_matrix_batch(((2, 3, 4), (2, 2, 2)), batch_size=2) res_actual = ops.full(ops.transpose(tt)) res_actual_val, tt_val = sess.run([res_actual, ops.full(tt)]) self.assertAllClose(tt_val.transpose((0, 2, 1)), res_actual_val)
def _TestSingleGradient(self, func, x, desired): actual1 = ops.full(autodiff.gradients(func, x, runtime_check=False)) actual2 = ops.full(autodiff.gradients(func, x, runtime_check=True)) with self.test_session() as sess: desired_v, actual1_v, actual2_v = sess.run([desired, actual1, actual2]) self.assertAllClose(desired_v, actual1_v, rtol=1e-4) self.assertAllClose(desired_v, actual2_v, rtol=1e-4)
def testProjectMatrixOnItself(self): # Project a TT-matrix on itself. # Projection of X into the tangent space of itself is X: P_x(x) = x. tt_mat = initializers.random_matrix(((2, 3, 4), (2, 3, 4)), dtype=self.dtype) proj = riemannian.project_sum(tt_mat, tt_mat) actual_val, desired_val = self.evaluate((ops.full(proj), ops.full(tt_mat))) self.assertAllClose(desired_val, actual_val)
def testTranspose(self): # Transpose a batch of TT-matrices. tt = initializers.random_matrix_batch(((2, 3, 4), (2, 2, 2)), batch_size=2, dtype=self.dtype) res_actual = ops.full(ops.transpose(tt)) res_actual_val, tt_val = self.evaluate([res_actual, ops.full(tt)]) self.assertAllClose(tt_val.transpose((0, 2, 1)), res_actual_val)
def testPlaceholderTensorIndexing(self): tens = initializers.random_tensor((3, 3, 4), dtype=self.dtype) with self.test_session() as sess: start = tf.placeholder(tf.int32) end = tf.placeholder(tf.int32) desired = ops.full(tens)[1:3, 1, :3] actual = ops.full(tens[start:end, start, :end]) desired, actual = sess.run([desired, actual], {start: 1, end: 3}) self.assertAllClose(desired, actual)
def _TestSingleHessianByVector(self, func, x, z, desired): actual1 = ops.full(autodiff.hessian_vector_product( func, x, z, runtime_check=False)) actual2 = ops.full(autodiff.hessian_vector_product(func, x, z, runtime_check=True)) desired_v, actual1_v, actual2_v = self.evaluate([desired, actual1, actual2]) self.assertAllClose(desired_v, actual1_v, rtol=1e-4) self.assertAllClose(desired_v, actual2_v, rtol=1e-4)
def testCompareProjectSumAndProject(self): # Compare results of project_sum and project. tens = initializers.random_tensor_batch((2, 3, 4), 3, batch_size=4) tangent_tens = initializers.random_tensor((2, 3, 4), 4) project_sum = riemannian.project_sum(tens, tangent_tens, tf.eye(4)) project = riemannian.project(tens, tangent_tens) with self.test_session() as sess: res = sess.run((ops.full(project_sum), ops.full(project))) project_sum_val, project_val = res self.assertAllClose(project_sum_val, project_val)
def testInv(self): # Tests the inv function initializer = initializers.random_matrix(((2, 3, 2), (2, 3, 2)), tt_rank=1) kron_mat = variables.get_variable('kron_mat', initializer=initializer) init_op = tf.global_variables_initializer() with self.test_session() as sess: sess.run(init_op) desired = np.linalg.inv(ops.full(kron_mat).eval()) actual = ops.full(kr.inv(kron_mat)).eval() self.assertAllClose(desired, actual)
def testProjectSum(self): # Test projecting a batch of TT-tensors. tens = initializers.random_tensor_batch((2, 3, 4), batch_size=3) tangent_tens = initializers.random_tensor((2, 3, 4), 3) weighted_sum = tens[0] + tens[1] + tens[2] direct_proj = riemannian.project_sum(weighted_sum, tangent_tens) actual_proj = riemannian.project_sum(tens, tangent_tens) with self.test_session() as sess: res = sess.run((ops.full(direct_proj), ops.full(actual_proj))) desired_val, actual_val = res self.assertAllClose(desired_val, actual_val)
def testInv(self): # Tests the inv function initializer = initializers.random_matrix(((2, 3, 2), (2, 3, 2)), tt_rank=1, dtype=self.dtype) kron_mat = variables.get_variable('kron_mat', initializer=initializer) init_op = tf.compat.v1.global_variables_initializer() self.evaluate(init_op) desired = np.linalg.inv(self.evaluate(ops.full(kron_mat))) actual = self.evaluate(ops.full(kr.inv(kron_mat))) self.assertAllClose(desired, actual)
def testBatchMultiply(self): # Test multiplying batch of TTMatrices by individual numbers. tt = initializers.random_matrix_batch(((2, 3), (3, 3)), batch_size=3) weights = [0.1, 0, -10] actual = batch_ops.multiply_along_batch_dim(tt, weights) individual_desired = [weights[i] * tt[i:i + 1] for i in range(3)] desired = batch_ops.concat_along_batch_dim(individual_desired) with self.test_session() as sess: desired_val, acutual_val = sess.run( (ops.full(desired), ops.full(actual))) self.assertAllClose(desired_val, acutual_val)
def testConcatMatrixPlaceholders(self): # Test concating TTMatrices of unknown batch sizes along batch dimension. number_of_objects = tf.placeholder(tf.int32) all = initializers.random_matrix_batch(((2, 3), (2, 3)), batch_size=5) actual = batch_ops.concat_along_batch_dim( (all[:number_of_objects], all[number_of_objects:])) with self.test_session() as sess: desired_val, actual_val = sess.run( (ops.full(all), ops.full(actual)), feed_dict={number_of_objects: 2}) self.assertAllClose(desired_val, actual_val)
def testMultiplyByNumber(self): # Multiply a tensor by a number. tt = initializers.random_tensor((1, 2, 3), tt_rank=(1, 2, 3, 1)) with self.test_session() as sess: res_actual = ops.full(ops.multiply(tt, 4)) res_actual2 = ops.full(4.0 * tt) res_desired = 4.0 * ops.full(tt) to_run = [res_actual, res_actual2, res_desired] res_actual_val, res_actual2_val, res_desired_val = sess.run(to_run) self.assertAllClose(res_actual_val, res_desired_val) self.assertAllClose(res_actual2_val, res_desired_val)
def testCompareProjectSumAndProject(self): # Compare results of project_sum and project. tens = initializers.random_tensor_batch((2, 3, 4), 3, batch_size=4, dtype=self.dtype) tangent_tens = initializers.random_tensor((2, 3, 4), 4, dtype=self.dtype) project_sum = riemannian.project_sum(tens, tangent_tens, np.eye(4)) project = riemannian.project(tens, tangent_tens) res = self.evaluate((ops.full(project_sum), ops.full(project))) project_sum_val, project_val = res self.assertAllClose(project_sum_val, project_val)
def testTranspose(self): # Transpose a TT-matrix. shape_list = (((2, 2), (3, 4)), ((2, 3, 4), (2, 2, 2))) rank_list = (1, 2) with self.test_session() as sess: for tensor_shape in shape_list: for rank in rank_list: tt = initializers.random_matrix(tensor_shape, tt_rank=rank) res_actual = ops.full(ops.transpose(tt)) res_actual_val, tt_val = sess.run( [res_actual, ops.full(tt)]) self.assertAllClose(tt_val.transpose(), res_actual_val)