def test_rv_linop_kroneckercov(): """Create a random variable with linear operator mean and Kronecker product covariance.""" def mv(v): return np.array([2 * v[0], 3 * v[1]]) A = linear_operators.LinearOperator(shape=(2, 2), matvec=mv) V = linear_operators.Kronecker(A, A) probability.RandomVariable(distribution=probability.Normal(mean=A, cov=V))
def _covariance_update(self, u, Ws): """Linear operator implementing the symmetric rank 2 covariance update (-= Ws u^T).""" def mv(x): return u @ (Ws.T @ x) def mm(X): return u @ (Ws.T @ X) return linear_operators.LinearOperator(shape=self.A_mean.shape, matvec=mv, matmat=mm)
def _mean_update(self, u, v): """Linear operator implementing the symmetric rank 2 mean update (+= uv' + vu').""" def mv(x): return u @ (v.T @ x) + v @ (u.T @ x) def mm(X): return u @ (v.T @ X) + v @ (u.T @ X) return linear_operators.LinearOperator(shape=self.A_mean.shape, matvec=mv, matmat=mm)
def _create_output_randvars(self, S=None, Y=None, Phi=None, Psi=None): """Return output random variables x, A, Ainv from their means and covariances.""" _A_covfactor = self.A_covfactor _Ainv_covfactor = self.Ainv_covfactor # Set degrees of freedom based on uncertainty calibration in unexplored space if Phi is not None: def _mv(x): def _I_S_fun(x): return x - S @ np.linalg.solve(S.T @ S, S.T @ x) return _I_S_fun(Phi @ _I_S_fun(x)) I_S_Phi_I_S_op = linear_operators.LinearOperator( shape=self.A.shape, matvec=_mv) _A_covfactor = self.A_covfactor + I_S_Phi_I_S_op if Psi is not None: def _mv(x): def _I_Y_fun(x): return x - Y @ np.linalg.solve(Y.T @ Y, Y.T @ x) return _I_Y_fun(Psi @ _I_Y_fun(x)) I_Y_Psi_I_Y_op = linear_operators.LinearOperator( shape=self.A.shape, matvec=_mv) _Ainv_covfactor = self.Ainv_covfactor + I_Y_Psi_I_Y_op # Create output random variables A = probability.RandomVariable( shape=self.A_mean.shape, dtype=float, distribution=probability.Normal( mean=self.A_mean, cov=linear_operators.SymmetricKronecker(A=_A_covfactor))) cov_Ainv = linear_operators.SymmetricKronecker(A=_Ainv_covfactor) Ainv = probability.RandomVariable(shape=self.Ainv_mean.shape, dtype=float, distribution=probability.Normal( mean=self.Ainv_mean, cov=cov_Ainv)) # Induced distribution on x via Ainv # Exp = x = A^-1 b, Cov = 1/2 (W b'Wb + Wbb'W) Wb = _Ainv_covfactor @ self.b bWb = np.squeeze(Wb.T @ self.b) def _mv(x): return 0.5 * (bWb * _Ainv_covfactor @ x + Wb @ (Wb.T @ x)) cov_op = linear_operators.LinearOperator( shape=np.shape(_Ainv_covfactor), dtype=float, matvec=_mv, matmat=_mv) x = probability.RandomVariable(shape=(self.A_mean.shape[0], ), dtype=float, distribution=probability.Normal( mean=self.x.ravel(), cov=cov_op)) return x, A, Ainv
def test_linop_construction(): """Create linear operators via various construction methods.""" # Custom linear operator linear_operators.LinearOperator(shape=(2, 2), matvec=mv)
# Custom linear operator linear_operators.LinearOperator(shape=(2, 2), matvec=mv) # Scipy linear operator # scipy_linop = scipy.sparse.linalg.LinearOperator(shape=(2, 2), matvec=mv) # linear_operators.LinearOperator(scipy_linop) # Linear operator arithmetic np.random.seed(42) scalars = [0, int(1), .1, -4.2, np.nan, np.inf] arrays = [np.random.normal(size=[5, 4]), np.array([[3, 4], [1, 5]])] ops = [linear_operators.MatrixMult(np.array([[-1.5, 3], [0, -230]])), linear_operators.LinearOperator(shape=(2, 2), matvec=mv), linear_operators.Identity(shape=4), linear_operators.Kronecker(A=linear_operators.MatrixMult(np.array([[2, -3.5], [12, 6.5]])), B=linear_operators.Identity(shape=2)), linear_operators.SymmetricKronecker(A=linear_operators.MatrixMult(np.array([[1, -2], [-2.2, 5]])), B=linear_operators.MatrixMult(np.array([[1, -3], [0, -.5]])))] @pytest.mark.parametrize("A, alpha", list(itertools.product(arrays, scalars))) def test_scalar_mult(A, alpha): """Matrix linear operator multiplication with scalars.""" Aop = linear_operators.MatrixMult(A)