Beispiel #1
0
    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)
Beispiel #2
0
    def test_symmkronecker_transpose(self):
        """Kronecker product transpose property: (A (x) B)^T = A^T (x) B^T."""
        for A, B in self.symmkronecker_matrices:
            with self.subTest():
                W = linops.SymmetricKronecker(A=A, B=B)
                V = linops.SymmetricKronecker(A=A.T, B=B.T)

                self.assertAllClose(W.T.todense(), V.todense())
Beispiel #3
0
    def test_symmkronecker_commutation(self):
        """Symmetric Kronecker products fulfill A (x)_s B = B (x)_s A"""
        for A, B in self.symmkronecker_matrices:
            with self.subTest():
                W = linops.SymmetricKronecker(A=A, B=B)
                V = linops.SymmetricKronecker(A=B, B=A)

                self.assertAllClose(W.todense(), V.todense())
Beispiel #4
0
def get_randvar(rv_name):
    """Return a random variable for a given distribution name."""
    # Distribution Means and Covariances

    mean_0d = np.random.rand()
    mean_1d = np.random.rand(5)
    mean_2d_mat = SPD_MATRIX_5x5
    mean_2d_linop = linops.MatrixMult(SPD_MATRIX_5x5)
    cov_0d = np.random.rand() + 10**-12
    cov_1d = SPD_MATRIX_5x5
    cov_2d_kron = linops.Kronecker(A=SPD_MATRIX_5x5, B=SPD_MATRIX_5x5)
    cov_2d_symkron = linops.SymmetricKronecker(A=SPD_MATRIX_5x5)

    if rv_name == "univar_normal":
        randvar = rvs.Normal(mean=mean_0d, cov=cov_0d)
    elif rv_name == "multivar_normal":
        randvar = rvs.Normal(mean=mean_1d, cov=cov_1d)
    elif rv_name == "matrixvar_normal":
        randvar = rvs.Normal(mean=mean_2d_mat, cov=cov_2d_kron)
    elif rv_name == "symmatrixvar_normal":
        randvar = rvs.Normal(mean=mean_2d_mat, cov=cov_2d_symkron)
    elif rv_name == "operatorvar_normal":
        randvar = rvs.Normal(mean=mean_2d_linop, cov=cov_2d_symkron)

    return randvar
Beispiel #5
0
    def test_matrixprior(self):
        """Solve random linear system with a matrix-based linear solver."""
        np.random.seed(1)
        # Linear system
        n = 10
        A = np.random.rand(n, n)
        A = A.dot(A.T) + n * np.eye(n)  # Symmetrize and make diagonally dominant
        x_true = np.random.normal(size=(n,))
        b = A @ x_true

        # Prior distributions on A
        covA = linops.SymmetricKronecker(A=np.eye(n))
        Ainv0 = rvs.Normal(mean=np.eye(n), cov=covA)

        for matblinsolve in self.matblinsolvers:
            with self.subTest():
                x, Ahat, Ainvhat, info = matblinsolve(A=A, Ainv0=Ainv0, b=b)

                self.assertAllClose(
                    x.mean,
                    x_true,
                    rtol=1e-6,
                    atol=1e-6,
                    msg="Solution for matrixvariate prior does not match true solution.",
                )
Beispiel #6
0
    def setUp(self):
        """Scalars, arrays, linear operators and random variables for tests."""
        # Seed
        np.random.seed(42)

        # Random variable instantiation
        self.scalars = [0, int(1), 0.1, np.nan, np.inf]
        self.arrays = [np.empty(2), np.zeros(4), np.array([]), np.array([1, 2])]

        # Random variable arithmetic
        self.arrays2d = [
            np.empty(2),
            np.zeros(2),
            np.array([np.inf, 1]),
            np.array([1, -2.5]),
        ]
        self.matrices2d = [np.array([[1, 2], [3, 2]]), np.array([[0, 0], [1.0, -4.3]])]
        self.linops2d = [linops.MatrixMult(A=np.array([[1, 2], [4, 5]]))]
        self.randvars2d = [
            rvs.Normal(mean=np.array([1, 2]), cov=np.array([[2, 0], [0, 5]]))
        ]
        self.randvars2x2 = [
            rvs.Normal(
                mean=np.array([[-2, 0.3], [0, 1]]),
                cov=linops.SymmetricKronecker(A=np.eye(2), B=np.ones((2, 2))),
            ),
        ]

        self.scipyrvs = [
            scipy.stats.bernoulli(0.75),
            scipy.stats.norm(4, 2.4),
            scipy.stats.multivariate_normal(np.random.randn(10), np.eye(10)),
            scipy.stats.gamma(0.74),
            scipy.stats.dirichlet(alpha=np.array([0.1, 0.1, 0.2, 0.3])),
        ]
Beispiel #7
0
    def setUp(self):
        """Resources for tests."""
        # Random Seed
        np.random.seed(42)

        # Scalars, arrays and operators
        self.scalars = [0, int(1), 0.1, -4.2, np.nan, np.inf]
        self.arrays = [
            np.random.normal(size=[5, 4]),
            np.array([[3, 4], [1, 5]])
        ]

        def mv(v):
            return np.array([2 * v[0], v[0] + 3 * v[1]])

        self.mv = mv
        self.ops = [
            linops.MatrixMult(np.array([[-1.5, 3], [0, -230]])),
            linops.LinearOperator(shape=(2, 2), matvec=mv),
            linops.Identity(shape=4),
            linops.Kronecker(
                A=linops.MatrixMult(np.array([[2, -3.5], [12, 6.5]])),
                B=linops.Identity(shape=3),
            ),
            linops.SymmetricKronecker(
                A=linops.MatrixMult(np.array([[1, -2], [-2.2, 5]])),
                B=linops.MatrixMult(np.array([[1, -3], [0, -0.5]])),
            ),
        ]
Beispiel #8
0
 def test_symmkronecker_todense_symmetric(self):
     """Dense matrix from symmetric Kronecker product of two symmetric matrices must be symmetric."""
     C = np.array([[5, 1], [1, 10]])
     D = np.array([[-2, 0.1], [0.1, 8]])
     Ws = linops.SymmetricKronecker(A=C, B=C)
     Ws_dense = Ws.todense()
     self.assertArrayEqual(
         Ws_dense,
         Ws_dense.T,
         msg=
         "Symmetric Kronecker product of symmetric matrices is not symmetric.",
     )
Beispiel #9
0
    def _symmetric_kronecker_identical_factors_cov_cholesky(
        self, ) -> linops.SymmetricKronecker:
        assert isinstance(self._cov,
                          linops.SymmetricKronecker) and self._cov._ABequal

        A = self._cov.A.todense()

        return linops.SymmetricKronecker(
            A=scipy.linalg.cholesky(
                A +
                COV_CHOLESKY_DAMPING * np.eye(A.shape[0], dtype=self.dtype),
                lower=True,
            ),
            dtype=self.dtype,
        )