Exemple #1
0
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 locations,
                 m_true,
                 Theta=None,
                 pen=1e1,
                 order=2,
                 rel_tol=1e-12,
                 max_iter=1000):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - locations:       the points x_i at which we assume to know the
                           true value of the parameter
        - m_true:          the true model
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - pen:             a penalization parameter for the mollifier
        """
        assert delta != 0. or pen != 0, "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        if Theta == None:
            varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        else:
            varfL = dl.inner(Theta * dl.grad(trial), dl.grad(test)) * dl.dx
        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        #mfun = Mollifier(gamma/delta, dl.inv(Theta), order, locations)
        mfun = dl.Expression(code_Mollifier,
                             degree=Vh.ufl_element().degree() + 2)
        mfun.l = gamma / delta
        mfun.o = order
        mfun.theta0 = 1. / Theta.theta0
        mfun.theta1 = 1. / Theta.theta1
        mfun.alpha = Theta.alpha
        for ii in range(locations.shape[0]):
            mfun.addLocation(locations[ii, 0], locations[ii, 1])

        varfmo = mfun * dl.inner(trial, test) * dl.dx
        MO = dl.assemble(pen * varfmo)

        self.A = dl.assemble(gamma * varfL + delta * varfM + pen * varfmo)

        self.Asolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Asolver.set_operator(self.A)
        self.Asolver.parameters["maximum_iterations"] = max_iter
        self.Asolver.parameters["relative_tolerance"] = rel_tol
        self.Asolver.parameters["error_on_nonconvergence"] = True
        self.Asolver.parameters["nonzero_initial_guess"] = False

        old_qr = dl.parameters["form_compiler"]["quadrature_degree"]
        dl.parameters["form_compiler"]["quadrature_degree"] = -1
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}

        if dlversion() == (2017, 1, 0):
            representation_old = dl.parameters["form_compiler"][
                "representation"]
            dl.parameters["form_compiler"]["representation"] = "quadrature"

        if dlversion() <= (1, 6, 0):
            Qh = dl.FunctionSpace(Vh.mesh(), 'Quadrature', qdegree)
        else:
            element = dl.FiniteElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)
        Mqh = dl.assemble(ph * qh * dl.dx(metadata=metadata))
        ones = dl.interpolate(dl.Constant(1.), Qh).vector()
        dMqh = Mqh * ones
        Mqh.zero()
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.set_diagonal(dMqh)
        MixedM = dl.assemble(ph * test * dl.dx(metadata=metadata))
        self.sqrtM = MatMatMult(MixedM, Mqh)

        dl.parameters["form_compiler"]["quadrature_degree"] = old_qr

        if dlversion() == (2017, 1, 0):
            dl.parameters["form_compiler"][
                "representation"] = representation_old

        self.R = _BilaplacianR(self.A, self.Msolver)
        self.Rsolver = _BilaplacianRsolver(self.Asolver, self.M)

        rhs = dl.Vector()
        self.mean = dl.Vector()
        self.init_vector(rhs, 0)
        self.init_vector(self.mean, 0)

        MO.mult(m_true, rhs)
        self.Asolver.solve(self.mean, rhs)
Exemple #2
0
class MollifiedBiLaplacianPrior(_Prior):
    """
    This class implement a Prior model with covariance matrix
    C = ( (\delta + pen \sum_i m(x - x_i) ) I + \gamma \div Theta \grad) ^ {-2},
    
    where
    - Theta is a s.p.d tensor that models anisotropy in the covariance kernel
    - x_i (i=1,...,n) are points were we assume to know exactly the value
    of the parameter (i.e. m(x_i) = m_true( x_i) for i=1,...,n).    
    - m is the mollifier function: m(x - x_i) = exp( - [\frac{\gamma}{\delta}|| x - x_i ||_Theta^{-1}]^order ).
    - pen is a penalization paramter
    
    The magnitude of \delta\gamma governs the variance of the samples, while
    the ratio \frac{\gamma}{\delta} governs the correlation lenght.
    
    The prior mean is computed by solving 
    ( (\delta + \sum_i m(x - x_i) ) I + \gamma \div Theta \grad ) m = \sum_i m(x - x_i) m_true.
    """
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 locations,
                 m_true,
                 Theta=None,
                 pen=1e1,
                 order=2,
                 rel_tol=1e-12,
                 max_iter=1000):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - locations:       the points x_i at which we assume to know the
                           true value of the parameter
        - m_true:          the true model
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - pen:             a penalization parameter for the mollifier
        """
        assert delta != 0. or pen != 0, "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        if Theta == None:
            varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        else:
            varfL = dl.inner(Theta * dl.grad(trial), dl.grad(test)) * dl.dx
        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        #mfun = Mollifier(gamma/delta, dl.inv(Theta), order, locations)
        mfun = dl.Expression(code_Mollifier,
                             degree=Vh.ufl_element().degree() + 2)
        mfun.l = gamma / delta
        mfun.o = order
        mfun.theta0 = 1. / Theta.theta0
        mfun.theta1 = 1. / Theta.theta1
        mfun.alpha = Theta.alpha
        for ii in range(locations.shape[0]):
            mfun.addLocation(locations[ii, 0], locations[ii, 1])

        varfmo = mfun * dl.inner(trial, test) * dl.dx
        MO = dl.assemble(pen * varfmo)

        self.A = dl.assemble(gamma * varfL + delta * varfM + pen * varfmo)

        self.Asolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Asolver.set_operator(self.A)
        self.Asolver.parameters["maximum_iterations"] = max_iter
        self.Asolver.parameters["relative_tolerance"] = rel_tol
        self.Asolver.parameters["error_on_nonconvergence"] = True
        self.Asolver.parameters["nonzero_initial_guess"] = False

        old_qr = dl.parameters["form_compiler"]["quadrature_degree"]
        dl.parameters["form_compiler"]["quadrature_degree"] = -1
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}

        if dlversion() == (2017, 1, 0):
            representation_old = dl.parameters["form_compiler"][
                "representation"]
            dl.parameters["form_compiler"]["representation"] = "quadrature"

        if dlversion() <= (1, 6, 0):
            Qh = dl.FunctionSpace(Vh.mesh(), 'Quadrature', qdegree)
        else:
            element = dl.FiniteElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)
        Mqh = dl.assemble(ph * qh * dl.dx(metadata=metadata))
        ones = dl.interpolate(dl.Constant(1.), Qh).vector()
        dMqh = Mqh * ones
        Mqh.zero()
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.set_diagonal(dMqh)
        MixedM = dl.assemble(ph * test * dl.dx(metadata=metadata))
        self.sqrtM = MatMatMult(MixedM, Mqh)

        dl.parameters["form_compiler"]["quadrature_degree"] = old_qr

        if dlversion() == (2017, 1, 0):
            dl.parameters["form_compiler"][
                "representation"] = representation_old

        self.R = _BilaplacianR(self.A, self.Msolver)
        self.Rsolver = _BilaplacianRsolver(self.Asolver, self.M)

        rhs = dl.Vector()
        self.mean = dl.Vector()
        self.init_vector(rhs, 0)
        self.init_vector(self.mean, 0)

        MO.mult(m_true, rhs)
        self.Asolver.solve(self.mean, rhs)

    def init_vector(self, x, dim):
        """
        Inizialize a vector x to be compatible with the range/domain of R.
        If dim == "noise" inizialize x to be compatible with the size of
        white noise used for sampling.
        """
        if dim == "noise":
            self.sqrtM.init_vector(x, 1)
        else:
            self.A.init_vector(x, dim)

    def sample(self, noise, s, add_mean=True):
        """
        Given a noise ~ N(0, I) compute a sample s from the prior.
        If add_mean=True add the prior mean value to s.
        """
        rhs = self.sqrtM * noise
        self.Asolver.solve(s, rhs)

        if add_mean:
            s.axpy(1., self.mean)
