def test_hessian_vector_multiply_operator_with_randomized_svd(self): num_dims = 100 rank = 21 num_qoi = 30 concurrency = 10 model = QuadraticMisfitModel(num_dims, rank, num_qoi) # --------------------------------- # # compute with exact misfit Hessian # # --------------------------------- # np.random.seed(2) Amatrix = model.Amatrix operator = MatVecOperator(np.dot(Amatrix.T, Amatrix)) adaptive_svd_opts = { 'tolerance': 1e-8, 'num_extra_samples': 10, 'max_num_iter_error_increase': 20, 'verbosity': 0, 'max_num_samples': 100, 'concurrency': concurrency } svd_opts = {'adaptive_opts': adaptive_svd_opts} U, S, V = randomized_svd(operator, svd_opts) Utrue, Strue, Vtrue = np.linalg.svd(np.dot(Amatrix.T, Amatrix), full_matrices=False) Utrue, Vtrue = adjust_sign_svd(Utrue, Vtrue) J = np.where(Strue > 1e-9)[0] assert J.shape[0] == rank assert np.allclose(Strue[J], S[J]) assert np.allclose(Utrue[:, J], U[:, J]) assert np.allclose(Vtrue[J, :], V[J, :]) # -------------------------------------------------------------- # # compute with finite difference approximation of Hessian action # # -------------------------------------------------------------- # np.random.seed(2) map_point = np.zeros(num_dims) operator = MisfitHessianVecOperator(model, map_point, fd_eps=1e-7) U, S, V = randomized_svd(operator, svd_opts) Utrue, Strue, Vtrue = np.linalg.svd(np.dot(Amatrix.T, Amatrix), full_matrices=False) Utrue, Vtrue = adjust_sign_svd(Utrue, Vtrue) J = np.where(Strue > 1e-9)[0] assert J.shape[0] == rank assert np.allclose(Strue[J], S[J]) assert np.allclose(Utrue[:, J], U[:, J]) assert np.allclose(Vtrue[J, :], V[J, :])
def posterior_covariance_helper(prior, rank, comparison_tol, test_sampling=False, plot=False): """ Test that the Laplace posterior approximation can be obtained using the action of the sqrt prior covariance computed using a PDE solve Parameters ---------- prior : MultivariateGaussian object The model which must be able to compute the action of the sqrt of the prior covariance (and its tranpose) on a set of vectors rank : integer The rank of the linear model used to generate the observations comparision_tol : tolerances for each of the internal comparisons. This allows different accuracy for PDE based operators """ # Define prior sqrt covariance and covariance operators L_op = prior.sqrt_covariance_operator # Extract prior information required for computing exact posterior # mean and covariance num_vars = prior.num_vars() prior_mean = np.zeros((num_vars), float) L = L_op(np.eye(num_vars), False) L_T = L_op(np.eye(num_vars), True) assert_ndarray_allclose(L.T, L_T, rtol=comparison_tol, atol=0, msg='Comparing prior sqrt and transpose') prior_covariance = np.dot(L, L_T) prior_pointwise_variance = prior.pointwise_variance() assert_ndarray_allclose(np.diag(prior_covariance), prior_pointwise_variance, rtol=1e-14, atol=0, msg='Comparing prior pointwise variance') misfit_model, noise_covariance_inv, obs = setup_quadratic_misfit_problem( prior, rank, noise_sigma2=1) # Get analytical mean and covariance prior_hessian = np.linalg.inv(prior_covariance) exact_laplace_mean, exact_laplace_covariance = \ laplace_posterior_approximation_for_linear_models( misfit_model.Amatrix, prior.mean, prior_hessian, noise_covariance_inv, obs) # Define prior conditioned misfit operator sample = np.zeros(num_vars) misfit_hessian_operator = MisfitHessianVecOperator(misfit_model, sample, fd_eps=None) LHL_op = PriorConditionedHessianMatVecOperator(L_op, misfit_hessian_operator) # For testing purposes build entire L*H*L matrix using operator # and compare to result based upon explicit matrix mutiplication LHL_op = LHL_op.apply(np.eye(num_vars), transpose=False) H = misfit_model.hessian(sample) assert np.allclose( H, np.dot(np.dot(misfit_model.Amatrix.T, noise_covariance_inv), misfit_model.Amatrix)) LHL_mat = np.dot(L_T, np.dot(H, L)) assert_ndarray_allclose( LHL_mat, LHL_op, rtol=comparison_tol, msg='Comparing prior matrix and operator based LHL') # Test singular values obtained by randomized svd using operator # are the same as those obtained using singular decomposition Utrue, Strue, Vtrue = np.linalg.svd(LHL_mat) Utrue, Vtrue = adjust_sign_svd(Utrue, Vtrue) standard_svd_opts = {'num_singular_values': rank, 'num_extra_samples': 10} svd_opts = {'single_pass': True, 'standard_opts': standard_svd_opts} L_post_op = get_laplace_covariance_sqrt_operator(L_op, misfit_hessian_operator, svd_opts, weights=None, min_singular_value=0.0) #print np.max((Strue[:rank]-L_post_op.e_r)/Strue[0]) max_error = np.max(Strue[:rank] - L_post_op.e_r) assert max_error / Strue[0] < comparison_tol, max_error / Strue[0] assert_ndarray_allclose(Vtrue.T[:, :rank], L_post_op.V_r, rtol=1e-6, msg='Comparing eigenvectors') L_post_op.V_r = Vtrue.T[:, :rank] # Test posterior sqrt covariance operator transpose is the same as # explicit matrix transpose of matrix obtained by prior sqrt # covariance operator L_post = L_post_op.apply(np.eye(num_vars), transpose=False) L_post_T = L_post_op.apply(np.eye(num_vars), transpose=True) assert_ndarray_allclose(L_post.T, L_post_T, rtol=comparison_tol, msg='Comparing posterior sqrt and transpose') # Test posterior covariance operator produced matrix is the same # as the exact posterior covariance obtained using analytical formula if rank == num_vars: # this test only makes sense if entire set of directions is found # if low rank approx is used then this will ofcourse induce errors post_covariance = np.dot(L_post, L_post_T) assert_ndarray_allclose( exact_laplace_covariance, post_covariance, rtol=comparison_tol, atol=0., msg='Comparing matrix and operator based posterior covariance') # Test pointwise covariance of posterior post_pointwise_variance, prior_pointwise_variance=\ get_pointwise_laplace_variance_using_prior_variance( prior, L_post_op, prior_pointwise_variance) assert_ndarray_allclose(np.diag(exact_laplace_covariance), post_pointwise_variance, rtol=comparison_tol, atol=0., msg='Comparing pointwise variance') if not test_sampling: return num_samples = int(2e5) posterior_samples = sample_from_laplace_posterior(exact_laplace_mean, L_post_op, num_vars, num_samples, weights=None) assert_ndarray_allclose(exact_laplace_covariance, np.cov(posterior_samples), atol=1e-2 * exact_laplace_covariance.max(), rtol=0., msg='Comparing posterior samples covariance') assert_ndarray_allclose(exact_laplace_mean.squeeze(), np.mean(posterior_samples, axis=1), atol=2e-2, rtol=0., msg='Comparing posterior samples mean') if plot: # plot marginals of posterior using orginal ordering from pyapprox.visualization import plot_multiple_2d_gaussian_slices texfilename = 'slices.tex' plot_multiple_2d_gaussian_slices( exact_laplace_mean[:10], np.diag(exact_laplace_covariance)[:10], texfilename, reference_gaussian_data=(0., 1.), show=False) # plot marginals of posterior in rotated coordinates # from most to least important. # The following is not feasiable in practice as we cannot compute # entire covariance matrix in full space. But we have # C_r = V_r*L*V_r*D*V_r.T*L.T*V_r.T # is we compute matrix products from right to left we only have to # compute at most (d x r) matrices. And if only want first 20 say # variances then can apply C_r to vectors e_i i=1,...,20 # then we need at most (dx20 matrices) texfilename = 'rotated-slices.tex' V_r = L_post_op.V_r plot_multiple_2d_gaussian_slices( np.dot(V_r.T, exact_laplace_mean[:10]), np.diag(np.dot(V_r.T, np.dot(exact_laplace_covariance, V_r)))[:10], texfilename, reference_gaussian_data=(0., 1.), show=True)