def test_posterior_mean_CG_equivalency(self): """ The probabilistic linear solver(s) should recover CG iterates as a posterior mean for specific covariances. """ # Linear system A, b = self.poisson_linear_system # Callback function to return CG iterates cg_iterates = [] def callback_iterates_CG(xk): cg_iterates.append( np.eye(np.shape(A)[0]) @ xk ) # identity hack to actually save different iterations # Solve linear system # Initial guess as chosen by PLS: x0 = Ainv.mean @ b x0 = b # Conjugate gradient method xhat_cg, info_cg = scipy.sparse.linalg.cg( A=A, b=b, x0=x0, tol=10 ** -6, callback=callback_iterates_CG ) cg_iters_arr = np.array([x0] + cg_iterates) # Matrix priors (encoding weak symmetric posterior correspondence) Ainv0 = rvs.Normal( mean=linops.Identity(A.shape[1]), cov=linops.SymmetricKronecker(A=linops.Identity(A.shape[1])), ) A0 = rvs.Normal( mean=linops.Identity(A.shape[1]), cov=linops.SymmetricKronecker(A) ) for kwargs in [{"assume_A": "sympos", "rtol": 10 ** -6}]: with self.subTest(): # Define callback function to obtain search directions pls_iterates = [] # pylint: disable=cell-var-from-loop def callback_iterates_PLS( xk, Ak, Ainvk, sk, yk, alphak, resid, **kwargs ): pls_iterates.append(xk.mean) # Probabilistic linear solver xhat_pls, _, _, info_pls = linalg.problinsolve( A=A, b=b, Ainv0=Ainv0, A0=A0, callback=callback_iterates_PLS, **kwargs ) pls_iters_arr = np.array([x0] + pls_iterates) self.assertAllClose(xhat_pls.mean, xhat_cg, rtol=10 ** -12) self.assertAllClose(pls_iters_arr, cg_iters_arr, rtol=10 ** -12)
def test_iterative_covariance_trace_update(self): """The solver's returned value for the trace must match the actual trace of the solution covariance.""" A, b, x_true = self.rbf_kernel_linear_system for calib_method in [None, 0, 1.0, "adhoc", "weightedmean", "gpkern"]: with self.subTest(): x_est, Ahat, Ainvhat, info = linalg.problinsolve( A=A, b=b, calibration=calib_method) self.assertAlmostEqual( info["trace_sol_cov"], x_est.cov.trace(), msg= "Iteratively computed trace not equal to trace of solution covariance.", )
def test_uncertainty_calibration_error(self): """Test if the available uncertainty calibration procedures affect the error of the returned solution.""" tol = 10**-6 A, b, x_true = self.rbf_kernel_linear_system for calib_method in [None, 0, "adhoc", "weightedmean", "gpkern"]: with self.subTest(): x_est, Ahat, Ainvhat, info = linalg.problinsolve( A=A, b=b, calibration=calib_method) self.assertLessEqual( (x_true - x_est.mean).T @ A @ (x_true - x_est.mean), tol, msg= "Estimated solution not sufficiently close to true solution.", )
def test_posterior_uncertainty_zero_in_explored_space(self): """ Test whether the posterior uncertainty over the matrices A and Ainv is zero in the already explored spaces span(S) and span(Y). """ A, b, x_true = self.rbf_kernel_linear_system n = A.shape[0] for calibrate in [False, 0.0]: # , 10 ** -6, 2.8]: # TODO (probnum#100) expand this test to the prior covariance class # admitting calibration with self.subTest(): # Define callback function to obtain search directions S = [] # search directions Y = [] # observations # pylint: disable=cell-var-from-loop def callback_postparams(xk, Ak, Ainvk, sk, yk, alphak, resid): S.append(sk) Y.append(yk) # Solve linear system u_solver, Ahat, Ainvhat, info = linalg.problinsolve( A=A, b=b, assume_A="sympos", callback=callback_postparams, calibration=calibrate, ) # Create arrays from lists S = np.squeeze(np.array(S)).T Y = np.squeeze(np.array(Y)).T self.assertAllClose( Ahat.cov.A @ S, np.zeros_like(S), atol=1e-6, msg= "Uncertainty over A in explored space span(S) not zero.", ) self.assertAllClose( Ainvhat.cov.A @ Y, np.zeros_like(S), atol=1e-6, msg= "Uncertainty over Ainv in explored space span(Y) not zero.", )