Exemple #3
0
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 Theta=None,
                 mean=None,
                 rel_tol=1e-12,
                 max_iter=1000):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - mean:            the prior mean
        """
        assert delta != 0., "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        if Theta == None:
            varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        else:
            varfL = dl.inner(Theta * dl.grad(trial), dl.grad(test)) * dl.dx

        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        self.A = dl.assemble(gamma * varfL + delta * varfM)
        self.Asolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Asolver.set_operator(self.A)
        self.Asolver.parameters["maximum_iterations"] = max_iter
        self.Asolver.parameters["relative_tolerance"] = rel_tol
        self.Asolver.parameters["error_on_nonconvergence"] = True
        self.Asolver.parameters["nonzero_initial_guess"] = False

        old_qr = dl.parameters["form_compiler"]["quadrature_degree"]
        dl.parameters["form_compiler"]["quadrature_degree"] = -1
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}

        if dlversion() == (2017, 1, 0):
            representation_old = dl.parameters["form_compiler"][
                "representation"]
            dl.parameters["form_compiler"]["representation"] = "quadrature"

        if dlversion() <= (1, 6, 0):
            Qh = dl.FunctionSpace(Vh.mesh(), 'Quadrature', qdegree)
        else:
            #element = dl.FiniteElement("Quadrature", Vh.mesh().ufl_cell(), qdegree, quad_scheme="default")
            element = dl.VectorElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)
        #Mqh = dl.assemble(ph*qh*dl.dx(metadata=metadata))
        Mqh = dl.assemble(dl.inner(ph, qh) * dl.dx(metadata=metadata))
        #ones = dl.interpolate(dl.Constant(1., 1., 1.), Qh).vector()
        ones = dl.Vector()
        Mqh.init_vector(ones, 0)
        ones.set_local(np.ones(ones.array().shape, dtype=ones.array().dtype))
        dMqh = Mqh * ones
        Mqh.zero()
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.set_diagonal(dMqh)
        #MixedM = dl.assemble(ph*test*dl.dx(metadata=metadata))
        MixedM = dl.assemble(dl.inner(ph, test) * dl.dx(metadata=metadata))
        self.sqrtM = MatMatMult(MixedM, Mqh)

        dl.parameters["form_compiler"]["quadrature_degree"] = old_qr

        if dlversion() == (2017, 1, 0):
            dl.parameters["form_compiler"][
                "representation"] = representation_old

        self.R = _BilaplacianR(self.A, self.Msolver)
        self.Rsolver = _BilaplacianRsolver(self.Asolver, self.M)

        self.mean = mean

        if self.mean is None:
            self.mean = dl.Vector()
            self.init_vector(self.mean, 0)
Exemple #4
0
class BiLaplacianPrior(_Prior):
    """
    This class implement a Prior model with covariance matrix
    C = (\delta I + \gamma \div Theta \grad) ^ {-2}.
    
    The magnitude of \delta\gamma governs the variance of the samples, while
    the ratio \frac{\gamma}{\delta} governs the correlation lenght.
    
    Here Theta is a s.p.d tensor that models anisotropy in the covariance kernel.
    """
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 Theta=None,
                 mean=None,
                 rel_tol=1e-12,
                 max_iter=1000):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - mean:            the prior mean
        """
        assert delta != 0., "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        if Theta == None:
            varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        else:
            varfL = dl.inner(Theta * dl.grad(trial), dl.grad(test)) * dl.dx

        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        self.A = dl.assemble(gamma * varfL + delta * varfM)
        self.Asolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Asolver.set_operator(self.A)
        self.Asolver.parameters["maximum_iterations"] = max_iter
        self.Asolver.parameters["relative_tolerance"] = rel_tol
        self.Asolver.parameters["error_on_nonconvergence"] = True
        self.Asolver.parameters["nonzero_initial_guess"] = False

        old_qr = dl.parameters["form_compiler"]["quadrature_degree"]
        dl.parameters["form_compiler"]["quadrature_degree"] = -1
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}

        if dlversion() == (2017, 1, 0):
            representation_old = dl.parameters["form_compiler"][
                "representation"]
            dl.parameters["form_compiler"]["representation"] = "quadrature"

        if dlversion() <= (1, 6, 0):
            Qh = dl.FunctionSpace(Vh.mesh(), 'Quadrature', qdegree)
        else:
            #element = dl.FiniteElement("Quadrature", Vh.mesh().ufl_cell(), qdegree, quad_scheme="default")
            element = dl.VectorElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)
        #Mqh = dl.assemble(ph*qh*dl.dx(metadata=metadata))
        Mqh = dl.assemble(dl.inner(ph, qh) * dl.dx(metadata=metadata))
        #ones = dl.interpolate(dl.Constant(1., 1., 1.), Qh).vector()
        ones = dl.Vector()
        Mqh.init_vector(ones, 0)
        ones.set_local(np.ones(ones.array().shape, dtype=ones.array().dtype))
        dMqh = Mqh * ones
        Mqh.zero()
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.set_diagonal(dMqh)
        #MixedM = dl.assemble(ph*test*dl.dx(metadata=metadata))
        MixedM = dl.assemble(dl.inner(ph, test) * dl.dx(metadata=metadata))
        self.sqrtM = MatMatMult(MixedM, Mqh)

        dl.parameters["form_compiler"]["quadrature_degree"] = old_qr

        if dlversion() == (2017, 1, 0):
            dl.parameters["form_compiler"][
                "representation"] = representation_old

        self.R = _BilaplacianR(self.A, self.Msolver)
        self.Rsolver = _BilaplacianRsolver(self.Asolver, self.M)

        self.mean = mean

        if self.mean is None:
            self.mean = dl.Vector()
            self.init_vector(self.mean, 0)

    def init_vector(self, x, dim):
        """
        Inizialize a vector x to be compatible with the range/domain of R.
        If dim == "noise" inizialize x to be compatible with the size of
        white noise used for sampling.
        """
        if dim == "noise":
            self.sqrtM.init_vector(x, 1)
        else:
            self.A.init_vector(x, dim)

    def sample(self, noise, s, add_mean=True):
        """
        Given a noise ~ N(0, I) compute a sample s from the prior.
        If add_mean=True add the prior mean value to s.
        """
        rhs = self.sqrtM * noise
        self.Asolver.solve(s, rhs)

        if add_mean:
            s.axpy(1., self.mean)
