def set_TR(self, radius, B_op): assert self.parameters["zero_initial_guess"] self.TR_radius_2 = radius * radius self.update_x = self.update_x_with_TR self.B_op = B_op self.Bx = Vector() self.B_op.init_vector(self.Bx, 0)
def trace(self, W=None): """ Compute the trace of A. If the weight W is given compute the trace of W^1/2AW^1/2. This is equivalent to tr_W(A) = \sum_i lambda_i, where lambda_i are the generalized eigenvalues of A x = lambda W^-1 x. Note if U is a W-orthogonal matrix then tr_W(A) = \sum_i D(i,i). """ if W is None: diagUtU = np.sum(self.U * self.U, 0) tr = np.sum(self.d * diagUtU) else: WU = np.zeros(self.U.shape, dtype=self.U.dtype) u, wu = Vector(), Vector() W.init_vector(u, 1) W.init_vector(wu, 0) for i in range(self.U.shape[1]): u.set_local(self.U[:, i]) W.mult(u, wu) WU[:, i] = wu.array() diagWUtU = np.sum(WU * self.U, 0) tr = np.sum(self.d * diagWUtU) return tr
def nonzero_values(function): serialized_vector = Vector(MPI.COMM_SELF) function.vector().gather( serialized_vector, array(range(function.function_space().dim()), "intc")) indices = nonzero(serialized_vector.get_local()) return sort(serialized_vector.get_local()[indices])
def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test) * dx() self.bf = inner(form, test) * dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = Solver_cache[(Mass, tuple(bcs), solver_type, preconditioner_type)] elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array())
def get_diagonal(A, d, solve_mode=True): """ Compute the diagonal of the square operator A or its inverse A^{-1} (if solve_mode=True). """ ej, xj = Vector(), Vector() if hasattr(A, "init_vector"): A.init_vector(ej, 1) A.init_vector(xj, 0) else: A.get_operator().init_vector(ej, 1) A.get_operator().init_vector(xj, 0) ncol = ej.size() da = np.zeros(ncol, dtype=ej.array().dtype) for j in range(ncol): ej[j] = 1. if solve_mode: A.solve(xj, ej) else: A.mult(ej, xj) da[j] = xj[j] ej[j] = 0. d.set_local(da)
def estimate_diagonal_inv2(Asolver, k, d): """ An unbiased stochastic estimator for the diagonal of A^-1. d = [ \sum_{j=1}^k vj .* A^{-1} vj ] ./ [ \sum_{j=1}^k vj .* vj ] where - vj are i.i.d. ~ N(0, I) - .* and ./ represent the element-wise multiplication and division of vectors, respectively. REFERENCE: Costas Bekas, Effrosyni Kokiopoulou, and Yousef Saad, An estimator for the diagonal of a matrix, Applied Numerical Mathematics, 57 (2007), pp. 1214-1229. """ x, b = Vector(), Vector() if hasattr(Asolver, "init_vector"): Asolver.init_vector(x, 1) Asolver.init_vector(b, 0) else: Asolver.get_operator().init_vector(x, 1) Asolver.get_operator().init_vector(b, 0) d.zero() for i in range(k): x.zero() Random.normal(b, 1., True) Asolver.solve(x, b) x *= b d.axpy(1. / float(k), x)
def estimate_diagonal_inv2(Asolver, k, d): """ An unbiased stochastic estimator for the diagonal of :math:`A^{-1}`. :math:`d = [ \sum_{j=1}^k v_j .* A^{-1} v_j ] ./ [ \sum_{j=1}^k v_j .* v_j ]` where - :math:`v_j` are i.i.d. :math:`\mathcal{N}(0, I)` - :math:`.*` and :math:`./` represent the element-wise multiplication and division of vectors, respectively. Reference: `Costas Bekas, Effrosyni Kokiopoulou, and Yousef Saad, An estimator for the diagonal of a matrix, Applied Numerical Mathematics, 57 (2007), pp. 1214-1229.` """ x, b = Vector(d.mpi_comm()), Vector(d.mpi_comm()) if hasattr(Asolver, "init_vector"): Asolver.init_vector(x,1) Asolver.init_vector(b,0) else: Asolver.get_operator().init_vector(x,1) Asolver.get_operator().init_vector(b,0) d.zero() for i in range(k): x.zero() parRandom.normal(1., b) Asolver.solve(x,b) x *= b d.axpy(1./float(k), x)
def __init__(self, A, solve_mode=False, accurancy = 1e-1, init_vector=None, random_engine=rademacher_engine, mpi_comm=mpi_comm_world()): """ Constructor: - :code:`A`: an operator - :code:`solve_mode`: if :code:`True` we estimate the trace of :code:`A`:math:`^{-1}`, otherwise of :code:`A`. - code:`accurancy`: we stop when the standard deviation of the estimator is less then :code:`accurancy`*tr(:code:`A`). - :code:`init_vector`: use a custom function to initialize a vector compatible with the range/domain of :code:`A`. - :code:`random_engine`: which type of i.i.d. random variables to use (Rademacher or Gaussian). """ if solve_mode: self.A = Solver2Operator(A) else: self.A = A self.accurancy = accurancy self.random_engine = random_engine self.iter = 0 self.z = Vector(mpi_comm) self.Az = Vector(mpi_comm) if init_vector is None: A.init_vector(self.z, 0) A.init_vector(self.Az, 0) else: init_vector(self.z, 0) init_vector(self.Az, 0)
def __init__(self, A, solve_mode=False, accurancy=1e-1, init_vector=None, random_engine=rademacher_engine): """ Constructor: - A: an operator - solve_mode: if True we estimate the trace of A^{-1}, otherwise of A. - accurancy: we stop when the standard deviation of the estimator is less then accurancy*tr(A). - init_vector: use a custom function to initialize a vector compatible with the range/domain of A - random_engine: which type of i.i.d. random variables to use (Rademacher or Gaussian) """ self.A = A self.accurancy = accurancy self.random_engine = random_engine self.iter = 0 self.z = Vector() self.Az = Vector() if solve_mode: self._apply = self._apply_solve else: self._apply = self._apply_mult if init_vector is None: A.init_vector(self.z, 0) A.init_vector(self.Az, 0) else: init_vector(self.z, 0) init_vector(self.Az, 0)
def estimate_diagonal_inv2(Asolver, k, d): """ An unbiased stochastic estimator for the diagonal of :math:`A^{-1}`. :math:`d = [ \sum_{j=1}^k v_j .* A^{-1} v_j ] ./ [ \sum_{j=1}^k v_j .* v_j ]` where - :math:`v_j` are i.i.d. :math:`\mathcal{N}(0, I)` - :math:`.*` and :math:`./` represent the element-wise multiplication and division of vectors, respectively. Reference: `Costas Bekas, Effrosyni Kokiopoulou, and Yousef Saad, An estimator for the diagonal of a matrix, Applied Numerical Mathematics, 57 (2007), pp. 1214-1229.` """ x, b = Vector(d.mpi_comm()), Vector(d.mpi_comm()) if hasattr(Asolver, "init_vector"): Asolver.init_vector(x, 1) Asolver.init_vector(b, 0) else: Asolver.get_operator().init_vector(x, 1) Asolver.get_operator().init_vector(b, 0) d.zero() for i in range(k): x.zero() parRandom.normal(1., b) Asolver.solve(x, b) x *= b d.axpy(1. / float(k), x)
def trace2(self, W=None): """ Compute the trace of A*A (Note this is the square of Frob norm, since A is symmetic). If the weight W is provided, it will compute the trace of (AW)^2. This is equivalent to tr_W(A) = \sum_i lambda_i^2, where lambda_i are the generalized eigenvalues of A x = lambda W^-1 x. Note if U is a W-orthogonal matrix then tr_W(A) = \sum_i D(i,i)^2. """ if W is None: UtU = np.dot(self.U.T, self.U) dUtU = self.d[:, None] * UtU #diag(d)*UtU. tr2 = np.sum(dUtU * dUtU) else: WU = np.zeros(self.U.shape, dtype=self.U.dtype) u, wu = Vector(), Vector() W.init_vector(u, 1) W.init_vector(wu, 0) for i in range(self.U.shape[1]): u.set_local(self.U[:, i]) W.mult(u, wu) WU[:, i] = wu.array() UtWU = np.dot(self.U.T, WU) dUtWU = self.d[:, None] * UtWU #diag(d)*UtU. tr2 = np.power(np.linalg.norm(dUtWU), 2) return tr2
def __init__(self, prior, d, U): self.prior = prior self.LowRankH = LowRankOperator(d, U) dsolve = d / (np.ones(d.shape, dtype=d.dtype) + d) self.LowRankHinv = LowRankOperator(dsolve, U) self.help = Vector() self.init_vector(self.help, 0) self.help1 = Vector() self.init_vector(self.help1, 0)
def __init__(self, S, mpi_comm=mpi_comm_world(), init_vector=None): self.S = S self.tmp = Vector(mpi_comm) self.my_init_vector = init_vector if self.my_init_vector is None: if hasattr(self.S, "init_vector"): self.my_init_vector = self.S.init_vector elif hasattr(self.S, "operator"): self.my_init_vector = self.S.operator().init_vector elif hasattr(self.S, "get_operator"): self.my_init_vector = self.S.get_operator().init_vector
def build_pressure_null_space(self): # Prepare function used to correct pressure values null_fcn = Function(self.subspace("p")) null_fcn.vector()[:] = 1.0 # Create vector that spans the null space and normalize null_vec = Vector(null_fcn.vector()) null_vec *= 1.0 / null_vec.norm("l2") # FIXME: Check what are relevant norms for different Krylov methods # Create null space basis object null_space = VectorSpaceBasis([null_vec]) return (null_space, null_fcn)
def AorthogonalityCheck(A, U, d): """ Test the frobenious norm of D^{-1}(U^TAU) - I_k """ V = np.zeros(U.shape) AV = np.zeros(U.shape) Av = Vector() v = Vector() A.init_vector(Av,0) A.init_vector(v,1) nvec = U.shape[1] for i in range(0,nvec): v.set_local(U[:,i]) v *= 1./math.sqrt(d[i]) A.mult(v,Av) AV[:,i] = Av.array() V[:,i] = v.array() VtAV = np.dot(V.T, AV) err = VtAV - np.eye(nvec, dtype=VtAV.dtype) # plt.imshow(np.abs(err)) # plt.colorbar() # plt.show() print "i, ||Vt(i,:)AV(:,i) - I_i||_F, V[:,i] = 1/sqrt(lambda_i) U[:,i]" for i in range(1,nvec+1): print i, np.linalg.norm(err[0:i,0:i], 'fro')
def doublePass(A,Omega,k): """ The double pass algorithm for the HEP as presented in [1]. Inputs: - A: the operator for which we need to estimate the dominant eigenpairs. - Omega: a random gassian matrix with m >= k columns. - k: the number of eigenpairs to extract. Outputs: - d: the estimate of the k dominant eigenvalues of A - U: the estimate of the k dominant eigenvectors of A. U^T U = I_k """ w = Vector() y = Vector() A.init_vector(w,1) A.init_vector(y,0) nvec = Omega.shape[1] assert(nvec >= k ) Y = np.zeros(Omega.shape) for ivect in range(0,nvec): w.set_local(Omega[:,ivect]) A.mult(w,y) Y[:,ivect] = y.array() Q,_ = np.linalg.qr(Y) AQ = np.zeros(Omega.shape) for ivect in range(0,nvec): w.set_local(Q[:,ivect]) A.mult(w,y) AQ[:,ivect] = y.array() T = np.dot(Q.T, AQ) d, V = np.linalg.eigh(T) sort_perm = d.argsort() sort_perm = sort_perm[::-1] d = d[sort_perm[0:k]] V = V[:, sort_perm[0:k]] U = np.dot(Q,V) return d, U
def __init__(self, prior, d, U): self.prior = prior ones = np.ones(d.shape, dtype=d.dtype) self.d = ones - np.power(ones + d, -.5) self.lrsqrt = LowRankOperator(self.d, U) self.help = Vector() self.init_vector(self.help, 0)
class Solver2Operator: def __init__(self, S, mpi_comm=mpi_comm_world(), init_vector=None): self.S = S self.tmp = Vector(mpi_comm) self.my_init_vector = init_vector if self.my_init_vector is None: if hasattr(self.S, "init_vector"): self.my_init_vector = self.S.init_vector elif hasattr(self.S, "operator"): self.my_init_vector = self.S.operator().init_vector elif hasattr(self.S, "get_operator"): self.my_init_vector = self.S.get_operator().init_vector def init_vector(self, x, dim): if self.my_init_vector: self.my_init_vector(x, dim) else: raise NotImplementedError("Solver2Operator.init_vector") def mult(self, x, y): self.S.solve(y, x) def inner(self, x, y): self.S.solve(self.tmp, y) return self.tmp.inner(x)
def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test) * dx() self.bf = inner(form, test) * dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = Solver_cache[(Mass, tuple( bcs), solver_type, preconditioner_type)] elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array())
class LowRankHessian: """ Operator that represents the action of the low rank approximation of the Hessian and of its inverse. """ def __init__(self, prior, d, U): self.prior = prior self.LowRankH = LowRankOperator(d, U) dsolve = d / (np.ones(d.shape, dtype=d.dtype) + d) self.LowRankHinv = LowRankOperator(dsolve, U) self.help = Vector(U[0].mpi_comm()) self.init_vector(self.help, 0) self.help1 = Vector(U[0].mpi_comm()) self.init_vector(self.help1, 0) def init_vector(self,x, dim): self.prior.init_vector(x,dim) def inner(self,x,y): Hx = Vector(self.help.mpi_comm()) self.init_vector(Hx, 0) self.mult(x, Hx) return Hx.inner(y) def mult(self, x, y): self.prior.R.mult(x,y) self.LowRankH.mult(y, self.help) self.prior.R.mult(self.help,self.help1) y.axpy(1, self.help1) def solve(self, sol, rhs): self.prior.Rsolver.solve(sol, rhs) self.LowRankHinv.mult(rhs, self.help) sol.axpy(-1, self.help)
def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test)*dx() self.bf = inner(form, test)*dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = KrylovSolver(solver_type, preconditioner_type) self.sol.parameters["preconditioner"]["structure"] = "same" self.sol.parameters["error_on_nonconvergence"] = False self.sol.parameters["monitor_convergence"] = False self.sol.parameters["report"] = False elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array())
class LowRankHessian: """ Operator that represents the action of the low rank approximation of the Hessian and of its inverse. """ def __init__(self, prior, d, U): self.prior = prior self.LowRankH = LowRankOperator(d, U) dsolve = d / (np.ones(d.shape, dtype=d.dtype) + d) self.LowRankHinv = LowRankOperator(dsolve, U) self.help = Vector(U[0].mpi_comm()) self.init_vector(self.help, 0) self.help1 = Vector(U[0].mpi_comm()) self.init_vector(self.help1, 0) def init_vector(self, x, dim): self.prior.init_vector(x, dim) def inner(self, x, y): Hx = Vector(self.help.mpi_comm()) self.init_vector(Hx, 0) self.mult(x, Hx) return Hx.inner(y) def mult(self, x, y): self.prior.R.mult(x, y) self.LowRankH.mult(y, self.help) self.prior.R.mult(self.help, self.help1) y.axpy(1, self.help1) def solve(self, sol, rhs): self.prior.Rsolver.solve(sol, rhs) self.LowRankHinv.mult(rhs, self.help) sol.axpy(-1, self.help)
def les_setup(u_, mesh, KineticEnergySGS, assemble_matrix, CG1Function, nut_krylov_solver, bcs, **NS_namespace): """ Set up for solving the Kinetic Energy SGS-model. """ DG = FunctionSpace(mesh, "DG", 0) CG1 = FunctionSpace(mesh, "CG", 1) dim = mesh.geometry().dim() delta = Function(DG) delta.vector().zero() delta.vector().axpy(1.0, assemble(TestFunction(DG) * dx)) delta.vector().set_local(delta.vector().array()**(1. / dim)) delta.vector().apply('insert') Ck = KineticEnergySGS["Ck"] ksgs = interpolate(Constant(1E-7), CG1) bc_ksgs = DirichletBC(CG1, 0, "on_boundary") A_mass = assemble_matrix(TrialFunction(CG1) * TestFunction(CG1) * dx) nut_form = Ck * delta * sqrt(ksgs) bcs_nut = derived_bcs(CG1, bcs['u0'], u_) nut_ = CG1Function(nut_form, mesh, method=nut_krylov_solver, bcs=bcs_nut, bounded=True, name="nut") At = Matrix() bt = Vector(nut_.vector()) ksgs_sol = KrylovSolver("bicgstab", "additive_schwarz") #ksgs_sol.parameters["preconditioner"]["structure"] = "same_nonzero_pattern" ksgs_sol.parameters["error_on_nonconvergence"] = False ksgs_sol.parameters["monitor_convergence"] = False ksgs_sol.parameters["report"] = False del NS_namespace return locals()
class Solver2Operator: def __init__(self,S,mpi_comm=mpi_comm_world(), init_vector = None): self.S = S self.tmp = Vector(mpi_comm) self.my_init_vector = init_vector if self.my_init_vector is None: if hasattr(self.S, "init_vector"): self.my_init_vector = self.S.init_vector elif hasattr(self.S, "operator"): self.my_init_vector = self.S.operator().init_vector elif hasattr(self.S, "get_operator"): self.my_init_vector = self.S.get_operator().init_vector def init_vector(self, x, dim): if self.my_init_vector: self.my_init_vector(x,dim) else: raise NotImplementedError("Solver2Operator.init_vector") def mult(self,x,y): self.S.solve(y,x) def inner(self, x, y): self.S.solve(self.tmp,y) return self.tmp.inner(x)
def build_pressure_null_space(self): # Prepare function used to correct pressure values W_ns = self.function_spaces()[1] # NOTE: The following works only for nodal elements # null_fcn = Function(W_ns) # W_ns.sub(1).dofmap().set(null_fcn.vector(), 1.0) null_fcn = self._get_constant_pressure(W_ns) # Create vector that spans the null space and normalize null_vec = Vector(null_fcn.vector()) null_vec *= 1.0 / null_vec.norm("l2") # FIXME: Check what are relevant norms for different Krylov methods # Create null space basis object null_space = VectorSpaceBasis([null_vec]) return (null_space, null_fcn)
def __init__(self, prior, d, U): self.prior = prior self.LowRankH = LowRankOperator(d, U) dsolve = d / (np.ones(d.shape, dtype=d.dtype) + d) self.LowRankHinv = LowRankOperator(dsolve, U) self.help = Vector(U[0].mpi_comm()) self.init_vector(self.help, 0) self.help1 = Vector(U[0].mpi_comm()) self.init_vector(self.help1, 0)
def apply_sqrtM(self, vect): """Compute M^{1/2}.vect from Vector() vect""" sqrtMv = Function(self.Vm) for ii in range(self.Vm.dim()): r, c, rx, cx = self.eigsolM.get_eigenpair(ii) RX = Vector(rx) sqrtMv.vector().axpy( np.sqrt(r) * np.dot(rx.array(), vect.array()), RX) return sqrtMv.vector()
def trace(A): """ Compute the trace of a sparse matrix A. """ v = Vector() A.init_vector(v) mpi_comm = v.mpi_comm() nprocs = MPI.size(mpi_comm) if nprocs > 1: raise Exception("trace is only serial") n = A.size(0) tr = 0. for i in range(0, n): [j, val] = A.getrow(i) tr += val[j == i] return tr
def __init__(self): self.parameters = {} self.parameters["rel_tolerance"] = 1e-9 self.parameters["abs_tolerance"] = 1e-12 self.parameters["max_iter"] = 1000 self.parameters["zero_initial_guess"] = True self.parameters["print_level"] = 0 self.A = None self.B = None self.converged = False self.iter = 0 self.reasonid = 0 self.final_norm = 0 self.r = Vector() self.z = Vector() self.d = Vector() if dolfin.__version__[2] == '5': self.vrs150 = True else: self.vrs150 = False
def __init__(self, parameters=CGSolverSteihaug_ParameterList(),comm = mpi_comm_world()): self.parameters = parameters self.A = None self.B_solver = None self.B_op = None self.converged = False self.iter = 0 self.reasonid = 0 self.final_norm = 0 self.TR_radius_2 = None self.update_x = self.update_x_without_TR self.r = Vector(comm) self.z = Vector(comm) self.Ad = Vector(comm) self.d = Vector(comm) self.Bx = Vector(comm)
def __init__(self,S,mpi_comm=mpi_comm_world(), init_vector = None): self.S = S self.tmp = Vector(mpi_comm) self.my_init_vector = init_vector if self.my_init_vector is None: if hasattr(self.S, "init_vector"): self.my_init_vector = self.S.init_vector elif hasattr(self.S, "operator"): self.my_init_vector = self.S.operator().init_vector elif hasattr(self.S, "get_operator"): self.my_init_vector = self.S.get_operator().init_vector
def estimate_diagonal_inv2(Asolver, k, d): """ An unbiased stochastic estimator for the diagonal of A^-1. d = [ \sum_{j=1}^k vj .* A^{-1} vj ] ./ [ \sum_{j=1}^k vj .* vj ] where - vj are i.i.d. ~ N(0, I) - .* and ./ represent the element-wise multiplication and division of vectors, respectively. REFERENCE: Costas Bekas, Effrosyni Kokiopoulou, and Yousef Saad, An estimator for the diagonal of a matrix, Applied Numerical Mathematics, 57 (2007), pp. 1214-1229. """ x, b = Vector(), Vector() if hasattr(Asolver, "init_vector"): Asolver.init_vector(x, 1) Asolver.init_vector(b, 0) else: Asolver.get_operator().init_vector(x, 1) Asolver.get_operator().init_vector(b, 0) num = np.zeros(b.array().shape, dtype=b.array().dtype) den = np.zeros(num.shape, dtype=num.dtype) for i in range(k): x.zero() b.set_local(np.random.randn(num.shape[0])) Asolver.solve(x, b) num = num + (x.array() * b.array()) den = den + (b.array() * b.array()) d.set_local(num / den)
def set_operator(self, A): """ Set the operator :math:`A`. """ self.r = Vector() self.z = Vector() self.Ad = Vector() self.d = Vector() self.A = A self.A.init_vector(self.r, 0) self.A.init_vector(self.z, 0) self.A.init_vector(self.d, 0) self.A.init_vector(self.Ad, 0)
def update_x_with_TR(self,x,alpha,d): x_bk = x.copy() x.axpy(alpha,d) self.Bx.zero() self.B_op.mult(x, self.Bx) x_Bnorm2 = self.Bx.inner(x) if x_Bnorm2 < self.TR_radius_2: return False else: # Move point to boundary of trust region self.Bx.zero() self.B_op.mult(x_bk, self.Bx) x_Bnorm2 = self.Bx.inner(x_bk) Bd = Vector() self.B_op.init_vector(Bd,0) Bd.zero() self.B_op.mult(self.d,Bd) d_Bnorm2 = Bd.inner(d) d_Bx = Bd.inner(x_bk) a_tau = alpha*alpha*d_Bnorm2 b_tau_half = alpha* d_Bx c_tau = x_Bnorm2- self.TR_radius_2 # Solve quadratic for tau tau = (-b_tau_half + math.sqrt(b_tau_half*b_tau_half - a_tau*c_tau))/a_tau x.zero() x.axpy(1,x_bk) x.axpy(tau*alpha, d) return True
def __init__(self): """ Construct the solver with default parameters :code:`tolerance = 1e-4` :code:`print_level = 0` :code:`verbose = 0` """ self.parameters = {} self.parameters["tolerance"] = 1e-4 self.parameters["print_level"] = 0 self.parameters["verbose"] = 0 self.A = None self.converged = False self.iter = 0 self.b = Vector() self.r = Vector() self.p = Vector() self.Ap = Vector()
def pointwise_variance(self, method="Exact", path_len=8): """ Compute/Estimate the pointwise variance of the posterior, prior distribution and the pointwise variance reduction informed by the data. See _Prior.pointwise_variance for more details. """ pr_pointwise_variance = self.prior.pointwise_variance(method, path_len) correction_pointwise_variance = Vector() self.init_vector(correction_pointwise_variance, 0) self.Hlr.LowRankHinv.get_diagonal(correction_pointwise_variance) post_pointwise_variance = pr_pointwise_variance - correction_pointwise_variance return post_pointwise_variance, pr_pointwise_variance, correction_pointwise_variance
def pointwise_variance(self, **kwargs): """ Compute/estimate the pointwise variance of the posterior, prior distribution and the pointwise variance reduction informed by the data. See :code:`_Prior.pointwise_variance` for more details. """ pr_pointwise_variance = self.prior.pointwise_variance(**kwargs) correction_pointwise_variance = Vector(self.prior.R.mpi_comm()) self.init_vector(correction_pointwise_variance, 0) self.Hlr.LowRankHinv.get_diagonal(correction_pointwise_variance) post_pointwise_variance = pr_pointwise_variance - correction_pointwise_variance return post_pointwise_variance, pr_pointwise_variance, correction_pointwise_variance
def __init__(self, times, tol=1e-10): """ Constructor: - times: time frame at which snapshots are stored - tol : tolerance to identify the frame of the snapshot. """ self.nsteps = len(times) self.data = [] for i in range(self.nsteps): self.data.append(Vector()) self.times = times self.tol = tol
def __init__(self, times, tol=1e-10, mpi_comm = mpi_comm_world()): """ Constructor: - :code:`times`: time frame at which snapshots are stored. - :code:`tol` : tolerance to identify the frame of the snapshot. """ self.nsteps = len(times) self.data = []; for i in range(self.nsteps): self.data.append( Vector(mpi_comm) ) self.times = times self.tol = tol
class Operator2Solver: def __init__(self,op, mpi_comm=mpi_comm_world()): self.op = op self.tmp = Vector(mpi_comm) def init_vector(self, x, dim): if hasattr(self.op, "init_vector"): self.op.init_vector(x,dim) else: raise def solve(self,y,x): self.op.mult(x,y) def inner(self, x, y): self.op.mult(y,self.tmp) return self.tmp.inner(x)
def get_diagonal(A, d): """ Compute the diagonal of the square operator :math:`A`. Use :code:`Solver2Operator` if :math:`A^{-1}` is needed. """ ej, xj = Vector(d.mpi_comm()), Vector(d.mpi_comm()) A.init_vector(ej,1) A.init_vector(xj,0) g_size = ej.size() d.zero() for gid in range(g_size): owns_gid = ej.owns_index(gid) if owns_gid: SetToOwnedGid(ej, gid, 1.) ej.apply("insert") A.mult(ej,xj) if owns_gid: val = GetFromOwnedGid(xj, gid) SetToOwnedGid(d, gid, val) SetToOwnedGid(ej, gid, 0.) ej.apply("insert") d.apply("insert")
def solve(self, F, u, grad = None, H = None): if grad is None: print "Using Automatic Differentiation to compute the gradient" grad = derivative(F,u) if H is None: print "Using Automatic Differentiation to compute the Hessian" H = derivative(grad, u) rtol = self.parameters["rel_tolerance"] atol = self.parameters["abs_tolerance"] gdu_tol = self.parameters["gdu_tolerance"] max_iter = self.parameters["max_iter"] c_armijo = self.parameters["c_armijo"] max_backtrack = self.parameters["max_backtracking_iter"] prt_level = self.parameters["print_level"] cg_coarsest_tol = self.parameters["cg_coarse_tolerance"] Fn = assemble(F) gn = assemble(grad) g0_norm = gn.norm("l2") gn_norm = g0_norm tol = max(g0_norm*rtol, atol) du = Vector() self.converged = False self.reason = 0 if prt_level > 0: print "{0:3} {1:15} {2:15} {3:15} {4:15} {5:15} {6:5}".format( "It", "Energy", "||g||", "(g,du)", "alpha", "tol_cg", "cg_it") for self.it in range(max_iter): Hn = assemble(H) Hn.init_vector(du,1) solver = PETScKrylovSolver("cg", "petsc_amg") solver.set_operator(Hn) solver.parameters["nonzero_initial_guess"] = False cg_tol = min(cg_coarsest_tol, math.sqrt( gn_norm/g0_norm) ) solver.parameters["relative_tolerance"] = cg_tol lin_it = solver.solve(du,gn) self.total_cg_iter += lin_it du_gn = -du.inner(gn) if(-du_gn < gdu_tol): self.converged=True self.reason = 3 break u_backtrack = u.copy(deepcopy=True) alpha = 1. bk_converged = False #Backtrack for j in range(max_backtrack): u.assign(u_backtrack) u.vector().axpy(-alpha, du) Fnext = assemble(F) if Fnext < Fn + alpha*c_armijo*du_gn: Fn = Fnext bk_converged = True break alpha = alpha/2. if not bk_converged: self.reason = 2 break gn = assemble(grad) gn_norm = gn.norm("l2") if prt_level > 0: print "{0:3d} {1:15f} {2:15f} {3:15f} {4:15f} {5:15f} {6:5d}".format( self.it, Fn, gn_norm, du_gn, alpha, cg_tol, lin_it) if gn_norm < tol: self.converged = True self.reason = 1 break self.final_grad_norm = gn_norm if prt_level > -1: print self.termination_reasons[self.reason] if self.converged: print "Inexact Newton CG converged in ", self.it, \ "nonlinear iterations and ", self.total_cg_iter, "linear iterations." else: print "Inexact Newton CG did NOT converge after ", self.it, \ "nonlinear iterations and ", self.total_cg_iter, "linear iterations." print "Final norm of the gradient", self.final_grad_norm print "Value of the cost functional", Fn
def __init__(self,op, mpi_comm=mpi_comm_world()): self.op = op self.tmp = Vector(mpi_comm)
def to_dense(A, mpi_comm = mpi_comm_world() ): """ Convert a sparse matrix A to dense. For debugging only. """ v = Vector(mpi_comm) A.init_vector(v) nprocs = MPI.size(mpi_comm) if nprocs > 1: raise Exception("to_dense is only serial") if hasattr(A, "getrow"): n = A.size(0) m = A.size(1) B = np.zeros( (n,m), dtype=np.float64) for i in range(0,n): [j, val] = A.getrow(i) B[i,j] = val return B else: x = Vector(mpi_comm) Ax = Vector(mpi_comm) A.init_vector(x,1) A.init_vector(Ax,0) n = Ax.get_local().shape[0] m = x.get_local().shape[0] B = np.zeros( (n,m), dtype=np.float64) for i in range(0,m): i_ind = np.array([i], dtype=np.intc) x.set_local(np.ones(i_ind.shape), i_ind) x.apply("sum_values") A.mult(x,Ax) B[:,i] = Ax.get_local() x.set_local(np.zeros(i_ind.shape), i_ind) x.apply("sum_values") return B
class OasisFunction(Function): """Function with more or less efficient projection methods of associated linear form. The matvec option is provided for letting the right hand side be computed through a fast matrix vector product. Both the matrix and the Coefficient of the required vector must be provided. method = "default" Solve projection with regular linear algebra using solver_type and preconditioner_type method = "lumping" Solve through lumping of mass matrix """ def __init__(self, form, Space, bcs=[], name="x", matvec=[None, None], method="default", solver_type="cg", preconditioner_type="default"): Function.__init__(self, Space, name=name) self.form = form self.method = method self.bcs = bcs self.matvec = matvec self.trial = trial = TrialFunction(Space) self.test = test = TestFunction(Space) Mass = inner(trial, test) * dx() self.bf = inner(form, test) * dx() self.rhs = Vector(self.vector()) if method.lower() == "default": self.A = A_cache[(Mass, tuple(bcs))] self.sol = Solver_cache[(Mass, tuple( bcs), solver_type, preconditioner_type)] elif method.lower() == "lumping": assert Space.ufl_element().degree() < 2 self.A = A_cache[(Mass, tuple(bcs))] ones = Function(Space) ones.vector()[:] = 1. self.ML = self.A * ones.vector() self.ML.set_local(1. / self.ML.array()) def assemble_rhs(self): """ Assemble right hand side (form*test*dx) in projection """ if not self.matvec[0] is None: mat, func = self.matvec self.rhs.zero() self.rhs.axpy(1.0, mat * func.vector()) else: assemble(self.bf, tensor=self.rhs) def __call__(self, assemb_rhs=True): """ Compute the projection """ timer = Timer("Projecting {}".format(self.name())) if assemb_rhs: self.assemble_rhs() for bc in self.bcs: bc.apply(self.rhs) if self.method.lower() == "default": self.sol.solve(self.A, self.vector(), self.rhs) else: self.vector().zero() self.vector().axpy(1.0, self.rhs * self.ML)
def inner(self,x,y): Hx = Vector(self.help.mpi_comm()) self.init_vector(Hx, 0) self.mult(x, Hx) return Hx.inner(y)
class CGSampler: """ This class implements the CG sampler algorithm to generate samples from :math:`\mathcal{N}(0, A^{-1})`. Reference: `Albert Parker and Colin Fox Sampling Gaussian Distributions in Krylov Spaces with Conjugate Gradient SIAM J SCI COMPUT, Vol 34, No. 3 pp. B312-B334` """ def __init__(self): """ Construct the solver with default parameters :code:`tolerance = 1e-4` :code:`print_level = 0` :code:`verbose = 0` """ self.parameters = {} self.parameters["tolerance"] = 1e-4 self.parameters["print_level"] = 0 self.parameters["verbose"] = 0 self.A = None self.converged = False self.iter = 0 self.b = Vector() self.r = Vector() self.p = Vector() self.Ap = Vector() def set_operator(self, A): """ Set the operator :code:`A`, such that :math:`x \sim \mathcal{N}(0, A^{-1})`. .. note:: :code:`A` is any object that provides the methods :code:`init_vector()` and :code:`mult()` """ self.A = A self.A.init_vector(self.r,0) self.A.init_vector(self.p,0) self.A.init_vector(self.Ap,0) self.A.init_vector(self.b,0) parRandom.normal(1., self.b) def sample(self, noise, s): """ Generate a sample :math:`s ~ N(0, A^{-1})`. :code:`noise` is a :code:`numpy.array` of i.i.d. normal variables used as input. For a fixed realization of noise the algorithm is fully deterministic. The size of noise determine the maximum number of CG iterations. """ s.zero() self.iter = 0 self.converged = False # r0 = b self.r.zero() self.r.axpy(1., self.b) #p0 = r0 self.p.zero() self.p.axpy(1., self.r) self.A.mult(self.p, self.Ap) d = self.p.inner(self.Ap) tol2 = self.parameters["tolerance"]*self.parameters["tolerance"] rnorm2_old = self.r.inner(self.r) if self.parameters["verbose"] > 0: print("initial residual = {0:g}".format( math.sqrt(rnorm2_old) )) while (not self.converged) and (self.iter < noise.shape[0]): gamma = rnorm2_old/d s.axpy(noise[self.iter]/math.sqrt(d), self.p) self.r.axpy(-gamma, self.Ap) rnorm2 = self.r.inner(self.r) beta = rnorm2/rnorm2_old # p_new = r + beta p self.p *= beta self.p.axpy(1., self.r) self.A.mult(self.p, self.Ap) d = self.p.inner(self.Ap) rnorm2_old = rnorm2 if rnorm2 < tol2: self.converged = True else: rnorm2_old = rnorm2 self.iter = self.iter+1 if self.parameters["verbose"] > 0: print("Final residual {0} after {1} iterations".format( math.sqrt(rnorm2_old), self.iter))
class CGSolverSteihaug: """ Solve the linear system A x = b using preconditioned conjugate gradient ( B preconditioner) and the Steihaug stopping criterion: - reason of termination 0: we reached the maximum number of iterations (no convergence) - reason of termination 1: we reduced the residual up to the given tolerance (convergence) - reason of termination 2: we reached a negative direction (premature termination due to not spd matrix) The operator A is set using the method set_operator(A). A must provide the following two methods: - A.mult(x,y): y = A*x - A.init_vector(x, dim): initialize the vector x so that it is compatible with the range (dim = 0) or the domain (dim = 1) of A. The preconditioner B is set using the method set_preconditioner(B). B must provide the following method: - B.solve(z,r): z is the action of the preconditioner B on the vector r To solve the linear system A*x = b call solve(x,b). Here x and b are assumed to be FEniCS::Vector objects Maximum number of iterations, tolerances, verbosity level etc can be set using the parameters attributes. """ reason = ["Maximum Number of Iterations Reached", "Relative/Absolute residual less than tol", "Reached a negative direction" ] def __init__(self): self.parameters = {} self.parameters["rel_tolerance"] = 1e-9 self.parameters["abs_tolerance"] = 1e-12 self.parameters["max_iter"] = 1000 self.parameters["zero_initial_guess"] = True self.parameters["print_level"] = 0 self.A = None self.B = None self.converged = False self.iter = 0 self.reasonid = 0 self.final_norm = 0 self.r = Vector() self.z = Vector() self.d = Vector() if dolfin.__version__[2] == '5': self.vrs150 = True else: self.vrs150 = False def set_operator(self, A): self.A = A if self.vrs150: self.A.init_vector(self.r,0) self.A.init_vector(self.z,0) self.A.init_vector(self.d,0) else: self.r = self.A.init_vector130() self.z = self.A.init_vector130() self.d = self.A.init_vector130() def set_preconditioner(self, B): self.B = B def solve(self,x,b): self.iter = 0 self.converged = False self.reasonid = 0 betanom = 0.0 alpha = 0.0 beta = 0.0 if self.parameters["zero_initial_guess"]: self.r.zero() self.r.axpy(1.0, b) x.zero() else: self.A.mult(x,self.r) self.r *= -1.0 self.r.axpy(1.0, b) self.z.zero() self.B.solve(self.z,self.r) #z = B^-1 r self.d.zero() self.d.axpy(1.,self.z); #d = z nom0 = self.d.inner(self.r) nom = nom0 if self.parameters["print_level"] == 1: print " Iterartion : ", 0, " (B r, r) = ", nom rtol2 = nom * self.parameters["rel_tolerance"] * self.parameters["rel_tolerance"] atol2 = self.parameters["abs_tolerance"] * self.parameters["abs_tolerance"] r0 = max(rtol2, atol2) if nom <= r0: self.converged = True self.reasonid = 1 self.final_norm = math.sqrt(nom) if(self.parameters["print_level"] >= 0): print self.reason[self.reasonid] print "Converged in ", self.iter, " iterations with final norm ", self.final_norm return self.A.mult(self.d, self.z) #z = A d den = self.z.inner(self.d) if den <= 0.0: self.converged = True self.reasonid = 2 self.final_norm = math.sqrt(nom) if(self.parameters["print_level"] >= 0): print self.reason[self.reasonid] print "Converged in ", self.iter, " iterations with final norm ", self.final_norm return # start iteration self.iter = 1 while True: alpha = nom/den x.axpy(alpha,self.d) # x = x + alpha d self.r.axpy(-alpha, self.z) # r = r - alpha A d self.B.solve(self.z, self.r) # z = B^-1 r betanom = self.r.inner(self.z) if self.parameters["print_level"] == 1: print " Iteration : ", self.iter, " (B r, r) = ", betanom if betanom < r0: self.converged = True self.reasonid = 1 self.final_norm = math.sqrt(betanom) if(self.parameters["print_level"] >= 0): print self.reason[self.reasonid] print "Converged in ", self.iter, " iterations with final norm ", self.final_norm break self.iter += 1 if self.iter > self.parameters["max_iter"]: self.converged = False self.reasonid = 0 self.final_norm = math.sqrt(betanom) if(self.parameters["print_level"] >= 0): print self.reason[self.reasonid] print "Not Converged. Final residual norm ", self.final_norm break beta = betanom/nom self.d *= beta self.d.axpy(1., self.z) #d = z + beta d self.A.mult(self.d,self.z) # z = A d den = self.d.inner(self.z) if den <= 0.0: self.converged = True self.reasonid = 2 self.final_norm = math.sqrt(nom) if(self.parameters["print_level"] >= 0): print self.reason[self.reasonid] print "Converged in ", self.iter, " iterations with final norm ", self.final_norm break nom = betanom