def test_conjgrad_martens_termination_criterion(self):
        """Tests conjugate gradient method with martens termination criterion."""
        n = 500
        mat = get_pd_mat(
            np.array([[((i + j) % n) for j in range(n)] for i in range(n)]))
        b = np.linspace(1, n, n) / n
        x0 = np.zeros(n)

        test_mvm_fn = lambda x: mat @ x

        x = hessian_free.mf_conjgrad_solver(
            test_mvm_fn, b, x0, n, 1e-6, 500, None,
            'relative_per_iteration_progress_test')
        f_value = np.dot(x, test_mvm_fn(x) - 2 * b) / 2
        self.assertAlmostEqual(f_value, -0.223612323, places=8)
    def test_conjgrad(self):
        """Tests conjugate gradient method."""
        n = 5
        mat = get_pd_mat(
            np.array([[2., 4., 5., 2., 8.], [0., 4., 3., 5., 3.],
                      [-2., -2., 9., -2., -6.], [4., 1., -11., 1., 4.],
                      [-5., 4., -9., 3., -2.]]))
        b = np.array([-3, 2, 0, 3, -4])
        x0 = np.ones(n)

        test_matmul_fn = lambda x: mat @ x
        x = hessian_free.mf_conjgrad_solver(test_matmul_fn, b, x0, n, 1e-6, 10,
                                            None, 'residual_norm_test')
        self.assertAlmostEqual(np.linalg.norm(test_matmul_fn(x) - b),
                               0,
                               places=3)
    def test_conjgrad_preconditioning(self):
        """Tests conjugate gradient method with preconditioning."""
        n = 5
        mat = get_pd_mat(
            np.array([[2., 4., 5., 2., 8.], [0., 4., 3., 5., 3.],
                      [-2., -2., 9., -2., -6.], [4., 1., -11., 1., 4.],
                      [-5., 4., -9., 3., -2.]]))
        precond_mat = get_pd_mat(
            np.array([[4., 2., 0., 2., 4.], [-2., 4., 4., 2., 6.],
                      [4., 4., -8., -2., -4.], [-2., 2., 4., 0., -2.],
                      [2., 2., -6., 4., 0.]]))
        b = np.array([-3, 2, 0, 3, -4])
        x0 = np.ones(n)

        test_matmul_fn = lambda x: mat @ x
        test_precond_fn = lambda x: precond_mat @ x
        x_arr, x_arr_idx = mf_conjgrad_solver(test_matmul_fn, b, x0, n, 1e-6,
                                              10, test_precond_fn,
                                              'residual_norm_test')
        x = x_arr[x_arr_idx]
        self.assertAlmostEqual(np.linalg.norm(test_matmul_fn(x) - b),
                               0,
                               places=3)