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 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 testAddN(self): # Sum a bunch of TT-matrices. tt_a = initializers.random_matrix(((2, 1, 4), (2, 2, 2)), tt_rank=2, dtype=self.dtype) tt_b = initializers.random_matrix(((2, 1, 4), (2, 2, 2)), tt_rank=[1, 2, 4, 1], dtype=self.dtype) def desired(tt_objects): res = tt_objects[0] for tt in tt_objects[1:]: res += tt return res res_actual = ops.full(approximate.add_n([tt_a, tt_b], 6)) res_desired = ops.full(desired([tt_a, tt_b])) res_desired_val, res_actual_val = self.evaluate([res_desired, res_actual]) self.assertAllClose(res_desired_val, res_actual_val, atol=1e-5, rtol=1e-5) res_actual = ops.full(approximate.add_n([tt_a, tt_b, tt_a], 8)) res_desired = ops.full(desired([tt_a, tt_b, tt_a])) res_desired_val, res_actual_val = self.evaluate([res_desired, res_actual]) self.assertAllClose(res_desired_val, res_actual_val, atol=1e-5, rtol=1e-5) res_actual = ops.full(approximate.add_n([tt_a, tt_b, tt_a, tt_a, tt_a], 12)) res_desired = ops.full(desired([tt_a, tt_b, tt_a, tt_a, tt_a])) res_desired_val, res_actual_val = self.evaluate([res_desired, res_actual]) self.assertAllClose(res_desired_val, res_actual_val, atol=1e-5, rtol=1e-5)
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 testPairwiseFlatInnerMatrix(self): # Compare pairwise_flat_inner_projected against naive implementation. what1 = initializers.random_matrix_batch(((2, 3, 4), None), 4, batch_size=3, dtype=self.dtype) what2 = initializers.random_matrix_batch(((2, 3, 4), None), 4, batch_size=4, dtype=self.dtype) where = initializers.random_matrix(((2, 3, 4), None), 3, dtype=self.dtype) projected1 = riemannian.project(what1, where) projected2 = riemannian.project(what2, where) desired = batch_ops.pairwise_flat_inner(projected1, projected2) actual = riemannian.pairwise_flat_inner_projected(projected1, projected2) with self.test_session() as sess: desired_val, actual_val = sess.run((desired, actual)) self.assertAllClose(desired_val, actual_val, atol=1e-5, rtol=1e-5) with self.assertRaises(ValueError): # Second argument is not a projection on the tangent space. riemannian.pairwise_flat_inner_projected(projected1, what2) where2 = initializers.random_matrix(((2, 3, 4), None), 3, dtype=self.dtype) another_projected2 = riemannian.project(what2, where2) with self.assertRaises(ValueError): # The arguments are projections on different tangent spaces. riemannian.pairwise_flat_inner_projected(projected1, another_projected2)
def testCastFloat(self): # Test cast function for float tt-matrices and vectors. tt_mat = initializers.random_matrix(((2, 3), (3, 2)), tt_rank=2) tt_vec = initializers.random_matrix(((2, 3), None), tt_rank=2) for tt in [tt_mat, tt_vec]: casted = ops.cast(tt, self.dtype) casted_val = self.evaluate(ops.full(casted)) self.assertEqual(self.dtype, casted.dtype) self.assertTrue(self.dtype, casted_val.dtype)
def testProjectMatmul(self): # Project a TT-matrix times TT-vector on a TT-vector. tt_mat = initializers.random_matrix(((2, 3, 4), (2, 3, 4))) tt_vec_what = initializers.random_matrix_batch(((2, 3, 4), None), batch_size=3) tt_vec_where = initializers.random_matrix(((2, 3, 4), None)) proj = riemannian.project_matmul(tt_vec_what, tt_vec_where, tt_mat) matvec = ops.matmul(tt_mat, tt_vec_what) proj_desired = riemannian.project(matvec, tt_vec_where) with self.test_session() as sess: actual_val, desired_val = sess.run((ops.full(proj), ops.full(proj_desired))) self.assertAllClose(desired_val, actual_val, atol=1e-5, rtol=1e-5)
def testCastFloat(self): # Test cast function for float tt-matrices and vectors. tt_mat = initializers.random_matrix(((2, 3), (3, 2)), tt_rank=2) tt_vec = initializers.random_matrix(((2, 3), None), tt_rank=2) with self.test_session() as sess: for tt in [tt_mat, tt_vec]: for dtype in [tf.float16, tf.float32, tf.float64]: casted = ops.cast(tt, dtype) casted_val = sess.run(ops.full(casted)) self.assertEqual(dtype, casted.dtype) self.assertTrue(dtype, casted_val.dtype)
def testRandomMatrix(self): shapes = [[1, 2, 3], [[1, 2], [1, 2, 3]], [[-1, 2, 3], [1, 2, 3]], [[0.5, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]] tt_ranks = [2, 2, 2, 2, -1, [[[1]]], [2.5, 3]] bad_cases = zip(shapes, tt_ranks) for case in bad_cases: with self.assertRaises(ValueError): initializers.random_matrix(case[0], tt_rank=case[1]) for case in bad_cases: with self.assertRaises(ValueError): initializers.matrix_with_random_cores(case[0], tt_rank=case[1]) with self.assertRaises(NotImplementedError): initializers.random_matrix([[2, 3, 4], [1, 2, 3]], mean=1.0)
def testTTMatTimesTTMat(self): # Multiply a TT-matrix by another TT-matrix. left_shape = (2, 3, 4) sum_shape = (4, 3, 5) right_shape = (4, 4, 4) with self.test_session() as sess: tt_mat_1 = initializers.random_matrix((left_shape, sum_shape), tt_rank=3, dtype=self.dtype) tt_mat_2 = initializers.random_matrix((sum_shape, right_shape), dtype=self.dtype) res_actual = ops.matmul(tt_mat_1, tt_mat_2) res_actual = ops.full(res_actual) res_desired = tf.matmul(ops.full(tt_mat_1), ops.full(tt_mat_2)) res_actual_val, res_desired_val = sess.run([res_actual, res_desired]) # TODO: why so bad accuracy? self.assertAllClose(res_actual_val, res_desired_val, atol=1e-4, rtol=1e-4)
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 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 testFlatInnerTTMatbySparseMat(self): # Inner product between a TT-matrix and a sparse matrix. shape_list = (((2, 2), (3, 4)), ((2, 3, 4), (2, 2, 2))) rank_list = (1, 2) np.random.seed(1) with self.test_session() as sess: for tensor_shape in shape_list: for rank in rank_list: for num_elements in [1, 9]: tt_1 = initializers.random_matrix(tensor_shape, tt_rank=rank) matrix_shape = np.prod(tensor_shape[0]), np.prod( tensor_shape[1]) sparse_flat_indices = np.random.choice( np.prod(matrix_shape), num_elements) sparse_flat_indices = sparse_flat_indices.astype(int) sparse_indices = np.unravel_index( sparse_flat_indices, matrix_shape) sparse_indices = np.vstack(sparse_indices).transpose() values = np.random.randn(num_elements).astype( np.float32) sparse_2 = tf.SparseTensor(indices=sparse_indices, values=values, dense_shape=matrix_shape) res_actual = ops.flat_inner(tt_1, sparse_2) res_actual_val, tt_1_val = sess.run( [res_actual, ops.full(tt_1)]) res_desired_val = tt_1_val.flatten( )[sparse_flat_indices].dot(values) self.assertAllClose(res_actual_val, res_desired_val)
def testIsKronNonKron(self): # Tests _is_kron on a non-Kronecker matrix initializer = initializers.random_matrix(((2, 3), (3, 2)), tt_rank=2, dtype=self.dtype) tt_mat = variables.get_variable('tt_mat', initializer=initializer) self.assertFalse(kr._is_kron(tt_mat))
def testIsKronKron(self): # Tests _is_kron on a Kronecker matrix initializer = initializers.random_matrix(((2, 3), (3, 2)), tt_rank=1, dtype=self.dtype) kron_mat = variables.get_variable('kron_mat', initializer=initializer) self.assertTrue(kr._is_kron(kron_mat))
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 testFlatInnerTTMatbyTTMat(self): # Inner product between two TT-Matrices. shape_list = (((2, 2), (3, 4)), ((2, 3, 4), (2, 2, 2))) rank_list = (1, 2) with self.test_session() as sess: for shape in shape_list: for rank in rank_list: tt_1 = initializers.random_matrix(shape, tt_rank=rank) tt_2 = initializers.random_matrix(shape, tt_rank=rank) res_actual = ops.flat_inner(tt_1, tt_2) tt_1_full = tf.reshape(ops.full(tt_1), (1, -1)) tt_2_full = tf.reshape(ops.full(tt_2), (-1, 1)) res_desired = tf.matmul(tt_1_full, tt_2_full) res_actual_val, res_desired_val = sess.run( [res_actual, res_desired]) self.assertAllClose(res_actual_val, np.squeeze(res_desired_val), rtol=1e-5, atol=1e-5)
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 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 testDet(self): # Tests the determinant 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.global_variables_initializer() with self.test_session() as sess: sess.run(init_op) desired = np.linalg.det(ops.full(kron_mat).eval()) actual = kr.determinant(kron_mat).eval() self.assertAllClose(desired, actual)
def testQuadraticForm(self): # Test quadratic form. 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: A = initializers.random_matrix(tensor_shape, tt_rank=rank) b = initializers.random_matrix((tensor_shape[0], None), tt_rank=rank) c = initializers.random_matrix((tensor_shape[1], None), tt_rank=rank) res_actual = ops.quadratic_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 = sess.run(vars) res_desired = b_val.T.dot(A_val).dot(c_val) self.assertAllClose(res_actual_val, np.squeeze(res_desired), atol=1e-5, rtol=1e-5)
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)
def testAttributes(self): # Test that after converting an initializer into a variable all the # attributes stays the same. tens = initializers.random_tensor([2, 3, 2], tt_rank=2) tens_v = variables.get_variable('tt_tens', initializer=tens) mat = initializers.random_matrix([[3, 2, 2], [3, 3, 3]], tt_rank=3) mat_v = variables.get_variable('tt_mat', initializer=mat) for (init, var) in [[tens, tens_v], [mat, mat_v]]: self.assertEqual(init.get_shape(), var.get_shape()) self.assertEqual(init.get_raw_shape(), var.get_raw_shape()) self.assertEqual(init.ndims(), var.ndims()) self.assertEqual(init.get_tt_ranks(), var.get_tt_ranks()) self.assertEqual(init.is_tt_matrix(), var.is_tt_matrix())
def testTranspose(self): # Transpose a TT-matrix. 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: tt = initializers.random_matrix(tensor_shape, tt_rank=rank, 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(), res_actual_val)
def testGradients(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) def func1(x): return 0.5 * ops.flat_inner(x, w) ** 2 desired1 = ops.full(riemannian.project(w, x) * ops.flat_inner(x, w)) self._TestSingleGradient(func1, x, desired1) def func2(x): return ops.bilinear_form(A, x, x) grad = ops.matmul(ops.transpose(A) + A, x) desired2 = ops.full(riemannian.project(grad, x)) self._TestSingleGradient(func2, x, desired2) 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. 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.gradients(func3, x)) self.evaluate(actual3)
def testTTMatTimesDenseVec(self): # Multiply a TT-matrix by a dense vector. inp_shape = (2, 3, 4) out_shape = (3, 4, 3) np.random.seed(1) vec = np.random.rand(np.prod(inp_shape), 1).astype(np.float32) with self.test_session() as sess: tf_vec = tf.constant(vec) tf.set_random_seed(1) tt_mat = initializers.random_matrix((out_shape, inp_shape)) res_actual = ops.matmul(tt_mat, tf_vec) res_desired = tf.matmul(ops.full(tt_mat), tf_vec) res_actual_val, res_desired_val = sess.run( [res_actual, res_desired]) self.assertAllClose(res_actual_val, res_desired_val)
def testDenseMatTimesTTVec(self): # Multiply a TT-matrix by a dense vector. inp_shape = (3, 3, 3, 3) out_shape = (3, 3, 3, 3) np.random.seed(1) mat = np.random.rand(np.prod(out_shape), np.prod(inp_shape)) mat = mat.astype(self.dtype.as_numpy_dtype) with self.test_session() as sess: tf_mat = tf.constant(mat) tf.set_random_seed(1) tt_vec = initializers.random_matrix((inp_shape, None), dtype=self.dtype) res_actual = ops.matmul(tf_mat, tt_vec) res_desired = tf.matmul(tf_mat, ops.full(tt_vec)) res_actual_val, res_desired_val = sess.run([res_actual, res_desired]) self.assertAllClose(res_actual_val, res_desired_val, atol=1e-4, rtol=1e-4)
def testTTMatTimesDenseVec(self): # Multiply a TT-matrix by a dense vector. inp_shape = (2, 3, 4) out_shape = (3, 4, 3) np.random.seed(1) vec = np.random.rand(np.prod(inp_shape), 1).astype(self.dtype.as_numpy_dtype) tf_vec = tf.constant(vec) tf.compat.v1.set_random_seed(1) tt_mat = initializers.random_matrix((out_shape, inp_shape), dtype=self.dtype) res_actual = ops.matmul(tt_mat, tf_vec) res_desired = tf.matmul(ops.full(tt_mat), tf_vec) res_actual_val, res_desired_val = self.evaluate( [res_actual, res_desired]) self.assertAllClose(res_actual_val, res_desired_val)
def testFrobeniusNormMatrix(self): # Frobenius norm of 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, dtype=self.dtype) norm_sq_actual = ops.frobenius_norm_squared(tt) norm_actual = ops.frobenius_norm(tt) vars = [norm_sq_actual, norm_actual, ops.full(tt)] norm_sq_actual_val, norm_actual_val, tt_val = sess.run(vars) tt_val = tt_val.flatten() norm_sq_desired_val = tt_val.dot(tt_val) norm_desired_val = np.linalg.norm(tt_val) self.assertAllClose(norm_sq_actual_val, norm_sq_desired_val) self.assertAllClose(norm_actual_val, norm_desired_val, atol=1e-5, rtol=1e-5)
def testGramMatrixWithMatrix(self): # Test Gram Matrix 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 = initializers.random_matrix_batch(((2, 3), None), batch_size=4) matrix = initializers.random_matrix(((2, 3), (2, 3))) res_actual = batch_ops.gram_matrix(tt_vectors, matrix) full_vectors = tf.reshape(ops.full(tt_vectors), (4, 6)) with self.test_session() as sess: res = sess.run((res_actual, full_vectors, ops.full(matrix))) res_actual_val, vectors_val, matrix_val = res res_desired_val = np.zeros((4, 4)) for i in range(4): for j in range(4): curr_val = np.dot(vectors_val[i], matrix_val) curr_val = np.dot(curr_val, vectors_val[j]) res_desired_val[i, j] = curr_val self.assertAllClose(res_desired_val, res_actual_val, atol=1e-5, rtol=1e-5)