Exemple #5
0
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 mean=None,
                 rel_tol=1e-12,
                 max_iter=100):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - mean:            the prior mean
        """
        assert delta != 0., "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.R = dl.assemble(gamma * varfL + delta * varfM)

        self.Rsolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Rsolver.set_operator(self.R)
        self.Rsolver.parameters["maximum_iterations"] = max_iter
        self.Rsolver.parameters["relative_tolerance"] = rel_tol
        self.Rsolver.parameters["error_on_nonconvergence"] = True
        self.Rsolver.parameters["nonzero_initial_guess"] = False

        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        ndim = Vh.mesh().geometry().dim()
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}
        if dlversion() <= (1, 6, 0):
            Qh = dl.VectorFunctionSpace(Vh.mesh(),
                                        'Quadrature',
                                        qdegree,
                                        dim=(ndim + 1))
        else:
            element = dl.VectorElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       dim=(ndim + 1),
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)

        pph = dl.split(ph)

        Mqh = dl.assemble(dl.inner(ph, qh) * dl.dx(metadata=metadata))
        ones = dl.Vector()
        Mqh.init_vector(ones, 0)
        ones.set_local(np.ones(ones.array().shape, dtype=ones.array().dtype))
        dMqh = Mqh * ones
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.zero()
        Mqh.set_diagonal(dMqh)

        sqrtdelta = math.sqrt(delta)
        sqrtgamma = math.sqrt(gamma)
        varfGG = sqrtdelta * pph[0] * test * dl.dx(metadata=metadata)
        for i in range(ndim):
            varfGG = varfGG + sqrtgamma * pph[i + 1] * test.dx(i) * dl.dx(
                metadata=metadata)

        GG = dl.assemble(varfGG)
        self.sqrtR = MatMatMult(GG, Mqh)

        self.mean = mean

        if self.mean is None:
            self.mean = dl.Vector()
            self.init_vector(self.mean, 0)
Exemple #6
0
class LaplacianPrior(_Prior):
    """
    This class implements a Prior model with covariance matrix
    C = (\delta I + \gamma \Delta) ^ {-1}.
    
    The magnitude of \gamma governs the variance of the samples, while
    the ratio \frac{\gamma}{\delta} governs the correlation lenght.
    
    Note that C is a trace class operator only in 1D while it is not
    a valid prior in 2D and 3D.
    """
    def __init__(self,
                 Vh,
                 gamma,
                 delta,
                 mean=None,
                 rel_tol=1e-12,
                 max_iter=100):
        """
        Construct the Prior model.
        Input:
        - Vh:              the finite element space for the parameter
        - gamma and delta: the coefficient in the PDE
        - Theta:           the s.p.d. tensor for anisotropic diffusion of the pde
        - mean:            the prior mean
        """
        assert delta != 0., "Intrinsic Gaussian Prior are not supported"
        self.Vh = Vh

        trial = dl.TrialFunction(Vh)
        test = dl.TestFunction(Vh)

        varfL = dl.inner(dl.nabla_grad(trial), dl.nabla_grad(test)) * dl.dx
        varfM = dl.inner(trial, test) * dl.dx

        self.M = dl.assemble(varfM)
        self.R = dl.assemble(gamma * varfL + delta * varfM)

        self.Rsolver = dl.PETScKrylovSolver("cg", amg_method())
        self.Rsolver.set_operator(self.R)
        self.Rsolver.parameters["maximum_iterations"] = max_iter
        self.Rsolver.parameters["relative_tolerance"] = rel_tol
        self.Rsolver.parameters["error_on_nonconvergence"] = True
        self.Rsolver.parameters["nonzero_initial_guess"] = False

        self.Msolver = dl.PETScKrylovSolver("cg", "jacobi")
        self.Msolver.set_operator(self.M)
        self.Msolver.parameters["maximum_iterations"] = max_iter
        self.Msolver.parameters["relative_tolerance"] = rel_tol
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False

        ndim = Vh.mesh().geometry().dim()
        qdegree = 2 * Vh._ufl_element.degree()
        metadata = {"quadrature_degree": qdegree}
        if dlversion() <= (1, 6, 0):
            Qh = dl.VectorFunctionSpace(Vh.mesh(),
                                        'Quadrature',
                                        qdegree,
                                        dim=(ndim + 1))
        else:
            element = dl.VectorElement("Quadrature",
                                       Vh.mesh().ufl_cell(),
                                       qdegree,
                                       dim=(ndim + 1),
                                       quad_scheme="default")
            Qh = dl.FunctionSpace(Vh.mesh(), element)

        ph = dl.TrialFunction(Qh)
        qh = dl.TestFunction(Qh)

        pph = dl.split(ph)

        Mqh = dl.assemble(dl.inner(ph, qh) * dl.dx(metadata=metadata))
        ones = dl.Vector()
        Mqh.init_vector(ones, 0)
        ones.set_local(np.ones(ones.array().shape, dtype=ones.array().dtype))
        dMqh = Mqh * ones
        dMqh.set_local(ones.array() / np.sqrt(dMqh.array()))
        Mqh.zero()
        Mqh.set_diagonal(dMqh)

        sqrtdelta = math.sqrt(delta)
        sqrtgamma = math.sqrt(gamma)
        varfGG = sqrtdelta * pph[0] * test * dl.dx(metadata=metadata)
        for i in range(ndim):
            varfGG = varfGG + sqrtgamma * pph[i + 1] * test.dx(i) * dl.dx(
                metadata=metadata)

        GG = dl.assemble(varfGG)
        self.sqrtR = MatMatMult(GG, Mqh)

        self.mean = mean

        if self.mean is None:
            self.mean = dl.Vector()
            self.init_vector(self.mean, 0)

    def init_vector(self, x, dim):
        """
        Inizialize a vector x to be compatible with the range/domain of R.
        If dim == "noise" inizialize x to be compatible with the size of
        white noise used for sampling.
        """
        if dim == "noise":
            self.sqrtR.init_vector(x, 1)
        else:
            self.R.init_vector(x, dim)

    def sample(self, noise, s, add_mean=True):
        """
        Given a noise ~ N(0, I) compute a sample s from the prior.
        If add_mean=True add the prior mean value to s.
        """

        rhs = self.sqrtR * noise
        self.Rsolver.solve(s, rhs)

        if add_mean:
            s.axpy(1., self.mean)