Expv: Matrix Exponentials Times Vectors
The main functionality of ExponentialUtilities is the computation of matrix-phi-vector products. The phi functions are defined as
ϕ_0(z) = exp(z)
ϕ_(k+1)(z) = (ϕ_k(z) - 1) / zIn exponential algorithms, products in the form of ϕ_m(tA)b are frequently encountered. Instead of computing the matrix function first and then computing the matrix-vector product, the common alternative is to construct a Krylov subspaceK_m(A,b) and then approximate the matrix-phi-vector product.
Support for matrix-free operators
You can use any object as the "matrix" A as long as it implements the following linear operator interface:
Base.eltype(A)Base.size(A, dim)LinearAlgebra.mul!(y, A, x)(for computingy = A * xin place).LinearAlgebra.opnorm(A, p=Inf). If this is not implemented or the default implementation can be slow, you can manually pass in the operator norm (a rough estimate is fine) using the keyword argumentopnorm.LinearAlgebra.ishermitian(A). If this is not implemented or the default implementation can be slow, you can manually pass in the value using the keyword argumentishermitian.
Core API
ExponentialUtilities.expv — Function
expv(t, A, b; kwargs) -> exp(tA)bCompute the matrix-exponential-vector product with a Krylov approximation.
Arguments
t: scalar time or scale factor.A: square matrix or matrix-free operator satisfying the Matrix-Free Operator Interface page in the manual.b: input vector.Ks: a precomputedKrylovSubspacefor the(A, b)pair.
Keywords
mode::happy_breakdown(default) builds a regular Arnoldi basis;:error_estimateuses the Hermitian error-estimate method.cache: optionalExpvCachefor the reduced exponential.expmethod: matrix-exponential implementation for the reduced problem.- Remaining keywords for the
(t, A, b)form are forwarded toarnoldi.
Returns
The vector approximating $\exp(t A)b$.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
expv(0.1, A, b; m = 2)A Krylov subspace is constructed using arnoldi and exp! is called on the Hessenberg matrix. Consult arnoldi for the values of the keyword arguments. An alternative algorithm, where an error estimate generated on-the-fly is used to terminate the Krylov iteration, can be employed by setting the kwarg mode=:error_estimate.
expv(t, Ks; cache) -> exp(tA)bCompute the expv product using a pre-constructed Krylov subspace.
ExponentialUtilities.phiv — Function
phiv(t,A,b,k;correct,kwargs) -> [phi_0(tA)b phi_1(tA)b ... phi_k(tA)b][, errest]Compute matrix-phi-vector products with a Krylov approximation. k >= 1.
Arguments
t: scalar time or scale factor.A: square matrix or matrix-free operator satisfying the Matrix-Free Operator Interface page in the manual.b: input vector.k: highest phi-function order.Ks: precomputedKrylovSubspacefor the(A, b)pair.
Keywords
cache: optionalPhivCachefor reduced phi-function work.correct: apply the last-Arnoldi-vector correction to orders0:k-1.errest: return(w, estimate)instead of justw.- Remaining keywords for the
(t, A, b, k)form are forwarded toarnoldi.
Returns
An n by k + 1 matrix whose columns are $\varphi_j(tA)b$ for j = 0:k, or that matrix paired with an error estimate when errest=true.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
phiv(0.1, A, b, 2; m = 2)The phi functions are defined as
\[\varphi_0(z) = \exp(z),\quad \varphi_{k+1}(z) = \frac{\varphi_k(z) - \varphi_k(0)}{z}\]
A Krylov subspace is constructed using arnoldi and phiv_dense is called on the Hessenberg matrix. If correct=true, then phi0 through phik-1 are updated using the last Arnoldi vector v_m+1 [1]. If errest=true then an additional error estimate for the second-to-last phi is also returned. For the additional keyword arguments, consult arnoldi.
phiv(t,Ks,k;correct,kwargs) -> [phi0(tA)b phi1(tA)b ... phi_k(tA)b][, errest]
Compute the matrix-phi-vector products using a pre-constructed Krylov subspace.
ExponentialUtilities.expv! — Function
expv!(w,t,Ks[;cache]) -> wCompute $\exp(t A)b$ from a precomputed Krylov basis without allocating the output vector.
Arguments
w: output vector, overwritten in place.t: scalar time or scale factor.Ks: populatedKrylovSubspace.
Keywords
cache:nothingor a reusableExpvCache.expmethod: reduced matrix-exponential implementation.
Returns
The mutated w.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
Ks = arnoldi(A, b; m = 2)
w = similar(b)
expv!(w, 0.1, Ks)expv!(w, t, A, b, Ks, cache)Calculate the action of exp(t*A) on b, storing the result in w. The Krylov iteration terminates when its subspace error estimate is below the requested tolerance. This method currently supports Hermitian operators and a cache returned by get_subspace_cache.
Arguments
w: output vector, overwritten in place.t: scalar time or scale factor.A: Hermitian matrix or matrix-free operator.b: input vector.Ks: reusableKrylovSubspacewith real recurrence coefficients.cache: subspace-exponential workspace fromget_subspace_cache.
Keywords
atol = 1e-8: absolute stopping tolerance.rtol = 1e-4: relative stopping tolerance, scaled bynorm(b).m: maximum Lanczos dimension, defaulting tomin(Ks.maxiter, size(A, 1)).ishermitian = LinearAlgebra.ishermitian(A): must betrue; non-Hermitian error estimation is not implemented.verbose = false: print each error estimate whentrue.expmethod: reduced matrix-exponential method. Reserved for compatibility.
Returns
The mutated w.
Examples
using LinearAlgebra
A = Hermitian([-2.0 1.0; 1.0 -2.0])
b = ComplexF64[1, 0]
Ks = KrylovSubspace{ComplexF64, Float64}(length(b), 2)
cache = get_subspace_cache(Ks)
w = similar(b)
expv!(w, 0.1, A, b, Ks, cache; atol = 1.0e-10, rtol = 1.0e-8)ExponentialUtilities.phiv! — Function
phiv!(w,t,Ks,k[;cache,correct,errest]) -> w[,errest]Compute phi-vector products from a precomputed Krylov basis without allocating the output matrix.
Arguments
w: output matrix withsize(w, 2) == k + 1, overwritten in place.t: scalar time or scale factor.Ks: populatedKrylovSubspace.k: highest phi-function order.
Keywords
cache, correct, and errest have the same meaning as for phiv.
Returns
The mutated w, or (w, estimate) when errest=true.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
Ks = arnoldi(A, b; m = 2)
w = similar(b, length(b), 3)
phiv!(w, 0.1, Ks, 2)ExponentialUtilities.expv_timestep — Function
expv_timestep(ts,A,b[;adaptive,tol,kwargs...]) -> UEvaluates the matrix exponentiation-vector product using time stepping
\[u = \exp(tA)b\]
ts is an array of time snapshots for u, with U[:,j] ≈ u(ts[j]). ts can also be just one value, in which case only the end result is returned and U is a vector.
The time stepping formula of Niesen & Wright is used [1]. If the time step tau is not specified, it is chosen according to (17) of Niesen & Wright. If adaptive==true, the time step and Krylov subspace size adaptation scheme of Niesen & Wright is used, the relative tolerance of which can be set using the keyword parameter tol. The delta and gamma parameters of the adaptation scheme can also be adjusted.
Set verbose=true to print out the internal steps (for debugging). For the other keyword arguments, consult arnoldi and phiv, which are used internally.
Note that this function is just a special case of phiv_timestep with a more intuitive interface (vector b instead of a n-by-1 matrix B).
Arguments
tsort: requested output time(s).A: square matrix or matrix-free operator satisfying the Matrix-Free Operator Interface page in the manual.b: initial vector.
Keywords
adaptive, tol, tau, m, iop, opnorm, ishermitian, delta, gamma, NA, and verbose are forwarded to phiv_timestep.
Returns
A vector for scalar t, or a matrix whose column j corresponds to ts[j].
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
expv_timestep(0.1, A, b; tol = 1.0e-8)ExponentialUtilities.phiv_timestep — Function
phiv_timestep(ts,A,B[;adaptive,tol,kwargs...]) -> UEvaluates the linear combination of phi-vector products using time stepping
\[u = \varphi_0(tA)b_0 + t\varphi_1(tA)b_1 + \cdots + t^p\varphi_p(tA)b_p\]
ts is an array of time snapshots for u, with U[:,j] ≈ u(ts[j]). ts can also be just one value, in which case only the end result is returned and U is a vector.
The time stepping formula of Niesen & Wright is used [1]. If the time step tau is not specified, it is chosen according to (17) of Niesen & Wright. If adaptive==true, the time step and Krylov subspace size adaptation scheme of Niesen & Wright is used, the relative tolerance of which can be set using the keyword parameter tol. The delta and gamma parameters of the adaptation scheme can also be adjusted.
The adaptation scheme drives the internal error estimate below tol times a scalar operator-norm scale of A. By default (opnorm === nothing) that scale is estimated matrix-free from the Arnoldi Hessenberg built on the first Krylov step, so opnorm(A, Inf) is never evaluated: this needs no operator method beyond mul! – suitable for matrix types (e.g. sparse GPU arrays) that do not support opnorm – and reflects A's action on the Krylov subspace the exponential actually explores. The first Arnoldi step supplies the scale used to seed the initial tau. Pass opnorm as a precomputed scalar (a norm or bound) or a function opnorm(A, Inf) to override the estimate with an explicit operator-norm scale, which additionally seeds the initial tau.
When encountering a happy breakdown in the Krylov subspace construction, the time step is set to the remainder of the time interval since time stepping is no longer necessary.
Set verbose=true to print out the internal steps (for debugging). For the other keyword arguments, consult arnoldi and phiv, which are used internally.
Arguments
tsort: requested output time(s).A: square matrix or matrix-free operator satisfying the Matrix-Free Operator Interface page in the manual.B: matrix whose columns are the coefficientsb_0, ..., b_p.
Keywords
tau: initial internal step size.0selects an estimate.m: maximum Krylov dimension per internal step.tol: requested relative tolerance for adaptive stepping.opnorm:nothing, a scalar bound, or callable(A, Inf) -> bound.adaptive: enable joint step-size and Krylov-dimension adaptation.iop,correct,caches,delta,ishermitian,gamma,NA, andverbose: advanced Arnoldi, correction, workspace, and adaptation controls.
Returns
A vector for scalar t, or a matrix whose column j is the requested linear combination at ts[j].
Examples
A = [-2.0 1.0; 0.0 -1.0]
B = [1.0 0.5; 0.0 0.0]
phiv_timestep(0.1, A, B; tol = 1.0e-8)ExponentialUtilities.expv_timestep! — Function
expv_timestep!(u,t,A,b[;kwargs]) -> uNon-allocating version of expv_timestep.
Arguments
uorU: output vector or output matrix, overwritten in place.torts: requested output time(s).A: square matrix or matrix-free operator.b: initial vector.
Keywords
caches: optional reusable workspace tuple from the internal timestep-cache constructor.- Remaining keywords have the same meaning as for
expv_timestep.
Returns
The mutated u or U.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
u = similar(b)
expv_timestep!(u, 0.1, A, b; tol = 1.0e-8)ExponentialUtilities.phiv_timestep! — Function
phiv_timestep!(U,ts,A,B[;kwargs]) -> UNon-allocating version of phiv_timestep.
Arguments
uorU: output vector or matrix, overwritten in place.torts: requested output time(s).A: square matrix or matrix-free operator.B: coefficient matrix for the phi-function combination.
Keywords
The keywords are the same as for phiv_timestep.
Returns
The mutated u or U.
Examples
A = [-2.0 1.0; 0.0 -1.0]
B = [1.0 0.5; 0.0 0.0]
u = similar(B, size(B, 1))
phiv_timestep!(u, 0.1, A, B; tol = 1.0e-8)ExponentialUtilities.kiops — Function
kiops(tau_out, A, u; kwargs...) -> (w, stats)Evaluate a linear combination of phi functions at the requested output times using the adaptive KIOPS Krylov algorithm:
\[ w_i = φ_0(t_i A) u_0 + φ_1(t_i A) u_1 + φ_2(t_i A) u_2 + \cdots.\]
The size of the Krylov subspace is changed dynamically during the integration. The Krylov subspace is computed using the incomplete orthogonalization method.
Arguments
tau_out: scalar or ordered collection of output times. All entries must have the same sign.A: square matrix or linear operator used as the phi-function argument.u: vectoru_0, or matrix whose columns areu_0, u_1, ...in the linear combination above.
Keywords
tol::Real = 1e-7: requested local convergence tolerance.mmin::Int = 10: minimum adaptive Krylov dimension.mmax::Int = 128: maximum adaptive Krylov dimension.m::Int = min(mmin, mmax): initial Krylov dimension estimate.iop::Int = 2: incomplete-orthogonalization length.ishermitian::Bool = LinearAlgebra.ishermitian(A): whetherAis Hermitian. Only set this totruewhen the operator obeys that contract.opnorm = LinearAlgebra.opnorm(A, Inf): operator-norm scale used by the adaptive controller.task1::Bool = false: apply the KIOPS task-one final scaling whentrue.
Returns
Returns (w, stats), where w contains the requested linear combinations and stats is the tuple (substeps, rejected_steps, krylov_steps, matrix_exponentials, final_krylov_dimension).
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
w, stats = kiops(0.1, A, b)References
- Gaudreault, S., Rainwater, G. and Tokman, M., 2018. KIOPS: A fast adaptive Krylov subspace solver for exponential integrators. Journal of Computational Physics. Based on the PHIPM and EXPMVP codes (http://www1.maths.leeds.ac.uk/~jitse/software.html). https://gitlab.com/stephane.gaudreault/kiops.
- Niesen, J. and Wright, W.M., 2011. A Krylov subspace method for option pricing. SSRN 1799124
- Niesen, J. and Wright, W.M., 2012. Algorithm 919: A Krylov subspace algorithm for evaluating the $φ$-functions appearing in exponential integrators. ACM Transactions on Mathematical Software (TOMS), 38(3), p.22
Caches
ExponentialUtilities.ExpvCache — Type
ExpvCache{T}(maxiter::Int)Reusable scratch workspace for the in-place expv! Krylov matrix-exponential-vector product with element type T. It holds a single flat buffer sized for an maxiter×maxiter Hessenberg matrix; pass the cache as the cache keyword to expv! to avoid reallocating that buffer on repeated calls. The buffer is grown automatically (via resize!) if a later call requests a larger subspace than the one it was allocated for.
Arguments
T: element type of the Krylov Hessenberg workspace.maxiter: largest Krylov dimension expected for repeated calls.
Returns
An ExpvCache sized for Krylov dimensions through maxiter.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
cache = ExpvCache{Float64}(30)
expv!(similar(b), 0.1, arnoldi(A, b); cache)Fields
mem::Vector{T}: flat storage of lengthmaxiter^2reshaped on demand into them×mworking copy of the Hessenberg matrix.expcache: typed, size-keyed reduced matrix-exponential workspaces.expcol::Vector{T}: contiguous storage for the first column of the reduced matrix exponential.
ExponentialUtilities.PhivCache — Type
PhivCache(w, maxiter::Int, p::Int)Reusable scratch workspace for the in-place phiv! Krylov matrix-phi-vector product. It packs all the intermediate buffers needed to evaluate ϕ_0(tA)b … ϕ_p(tA)b over a Krylov subspace of dimension up to maxiter into a single flat vector, whose element type matches eltype(w) (the output array w). Pass the cache as the cache keyword to phiv! to avoid reallocating these buffers on repeated calls; the buffer is grown automatically (via resize!) if a later call requests a larger subspace or higher order than the one it was allocated for.
The useview type parameter records whether views into the flat buffer are usable (true for ordinary Arrays, false for GPU arrays, which get freshly allocated reshaped copies instead).
Arguments
w: representative output array whose element type and device determine the workspace layout.maxiter: largest Krylov dimension expected for repeated calls.p: highest phi-function order expected for repeated calls.
Returns
A PhivCache sized for Krylov dimensions through maxiter and phi orders through p.
Examples
A = [-2.0 1.0; 0.0 -1.0]
b = [1.0, 0.0]
cache = PhivCache(similar(b, length(b), 3), 30, 2)
phiv!(similar(b, length(b), 3), 0.1, arnoldi(A, b), 2; cache)Fields
mem::Vector{T}: flat storage that is carved (byget_caches) into the subspace vector, a Hessenberg working copy, and the two augmented matrices used by the phi-function recurrence.expcache:exponential!workspaces (seealloc_mem) keyed by extended-matrix size, reused across calls.Wis a single concrete workspace type; seeExpvCachefor why one type covers all sizes.coeffs::Vector{T}: coefficient scratch used by timestep evaluations.ts1::Vector{Float64}: one-element time buffer used by scalar-time wrappers.
ExponentialUtilities.StegrCache — Type
StegrCache(T, n::Integer)Subspace-exponential cache for the error-estimate variant of expv! (the mode = :error_estimate path) on Hermitian operators with element type T. It is a concrete HermitianSubspaceCache sized for a Lanczos subspace of dimension up to n. Every iteration diagonalizes the tridiagonal subspace matrix with a preallocated implicit-QL eigensolver, so applying the subspace exponential allocates nothing. Construct one directly, or let get_subspace_cache build the right cache for a given KrylovSubspace.
Arguments
T: element type of the propagated subspace vector.n::Integer: maximum Lanczos subspace dimension.
Returns
A StegrCache sized for subspaces through dimension n.
Fields
v::Vector{T}: the subspace-propagated vector (lengthn) that holds the result of applying the subspace exponential.w::Vector{T}: scratch vector (lengthn) for intermediate values.d::Vector{R},e::Vector{R}: working copies (lengthn) of the tridiagonal diagonal and off-diagonal, overwritten by the eigensolver, whereR = real(T).Z::Matrix{R}:n × neigenvector workspace.
Examples
cache = StegrCache(ComplexF64, 30)ExponentialUtilities.get_subspace_cache — Function
get_subspace_cache(Ks::KrylovSubspace) -> SubspaceCacheConstruct the subspace-exponential cache appropriate for the Krylov subspace Ks, for use with the error-estimate variant of expv!. For a real (Hermitian) subspace this returns a StegrCache sized to Ks.maxiter, which diagonalizes the tridiagonal subspace matrix with a preallocated implicit-QL eigensolver. Non-Hermitian (complex) subspaces are not yet supported and raise an error.
Arguments
Ks: a populated or reusableKrylovSubspacewith real-valued Hessenberg coefficients.
Returns
A StegrCache that can be supplied to the error-estimate expv! method. This function is a cache constructor, not an extension point.
Examples
Ks = KrylovSubspace{ComplexF64, Float64}(100, 30)
cache = get_subspace_cache(Ks)- 1Niesen, J., & Wright, W. (2009). A Krylov subspace algorithm for evaluating the φ-functions in exponential integrators. arXiv preprint arXiv:0907.4631. Formula (10).
- 1Niesen, J., & Wright, W. (2009). A Krylov subspace algorithm for evaluating the φ-functions in exponential integrators. arXiv preprint arXiv:0907.4631.