Algorithms
Optimizers, linear solvers, randomized linear algebra.
hippymfem.algorithms.NewtonCG
Inexact Newton-CG in the reduced parameter space.
At each outer iteration the Newton system \(H \hat m = -g\) is solved
inexactly by preconditioned CG (preconditioner \(R\)), truncated by an
Eisenstat-Walker tolerance \(\min(\tau_0, \sqrt{\|g\|/\|g_0\|})\). The first
GN_iter iterations use the Gauss-Newton Hessian, which is positive definite
far from the minimum; the full Hessian takes over once the iterate is close
enough for its indefiniteness not to matter.
Globalization is either an Armijo line search or a trust region; both are ported from hIPPYlib, with the same parameters, the same termination codes and the same printed table.
One default departs from hIPPYlib: cg_reorthogonalize. The CG iteration of a Newton
step keeps its residuals orthogonal explicitly instead of by its recurrence
(cgsolverSteihaug). On the model problem of the benchmarks
with 2.1 million state dofs this took 131 CG iterations for twelve Newton steps where
the recurrence took 193 to 210, a number that changed from run to run and from one GPU
to another, and the 131 did not change when the incremental solves of the Hessian
action were stopped at a relative residual of 1e-6 instead of 1e-12; with the
recurrence, incremental solves stopped at 1e-8 already cost a third more CG iterations.
The gradient stays exact (its forward and adjoint solves keep their tolerance), so the
MAP point is the same; only the Newton directions come from a Hessian of lower accuracy.
cg_reorthogonalize = False gives hIPPYlib’s iteration, for a comparison step by step.
Three parameters make the solves inside that CG cheaper. cg_preconditioner_tolerance
(1e-6 by default) stops the prior’s solves where they precondition it; 0 leaves
them at their own tolerance, as hIPPYlib does. cg_preconditioner_cycles (off by
default) replaces each of those solves by a fixed polynomial of that many multigrid
cycles, which is a linear map and needs no tolerance. cg_hessian_relaxation (off by
default) lets the incremental solves of a Hessian action lose accuracy as the CG
converges. They are described, with what they save and why they cannot go further,
in the guide (docs/source/guide/optimization.rst).
- exception hippymfem.algorithms.NewtonCG.ModelConvergenceError[source]
Bases:
RuntimeErrorRaised by a forward solve that fails, so the line search can back off.
- class hippymfem.algorithms.NewtonCG.ReducedSpaceNewtonCG(model, parameters=None, callback=None)[source]
Bases:
objectInexact Newton-CG for the reduced (parameter-space) problem.
- termination_reasons = ['Maximum number of Iteration reached', 'Norm of the gradient less than tolerance', 'Maximum number of backtracking reached', 'Norm of (g, dm) less than tolerance', 'Forward solve failed during backtracking']
- initial_grad_norm
gradient norm at the starting point, so callers can judge the reduction achieved independently of whether a requested tolerance was met: with inexact forward solves the attainable floor is set by the linear solver, not by the optimizer
- grad_reduction
||g||/||g_0|| when a line search exhausted its backtracks
- property comm
hippymfem.algorithms.bfgs
(Limited-memory) BFGS for the reduced problem.
An alternative to Newton-CG when second-order information is unavailable or too expensive: it needs only gradients, but typically many more iterations. The inverse-Hessian approximation is applied by the two-loop recursion, with Powell damping so that the curvature condition \(s^{\!\top}y > 0\) holds even when the line search returns a poor step.
H0inv is the initial inverse-Hessian guess. Using the prior solver
\(R^{-1}\) there, rather than a scaled identity, makes the iteration
mesh-independent, for the same reason it preconditions Newton-CG.
- class hippymfem.algorithms.bfgs.RescaledIdentity(init_vector=None)[source]
Bases:
objectDefault
H0inv: multiplication by a scalar \(d_0\).The scalar is refreshed from the oldest stored secant pair, which is the standard scaling that keeps the first steps from being wildly wrong.
- class hippymfem.algorithms.bfgs.BFGS_operator(parameters=None)[source]
Bases:
objectThe BFGS inverse-Hessian approximation, applied by the two-loop recursion.
- update(s, y)[source]
Add the secant pair
(s, y), damping it if curvature is too small.The damping needs \(H y\), written into
help. hIPPYlib’s version (and this one in 0.1.0) computed it withsolve()working inhelpas well, soH0inv.solvegot its input as its output: the default rescaled identity zeroed it, \(y^{\!\top} H y\) came out 0, and a pair that needed damping was damped tos = 0and raised.
- class hippymfem.algorithms.bfgs.BFGS(model, parameters=None)[source]
Bases:
objectBFGS (or L-BFGS) for the reduced inverse problem.
- termination_reasons = ['Maximum number of Iteration reached', 'Norm of the gradient less than tolerance', 'Maximum number of backtracking reached', 'Norm of (g, dm) less than tolerance']
hippymfem.algorithms.steepestDescent
Steepest descent in the prior-preconditioned metric.
The descent direction is \(-R^{-1}g\), not \(-g\): gradients live in the dual space, and using the Euclidean gradient on a mesh-dependent discretization gives steps whose size changes with refinement.
hippymfem.algorithms.cgsolverSteihaug
Preconditioned CG with the Steihaug trust-region stopping rule.
Ported from hIPPYlib, including the termination codes, so that iteration counts and stopping behaviour are directly comparable between the two libraries, with one departure: with a trust region, a direction of nonpositive curvature is followed to the boundary, as Steihaug’s method prescribes. hIPPYlib takes the full first direction wherever that lands (outside the region when the radius is small) and, at a later iteration, stops at the current iterate. Without a trust region the two agree: the first direction in full, or the current iterate.
Termination reasons:
the iteration limit was reached (no convergence);
the preconditioned residual met the tolerance (convergence);
a direction of nonpositive curvature was found (the operator is not SPD);
the trust-region boundary was reached.
The stopping test is on the \(B^{-1}\)-norm of the residual, relative to its initial value or against an absolute floor.
A second departure is optional (reorthogonalize). The recurrence of CG keeps a
residual orthogonal to the earlier ones only for one symmetric operator in exact
arithmetic. A prior-preconditioned Hessian has a few large, well separated eigenvalues,
for which that orthogonality is lost to rounding within a few tens of iterations, and a
Hessian action computed with inexact solves is not one symmetric operator at all: in
both cases the iteration is delayed, and by an amount that changes with the last digit
of the operands. With reorthogonalize every new residual is made orthogonal, in the
inner product of the preconditioner, to all the earlier ones explicitly, which costs
k inner products and updates at iteration k and two stored vectors per
iteration. The iteration then takes the number of steps of exact arithmetic, the same
in every run, and keeps taking it when the operator carries an error far above
rounding. ReducedSpaceNewtonCG turns it on (cg_reorthogonalize); here it is
off by default, since a solve of a thousand iterations would store two thousand vectors.
- class hippymfem.algorithms.cgsolverSteihaug.CGSolverSteihaug(parameters=None, comm=None)[source]
Bases:
objectSolve \(Ax=b\) with preconditioner \(B\) and Steihaug truncation.
Amust providemult(x, y)andinit_vector(x, dim);B_solvermust providesolve(x, b).- reason = ['Maximum Number of Iterations Reached', 'Relative/Absolute residual less than tol', 'Reached a negative direction', 'Reached trust region boundary']
hippymfem.algorithms.linSolvers
Linear solvers.
hIPPYlib leans on PETSc here, and in particular on PETScLUSolver for the
forward, adjoint and incremental solves. PyMFEM’s standard parallel build links
no direct solver (no MUMPS, SuperLU_dist, STRUMPACK or PETSc), so the exact-solve
capability is supplied from the Python side in
directSolvers:
hIPPYlib |
hIPPyMFEM |
|---|---|
|
|
|
|
|
hypre BoomerAMG |
In parallel the factorization is replicated on every rank, so LUSolver
refuses a matrix larger than max_global_size rather than silently exhausting
memory.
- hippymfem.algorithms.linSolvers.HYPRE_PCG = True
Whether a CG solve with a hypre preconditioner runs in hypre’s own PCG (MFEM’s
HyprePCG) instead of MFEM’sCGSolver(HIPPYMFEM_HYPRE_PCG, on by default). The two make the same iteration: the same iterates, counts and stopping test from a zero initial guess. hypre’s PCG tells BoomerAMG that the initial guess of a V-cycle is zero, which saves the first relaxation its matrix-vector product on the finest level; MFEM zeroes the vector without saying so. With l1-Jacobi relaxation on a GPU that is one product of four per iteration: a solve of 2.1 million dofs to 1e-12 took 0.103 s and 0.089 s on an H100, 0.274 s and 0.227 s on an L40S (24 iterations each).
- hippymfem.algorithms.linSolvers.set_hypre_pcg(on=True)[source]
Choose hypre’s own PCG (
True) or MFEM’s CG (False) for the CG solves with a hypre preconditioner that are set up from now on (seeHYPRE_PCG); returns the old choice.
- class hippymfem.algorithms.linSolvers.KrylovSolver(comm, method='cg', precond='amg', systems_dim=None, elasticity=False, fes=None, systems_bynodes=None)[source]
Bases:
_SolverBaseMFEM Krylov solver with a hypre preconditioner.
- Parameters:
comm (mpi4py communicator)
method ({"cg", "gmres", "fgmres", "bicgstab", "minres"})
precond ({"amg", "ilu", "jacobi", "l1jacobi", "gs", "parasails", "euclid", "none"})
systems_dim (int, optional) – Pass the number of components for BoomerAMG’s systems-of-PDEs mode; use this for vector-valued (elasticity-like) operators, where scalar AMG converges poorly.
elasticity (bool) – Turn on BoomerAMG’s elasticity interpolation (needs
systems_dim). With hypre on a GPU, where MFEM has none, the systems options alone, with a warning.systems_bynodes (bool, optional) – How the unknowns of the components are ordered, which BoomerAMG has to be told to pair the rows of one component: component after component (
mfem.Ordering.byNODES, the ordering of this library’s spaces unless another is asked for, and the default here), or interleaved (byVDIM: passFalse). Taken fromfeswhere that is given. Withsystems_dimand neither of the two, the default is taken and a warning says so once: on an interleaved space BoomerAMG then pairs rows of different components, and a solve takes 15 to 50 times the iterations.fes (FunctionSpace or mfem.ParFiniteElementSpace, optional) – The space of the unknowns, for their ordering and for the elasticity options.
- ERROR_MODE
How hypre setup failures are handled. MFEM’s default aborts the whole job via
MFEM_VERIFY, which is wrong inside an optimizer: a line search routinely proposes a parameter whose operator defeats AMG’s coarsening (exp(m)spanning hundreds of decades), and that step should be rejected, not the run killed. Warning lets the solve fail to converge and surface as aRuntimeError, which the line search already catches.
- set_operator(A, pc=None, donor=None)[source]
Point the solver at
A;pcshares an existing preconditioner ofA.The forward solver and the forward-incremental solver hold the same Jacobian; the PDE passes the forward solver’s preconditioner here when the operators are the same object, so the hierarchy is built once and held once.
donoris the solverpccomes from: when that one runs hypre’s PCG on this very matrix (HYPRE_PCG), its PCG object is shared too, since each such object sets its preconditioner up at its first solve.A
SingleParMatrixis solved in the single-precision hypre it lives in (CG with BoomerAMG only);pcis then the engine of another solver of the same matrix.
- SetOperator(A, pc=None, donor=None)
Point the solver at
A;pcshares an existing preconditioner ofA.The forward solver and the forward-incremental solver hold the same Jacobian; the PDE passes the forward solver’s preconditioner here when the operators are the same object, so the hierarchy is built once and held once.
donoris the solverpccomes from: when that one runs hypre’s PCG on this very matrix (HYPRE_PCG), its PCG object is shared too, since each such object sets its preconditioner up at its first solve.A
SingleParMatrixis solved in the single-precision hypre it lives in (CG with BoomerAMG only);pcis then the engine of another solver of the same matrix.
- property tolerance_floor
The relative residual below which a solve with the present operator does not go, whatever tolerance is asked: that of single precision for a matrix of the single-precision hypre, zero otherwise.
- class hippymfem.algorithms.linSolvers.ChebyshevSolver(krylov, degree=4, lanczos=12, safety=(0.85, 1.05))[source]
Bases:
KeepAliveA fixed polynomial in place of a Krylov solve: \(x = q(CA)\,C\,b\).
krylovis aKrylovSolverwhose operator \(A\) is set, with its preconditioner \(C\) (a multigrid cycle).degreesteps of the Chebyshev iteration on the interval of the eigenvalues of \(CA\) apply \(C\) that many times (a BoomerAMG cycle from the iterate before it, any other preconditioner to a residual updated with a product of \(A\)), with coefficients that do not depend on the right-hand side. The result is one linear map, symmetric and positive definite where \(C\) is, and its error in the energy norm is at most\[1 / T_{\mathrm{degree}}\Bigl(\frac{\lambda_{\max} + \lambda_{\min}} {\lambda_{\max} - \lambda_{\min}}\Bigr)\]of the solution (
error_bound): 1.6 % with four cycles of a multigrid with which CG gains a factor of 0.3 an iteration, where CG needs twelve iterations for six digits.That is what a preconditioner has to be. A Krylov solve stopped at a tolerance is a different map for every right-hand side, so it has to be stopped far below the tolerance of the iteration it preconditions (
ReducedSpaceNewtonCG,cg_preconditioner_tolerance); a fixed polynomial needs no tolerance, and the iteration it preconditions sees an operator whose spectrum is that of the exact preconditioner moved by at most twice the bound. It does not replace the solve where the solution itself is wanted (a sample of the prior, a variance).The interval is estimated once, at the first solve, from the Lanczos matrix of
lanczosCG iterations on a vector of random signs; the Ritz values lie inside the spectrum, so the interval used is wider (safety). Outside the interval the polynomial’s error grows slowly and stays below one up to twice its centre, so an estimate that is somewhat off costs accuracy, not definiteness.- interval
the interval of the polynomial, and the Ritz values it came from
- ritz
the interval of the polynomial, and the Ritz values it came from
- property error_bound
The bound on the relative error of a solve in the energy norm, for eigenvalues inside
interval(Nonebefore the first solve).
- setup()[source]
Estimate the interval (and let MFEM set the preconditioner up for a direct application, which it does at the first one whatever hypre’s PCG did before).
- solve(x, b)[source]
x = q(C A) C b; returns the number of cycles.A step of the iteration needs
C rfor the residualrof the current iteratey. With BoomerAMG a cycle applied fromy(MFEM’siterative_mode) returnsy + C (b - A y), the residual formed inside it by the relaxation that a cycle begins with: the step is the difference, and no product of the operator is made here. (Applied from zero, a cycle begins with that same product, of a zero vector, since MFEM does not mark the vector as zero for hypre: one product with its exchange more in every cycle.) Any other preconditioner is applied to a residual that is updated with a product, since not every one of them starts from the vector it is given.
- class hippymfem.algorithms.linSolvers.CycleOf(cycle, krylov)[source]
Bases:
objectA Krylov solver’s operator with another preconditioner than its own, as
ChebyshevSolverreads a solver:cycle(Mult(b, x)on MFEM’s vectors, from zero) in place of the preconditioner ofkrylov.- method = 'cg'
- precond_type = 'cycle'
the polynomial then applies the cycle to a residual that it updates)
- Type:
(not “amg”
- property A
- class hippymfem.algorithms.linSolvers.LUSolver(comm=None, method='superlu', max_global_size=None, replicate=False)[source]
Bases:
_SolverBaseSparse direct solve via scipy’s SuperLU, on any number of ranks.
Stands in for hIPPYlib’s
PETScLUSolver. On one rank this factorizes the matrix in place. In parallel it gathers the matrix onto rank 0 and factorizes it there (on every rank withreplicate=True), which is exact and independent of the rank count; seeReplicatedLUSolver, which this delegates to, for the memory trade and the size guard.- Parameters:
- SetOperator(A)
- class hippymfem.algorithms.linSolvers.TransposeSolver(solver_factory)[source]
Bases:
KeepAliveSolve with
A^Tby forming the transpose once and wrapping a solver.- SetOperator(A)
- property parameters
- class hippymfem.algorithms.linSolvers.LumpedMassSolver(M, comm=None)[source]
Bases:
KeepAliveInverse of the row-sum-lumped diagonal of a mass matrix.
A cheap, robust stand-in for an exact mass solve when only a spectrally equivalent operator is needed.
- hippymfem.algorithms.linSolvers.auto_solver(space_or_size, comm=None, max_direct=None, method='cg', precond='amg', **kw)[source]
A direct solve where one is affordable, Krylov where it is not.
An exact solve makes a Hessian spectrum a property of the discretization rather than of a Krylov tolerance, and it is rank-count independent. It is the wrong default at scale, because the replicated factorization (the only one available without petsc4py) costs
O(nnz(L+U))on every rank. The choice therefore follows the problem size, not the rank count: a one-rank run on a fine mesh is exactly where a direct solve fails.- Parameters:
space_or_size (FunctionSpace or int) – The space to be solved on, or its global number of unknowns.
max_direct (int, optional) – Largest problem to factorize. Defaults to the replicated solver’s own refusal threshold, so this never selects a solver that would then decline.
preferred (Where petsc4py is available its distributed factorization is)
since
fill. (it is exact without replicating the)
- hippymfem.algorithms.linSolvers.make_solver(comm, kind='krylov', method='cg', precond='amg', **kw)[source]
Factory used by the priors and PDE problems.
kindis one of"krylov"/"iterative"MFEM Krylov with a hypre preconditioner.
"lu"/"direct"Exact scipy SuperLU, replicated in parallel (
LUSolver)."petsc_lu"PETSc’s best available distributed factorization.
"petsc"PETSc Krylov.
"auto"auto_solver(); needsspace_or_size.
The PETSc kinds need petsc4py to have been imported before PyMFEM; see
directSolvers.
hippymfem.algorithms.directSolvers
Parallel direct (exact) linear solvers.
hIPPYlib uses PETScLUSolver, a distributed MUMPS factorization, whenever an
exact forward, adjoint or incremental solve is wanted: an exact Hessian action
lets a randomized eigensolver or a Newton-CG tolerance study measure the
discretization rather than the Krylov tolerance. PyMFEM’s standard parallel
build links none of MUMPS, SuperLU_dist, STRUMPACK or PETSc, so this module
supplies the capability from the Python side, by two exact routes:
ReplicatedLUSolverGathers the distributed matrix onto rank 0 and factorizes it there with scipy’s SuperLU; a solve gathers the right-hand side, solves on rank 0 and scatters the result. Needs nothing beyond scipy, works on any rank count, and matches a distributed direct solve to round-off. It costs one serial factorization of the whole matrix in time and
O(nnz(L+U))on rank 0 in memory, the standard “redundant coarse solve” trade and the right one up to a few hundred thousand unknowns.replicate=Truefactorizes on every rank instead, which the name comes from: every rank then solves for itself and no rank waits on rank 0, forO(nnz(L+U))on each. Beyond that size use a Krylov solver.PETScLUSolverA distributed factorization when petsc4py is importable, picking the best package the local PETSc was built with (MUMPS, SuperLU_dist, STRUMPACK, PaStiX) and falling back to PETSc’s own
redundant+lu, which is exact as well.PETScKrylovSolverexposes PETSc’s Krylov methods and preconditioners for the same matrices.
Import order matters for the PETSc route. petsc4py and PyMFEM are linked
against MPI libraries that must be resolved in that order; importing petsc4py
after mfem raises ImportError: libmpi_mpifh.so.40: undefined symbol:
mpi_conversion_fn_null_. Set HIPPYMFEM_PETSC=1 before importing
hippymfem (which then imports petsc4py first), or import petsc4py yourself at
the top of the script. petsc_available() reports what happened instead of
guessing.
- class hippymfem.algorithms.directSolvers.ReplicatedLUSolver(comm=None, max_global_size=None, permc_spec=None, replicate=False)[source]
Bases:
_BaseExact sparse LU of the gathered matrix, on rank 0 or on every rank.
A drop-in stand-in for hIPPYlib’s
PETScLUSolverthat needs no external package. By default the matrix is gathered onto rank 0 and factorized there;solvegathers the right-hand side, solves on rank 0 and scatters the result, so the other ranks hold no factorization and a node’s memory is charged once, not once per rank. Withreplicate=Trueevery rank holds the factorization and solves for itself from an all-gathered right-hand side. Either way the result is independent of the rank count and the same on every rank.- Parameters:
- SetOperator(A)
- class hippymfem.algorithms.directSolvers.PETScLUSolver(comm=None, package='auto')[source]
Bases:
_PETScBaseDistributed exact LU through PETSc.
package="auto"tries the parallel factorizations the local PETSc was built with and, if none is available, falls back toredundantwith an innerlu: every rank factorizes the gathered matrix, which is still an exact solve.package_usedrecords what was chosen, so a script can report it rather than assume it.- SetOperator(A)
- class hippymfem.algorithms.directSolvers.PETScKrylovSolver(comm=None, method='cg', precond='gamg', **options)[source]
Bases:
_PETScBasePETSc Krylov solver, the counterpart of hIPPYlib’s
PETScKrylovSolver.methodandprecondare PETSc names ("cg","gmres";"gamg","hypre","ilu","jacobi","none"). Extra PETSc options can be passed as keywords, e.g.pc_hypre_type="boomeramg".- SetOperator(A)
- hippymfem.algorithms.directSolvers.petsc_available()[source]
(True, "")when the PETSc route can be used, else(False, reason).
- hippymfem.algorithms.directSolvers.preload_petsc()[source]
Import petsc4py and return the
PETScmodule, orNoneon failure.
- hippymfem.algorithms.directSolvers.gather_matrix(A, comm=None, format='csc', root=None)[source]
Assemble a distributed
HypreParMatrixinto a scipy matrix on every rank, or onrootalone (Noneon the other ranks) when a root is given.Blocks are ordered by their true row offset rather than by rank, so this does not assume that hypre’s partition runs in rank order.
hippymfem.algorithms.multivector
A collection of distributed vectors with BLAS-3 reductions.
hIPPYlib stores a MultiVector as a Python list of vectors, so V^T W
costs nvec^2 separate global reductions. Here the columns are rows of a
single (nvec, n_local) C-contiguous array, so V^T W is one local GEMM
plus one Allreduce. Each column is still exposed as a
ParVector aliasing its row, so code written
against the hIPPYlib interface works unchanged.
- class hippymfem.algorithms.multivector.MultiVector(v=None, nvec=None)[source]
Bases:
objectnvecdistributed vectors sharing one layout.- Parameters:
v (ParVector or MultiVector, optional) – Layout template, or a MultiVector to deep-copy.
nvec (int, optional) – Number of columns; required when
vis a ParVector.
- property data
The
(nvec, n_local)backing array, current on the host.
- hippymfem.algorithms.multivector.MatMvMult(A, x, y, ensemble=None)[source]
y[i] = A x[i]for every column.ensembleis a communicator whose ranks each hold a complete copy of the problem (operators and vectors onMPI.COMM_SELF, or on a communicator of their own). The columns are then divided among its ranks, rankrappliesAto the columnsr, r + size, ..., and the results are exchanged so that every rank ends with all ofy. The applications are independent, so this scales with the number of ranks, which a domain decomposition of a small problem over the same GPUs does not (seeensemble_columns()).
- hippymfem.algorithms.multivector.ensemble_columns(ensemble, nvec, rank=None)[source]
The columns of an
nvec-column MultiVector that a rank ofensemblecomputes.
- hippymfem.algorithms.multivector.ensemble_exchange(ensemble, y)[source]
Give every rank of
ensembleall columns ofy, each taken from the rank that computed it (ensemble_columns()). The copies ofymust have the same local size on every rank, which they have when each rank holds the whole problem.
hippymfem.algorithms.randomizedEigensolver
Randomized eigensolvers for the (generalized) Hermitian eigenvalue problem.
The Laplace approximation needs the dominant eigenpairs of the prior-preconditioned data misfit Hessian, i.e. \(H_{\rm misfit} u = \lambda R u\). The dominant spectrum decays fast for ill-posed inverse problems, which is exactly the regime where randomized methods are appropriate.
References
N. Halko, P.G. Martinsson, J.A. Tropp, Finding structure with randomness, SIAM Review 53(2), 2011.
A.K. Saibaba, J. Lee, P.K. Kitanidis, Randomized algorithms for generalized Hermitian eigenvalue problems, NLAA 23(2), 2016.
- hippymfem.algorithms.randomizedEigensolver.singlePass(A, Omega, k, s=1, check=False, ensemble=None)[source]
Dominant eigenpairs of
Afrom one pass over the operator.Returns
(d, U)with \(U^{\!\top}U = I_k\).ensemble: seedoublePassG().
- hippymfem.algorithms.randomizedEigensolver.doublePass(A, Omega, k, s=1, check=False, ensemble=None)[source]
Dominant eigenpairs of
Awithspower iterations and two passes.More accurate than
singlePass()at the cost ofkextra operator applications; this is the default for the Laplace approximation.ensemble: seedoublePassG().
- hippymfem.algorithms.randomizedEigensolver.singlePassG(A, B, Binv, Omega, k, s=1, check=False, ensemble=None)[source]
Single-pass solver for \(A u = \lambda B u\), with \(U^{\!\top}BU=I\).
ensemble: seedoublePassG().
- hippymfem.algorithms.randomizedEigensolver.doublePassG(A, B, Binv, Omega, k, s=1, check=False, ensemble=None)[source]
Double-pass solver for \(A u = \lambda B u\), with \(U^{\!\top}BU=I\).
Ais the data-misfit Hessian,Bthe prior precisionRandBinva solver for it.ensembleis a communicator whose ranks each hold a complete copy of the problem, with the sameOmega; the applications ofAand ofBinvto the columns are then divided among its ranks (MatMvMult()), and every rank returns the same eigenpairs. The B-orthogonalization, which is sequential in the columns, is carried out by every rank.
hippymfem.algorithms.randomizedSVD
Randomized SVD.
Needed for non-symmetric reduced operators, above all the parameter-to-observable map \(B A^{-1} C\) of a linear inverse problem, whose singular values say how much of the parameter the data can see.
- hippymfem.algorithms.randomizedSVD.accuracyEnhancedSVD(A, Omega, k, s=1, check=False)[source]
Randomized SVD of a (possibly non-symmetric) operator.
Returns
(U, sigma, V)withUin the range,Vin the domain, and \(A \approx U \,\mathrm{diag}(\sigma)\, V^{\!\top}\).spower iterations are applied with re-orthogonalization at every step, which is what keeps the small singular values from being swamped.
- hippymfem.algorithms.randomizedSVD.singlePassSVD(A, Omega, k, check=False)[source]
Randomized SVD:
accuracyEnhancedSVD()withs=1, its default.
hippymfem.algorithms.lowRankOperator
Low-rank operators built from a spectral decomposition.
Represents \(U D U^{\!\top}\) (optionally in a \(B\) inner product, where
the columns of U are \(B\)-orthonormal). Used by the Laplace
approximation, where d and U come from the randomized generalized
eigensolver.
- class hippymfem.algorithms.lowRankOperator.LowRankOperator(d, U, my_init_vector=None)[source]
Bases:
Operator\(A = U\,\mathrm{diag}(d)\,U^{\!\top} B\).
- Parameters:
d (ndarray)
U (MultiVector) – Columns \(B\)-orthonormal: \(U^{\!\top}BU = I\).
my_init_vector (callable, optional) – Supplies the layout, when
U’s own is not what the caller wants.
- multTranspose(x, y)
y = U diag(d) U^T x.
hippymfem.algorithms.traceEstimator
Hutchinson trace estimation with a running error estimate.
Used for the integrated prior variance when the exact diagonal would cost one solve per degree of freedom.
hippymfem.algorithms.cgsampler
Sampling from a Gaussian using conjugate gradients.
Draws \(x \sim N(0, A^{-1})\) by accumulating the Lanczos/CG search directions, which avoids ever forming a square root of \(A^{-1}\): if \(d_i\) are the CG directions for a random right-hand side, then \(\sum_i (z_i / \sqrt{d_i^{\!\top} A d_i})\, d_i\) has covariance \(A^{-1}\) restricted to the Krylov subspace explored.
Reference: Parker & Fox, Sampling Gaussian distributions in Krylov spaces with conjugate gradients, SIAM J. Sci. Comput. 34(3), 2012.
- class hippymfem.algorithms.cgsampler.CGSampler(parameters=None)[source]
Bases:
objectSample \(N(0, A^{-1})\) with conjugate gradients.
- sample(noise, s)[source]
sreceives a sample;noisesupplies the standard normals.noisemust have at least as many entries as CG takes iterations; an exhausted supply raises rather than reusing numbers. A numpy array must be the same on every rank. A ParVector contributes its global entries, in global order, so every rank uses the same variate at each iteration.