diff --git a/docs/make.jl b/docs/make.jl index bd52516b..ecf81111 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,24 +2,19 @@ using Documenter using KrylovKit makedocs(; - modules = [KrylovKit], - sitename = "KrylovKit.jl", - authors = "Jutho Haegeman and collaborators", - pages = [ - "Home" => "index.md", - "Manual" => [ - "man/intro.md", - "man/linear.md", - "man/leastsquares.md", - "man/eig.md", - "man/svd.md", - "man/matfun.md", - "man/reallinear.md", - "man/algorithms.md", - "man/implementation.md", - ], - ], - format = Documenter.HTML(; prettyurls = get(ENV, "CI", nothing) == "true") -) + modules=[KrylovKit], + sitename="KrylovKit.jl", + authors="Jutho Haegeman and collaborators", + pages=["Home" => "index.md", + "Manual" => ["man/intro.md", + "man/linear.md", + "man/leastsquares.md", + "man/eig.md", + "man/svd.md", + "man/matfun.md", + "man/reallinear.md", + "man/algorithms.md", + "man/implementation.md"]], + format=Documenter.HTML(; prettyurls=get(ENV, "CI", nothing) == "true")) -deploydocs(; repo = "github.com/Jutho/KrylovKit.jl.git") +deploydocs(; repo="github.com/Jutho/KrylovKit.jl.git") diff --git a/ext/KrylovKitChainRulesCoreExt/eigsolve.jl b/ext/KrylovKitChainRulesCoreExt/eigsolve.jl index 9686066a..f767c6dc 100644 --- a/ext/KrylovKitChainRulesCoreExt/eigsolve.jl +++ b/ext/KrylovKitChainRulesCoreExt/eigsolve.jl @@ -1,20 +1,17 @@ -function ChainRulesCore.rrule( - config::RuleConfig, - ::typeof(eigsolve), - f, - x₀, - howmany, - which, - alg_primal; - alg_rrule = Arnoldi(; - tol = alg_primal.tol, - krylovdim = alg_primal.krylovdim, - maxiter = alg_primal.maxiter, - eager = alg_primal.eager, - orth = alg_primal.orth, - verbosity = alg_primal.verbosity - ) - ) +function ChainRulesCore.rrule(config::RuleConfig, + ::typeof(eigsolve), + f, + x₀, + howmany, + which, + alg_primal; + alg_rrule=Arnoldi(; + tol=alg_primal.tol, + krylovdim=alg_primal.krylovdim, + maxiter=alg_primal.maxiter, + eager=alg_primal.eager, + orth=alg_primal.orth, + verbosity=alg_primal.verbosity)) (vals, vecs, info) = eigsolve(f, x₀, howmany, which, alg_primal) if alg_primal isa Lanczos fᴴ = f @@ -25,17 +22,13 @@ function ChainRulesCore.rrule( v -> unthunk(pb(v)[2]) end end - eigsolve_pullback = make_eigsolve_pullback( - config, f, fᴴ, x₀, howmany, which, - alg_primal, alg_rrule, vals, vecs, info - ) + eigsolve_pullback = make_eigsolve_pullback(config, f, fᴴ, x₀, howmany, which, + alg_primal, alg_rrule, vals, vecs, info) return (vals, vecs, info), eigsolve_pullback end -function make_eigsolve_pullback( - config, f, fᴴ, x₀, howmany, which, alg_primal, alg_rrule, - vals, vecs, info - ) +function make_eigsolve_pullback(config, f, fᴴ, x₀, howmany, which, alg_primal, alg_rrule, + vals, vecs, info) function eigsolve_pullback(ΔX) ∂self = NoTangent() ∂x₀ = ZeroTangent() @@ -55,7 +48,7 @@ function make_eigsolve_pullback( # discard vals/vecs from n + 1 onwards if contribution is zero _n_vals = _Δvals isa AbstractZero ? nothing : findlast(!iszero, _Δvals) _n_vecs = _Δvecs isa AbstractZero ? nothing : - findlast(!Base.Fix2(isa, AbstractZero), _Δvecs) + findlast(!Base.Fix2(isa, AbstractZero), _Δvecs) n_vals = isnothing(_n_vals) ? 0 : _n_vals n_vecs = isnothing(_n_vecs) ? 0 : _n_vecs n = max(n_vals, n_vecs) @@ -93,10 +86,8 @@ function make_eigsolve_pullback( # Compute actual pullback data: #------------------------------ - ws = compute_eigsolve_pullback_data( - Δvals, Δvecs, view(vals, 1:n), view(vecs, 1:n), - info, which, fᴴ, alg_primal, alg_rrule - ) + ws = compute_eigsolve_pullback_data(Δvals, Δvecs, view(vals, 1:n), view(vecs, 1:n), + info, which, fᴴ, alg_primal, alg_rrule) # Return pullback in correct form: #--------------------------------- @@ -106,10 +97,8 @@ function make_eigsolve_pullback( return eigsolve_pullback end -function compute_eigsolve_pullback_data( - Δvals, Δvecs, vals, vecs, info, which, fᴴ, - alg_primal, alg_rrule::Union{GMRES, BiCGStab} - ) +function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, + alg_primal, alg_rrule::Union{GMRES,BiCGStab}) ws = similar(vecs, length(Δvecs)) T = scalartype(vecs[1]) @inbounds for i in 1:length(Δvecs) @@ -157,21 +146,18 @@ function compute_eigsolve_pullback_data( end w, reverse_info = let λ = λ, v = v linsolve(b, zerovector(b), alg_rrule) do (x1, x2) - y1 = VectorInterface.add!!( - VectorInterface.add!!( - KrylovKit.apply(fᴴ, x1), x1, conj(λ), -1 - ), - v, x2 - ) + y1 = VectorInterface.add!!(VectorInterface.add!!(KrylovKit.apply(fᴴ, x1), + x1, conj(λ), -1), + v, x2) y2 = inner(v, x1) return (y1, y2) end end if info.converged >= i && reverse_info.converged == 0 && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`eigsolve` cotangent linear problem ($i) did not converge, whereas the primal eigenvalue problem did: normres = $(reverse_info.normres)" elseif abs(w[2]) > (alg_rrule.tol * norm(w[1])) && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`eigsolve` cotangent linear problem ($i) returns unexpected result: error = $(w[2])" end ws[i] = w[1] @@ -179,10 +165,8 @@ function compute_eigsolve_pullback_data( return ws end -function compute_eigsolve_pullback_data( - Δvals, Δvecs, vals, vecs, info, which, fᴴ, - alg_primal::Arnoldi, alg_rrule::Arnoldi - ) +function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, + alg_primal::Arnoldi, alg_rrule::Arnoldi) n = length(Δvecs) T = scalartype(vecs[1]) G = zeros(T, n, n) @@ -268,7 +252,7 @@ function compute_eigsolve_pullback_data( # TODO: is `realeigsolve` every used here, as there is a separate `alg_primal::Lanczos` method below solver = (T <: Real) ? KrylovKit.realeigsolve : KrylovKit.eigsolve # for `eigsolve`, `T` will always be a Complex subtype` rvals, Ws, reverse_info = let P = P, ΔV = sylvesterarg, shift = shift, - eigsort = EigSorter(v -> minimum(DistanceTo(conj(v)), vals)) + eigsort = EigSorter(v -> minimum(DistanceTo(conj(v)), vals)) solver(W₀, n, eigsort, alg_rrule) do (w, x) w₀ = P(w) @@ -283,7 +267,7 @@ function compute_eigsolve_pullback_data( end end if info.converged >= n && reverse_info.converged < n && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`eigsolve` cotangent problem did not converge, whereas the primal eigenvalue problem did" end @@ -315,10 +299,8 @@ end (d::DistanceTo)(y) = norm(y - d.x) # several simplications happen in the case of a Hermitian eigenvalue problem -function compute_eigsolve_pullback_data( - Δvals, Δvecs, vals, vecs, info, which, fᴴ, - alg_primal::Lanczos, alg_rrule::Arnoldi - ) +function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, + alg_primal::Lanczos, alg_rrule::Arnoldi) n = length(Δvecs) T = scalartype(vecs[1]) VdΔV = zeros(T, n, n) @@ -377,7 +359,7 @@ function compute_eigsolve_pullback_data( P = orthogonalprojector(vecs, n) solver = (T <: Real) ? KrylovKit.realeigsolve : KrylovKit.eigsolve rvals, Ws, reverse_info = let P = P, ΔV = sylvesterarg, shift = shift, - eigsort = EigSorter(v -> minimum(DistanceTo(conj(v)), vals)) + eigsort = EigSorter(v -> minimum(DistanceTo(conj(v)), vals)) solver(W₀, n, eigsort, alg_rrule) do (w, x) w₀ = P(w) @@ -392,7 +374,7 @@ function compute_eigsolve_pullback_data( end end if info.converged >= n && reverse_info.converged < n && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`eigsolve` cotangent problem did not converge, whereas the primal eigenvalue problem did" end @@ -434,10 +416,8 @@ function construct∂f_eig(config, f, vecs, ws) end function construct∂f_eig(config, A::AbstractMatrix, vecs, ws) if A isa StridedMatrix - return InplaceableThunk( - Ā -> _buildĀ_eig!(Ā, vecs, ws), - @thunk(_buildĀ_eig!(zero(A), vecs, ws)) - ) + return InplaceableThunk(Ā -> _buildĀ_eig!(Ā, vecs, ws), + @thunk(_buildĀ_eig!(zero(A), vecs, ws))) else return @thunk(ProjectTo(A)(_buildĀ_eig!(zero(A), vecs, ws))) end diff --git a/ext/KrylovKitChainRulesCoreExt/linsolve.jl b/ext/KrylovKitChainRulesCoreExt/linsolve.jl index f4fa9c9b..3c97908f 100644 --- a/ext/KrylovKitChainRulesCoreExt/linsolve.jl +++ b/ext/KrylovKitChainRulesCoreExt/linsolve.jl @@ -1,19 +1,15 @@ -function ChainRulesCore.rrule( - config::RuleConfig, - ::typeof(linsolve), - f, - b, - x₀, - alg_primal, - a₀, - a₁; alg_rrule = alg_primal - ) +function ChainRulesCore.rrule(config::RuleConfig, + ::typeof(linsolve), + f, + b, + x₀, + alg_primal, + a₀, + a₁; alg_rrule=alg_primal) (x, info) = linsolve(f, b, x₀, alg_primal, a₀, a₁) fᴴ, construct∂f = lin_preprocess(config, f, x) - linsolve_pullback = make_linsolve_pullback( - fᴴ, b, a₀, a₁, alg_rrule, construct∂f, x, - info - ) + linsolve_pullback = make_linsolve_pullback(fᴴ, b, a₀, a₁, alg_rrule, construct∂f, x, + info) return (x, info), linsolve_pullback end @@ -32,19 +28,14 @@ function make_linsolve_pullback(fᴴ, b, a₀, a₁, alg_rrule, construct∂f, x return ∂self, ∂f, ∂b, ∂x₀, ∂algorithm, ∂a₀, ∂a₁ end - x̄₀ = zerovector( - x̄, - VectorInterface.promote_scale( - scalartype(x̄), - VectorInterface.promote_scale(a₀, a₁) - ) - ) - ∂b, reverse_info = linsolve( - fᴴ, x̄, x̄₀, alg_rrule, conj(a₀), - conj(a₁) - ) + x̄₀ = zerovector(x̄, + VectorInterface.promote_scale(scalartype(x̄), + VectorInterface.promote_scale(a₀, + a₁))) + ∂b, reverse_info = linsolve(fᴴ, x̄, x̄₀, alg_rrule, conj(a₀), + conj(a₁)) if info.converged > 0 && reverse_info.converged == 0 && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`linsolve` cotangent problem did not converge, whereas the primal linear problem did: normres = $(reverse_info.normres)" end x∂b = inner(x, ∂b) @@ -69,10 +60,8 @@ end function lin_preprocess(config, A::AbstractMatrix, x) fᴴ = adjoint(A) if A isa StridedMatrix - construct∂f_lin = w -> InplaceableThunk( - Ā -> _buildĀ_lin!(Ā, x, w), - @thunk(_buildĀ_lin!(zero(A), x, w)) - ) + construct∂f_lin = w -> InplaceableThunk(Ā -> _buildĀ_lin!(Ā, x, w), + @thunk(_buildĀ_lin!(zero(A), x, w))) else construct∂f_lin = let project_A = ProjectTo(A) w -> @thunk(project_A(_buildĀ_lin!(zero(A), x, w))) diff --git a/ext/KrylovKitChainRulesCoreExt/svdsolve.jl b/ext/KrylovKitChainRulesCoreExt/svdsolve.jl index e2126923..b9091670 100644 --- a/ext/KrylovKitChainRulesCoreExt/svdsolve.jl +++ b/ext/KrylovKitChainRulesCoreExt/svdsolve.jl @@ -1,28 +1,21 @@ # Reverse rule adopted from tsvd! rrule as found in TensorKit.jl -function ChainRulesCore.rrule( - config::RuleConfig, ::typeof(svdsolve), f, x₀, howmany, which, - alg_primal::GKL; - alg_rrule = Arnoldi(; - tol = alg_primal.tol, - krylovdim = alg_primal.krylovdim, - maxiter = alg_primal.maxiter, - eager = alg_primal.eager, - orth = alg_primal.orth, - verbosity = alg_primal.verbosity - ) - ) +function ChainRulesCore.rrule(config::RuleConfig, ::typeof(svdsolve), f, x₀, howmany, which, + alg_primal::GKL; + alg_rrule=Arnoldi(; + tol=alg_primal.tol, + krylovdim=alg_primal.krylovdim, + maxiter=alg_primal.maxiter, + eager=alg_primal.eager, + orth=alg_primal.orth, + verbosity=alg_primal.verbosity)) vals, lvecs, rvecs, info = svdsolve(f, x₀, howmany, which, alg_primal) - svdsolve_pullback = make_svdsolve_pullback( - config, f, x₀, howmany, which, alg_primal, - alg_rrule, vals, lvecs, rvecs, info - ) + svdsolve_pullback = make_svdsolve_pullback(config, f, x₀, howmany, which, alg_primal, + alg_rrule, vals, lvecs, rvecs, info) return (vals, lvecs, rvecs, info), svdsolve_pullback end -function make_svdsolve_pullback( - config, f, x₀, howmany, which, alg_primal, alg_rrule, vals, - lvecs, rvecs, info - ) +function make_svdsolve_pullback(config, f, x₀, howmany, which, alg_primal, alg_rrule, vals, + lvecs, rvecs, info) function svdsolve_pullback(ΔX) ∂self = NoTangent() ∂x₀ = ZeroTangent() @@ -43,9 +36,9 @@ function make_svdsolve_pullback( # discard vals/vecs from n + 1 onwards if contribution is zero _n_vals = _Δvals isa AbstractZero ? nothing : findlast(!iszero, _Δvals) _n_lvecs = _Δlvecs isa AbstractZero ? nothing : - findlast(!Base.Fix2(isa, AbstractZero), _Δlvecs) + findlast(!Base.Fix2(isa, AbstractZero), _Δlvecs) _n_rvecs = _Δrvecs isa AbstractZero ? nothing : - findlast(!Base.Fix2(isa, AbstractZero), _Δrvecs) + findlast(!Base.Fix2(isa, AbstractZero), _Δrvecs) n_vals = isnothing(_n_vals) ? 0 : _n_vals n_lvecs = isnothing(_n_lvecs) ? 0 : _n_lvecs n_rvecs = isnothing(_n_rvecs) ? 0 : _n_rvecs @@ -87,12 +80,11 @@ function make_svdsolve_pullback( # Compute actual pullback data: #------------------------------ - xs, ys = compute_svdsolve_pullback_data( - Δvals, Δlvecs, Δrvecs, - view(vals, 1:n), view(lvecs, 1:n), view(rvecs, 1:n), - info, f, which, - alg_primal, alg_rrule - ) + xs, ys = compute_svdsolve_pullback_data(Δvals, Δlvecs, Δrvecs, + view(vals, 1:n), view(lvecs, 1:n), + view(rvecs, 1:n), + info, f, which, + alg_primal, alg_rrule) # Return pullback in correct form: #--------------------------------- @@ -102,12 +94,10 @@ function make_svdsolve_pullback( return svdsolve_pullback end -function compute_svdsolve_pullback_data( - Δvals, Δlvecs, Δrvecs, - vals, lvecs, rvecs, - info, f, which, - alg_primal, alg_rrule::Union{GMRES, BiCGStab} - ) +function compute_svdsolve_pullback_data(Δvals, Δlvecs, Δrvecs, + vals, lvecs, rvecs, + info, f, which, + alg_primal, alg_rrule::Union{GMRES,BiCGStab}) xs = similar(lvecs, length(Δvals)) ys = similar(rvecs, length(Δvals)) for i in 1:length(vals) @@ -147,7 +137,7 @@ function compute_svdsolve_pullback_data( end end if info.converged >= i && reverse_info.converged == 0 && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`svdsolve` cotangent linear problem ($i) did not converge, whereas the primal eigenvalue problem did: normres = $(reverse_info.normres)" end x = VectorInterface.add!!(x, u, Δs / 2) @@ -157,12 +147,10 @@ function compute_svdsolve_pullback_data( end return xs, ys end -function compute_svdsolve_pullback_data( - Δvals, Δlvecs, Δrvecs, - vals, lvecs, rvecs, - info, f, which, - alg_primal, alg_rrule::Arnoldi - ) +function compute_svdsolve_pullback_data(Δvals, Δlvecs, Δrvecs, + vals, lvecs, rvecs, + info, f, which, + alg_primal, alg_rrule::Arnoldi) @assert which == :LR "pullback currently only implemented for `which == :LR`" T = scalartype(lvecs) n = length(Δvals) @@ -190,7 +178,7 @@ function compute_svdsolve_pullback_data( @warn "`svdsolve` cotangents for singular vectors are sensitive to gauge choice: (|gauge| = $gauge)" end UdΔAV = (aUdΔU .+ aVdΔV) .* safe_inv.(vals' .- vals, tol) .+ - (aUdΔU .- aVdΔV) .* safe_inv.(vals' .+ vals, tol) + (aUdΔU .- aVdΔV) .* safe_inv.(vals' .+ vals, tol) if !(Δvals isa ZeroTangent) UdΔAV[diagind(UdΔAV)] .+= real.(Δvals) end @@ -248,7 +236,7 @@ function compute_svdsolve_pullback_data( end end if info.converged >= n && reverse_info.converged < n && - alg_primal.verbosity >= WARN_LEVEL + alg_primal.verbosity >= WARN_LEVEL @warn "`svdsolve` cotangent problem did not converge, whereas the primal singular value problem did" end @@ -288,7 +276,7 @@ function construct∂f_svd(config, f, lvecs, rvecs, xs, ys) end return ∂f end -function construct∂f_svd(config, (f, fᴴ)::Tuple{Any, Any}, lvecs, rvecs, xs, ys) +function construct∂f_svd(config, (f, fᴴ)::Tuple{Any,Any}, lvecs, rvecs, xs, ys) config isa RuleConfig{>:HasReverseMode} || throw(ArgumentError("`svdsolve` reverse-mode AD requires AD engine that supports calling back into AD")) @@ -306,10 +294,8 @@ function construct∂f_svd(config, (f, fᴴ)::Tuple{Any, Any}, lvecs, rvecs, xs, end function construct∂f_svd(config, A::AbstractMatrix, lvecs, rvecs, xs, ys) if A isa StridedMatrix - return InplaceableThunk( - Ā -> _buildĀ_svd!(Ā, lvecs, rvecs, xs, ys), - @thunk(_buildĀ_svd!(zero(A), lvecs, rvecs, xs, ys)) - ) + return InplaceableThunk(Ā -> _buildĀ_svd!(Ā, lvecs, rvecs, xs, ys), + @thunk(_buildĀ_svd!(zero(A), lvecs, rvecs, xs, ys))) else return @thunk(ProjectTo(A)(_buildĀ_svd!(zero(A), lvecs, rvecs, xs, ys))) end diff --git a/src/KrylovKit.jl b/src/KrylovKit.jl index 27d8e615..ba14a7ef 100644 --- a/src/KrylovKit.jl +++ b/src/KrylovKit.jl @@ -36,7 +36,8 @@ export basis, rayleighquotient, residual, normres, rayleighextension export initialize, initialize!, expand!, shrink! export ClassicalGramSchmidt, ClassicalGramSchmidt2, ClassicalGramSchmidtIR export ModifiedGramSchmidt, ModifiedGramSchmidt2, ModifiedGramSchmidtIR -export LanczosIterator, BlockLanczosIterator, ArnoldiIterator, GKLIterator, BiArnoldiIterator +export LanczosIterator, BlockLanczosIterator, ArnoldiIterator, GKLIterator, + BiArnoldiIterator export CG, GMRES, BiCGStab, Lanczos, BlockLanczos, Arnoldi, GKL, GolubYe, LSMR, BiArnoldi export KrylovDefaults, EigSorter export RecursiveVec, InnerProductVec, Block @@ -85,7 +86,7 @@ function splitrange(r::OrdinalRange, n::Integer) outerlength = n return SplitRange(start, stp, stop, innerlength, outerlength1, outerlength) end -function Base.iterate(r::SplitRange, i = 1) +function Base.iterate(r::SplitRange, i=1) step = r.step if i <= r.outerlength1 offset = (i - 1) * (r.innerlength + 1) * step @@ -146,7 +147,7 @@ function checkposdef(z) error("operator does not appear to be positive definite: diagonal element $z") return r end -function checkhermitian(z, n = abs(z)) +function checkhermitian(z, n=abs(z)) imag(z) <= sqrt(max(eps(n), eps(one(n)))) || error("operator does not appear to be hermitian: diagonal element $z") return real(z) @@ -209,7 +210,7 @@ Used to return information about the solution found by the iterative method. - `numiter`: the number of iterations (sometimes called restarts) used by the algorithm. - `numops`: the number of times the linear map or operator was applied """ -struct ConvergenceInfo{S, T} +struct ConvergenceInfo{S,T} converged::Int # how many vectors have converged, 0 or 1 for linear systems, exponentiate, any integer for eigenvalue problems residual::T normres::S @@ -222,9 +223,8 @@ function Base.show(io::IO, info::ConvergenceInfo) info.converged == 0 && print(io, "no converged values ") info.converged == 1 && print(io, "one converged value ") info.converged > 1 && print(io, "$(info.converged) converged values ") - println( - io, "after ", info.numiter, " iterations and ", info.numops, " applications of the linear map;" - ) + println(io, "after ", info.numiter, " iterations and ", info.numops, + " applications of the linear map;") return print(io, "norms of residuals are given by ", normres2string(info.normres), ".") end @@ -245,13 +245,13 @@ include("innerproductvec.jl") # support for real _realinner(v, w) = real(inner(v, w)) -const RealVec{V} = InnerProductVec{typeof(_realinner), V} +const RealVec{V} = InnerProductVec{typeof(_realinner),V} RealVec(v) = InnerProductVec(v, _realinner) apply(A, x::RealVec) = RealVec(apply(A, x[])) -apply_normal(f::Tuple{Any, Any}, x::RealVec) = RealVec(apply_normal(f, x[])) -apply_adjoint(f::Tuple{Any, Any}, x::RealVec) = RealVec(apply_adjoint(f, x[])) +apply_normal(f::Tuple{Any,Any}, x::RealVec) = RealVec(apply_normal(f, x[])) +apply_adjoint(f::Tuple{Any,Any}, x::RealVec) = RealVec(apply_adjoint(f, x[])) apply_normal(f, x::RealVec) = RealVec(apply_normal(f, x[])) apply_adjoint(f, x::RealVec) = RealVec(apply_adjoint(f, x[])) diff --git a/src/algorithms.jl b/src/algorithms.jl index 6aa2cdcc..567f404d 100644 --- a/src/algorithms.jl +++ b/src/algorithms.jl @@ -61,7 +61,7 @@ reorthogonalization until the norm of the vector after an orthogonalization step decreased by a factor smaller than `η` with respect to the norm before the step. The default value corresponds to the Daniel-Gragg-Kaufman-Stewart condition. """ -struct ClassicalGramSchmidtIR{S <: Real} <: Reorthogonalizer +struct ClassicalGramSchmidtIR{S<:Real} <: Reorthogonalizer η::S end ClassicalGramSchmidtIR() = ClassicalGramSchmidtIR(1 / sqrt(2)) # Daniel-Gragg-Kaufman-Stewart @@ -74,7 +74,7 @@ reorthogonalization until the norm of the vector after an orthogonalization step decreased by a factor smaller than `η` with respect to the norm before the step. The default value corresponds to the Daniel-Gragg-Kaufman-Stewart condition. """ -struct ModifiedGramSchmidtIR{S <: Real} <: Reorthogonalizer +struct ModifiedGramSchmidtIR{S<:Real} <: Reorthogonalizer η::S end ModifiedGramSchmidtIR() = ModifiedGramSchmidtIR(1 / sqrt(2)) # Daniel-Gragg-Kaufman-Stewart @@ -107,7 +107,7 @@ Use `Arnoldi` for non-symmetric or non-Hermitian linear operators. See also: [Factorization types](@ref), [`eigsolve`](@ref), [`exponentiate`](@ref), [`Arnoldi`](@ref), [`Orthogonalizer`](@ref) """ -struct Lanczos{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct Lanczos{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -116,13 +116,12 @@ struct Lanczos{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function Lanczos(; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[]) return Lanczos(orth, krylovdim, maxiter, tol, eager, verbosity) end @@ -149,7 +148,7 @@ Use `Arnoldi` for non-symmetric or non-Hermitian linear operators. See also: [Factorization types](@ref), [`eigsolve`](@ref), [`Arnoldi`](@ref), [`Orthogonalizer`](@ref) """ -struct BlockLanczos{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct BlockLanczos{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -159,14 +158,13 @@ struct BlockLanczos{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function BlockLanczos(; - krylovdim::Int = KrylovDefaults.blockkrylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - qr_tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.blockkrylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + qr_tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[]) return BlockLanczos(orth, krylovdim, maxiter, promote(tol, qr_tol)..., eager, verbosity) end @@ -189,7 +187,7 @@ verbosity level `verbosity` amounts to printing warnings upon lack of convergenc See also: [`svdsolve`](@ref), [`Orthogonalizer`](@ref) """ -struct GKL{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct GKL{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -198,13 +196,12 @@ struct GKL{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function GKL(; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[]) return GKL(orth, krylovdim, maxiter, tol, eager, verbosity) end @@ -232,7 +229,7 @@ Use `Lanczos` for real symmetric or complex Hermitian linear operators. See also: [`eigsolve`](@ref), [`exponentiate`](@ref), [`Lanczos`](@ref), [`Orthogonalizer`](@ref) """ -struct Arnoldi{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct Arnoldi{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -241,13 +238,12 @@ struct Arnoldi{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function Arnoldi(; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[]) return Arnoldi(orth, krylovdim, maxiter, tol, eager, verbosity) end @@ -271,7 +267,7 @@ subspace of dimension `krylovdim` is constructed. The default verbosity level See also: [`bieigsolve`](@ref), [`Orthogonalizer`](@ref) """ -struct BiArnoldi{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct BiArnoldi{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -280,13 +276,12 @@ struct BiArnoldi{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function BiArnoldi(; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[]) return BiArnoldi(orth, krylovdim, maxiter, tol, eager, verbosity) end @@ -307,7 +302,7 @@ subspace will also be expanded to size `krylovdim+1` by adding ``x_k - x_{k-1}`` known as the LOPCG correction and was suggested by Money and Ye. With `krylovdim=2`, this algorithm becomes equivalent to `LOPCG`. """ -struct GolubYe{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm +struct GolubYe{O<:Orthogonalizer,S<:Real} <: KrylovAlgorithm orth::O krylovdim::Int maxiter::Int @@ -315,12 +310,11 @@ struct GolubYe{O <: Orthogonalizer, S <: Real} <: KrylovAlgorithm verbosity::Int end function GolubYe(; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + verbosity::Int=KrylovDefaults.verbosity[]) return GolubYe(orth, krylovdim, maxiter, tol, verbosity) end @@ -341,16 +335,15 @@ upon lack of convergence. See also: [`linsolve`](@ref), [`MINRES`](@ref), [`GMRES`](@ref), [`BiCG`](@ref), [`LSMR`](@ref), [`BiCGStab`](@ref) """ -struct CG{S <: Real} <: LinearSolver +struct CG{S<:Real} <: LinearSolver maxiter::Int tol::S verbosity::Int end function CG(; - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - verbosity::Int = KrylovDefaults.verbosity[] - ) + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + verbosity::Int=KrylovDefaults.verbosity[]) return CG(maxiter, tol, verbosity) end @@ -377,7 +370,7 @@ cycles. The total iteration count, i.e. the number of expansion steps, is roughl See also: [`linsolve`](@ref), [`BiCG`](@ref), [`BiCGStab`](@ref), [`CG`](@ref), [`LSMR`](@ref), [`MINRES`](@ref) """ -struct GMRES{O <: Orthogonalizer, S <: Real} <: LinearSolver +struct GMRES{O<:Orthogonalizer,S<:Real} <: LinearSolver orth::O maxiter::Int krylovdim::Int @@ -385,12 +378,11 @@ struct GMRES{O <: Orthogonalizer, S <: Real} <: LinearSolver verbosity::Int end function GMRES(; - krylovdim::Integer = KrylovDefaults.krylovdim[], - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Integer=KrylovDefaults.krylovdim[], + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + verbosity::Int=KrylovDefaults.verbosity[]) return GMRES(orth, maxiter, krylovdim, tol, verbosity) end @@ -412,16 +404,15 @@ end See also: [`linsolve`](@ref), [`CG`](@ref), [`GMRES`](@ref), [`BiCG`](@ref), [`LSMR`](@ref), [`BiCGStab`](@ref) """ -struct MINRES{S <: Real} <: LinearSolver +struct MINRES{S<:Real} <: LinearSolver maxiter::Int tol::S verbosity::Int end function MINRES(; - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - verbosity::Int = KrylovDefaults.verbosity[] - ) + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + verbosity::Int=KrylovDefaults.verbosity[]) return MINRES(maxiter, tol, verbosity) end @@ -441,16 +432,15 @@ end See also: [`linsolve`](@ref), [`GMRES`](@ref), [`CG`](@ref), [`BiCGStab`](@ref), [`LSMR`](@ref), [`MINRES`](@ref) """ -struct BiCG{S <: Real} <: LinearSolver +struct BiCG{S<:Real} <: LinearSolver maxiter::Int tol::S verbosity::Int end function BiCG(; - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - verbosity::Int = KrylovDefaults.verbosity[] - ) + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + verbosity::Int=KrylovDefaults.verbosity[]) return BiCG(maxiter, tol, verbosity) end @@ -466,16 +456,15 @@ end See also: [`linsolve`](@ref), [`GMRES`](@ref), [`CG`](@ref), [`BiCG`](@ref), [`LSMR`](@ref), [`MINRES`](@ref) """ -struct BiCGStab{S <: Real} <: LinearSolver +struct BiCGStab{S<:Real} <: LinearSolver maxiter::Int tol::S verbosity::Int end function BiCGStab(; - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - verbosity::Int = KrylovDefaults.verbosity[] - ) + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + verbosity::Int=KrylovDefaults.verbosity[]) return BiCGStab(maxiter, tol, verbosity) end @@ -503,7 +492,7 @@ The default verbosity level `verbosity` amounts to printing warnings upon lack o See also: [`lssolve`](@ref) """ -struct LSMR{O <: Orthogonalizer, S <: Real} <: LeastSquaresSolver +struct LSMR{O<:Orthogonalizer,S<:Real} <: LeastSquaresSolver orth::O maxiter::Int krylovdim::Int @@ -511,12 +500,11 @@ struct LSMR{O <: Orthogonalizer, S <: Real} <: LeastSquaresSolver verbosity::Int end function LSMR(; - krylovdim::Integer = KrylovDefaults.krylovdim[], - maxiter::Integer = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = ModifiedGramSchmidt(), - verbosity::Int = KrylovDefaults.verbosity[] - ) + krylovdim::Integer=KrylovDefaults.krylovdim[], + maxiter::Integer=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=ModifiedGramSchmidt(), + verbosity::Int=KrylovDefaults.verbosity[]) return LSMR(orth, maxiter, krylovdim, tol, verbosity) end @@ -554,11 +542,11 @@ A module listing the default values for the typical parameters in Krylov based a will not be attainable. """ module KrylovDefaults - using ..KrylovKit - const orth = KrylovKit.ModifiedGramSchmidt2() # conservative choice - const krylovdim = Ref(30) - const maxiter = Ref(100) - const blockkrylovdim = Ref(100) - const tol = Ref(1.0e-12) - const verbosity = Ref(KrylovKit.WARN_LEVEL) +using ..KrylovKit +const orth = KrylovKit.ModifiedGramSchmidt2() # conservative choice +const krylovdim = Ref(30) +const maxiter = Ref(100) +const blockkrylovdim = Ref(100) +const tol = Ref(1.0e-12) +const verbosity = Ref(KrylovKit.WARN_LEVEL) end diff --git a/src/apply.jl b/src/apply.jl index 89eca930..1ea7b1bd 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -13,13 +13,13 @@ end # GKL, SVD, LSMR apply_normal(A::AbstractMatrix, x::AbstractVector) = A * x apply_adjoint(A::AbstractMatrix, x::AbstractVector) = A' * x -apply_normal((f, fadjoint)::Tuple{Any, Any}, x) = f(x) -apply_adjoint((f, fadjoint)::Tuple{Any, Any}, x) = fadjoint(x) +apply_normal((f, fadjoint)::Tuple{Any,Any}, x) = f(x) +apply_adjoint((f, fadjoint)::Tuple{Any,Any}, x) = fadjoint(x) apply_normal(f, x) = f(x, Val(false)) apply_adjoint(f, x) = f(x, Val(true)) # generalized eigenvalue problem -genapply((A, B)::Tuple{Any, Any}, x) = (apply(A, x), apply(B, x)) +genapply((A, B)::Tuple{Any,Any}, x) = (apply(A, x), apply(B, x)) genapply(f, x) = f(x) # attempt type inference first but fall back to actual values if failed diff --git a/src/dense/linalg.jl b/src/dense/linalg.jl index 85de7f71..78e5ba74 100644 --- a/src/dense/linalg.jl +++ b/src/dense/linalg.jl @@ -1,7 +1,8 @@ # Some modified wrappers for Lapack import LinearAlgebra: BlasFloat, BlasInt, - LAPACKException, DimensionMismatch, SingularException, PosDefException, - chkstride1, checksquare + LAPACKException, DimensionMismatch, SingularException, + PosDefException, + chkstride1, checksquare import LinearAlgebra.BLAS: @blasfunc, BlasReal, BlasComplex import LinearAlgebra.LAPACK: chklapackerror @static if VERSION >= v"1.7" @@ -15,15 +16,15 @@ end else function require_one_based_indexing(A...) return !Base.has_offset_axes(A...) || - throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) + throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) end end -struct RowIterator{A <: AbstractMatrix, R <: IndexRange} +struct RowIterator{A<:AbstractMatrix,R<:IndexRange} a::A r::R end -rows(a::AbstractMatrix, r::IndexRange = axes(a, 1)) = RowIterator(a, r) +rows(a::AbstractMatrix, r::IndexRange=axes(a, 1)) = RowIterator(a, r) function Base.iterate(iter::RowIterator) next = iterate(iter.r) @@ -47,15 +48,15 @@ end Base.IteratorSize(::Type{<:RowIterator}) = Base.HasLength() Base.IteratorEltype(::Type{<:RowIterator}) = Base.HasEltype() Base.length(iter::RowIterator) = length(iter.r) -function Base.eltype(iter::RowIterator{A}) where {T, A <: DenseArray{T}} - return SubArray{T, 1, A, Tuple{Int, Base.Slice{Base.OneTo{Int}}}, true} +function Base.eltype(iter::RowIterator{A}) where {T,A<:DenseArray{T}} + return SubArray{T,1,A,Tuple{Int,Base.Slice{Base.OneTo{Int}}},true} end -struct ColumnIterator{A <: AbstractMatrix, R <: IndexRange} +struct ColumnIterator{A<:AbstractMatrix,R<:IndexRange} a::A r::R end -cols(a::AbstractMatrix, r::IndexRange = axes(a, 2)) = ColumnIterator(a, r) +cols(a::AbstractMatrix, r::IndexRange=axes(a, 2)) = ColumnIterator(a, r) function Base.iterate(iter::ColumnIterator) next = iterate(iter.r) @@ -79,8 +80,8 @@ end Base.IteratorSize(::Type{<:ColumnIterator}) = Base.HasLength() Base.IteratorEltype(::Type{<:ColumnIterator}) = Base.HasEltype() Base.length(iter::ColumnIterator) = length(iter.r) -function Base.eltype(iter::ColumnIterator{A}) where {T, A <: DenseArray{T}} - return SubArray{T, 1, A, Tuple{Base.Slice{Base.OneTo{Int}}, Int}, true} +function Base.eltype(iter::ColumnIterator{A}) where {T,A<:DenseArray{T}} + return SubArray{T,1,A,Tuple{Base.Slice{Base.OneTo{Int}},Int},true} end # # QR decomposition @@ -93,7 +94,7 @@ end # end # Triangular division: for some reason this is faster than LAPACK's trsv -function ldiv!(A::UpperTriangular, y::AbstractVector, r::UnitRange{Int} = 1:length(y)) +function ldiv!(A::UpperTriangular, y::AbstractVector, r::UnitRange{Int}=1:length(y)) R = A.data @inbounds for j in reverse(r) R[j, j] == zero(R[j, j]) && throw(SingularException(j)) @@ -106,25 +107,23 @@ function ldiv!(A::UpperTriangular, y::AbstractVector, r::UnitRange{Int} = 1:leng end # Eigenvalue decomposition of SymTridiagonal matrix -function tridiageigh!(A::SymTridiagonal{T}) where {T <: BlasFloat} +function tridiageigh!(A::SymTridiagonal{T}) where {T<:BlasFloat} Z = copyto!(similar(A.ev, size(A)), LinearAlgebra.I) return tridiageigh!(A, Z) end -function tridiageigh!(A::SymTridiagonal{T}, Z::StridedMatrix{T}) where {T <: BlasFloat} +function tridiageigh!(A::SymTridiagonal{T}, Z::StridedMatrix{T}) where {T<:BlasFloat} return stegr!(A.dv, A.ev, Z) end # redefined # Generalized eigenvalue decomposition of symmetric / Hermitian problem -function geneigh!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T <: BlasFloat} +function geneigh!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} return LAPACK.sygvd!(1, 'V', 'U', A, B) end # Singular value decomposition of a Bidiagonal matrix -function bidiagsvd!( - B::Bidiagonal{T}, - U::AbstractMatrix{T} = one(B), - VT::AbstractMatrix{T} = one(B) - ) where {T <: BlasReal} +function bidiagsvd!(B::Bidiagonal{T}, + U::AbstractMatrix{T}=one(B), + VT::AbstractMatrix{T}=one(B)) where {T<:BlasReal} s, Vt, U = LAPACK.bdsqr!(B.uplo, B.dv, B.ev, VT, U, similar(U, (size(B, 1), 0))) return U, s, Vt end @@ -149,7 +148,7 @@ function reverserows!(V::AbstractVecOrMat) end # Schur factorization of a Hessenberg matrix -function hschur!(H::AbstractMatrix{T}, Z::AbstractMatrix{T} = one(H)) where {T <: BlasFloat} +function hschur!(H::AbstractMatrix{T}, Z::AbstractMatrix{T}=one(H)) where {T<:BlasFloat} return hseqr!(H, Z) end @@ -335,9 +334,8 @@ end function permuteschur!(T::AbstractMatrix{<:BlasFloat}, p::AbstractVector{Int}) return permuteschur!(T, one(T), p) end -function permuteschur!( - T::AbstractMatrix{S}, Q::AbstractMatrix{S}, order::AbstractVector{Int} - ) where {S <: BlasComplex} +function permuteschur!(T::AbstractMatrix{S}, Q::AbstractMatrix{S}, + order::AbstractVector{Int}) where {S<:BlasComplex} n = checksquare(T) p = collect(order) # makes copy cause will be overwritten @inbounds for i in 1:length(p) @@ -353,9 +351,8 @@ function permuteschur!( return T, Q, schur2eigvals(T) end -function permuteschur!( - T::AbstractMatrix{S}, Q::AbstractMatrix{S}, order::AbstractVector{Int} - ) where {S <: BlasReal} +function permuteschur!(T::AbstractMatrix{S}, Q::AbstractMatrix{S}, + order::AbstractVector{Int}) where {S<:BlasReal} n = checksquare(T) p = collect(order) # makes copy cause will be overwritten i = 1 @@ -385,9 +382,8 @@ function permuteschur!( return T, Q, schur2eigvals(T) end -function partitionschur!( - T::AbstractMatrix{S}, Q::AbstractMatrix{S}, select::AbstractVector{Bool} - ) where {S <: BlasFloat} +function partitionschur!(T::AbstractMatrix{S}, Q::AbstractMatrix{S}, + select::AbstractVector{Bool}) where {S<:BlasFloat} T, Q, vals = trsen!('N', 'V', convert(Vector{BlasInt}, select), T, Q) return T, Q, vals end @@ -395,9 +391,8 @@ end # redefine LAPACK interface to tridiagonal eigenvalue problem for (stegr, elty) in ((:dstegr_, :Float64), (:sstegr_, :Float32)) @eval begin - function stegr!( - dv::AbstractVector{$elty}, ev::AbstractVector{$elty}, Z::AbstractMatrix{$elty} - ) + function stegr!(dv::AbstractVector{$elty}, ev::AbstractVector{$elty}, + Z::AbstractMatrix{$elty}) require_one_based_indexing(dv, ev, Z) chkstride1(dv, ev, Z) n = length(dv) @@ -426,24 +421,20 @@ for (stegr, elty) in ((:dstegr_, :Float64), (:sstegr_, :Float32)) liwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall( - (@blasfunc($stegr), liblapack), - Cvoid, - ( - Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{$elty}, Ref{$elty}, - Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong, - ), - jobz, range, n, - dv, eev, vl, vu, - il, iu, abstol, m, - w, Z, ldz, - isuppz, work, lwork, iwork, liwork, - info, 1, 1 - ) + ccall((@blasfunc($stegr), liblapack), + Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{$elty}, Ref{$elty}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{BlasInt}, Clong, Clong), + jobz, range, n, + dv, eev, vl, vu, + il, iu, abstol, m, + w, Z, ldz, + isuppz, work, lwork, iwork, liwork, + info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -461,7 +452,7 @@ end for (hseqr, trevc, trsen, elty) in ((:dhseqr_, :dtrevc_, :dtrsen_, :Float64), (:shseqr_, :strevc_, :strsen_, :Float32)) @eval begin - function hseqr!(H::StridedMatrix{$elty}, Z::StridedMatrix{$elty} = one(H)) + function hseqr!(H::StridedMatrix{$elty}, Z::StridedMatrix{$elty}=one(H)) require_one_based_indexing(H, Z) chkstride1(H, Z) n = checksquare(H) @@ -478,18 +469,15 @@ for (hseqr, trevc, trsen, elty) in lwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] - ccall( - (@blasfunc($hseqr), liblapack), - Cvoid, - ( - Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, - ), - job, compz, n, ilo, ihi, - H, ldh, wr, wi, Z, ldz, - work, lwork, info, 1, 1 - ) + ccall((@blasfunc($hseqr), liblapack), + Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + job, compz, n, ilo, ihi, + H, ldh, wr, wi, Z, ldz, + work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -498,17 +486,16 @@ for (hseqr, trevc, trsen, elty) in end return H, Z, complex.(wr, wi) end - function trevc!( - side::Char, howmny::Char, select::StridedVector{BlasInt}, - T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty}, VR::AbstractMatrix{$elty} - ) + function trevc!(side::Char, howmny::Char, select::StridedVector{BlasInt}, + T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty}, + VR::AbstractMatrix{$elty}) # Extract if side ∉ ['L', 'R', 'B'] throw(ArgumentError("side argument must be 'L' (left eigenvectors), 'R' (right eigenvectors), or 'B' (both), got $side")) end n = checksquare(T) mm = side == 'L' ? size(VL, 2) : - (side == 'R' ? size(VR, 2) : min(size(VL, 2), size(VR, 2))) + (side == 'R' ? size(VR, 2) : min(size(VL, 2), size(VR, 2))) ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) # Check @@ -519,26 +506,22 @@ for (hseqr, trevc, trsen, elty) in work = Vector{$elty}(undef, 3n) info = Ref{BlasInt}() - ccall( - (@blasfunc($trevc), liblapack), - Cvoid, - ( - Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong, - ), - side, howmny, select, n, - T, ldt, VL, ldvl, VR, ldvr, - mm, m, work, info, 1, 1 - ) + ccall((@blasfunc($trevc), liblapack), + Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), + side, howmny, select, n, + T, ldt, VL, ldvl, VR, ldvr, + mm, m, work, info, 1, 1) chklapackerror(info[]) return VL, VR, m end - function trsen!( - job::AbstractChar, compq::AbstractChar, select::AbstractVector{BlasInt}, - T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty} - ) + function trsen!(job::AbstractChar, compq::AbstractChar, + select::AbstractVector{BlasInt}, + T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) chkstride1(T, Q, select) n = checksquare(T) checksquare(Q) == n || throw(DimensionMismatch()) @@ -557,21 +540,17 @@ for (hseqr, trevc, trsen, elty) in s = Ref{$elty}(zero($elty)) sep = Ref{$elty}(zero($elty)) for i in 1:2 # first call returns lwork as work[1] and liwork as iwork[1] - ccall( - (@blasfunc($trsen), liblapack), Cvoid, - ( - Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, - Ptr{BlasInt}, Clong, Clong, - ), - job, compq, select, n, - T, ldt, Q, ldq, - wr, wi, m, s, sep, - work, lwork, iwork, liwork, - info, 1, 1 - ) + ccall((@blasfunc($trsen), liblapack), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{BlasInt}, Clong, Clong), + job, compq, select, n, + T, ldt, Q, ldq, + wr, wi, m, s, sep, + work, lwork, iwork, liwork, + info, 1, 1) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) @@ -586,12 +565,10 @@ for (hseqr, trevc, trsen, elty) in end for (hseqr, trevc, trsen, elty, relty) in - ( - (:zhseqr_, :ztrevc_, :ztrsen_, :ComplexF64, :Float64), - (:chseqr_, :ctrevc_, :ctrsen_, :ComplexF32, :Float32), - ) + ((:zhseqr_, :ztrevc_, :ztrsen_, :ComplexF64, :Float64), + (:chseqr_, :ctrevc_, :ctrsen_, :ComplexF32, :Float32)) @eval begin - function hseqr!(H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty} = one(H)) + function hseqr!(H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}=one(H)) require_one_based_indexing(H, Z) chkstride1(H, Z) n = checksquare(H) @@ -607,18 +584,14 @@ for (hseqr, trevc, trsen, elty, relty) in lwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] - ccall( - (@blasfunc($hseqr), liblapack), - Cvoid, - ( - Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, - ), - job, compz, n, ilo, ihi, - H, ldh, w, Z, ldz, - work, lwork, info, 1, 1 - ) + ccall((@blasfunc($hseqr), liblapack), + Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + job, compz, n, ilo, ihi, + H, ldh, w, Z, ldz, + work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -627,10 +600,9 @@ for (hseqr, trevc, trsen, elty, relty) in end return H, Z, w end - function trevc!( - side::Char, howmny::Char, select::AbstractVector{BlasInt}, - T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty} = similar(T), VR::AbstractMatrix{$elty} = similar(T) - ) + function trevc!(side::Char, howmny::Char, select::AbstractVector{BlasInt}, + T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty}=similar(T), + VR::AbstractMatrix{$elty}=similar(T)) # Check require_one_based_indexing(select, T, VL, VR) @@ -642,7 +614,7 @@ for (hseqr, trevc, trsen, elty, relty) in end n = checksquare(T) mm = side == 'L' ? size(VL, 2) : - (side == 'R' ? size(VR, 2) : min(size(VL, 2), size(VR, 2))) + (side == 'R' ? size(VR, 2) : min(size(VL, 2), size(VR, 2))) ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) # Allocate @@ -650,57 +622,51 @@ for (hseqr, trevc, trsen, elty, relty) in work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, n) info = Ref{BlasInt}() - ccall( - (@blasfunc($trevc), liblapack), - Cvoid, - ( - Ref{UInt8}, - Ref{UInt8}, - Ptr{BlasInt}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ref{BlasInt}, - Ptr{BlasInt}, - Ptr{$elty}, - Ptr{$relty}, - Ptr{BlasInt}, - Clong, - Clong, - ), - side, - howmny, - select, - n, - T, - ldt, - VL, - ldvl, - VR, - ldvr, - mm, - m, - work, - rwork, - info, - 1, - 1 - ) + ccall((@blasfunc($trevc), liblapack), + Cvoid, + (Ref{UInt8}, + Ref{UInt8}, + Ptr{BlasInt}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ref{BlasInt}, + Ptr{BlasInt}, + Ptr{$elty}, + Ptr{$relty}, + Ptr{BlasInt}, + Clong, + Clong), + side, + howmny, + select, + n, + T, + ldt, + VL, + ldvl, + VR, + ldvr, + mm, + m, + work, + rwork, + info, + 1, + 1) chklapackerror(info[]) return VL, VR, m end - function trsen!( - job::Char, - compq::Char, - select::AbstractVector{BlasInt}, - T::AbstractMatrix{$elty}, - Q::AbstractMatrix{$elty} - ) + function trsen!(job::Char, + compq::Char, + select::AbstractVector{BlasInt}, + T::AbstractMatrix{$elty}, + Q::AbstractMatrix{$elty}) chkstride1(select, T, Q) n = checksquare(T) ldt = max(1, stride(T, 2)) @@ -714,42 +680,38 @@ for (hseqr, trevc, trsen, elty, relty) in s = Ref{$relty}(zero($relty)) sep = Ref{$relty}(zero($relty)) for i in 1:2 # first call returns lwork as work[1] - ccall( - (@blasfunc($trsen), liblapack), - Nothing, - ( - Ref{UInt8}, - Ref{UInt8}, - Ptr{BlasInt}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ptr{$elty}, - Ref{BlasInt}, - Ref{$relty}, - Ref{$relty}, - Ptr{$elty}, - Ref{BlasInt}, - Ptr{BlasInt}, - ), - job, - compq, - select, - n, - T, - ldt, - Q, - ldq, - w, - m, - s, - sep, - work, - lwork, - info - ) + ccall((@blasfunc($trsen), liblapack), + Nothing, + (Ref{UInt8}, + Ref{UInt8}, + Ptr{BlasInt}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ptr{$elty}, + Ref{BlasInt}, + Ref{$relty}, + Ref{$relty}, + Ptr{$elty}, + Ref{BlasInt}, + Ptr{BlasInt}), + job, + compq, + select, + n, + T, + ldt, + Q, + ldq, + w, + m, + s, + sep, + work, + lwork, + info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) diff --git a/src/dense/packedhessenberg.jl b/src/dense/packedhessenberg.jl index 535f9370..ede6bed0 100644 --- a/src/dense/packedhessenberg.jl +++ b/src/dense/packedhessenberg.jl @@ -7,25 +7,23 @@ A custom struct to store a Hessenberg matrix in a packed format (without zeros). Hereto, the non-zero entries are stored sequentially in vector `data` of length `n(n+1)/2`. """ -struct PackedHessenberg{T, V <: AbstractVector{T}} <: AbstractMatrix{T} +struct PackedHessenberg{T,V<:AbstractVector{T}} <: AbstractMatrix{T} data::V n::Int - function PackedHessenberg{T, V}(data::V, n::Int) where {T, V <: AbstractVector{T}} + function PackedHessenberg{T,V}(data::V, n::Int) where {T,V<:AbstractVector{T}} @assert length(data) >= ((n * n + 3 * n - 2) >> 1) - return new{T, V}(data, n) + return new{T,V}(data, n) end end function PackedHessenberg(data::AbstractVector, n::Int) - return PackedHessenberg{eltype(data), typeof(data)}(data, n) + return PackedHessenberg{eltype(data),typeof(data)}(data, n) end Base.size(A::PackedHessenberg) = (A.n, A.n) -function Base.replace_in_print_matrix( - A::PackedHessenberg, - i::Integer, - j::Integer, - s::AbstractString - ) +function Base.replace_in_print_matrix(A::PackedHessenberg, + i::Integer, + j::Integer, + s::AbstractString) return i <= j + 1 ? s : Base.replace_with_centered_mark(s) end diff --git a/src/dense/reflector.jl b/src/dense/reflector.jl index 2312a0f8..ec866f7d 100644 --- a/src/dense/reflector.jl +++ b/src/dense/reflector.jl @@ -1,5 +1,5 @@ # Elementary Householder reflection -struct Householder{T, V <: AbstractVector, R <: IndexRange} +struct Householder{T,V<:AbstractVector,R<:IndexRange} β::T v::V r::R @@ -7,21 +7,21 @@ end Base.adjoint(H::Householder) = Householder(conj(H.β), H.v, H.r) -function householder(x::AbstractVector, r::IndexRange = axes(x, 1), k = first(r)) +function householder(x::AbstractVector, r::IndexRange=axes(x, 1), k=first(r)) i = findfirst(isequal(k), r) i isa Nothing && error("k = $k should be in the range r = $r") β, v, ν = _householder!(x[r], i) return Householder(β, v, r), ν end # Householder reflector h that zeros the elements A[r,col] (except for A[k,col]) upon lmul!(A,h) -function householder(A::AbstractMatrix, r::IndexRange, col::Int, k = first(r)) +function householder(A::AbstractMatrix, r::IndexRange, col::Int, k=first(r)) i = findfirst(isequal(k), r) i isa Nothing && error("k = $k should be in the range r = $r") β, v, ν = _householder!(A[r, col], i) return Householder(β, v, r), ν end # Householder reflector that zeros the elements A[row,r] (except for A[row,k]) upon rmul!(A,h') -function householder(A::AbstractMatrix, row::Int, r::IndexRange, k = first(r)) +function householder(A::AbstractMatrix, row::Int, r::IndexRange, k=first(r)) i = findfirst(isequal(k), r) i isa Nothing && error("k = $k should be in the range r = $r") β, v, ν = _householder!(conj!(A[row, r]), i) @@ -86,7 +86,7 @@ function LinearAlgebra.lmul!(H::Householder, x::AbstractVector) end return x end -function LinearAlgebra.lmul!(H::Householder, A::AbstractMatrix, cols = axes(A, 2)) +function LinearAlgebra.lmul!(H::Householder, A::AbstractMatrix, cols=axes(A, 2)) v = H.v r = H.r β = H.β @@ -109,7 +109,7 @@ function LinearAlgebra.lmul!(H::Householder, A::AbstractMatrix, cols = axes(A, 2 end return A end -function LinearAlgebra.rmul!(A::AbstractMatrix, H::Householder, rows = axes(A, 1)) +function LinearAlgebra.rmul!(A::AbstractMatrix, H::Householder, rows=axes(A, 1)) v = H.v r = H.r β = H.β diff --git a/src/eigsolve/arnoldi.jl b/src/eigsolve/arnoldi.jl index 8469f772..8ddccff2 100644 --- a/src/eigsolve/arnoldi.jl +++ b/src/eigsolve/arnoldi.jl @@ -141,10 +141,10 @@ function schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) * number of operations = $numops""" end return TT, vectors, values, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end -function eigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_rrule = alg) +function eigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_rrule=alg) T, U, fact, converged, numiter, numops = _schursolve(A, x₀, howmany, which, alg) howmany′ = howmany if eltype(T) <: Real && howmany < length(fact) && T[howmany + 1, howmany] != 0 @@ -180,7 +180,7 @@ function eigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_rrul * number of operations = $numops""" end return values, vectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end """ @@ -290,10 +290,9 @@ The return value is always of the form `vals, vecs, info = eigsolve(...)` with No warning is printed if not all requested eigenvalues were converged, so always check if `info.converged >= howmany`. """ -function realeigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_rrule = alg) - T, U, fact, converged, numiter, numops = _schursolve( - A, RealVec(x₀), howmany, which, alg - ) +function realeigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_rrule=alg) + T, U, fact, converged, numiter, numops = _schursolve(A, RealVec(x₀), howmany, which, + alg) i = 0 while i < howmany i += 1 @@ -345,7 +344,7 @@ function realeigsolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi; alg_ * number of operations = $numops""" end return values, vectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) @@ -358,7 +357,7 @@ function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) numiter = 1 # initialize arnoldi factorization iter = ArnoldiIterator(A, x₀, alg.orth) - fact = initialize(iter; verbosity = alg.verbosity) + fact = initialize(iter; verbosity=alg.verbosity) numops = 1 sizehint!(fact, krylovdim) β = normres(fact) @@ -394,7 +393,7 @@ function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) # compute dense schur factorization T, U, values = hschur!(H, U) by, rev = eigsort(which) - p = sortperm(values; by = by, rev = rev) + p = sortperm(values; by=by, rev=rev) T, U = permuteschur!(T, U, p) f .= conj.(view(U, K, :)) .* β converged = 0 @@ -413,7 +412,7 @@ function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) end if K < krylovdim # expand - fact = expand!(iter, fact; verbosity = alg.verbosity) + fact = expand!(iter, fact; verbosity=alg.verbosity) numops += 1 else # shrink numiter == maxiter && break @@ -444,7 +443,7 @@ function _schursolve(A, x₀, howmany::Int, which::Selector, alg::Arnoldi) basistransform!(B, view(U, :, 1:keep)) B[keep + 1] = scale!!(residual(fact), 1 / β) # Everything is set up to shrink Arnoldi factorization - fact = shrink!(fact, keep; verbosity = alg.verbosity) + fact = shrink!(fact, keep; verbosity=alg.verbosity) numiter += 1 end end diff --git a/src/eigsolve/biarnoldi.jl b/src/eigsolve/biarnoldi.jl index d66d9dcc..0afc7903 100644 --- a/src/eigsolve/biarnoldi.jl +++ b/src/eigsolve/biarnoldi.jl @@ -101,35 +101,28 @@ See also: [`eigsolve`](@ref), [`realeigsolve`](@ref), [`BiArnoldi`](@ref) """ function bieigsolve end -function bieigsolve( - A::AbstractMatrix, howmany::Int = 1, which::Selector = :LM, T::Type = eltype(A); - kwargs... - ) +function bieigsolve(A::AbstractMatrix, howmany::Int=1, which::Selector=:LM, + T::Type=eltype(A); + kwargs...) v₀ = Random.rand!(similar(A, T, size(A, 1))) w₀ = Random.rand!(similar(A, T, size(A, 1))) return bieigsolve(A, v₀, w₀, howmany, which; kwargs...) end -function bieigsolve( - f, n::Int, howmany::Int = 1, which::Selector = :LM, T::Type = Float64; - kwargs... - ) +function bieigsolve(f, n::Int, howmany::Int=1, which::Selector=:LM, T::Type=Float64; + kwargs...) return bieigsolve(f, rand(T, n), rand(T, n), howmany, which; kwargs...) end -function bieigsolve( - f, v₀, w₀, howmany::Int = 1, which::Selector = :LM; - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - eager::Bool = false, - tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - verbosity::Int = KrylovDefaults.verbosity[] - ) - return bieigsolve( - f, v₀, w₀, howmany, which, - BiArnoldi(; orth, krylovdim, maxiter, tol, eager, verbosity) - ) +function bieigsolve(f, v₀, w₀, howmany::Int=1, which::Selector=:LM; + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + eager::Bool=false, + tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + verbosity::Int=KrylovDefaults.verbosity[]) + return bieigsolve(f, v₀, w₀, howmany, which, + BiArnoldi(; orth, krylovdim, maxiter, tol, eager, verbosity)) end function bieigsolve(f, v₀, w₀, howmany::Int, which::Selector, alg::BiArnoldi) @@ -209,7 +202,7 @@ function _bischursolve(f, v₀, w₀, howmany::Int, which::Selector, alg::BiArno numiter = 1 # initialize arnoldi factorization iter = BiArnoldiIterator(f, v₀, w₀, alg.orth) - fact = initialize(iter; verbosity = alg.verbosity) + fact = initialize(iter; verbosity=alg.verbosity) numops = 1 sizehint!(fact, krylovdim) βv, βw = normres(fact) @@ -307,8 +300,8 @@ function _bischursolve(f, v₀, w₀, howmany::Int, which::Selector, alg::BiArno # Step 6 - Order the Schur decompositions by, rev = eigsort(which) - pH = sortperm(valuesH; by = by, rev = rev) - pK = sortperm(valuesK; by = by ∘ conj, rev = rev) + pH = sortperm(valuesH; by=by, rev=rev) + pK = sortperm(valuesK; by=by ∘ conj, rev=rev) S, Q = permuteschur!(S, Q, pH) T, Z = permuteschur!(T, Z, pK) @@ -347,7 +340,7 @@ function _bischursolve(f, v₀, w₀, howmany::Int, which::Selector, alg::BiArno end if L < krylovdim # expand - fact = expand!(iter, fact; verbosity = alg.verbosity) + fact = expand!(iter, fact; verbosity=alg.verbosity) V, W = basis(fact) # update M with the new basis vectors @@ -440,7 +433,7 @@ function _bischursolve(f, v₀, w₀, howmany::Int, which::Selector, alg::BiArno copy!(view(MM, 1:keep, 1:keep), ZMQ) # Shrink BiArnoldi factorization - fact = shrink!(fact, keep; verbosity = alg.verbosity) + fact = shrink!(fact, keep; verbosity=alg.verbosity) numiter += 1 end end diff --git a/src/eigsolve/blocklanczos.jl b/src/eigsolve/blocklanczos.jl index 769b2313..a50022e6 100644 --- a/src/eigsolve/blocklanczos.jl +++ b/src/eigsolve/blocklanczos.jl @@ -1,13 +1,10 @@ -function eigsolve( - A, x₀::Block{T}, howmany::Int, which::Selector, alg::BlockLanczos; - alg_rrule = Arnoldi(; - tol = alg.tol, - krylovdim = alg.krylovdim, - maxiter = alg.maxiter, - eager = alg.eager, - orth = alg.orth - ) - ) where {T} +function eigsolve(A, x₀::Block{T}, howmany::Int, which::Selector, alg::BlockLanczos; + alg_rrule=Arnoldi(; + tol=alg.tol, + krylovdim=alg.krylovdim, + maxiter=alg.maxiter, + eager=alg.eager, + orth=alg.orth)) where {T} maxiter = alg.maxiter krylovdim = alg.krylovdim if howmany > krylovdim @@ -18,7 +15,7 @@ function eigsolve( bs = length(x₀) iter = BlockLanczosIterator(A, x₀, krylovdim + bs, alg.orth, alg.qr_tol) - fact = initialize(iter; verbosity = verbosity) # Returns a BlockLanczosFactorization + fact = initialize(iter; verbosity=verbosity) # Returns a BlockLanczosFactorization numops = bs + 1 # Number of matrix-vector multiplications (for logging) numiter = 1 @@ -41,7 +38,7 @@ function eigsolve( BTD = view(fact.H, 1:K, 1:K) D, U = eigen(Hermitian(BTD)) by, rev = eigsort(which) - p = sortperm(D; by = by, rev = rev) + p = sortperm(D; by=by, rev=rev) D, U = permuteeig!(D, U, p) # Detect convergence by computing the residuals @@ -63,7 +60,7 @@ function eigsolve( end if K < krylovdim - expand!(iter, fact; verbosity = verbosity) + expand!(iter, fact; verbosity=verbosity) numops += fact.R_size else # Shrink and restart following the shrinking method of `Lanczos`. numiter >= maxiter && break @@ -140,5 +137,5 @@ function eigsolve( end return values, vectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end diff --git a/src/eigsolve/eigsolve.jl b/src/eigsolve/eigsolve.jl index 0e3e46fc..d8051cf5 100644 --- a/src/eigsolve/eigsolve.jl +++ b/src/eigsolve/eigsolve.jl @@ -188,27 +188,23 @@ struct EigSorter{F} by::F rev::Bool end -EigSorter(f::F; rev = false) where {F} = EigSorter{F}(f, rev) +EigSorter(f::F; rev=false) where {F} = EigSorter{F}(f, rev) -const Selector = Union{Symbol, EigSorter} +const Selector = Union{Symbol,EigSorter} -function eigsolve( - A::AbstractMatrix, howmany::Int = 1, which::Selector = :LM, T::Type = eltype(A); - kwargs... - ) +function eigsolve(A::AbstractMatrix, howmany::Int=1, which::Selector=:LM, T::Type=eltype(A); + kwargs...) x₀ = Random.rand!(similar(A, T, size(A, 1))) return eigsolve(A, x₀, howmany, which; kwargs...) end -function eigsolve( - f, n::Int, howmany::Int = 1, which::Selector = :LM, T::Type = Float64; - kwargs... - ) +function eigsolve(f, n::Int, howmany::Int=1, which::Selector=:LM, T::Type=Float64; + kwargs...) return eigsolve(f, rand(T, n), howmany, which; kwargs...) end -function eigsolve(f, x₀, howmany::Int = 1, which::Selector = :LM; kwargs...) +function eigsolve(f, x₀, howmany::Int=1, which::Selector=:LM; kwargs...) T = apply_scalartype(f, x₀) - alg = eigselector(f, T; Tx = typeof(x₀), kwargs...) + alg = eigselector(f, T; Tx=typeof(x₀), kwargs...) checkwhich(which) || error("Unknown eigenvalue selector: which = $which") if alg isa Lanczos || alg isa BlockLanczos if which == :LI || which == :SI @@ -225,106 +221,97 @@ function eigsolve(f, x₀, howmany::Int = 1, which::Selector = :LM; kwargs...) alg_rrule = kwargs[:alg_rrule] else alg_rrule = Arnoldi(; - tol = alg.tol, - krylovdim = alg.krylovdim, - maxiter = alg.maxiter, - eager = alg.eager, - orth = alg.orth - ) + tol=alg.tol, + krylovdim=alg.krylovdim, + maxiter=alg.maxiter, + eager=alg.eager, + orth=alg.orth) end - return eigsolve(f, x₀, howmany, which, alg; alg_rrule = alg_rrule) + return eigsolve(f, x₀, howmany, which, alg; alg_rrule=alg_rrule) end -function eigselector( - f, T::Type; - Tx::Type = Nothing, - issymmetric::Bool = false, - ishermitian::Bool = issymmetric && (T <: Real), - krylovdim::Int = Tx <: Block ? KrylovDefaults.blockkrylovdim[] : KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - qr_tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[], - alg_rrule = nothing - ) +function eigselector(f, T::Type; + Tx::Type=Nothing, + issymmetric::Bool=false, + ishermitian::Bool=issymmetric && (T <: Real), + krylovdim::Int=Tx <: Block ? KrylovDefaults.blockkrylovdim[] : + KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + qr_tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[], + alg_rrule=nothing) if Tx <: Block !(issymmetric || ishermitian) && error("BlockLanczos requires a symmetric or hermitian linear map. A BlockArnoldi method has not yet been implemented but will be in the future") return BlockLanczos(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - qr_tol = qr_tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + qr_tol=qr_tol, + orth=orth, + eager=eager, + verbosity=verbosity) elseif (T <: Real && issymmetric) || ishermitian return Lanczos(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + eager=eager, + verbosity=verbosity) else return Arnoldi(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + eager=eager, + verbosity=verbosity) end end -function eigselector( - A::AbstractMatrix, T::Type; - Tx::Type = Nothing, - issymmetric::Bool = (T <: Real && LinearAlgebra.issymmetric(A)), - ishermitian::Bool = issymmetric || LinearAlgebra.ishermitian(A), - krylovdim::Int = Tx <: Block ? KrylovDefaults.blockkrylovdim[] : KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - tol::Real = KrylovDefaults.tol[], - qr_tol::Real = KrylovDefaults.tol[], - orth::Orthogonalizer = KrylovDefaults.orth, - eager::Bool = false, - verbosity::Int = KrylovDefaults.verbosity[], - alg_rrule = nothing - ) +function eigselector(A::AbstractMatrix, T::Type; + Tx::Type=Nothing, + issymmetric::Bool=(T <: Real && LinearAlgebra.issymmetric(A)), + ishermitian::Bool=issymmetric || LinearAlgebra.ishermitian(A), + krylovdim::Int=Tx <: Block ? KrylovDefaults.blockkrylovdim[] : + KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + tol::Real=KrylovDefaults.tol[], + qr_tol::Real=KrylovDefaults.tol[], + orth::Orthogonalizer=KrylovDefaults.orth, + eager::Bool=false, + verbosity::Int=KrylovDefaults.verbosity[], + alg_rrule=nothing) if Tx <: Block !(issymmetric || ishermitian) && error("BlockLanczos requires a symmetric or hermitian linear map. A BlockArnoldi method has not yet been implemented but will be in the future") return BlockLanczos(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - qr_tol = qr_tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + qr_tol=qr_tol, + orth=orth, + eager=eager, + verbosity=verbosity) elseif (T <: Real && issymmetric) || ishermitian return Lanczos(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + eager=eager, + verbosity=verbosity) else return Arnoldi(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - eager = eager, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + eager=eager, + verbosity=verbosity) end end diff --git a/src/eigsolve/geneigsolve.jl b/src/eigsolve/geneigsolve.jl index 52bf9b58..c49ff1b9 100644 --- a/src/eigsolve/geneigsolve.jl +++ b/src/eigsolve/geneigsolve.jl @@ -145,48 +145,40 @@ equivalent to `LOPCG`. """ function geneigsolve end -function geneigsolve( - AB::Tuple{AbstractMatrix, AbstractMatrix}, - howmany::Int = 1, - which::Selector = :LM, - T = promote_type(eltype.(AB)...); - kwargs... - ) +function geneigsolve(AB::Tuple{AbstractMatrix,AbstractMatrix}, + howmany::Int=1, + which::Selector=:LM, + T=promote_type(eltype.(AB)...); + kwargs...) if !(size(AB[1], 1) == size(AB[1], 2) == size(AB[2], 1) == size(AB[2], 2)) throw(DimensionMismatch("Matrices `A` and `B` should be square and have matching size")) end x₀ = Random.rand!(similar(AB[1], T, size(AB[1], 1))) return geneigsolve(AB, x₀, howmany::Int, which; kwargs...) end -function geneigsolve( - AB::Tuple{Any, AbstractMatrix}, - howmany::Int = 1, - which::Selector = :LM, - T = eltype(AB[2]); - kwargs... - ) +function geneigsolve(AB::Tuple{Any,AbstractMatrix}, + howmany::Int=1, + which::Selector=:LM, + T=eltype(AB[2]); + kwargs...) x₀ = Random.rand!(similar(AB[2], T, size(AB[2], 1))) return geneigsolve(AB, x₀, howmany, which; kwargs...) end -function geneigsolve( - AB::Tuple{AbstractMatrix, Any}, - howmany::Int = 1, - which::Selector = :LM, - T = eltype(AB[1]); - kwargs... - ) +function geneigsolve(AB::Tuple{AbstractMatrix,Any}, + howmany::Int=1, + which::Selector=:LM, + T=eltype(AB[1]); + kwargs...) x₀ = Random.rand!(similar(AB[1], T, size(AB[1], 1))) return geneigsolve(AB, x₀, howmany, which; kwargs...) end -function geneigsolve( - f, n::Int, howmany::Int = 1, which::Selector = :LM, T::Type = Float64; - kwargs... - ) +function geneigsolve(f, n::Int, howmany::Int=1, which::Selector=:LM, T::Type=Float64; + kwargs...) return geneigsolve(f, rand(T, n), howmany, which; kwargs...) end -function geneigsolve(f, x₀, howmany::Int = 1, which::Selector = :LM; kwargs...) +function geneigsolve(f, x₀, howmany::Int=1, which::Selector=:LM; kwargs...) T = genapply_scalartype(f, x₀) alg = geneigselector(f, T; kwargs...) if alg isa GolubYe && (which == :LI || which == :SI) @@ -195,26 +187,22 @@ function geneigsolve(f, x₀, howmany::Int = 1, which::Selector = :LM; kwargs... return geneigsolve(f, x₀, howmany, which, alg) end -function geneigselector( - AB::Tuple{AbstractMatrix, AbstractMatrix}, T::Type; - issymmetric = (T <: Real && all(LinearAlgebra.issymmetric, AB)), - ishermitian = issymmetric || all(LinearAlgebra.ishermitian, AB), - isposdef = ishermitian && LinearAlgebra.isposdef(AB[2]), - kwargs... - ) +function geneigselector(AB::Tuple{AbstractMatrix,AbstractMatrix}, T::Type; + issymmetric=(T <: Real && all(LinearAlgebra.issymmetric, AB)), + ishermitian=issymmetric || all(LinearAlgebra.ishermitian, AB), + isposdef=ishermitian && LinearAlgebra.isposdef(AB[2]), + kwargs...) if (issymmetric || ishermitian) && isposdef return GolubYe(; kwargs...) else throw(ArgumentError("Only symmetric or hermitian generalized eigenvalue problems with positive definite `B` matrix are currently supported.")) end end -function geneigselector( - f, T::Type; - issymmetric = false, - ishermitian = issymmetric, - isposdef = false, - kwargs... - ) +function geneigselector(f, T::Type; + issymmetric=false, + ishermitian=issymmetric, + isposdef=false, + kwargs...) if (issymmetric || ishermitian) && isposdef return GolubYe(; kwargs...) else diff --git a/src/eigsolve/golubye.jl b/src/eigsolve/golubye.jl index 6c14502c..3901feb7 100644 --- a/src/eigsolve/golubye.jl +++ b/src/eigsolve/golubye.jl @@ -190,7 +190,7 @@ function geneigsolve(f, x₀, howmany::Int, which::Selector, alg::GolubYe) end return values, vectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end function golubyerecurrence(f, ρ, V::OrthonormalBasis, β, orth::ClassicalGramSchmidt) diff --git a/src/eigsolve/lanczos.jl b/src/eigsolve/lanczos.jl index b2e7c8d0..75d1bcd6 100644 --- a/src/eigsolve/lanczos.jl +++ b/src/eigsolve/lanczos.jl @@ -1,13 +1,10 @@ -function eigsolve( - A, x₀, howmany::Int, which::Selector, alg::Lanczos; - alg_rrule = Arnoldi(; - tol = alg.tol, - krylovdim = alg.krylovdim, - maxiter = alg.maxiter, - eager = alg.eager, - orth = alg.orth - ) - ) +function eigsolve(A, x₀, howmany::Int, which::Selector, alg::Lanczos; + alg_rrule=Arnoldi(; + tol=alg.tol, + krylovdim=alg.krylovdim, + maxiter=alg.maxiter, + eager=alg.eager, + orth=alg.orth)) krylovdim = alg.krylovdim maxiter = alg.maxiter if howmany > krylovdim @@ -17,7 +14,7 @@ function eigsolve( ## FIRST ITERATION: setting up # Initialize Lanczos factorization iter = LanczosIterator(A, x₀, alg.orth) - fact = initialize(iter; verbosity = alg.verbosity) + fact = initialize(iter; verbosity=alg.verbosity) numops = 1 numiter = 1 sizehint!(fact, krylovdim) @@ -58,7 +55,7 @@ function eigsolve( end D, U = tridiageigh!(T, U) by, rev = eigsort(which) - p = sortperm(D; by = by, rev = rev) + p = sortperm(D; by=by, rev=rev) D, U = permuteeig!(D, U, p) mul!(f, view(U, K, :), β) converged = 0 @@ -75,7 +72,7 @@ function eigsolve( end if K < krylovdim # expand Krylov factorization - fact = expand!(iter, fact; verbosity = alg.verbosity) + fact = expand!(iter, fact; verbosity=alg.verbosity) numops += 1 else ## shrink and restart if numiter == maxiter @@ -111,7 +108,7 @@ function eigsolve( B[keep + 1] = scale!!(r, 1 / β) # Shrink Lanczos factorization - fact = shrink!(fact, keep; verbosity = alg.verbosity) + fact = shrink!(fact, keep; verbosity=alg.verbosity) numiter += 1 end end @@ -151,5 +148,5 @@ function eigsolve( end return values, vectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end diff --git a/src/eigsolve/svdsolve.jl b/src/eigsolve/svdsolve.jl index bf09e2a6..d419095d 100644 --- a/src/eigsolve/svdsolve.jl +++ b/src/eigsolve/svdsolve.jl @@ -120,38 +120,31 @@ to the Krylov-Schur factorization for eigenvalues. """ function svdsolve end -function svdsolve( - A::AbstractMatrix, howmany::Int = 1, which::Selector = :LR, T::Type = eltype(A); - kwargs... - ) +function svdsolve(A::AbstractMatrix, howmany::Int=1, which::Selector=:LR, T::Type=eltype(A); + kwargs...) x₀ = Random.rand!(similar(A, T, size(A, 1))) return svdsolve(A, x₀, howmany, which; kwargs...) end -function svdsolve( - f, n::Int, howmany::Int = 1, which::Selector = :LR, T::Type = Float64; - kwargs... - ) +function svdsolve(f, n::Int, howmany::Int=1, which::Selector=:LR, T::Type=Float64; + kwargs...) return svdsolve(f, rand(T, n), howmany, which; kwargs...) end -function svdsolve(f, x₀, howmany::Int = 1, which::Selector = :LR; kwargs...) +function svdsolve(f, x₀, howmany::Int=1, which::Selector=:LR; kwargs...) which == :LR || which == :SR || error("invalid specification of which singular values to target: which = $which") alg = GKL(; kwargs...) return svdsolve(f, x₀, howmany, which, alg) end -function svdsolve( - A, x₀, howmany::Int, which::Symbol, alg::GKL; - alg_rrule = Arnoldi(; - tol = alg.tol, - krylovdim = alg.krylovdim, - maxiter = alg.maxiter, - eager = alg.eager, - orth = alg.orth, - verbosity = alg.verbosity - ) - ) +function svdsolve(A, x₀, howmany::Int, which::Symbol, alg::GKL; + alg_rrule=Arnoldi(; + tol=alg.tol, + krylovdim=alg.krylovdim, + maxiter=alg.maxiter, + eager=alg.eager, + orth=alg.orth, + verbosity=alg.verbosity)) krylovdim = alg.krylovdim maxiter = alg.maxiter howmany > krylovdim && @@ -161,7 +154,7 @@ function svdsolve( numiter = 1 # initialize GKL factorization iter = GKLIterator(A, x₀, alg.orth) - fact = initialize(iter; verbosity = alg.verbosity) + fact = initialize(iter; verbosity=alg.verbosity) numops = 2 sizehint!(fact, krylovdim) β = normres(fact) @@ -218,7 +211,7 @@ function svdsolve( end if K < krylovdim # expand - fact = expand!(iter, fact; verbosity = alg.verbosity) + fact = expand!(iter, fact; verbosity=alg.verbosity) numops += 2 else ## shrink and restart if numiter == maxiter @@ -271,7 +264,7 @@ function svdsolve( fact.βs[j] = H[j + 1, j] end # Shrink GKL factorization - fact = shrink!(fact, keep; verbosity = alg.verbosity) + fact = shrink!(fact, keep; verbosity=alg.verbosity) numiter += 1 end end @@ -310,5 +303,5 @@ function svdsolve( end return values, leftvectors, rightvectors, - ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) + ConvergenceInfo(converged, residuals, normresiduals, numiter, numops) end diff --git a/src/factorizations/arnoldi.jl b/src/factorizations/arnoldi.jl index 5b1ead15..f272d3ce 100644 --- a/src/factorizations/arnoldi.jl +++ b/src/factorizations/arnoldi.jl @@ -28,7 +28,7 @@ Arnoldi factorizations of a given linear map and a starting vector. See [`LanczosFactorization`](@ref) and [`LanczosIterator`](@ref) for a Krylov factorization that is optimized for real symmetric or complex hermitian linear maps. """ -mutable struct ArnoldiFactorization{T, S} <: KrylovFactorization{T, S} +mutable struct ArnoldiFactorization{T,S} <: KrylovFactorization{T,S} k::Int # current Krylov dimension V::OrthonormalBasis{T} # basis of length k H::Vector{S} # stores the Hessenberg matrix in packed form @@ -42,7 +42,7 @@ Base.sizehint!(F::ArnoldiFactorization, n) = begin return F end Base.eltype(F::ArnoldiFactorization) = eltype(typeof(F)) -Base.eltype(::Type{<:ArnoldiFactorization{<:Any, S}}) where {S} = S +Base.eltype(::Type{<:ArnoldiFactorization{<:Any,S}}) where {S} = S basis(F::ArnoldiFactorization) = F.V rayleighquotient(F::ArnoldiFactorization) = PackedHessenberg(F.H, F.k) @@ -108,7 +108,7 @@ factorization in place. See also [`initialize!(::KrylovIterator, information will be discarded) and [`shrink!(::KrylovFactorization, k)`](@ref) to shrink an existing factorization down to length `k`. """ -struct ArnoldiIterator{F, T, O <: Orthogonalizer} <: KrylovIterator{F, T} +struct ArnoldiIterator{F,T,O<:Orthogonalizer} <: KrylovIterator{F,T} operator::F x₀::T orth::O @@ -132,7 +132,7 @@ function Base.iterate(iter::ArnoldiIterator, state) end end -function initialize(iter::ArnoldiIterator; verbosity::Int = KrylovDefaults.verbosity[]) +function initialize(iter::ArnoldiIterator; verbosity::Int=KrylovDefaults.verbosity[]) # initialize without using eltype x₀ = iter.x₀ β₀ = norm(x₀) @@ -152,12 +152,12 @@ function initialize(iter::ArnoldiIterator; verbosity::Int = KrylovDefaults.verbo r = add!!(r, v, -α) β = norm(r) # possibly reorthogonalize - if iter.orth isa Union{ClassicalGramSchmidt2, ModifiedGramSchmidt2} + if iter.orth isa Union{ClassicalGramSchmidt2,ModifiedGramSchmidt2} dα = inner(v, r) α += dα r = add!!(r, v, -dα) β = norm(r) - elseif iter.orth isa Union{ClassicalGramSchmidtIR, ModifiedGramSchmidtIR} + elseif iter.orth isa Union{ClassicalGramSchmidtIR,ModifiedGramSchmidtIR} while eps(one(β)) < β < iter.orth.η * βold βold = β dα = inner(v, r) @@ -173,10 +173,8 @@ function initialize(iter::ArnoldiIterator; verbosity::Int = KrylovDefaults.verbo end return state = ArnoldiFactorization(1, V, H, r) end -function initialize!( - iter::ArnoldiIterator, state::ArnoldiFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function initialize!(iter::ArnoldiIterator, state::ArnoldiFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) x₀ = iter.x₀ V = state.V while length(V) > 1 @@ -196,10 +194,8 @@ function initialize!( end return state end -function expand!( - iter::ArnoldiIterator, state::ArnoldiFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function expand!(iter::ArnoldiIterator, state::ArnoldiFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) state.k += 1 k = state.k V = state.V @@ -217,7 +213,7 @@ function expand!( end return state end -function shrink!(state::ArnoldiFactorization, k; verbosity::Int = KrylovDefaults.verbosity[]) +function shrink!(state::ArnoldiFactorization, k; verbosity::Int=KrylovDefaults.verbosity[]) length(state) <= k && return state V = state.V H = state.H @@ -236,9 +232,8 @@ function shrink!(state::ArnoldiFactorization, k; verbosity::Int = KrylovDefaults end # Arnoldi recurrence: simply use provided orthonormalization routines -function arnoldirecurrence!!( - operator, V::OrthonormalBasis, h::AbstractVector, orth::Orthogonalizer - ) +function arnoldirecurrence!!(operator, V::OrthonormalBasis, h::AbstractVector, + orth::Orthogonalizer) w = apply(operator, last(V)) r, h = orthogonalize!!(w, V, h, orth) return r, norm(r) diff --git a/src/factorizations/biarnoldi.jl b/src/factorizations/biarnoldi.jl index ce259830..3b05239c 100644 --- a/src/factorizations/biarnoldi.jl +++ b/src/factorizations/biarnoldi.jl @@ -1,6 +1,6 @@ -mutable struct BiArnoldiFactorization{T, S} <: KrylovFactorization{T, S} - VH::ArnoldiFactorization{T, S} - WK::ArnoldiFactorization{T, S} +mutable struct BiArnoldiFactorization{T,S} <: KrylovFactorization{T,S} + VH::ArnoldiFactorization{T,S} + WK::ArnoldiFactorization{T,S} end Base.length(F::BiArnoldiFactorization) = length(F.VH) @@ -10,7 +10,7 @@ Base.sizehint!(F::BiArnoldiFactorization, n) = begin return F end Base.eltype(F::BiArnoldiFactorization) = eltype(typeof(F)) -Base.eltype(::Type{<:BiArnoldiFactorization{<:Any, S}}) where {S} = S +Base.eltype(::Type{<:BiArnoldiFactorization{<:Any,S}}) where {S} = S basis(F::BiArnoldiFactorization) = basis.((F.VH, F.WK)) rayleighquotient(F::BiArnoldiFactorization) = rayleighquotient.((F.VH, F.WK)) @@ -18,20 +18,19 @@ residual(F::BiArnoldiFactorization) = residual.((F.VH, F.WK)) normres(F::BiArnoldiFactorization) = normres.((F.VH, F.WK)) rayleighextension(F::BiArnoldiFactorization) = rayleighextension.((F.VH, F.WK)) -struct BiArnoldiIterator{I1 <: ArnoldiIterator, I2 <: ArnoldiIterator} +struct BiArnoldiIterator{I1<:ArnoldiIterator,I2<:ArnoldiIterator} iterVH::I1 iterWK::I2 - function BiArnoldiIterator( - f::F, v₀, w₀, orth1::Orthogonalizer, orth2::Orthogonalizer - ) where {F} + function BiArnoldiIterator(f::F, v₀, w₀, orth1::Orthogonalizer, + orth2::Orthogonalizer) where {F} iterVH = ArnoldiIterator(Base.Fix1(apply_normal, f), v₀, orth1) iterWK = ArnoldiIterator(Base.Fix1(apply_adjoint, f), w₀, orth2) I1 = typeof(iterVH) I2 = typeof(iterWK) - return new{I1, I2}(iterVH, iterWK) + return new{I1,I2}(iterVH, iterWK) end end -function BiArnoldiIterator(f, v₀, w₀ = v₀, orth = KrylovDefaults.orth) +function BiArnoldiIterator(f, v₀, w₀=v₀, orth=KrylovDefaults.orth) return BiArnoldiIterator(f, v₀, w₀, orth, orth) end @@ -52,32 +51,26 @@ function Base.iterate(iter::BiArnoldiIterator, state) end end -function initialize(iter::BiArnoldiIterator; verbosity::Int = KrylovDefaults.verbosity[]) - VH = initialize(iter.iterVH; verbosity = verbosity) - WK = initialize(iter.iterWK; verbosity = verbosity) +function initialize(iter::BiArnoldiIterator; verbosity::Int=KrylovDefaults.verbosity[]) + VH = initialize(iter.iterVH; verbosity=verbosity) + WK = initialize(iter.iterWK; verbosity=verbosity) return BiArnoldiFactorization(VH, WK) end -function initialize!( - iter::BiArnoldiIterator, state::BiArnoldiFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) - state.VH = initialize!(iter.iterVH, state.VH; verbosity = verbosity) - state.WK = initialize!(iter.iterWK, state.WK; verbosity = verbosity) +function initialize!(iter::BiArnoldiIterator, state::BiArnoldiFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) + state.VH = initialize!(iter.iterVH, state.VH; verbosity=verbosity) + state.WK = initialize!(iter.iterWK, state.WK; verbosity=verbosity) return state end -function expand!( - iter::BiArnoldiIterator, state::BiArnoldiFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) - state.VH = expand!(iter.iterVH, state.VH; verbosity = verbosity) - state.WK = expand!(iter.iterWK, state.WK; verbosity = verbosity) +function expand!(iter::BiArnoldiIterator, state::BiArnoldiFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) + state.VH = expand!(iter.iterVH, state.VH; verbosity=verbosity) + state.WK = expand!(iter.iterWK, state.WK; verbosity=verbosity) return state end -function shrink!( - state::BiArnoldiFactorization, k; - verbosity::Int = KrylovDefaults.verbosity[] - ) - state.VH = shrink!(state.VH, k; verbosity = verbosity) - state.WK = shrink!(state.WK, k; verbosity = verbosity) +function shrink!(state::BiArnoldiFactorization, k; + verbosity::Int=KrylovDefaults.verbosity[]) + state.VH = shrink!(state.VH, k; verbosity=verbosity) + state.WK = shrink!(state.WK, k; verbosity=verbosity) return state end diff --git a/src/factorizations/blocklanczos.jl b/src/factorizations/blocklanczos.jl index 3c5ba531..20696c46 100644 --- a/src/factorizations/blocklanczos.jl +++ b/src/factorizations/blocklanczos.jl @@ -27,9 +27,8 @@ Base.@propagate_inbounds function Base.setindex!(b::Block, v, i::Int) b.vec[i] = v return b end -Base.@propagate_inbounds function Base.setindex!( - b₁::Block, b₂::Block, idxs::AbstractVector{Int} - ) +Base.@propagate_inbounds function Base.setindex!(b₁::Block, b₂::Block, + idxs::AbstractVector{Int}) b₁.vec[idxs] = b₂.vec return b₁ end @@ -86,7 +85,7 @@ because it is implemented in its `eigsolve`. See also [`BlockLanczosIterator`](@ref) for an iterator that constructs a progressively expanding BlockLanczos factorizations of a given linear map and a starting block. """ -mutable struct BlockLanczosFactorization{T, S <: Number, SR <: Real} <: KrylovFactorization{T, S} +mutable struct BlockLanczosFactorization{T,S<:Number,SR<:Real} <: KrylovFactorization{T,S} k::Int V::OrthonormalBasis{T} # BlockLanczos Basis H::Matrix{S} # block tridiagonal matrix, and S is the matrix element type @@ -131,35 +130,29 @@ Here, [`initialize(::KrylovIterator)`](@ref) produces the first Krylov factoriza and [`expand!(iter::KrylovIterator, fact::KrylovFactorization)`](@ref) expands the factorization in place. """ -struct BlockLanczosIterator{F, T, O <: Orthogonalizer, S <: Real} <: KrylovIterator{F, T} +struct BlockLanczosIterator{F,T,O<:Orthogonalizer,S<:Real} <: KrylovIterator{F,T} operator::F x₀::Block{T} maxdim::Int orth::O qr_tol::S - function BlockLanczosIterator{F, T, O, S}( - operator::F, x₀::Block{T}, maxdim::Int, orth::O, qr_tol::S - ) where {F, T, O <: Orthogonalizer, S <: Real} - return new{F, T, O, S}(operator, x₀, maxdim, orth, qr_tol) + function BlockLanczosIterator{F,T,O,S}(operator::F, x₀::Block{T}, maxdim::Int, orth::O, + qr_tol::S) where {F,T,O<:Orthogonalizer,S<:Real} + return new{F,T,O,S}(operator, x₀, maxdim, orth, qr_tol) end end -function BlockLanczosIterator( - operator::F, x₀::Block{T}, maxdim::Int, - orth::O = KrylovDefaults.orth, qr_tol::Real = KrylovDefaults.tol[] - ) where { - F, T, - O <: Orthogonalizer, - } +function BlockLanczosIterator(operator::F, x₀::Block{T}, maxdim::Int, + orth::O=KrylovDefaults.orth, + qr_tol::Real=KrylovDefaults.tol[]) where {F,T, + O<:Orthogonalizer} norm(x₀) < qr_tol && @error "initial vector should not have norm zero" orth != ModifiedGramSchmidt2() && @error "BlockLanczosIterator only supports ModifiedGramSchmidt2 orthogonalizer" - return BlockLanczosIterator{F, T, O, typeof(qr_tol)}(operator, x₀, maxdim, orth, qr_tol) + return BlockLanczosIterator{F,T,O,typeof(qr_tol)}(operator, x₀, maxdim, orth, qr_tol) end -function initialize( - iter::BlockLanczosIterator; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function initialize(iter::BlockLanczosIterator; + verbosity::Int=KrylovDefaults.verbosity[]) X₀ = iter.x₀ maxdim = iter.maxdim A = iter.operator @@ -173,7 +166,10 @@ function initialize( X₁ = Block(map(Base.Fix2(scale, one(α)), X₀.vec)) # Orthogonalization of the initial block - _, good_idx = block_qr!(X₁, iter.qr_tol) + _, good_idx, is_draft = block_qr!(X₁, iter.qr_tol) + is_draft && + @warn "Block size may be more than operator's rank. Please decrease Krylovdim or increase qr_tol." + X₁ = X₁[good_idx] V = OrthonormalBasis(X₁.vec) bs = length(X₁) # block size of the first block @@ -197,17 +193,24 @@ function initialize( return BlockLanczosFactorization(bs, V, BTD, AX₁, bs, norm_R) end -function expand!( - iter::BlockLanczosIterator, state::BlockLanczosFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function expand!(iter::BlockLanczosIterator, state::BlockLanczosFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) k = state.k R = state.R[1:(state.R_size)] bs = length(R) V = state.V + Rcopy = copy(R) # Calculate the new basis and B - B, good_idx = block_qr!(R, iter.qr_tol) + B, good_idx, is_draft = block_qr!(R, iter.qr_tol) + if is_draft # Prevent column subspace of R from drifting caused by an excessively small β in block_qr! + block_reorthogonalize!(R, V) + _, good_idx, is_draft = block_qr!(R, iter.qr_tol) + B = block_inner(R[good_idx], Rcopy) # Make sure R = XB + end + is_draft && + @warn "Block size may be more than operator's rank. Please decrease Krylovdim or increase qr_tol." + bs_next = length(good_idx) push!(V, R[good_idx]) state.H[(k + 1):(k + bs_next), (k - bs + 1):k] = view(B, 1:bs_next, 1:bs) @@ -216,11 +219,8 @@ function expand!( # Calculate the new residual and orthogonalize the new basis Rnext, Mnext = block_lanczosrecurrence(iter.operator, V, B, iter.orth) verbosity >= WARN_LEVEL && warn_nonhermitian(Mnext) - - state.H[(k + 1):(k + bs_next), (k + 1):(k + bs_next)] = view( - Mnext, 1:bs_next, - 1:bs_next - ) + state.H[(k + 1):(k + bs_next), (k + 1):(k + bs_next)] = view(Mnext, 1:bs_next, + 1:bs_next) state.R.vec[1:bs_next] .= Rnext.vec state.norm_R = norm(Rnext) state.k += bs_next @@ -232,12 +232,10 @@ function expand!( return state end -function block_lanczosrecurrence( - operator, V::OrthonormalBasis, B::AbstractMatrix, orth::ModifiedGramSchmidt2 - ) +function block_lanczosrecurrence(operator, V::OrthonormalBasis, B::AbstractMatrix, + orth::ModifiedGramSchmidt2) # Apply the operator and calculate the M. Get Xnext and Mnext. bs, bs_prev = size(B) - S = eltype(B) k = length(V) X = Block(V[(k - bs + 1):k]) AX = apply(operator, X) @@ -278,7 +276,7 @@ function block_reorthogonalize!(R::Block{T}, V::OrthonormalBasis{T}) where {T} end function warn_nonhermitian(M::AbstractMatrix) - return if !isapprox(M, M'; atol = eps(real(eltype(M)))^(2 / 5)) + return if !isapprox(M, M'; atol=eps(real(eltype(M)))^(2 / 5)) @warn "ignoring the antihermitian part of the block triangular matrix: operator might not be hermitian?" M end end @@ -295,14 +293,16 @@ This function performs a QR factorization of a block of abstract vectors using t It takes as input a block of abstract vectors and a tolerance parameter, which is used to determine whether a vector is considered numerically zero. The operation is performed in-place, transforming the input block into a block of orthonormal vectors. -The function returns a matrix of size `(r, p)` and a vector of indices goodidx. Here, `p` denotes the number of input vectors, +The function returns a matrix of size `(r, p)`, a vector of indices goodidx and a boolean flag is_draft. Here, `p` denotes the number of input vectors, and `r` is the numerical rank of the input block. The matrix represents the upper-triangular factor of the QR decomposition, restricted to the `r` linearly independent components. The vector `goodidx` contains the indices of the non-zero (i.e., numerically independent) vectors in the orthonormalized block. +If a small value of β is detected, the function will carry out an additional reorthogonalization step to further ensure the input block vectors are orthonormalized. +In such cases, is_draft is set to true to indicate potential numerical instability. """ function block_qr!(block::Block, tol::Real) n = length(block) - rank_shrink = false + is_draft = false idx = trues(n) r₁₁ = inner(block[1], block[1]) R = zeros(typeof(r₁₁), n, n) @@ -312,28 +312,42 @@ function block_qr!(block::Block, tol::Real) block[1] = scale!!(block[1], 1 / β) else block[1] = zerovector!!(block[1]) - rank_shrink = true idx[1] = false end - @inbounds for j in 2:n + for j in 2:n + # first MGS for i in 1:(j - 1) R[i, j] = inner(block[i], block[j]) block[j] = add!!(block[j], block[i], -R[i, j]) end β = norm(block[j]) - if β > tol - R[j, j] = β - block[j] = scale!!(block[j], 1 / β) - else + + if β < tol block[j] = zerovector!!(block[j]) - rank_shrink = true idx[j] = false + continue + else + R[j, j] = β + block[j] = scale!!(block[j], 1/β) + # DGKS reorthogonalization + if β < 100 * tol + is_draft = true + for i in 1:(j - 1) + δ = inner(block[i], block[j]) + R[i, j] += δ + block[j] = add!!(block[j], block[i], -δ) + end + β = norm(block[j]) + if β < tol + block[j] = zerovector!!(block[j]) + idx[j] = false + else + R[j, j] = β + block[j] = scale!!(block[j], 1/β) + end + end end end - if rank_shrink - good_idx = findall(idx) - return R[good_idx, :], good_idx - else - return R, collect(Int, 1:n) - end + good_idx = findall(idx) + return R[good_idx, :], good_idx, is_draft end diff --git a/src/factorizations/gkl.jl b/src/factorizations/gkl.jl index fae9f256..cf9519ef 100644 --- a/src/factorizations/gkl.jl +++ b/src/factorizations/gkl.jl @@ -28,7 +28,7 @@ A GKL factorization `fact` can be destructured as `U, V, B, r, nr, b = fact` wit See also [`GKLIterator`](@ref) for an iterator that constructs a progressively expanding GKL factorizations of a given linear map and a starting vector `u₀`. """ -mutable struct GKLFactorization{TU, TV, S <: Real} +mutable struct GKLFactorization{TU,TV,S<:Real} k::Int # current Krylov dimension U::OrthonormalBasis{TU} # basis of length k V::OrthonormalBasis{TV} # basis of length k @@ -46,7 +46,7 @@ Base.sizehint!(F::GKLFactorization, n) = begin return F end Base.eltype(F::GKLFactorization) = eltype(typeof(F)) -Base.eltype(::Type{<:GKLFactorization{<:Any, <:Any, S}}) where {S} = S +Base.eltype(::Type{<:GKLFactorization{<:Any,<:Any,S}}) where {S} = S # iteration for destructuring into components Base.iterate(F::GKLFactorization) = (basis(F, Val(:U)), Val(:V)) @@ -143,24 +143,22 @@ See also [`initialize!(::GKLIterator, ::GKLFactorization)`](@ref) to initialize already existing factorization (most information will be discarded) and [`shrink!(::GKLIterator, k)`](@ref) to shrink an existing factorization down to length `k`. """ -struct GKLIterator{F, TU, O <: Orthogonalizer} +struct GKLIterator{F,TU,O<:Orthogonalizer} operator::F u₀::TU orth::O keepvecs::Bool - function GKLIterator{F, TU, O}( - operator::F, u₀::TU, orth::O, keepvecs::Bool - ) where {F, TU, O <: Orthogonalizer} + function GKLIterator{F,TU,O}(operator::F, u₀::TU, orth::O, + keepvecs::Bool) where {F,TU,O<:Orthogonalizer} if !keepvecs && isa(orth, Reorthogonalizer) error("Cannot use reorthogonalization without keeping all Krylov vectors") end - return new{F, TU, O}(operator, u₀, orth, keepvecs) + return new{F,TU,O}(operator, u₀, orth, keepvecs) end end -function GKLIterator( - operator::F, u₀::TU, orth::O = KrylovDefaults.orth, keepvecs::Bool = true - ) where {F, TU, O <: Orthogonalizer} - return GKLIterator{F, TU, O}(operator, u₀, orth, keepvecs) +function GKLIterator(operator::F, u₀::TU, orth::O=KrylovDefaults.orth, + keepvecs::Bool=true) where {F,TU,O<:Orthogonalizer} + return GKLIterator{F,TU,O}(operator, u₀, orth, keepvecs) end Base.IteratorSize(::Type{<:GKLIterator}) = Base.SizeUnknown() @@ -180,7 +178,7 @@ function Base.iterate(iter::GKLIterator, state::GKLFactorization) end end -function initialize(iter::GKLIterator; verbosity::Int = KrylovDefaults.verbosity[]) +function initialize(iter::GKLIterator; verbosity::Int=KrylovDefaults.verbosity[]) # initialize without using eltype u₀ = iter.u₀ β₀ = norm(u₀) @@ -213,10 +211,8 @@ function initialize(iter::GKLIterator; verbosity::Int = KrylovDefaults.verbosity end return GKLFactorization(1, U, V, αs, βs, r) end -function initialize!( - iter::GKLIterator, state::GKLFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function initialize!(iter::GKLIterator, state::GKLFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) U = state.U while length(U) > 1 pop!(U) @@ -243,10 +239,8 @@ function initialize!( end return state end -function expand!( - iter::GKLIterator, state::GKLFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function expand!(iter::GKLIterator, state::GKLFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) βold = normres(state) U = state.U V = state.V @@ -267,7 +261,7 @@ function expand!( end return state end -function shrink!(state::GKLFactorization, k; verbosity::Int = KrylovDefaults.verbosity[]) +function shrink!(state::GKLFactorization, k; verbosity::Int=KrylovDefaults.verbosity[]) length(state) == length(state.V) || error("we cannot shrink GKLFactorization without keeping vectors") length(state) <= k && return state @@ -291,9 +285,8 @@ function shrink!(state::GKLFactorization, k; verbosity::Int = KrylovDefaults.ver end # Golub-Kahan-Lanczos recurrence relation -function gklrecurrence( - operator, U::OrthonormalBasis, V::OrthonormalBasis, β, orth::Union{ClassicalGramSchmidt, ModifiedGramSchmidt} - ) +function gklrecurrence(operator, U::OrthonormalBasis, V::OrthonormalBasis, β, + orth::Union{ClassicalGramSchmidt,ModifiedGramSchmidt}) u = U[end] v = apply_adjoint(operator, u) v = add!!(v, V[end], -β) @@ -305,9 +298,8 @@ function gklrecurrence( β = norm(r) return v, r, α, β end -function gklrecurrence( - operator, U::OrthonormalBasis, V::OrthonormalBasis, β, orth::ClassicalGramSchmidt2 - ) +function gklrecurrence(operator, U::OrthonormalBasis, V::OrthonormalBasis, β, + orth::ClassicalGramSchmidt2) u = U[end] v = apply_adjoint(operator, u) v = add!!(v, V[end], -β) # not necessary if we definitely reorthogonalize next step and previous step @@ -321,9 +313,8 @@ function gklrecurrence( β = norm(r) return v, r, α, β end -function gklrecurrence( - operator, U::OrthonormalBasis, V::OrthonormalBasis, β, orth::ModifiedGramSchmidt2 - ) +function gklrecurrence(operator, U::OrthonormalBasis, V::OrthonormalBasis, β, + orth::ModifiedGramSchmidt2) u = U[end] v = apply_adjoint(operator, u) v = add!!(v, V[end], -β) @@ -344,9 +335,8 @@ function gklrecurrence( β = norm(r) return v, r, α, β end -function gklrecurrence( - operator, U::OrthonormalBasis, V::OrthonormalBasis, β, orth::ClassicalGramSchmidtIR - ) +function gklrecurrence(operator, U::OrthonormalBasis, V::OrthonormalBasis, β, + orth::ClassicalGramSchmidtIR) u = U[end] v = apply_adjoint(operator, u) v = add!!(v, V[end], -β) @@ -371,9 +361,8 @@ function gklrecurrence( return v, r, α, β end -function gklrecurrence( - operator, U::OrthonormalBasis, V::OrthonormalBasis, β, orth::ModifiedGramSchmidtIR - ) +function gklrecurrence(operator, U::OrthonormalBasis, V::OrthonormalBasis, β, + orth::ModifiedGramSchmidtIR) u = U[end] v = apply_adjoint(operator, u) v = add!!(v, V[end], -β) diff --git a/src/factorizations/krylov.jl b/src/factorizations/krylov.jl index 36d39baa..18be6885 100644 --- a/src/factorizations/krylov.jl +++ b/src/factorizations/krylov.jl @@ -27,7 +27,7 @@ implementations, and [`KrylovIterator`](@ref) (with in particular [`LanczosItera and [`ArnoldiIterator`](@ref)) for iterators that construct progressively expanding Krylov factorizations of a given linear map and a starting vector. """ -abstract type KrylovFactorization{T, S} end +abstract type KrylovFactorization{T,S} end """ abstract type KrylovIterator{F,T} @@ -43,7 +43,7 @@ of [`KrylovFactorization`](@ref), which can be immediately destructured into a See [`LanczosIterator`](@ref) and [`ArnoldiIterator`](@ref) for concrete implementations and more information. """ -abstract type KrylovIterator{F, T} end +abstract type KrylovIterator{F,T} end """ basis(fact::KrylovFactorization) diff --git a/src/factorizations/lanczos.jl b/src/factorizations/lanczos.jl index 7d8a7c3a..98cf48a3 100644 --- a/src/factorizations/lanczos.jl +++ b/src/factorizations/lanczos.jl @@ -28,7 +28,7 @@ Lanczos factorizations of a given linear map and a starting vector. See [`ArnoldiFactorization`](@ref) and [`ArnoldiIterator`](@ref) for a Krylov factorization that works for general (non-symmetric) linear maps. """ -mutable struct LanczosFactorization{T, S <: Real} <: KrylovFactorization{T, S} +mutable struct LanczosFactorization{T,S<:Real} <: KrylovFactorization{T,S} k::Int # current Krylov dimension V::OrthonormalBasis{T} # basis of length k αs::Vector{S} @@ -44,11 +44,11 @@ Base.sizehint!(F::LanczosFactorization, n) = begin return F end Base.eltype(F::LanczosFactorization) = eltype(typeof(F)) -Base.eltype(::Type{<:LanczosFactorization{<:Any, S}}) where {S} = S +Base.eltype(::Type{<:LanczosFactorization{<:Any,S}}) where {S} = S function basis(F::LanczosFactorization) return length(F.V) == F.k ? F.V : - error("Not keeping vectors during Lanczos factorization") + error("Not keeping vectors during Lanczos factorization") end rayleighquotient(F::LanczosFactorization) = SymTridiagonal(F.αs, F.βs) residual(F::LanczosFactorization) = F.r @@ -124,30 +124,26 @@ factorization in place. See also [`initialize!(::KrylovIterator, information will be discarded) and [`shrink!(::KrylovFactorization, k)`](@ref) to shrink an existing factorization down to length `k`. """ -struct LanczosIterator{F, T, O <: Orthogonalizer} <: KrylovIterator{F, T} +struct LanczosIterator{F,T,O<:Orthogonalizer} <: KrylovIterator{F,T} operator::F x₀::T orth::O keepvecs::Bool - function LanczosIterator{F, T, O}( - operator::F, - x₀::T, - orth::O, - keepvecs::Bool - ) where {F, T, O <: Orthogonalizer} + function LanczosIterator{F,T,O}(operator::F, + x₀::T, + orth::O, + keepvecs::Bool) where {F,T,O<:Orthogonalizer} if !keepvecs && isa(orth, Reorthogonalizer) error("Cannot use reorthogonalization without keeping all Krylov vectors") end - return new{F, T, O}(operator, x₀, orth, keepvecs) + return new{F,T,O}(operator, x₀, orth, keepvecs) end end -function LanczosIterator( - operator::F, - x₀::T, - orth::O = KrylovDefaults.orth, - keepvecs::Bool = true - ) where {F, T, O <: Orthogonalizer} - return LanczosIterator{F, T, O}(operator, x₀, orth, keepvecs) +function LanczosIterator(operator::F, + x₀::T, + orth::O=KrylovDefaults.orth, + keepvecs::Bool=true) where {F,T,O<:Orthogonalizer} + return LanczosIterator{F,T,O}(operator, x₀, orth, keepvecs) end Base.IteratorSize(::Type{<:LanczosIterator}) = Base.SizeUnknown() @@ -175,7 +171,7 @@ function warn_nonhermitian(α, β₁, β₂) return nothing end -function initialize(iter::LanczosIterator; verbosity::Int = KrylovDefaults.verbosity[]) +function initialize(iter::LanczosIterator; verbosity::Int=KrylovDefaults.verbosity[]) # initialize without using eltype x₀ = iter.x₀ β₀ = norm(x₀) @@ -195,12 +191,12 @@ function initialize(iter::LanczosIterator; verbosity::Int = KrylovDefaults.verbo r = add!!(r, v, -α) # should we use real(α) here? β = norm(r) # possibly reorthogonalize - if iter.orth isa Union{ClassicalGramSchmidt2, ModifiedGramSchmidt2} + if iter.orth isa Union{ClassicalGramSchmidt2,ModifiedGramSchmidt2} dα = inner(v, r) α += dα r = add!!(r, v, -dα) # should we use real(dα) here? β = norm(r) - elseif iter.orth isa Union{ClassicalGramSchmidtIR, ModifiedGramSchmidtIR} + elseif iter.orth isa Union{ClassicalGramSchmidtIR,ModifiedGramSchmidtIR} while eps(one(β)) < β < iter.orth.η * βold βold = β dα = inner(v, r) @@ -218,10 +214,8 @@ function initialize(iter::LanczosIterator; verbosity::Int = KrylovDefaults.verbo end return LanczosFactorization(1, V, αs, βs, r) end -function initialize!( - iter::LanczosIterator, state::LanczosFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function initialize!(iter::LanczosIterator, state::LanczosFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) x₀ = iter.x₀ V = state.V while length(V) > 1 @@ -245,10 +239,8 @@ function initialize!( end return state end -function expand!( - iter::LanczosIterator, state::LanczosFactorization; - verbosity::Int = KrylovDefaults.verbosity[] - ) +function expand!(iter::LanczosIterator, state::LanczosFactorization; + verbosity::Int=KrylovDefaults.verbosity[]) βold = normres(state) V = state.V r = state.r @@ -268,7 +260,7 @@ function expand!( end return state end -function shrink!(state::LanczosFactorization, k; verbosity::Int = KrylovDefaults.verbosity[]) +function shrink!(state::LanczosFactorization, k; verbosity::Int=KrylovDefaults.verbosity[]) length(state) == length(state.V) || error("we cannot shrink LanczosFactorization without keeping Lanczos vectors") length(state) <= k && return state diff --git a/src/innerproductvec.jl b/src/innerproductvec.jl index d04e9df1..97fdc64f 100644 --- a/src/innerproductvec.jl +++ b/src/innerproductvec.jl @@ -15,7 +15,7 @@ is defined as `v = sqrt(real(dot(v, v))) = sqrt(real(dotf(vec, vec)))`. In a (linear) map applied to `v`, the original vector can be obtained as `v.vec` or simply as `v[]`. """ -struct InnerProductVec{F, T} +struct InnerProductVec{F,T} vec::T dotf::F end @@ -32,7 +32,7 @@ Base.:*(a::Number, v::InnerProductVec) = InnerProductVec(a * v.vec, v.dotf) Base.:/(v::InnerProductVec, a::Number) = InnerProductVec(v.vec / a, v.dotf) Base.:\(a::Number, v::InnerProductVec) = InnerProductVec(a \ v.vec, v.dotf) -function Base.similar(v::InnerProductVec, (::Type{T}) = scalartype(v)) where {T} +function Base.similar(v::InnerProductVec, (::Type{T})=scalartype(v)) where {T} return InnerProductVec(similar(v.vec), v.dotf) end @@ -43,16 +43,14 @@ function Base.copy!(w::InnerProductVec{F}, v::InnerProductVec{F}) where {F} return w end -function LinearAlgebra.mul!( - w::InnerProductVec{F}, a::Number, v::InnerProductVec{F} - ) where {F} +function LinearAlgebra.mul!(w::InnerProductVec{F}, a::Number, + v::InnerProductVec{F}) where {F} mul!(w.vec, a, v.vec) return w end -function LinearAlgebra.mul!( - w::InnerProductVec{F}, v::InnerProductVec{F}, a::Number - ) where {F} +function LinearAlgebra.mul!(w::InnerProductVec{F}, v::InnerProductVec{F}, + a::Number) where {F} mul!(w.vec, v.vec, a) return w end @@ -62,15 +60,13 @@ function LinearAlgebra.rmul!(v::InnerProductVec, a::Number) return v end -function LinearAlgebra.axpy!( - a::Number, v::InnerProductVec{F}, w::InnerProductVec{F} - ) where {F} +function LinearAlgebra.axpy!(a::Number, v::InnerProductVec{F}, + w::InnerProductVec{F}) where {F} axpy!(a, v.vec, w.vec) return w end -function LinearAlgebra.axpby!( - a::Number, v::InnerProductVec{F}, b, w::InnerProductVec{F} - ) where {F} +function LinearAlgebra.axpby!(a::Number, v::InnerProductVec{F}, b, + w::InnerProductVec{F}) where {F} axpby!(a, v.vec, b, w.vec) return w end @@ -79,7 +75,7 @@ function LinearAlgebra.dot(v::InnerProductVec{F}, w::InnerProductVec{F}) where { return v.dotf(v.vec, w.vec) end -VectorInterface.scalartype(::Type{<:InnerProductVec{F, T}}) where {F, T} = scalartype(T) +VectorInterface.scalartype(::Type{<:InnerProductVec{F,T}}) where {F,T} = scalartype(T) function VectorInterface.zerovector(v::InnerProductVec, T::Type{<:Number}) return InnerProductVec(zerovector(v.vec, T), v.dotf) @@ -101,31 +97,26 @@ function VectorInterface.scale!(v::InnerProductVec, a::Number) scale!(v.vec, a) return v end -function VectorInterface.scale!!( - w::InnerProductVec{F}, v::InnerProductVec{F}, a::Number - ) where {F} +function VectorInterface.scale!!(w::InnerProductVec{F}, v::InnerProductVec{F}, + a::Number) where {F} return InnerProductVec(scale!!(w.vec, v.vec, a), w.dotf) end -function VectorInterface.scale!( - w::InnerProductVec{F}, v::InnerProductVec{F}, a::Number - ) where {F} +function VectorInterface.scale!(w::InnerProductVec{F}, v::InnerProductVec{F}, + a::Number) where {F} scale!(w.vec, v.vec, a) return w end -function VectorInterface.add( - v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, b::Number - ) where {F} +function VectorInterface.add(v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, + b::Number) where {F} return InnerProductVec(add(v.vec, w.vec, a, b), v.dotf) end -function VectorInterface.add!!( - v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, b::Number - ) where {F} +function VectorInterface.add!!(v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, + b::Number) where {F} return InnerProductVec(add!!(v.vec, w.vec, a, b), v.dotf) end -function VectorInterface.add!( - v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, b::Number - ) where {F} +function VectorInterface.add!(v::InnerProductVec{F}, w::InnerProductVec{F}, a::Number, + b::Number) where {F} add!(v.vec, w.vec, a, b) return v end diff --git a/src/linsolve/bicgstab.jl b/src/linsolve/bicgstab.jl index fe2e6eb3..954f0437 100644 --- a/src/linsolve/bicgstab.jl +++ b/src/linsolve/bicgstab.jl @@ -1,4 +1,4 @@ -function linsolve(operator, b, x₀, alg::BiCGStab, a₀::Number = 0, a₁::Number = 1; alg_rrule = alg) +function linsolve(operator, b, x₀, alg::BiCGStab, a₀::Number=0, a₁::Number=1; alg_rrule=alg) # Initial function operation and division defines number type y₀ = apply(operator, x₀) T = typeof(inner(b, y₀) / norm(b) * one(a₀) * one(a₁)) diff --git a/src/linsolve/cg.jl b/src/linsolve/cg.jl index 3d771c08..5f0a4681 100644 --- a/src/linsolve/cg.jl +++ b/src/linsolve/cg.jl @@ -1,4 +1,4 @@ -function linsolve(operator, b, x₀, alg::CG, a₀::Real = 0, a₁::Real = 1; alg_rrule = alg) +function linsolve(operator, b, x₀, alg::CG, a₀::Real=0, a₁::Real=1; alg_rrule=alg) # Initial function operation and division defines number type y₀ = apply(operator, x₀) T = typeof(inner(b, y₀) / norm(b) * one(a₀) * one(a₁)) diff --git a/src/linsolve/gmres.jl b/src/linsolve/gmres.jl index 1198cd2d..9c425149 100644 --- a/src/linsolve/gmres.jl +++ b/src/linsolve/gmres.jl @@ -1,4 +1,4 @@ -function linsolve(operator, b, x₀, alg::GMRES, a₀::Number = 0, a₁::Number = 1; alg_rrule = alg) +function linsolve(operator, b, x₀, alg::GMRES, a₀::Number=0, a₁::Number=1; alg_rrule=alg) # Initial function operation and division defines number type y₀ = apply(operator, x₀) T = typeof(inner(b, y₀) / norm(b) * one(a₀) * one(a₁)) @@ -37,7 +37,7 @@ function linsolve(operator, b, x₀, alg::GMRES, a₀::Number = 0, a₁::Number numops = 1 # operator has been applied once to determine T and r iter = ArnoldiIterator(operator, r, alg.orth) - fact = initialize(iter; verbosity = SILENT_LEVEL) + fact = initialize(iter; verbosity=SILENT_LEVEL) sizehint!(fact, alg.krylovdim) numops += 1 # start applies operator once @@ -56,7 +56,7 @@ function linsolve(operator, b, x₀, alg::GMRES, a₀::Number = 0, a₁::Number if alg.verbosity >= EACHITERATION_LEVEL @info "GMRES linsolve in iteration $numiter; step $k: normres = $(normres2string(β))" end - fact = expand!(iter, fact; verbosity = SILENT_LEVEL) + fact = expand!(iter, fact; verbosity=SILENT_LEVEL) numops += 1 # expand! applies the operator once k = length(fact) H = rayleighquotient(fact) @@ -132,7 +132,7 @@ function linsolve(operator, b, x₀, alg::GMRES, a₀::Number = 0, a₁::Number # Restart Arnoldi factorization with new r iter = ArnoldiIterator(operator, r, alg.orth) - fact = initialize!(iter, fact; verbosity = SILENT_LEVEL) + fact = initialize!(iter, fact; verbosity=SILENT_LEVEL) end return end diff --git a/src/linsolve/linsolve.jl b/src/linsolve/linsolve.jl index ed437e29..c0732e50 100644 --- a/src/linsolve/linsolve.jl +++ b/src/linsolve/linsolve.jl @@ -98,18 +98,16 @@ maximal number of CG iterations that can be used by the `CG` algorithm. """ function linsolve end -function linsolve( - A::AbstractMatrix, b::AbstractVector, a₀::Number = 0, a₁::Number = 1; - kwargs... - ) +function linsolve(A::AbstractMatrix, b::AbstractVector, a₀::Number=0, a₁::Number=1; + kwargs...) return linsolve(A, b, (zero(a₀) * zero(a₁)) * b, a₀, a₁; kwargs...) end -function linsolve(f, b, a₀::Number = 0, a₁::Number = 1; kwargs...) +function linsolve(f, b, a₀::Number=0, a₁::Number=1; kwargs...) return linsolve(f, b, scale(b, zero(a₀) * zero(a₁)), a₀, a₁; kwargs...) end -function linsolve(f, b, x₀, a₀::Number = 0, a₁::Number = 1; kwargs...) +function linsolve(f, b, x₀, a₀::Number=0, a₁::Number=1; kwargs...) T = apply_scalartype(f, x₀, a₀, a₁) alg = linselector(f, b, T; kwargs...) if haskey(kwargs, :alg_rrule) @@ -117,66 +115,60 @@ function linsolve(f, b, x₀, a₀::Number = 0, a₁::Number = 1; kwargs...) else alg_rrule = alg end - return linsolve(f, b, x₀, alg, a₀, a₁; alg_rrule = alg_rrule) + return linsolve(f, b, x₀, alg, a₀, a₁; alg_rrule=alg_rrule) end -function linselector( - f, b, T::Type; - issymmetric::Bool = false, - ishermitian::Bool = (T <: Real && issymmetric), - isposdef::Bool = false, - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - rtol::Real = KrylovDefaults.tol[], - atol::Real = KrylovDefaults.tol[], - tol::Real = max(atol, rtol * norm(b)), - orth = KrylovDefaults.orth, - verbosity::Int = KrylovDefaults.verbosity[] - ) +function linselector(f, b, T::Type; + issymmetric::Bool=false, + ishermitian::Bool=(T <: Real && issymmetric), + isposdef::Bool=false, + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + rtol::Real=KrylovDefaults.tol[], + atol::Real=KrylovDefaults.tol[], + tol::Real=max(atol, rtol * norm(b)), + orth=KrylovDefaults.orth, + verbosity::Int=KrylovDefaults.verbosity[]) if (T <: Real && issymmetric) || ishermitian if isposdef - return CG(; maxiter = krylovdim * maxiter, tol = tol, verbosity = verbosity) + return CG(; maxiter=krylovdim * maxiter, tol=tol, verbosity=verbosity) else # TODO: implement MINRES for symmetric but not posdef; for now use GRMES # return MINRES(krylovdim*maxiter, tol=tol) end end return GMRES(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + verbosity=verbosity) end -function linselector( - A::AbstractMatrix, b, T::Type; - issymmetric::Bool = (T <: Real && LinearAlgebra.issymmetric(A)), - ishermitian::Bool = issymmetric || LinearAlgebra.ishermitian(A), - isposdef::Bool = ishermitian ? LinearAlgebra.isposdef(A) : false, - krylovdim::Int = KrylovDefaults.krylovdim[], - maxiter::Int = KrylovDefaults.maxiter[], - rtol::Real = KrylovDefaults.tol[], - atol::Real = KrylovDefaults.tol[], - tol::Real = max(atol, rtol * norm(b)), - orth = KrylovDefaults.orth, - verbosity::Int = KrylovDefaults.verbosity[] - ) +function linselector(A::AbstractMatrix, b, T::Type; + issymmetric::Bool=(T <: Real && LinearAlgebra.issymmetric(A)), + ishermitian::Bool=issymmetric || LinearAlgebra.ishermitian(A), + isposdef::Bool=ishermitian ? LinearAlgebra.isposdef(A) : false, + krylovdim::Int=KrylovDefaults.krylovdim[], + maxiter::Int=KrylovDefaults.maxiter[], + rtol::Real=KrylovDefaults.tol[], + atol::Real=KrylovDefaults.tol[], + tol::Real=max(atol, rtol * norm(b)), + orth=KrylovDefaults.orth, + verbosity::Int=KrylovDefaults.verbosity[]) if (T <: Real && issymmetric) || ishermitian if isposdef - return CG(; maxiter = krylovdim * maxiter, tol = tol, verbosity = verbosity) + return CG(; maxiter=krylovdim * maxiter, tol=tol, verbosity=verbosity) else # TODO: implement MINRES for symmetric but not posdef; for now use GRMES # return MINRES(krylovdim*maxiter, tol=tol) end end return GMRES(; - krylovdim = krylovdim, - maxiter = maxiter, - tol = tol, - orth = orth, - verbosity = verbosity - ) + krylovdim=krylovdim, + maxiter=maxiter, + tol=tol, + orth=orth, + verbosity=verbosity) end """ @@ -247,12 +239,10 @@ and our `maxiter` parameter counts the number of outer iterations, i.e. restart used, and therefore no restarts are required. Therefore, we pass `krylovdim*maxiter` as the maximal number of CG iterations that can be used by the `CG` algorithm. """ -function reallinsolve(f, b, x₀, alg, a₀::Real = 0, a₁::Real = 1) +function reallinsolve(f, b, x₀, alg, a₀::Real=0, a₁::Real=1) x, info = linsolve(f, RealVec(b), RealVec(x₀), alg, a₀, a₁) - newinfo = ConvergenceInfo( - info.converged, info.residual[], info.normres, info.numiter, - info.numops - ) + newinfo = ConvergenceInfo(info.converged, info.residual[], info.normres, info.numiter, + info.numops) return x[], newinfo end diff --git a/src/lssolve/lsmr.jl b/src/lssolve/lsmr.jl index 75043afc..5d4b74f6 100644 --- a/src/lssolve/lsmr.jl +++ b/src/lssolve/lsmr.jl @@ -1,4 +1,4 @@ -function lssolve(operator, b, alg::LSMR, λ_::Real = 0) +function lssolve(operator, b, alg::LSMR, λ_::Real=0) # Initialisation: determine number type u₀ = b v₀ = apply_adjoint(operator, u₀) diff --git a/src/lssolve/lssolve.jl b/src/lssolve/lssolve.jl index 2c20e9a8..ca28994b 100644 --- a/src/lssolve/lssolve.jl +++ b/src/lssolve/lssolve.jl @@ -98,14 +98,12 @@ Currently, only [`LSMR`](@ref) is available and thus selected. """ function lssolve end -function lssolve( - f, b, λ::Real = 0; - rtol::Real = KrylovDefaults.tol[], - atol::Real = KrylovDefaults.tol[], - tol::Real = max(atol, rtol * norm(b)), - kwargs... - ) - alg = LSMR(; tol = tol, kwargs...) +function lssolve(f, b, λ::Real=0; + rtol::Real=KrylovDefaults.tol[], + atol::Real=KrylovDefaults.tol[], + tol::Real=max(atol, rtol * norm(b)), + kwargs...) + alg = LSMR(; tol=tol, kwargs...) return lssolve(f, b, alg, λ) end @@ -187,11 +185,9 @@ The final (expert) method, without default values and keyword arguments, is the finally called, and can also be used directly. Here, one specifies the algorithm explicitly. Currently, only [`LSMR`](@ref) is available and thus selected. """ -function reallssolve(f, b, alg, λ::Real = 0) +function reallssolve(f, b, alg, λ::Real=0) x, info = lssolve(f, RealVec(b), alg, λ) - newinfo = ConvergenceInfo( - info.converged, info.residual[], info.normres, info.numiter, - info.numops - ) + newinfo = ConvergenceInfo(info.converged, info.residual[], info.normres, info.numiter, + info.numops) return x[], newinfo end diff --git a/src/matrixfun/expintegrator.jl b/src/matrixfun/expintegrator.jl index 2eed54a1..4e22cb10 100644 --- a/src/matrixfun/expintegrator.jl +++ b/src/matrixfun/expintegrator.jl @@ -98,7 +98,7 @@ function expintegrator(A, t::Number, u₀, us...; kwargs...) return expintegrator(A, t, (u₀, us...), alg) end -function expintegrator(A, t::Number, u::Tuple, alg::Union{Lanczos, Arnoldi}) +function expintegrator(A, t::Number, u::Tuple, alg::Union{Lanczos,Arnoldi}) length(u) == 1 && return expintegrator(A, t, (u[1], zerovector(u[1])), alg) p = length(u) - 1 @@ -172,7 +172,7 @@ function expintegrator(A, t::Number, u::Tuple, alg::Union{Lanczos, Arnoldi}) else iter = ArnoldiIterator(A, w[p + 1], alg.orth) end - fact = initialize(iter; verbosity = alg.verbosity) + fact = initialize(iter; verbosity=alg.verbosity) numops += 1 sizehint!(fact, krylovdim) @@ -283,7 +283,7 @@ function expintegrator(A, t::Number, u::Tuple, alg::Union{Lanczos, Arnoldi}) end end if K < krylovdim - fact = expand!(iter, fact; verbosity = alg.verbosity) + fact = expand!(iter, fact; verbosity=alg.verbosity) numops += 1 else for j in 1:p @@ -314,7 +314,7 @@ function expintegrator(A, t::Number, u::Tuple, alg::Union{Lanczos, Arnoldi}) else iter = ArnoldiIterator(A, w[p + 1], alg.orth) end - fact = initialize!(iter, fact; verbosity = alg.verbosity) + fact = initialize!(iter, fact; verbosity=alg.verbosity) numops += 1 numiter += 1 end diff --git a/src/matrixfun/exponentiate.jl b/src/matrixfun/exponentiate.jl index cf264e3f..9fc9628c 100644 --- a/src/matrixfun/exponentiate.jl +++ b/src/matrixfun/exponentiate.jl @@ -81,4 +81,4 @@ for integrating a linear non-homogeneous ODE. function exponentiate end exponentiate(A, t::Number, v; kwargs...) = expintegrator(A, t, v; kwargs...) -exponentiate(A, t::Number, v, alg::Union{Lanczos, Arnoldi}) = expintegrator(A, t, (v,), alg) +exponentiate(A, t::Number, v, alg::Union{Lanczos,Arnoldi}) = expintegrator(A, t, (v,), alg) diff --git a/src/orthonormal.jl b/src/orthonormal.jl index 9089b8f3..dbd8f88d 100644 --- a/src/orthonormal.jl +++ b/src/orthonormal.jl @@ -65,7 +65,7 @@ const BLOCKSIZE = 4096 # this uses functionality beyond VectorInterface, but can be faster _use_multithreaded_array_kernel(y) = _use_multithreaded_array_kernel(typeof(y)) _use_multithreaded_array_kernel(::Type) = false -function _use_multithreaded_array_kernel(::Type{<:Array{T}}) where {T <: Number} +function _use_multithreaded_array_kernel(::Type{<:Array{T}}) where {T<:Number} return isbitstype(T) && get_num_threads() > 1 end function _use_multithreaded_array_kernel(::Type{<:OrthonormalBasis{T}}) where {T} @@ -85,10 +85,8 @@ projecting the vector `x` onto the subspace spanned by `b`; more specifically th for all ``j ∈ r``. """ -function project!!( - y::AbstractVector, b::OrthonormalBasis, x, - α::Number = true, β::Number = false, r = Base.OneTo(length(b)) - ) +function project!!(y::AbstractVector, b::OrthonormalBasis, x, + α::Number=true, β::Number=false, r=Base.OneTo(length(b))) # no specialized routine for IndexLinear x because reduction dimension is large dimension length(y) == length(r) || throw(DimensionMismatch()) if get_num_threads() > 1 @@ -129,10 +127,8 @@ this computes y = β*y + α * sum(b[r[i]]*x[i] for i = 1:length(r)) ``` """ -function unproject!!( - y, b::OrthonormalBasis, x::AbstractVector, - α::Number = true, β::Number = false, r = Base.OneTo(length(b)) - ) +function unproject!!(y, b::OrthonormalBasis, x::AbstractVector, + α::Number=true, β::Number=false, r=Base.OneTo(length(b))) if _use_multithreaded_array_kernel(y) return unproject_linear_multithreaded!(y, b, x, α, β, r) end @@ -148,10 +144,11 @@ function unproject!!( end return y end -function unproject_linear_multithreaded!( - y::AbstractArray, b::OrthonormalBasis{<:AbstractArray}, x::AbstractVector, - α::Number = true, β::Number = false, r = Base.OneTo(length(b)) - ) +function unproject_linear_multithreaded!(y::AbstractArray, + b::OrthonormalBasis{<:AbstractArray}, + x::AbstractVector, + α::Number=true, β::Number=false, + r=Base.OneTo(length(b))) # multi-threaded implementation, similar to BLAS level 2 matrix vector multiplication m = length(y) n = length(r) @@ -171,10 +168,9 @@ function unproject_linear_multithreaded!( end return y end -function unproject_linear_kernel!( - y::AbstractArray, b::OrthonormalBasis{<:AbstractArray}, x::AbstractVector, - I, α::Number, β::Number, r - ) +function unproject_linear_kernel!(y::AbstractArray, b::OrthonormalBasis{<:AbstractArray}, + x::AbstractVector, + I, α::Number, β::Number, r) return @inbounds begin if β == 0 @simd for i in I @@ -207,10 +203,8 @@ Perform a rank 1 update of a basis `b`, i.e. update the basis vectors as It is the user's responsibility to make sure that the result is still an orthonormal basis. """ -@fastmath function rank1update!( - b::OrthonormalBasis, y, x::AbstractVector, - α::Number = true, β::Number = true, r = Base.OneTo(length(b)) - ) +@fastmath function rank1update!(b::OrthonormalBasis, y, x::AbstractVector, + α::Number=true, β::Number=true, r=Base.OneTo(length(b))) if _use_multithreaded_array_kernel(y) return rank1update_linear_multithreaded!(b, y, x, α, β, r) end @@ -227,10 +221,9 @@ It is the user's responsibility to make sure that the result is still an orthono end return b end -@fastmath function rank1update_linear_multithreaded!( - b::OrthonormalBasis{<:AbstractArray}, y::AbstractArray, x::AbstractVector, - α::Number, β::Number, r - ) +@fastmath function rank1update_linear_multithreaded!(b::OrthonormalBasis{<:AbstractArray}, + y::AbstractArray, x::AbstractVector, + α::Number, β::Number, r) # multi-threaded implementation, similar to BLAS level 2 matrix vector multiplication m = length(y) n = length(r) @@ -320,9 +313,8 @@ function basistransform!(b::OrthonormalBasis{T}, U::AbstractMatrix) where {T} # return b end -function basistransform_linear_multithreaded!( - b::OrthonormalBasis{<:AbstractArray}, U::AbstractMatrix - ) # U should be unitary or isometric +function basistransform_linear_multithreaded!(b::OrthonormalBasis{<:AbstractArray}, + U::AbstractMatrix) # U should be unitary or isometric m, n = size(U) m == length(b) || throw(DimensionMismatch()) K = length(b[1]) @@ -375,31 +367,27 @@ function orthogonalize!!(v::T, b::OrthonormalBasis{T}, alg::Orthogonalizer) wher return orthogonalize!!(v, b, c, alg) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ClassicalGramSchmidt - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ClassicalGramSchmidt) where {T} x = project!!(x, b, v) v = unproject!!(v, b, x, -1, 1) return (v, x) end -function reorthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ClassicalGramSchmidt - ) where {T} +function reorthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ClassicalGramSchmidt) where {T} s = similar(x) ## EXTRA ALLOCATION s = project!!(s, b, v) v = unproject!!(v, b, s, -1, 1) x .+= s return (v, x) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ClassicalGramSchmidt2 - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ClassicalGramSchmidt2) where {T} (v, x) = orthogonalize!!(v, b, x, ClassicalGramSchmidt()) return reorthogonalize!!(v, b, x, ClassicalGramSchmidt()) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, alg::ClassicalGramSchmidtIR - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + alg::ClassicalGramSchmidtIR) where {T} nold = norm(v) (v, x) = orthogonalize!!(v, b, x, ClassicalGramSchmidt()) nnew = norm(v) @@ -411,9 +399,8 @@ function orthogonalize!!( return (v, x) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ModifiedGramSchmidt - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ModifiedGramSchmidt) where {T} for (i, q) in enumerate(b) s = inner(q, v) v = add!!(v, q, -s) @@ -421,9 +408,8 @@ function orthogonalize!!( end return (v, x) end -function reorthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ModifiedGramSchmidt - ) where {T} +function reorthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ModifiedGramSchmidt) where {T} for (i, q) in enumerate(b) s = inner(q, v) v = add!!(v, q, -s) @@ -431,15 +417,13 @@ function reorthogonalize!!( end return (v, x) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, ::ModifiedGramSchmidt2 - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + ::ModifiedGramSchmidt2) where {T} (v, x) = orthogonalize!!(v, b, x, ModifiedGramSchmidt()) return reorthogonalize!!(v, b, x, ModifiedGramSchmidt()) end -function orthogonalize!!( - v::T, b::OrthonormalBasis{T}, x::AbstractVector, alg::ModifiedGramSchmidtIR - ) where {T} +function orthogonalize!!(v::T, b::OrthonormalBasis{T}, x::AbstractVector, + alg::ModifiedGramSchmidtIR) where {T} nold = norm(v) (v, x) = orthogonalize!!(v, b, x, ModifiedGramSchmidt()) nnew = norm(v) @@ -455,25 +439,22 @@ end orthogonalize!!(v::T, q::T, alg::Orthogonalizer) where {T} = _orthogonalize!!(v, q, alg) # avoid method ambiguity on Julia 1.0 according to Aqua.jl -function _orthogonalize!!( - v::T, q::T, alg::Union{ClassicalGramSchmidt, ModifiedGramSchmidt} - ) where {T} +function _orthogonalize!!(v::T, q::T, + alg::Union{ClassicalGramSchmidt,ModifiedGramSchmidt}) where {T} s = inner(q, v) v = add!!(v, q, -s) return (v, s) end -function _orthogonalize!!( - v::T, q::T, alg::Union{ClassicalGramSchmidt2, ModifiedGramSchmidt2} - ) where {T} +function _orthogonalize!!(v::T, q::T, + alg::Union{ClassicalGramSchmidt2,ModifiedGramSchmidt2}) where {T} s = inner(q, v) v = add!!(v, q, -s) ds = inner(q, v) v = add!!(v, q, -ds) return (v, s + ds) end -function _orthogonalize!!( - v::T, q::T, alg::Union{ClassicalGramSchmidtIR, ModifiedGramSchmidtIR} - ) where {T} +function _orthogonalize!!(v::T, q::T, + alg::Union{ClassicalGramSchmidtIR,ModifiedGramSchmidtIR}) where {T} nold = norm(v) s = inner(q, v) v = add!!(v, q, -s) diff --git a/test/ad/eigsolve.jl b/test/ad/eigsolve.jl index 2e363d87..a8cbf2c0 100644 --- a/test/ad/eigsolve.jl +++ b/test/ad/eigsolve.jl @@ -17,14 +17,14 @@ function build_mat_example(A, x, howmany::Int, which, alg, alg_rrule) vals, vecs, info = eigsolve(A, x, howmany, which, alg) info.converged < howmany && @warn "eigsolve did not converge" if eltype(A) <: Real && length(vals) > howmany && - vals[howmany] == conj(vals[howmany + 1]) + vals[howmany] == conj(vals[howmany + 1]) howmany += 1 end function mat_example(Av, xv) à = A_fromvec(Av) x̃ = x_fromvec(xv) - vals′, vecs′, info′ = eigsolve(Ã, x̃, howmany, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(Ã, x̃, howmany, which, alg; alg_rrule=alg_rrule) info′.converged < howmany && println("eigsolve did not converge") catresults = vcat(vals′[1:howmany], vecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -38,7 +38,7 @@ function build_mat_example(A, x, howmany::Int, which, alg, alg_rrule) à = A_fromvec(Av) x̃ = x_fromvec(xv) f = x -> à * x - vals′, vecs′, info′ = eigsolve(f, x̃, howmany, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(f, x̃, howmany, which, alg; alg_rrule=alg_rrule) info′.converged < howmany && println("eigsolve did not converge") catresults = vcat(vals′[1:howmany], vecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -51,7 +51,7 @@ function build_mat_example(A, x, howmany::Int, which, alg, alg_rrule) function mat_example_fd(Av, xv) à = A_fromvec(Av) x̃ = x_fromvec(xv) - vals′, vecs′, info′ = eigsolve(Ã, x̃, howmany, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(Ã, x̃, howmany, which, alg; alg_rrule=alg_rrule) info′.converged < howmany && println("eigsolve did not converge") for i in 1:howmany d = dot(vecs[i], vecs′[i]) @@ -74,10 +74,8 @@ function testfun(A, x, c, d) return A * x + c * dot(d, x) end testfunthunk(A, x, c, d) = testfun(A, x, c, d) -function ChainRulesCore.rrule( - config::RuleConfig{>:HasReverseMode}, ::typeof(testfunthunk), - args... - ) +function ChainRulesCore.rrule(config::RuleConfig{>:HasReverseMode}, ::typeof(testfunthunk), + args...) y = testfunthunk(args...) function thunkedpb(dy) pb = rrule_via_ad(config, testfun, args...)[2] @@ -97,7 +95,7 @@ function build_fun_example(A, x, c, d, howmany::Int, which, alg, alg_rrule) end info.converged < howmany && @warn "eigsolve did not converge" if eltype(A) <: Real && length(vals) > howmany && - vals[howmany] == conj(vals[howmany + 1]) + vals[howmany] == conj(vals[howmany + 1]) howmany += 1 end @@ -108,10 +106,8 @@ function build_fun_example(A, x, c, d, howmany::Int, which, alg, alg_rrule) c̃ = vecfromvec(cv) d̃ = vecfromvec(dv) - vals′, vecs′, info′ = eigsolve( - x̃, howmany′, which, alg; - alg_rrule = alg_rrule - ) do y + vals′, vecs′, info′ = eigsolve(x̃, howmany′, which, alg; + alg_rrule=alg_rrule) do y return testfunthunk(Ã, y, c̃, d̃) end info′.converged < howmany′ && println("eigsolve did not converge") @@ -131,10 +127,8 @@ function build_fun_example(A, x, c, d, howmany::Int, which, alg, alg_rrule) c̃ = vecfromvec(cv) d̃ = vecfromvec(dv) - vals′, vecs′, info′ = eigsolve( - x̃, howmany′, which, alg; - alg_rrule = alg_rrule - ) do y + vals′, vecs′, info′ = eigsolve(x̃, howmany′, which, alg; + alg_rrule=alg_rrule) do y return à * y + c̃ * dot(d̃, y) end info′.converged < howmany′ && println("eigsolve did not converge") @@ -171,10 +165,8 @@ function build_hermitianfun_example(A, x, c, howmany::Int, which, alg, alg_rrule x̃ = xvecfromvec(xv) c̃ = cvecfromvec(cv) - vals′, vecs′, info′ = eigsolve( - x̃, howmany, which, alg; - alg_rrule = alg_rrule - ) do y + vals′, vecs′, info′ = eigsolve(x̃, howmany, which, alg; + alg_rrule=alg_rrule) do y return testfunthunk(Hermitian(Ã), y, c̃, c̃) end info′.converged < howmany && println("eigsolve did not converge") @@ -191,10 +183,8 @@ function build_hermitianfun_example(A, x, c, howmany::Int, which, alg, alg_rrule x̃ = xvecfromvec(xv) c̃ = cvecfromvec(cv) - vals′, vecs′, info′ = eigsolve( - x̃, howmany, which, alg; - alg_rrule = alg_rrule - ) do y + vals′, vecs′, info′ = eigsolve(x̃, howmany, which, alg; + alg_rrule=alg_rrule) do y return Hermitian(Ã) * y + c̃ * dot(c̃, y) end info′.converged < howmany && println("eigsolve did not converge") @@ -216,10 +206,8 @@ function build_hermitianfun_example(A, x, c, howmany::Int, which, alg, alg_rrule end @timedtestset "Small eigsolve AD test for eltype=$T" for T in - ( - Float32, Float64, ComplexF32, - ComplexF64, - ) + (Float32, Float64, ComplexF32, + ComplexF64) if T <: Complex whichlist = (:LM, :SR, :LR, :SI, :LI) else @@ -232,18 +220,16 @@ end howmany = 3 condA = cond(A) tol = tolerance(T) # n * condA * (T <: Real ? eps(T) : 4 * eps(real(T))) - alg = Arnoldi(; tol = tol, krylovdim = n) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) - alg_rrule2 = GMRES(; tol = tol, krylovdim = n + 1, verbosity = SILENT_LEVEL) + alg = Arnoldi(; tol=tol, krylovdim=n) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) + alg_rrule2 = GMRES(; tol=tol, krylovdim=n + 1, verbosity=SILENT_LEVEL) config = Zygote.ZygoteRuleConfig() @testset for which in whichlist for alg_rrule in (alg_rrule1, alg_rrule2) # unfortunately, rrule does not seem type stable for function arguments, because the # `rrule_via_ad` call does not produce type stable `rrule`s for the function - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - which, alg; alg_rrule = alg_rrule - ) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + which, alg; alg_rrule=alg_rrule) # NOTE: the following is not necessary here, as it is corrected for in the `eigsolve` rrule # if length(vals) > howmany && vals[howmany] == conj(vals[howmany + 1]) # howmany += 1 @@ -251,12 +237,8 @@ end @constinferred pb((ZeroTangent(), ZeroTangent(), NoTangent())) @constinferred pb((randn(T, howmany), ZeroTangent(), NoTangent())) @constinferred pb((randn(T, howmany), [randn(T, n)], NoTangent())) - @constinferred pb( - ( - randn(T, howmany), [randn(T, n) for _ in 1:howmany], - NoTangent(), - ) - ) + @constinferred pb((randn(T, howmany), [randn(T, n) for _ in 1:howmany], + NoTangent())) end for alg_rrule in (alg_rrule1, alg_rrule2) @@ -269,8 +251,8 @@ end (JA2, Jx2) = Zygote.jacobian(mat_example_fun, Avec, xvec) # finite difference comparison using some kind of tolerance heuristic - @test isapprox(JA, JA1; rtol = condA * sqrt(eps(real(T)))) - @test all(isapprox.(JA1, JA2; atol = n * eps(real(T)))) + @test isapprox(JA, JA1; rtol=condA * sqrt(eps(real(T)))) + @test all(isapprox.(JA1, JA2; atol=n * eps(real(T)))) @test norm(Jx, Inf) < condA * sqrt(eps(real(T))) @test all(iszero, Jx1) @test all(iszero, Jx2) @@ -278,108 +260,78 @@ end # some analysis ∂vals = complex.(JA1[1:howmany, :], JA1[howmany * (n + 1) .+ (1:howmany), :]) ∂vecs = map(1:howmany) do i - return complex.( - JA1[(howmany + (i - 1) * n) .+ (1:n), :], - JA1[(howmany * (n + 2) + (i - 1) * n) .+ (1:n), :] - ) + return complex.(JA1[(howmany + (i - 1) * n) .+ (1:n), :], + JA1[(howmany * (n + 2) + (i - 1) * n) .+ (1:n), :]) end if eltype(A) <: Complex # test holomorphicity / Cauchy-Riemann equations # for eigenvalues - @test real(∂vals[:, 1:2:(2n^2)]) ≈ +imag(∂vals[:, 2:2:(2n^2)]) - @test imag(∂vals[:, 1:2:(2n^2)]) ≈ -real(∂vals[:, 2:2:(2n^2)]) + @test real(∂vals[:, 1:2:(2n ^ 2)]) ≈ +imag(∂vals[:, 2:2:(2n ^ 2)]) + @test imag(∂vals[:, 1:2:(2n ^ 2)]) ≈ -real(∂vals[:, 2:2:(2n ^ 2)]) # and for eigenvectors for i in 1:howmany - @test real(∂vecs[i][:, 1:2:(2n^2)]) ≈ +imag(∂vecs[i][:, 2:2:(2n^2)]) - @test imag(∂vecs[i][:, 1:2:(2n^2)]) ≈ -real(∂vecs[i][:, 2:2:(2n^2)]) + @test real(∂vecs[i][:, 1:2:(2n ^ 2)]) ≈ +imag(∂vecs[i][:, 2:2:(2n ^ 2)]) + @test imag(∂vecs[i][:, 1:2:(2n ^ 2)]) ≈ -real(∂vecs[i][:, 2:2:(2n ^ 2)]) end end # test orthogonality of vecs and ∂vecs for i in 1:howmany - @test all(isapprox.(abs.(vecs[i]' * ∂vecs[i]), 0; atol = sqrt(eps(real(T))))) + @test all(isapprox.(abs.(vecs[i]' * ∂vecs[i]), 0; atol=sqrt(eps(real(T))))) end end end if T <: Complex @testset "test warnings and info" begin - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) @test_logs pb((ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], NoTangent())) - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = n, verbosity = WARN_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) - @test_logs (:warn,) pb( - ( - ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], - NoTangent(), - ) - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=n, verbosity=WARN_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) + @test_logs (:warn,) pb((ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], + NoTangent())) pbs = @test_logs pb((ZeroTangent(), vecs[1:2], NoTangent())) @test norm(unthunk(pbs[1]), Inf) < condA * sqrt(eps(real(T))) - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = WARN_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = n, verbosity = STARTSTOP_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:info,) pb( - ( - ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], - NoTangent(), - ) - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=WARN_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=n, verbosity=STARTSTOP_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) + @test_logs (:warn,) (:info,) pb((ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], + NoTangent())) pbs = @test_logs (:info,) pb((ZeroTangent(), vecs[1:2], NoTangent())) @test norm(unthunk(pbs[1]), Inf) < condA * sqrt(eps(real(T))) - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) @test_logs pb((ZeroTangent(), im .* vecs[1:2] .+ vecs[2:-1:1], NoTangent())) - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = SILENT_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = n, verbosity = WARN_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:warn,) pb( - ( - ZeroTangent(), - im .* vecs[1:2] .+ - vecs[2:-1:1], - NoTangent(), - ) - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=SILENT_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=n, verbosity=WARN_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) + @test_logs (:warn,) (:warn,) pb((ZeroTangent(), + im .* vecs[1:2] .+ + vecs[2:-1:1], + NoTangent())) pbs = @test_logs pb((ZeroTangent(), vecs[1:2], NoTangent())) @test norm(unthunk(pbs[1]), Inf) < condA * sqrt(eps(real(T))) - alg = Arnoldi(; tol = tol, krylovdim = n, verbosity = WARN_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = n, verbosity = STARTSTOP_LEVEL) - (vals, vecs, info), pb = ChainRulesCore.rrule( - config, eigsolve, A, x, howmany, - :LR, alg; alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb( - ( - ZeroTangent(), - im .* - vecs[1:2] .+ - vecs[2:-1:1], - NoTangent(), - ) - ) + alg = Arnoldi(; tol=tol, krylovdim=n, verbosity=WARN_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=n, verbosity=STARTSTOP_LEVEL) + (vals, vecs, info), pb = ChainRulesCore.rrule(config, eigsolve, A, x, howmany, + :LR, alg; alg_rrule=alg_rrule) + @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb((ZeroTangent(), + im .* + vecs[1:2] .+ + vecs[2:-1:1], + NoTangent())) pbs = @test_logs (:info,) (:info,) pb((ZeroTangent(), vecs[1:2], NoTangent())) @test norm(unthunk(pbs[1]), Inf) < condA * sqrt(eps(real(T))) end @@ -401,30 +353,28 @@ end howmany = 2 tol = tolerance(T) # 2 * N^2 * eps(real(T)) - alg = Arnoldi(; tol = tol, krylovdim = 2n) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) - alg_rrule2 = GMRES(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) + alg = Arnoldi(; tol=tol, krylovdim=2n) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) + alg_rrule2 = GMRES(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) @testset for alg_rrule in (alg_rrule1, alg_rrule2) #! format: off fun_example, fun_example_fd, Avec, xvec, cvec, dvec, vals, vecs, howmany = build_fun_example(A, x, c, d, howmany, which, alg, alg_rrule) #! format: on - (JA, Jx, Jc, Jd) = FiniteDifferences.jacobian( - fdm, fun_example_fd, Avec, xvec, - cvec, dvec - ) + (JA, Jx, Jc, Jd) = FiniteDifferences.jacobian(fdm, fun_example_fd, Avec, xvec, + cvec, dvec) (JA′, Jx′, Jc′, Jd′) = Zygote.jacobian(fun_example, Avec, xvec, cvec, dvec) rtol = cond(A + c * d') * sqrt(eps(real(T))) - @test isapprox(JA, JA′; rtol = rtol) - @test isapprox(Jc, Jc′; rtol = rtol) - @test isapprox(Jd, Jd′; rtol = rtol) + @test isapprox(JA, JA′; rtol=rtol) + @test isapprox(Jc, Jc′; rtol=rtol) + @test isapprox(Jd, Jd′; rtol=rtol) end end end @timedtestset "Large Hermitian eigsolve AD test with eltype=$T" for T in - (Float64, ComplexF64) + (Float64, ComplexF64) whichlist = (:LR, :SR) @testset for which in whichlist A = rand(T, (N, N)) .- one(T) / 2 @@ -435,23 +385,21 @@ end howmany = 2 tol = tolerance(T) - alg = Lanczos(; tol = tol, krylovdim = 2n) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) - alg_rrule2 = GMRES(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) + alg = Lanczos(; tol=tol, krylovdim=2n) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) + alg_rrule2 = GMRES(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) @testset for alg_rrule in (alg_rrule1, alg_rrule2) #! format: off fun_example, fun_example_fd, Avec, xvec, cvec, vals, vecs, howmany = build_hermitianfun_example(A, x, c, howmany, which, alg, alg_rrule) #! format: on - (JA, Jx, Jc) = FiniteDifferences.jacobian( - fdm, fun_example_fd, Avec, xvec, - cvec - ) + (JA, Jx, Jc) = FiniteDifferences.jacobian(fdm, fun_example_fd, Avec, xvec, + cvec) (JA′, Jx′, Jc′) = Zygote.jacobian(fun_example, Avec, xvec, cvec) rtol = cond(A + c * c') * sqrt(eps(real(T))) - @test isapprox(JA, JA′; rtol = rtol) - @test isapprox(Jc, Jc′; rtol = rtol) + @test isapprox(JA, JA′; rtol=rtol) + @test isapprox(Jc, Jc′; rtol=rtol) end end end diff --git a/test/ad/linsolve.jl b/test/ad/linsolve.jl index 0bee8699..e83eb6c6 100644 --- a/test/ad/linsolve.jl +++ b/test/ad/linsolve.jl @@ -18,7 +18,7 @@ function build_mat_example(A, b, x, alg, alg_rrule) à = A_fromvec(Av) b̃ = b_fromvec(bv) x̃ = x_fromvec(xv) - x, info = linsolve(Ã, b̃, x̃, alg; alg_rrule = alg_rrule) + x, info = linsolve(Ã, b̃, x̃, alg; alg_rrule=alg_rrule) info.converged == 0 && println("linsolve did not converge: normres = ", info.normres) xv, = to_vec(x) @@ -29,7 +29,7 @@ function build_mat_example(A, b, x, alg, alg_rrule) b̃ = b_fromvec(bv) x̃ = x_fromvec(xv) f = x -> à * x - x, info = linsolve(f, b̃, x̃, alg; alg_rrule = alg_rrule) + x, info = linsolve(f, b̃, x̃, alg; alg_rrule=alg_rrule) info.converged == 0 && println("linsolve did not converge: normres = ", info.normres) xv, = to_vec(x) @@ -42,10 +42,8 @@ function testfun(A, x, c, d) return A * x + c * dot(d, x) end testfunthunk(A, x, c, d) = testfun(A, x, c, d) -function ChainRulesCore.rrule( - config::RuleConfig{>:HasReverseMode}, ::typeof(testfunthunk), - args... - ) +function ChainRulesCore.rrule(config::RuleConfig{>:HasReverseMode}, ::typeof(testfunthunk), + args...) y = testfunthunk(args...) function thunkedpb(dy) pb = rrule_via_ad(config, testfun, args...)[2] @@ -70,7 +68,7 @@ function build_fun_example(A, b, c, d, e, f, alg, alg_rrule) ẽ = scalarfromvec(ev) f̃ = scalarfromvec(fv) - x, info = linsolve(b̃, zero(b̃), alg, ẽ, f̃; alg_rrule = alg_rrule) do y + x, info = linsolve(b̃, zero(b̃), alg, ẽ, f̃; alg_rrule=alg_rrule) do y return testfunthunk(Ã, y, c̃, d̃) end # info.converged > 0 || @warn "not converged" @@ -80,10 +78,8 @@ function build_fun_example(A, b, c, d, e, f, alg, alg_rrule) return fun_example, Avec, bvec, cvec, dvec, evec, fvec end -@testset "Small linsolve AD test with eltype=$T" for T in ( - Float32, Float64, ComplexF32, - ComplexF64, - ) +@testset "Small linsolve AD test with eltype=$T" for T in (Float32, Float64, ComplexF32, + ComplexF64) A = 2 * (rand(T, (n, n)) .- one(T) / 2) b = 2 * (rand(T, n) .- one(T) / 2) b /= norm(b) @@ -91,10 +87,10 @@ end condA = cond(A) tol = tolerance(T) #condA * (T <: Real ? eps(T) : 4 * eps(real(T))) - alg = GMRES(; tol = tol, krylovdim = n, maxiter = 1) + alg = GMRES(; tol=tol, krylovdim=n, maxiter=1) config = Zygote.ZygoteRuleConfig() - _, pb = ChainRulesCore.rrule(config, linsolve, A, b, x, alg, 0, 1; alg_rrule = alg) + _, pb = ChainRulesCore.rrule(config, linsolve, A, b, x, alg, 0, 1; alg_rrule=alg) @constinferred pb((ZeroTangent(), NoTangent())) @constinferred pb((rand(T, n), NoTangent())) @@ -103,8 +99,8 @@ end (JA1, Jb1, Jx1) = Zygote.jacobian(mat_example, Avec, bvec, xvec) (JA2, Jb2, Jx2) = Zygote.jacobian(mat_example_fun, Avec, bvec, xvec) - @test isapprox(JA, JA1; rtol = condA * sqrt(eps(real(T)))) - @test all(isapprox.(JA1, JA2; atol = n * eps(real(T)))) + @test isapprox(JA, JA1; rtol=condA * sqrt(eps(real(T)))) + @test all(isapprox.(JA1, JA2; atol=n * eps(real(T)))) # factor 2 is minimally necessary for complex case, but 3 is more robust @test norm(Jx, Inf) < condA * sqrt(eps(real(T))) @test all(iszero, Jx1) @@ -121,30 +117,26 @@ end # mix algorithms] tol = tolerance(T) # N^2 * eps(real(T)) - alg1 = GMRES(; tol = tol, krylovdim = 20) - alg2 = BiCGStab(; tol = tol, maxiter = 100) # BiCGStab seems to require slightly smaller tolerance for tests to work + alg1 = GMRES(; tol=tol, krylovdim=20) + alg2 = BiCGStab(; tol=tol, maxiter=100) # BiCGStab seems to require slightly smaller tolerance for tests to work for (alg, alg_rrule) in ((alg1, alg2), (alg2, alg1)) #! format: off fun_example, Avec, bvec, cvec, dvec, evec, fvec = build_fun_example(A, b, c, d, e, f, alg, alg_rrule) #! format: on - (JA, Jb, Jc, Jd, Je, Jf) = FiniteDifferences.jacobian( - fdm, fun_example, - Avec, bvec, cvec, dvec, evec, - fvec - ) - (JA′, Jb′, Jc′, Jd′, Je′, Jf′) = Zygote.jacobian( - fun_example, Avec, bvec, cvec, - dvec, evec, fvec - ) + (JA, Jb, Jc, Jd, Je, Jf) = FiniteDifferences.jacobian(fdm, fun_example, + Avec, bvec, cvec, dvec, evec, + fvec) + (JA′, Jb′, Jc′, Jd′, Je′, Jf′) = Zygote.jacobian(fun_example, Avec, bvec, cvec, + dvec, evec, fvec) rtol = 2 * cond(A + c * d') * sqrt(eps(real(T))) - @test isapprox(JA, JA′; rtol = rtol) - @test isapprox(Jb, Jb′; rtol = rtol) - @test isapprox(Jc, Jc′; rtol = rtol) - @test isapprox(Jd, Jd′; rtol = rtol) - @test isapprox(Je, Je′; rtol = rtol) - @test isapprox(Jf, Jf′; rtol = rtol) + @test isapprox(JA, JA′; rtol=rtol) + @test isapprox(Jb, Jb′; rtol=rtol) + @test isapprox(Jc, Jc′; rtol=rtol) + @test isapprox(Jd, Jd′; rtol=rtol) + @test isapprox(Je, Je′; rtol=rtol) + @test isapprox(Jf, Jf′; rtol=rtol) end end end diff --git a/test/ad/repeatedeigsolve.jl b/test/ad/repeatedeigsolve.jl index b53aa066..25fe3b16 100644 --- a/test/ad/repeatedeigsolve.jl +++ b/test/ad/repeatedeigsolve.jl @@ -30,7 +30,7 @@ function build_mat_example(A, B, C, x, alg, alg_rrule) C̃ = C_fromvec(Cv) x̃ = x_fromvec(xv) M̃ = [zero(Ã) zero(Ã) C̃; à zero(Ã) zero(Ã); zero(Ã) B̃ zero(Ã)] - vals′, vecs′, info′ = eigsolve(M̃, x̃, howmany, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(M̃, x̃, howmany, which, alg; alg_rrule=alg_rrule) info′.converged < howmany && println("eigsolve did not converge") catresults = vcat(vals′[1:howmany], vecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -47,7 +47,7 @@ function build_mat_example(A, B, C, x, alg, alg_rrule) x̃ = x_fromvec(xv) M̃ = [zero(Ã) zero(Ã) C̃; à zero(Ã) zero(Ã); zero(Ã) B̃ zero(Ã)] f = x -> M̃ * x - vals′, vecs′, info′ = eigsolve(f, x̃, howmany, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(f, x̃, howmany, which, alg; alg_rrule=alg_rrule) info′.converged < howmany && println("eigsolve did not converge") catresults = vcat(vals′[1:howmany], vecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -64,7 +64,7 @@ function build_mat_example(A, B, C, x, alg, alg_rrule) x̃ = x_fromvec(xv) M̃ = [zero(Ã) zero(Ã) C̃; à zero(Ã) zero(Ã); zero(Ã) B̃ zero(Ã)] howmany′ = (eltype(Av) <: Complex ? 3 : 6) * howmany - vals′, vecs′, info′ = eigsolve(M̃, x̃, howmany′, which, alg; alg_rrule = alg_rrule) + vals′, vecs′, info′ = eigsolve(M̃, x̃, howmany′, which, alg; alg_rrule=alg_rrule) _, i = findmin(abs.(vals′ .- vals[1])) info′.converged < i && println("eigsolve did not converge") d = dot(vecs[1], vecs′[i]) @@ -80,7 +80,7 @@ function build_mat_example(A, B, C, x, alg, alg_rrule) end return mat_example, mat_example_fun, mat_example_fd, Avec, Bvec, Cvec, xvec, vals, - vecs + vecs end @timedtestset "Repeated eigsolve AD test with eltype=$T" for T in (Float64, ComplexF64) @@ -95,39 +95,37 @@ end x = randn(T, N) tol = tolerance(T) #2 * N^2 * eps(real(T)) - alg = Arnoldi(; tol = tol, krylovdim = 2n) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) - alg_rrule2 = GMRES(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL) + alg = Arnoldi(; tol=tol, krylovdim=2n) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) + alg_rrule2 = GMRES(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL) #! format: off mat_example1, mat_example_fun1, mat_example_fd, Avec, Bvec, Cvec, xvec, vals, vecs = build_mat_example(A, B, C, x, alg, alg_rrule1) mat_example2, mat_example_fun2, mat_example_fd, Avec, Bvec, Cvec, xvec, vals, vecs = build_mat_example(A, B, C, x, alg, alg_rrule2) #! format: on - (JA, JB, JC, Jx) = FiniteDifferences.jacobian( - fdm, mat_example_fd, Avec, Bvec, - Cvec, xvec - ) + (JA, JB, JC, Jx) = FiniteDifferences.jacobian(fdm, mat_example_fd, Avec, Bvec, + Cvec, xvec) (JA1, JB1, JC1, Jx1) = Zygote.jacobian(mat_example1, Avec, Bvec, Cvec, xvec) (JA2, JB2, JC2, Jx2) = Zygote.jacobian(mat_example_fun1, Avec, Bvec, Cvec, xvec) (JA3, JB3, JC3, Jx3) = Zygote.jacobian(mat_example2, Avec, Bvec, Cvec, xvec) (JA4, JB4, JC4, Jx4) = Zygote.jacobian(mat_example_fun2, Avec, Bvec, Cvec, xvec) - @test isapprox(JA, JA1; rtol = N * sqrt(eps(real(T)))) - @test isapprox(JB, JB1; rtol = N * sqrt(eps(real(T)))) - @test isapprox(JC, JC1; rtol = N * sqrt(eps(real(T)))) + @test isapprox(JA, JA1; rtol=N * sqrt(eps(real(T)))) + @test isapprox(JB, JB1; rtol=N * sqrt(eps(real(T)))) + @test isapprox(JC, JC1; rtol=N * sqrt(eps(real(T)))) - @test all(isapprox.(JA1, JA2; atol = n * eps(real(T)))) - @test all(isapprox.(JB1, JB2; atol = n * eps(real(T)))) - @test all(isapprox.(JC1, JC2; atol = n * eps(real(T)))) + @test all(isapprox.(JA1, JA2; atol=n * eps(real(T)))) + @test all(isapprox.(JB1, JB2; atol=n * eps(real(T)))) + @test all(isapprox.(JC1, JC2; atol=n * eps(real(T)))) - @test all(isapprox.(JA1, JA3; atol = tol)) - @test all(isapprox.(JB1, JB3; atol = tol)) - @test all(isapprox.(JC1, JC3; atol = tol)) + @test all(isapprox.(JA1, JA3; atol=tol)) + @test all(isapprox.(JB1, JB3; atol=tol)) + @test all(isapprox.(JC1, JC3; atol=tol)) - @test all(isapprox.(JA1, JA4; atol = tol)) - @test all(isapprox.(JB1, JB4; atol = tol)) - @test all(isapprox.(JC1, JC4; atol = tol)) + @test all(isapprox.(JA1, JA4; atol=tol)) + @test all(isapprox.(JB1, JB4; atol=tol)) + @test all(isapprox.(JC1, JC4; atol=tol)) @test norm(Jx, Inf) < N * sqrt(eps(real(T))) @test all(iszero, Jx1) @@ -144,24 +142,24 @@ end ∂vecsC = complex.(JC1[1 .+ (1:N), :], JC1[N + 2 .+ (1:N), :]) if T <: Complex # test holomorphicity / Cauchy-Riemann equations # for eigenvalues - @test real(∂valsA[1:2:(2n^2)]) ≈ +imag(∂valsA[2:2:(2n^2)]) - @test imag(∂valsA[1:2:(2n^2)]) ≈ -real(∂valsA[2:2:(2n^2)]) - @test real(∂valsB[1:2:(2n^2)]) ≈ +imag(∂valsB[2:2:(2n^2)]) - @test imag(∂valsB[1:2:(2n^2)]) ≈ -real(∂valsB[2:2:(2n^2)]) - @test real(∂valsC[1:2:(2n^2)]) ≈ +imag(∂valsC[2:2:(2n^2)]) - @test imag(∂valsC[1:2:(2n^2)]) ≈ -real(∂valsC[2:2:(2n^2)]) + @test real(∂valsA[1:2:(2n ^ 2)]) ≈ +imag(∂valsA[2:2:(2n ^ 2)]) + @test imag(∂valsA[1:2:(2n ^ 2)]) ≈ -real(∂valsA[2:2:(2n ^ 2)]) + @test real(∂valsB[1:2:(2n ^ 2)]) ≈ +imag(∂valsB[2:2:(2n ^ 2)]) + @test imag(∂valsB[1:2:(2n ^ 2)]) ≈ -real(∂valsB[2:2:(2n ^ 2)]) + @test real(∂valsC[1:2:(2n ^ 2)]) ≈ +imag(∂valsC[2:2:(2n ^ 2)]) + @test imag(∂valsC[1:2:(2n ^ 2)]) ≈ -real(∂valsC[2:2:(2n ^ 2)]) # and for eigenvectors - @test real(∂vecsA[:, 1:2:(2n^2)]) ≈ +imag(∂vecsA[:, 2:2:(2n^2)]) - @test imag(∂vecsA[:, 1:2:(2n^2)]) ≈ -real(∂vecsA[:, 2:2:(2n^2)]) - @test real(∂vecsB[:, 1:2:(2n^2)]) ≈ +imag(∂vecsB[:, 2:2:(2n^2)]) - @test imag(∂vecsB[:, 1:2:(2n^2)]) ≈ -real(∂vecsB[:, 2:2:(2n^2)]) - @test real(∂vecsC[:, 1:2:(2n^2)]) ≈ +imag(∂vecsC[:, 2:2:(2n^2)]) - @test imag(∂vecsC[:, 1:2:(2n^2)]) ≈ -real(∂vecsC[:, 2:2:(2n^2)]) + @test real(∂vecsA[:, 1:2:(2n ^ 2)]) ≈ +imag(∂vecsA[:, 2:2:(2n ^ 2)]) + @test imag(∂vecsA[:, 1:2:(2n ^ 2)]) ≈ -real(∂vecsA[:, 2:2:(2n ^ 2)]) + @test real(∂vecsB[:, 1:2:(2n ^ 2)]) ≈ +imag(∂vecsB[:, 2:2:(2n ^ 2)]) + @test imag(∂vecsB[:, 1:2:(2n ^ 2)]) ≈ -real(∂vecsB[:, 2:2:(2n ^ 2)]) + @test real(∂vecsC[:, 1:2:(2n ^ 2)]) ≈ +imag(∂vecsC[:, 2:2:(2n ^ 2)]) + @test imag(∂vecsC[:, 1:2:(2n ^ 2)]) ≈ -real(∂vecsC[:, 2:2:(2n ^ 2)]) end # test orthogonality of vecs and ∂vecs - @test all(isapprox.(abs.(vecs[1]' * ∂vecsA), 0; atol = sqrt(eps(real(T))))) - @test all(isapprox.(abs.(vecs[1]' * ∂vecsB), 0; atol = sqrt(eps(real(T))))) - @test all(isapprox.(abs.(vecs[1]' * ∂vecsC), 0; atol = sqrt(eps(real(T))))) + @test all(isapprox.(abs.(vecs[1]' * ∂vecsA), 0; atol=sqrt(eps(real(T))))) + @test all(isapprox.(abs.(vecs[1]' * ∂vecsB), 0; atol=sqrt(eps(real(T))))) + @test all(isapprox.(abs.(vecs[1]' * ∂vecsC), 0; atol=sqrt(eps(real(T))))) end end diff --git a/test/ad/svdsolve.jl b/test/ad/svdsolve.jl index de405edd..659a9673 100644 --- a/test/ad/svdsolve.jl +++ b/test/ad/svdsolve.jl @@ -19,10 +19,8 @@ function build_mat_example(A, x, howmany::Int, alg, alg_rrule) function mat_example_mat(Av, xv) à = A_fromvec(Av) x̃ = x_fromvec(xv) - vals′, lvecs′, rvecs′, info′ = svdsolve( - Ã, x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve(Ã, x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") catresults = vcat(vals′[1:howmany], lvecs′[1:howmany]..., rvecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -35,10 +33,8 @@ function build_mat_example(A, x, howmany::Int, alg, alg_rrule) à = A_fromvec(Av) x̃ = x_fromvec(xv) f = (x, adj::Val) -> (adj isa Val{true}) ? adjoint(Ã) * x : à * x - vals′, lvecs′, rvecs′, info′ = svdsolve( - f, x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve(f, x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") catresults = vcat(vals′[1:howmany], lvecs′[1:howmany]..., rvecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -51,10 +47,8 @@ function build_mat_example(A, x, howmany::Int, alg, alg_rrule) à = A_fromvec(Av) x̃ = x_fromvec(xv) (f, fᴴ) = (x -> à * x, x -> adjoint(Ã) * x) - vals′, lvecs′, rvecs′, info′ = svdsolve( - (f, fᴴ), x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve((f, fᴴ), x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") catresults = vcat(vals′[1:howmany], lvecs′[1:howmany]..., rvecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -67,10 +61,8 @@ function build_mat_example(A, x, howmany::Int, alg, alg_rrule) function mat_example_fd(Av, xv) à = A_fromvec(Av) x̃ = x_fromvec(xv) - vals′, lvecs′, rvecs′, info′ = svdsolve( - Ã, x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve(Ã, x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") for i in 1:howmany dl = dot(lvecs[i], lvecs′[i]) @@ -90,7 +82,7 @@ function build_mat_example(A, x, howmany::Int, alg, alg_rrule) end return mat_example_mat, mat_example_ftuple, mat_example_fval, mat_example_fd, Avec, - xvec, vals, lvecs, rvecs + xvec, vals, lvecs, rvecs end function build_fun_example(A, x, c, d, howmany::Int, alg, alg_rrule) @@ -112,10 +104,8 @@ function build_fun_example(A, x, c, d, howmany::Int, alg, alg_rrule) f = y -> à * y + c̃ * dot(d̃, y) fᴴ = y -> adjoint(Ã) * y + d̃ * dot(c̃, y) - vals′, lvecs′, rvecs′, info′ = svdsolve( - (f, fᴴ), x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve((f, fᴴ), x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") catresults = vcat(vals′[1:howmany], lvecs′[1:howmany]..., rvecs′[1:howmany]...) if eltype(catresults) <: Complex @@ -132,10 +122,8 @@ function build_fun_example(A, x, c, d, howmany::Int, alg, alg_rrule) f = y -> à * y + c̃ * dot(d̃, y) fᴴ = y -> adjoint(Ã) * y + d̃ * dot(c̃, y) - vals′, lvecs′, rvecs′, info′ = svdsolve( - (f, fᴴ), x̃, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + vals′, lvecs′, rvecs′, info′ = svdsolve((f, fᴴ), x̃, howmany, :LR, alg; + alg_rrule=alg_rrule) info′.converged < howmany && println("svdsolve did not converge") for i in 1:howmany dl = dot(lvecs[i], lvecs′[i]) @@ -157,10 +145,8 @@ function build_fun_example(A, x, c, d, howmany::Int, alg, alg_rrule) end @timedtestset "Small svdsolve AD test with eltype=$T" for T in - ( - Float32, Float64, ComplexF32, - ComplexF64, - ) + (Float32, Float64, ComplexF32, + ComplexF64) A = 2 * (rand(T, (n, 2 * n)) .- one(T) / 2) x = 2 * (rand(T, n) .- one(T) / 2) x /= norm(x) @@ -168,42 +154,26 @@ end howmany = 3 tol = 3 * n * condA * (T <: Real ? eps(T) : 4 * eps(real(T))) - alg = GKL(; krylovdim = 2n, tol = tol) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 4n, verbosity = SILENT_LEVEL) - alg_rrule2 = GMRES(; tol = tol, krylovdim = 3n, verbosity = SILENT_LEVEL) + alg = GKL(; krylovdim=2n, tol=tol) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=4n, verbosity=SILENT_LEVEL) + alg_rrule2 = GMRES(; tol=tol, krylovdim=3n, verbosity=SILENT_LEVEL) config = Zygote.ZygoteRuleConfig() for alg_rrule in (alg_rrule1, alg_rrule2) # unfortunately, rrule does not seem type stable for function arguments, because the # `rrule_via_ad` call does not produce type stable `rrule`s for the function - _, pb = ChainRulesCore.rrule( - config, svdsolve, A, x, howmany, :LR, alg; - alg_rrule = alg_rrule - ) + _, pb = ChainRulesCore.rrule(config, svdsolve, A, x, howmany, :LR, alg; + alg_rrule=alg_rrule) @constinferred pb((ZeroTangent(), ZeroTangent(), ZeroTangent(), NoTangent())) - @constinferred pb( - ( - randn(real(T), howmany), ZeroTangent(), ZeroTangent(), - NoTangent(), - ) - ) - @constinferred pb( - ( - randn(real(T), howmany), [randn(T, n)], ZeroTangent(), - NoTangent(), - ) - ) - @constinferred pb( - ( - randn(real(T), howmany), [randn(T, n) for _ in 1:howmany], - [randn(T, 2 * n) for _ in 1:howmany], NoTangent(), - ) - ) + @constinferred pb((randn(real(T), howmany), ZeroTangent(), ZeroTangent(), + NoTangent())) + @constinferred pb((randn(real(T), howmany), [randn(T, n)], ZeroTangent(), + NoTangent())) + @constinferred pb((randn(real(T), howmany), [randn(T, n) for _ in 1:howmany], + [randn(T, 2 * n) for _ in 1:howmany], NoTangent())) end for alg_rrule in (alg_rrule1, alg_rrule2) - ( - mat_example_mat, mat_example_ftuple, mat_example_fval, mat_example_fd, - Avec, xvec, vals, lvecs, rvecs, - ) = build_mat_example(A, x, howmany, alg, alg_rrule) + (mat_example_mat, mat_example_ftuple, mat_example_fval, mat_example_fd, + Avec, xvec, vals, lvecs, rvecs) = build_mat_example(A, x, howmany, alg, alg_rrule) (JA, Jx) = FiniteDifferences.jacobian(fdm, mat_example_fd, Avec, xvec) (JA1, Jx1) = Zygote.jacobian(mat_example_mat, Avec, xvec) @@ -211,9 +181,9 @@ end (JA3, Jx3) = Zygote.jacobian(mat_example_ftuple, Avec, xvec) # finite difference comparison using some kind of tolerance heuristic - @test isapprox(JA, JA1; rtol = 3 * n * n * condA * sqrt(eps(real(T)))) - @test all(isapprox.(JA1, JA2; atol = n * eps(real(T)))) - @test all(isapprox.(JA1, JA3; atol = n * eps(real(T)))) + @test isapprox(JA, JA1; rtol=3 * n * n * condA * sqrt(eps(real(T)))) + @test all(isapprox.(JA1, JA2; atol=n * eps(real(T)))) + @test all(isapprox.(JA1, JA3; atol=n * eps(real(T)))) @test norm(Jx, Inf) < 5 * condA * sqrt(eps(real(T))) @test all(iszero, Jx1) @test all(iszero, Jx2) @@ -221,21 +191,15 @@ end # some analysis if eltype(A) <: Complex # test holomorphicity / Cauchy-Riemann equations - ∂vals = complex.( - JA1[1:howmany, :], - JA1[howmany * (3 * n + 1) .+ (1:howmany), :] - ) + ∂vals = complex.(JA1[1:howmany, :], + JA1[howmany * (3 * n + 1) .+ (1:howmany), :]) ∂lvecs = map(1:howmany) do i - return complex.( - JA1[(howmany + (i - 1) * n) .+ (1:n), :], - JA1[(howmany * (3 * n + 2) + (i - 1) * n) .+ (1:n), :] - ) + return complex.(JA1[(howmany + (i - 1) * n) .+ (1:n), :], + JA1[(howmany * (3 * n + 2) + (i - 1) * n) .+ (1:n), :]) end ∂rvecs = map(1:howmany) do i - return complex.( - JA1[(howmany * (n + 1) + (i - 1) * (2 * n)) .+ (1:(2n)), :], - JA1[(howmany * (4 * n + 2) + (i - 1) * 2n) .+ (1:(2n)), :] - ) + return complex.(JA1[(howmany * (n + 1) + (i - 1) * (2 * n)) .+ (1:(2n)), :], + JA1[(howmany * (4 * n + 2) + (i - 1) * 2n) .+ (1:(2n)), :]) end else ∂vals = JA1[1:howmany, :] @@ -256,233 +220,133 @@ end end if T <: Complex @testset "test warnings and info" begin - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = SILENT_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = 4n, verbosity = SILENT_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs pb( - ( - ZeroTangent(), im .* lvecs[1:2] .+ lvecs[2:-1:1], ZeroTangent(), - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=SILENT_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=4n, verbosity=SILENT_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs pb((ZeroTangent(), im .* lvecs[1:2] .+ lvecs[2:-1:1], ZeroTangent(), + NoTangent())) - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = WARN_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = 4n, verbosity = WARN_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs (:warn,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) pb( - ( - ZeroTangent(), lvecs[2:-1:1], - im .* rvecs[1:2] .+ rvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs pb( - ( - ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ lvecs[2:-1:1], - +im .* rvecs[1:2] + rvecs[2:-1:1], - NoTangent(), - ) - ) - @test_logs pb( - ( - ZeroTangent(), (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], - (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=WARN_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=4n, verbosity=WARN_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs (:warn,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) pb((ZeroTangent(), lvecs[2:-1:1], + im .* rvecs[1:2] .+ rvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs pb((ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ lvecs[2:-1:1], + +im .* rvecs[1:2] + rvecs[2:-1:1], + NoTangent())) + @test_logs pb((ZeroTangent(), (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], + (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], + NoTangent())) - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = WARN_LEVEL) - alg_rrule = Arnoldi(; tol = tol, krylovdim = 4n, verbosity = STARTSTOP_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:info,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:info,) pb( - ( - ZeroTangent(), lvecs[2:-1:1], - im .* rvecs[1:2] .+ rvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:info,) pb( - ( - ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:info,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ lvecs[2:-1:1], - +im .* rvecs[1:2] + rvecs[2:-1:1], - NoTangent(), - ) - ) - @test_logs (:info,) pb( - ( - ZeroTangent(), (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], - (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=WARN_LEVEL) + alg_rrule = Arnoldi(; tol=tol, krylovdim=4n, verbosity=STARTSTOP_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs (:warn,) (:info,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:info,) pb((ZeroTangent(), lvecs[2:-1:1], + im .* rvecs[1:2] .+ rvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:info,) pb((ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:info,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ lvecs[2:-1:1], + +im .* rvecs[1:2] + rvecs[2:-1:1], + NoTangent())) + @test_logs (:info,) pb((ZeroTangent(), (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], + (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], + NoTangent())) - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = SILENT_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = 3n, verbosity = SILENT_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs pb( - ( - ZeroTangent(), im .* lvecs[1:2] .+ lvecs[2:-1:1], ZeroTangent(), - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=SILENT_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=3n, verbosity=SILENT_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs pb((ZeroTangent(), im .* lvecs[1:2] .+ lvecs[2:-1:1], ZeroTangent(), + NoTangent())) - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = WARN_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = 3n, verbosity = WARN_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:warn,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ - lvecs[2:-1:1], ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:warn,) pb( - ( - ZeroTangent(), lvecs[2:-1:1], - im .* rvecs[1:2] .+ - rvecs[2:-1:1], ZeroTangent(), - NoTangent(), - ) - ) - @test_logs pb( - ( - ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:warn,) pb( - ( - ZeroTangent(), - im .* lvecs[1:2] .+ - lvecs[2:-1:1], - +im .* rvecs[1:2] + - rvecs[2:-1:1], - NoTangent(), - ) - ) - @test_logs pb( - ( - ZeroTangent(), - (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], - (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=WARN_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=3n, verbosity=WARN_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs (:warn,) (:warn,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ + lvecs[2:-1:1], ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:warn,) pb((ZeroTangent(), lvecs[2:-1:1], + im .* rvecs[1:2] .+ + rvecs[2:-1:1], ZeroTangent(), + NoTangent())) + @test_logs pb((ZeroTangent(), lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:warn,) pb((ZeroTangent(), + im .* lvecs[1:2] .+ + lvecs[2:-1:1], + +im .* rvecs[1:2] + + rvecs[2:-1:1], + NoTangent())) + @test_logs pb((ZeroTangent(), + (1 + im) .* lvecs[1:2] .+ lvecs[2:-1:1], + (1 - im) .* rvecs[1:2] + rvecs[2:-1:1], + NoTangent())) - alg = GKL(; krylovdim = 2n, tol = tol, verbosity = WARN_LEVEL) - alg_rrule = GMRES(; tol = tol, krylovdim = 3n, verbosity = STARTSTOP_LEVEL) - (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule( - config, svdsolve, A, x, - howmany, :LR, alg; - alg_rrule = alg_rrule - ) - @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb( - ( - ZeroTangent(), - im .* - lvecs[1:2] .+ - lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb( - ( - ZeroTangent(), - lvecs[2:-1:1], - im .* - rvecs[1:2] .+ - rvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:info,) (:info,) (:info,) (:info,) pb( - ( - ZeroTangent(), - lvecs[1:2] .+ lvecs[2:-1:1], - ZeroTangent(), - NoTangent(), - ) - ) - @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb( - ( - ZeroTangent(), - im .* - lvecs[1:2] .+ - lvecs[2:-1:1], - +im .* - rvecs[1:2] + - rvecs[2:-1:1], - NoTangent(), - ) - ) - @test_logs (:info,) (:info,) (:info,) (:info,) pb( - ( - ZeroTangent(), - (1 + im) .* lvecs[1:2] .+ - lvecs[2:-1:1], - (1 - im) .* rvecs[1:2] + - rvecs[2:-1:1], - NoTangent(), - ) - ) + alg = GKL(; krylovdim=2n, tol=tol, verbosity=WARN_LEVEL) + alg_rrule = GMRES(; tol=tol, krylovdim=3n, verbosity=STARTSTOP_LEVEL) + (vals, lvecs, rvecs, info), pb = ChainRulesCore.rrule(config, svdsolve, A, x, + howmany, :LR, alg; + alg_rrule=alg_rrule) + @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb((ZeroTangent(), + im .* + lvecs[1:2] .+ + lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb((ZeroTangent(), + lvecs[2:-1:1], + im .* + rvecs[1:2] .+ + rvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:info,) (:info,) (:info,) (:info,) pb((ZeroTangent(), + lvecs[1:2] .+ lvecs[2:-1:1], + ZeroTangent(), + NoTangent())) + @test_logs (:warn,) (:info,) (:info,) (:warn,) (:info,) (:info,) pb((ZeroTangent(), + im .* + lvecs[1:2] .+ + lvecs[2:-1:1], + +im .* + rvecs[1:2] + + rvecs[2:-1:1], + NoTangent())) + @test_logs (:info,) (:info,) (:info,) (:info,) pb((ZeroTangent(), + (1 + im) .* lvecs[1:2] .+ + lvecs[2:-1:1], + (1 - im) .* rvecs[1:2] + + rvecs[2:-1:1], + NoTangent())) end end end @@ -497,19 +361,17 @@ end howmany = 2 tol = 2 * N^2 * eps(real(T)) - alg = GKL(; tol = tol, krylovdim = 2n) - alg_rrule1 = Arnoldi(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL - 1) - alg_rrule2 = GMRES(; tol = tol, krylovdim = 2n, verbosity = SILENT_LEVEL - 1) + alg = GKL(; tol=tol, krylovdim=2n) + alg_rrule1 = Arnoldi(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL - 1) + alg_rrule2 = GMRES(; tol=tol, krylovdim=2n, verbosity=SILENT_LEVEL - 1) for alg_rrule in (alg_rrule1, alg_rrule2) #! format: off fun_example_ad, fun_example_fd, Avec, xvec, cvec, dvec, vals, lvecs, rvecs = build_fun_example(A, x, c, d, howmany, alg, alg_rrule) #! format: on - (JA, Jx, Jc, Jd) = FiniteDifferences.jacobian( - fdm, fun_example_fd, Avec, xvec, - cvec, dvec - ) + (JA, Jx, Jc, Jd) = FiniteDifferences.jacobian(fdm, fun_example_fd, Avec, xvec, + cvec, dvec) (JA′, Jx′, Jc′, Jd′) = Zygote.jacobian(fun_example_ad, Avec, xvec, cvec, dvec) @test JA ≈ JA′ @test Jc ≈ Jc′ diff --git a/test/bieigsolve.jl b/test/bieigsolve.jl index 00df5d86..04a47d1e 100644 --- a/test/bieigsolve.jl +++ b/test/bieigsolve.jl @@ -1,6 +1,6 @@ @testset "BiArnoldi - eigsolve full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -8,7 +8,7 @@ v = rand(T, (n,)) w = rand(T, (n,)) n1 = div(n, 2) - alg = BiArnoldi(; orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T)) + alg = BiArnoldi(; orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T)) #! format: off D1, (V1, W1), (infoV1, infoV2) = @constinferred bieigsolve(wrapop(A, Val(mode)), @@ -18,75 +18,53 @@ # Some of these still fail alg = BiArnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) - @test_logs bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=SILENT_LEVEL) + @test_logs bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), n1, :SR, alg) alg = BiArnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), n1, :SR, alg) alg = BiArnoldi(; - orth = orth, krylovdim = n1 + 2, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), n1, - :SR, alg - ) + orth=orth, krylovdim=n1 + 2, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), n1, + :SR, alg) alg = BiArnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), n1, - :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), n1, + :SR, alg) alg = BiArnoldi(; - orth = orth, krylovdim = n1, maxiter = 3, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), 1, :SR, alg - ) - ) + orth=orth, krylovdim=n1, maxiter=3, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL) + @test_logs((:info,), (:info,), (:info,), (:warn,), + bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), 1, :SR, alg)) alg = BiArnoldi(; - orth = orth, krylovdim = 4, maxiter = 1, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) + orth=orth, krylovdim=4, maxiter=1, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) # since it is impossible to know exactly the size of the Krylov subspace after shrinking, # we only know the output for a sigle iteration - @test_logs( - (:info,), (:info,), (:info,), (:info,), - (:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), - bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), 1, :SR, alg - ) - ) + @test_logs((:info,), (:info,), (:info,), (:info,), + (:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), + bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), 1, :SR, alg)) n2 = n - n1 - alg = BiArnoldi(; orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T)) + alg = BiArnoldi(; orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T)) #! format: off D2, (V2, W2), (infoV2, infoW2) = @constinferred bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), wrapvec(w, Val(mode)), n2, :LR, alg) #! format: on - D = sort(sort(eigvals(A); by = imag, rev = true); alg = MergeSort, by = real) - D2′ = sort(sort(D2; by = imag, rev = true); alg = MergeSort, by = real) + D = sort(sort(eigvals(A); by=imag, rev=true); alg=MergeSort, by=real) + D2′ = sort(sort(D2; by=imag, rev=true); alg=MergeSort, by=real) @test vcat(D1[1:n1], D2′[(end - n2 + 1):end]) ≊ D UV1 = stack(unwrapvec, V1) @@ -105,20 +83,16 @@ if T <: Complex n1 = div(n, 2) - D1, (V1, W1), (infoV1, infoW1) = bieigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), - n1, :SI, alg - ) + D1, (V1, W1), (infoV1, infoW1) = bieigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), + n1, :SI, alg) n2 = n - n1 - D2, (V2, W2), (infoV2, infoW2) = bieigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), - n2, :LI, alg - ) - D = sort(eigvals(A); by = imag) + D2, (V2, W2), (infoV2, infoW2) = bieigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), + n2, :LI, alg) + D = sort(eigvals(A); by=imag) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≊ D UV1 = stack(unwrapvec, V1) @@ -140,9 +114,9 @@ end @testset "BiArnoldi - eigsolve iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for eager in (true, false) @testset for T in scalartypes @@ -151,9 +125,8 @@ end v = rand(T, (N,)) w = rand(T, (N,)) alg = BiArnoldi(; - krylovdim = 3 * n, maxiter = 40, eager, - tol = tolerance(T), verbosity = SILENT_LEVEL - ) + krylovdim=3 * n, maxiter=40, eager, + tol=tolerance(T), verbosity=SILENT_LEVEL) #! format: off D1, (V1, W1), (infoV1, infoW1) = @constinferred bieigsolve( wrapop(A, Val(mode)), wrapvec(v, Val(mode)), wrapvec(w, Val(mode)), @@ -165,7 +138,7 @@ end wrapop(A, Val(mode)), wrapvec(v, Val(mode)), wrapvec(w, Val(mode)), n, :LM, alg) #! format: on - D = sort(eigvals(A); by = imag, rev = true) + D = sort(eigvals(A); by=imag, rev=true) @test infoV1.converged == infoW1.converged @test infoV2.converged == infoW2.converged @@ -177,11 +150,11 @@ end @test l1 > 0 @test l2 > 0 @test l3 > 0 - @test D1[1:l1] ≊ sort(D; alg = MergeSort, by = real)[1:l1] - @test D2[1:l2] ≊ sort(D; alg = MergeSort, by = real, rev = true)[1:l2] + @test D1[1:l1] ≊ sort(D; alg=MergeSort, by=real)[1:l1] + @test D2[1:l2] ≊ sort(D; alg=MergeSort, by=real, rev=true)[1:l2] # sorting by abs does not seem very reliable if two distinct eigenvalues are close # in absolute value, so we perform a second sort afterwards using the real part - @test D3[1:l3] ≊ sort(D; by = abs, rev = true)[1:l3] + @test D3[1:l3] ≊ sort(D; by=abs, rev=true)[1:l3] UV1 = stack(unwrapvec, V1) UV2 = stack(unwrapvec, V2) @@ -221,8 +194,8 @@ end l2 = infoV2.converged @test l1 > 0 @test l2 > 0 - @test D1[1:l1] ≈ sort(D; by = imag)[1:l1] - @test D2[1:l2] ≈ sort(D; by = imag, rev = true)[1:l2] + @test D1[1:l1] ≈ sort(D; by=imag)[1:l1] + @test D2[1:l2] ≈ sort(D; by=imag, rev=true)[1:l2] UV1 = stack(unwrapvec, V1) UV2 = stack(unwrapvec, V2) @@ -246,7 +219,7 @@ end @testset "BiArnoldi - shortened api ($mode)" for mode in (:vector,) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -255,17 +228,13 @@ end w = rand(T, (n,)) n1 = div(n, 2) - @constinferred bieigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - wrapvec(w, Val(mode)), - n1; orth = orth, krylovdim = n, maxiter = 1, - tol = tolerance(T) - ) + @constinferred bieigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + wrapvec(w, Val(mode)), + n1; orth=orth, krylovdim=n, maxiter=1, + tol=tolerance(T)) - @constinferred bieigsolve( - wrapop(A, Val(mode)), n, n1; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T) - ) + @constinferred bieigsolve(wrapop(A, Val(mode)), n, n1; + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T)) end end end diff --git a/test/block.jl b/test/block.jl index d4b059e9..2bee3145 100644 --- a/test/block.jl +++ b/test/block.jl @@ -1,7 +1,7 @@ @testset "Block constructor" begin for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes x₀ = Block([wrapvec(rand(T, n), Val(mode)) for _ in 1:n]) x₁ = Block([wrapvec(rand(T, n), Val(mode)) for _ in 1:n]) @@ -20,7 +20,7 @@ end @testset "apply on Block" begin for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, n, n) .- one(T) / 2 A = (A + A') / 2 @@ -28,7 +28,7 @@ end wy = KrylovKit.apply(wrapop(A, Val(mode)), wx₀) y = unwrapvec.(wy) x₀ = unwrapvec.(wx₀) - @test isapprox(hcat(y...), A * hcat(x₀...); atol = tolerance(T)) + @test isapprox(hcat(y...), A * hcat(x₀...); atol=tolerance(T)) end end T = ComplexF64 @@ -38,16 +38,14 @@ end Af(x::InnerProductVec) = KrylovKit.InnerProductVec(A * x[], x.dotf) x₀ = Block([InnerProductVec(rand(T, n), f) for _ in 1:n]) y = KrylovKit.apply(Af, x₀) - @test isapprox( - hcat([y[i].vec for i in 1:n]...), A * hcat([x₀[i].vec for i in 1:n]...); - atol = tolerance(T) - ) + @test isapprox(hcat([y[i].vec for i in 1:n]...), A * hcat([x₀[i].vec for i in 1:n]...); + atol=tolerance(T)) end @testset "copy for Block" begin for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block0 = Block([wrapvec(rand(T, N), Val(mode)) for _ in 1:n]) block1 = copy(block0) @@ -81,7 +79,7 @@ M[i,j] = inner(x[i],y[j]) BlockB = Block(wrapvec.(B, Val(mode))) M = KrylovKit.block_inner(BlockA, BlockB) @test eltype(M) == T - @test isapprox(M, M0; atol = relax_tol(T)) + @test isapprox(M, M0; atol=relax_tol(T)) end end @@ -99,16 +97,14 @@ end Ym = hcat([Y[i].vec for i in 1:n]...) M0 = Xm' * H * Ym @test eltype(M) == T - @test isapprox(M, M0; atol = relax_tol(T)) + @test isapprox(M, M0; atol=relax_tol(T)) end @testset "block_reorthogonalize! for non-full vectors $mode" for mode in - ( - :vector, :inplace, - :outplace, - ) + (:vector, :inplace, + :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, n, n) .- one(T) / 2 A = (A + A') / 2 @@ -140,9 +136,9 @@ end end @testset "block_qr! for non-full vectors $mode" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, n, n) .- one(T) / 2 B = copy(A) @@ -155,8 +151,8 @@ end Av1 = [unwrapvec(wAv[i]) for i in gi] @test length(gi) < n @test eltype(R) == eltype(A) == T - @test isapprox(hcat(Av1...) * R, hcat(Bv...); atol = tolerance(T)) - @test isapprox(hcat(Av1...)' * hcat(Av1...), I; atol = tolerance(T)) + @test isapprox(hcat(Av1...) * R, hcat(Bv...); atol=tolerance(T)) + @test isapprox(hcat(Av1...)' * hcat(Av1...), I; atol=tolerance(T)) end end @@ -175,9 +171,7 @@ end @test length(gi) < n @test eltype(R) == T BlockX = Block(X[gi]) - @test isapprox(KrylovKit.block_inner(BlockX, BlockX), I; atol = tolerance(T)) - @test isapprox( - hcat([X[i].vec for i in gi]...) * R, - hcat([Xcopy[i].vec for i in 1:n]...); atol = tolerance(T) - ) + @test isapprox(KrylovKit.block_inner(BlockX, BlockX), I; atol=tolerance(T)) + @test isapprox(hcat([X[i].vec for i in gi]...) * R, + hcat([Xcopy[i].vec for i in 1:n]...); atol=tolerance(T)) end diff --git a/test/eigsolve.jl b/test/eigsolve.jl index a4e8bd56..0630d9c8 100644 --- a/test/eigsolve.jl +++ b/test/eigsolve.jl @@ -1,6 +1,6 @@ @testset "Lanczos - eigsolve full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -9,68 +9,46 @@ v = rand(T, (n,)) n1 = div(n, 2) alg = Lanczos(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - D1, V1, info = @test_logs (:info,) eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + D1, V1, info = @test_logs (:info,) eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, alg) alg = Lanczos(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, alg) alg = Lanczos(; - orth = orth, krylovdim = n1 + 1, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n1 + 1, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, alg) alg = Lanczos(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, alg) alg = Lanczos(; - orth = orth, krylovdim = n1, maxiter = 3, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg) - ) + orth=orth, krylovdim=n1, maxiter=3, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL) + @test_logs((:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg)) alg = Lanczos(; - orth = orth, krylovdim = 4, maxiter = 1, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) + orth=orth, krylovdim=4, maxiter=1, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) # since it is impossible to know exactly the size of the Krylov subspace after shrinking, # we only know the output for a sigle iteration - @test_logs( - (:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg) - ) - - @test KrylovKit.eigselector( - wrapop(A, Val(mode)), scalartype(v); krylovdim = n, - maxiter = 1, - tol = tolerance(T), ishermitian = true - ) isa Lanczos + @test_logs((:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg)) + + @test KrylovKit.eigselector(wrapop(A, Val(mode)), scalartype(v); krylovdim=n, + maxiter=1, + tol=tolerance(T), ishermitian=true) isa Lanczos n2 = n - n1 - alg = Lanczos(; krylovdim = 2 * n, maxiter = 1, tol = tolerance(T)) - D2, V2, info = @constinferred eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), - n2, :LR, alg - ) + alg = Lanczos(; krylovdim=2 * n, maxiter=1, tol=tolerance(T)) + D2, V2, info = @constinferred eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), + n2, :LR, alg) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≊ eigvals(A) U1 = stack(unwrapvec, V1) @@ -82,20 +60,17 @@ @test A * U2 ≈ U2 * Diagonal(D2) alg = Lanczos(; - orth = orth, krylovdim = 2n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) (:warn,) eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n + 1, :LM, alg - ) + orth=orth, krylovdim=2n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) (:warn,) eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n + 1, :LM, alg) end end end @testset "Lanczos - eigsolve iteratively ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -103,17 +78,12 @@ end A = (A + A') / 2 v = rand(T, (N,)) alg = Lanczos(; - krylovdim = 2 * n, maxiter = 10, - tol = tolerance(T), eager = true, verbosity = SILENT_LEVEL - ) - D1, V1, info1 = @constinferred eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n, :SR, alg - ) - D2, V2, info2 = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LR, - alg - ) + krylovdim=2 * n, maxiter=10, + tol=tolerance(T), eager=true, verbosity=SILENT_LEVEL) + D1, V1, info1 = @constinferred eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n, :SR, alg) + D2, V2, info2 = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LR, + alg) l1 = info1.converged l2 = info2.converged @@ -137,77 +107,57 @@ end @testset "Arnoldi - eigsolve full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = rand(T, (n, n)) .- one(T) / 2 v = rand(T, (n,)) n1 = div(n, 2) - alg = Arnoldi(; orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T)) - D1, V1, info1 = @constinferred eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, alg - ) + alg = Arnoldi(; orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T)) + D1, V1, info1 = @constinferred eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, alg) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=SILENT_LEVEL) @test_logs eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, alg) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) @test_logs eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, alg) alg = Arnoldi(; - orth = orth, krylovdim = n1 + 2, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, - :SR, alg - ) + orth=orth, krylovdim=n1 + 2, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, + :SR, alg) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, - :SR, alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, + :SR, alg) alg = Arnoldi(; - orth = orth, krylovdim = n1, maxiter = 3, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg) - ) + orth=orth, krylovdim=n1, maxiter=3, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL) + @test_logs((:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg)) alg = Arnoldi(; - orth = orth, krylovdim = 4, maxiter = 1, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) + orth=orth, krylovdim=4, maxiter=1, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) # since it is impossible to know exactly the size of the Krylov subspace after shrinking, # we only know the output for a sigle iteration - @test_logs( - (:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg) - ) - - @test KrylovKit.eigselector( - wrapop(A, Val(mode)), eltype(v); orth = orth, - krylovdim = n, maxiter = 1, - tol = tolerance(T) - ) isa Arnoldi + @test_logs((:info,), (:info,), (:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), 1, :SR, alg)) + + @test KrylovKit.eigselector(wrapop(A, Val(mode)), eltype(v); orth=orth, + krylovdim=n, maxiter=1, + tol=tolerance(T)) isa Arnoldi n2 = n - n1 - alg = Arnoldi(; orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T)) - D2, V2, info2 = @constinferred eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n2, :LR, alg - ) - D = sort(sort(eigvals(A); by = imag, rev = true); alg = MergeSort, by = real) - D2′ = sort(sort(D2; by = imag, rev = true); alg = MergeSort, by = real) + alg = Arnoldi(; orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T)) + D2, V2, info2 = @constinferred eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n2, :LR, alg) + D = sort(sort(eigvals(A); by=imag, rev=true); alg=MergeSort, by=real) + D2′ = sort(sort(D2; by=imag, rev=true); alg=MergeSort, by=real) @test vcat(D1[1:n1], D2′[(end - n2 + 1):end]) ≈ D U1 = stack(unwrapvec, V1) @@ -217,18 +167,14 @@ end if T <: Complex n1 = div(n, 2) - D1, V1, info = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, - :SI, - alg - ) + D1, V1, info = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, + :SI, + alg) n2 = n - n1 - D2, V2, info = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n2, - :LI, - alg - ) - D = sort(eigvals(A); by = imag) + D2, V2, info = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n2, + :LI, + alg) + D = sort(eigvals(A); by=imag) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≊ D @@ -239,42 +185,32 @@ end end alg = Arnoldi(; - orth = orth, krylovdim = 2n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) (:warn,) eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n + 1, :LM, alg - ) + orth=orth, krylovdim=2n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) (:warn,) eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n + 1, :LM, alg) end end end @testset "Arnoldi - eigsolve iteratively ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = rand(T, (N, N)) .- one(T) / 2 v = rand(T, (N,)) alg = Arnoldi(; - krylovdim = 3 * n, maxiter = 20, - tol = tolerance(T), eager = true, verbosity = SILENT_LEVEL - ) - D1, V1, info1 = @constinferred eigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n, :SR, alg - ) - D2, V2, info2 = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LR, - alg - ) - D3, V3, info3 = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LM, - alg - ) - D = sort(eigvals(A); by = imag, rev = true) + krylovdim=3 * n, maxiter=20, + tol=tolerance(T), eager=true, verbosity=SILENT_LEVEL) + D1, V1, info1 = @constinferred eigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n, :SR, alg) + D2, V2, info2 = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LR, + alg) + D3, V3, info3 = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, :LM, + alg) + D = sort(eigvals(A); by=imag, rev=true) l1 = info1.converged l2 = info2.converged @@ -282,11 +218,11 @@ end @test l1 > 0 @test l2 > 0 @test l3 > 0 - @test D1[1:l1] ≊ sort(D; alg = MergeSort, by = real)[1:l1] - @test D2[1:l2] ≊ sort(D; alg = MergeSort, by = real, rev = true)[1:l2] + @test D1[1:l1] ≊ sort(D; alg=MergeSort, by=real)[1:l1] + @test D2[1:l2] ≊ sort(D; alg=MergeSort, by=real, rev=true)[1:l2] # sorting by abs does not seem very reliable if two distinct eigenvalues are close # in absolute value, so we perform a second sort afterwards using the real part - @test D3[1:l3] ≊ sort(D; by = abs, rev = true)[1:l3] + @test D3[1:l3] ≊ sort(D; by=abs, rev=true)[1:l3] U1 = stack(unwrapvec, V1) U2 = stack(unwrapvec, V2) @@ -299,22 +235,18 @@ end @test A * U3 ≈ U3 * Diagonal(D3) + R3 if T <: Complex - D1, V1, info1 = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :SI, alg - ) - D2, V2, info2 = eigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :LI, alg - ) + D1, V1, info1 = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :SI, alg) + D2, V2, info2 = eigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :LI, alg) D = eigvals(A) l1 = info1.converged l2 = info2.converged @test l1 > 0 @test l2 > 0 - @test D1[1:l1] ≈ sort(D; by = imag)[1:l1] - @test D2[1:l2] ≈ sort(D; by = imag, rev = true)[1:l2] + @test D1[1:l1] ≈ sort(D; by=imag)[1:l1] + @test D2[1:l2] ≈ sort(D; by=imag, rev=true)[1:l2] U1 = stack(unwrapvec, V1) U2 = stack(unwrapvec, V2) @@ -328,7 +260,7 @@ end end @testset "Arnoldi - realeigsolve iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64) : (Float64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @@ -338,34 +270,27 @@ end A = V * Diagonal(D) / V v = rand(T, (N,)) alg = Arnoldi(; - krylovdim = 3 * n, maxiter = 20, - tol = tolerance(T), eager = true, verbosity = SILENT_LEVEL - ) - D1, V1, info1 = @constinferred realeigsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n, :SR, alg - ) - D2, V2, info2 = realeigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :LR, - alg - ) - D3, V3, info3 = realeigsolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :LM, - alg - ) + krylovdim=3 * n, maxiter=20, + tol=tolerance(T), eager=true, verbosity=SILENT_LEVEL) + D1, V1, info1 = @constinferred realeigsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n, :SR, alg) + D2, V2, info2 = realeigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :LR, + alg) + D3, V3, info3 = realeigsolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :LM, + alg) l1 = info1.converged l2 = info2.converged l3 = info3.converged @test l1 > 0 @test l2 > 0 @test l3 > 0 - @test D1[1:l1] ≊ sort(D; alg = MergeSort)[1:l1] - @test D2[1:l2] ≊ sort(D; alg = MergeSort, rev = true)[1:l2] + @test D1[1:l1] ≊ sort(D; alg=MergeSort)[1:l1] + @test D2[1:l2] ≊ sort(D; alg=MergeSort, rev=true)[1:l2] # sorting by abs does not seem very reliable if two distinct eigenvalues are close # in absolute value, so we perform a second sort afterwards using the real part - @test D3[1:l3] ≊ sort(D; by = abs, rev = true)[1:l3] + @test D3[1:l3] ≊ sort(D; by=abs, rev=true)[1:l3] @test eltype(D1) == T @test eltype(D2) == T @@ -394,9 +319,8 @@ end f = buildrealmap(A, B) v = rand(complex(T), (N,)) alg = Arnoldi(; - krylovdim = 3 * n, maxiter = 20, - tol = tolerance(T), eager = true, verbosity = SILENT_LEVEL - ) + krylovdim=3 * n, maxiter=20, + tol=tolerance(T), eager=true, verbosity=SILENT_LEVEL) D1, V1, info1 = @constinferred realeigsolve(f, v, n, :SR, alg) D2, V2, info2 = realeigsolve(f, v, n, :LR, alg) D3, V3, info3 = realeigsolve(f, v, n, :LM, alg) @@ -407,11 +331,11 @@ end @test l1 > 0 @test l2 > 0 @test l3 > 0 - @test D1[1:l1] ≊ sort(D; alg = MergeSort)[1:l1] - @test D2[1:l2] ≊ sort(D; alg = MergeSort, rev = true)[1:l2] + @test D1[1:l1] ≊ sort(D; alg=MergeSort)[1:l1] + @test D2[1:l2] ≊ sort(D; alg=MergeSort, rev=true)[1:l2] # sorting by abs does not seem very reliable if two distinct eigenvalues are close # in absolute value, so we perform a second sort afterwards using the real part - @test D3[1:l3] ≊ sort(D; by = abs, rev = true)[1:l3] + @test D3[1:l3] ≊ sort(D; by=abs, rev=true)[1:l3] @test eltype(D1) == T @test eltype(D2) == T @@ -436,23 +360,16 @@ end A[2, 1] = 1.0e-9 A[1, 2] = -1.0e-9 v = ones(Float64, size(A, 1)) - @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol = 1.0e-8, verbosity = SILENT_LEVEL)) - @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol = 1.0e-8, verbosity = WARN_LEVEL)) - @test_logs (:info,) realeigsolve( - A, v, 1, :LM, - Arnoldi(; tol = 1.0e-8, verbosity = STARTSTOP_LEVEL) - ) - @test_logs (:warn,) realeigsolve( - A, v, 1, :LM, - Arnoldi(; tol = 1.0e-10, verbosity = WARN_LEVEL) - ) - @test_logs (:warn,) (:info,) realeigsolve( - A, v, 1, :LM, - Arnoldi(; - tol = 1.0e-10, - verbosity = STARTSTOP_LEVEL - ) - ) + @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol=1.0e-8, verbosity=SILENT_LEVEL)) + @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol=1.0e-8, verbosity=WARN_LEVEL)) + @test_logs (:info,) realeigsolve(A, v, 1, :LM, + Arnoldi(; tol=1.0e-8, verbosity=STARTSTOP_LEVEL)) + @test_logs (:warn,) realeigsolve(A, v, 1, :LM, + Arnoldi(; tol=1.0e-10, verbosity=WARN_LEVEL)) + @test_logs (:warn,) (:info,) realeigsolve(A, v, 1, :LM, + Arnoldi(; + tol=1.0e-10, + verbosity=STARTSTOP_LEVEL)) # this should not trigger a warning A[1, 2] = A[2, 1] = 0 @@ -460,12 +377,10 @@ end A[2, 2] = A[3, 3] = 0.99 A[3, 2] = 1.0e-6 A[2, 3] = -1.0e-6 - @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol = 1.0e-12, verbosity = SILENT_LEVEL)) - @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol = 1.0e-12, verbosity = WARN_LEVEL)) - @test_logs (:info,) realeigsolve( - A, v, 1, :LM, - Arnoldi(; tol = 1.0e-12, verbosity = STARTSTOP_LEVEL) - ) + @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol=1.0e-12, verbosity=SILENT_LEVEL)) + @test_logs realeigsolve(A, v, 1, :LM, Arnoldi(; tol=1.0e-12, verbosity=WARN_LEVEL)) + @test_logs (:info,) realeigsolve(A, v, 1, :LM, + Arnoldi(; tol=1.0e-12, verbosity=STARTSTOP_LEVEL)) end @testset "BlockLanczos - eigsolve for large sparse matrix and map input" begin @@ -475,8 +390,8 @@ end li = LinearIndices((m, n)) bottom(i, j) = li[mod1(i, m), mod1(j, n)] + m * n right(i, j) = li[mod1(i, m), mod1(j, n)] - xstrings = NTuple{4, Int}[] - zstrings = NTuple{4, Int}[] + xstrings = NTuple{4,Int}[] + zstrings = NTuple{4,Int}[] for i in 1:m, j in 1:n # plaquette push!(xstrings, (bottom(i, j + 1), right(i, j), bottom(i, j), right(i - 1, j))) @@ -486,7 +401,7 @@ end return xstrings, zstrings end - function pauli_kron(n::Int, ops::Pair{Int, Char}...) + function pauli_kron(n::Int, ops::Pair{Int,Char}...) mat = sparse(1.0I, 2^n, 2^n) for (pos, op) in ops if op == 'X' @@ -537,7 +452,7 @@ end h_mat = toric_code_hamiltonian_matrix(sites_num, sites_num) # matrix input - alg = BlockLanczos(; tol = tol, maxiter = 1) + alg = BlockLanczos(; tol=tol, maxiter=1) D, U, info = eigsolve(-h_mat, x₀, get_value_num, :SR, alg) @test count(x -> abs(x + 16.0) < 2.0 - tol, D[1:get_value_num]) == 4 @test count(x -> abs(x + 16.0) < tol, D[1:get_value_num]) == 4 @@ -551,7 +466,7 @@ end # For user interface, input is a block. @testset "BlockLanczos - eigsolve full $mode" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 2 A = mat_with_eigrepition(T, n, block_size) @@ -559,46 +474,34 @@ end n1 = div(n, 2) # eigenvalues to solve eigvalsA = eigvals(A) alg = BlockLanczos(; - krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - D1, V1, info = @test_logs (:info,) eigsolve( - wrapop(A, Val(mode)), - x₀, n1, :SR, alg - ) + krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + D1, V1, info = @test_logs (:info,) eigsolve(wrapop(A, Val(mode)), + x₀, n1, :SR, alg) alg = BlockLanczos(; - krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) + krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) @test_logs eigsolve(wrapop(A, Val(mode)), x₀, n1, :SR, alg) alg = BlockLanczos(; - krylovdim = n1 + 1, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) + krylovdim=n1 + 1, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) @test_logs (:warn,) eigsolve(wrapop(A, Val(mode)), x₀, n1, :SR, alg) alg = BlockLanczos(; - krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) + krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) @test_logs (:info,) eigsolve(wrapop(A, Val(mode)), x₀, n1, :SR, alg) alg = BlockLanczos(; - krylovdim = 3, maxiter = 3, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), x₀, 1, :SR, alg) - ) + krylovdim=3, maxiter=3, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL) + @test_logs((:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), x₀, 1, :SR, alg)) alg = BlockLanczos(; - krylovdim = 4, maxiter = 1, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - eigsolve(wrapop(A, Val(mode)), x₀, 1, :SR, alg) - ) + krylovdim=4, maxiter=1, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) + @test_logs((:info,), (:info,), (:info,), (:warn,), + eigsolve(wrapop(A, Val(mode)), x₀, 1, :SR, alg)) n2 = n - n1 - alg = BlockLanczos(; krylovdim = 2 * n, maxiter = 4, tol = tolerance(T)) + alg = BlockLanczos(; krylovdim=2 * n, maxiter=4, tol=tolerance(T)) D2, V2, info = eigsolve(wrapop(A, Val(mode)), x₀, n2, :LR, alg) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≊ eigvalsA @@ -612,17 +515,16 @@ end @test (x -> KrylovKit.apply(A, x)).(unwrapvec.(V2)) ≈ D2 .* unwrapvec.(V2) alg = BlockLanczos(; - krylovdim = 2n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) + krylovdim=2n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) @test_logs (:warn,) (:warn,) eigsolve(wrapop(A, Val(mode)), x₀, n + 1, :LM, alg) end end @testset "BlockLanczos - eigsolve iteratively $mode" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 4 A = mat_with_eigrepition(T, N, block_size) @@ -630,9 +532,8 @@ end eigvalsA = eigvals(A) alg = BlockLanczos(; - krylovdim = N, maxiter = 10, tol = tolerance(T), - eager = true, verbosity = SILENT_LEVEL - ) + krylovdim=N, maxiter=10, tol=tolerance(T), + eager=true, verbosity=SILENT_LEVEL) D1, V1, info1 = eigsolve(wrapop(A, Val(mode)), x₀, n, :SR, alg) D2, V2, info2 = eigsolve(wrapop(A, Val(mode)), x₀, n, :LR, alg) @@ -652,9 +553,9 @@ end @test U1' * U1 ≈ I @test U2' * U2 ≈ I @test hcat([KrylovKit.apply(A, U1[:, i]) for i in 1:l1]...) ≈ - U1 * Diagonal(D1) + R1 + U1 * Diagonal(D1) + R1 @test hcat([KrylovKit.apply(A, U2[:, i]) for i in 1:l2]...) ≈ - U2 * Diagonal(D2) + R2 + U2 * Diagonal(D2) + R2 end end @@ -667,13 +568,10 @@ end Hip(x::Vector, y::Vector) = x' * H * y x₀ = Block([InnerProductVec(rand(T, n), Hip) for _ in 1:block_size]) Aip(x::InnerProductVec) = InnerProductVec(H * x.vec, Hip) - D, V, info = eigsolve( - Aip, x₀, eig_num, :SR, - BlockLanczos(; - krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) - ) + D, V, info = eigsolve(Aip, x₀, eig_num, :SR, + BlockLanczos(; + krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=SILENT_LEVEL)) D_true = eigvals(H) BlockV = KrylovKit.Block(V) @test D[1:eig_num] ≈ D_true[1:eig_num] @@ -683,9 +581,9 @@ end # with the same krylovdim, BlockLanczos has lower accuracy with the size of block larger than 1. @testset "Compare Lanczos and BlockLanczos $mode" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (2N, 2N)) .- one(T) / 2 A = (A + A') / 2 @@ -693,28 +591,22 @@ end x₀_block = Block([wrapvec(rand(T, 2N), Val(mode)) for _ in 1:block_size]) x₀_lanczos = x₀_block[1] alg1 = Lanczos(; - krylovdim = 2n, maxiter = 10, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) + krylovdim=2n, maxiter=10, tol=tolerance(T), + verbosity=SILENT_LEVEL) alg2 = BlockLanczos(; - krylovdim = 2n, maxiter = 10, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) + krylovdim=2n, maxiter=10, tol=tolerance(T), + verbosity=SILENT_LEVEL) evals1, _, info1 = eigsolve(wrapop(A, Val(mode)), x₀_lanczos, n, :SR, alg1) evals2, _, info2 = eigsolve(wrapop(A, Val(mode)), x₀_block, n, :SR, alg2) @test info1.converged == info2.converged @test info1.numiter == info2.numiter @test info1.numops + 1 == info2.numops # one extra operation for the BlockLanczos initialization - @test isapprox(info1.normres, info2.normres, atol = tolerance(T)) - @test isapprox( - unwrapvec.(info1.residual[1:(info1.converged)])[2], - unwrapvec.(info2.residual[1:(info2.converged)])[2]; - atol = tolerance(T) - ) - @test isapprox( - evals1[1:(info1.converged)], evals2[1:(info2.converged)]; - atol = tolerance(T) - ) + @test isapprox(info1.normres, info2.normres, atol=tolerance(T)) + @test isapprox(unwrapvec.(info1.residual[1:(info1.converged)])[2], + unwrapvec.(info2.residual[1:(info2.converged)])[2]; + atol=tolerance(T)) + @test isapprox(evals1[1:(info1.converged)], evals2[1:(info2.converged)]; + atol=tolerance(T)) end @testset for T in (Float64, ComplexF64) A = rand(T, (2N, 2N)) .- one(T) / 2 @@ -726,13 +618,11 @@ end x₀_lanczos = wrapvec(x₀, Val(mode)) x₀_block = Block([wrapvec(x₀, Val(mode)), wrapvec(x₁, Val(mode))]) alg1 = Lanczos(; - krylovdim = 2n, maxiter = 1, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) + krylovdim=2n, maxiter=1, tol=tolerance(T), + verbosity=SILENT_LEVEL) alg2 = BlockLanczos(; - krylovdim = 2n, maxiter = 1, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) + krylovdim=2n, maxiter=1, tol=tolerance(T), + verbosity=SILENT_LEVEL) vals1, vecs1, info1 = eigsolve(wrapop(A, Val(mode)), x₀_lanczos, n, :SR, alg1) vals2, vecs2, info2 = eigsolve(wrapop(A, Val(mode)), x₀_block, n, :SR, alg2) @test vals1 ≈ vals2 @@ -743,12 +633,10 @@ end # Test effectiveness of shrink!() in BlockLanczos @testset "Test effectiveness of shrink!() in BlockLanczos $mode" for mode in - ( - :vector, :inplace, - :outplace, - ) + (:vector, :inplace, + :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 5 A = mat_with_eigrepition(T, N, block_size) @@ -756,15 +644,13 @@ end values0 = eigvals(A)[1:n] n1 = n ÷ 2 alg = BlockLanczos(; - krylovdim = 3 * n ÷ 2, maxiter = 1, tol = 1.0e-12, - verbosity = SILENT_LEVEL - ) + krylovdim=3 * n ÷ 2, maxiter=1, tol=1.0e-12, + verbosity=SILENT_LEVEL) values, _, _ = eigsolve(wrapop(A, Val(mode)), x₀, n, :SR, alg) error1 = norm(values[1:n1] - values0[1:n1]) alg_shrink = BlockLanczos(; - krylovdim = 3 * n ÷ 2, maxiter = 2, tol = 1.0e-12, - verbosity = SILENT_LEVEL - ) + krylovdim=3 * n ÷ 2, maxiter=2, tol=1.0e-12, + verbosity=SILENT_LEVEL) values_shrink, _, _ = eigsolve(wrapop(A, Val(mode)), x₀, n, :SR, alg_shrink) error2 = norm(values_shrink[1:n1] - values0[1:n1]) @test error2 < error1 @@ -772,9 +658,9 @@ end end @testset "BlockLanczos - eigsolve without alg $mode" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 2 A = mat_with_eigrepition(T, n, block_size) diff --git a/test/expintegrator.jl b/test/expintegrator.jl index 99b92ffa..067d24d3 100644 --- a/test/expintegrator.jl +++ b/test/expintegrator.jl @@ -14,7 +14,7 @@ end @testset "Lanczos - expintegrator full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -23,36 +23,26 @@ end V = one(A) W = zero(A) alg = Lanczos(; - orth = orth, krylovdim = n, maxiter = 2, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) + orth=orth, krylovdim=n, maxiter=2, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) for k in 1:n - w, = @test_logs (:info,) exponentiate( - wrapop(A, Val(mode)), 1, - wrapvec( - view(V, :, k), - Val(mode) - ), alg - ) + w, = @test_logs (:info,) exponentiate(wrapop(A, Val(mode)), 1, + wrapvec(view(V, :, k), + Val(mode)), alg) W[:, k] = unwrapvec(w) end @test W ≈ exp(A) pmax = 5 alg = Lanczos(; - orth = orth, krylovdim = n, maxiter = 2, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) - for t in ( - rand(real(T)), -rand(real(T)), im * randn(real(T)), - randn(real(T)) + im * randn(real(T)), - ) + orth=orth, krylovdim=n, maxiter=2, tol=tolerance(T), + verbosity=SILENT_LEVEL) + for t in (rand(real(T)), -rand(real(T)), im * randn(real(T)), + randn(real(T)) + im * randn(real(T))) for p in 1:pmax u = ntuple(i -> rand(T, n), p + 1) - w, info = @constinferred expintegrator( - wrapop(A, Val(mode)), t, - wrapvec.(u, Ref(Val(mode))), alg - ) + w, info = @constinferred expintegrator(wrapop(A, Val(mode)), t, + wrapvec.(u, Ref(Val(mode))), alg) w2 = exp(t * A) * u[1] for j in 1:p w2 .+= t^j * ϕ(t * A, u[j + 1], j) @@ -67,7 +57,7 @@ end @testset "Arnoldi - expintegrator full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -75,36 +65,26 @@ end V = one(A) W = zero(A) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 2, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) + orth=orth, krylovdim=n, maxiter=2, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) for k in 1:n - w, = @test_logs (:info,) exponentiate( - wrapop(A, Val(mode)), 1, - wrapvec( - view(V, :, k), - Val(mode) - ), alg - ) + w, = @test_logs (:info,) exponentiate(wrapop(A, Val(mode)), 1, + wrapvec(view(V, :, k), + Val(mode)), alg) W[:, k] = unwrapvec(w) end @test W ≈ exp(A) pmax = 5 alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 2, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) - for t in ( - rand(real(T)), -rand(real(T)), im * randn(real(T)), - randn(real(T)) + im * randn(real(T)), - ) + orth=orth, krylovdim=n, maxiter=2, tol=tolerance(T), + verbosity=SILENT_LEVEL) + for t in (rand(real(T)), -rand(real(T)), im * randn(real(T)), + randn(real(T)) + im * randn(real(T))) for p in 1:pmax u = ntuple(i -> rand(T, n), p + 1) - w, info = @constinferred expintegrator( - wrapop(A, Val(mode)), t, - wrapvec.(u, Ref(Val(mode))), alg - ) + w, info = @constinferred expintegrator(wrapop(A, Val(mode)), t, + wrapvec.(u, Ref(Val(mode))), alg) w2 = exp(t * A) * u[1] for j in 1:p w2 .+= t^j * ϕ(t * A, u[j + 1], j) @@ -118,39 +98,33 @@ end end @testset "Lanczos - expintegrator iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = (1 // 2) .* (rand(T, (N, N)) .- one(T) / 2) A = (A + A') / 2 pmax = 5 - for t in ( - rand(real(T)), -rand(real(T)), im * randn(real(T)), - randn(real(T)) + im * randn(real(T)), - ) + for t in (rand(real(T)), -rand(real(T)), im * randn(real(T)), + randn(real(T)) + im * randn(real(T))) for p in 1:pmax u = ntuple(i -> rand(T, N), p + 1) - w1, info = @constinferred expintegrator( - wrapop(A, Val(mode)), t, - wrapvec.(u, Ref(Val(mode)))...; - maxiter = 100, krylovdim = n, - eager = true - ) + w1, info = @constinferred expintegrator(wrapop(A, Val(mode)), t, + wrapvec.(u, Ref(Val(mode)))...; + maxiter=100, krylovdim=n, + eager=true) @test info.converged > 0 w2 = exp(t * A) * u[1] for j in 1:p w2 .+= t^j * ϕ(t * A, u[j + 1], j) end @test w2 ≈ unwrapvec(w1) - w1, info = @constinferred expintegrator( - wrapop(A, Val(mode)), t, - wrapvec.(u, Ref(Val(mode)))...; - maxiter = 100, krylovdim = n, - tol = 1.0e-3, eager = true - ) + w1, info = @constinferred expintegrator(wrapop(A, Val(mode)), t, + wrapvec.(u, Ref(Val(mode)))...; + maxiter=100, krylovdim=n, + tol=1.0e-3, eager=true) @test unwrapvec(w1) ≈ w2 atol = 1.0e-2 * abs(t) end end @@ -159,26 +133,22 @@ end end @testset "Arnoldi - expintegrator iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = (1 // 2) .* (rand(T, (N, N)) .- one(T) / 2) pmax = 5 - for t in ( - rand(real(T)), -rand(real(T)), im * randn(real(T)), - randn(real(T)) + im * randn(real(T)), - ) + for t in (rand(real(T)), -rand(real(T)), im * randn(real(T)), + randn(real(T)) + im * randn(real(T))) for p in 1:pmax u = ntuple(i -> rand(T, N), p + 1) - w1, info = @constinferred expintegrator( - wrapop(A, Val(mode)), t, - wrapvec.(u, Ref(Val(mode)))...; - maxiter = 100, krylovdim = n, - eager = true - ) + w1, info = @constinferred expintegrator(wrapop(A, Val(mode)), t, + wrapvec.(u, Ref(Val(mode)))...; + maxiter=100, krylovdim=n, + eager=true) @test info.converged > 0 w2 = exp(t * A) * u[1] for j in 1:p diff --git a/test/factorize.jl b/test/factorize.jl index f1a25b2b..3e5c3b7b 100644 --- a/test/factorize.jl +++ b/test/factorize.jl @@ -1,7 +1,7 @@ # Test complete Lanczos factorization @testset "Complete Lanczos factorization ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (cgs2,) @testset for T in scalartypes @testset for orth in orths # tests fail miserably for cgs and mgs @@ -11,15 +11,15 @@ iter = LanczosIterator(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), orth) fact = @constinferred initialize(iter) @constinferred expand!(iter, fact) - @test_logs initialize(iter; verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize(iter; verbosity = EACHITERATION_LEVEL + 1) + @test_logs initialize(iter; verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize(iter; verbosity=EACHITERATION_LEVEL + 1) verbosity = EACHITERATION_LEVEL + 1 while length(fact) < n if verbosity == EACHITERATION_LEVEL + 1 - @test_logs (:info,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:info,) expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL + 1 end end @@ -33,30 +33,28 @@ @test rayleighquotient(last(states)) ≈ H @constinferred shrink!(fact, n - 1) - @test_logs (:info,) shrink!(fact, n - 2; verbosity = EACHITERATION_LEVEL + 1) - @test_logs shrink!(fact, n - 3; verbosity = EACHITERATION_LEVEL) + @test_logs (:info,) shrink!(fact, n - 2; verbosity=EACHITERATION_LEVEL + 1) + @test_logs shrink!(fact, n - 3; verbosity=EACHITERATION_LEVEL) @constinferred initialize!(iter, deepcopy(fact)) - @test_logs initialize!(iter, deepcopy(fact); verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize!( - iter, deepcopy(fact); - verbosity = EACHITERATION_LEVEL + 1 - ) + @test_logs initialize!(iter, deepcopy(fact); verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize!(iter, deepcopy(fact); + verbosity=EACHITERATION_LEVEL + 1) if T <: Complex A = rand(T, (n, n)) # test warnings for non-hermitian matrices v = rand(T, (n,)) iter = LanczosIterator(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), orth) - fact = @constinferred initialize(iter; verbosity = SILENT_LEVEL) - @constinferred expand!(iter, fact; verbosity = SILENT_LEVEL) - @test_logs initialize(iter; verbosity = SILENT_LEVEL) + fact = @constinferred initialize(iter; verbosity=SILENT_LEVEL) + @constinferred expand!(iter, fact; verbosity=SILENT_LEVEL) + @test_logs initialize(iter; verbosity=SILENT_LEVEL) @test_logs (:warn,) initialize(iter) verbosity = WARN_LEVEL while length(fact) < n if verbosity == WARN_LEVEL - @test_logs (:warn,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:warn,) expand!(iter, fact; verbosity=verbosity) verbosity = SILENT_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = WARN_LEVEL end end @@ -68,7 +66,7 @@ end # Test complete Arnoldi factorization @testset "Complete Arnoldi factorization ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs, mgs, cgs2, mgs2, cgsr, mgsr) : (cgs2,) @testset for T in scalartypes @@ -78,15 +76,15 @@ end iter = ArnoldiIterator(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), orth) fact = @constinferred initialize(iter) @constinferred expand!(iter, fact) - @test_logs initialize(iter; verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize(iter; verbosity = EACHITERATION_LEVEL + 1) + @test_logs initialize(iter; verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize(iter; verbosity=EACHITERATION_LEVEL + 1) verbosity = EACHITERATION_LEVEL + 1 while length(fact) < n if verbosity == EACHITERATION_LEVEL + 1 - @test_logs (:info,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:info,) expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL + 1 end end @@ -101,23 +99,21 @@ end @test rayleighquotient(last(states)) ≈ H @constinferred shrink!(fact, n - 1) - @test_logs (:info,) shrink!(fact, n - 2; verbosity = EACHITERATION_LEVEL + 1) - @test_logs shrink!(fact, n - 3; verbosity = EACHITERATION_LEVEL) + @test_logs (:info,) shrink!(fact, n - 2; verbosity=EACHITERATION_LEVEL + 1) + @test_logs shrink!(fact, n - 3; verbosity=EACHITERATION_LEVEL) @constinferred initialize!(iter, deepcopy(fact)) - @test_logs initialize!(iter, deepcopy(fact); verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize!( - iter, deepcopy(fact); - verbosity = EACHITERATION_LEVEL + 1 - ) + @test_logs initialize!(iter, deepcopy(fact); verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize!(iter, deepcopy(fact); + verbosity=EACHITERATION_LEVEL + 1) end end end # Test incomplete Lanczos factorization @testset "Incomplete Lanczos factorization ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? - (Float32, Float64, ComplexF32, ComplexF64, Complex{Int}) : (ComplexF64,) + (Float32, Float64, ComplexF32, ComplexF64, Complex{Int}) : (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (cgs2,) @testset for T in scalartypes @@ -130,11 +126,9 @@ end v = rand(T, (N,)) end A = (A + A') - iter = @constinferred LanczosIterator( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), - orth - ) + iter = @constinferred LanczosIterator(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), + orth) krylovdim = n fact = @constinferred initialize(iter) while normres(fact) > eps(float(real(T))) && length(fact) < krylovdim @@ -162,9 +156,9 @@ end # Test incomplete Arnoldi factorization @testset "Incomplete Arnoldi factorization ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? - (Float32, Float64, ComplexF32, ComplexF64, Complex{Int}) : (ComplexF64,) + (Float32, Float64, ComplexF32, ComplexF64, Complex{Int}) : (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (cgs2,) @testset for T in scalartypes @@ -176,10 +170,8 @@ end A = rand(T, (N, N)) v = rand(T, (N,)) end - iter = @constinferred ArnoldiIterator( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), orth - ) + iter = @constinferred ArnoldiIterator(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), orth) krylovdim = 3 * n fact = @constinferred initialize(iter) while normres(fact) > eps(float(real(T))) && length(fact) < krylovdim @@ -207,12 +199,10 @@ end # Test complete Golub-Kahan-Lanczos factorization @testset "Complete Golub-Kahan-Lanczos factorization ($mode)" for mode in - ( - :vector, :inplace, - :outplace, :mixed, - ) + (:vector, :inplace, + :outplace, :mixed) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -221,15 +211,15 @@ end iter = GKLIterator(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), orth) fact = @constinferred initialize(iter) @constinferred expand!(iter, fact) - @test_logs initialize(iter; verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize(iter; verbosity = EACHITERATION_LEVEL + 1) + @test_logs initialize(iter; verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize(iter; verbosity=EACHITERATION_LEVEL + 1) verbosity = EACHITERATION_LEVEL + 1 while length(fact) < n if verbosity == EACHITERATION_LEVEL + 1 - @test_logs (:info,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:info,) expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL + 1 end end @@ -246,26 +236,22 @@ end @test rayleighquotient(last(states)) ≈ B @constinferred shrink!(fact, n - 1) - @test_logs (:info,) shrink!(fact, n - 2; verbosity = EACHITERATION_LEVEL + 1) - @test_logs shrink!(fact, n - 3; verbosity = EACHITERATION_LEVEL) + @test_logs (:info,) shrink!(fact, n - 2; verbosity=EACHITERATION_LEVEL + 1) + @test_logs shrink!(fact, n - 3; verbosity=EACHITERATION_LEVEL) @constinferred initialize!(iter, deepcopy(fact)) - @test_logs initialize!(iter, deepcopy(fact); verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize!( - iter, deepcopy(fact); - verbosity = EACHITERATION_LEVEL + 1 - ) + @test_logs initialize!(iter, deepcopy(fact); verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize!(iter, deepcopy(fact); + verbosity=EACHITERATION_LEVEL + 1) end end end # Test incomplete Golub-Kahan-Lanczos factorization @testset "Incomplete Golub-Kahan-Lanczos factorization ($mode)" for mode in - ( - :vector, :inplace, - :outplace, :mixed, - ) + (:vector, :inplace, + :outplace, :mixed) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -276,10 +262,8 @@ end A = rand(T, (N, N)) v = rand(T, (N,)) end - iter = @constinferred GKLIterator( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - orth - ) + iter = @constinferred GKLIterator(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + orth) krylovdim = 3 * n fact = @constinferred initialize(iter) while normres(fact) > eps(float(real(T))) && length(fact) < krylovdim @@ -314,28 +298,26 @@ end # Test complete BlockLanczos factorization @testset "Complete BlockLanczos factorization " for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 5 A = mat_with_eigrepition(T, N, block_size) x₀m = Matrix(qr(rand(T, N, block_size)).Q) x₀ = KrylovKit.Block([wrapvec(x₀m[:, i], Val(mode)) for i in 1:block_size]) eigvalsA = eigvals(A) - iter = BlockLanczosIterator( - wrapop(A, Val(mode)), x₀, N, - KrylovKit.KrylovDefaults.orth, tolerance(T) - ) + iter = BlockLanczosIterator(wrapop(A, Val(mode)), x₀, N, + KrylovKit.KrylovDefaults.orth, tolerance(T)) fact = @constinferred initialize(iter) @constinferred expand!(iter, fact) - @test_logs initialize(iter; verbosity = EACHITERATION_LEVEL) - @test_logs (:info,) initialize(iter; verbosity = EACHITERATION_LEVEL + 1) + @test_logs initialize(iter; verbosity=EACHITERATION_LEVEL) + @test_logs (:info,) initialize(iter; verbosity=EACHITERATION_LEVEL + 1) verbosity = EACHITERATION_LEVEL + 1 while fact.k < n if verbosity == EACHITERATION_LEVEL + 1 - @test_logs (:info,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:info,) expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = EACHITERATION_LEVEL + 1 end end @@ -345,21 +327,19 @@ end bs = 2 v₀m = Matrix(qr(rand(T, n, bs)).Q) v₀ = KrylovKit.Block([wrapvec(v₀m[:, i], Val(mode)) for i in 1:bs]) - iter = BlockLanczosIterator( - wrapop(B, Val(mode)), v₀, N, - KrylovKit.KrylovDefaults.orth, tolerance(T) - ) - fact = @constinferred initialize(iter; verbosity = SILENT_LEVEL) - @constinferred expand!(iter, fact; verbosity = SILENT_LEVEL) - @test_logs initialize(iter; verbosity = SILENT_LEVEL) + iter = BlockLanczosIterator(wrapop(B, Val(mode)), v₀, N, + KrylovKit.KrylovDefaults.orth, tolerance(T)) + fact = @constinferred initialize(iter; verbosity=SILENT_LEVEL) + @constinferred expand!(iter, fact; verbosity=SILENT_LEVEL) + @test_logs initialize(iter; verbosity=SILENT_LEVEL) @test_logs (:warn,) initialize(iter) verbosity = WARN_LEVEL while fact.k < n if verbosity == WARN_LEVEL - @test_logs (:warn,) expand!(iter, fact; verbosity = verbosity) + @test_logs (:warn,) expand!(iter, fact; verbosity=verbosity) verbosity = SILENT_LEVEL else - @test_logs expand!(iter, fact; verbosity = verbosity) + @test_logs expand!(iter, fact; verbosity=verbosity) verbosity = WARN_LEVEL end end @@ -369,19 +349,17 @@ end # Test incomplete BlockLanczos factorization @testset "Incomplete BlockLanczos factorization " for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes block_size = 5 A = mat_with_eigrepition(T, N, block_size) x₀m = Matrix(qr(rand(T, N, block_size)).Q) x₀ = KrylovKit.Block([wrapvec(x₀m[:, i], Val(mode)) for i in 1:block_size]) - iter = @constinferred BlockLanczosIterator( - wrapop(A, Val(mode)), x₀, N, - KrylovKit.KrylovDefaults.orth, - tolerance(T) - ) + iter = @constinferred BlockLanczosIterator(wrapop(A, Val(mode)), x₀, N, + KrylovKit.KrylovDefaults.orth, + tolerance(T)) krylovdim = n fact = @constinferred initialize(iter) while fact.norm_R > eps(float(real(T))) && fact.k < krylovdim diff --git a/test/geneigsolve.jl b/test/geneigsolve.jl index a09c045c..8077480b 100644 --- a/test/geneigsolve.jl +++ b/test/geneigsolve.jl @@ -1,6 +1,6 @@ @testset "GolubYe - geneigsolve full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -10,106 +10,69 @@ B = sqrt(B * B') v = rand(T, (n,)) alg = GolubYe(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) n1 = div(n, 2) - D1, V1, info = @constinferred geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n1, :SR; orth = orth, krylovdim = n, - maxiter = 1, tol = tolerance(T), - ishermitian = true, isposdef = true, - verbosity = SILENT_LEVEL - ) + D1, V1, info = @constinferred geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n1, :SR; orth=orth, krylovdim=n, + maxiter=1, tol=tolerance(T), + ishermitian=true, isposdef=true, + verbosity=SILENT_LEVEL) if info.converged < n1 - @test_logs geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n1, :SR; orth = orth, krylovdim = n, - maxiter = 1, tol = tolerance(T), - ishermitian = true, isposdef = true, - verbosity = SILENT_LEVEL - ) - @test_logs geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n1, :SR; orth = orth, krylovdim = n, - maxiter = 1, tol = tolerance(T), - ishermitian = true, isposdef = true, - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n1, :SR; orth = orth, krylovdim = n1 + 1, - maxiter = 1, tol = tolerance(T), - ishermitian = true, isposdef = true, - verbosity = WARN_LEVEL - ) - @test_logs (:info,) geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n1, :SR; orth = orth, krylovdim = n, - maxiter = 1, tol = tolerance(T), - ishermitian = true, isposdef = true, - verbosity = STARTSTOP_LEVEL - ) + @test_logs geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n1, :SR; orth=orth, krylovdim=n, + maxiter=1, tol=tolerance(T), + ishermitian=true, isposdef=true, + verbosity=SILENT_LEVEL) + @test_logs geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n1, :SR; orth=orth, krylovdim=n, + maxiter=1, tol=tolerance(T), + ishermitian=true, isposdef=true, + verbosity=WARN_LEVEL) + @test_logs (:warn,) geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n1, :SR; orth=orth, krylovdim=n1 + 1, + maxiter=1, tol=tolerance(T), + ishermitian=true, isposdef=true, + verbosity=WARN_LEVEL) + @test_logs (:info,) geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n1, :SR; orth=orth, krylovdim=n, + maxiter=1, tol=tolerance(T), + ishermitian=true, isposdef=true, + verbosity=STARTSTOP_LEVEL) alg = GolubYe(; - orth = orth, krylovdim = n1, maxiter = 3, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) - @test_logs( - (:info,), (:info,), (:info,), (:warn,), - geneigsolve( - (wrapop(A, Val(mode)), wrapop(B, Val(mode))), - wrapvec(v, Val(mode)), 1, :SR, alg - ) - ) + orth=orth, krylovdim=n1, maxiter=3, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL) + @test_logs((:info,), (:info,), (:info,), (:warn,), + geneigsolve((wrapop(A, Val(mode)), wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), 1, :SR, alg)) alg = GolubYe(; - orth = orth, krylovdim = 3, maxiter = 2, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) - @test_logs( - (:info,), (:info,), (:info,), (:info,), - (:info,), (:info,), (:info,), (:info,), (:warn,), - geneigsolve( - (wrapop(A, Val(mode)), wrapop(B, Val(mode))), - wrapvec(v, Val(mode)), 1, :SR, alg - ) - ) + orth=orth, krylovdim=3, maxiter=2, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) + @test_logs((:info,), (:info,), (:info,), (:info,), + (:info,), (:info,), (:info,), (:info,), (:warn,), + geneigsolve((wrapop(A, Val(mode)), wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), 1, :SR, alg)) end - @test KrylovKit.geneigselector( - (wrapop(A, Val(mode)), wrapop(B, Val(mode))), - scalartype(v); orth = orth, krylovdim = n, - maxiter = 1, tol = tolerance(T), ishermitian = true, - isposdef = true - ) isa GolubYe + @test KrylovKit.geneigselector((wrapop(A, Val(mode)), wrapop(B, Val(mode))), + scalartype(v); orth=orth, krylovdim=n, + maxiter=1, tol=tolerance(T), ishermitian=true, + isposdef=true) isa GolubYe n2 = n - n1 - D2, V2, info = @constinferred geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n2, :LR, alg - ) + D2, V2, info = @constinferred geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n2, :LR, alg) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≈ eigvals(A, B) U1 = stack(unwrapvec, V1) @@ -124,9 +87,9 @@ end @testset "GolubYe - geneigsolve iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float64, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -136,21 +99,14 @@ end B = sqrt(B * B') v = rand(T, (N,)) alg = GolubYe(; - orth = orth, krylovdim = 3 * n, maxiter = 100, - tol = cond(B) * tolerance(T), verbosity = SILENT_LEVEL - ) - D1, V1, info1 = @constinferred geneigsolve( - ( - wrapop(A, Val(mode)), - wrapop(B, Val(mode)), - ), - wrapvec(v, Val(mode)), - n, :SR, alg - ) - D2, V2, info2 = geneigsolve( - (wrapop(A, Val(mode)), wrapop(B, Val(mode))), - wrapvec(v, Val(mode)), n, :LR, alg - ) + orth=orth, krylovdim=3 * n, maxiter=100, + tol=cond(B) * tolerance(T), verbosity=SILENT_LEVEL) + D1, V1, info1 = @constinferred geneigsolve((wrapop(A, Val(mode)), + wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), + n, :SR, alg) + D2, V2, info2 = geneigsolve((wrapop(A, Val(mode)), wrapop(B, Val(mode))), + wrapvec(v, Val(mode)), n, :LR, alg) l1 = info1.converged l2 = info2.converged diff --git a/test/issues.jl b/test/issues.jl index f2edc5d3..4e25a717 100644 --- a/test/issues.jl +++ b/test/issues.jl @@ -5,13 +5,13 @@ A += A' v₀ = [rand(N ÷ 2), rand(N ÷ 2)] - vals, vecs = eigsolve(v₀, 4, :LM; ishermitian = true) do v + vals, vecs = eigsolve(v₀, 4, :LM; ishermitian=true) do v v′ = vcat(v...) y = A * v′ return [y[1:(N ÷ 2)], y[(N ÷ 2 + 1):end]] end - vals2, vecs2 = eigsolve(A, 4, :LM; ishermitian = true) + vals2, vecs2 = eigsolve(A, 4, :LM; ishermitian=true) @test vals ≈ vals2 for (v, v′) in zip(vecs, vecs2) @test abs(inner(vcat(v...), v′)) ≈ 1 diff --git a/test/linalg.jl b/test/linalg.jl index e5fc07df..b253a4da 100644 --- a/test/linalg.jl +++ b/test/linalg.jl @@ -1,5 +1,5 @@ using KrylovKit: OrthonormalBasis, householder, rows, cols, hschur!, schur2eigvals, - schur2eigvecs, permuteschur! + schur2eigvecs, permuteschur! @testset "Orthonormalize with algorithm $alg" for alg in (cgs, mgs, cgs2, mgs2, cgsr, mgsr) @testset for S in (Float32, Float64, ComplexF32, ComplexF64) @@ -75,12 +75,12 @@ end @test T * V2 ≈ V2 * Diagonal(w[select]) # permuting / reordering schur: take permutations that keep 2x2 blocks together in real case - p = sortperm(w; by = real) + p = sortperm(w; by=real) T2, U2 = permuteschur!(copy(T), copy(U), p) @test H * U2 ≈ U2 * T2 @test schur2eigvals(T2) ≈ w[p] - p = sortperm(w; by = abs) + p = sortperm(w; by=abs) T2, U2 = permuteschur!(copy(T), copy(U), p) @test H * U2 ≈ U2 * T2 @test schur2eigvals(T2) ≈ w[p] diff --git a/test/linsolve.jl b/test/linsolve.jl index 19a5d806..1ed3ab59 100644 --- a/test/linsolve.jl +++ b/test/linsolve.jl @@ -1,64 +1,52 @@ # Test CG complete @testset "CG small problem ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (n, n)) A = sqrt(A * A') b = rand(T, n) - alg = CG(; maxiter = 2n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) # because of loss of orthogonality, we choose maxiter = 2n - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - ishermitian = true, isposdef = true, maxiter = 2n, - krylovdim = 1, rtol = tolerance(T), - verbosity = SILENT_LEVEL - ) + alg = CG(; maxiter=2n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) # because of loss of orthogonality, we choose maxiter = 2n + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + ishermitian=true, isposdef=true, maxiter=2n, + krylovdim=1, rtol=tolerance(T), + verbosity=SILENT_LEVEL) @test info.converged > 0 @test unwrapvec(b) ≈ A * unwrapvec(x) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - ishermitian = true, isposdef = true, maxiter = 2n, - krylovdim = 1, rtol = tolerance(T), - verbosity = SILENT_LEVEL - ) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - ishermitian = true, isposdef = true, maxiter = 2n, - krylovdim = 1, rtol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:info,) (:info,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - ishermitian = true, isposdef = true, maxiter = 2n, - krylovdim = 1, rtol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs min_level = Logging.Warn linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)); - ishermitian = true, isposdef = true, - maxiter = 2n, - krylovdim = 1, rtol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + ishermitian=true, isposdef=true, maxiter=2n, + krylovdim=1, rtol=tolerance(T), + verbosity=SILENT_LEVEL) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + ishermitian=true, isposdef=true, maxiter=2n, + krylovdim=1, rtol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:info,) (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + ishermitian=true, isposdef=true, maxiter=2n, + krylovdim=1, rtol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs min_level = Logging.Warn linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)); + ishermitian=true, isposdef=true, + maxiter=2n, + krylovdim=1, rtol=tolerance(T), + verbosity=EACHITERATION_LEVEL) x, info = linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) @test info.numops == 1 @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = CG(; maxiter = 2n, tol = tolerance(T) * norm(b), verbosity = WARN_LEVEL) + alg = CG(; maxiter=2n, tol=tolerance(T) * norm(b), verbosity=WARN_LEVEL) @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = CG(; maxiter = 2n, tol = tolerance(T) * norm(b), verbosity = STARTSTOP_LEVEL) + alg = CG(; maxiter=2n, tol=tolerance(T) * norm(b), verbosity=STARTSTOP_LEVEL) @test_logs (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = CG(; maxiter = 2n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) + alg = CG(; maxiter=2n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) A = rand(T, (n, n)) A = sqrt(A * A') α₀ = rand(real(T)) + 1 α₁ = rand(real(T)) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁ - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁) @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) @test info.converged > 0 end @@ -67,50 +55,40 @@ end # Test CG complete @testset "CG large problem ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (N, N)) A = sqrt(sqrt(A * A')) / N b = rand(T, N) x₀ = rand(T, N) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - isposdef = true, maxiter = 1, krylovdim = N, - rtol = tolerance(T) - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + isposdef=true, maxiter=1, krylovdim=N, + rtol=tolerance(T)) @test unwrapvec(b) ≈ A * unwrapvec(x) + unwrapvec(info.residual) if info.converged == 0 - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - isposdef = true, maxiter = 1, krylovdim = N, - rtol = tolerance(T), verbosity = SILENT_LEVEL - ) - @test_logs (:warn,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - isposdef = true, maxiter = 1, krylovdim = N, - rtol = tolerance(T), verbosity = WARN_LEVEL - ) - @test_logs (:info,) (:warn,) linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - isposdef = true, maxiter = 1, krylovdim = N, - rtol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + isposdef=true, maxiter=1, krylovdim=N, + rtol=tolerance(T), verbosity=SILENT_LEVEL) + @test_logs (:warn,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + isposdef=true, maxiter=1, krylovdim=N, + rtol=tolerance(T), verbosity=WARN_LEVEL) + @test_logs (:info,) (:warn,) linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + isposdef=true, maxiter=1, krylovdim=N, + rtol=tolerance(T), + verbosity=STARTSTOP_LEVEL) end α₀ = rand(real(T)) + 1 α₁ = rand(real(T)) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - α₀, α₁; - isposdef = true, maxiter = 1, krylovdim = N, - rtol = tolerance(T) - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + α₀, α₁; + isposdef=true, maxiter=1, krylovdim=N, + rtol=tolerance(T)) @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) + unwrapvec(info.residual) end end @@ -118,78 +96,58 @@ end # Test GMRES complete @testset "GMRES full factorization ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (n, n)) .- one(T) / 2 b = rand(T, n) alg = GMRES(; - krylovdim = n, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = SILENT_LEVEL - ) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - krylovdim = n, maxiter = 2, - rtol = tolerance(T), verbosity = SILENT_LEVEL - ) + krylovdim=n, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=SILENT_LEVEL) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + krylovdim=n, maxiter=2, + rtol=tolerance(T), verbosity=SILENT_LEVEL) @test info.converged == 1 @test unwrapvec(b) ≈ A * unwrapvec(x) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - krylovdim = n, maxiter = 2, - rtol = tolerance(T), verbosity = SILENT_LEVEL - ) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - krylovdim = n, maxiter = 2, - rtol = tolerance(T), verbosity = WARN_LEVEL - ) - @test_logs (:info,) (:info,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - krylovdim = n, maxiter = 2, - rtol = tolerance(T), verbosity = STARTSTOP_LEVEL - ) - @test_logs min_level = Logging.Warn linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)); - krylovdim = n, maxiter = 2, - rtol = tolerance(T), - verbosity = EACHITERATION_LEVEL - ) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + krylovdim=n, maxiter=2, + rtol=tolerance(T), verbosity=SILENT_LEVEL) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + krylovdim=n, maxiter=2, + rtol=tolerance(T), verbosity=WARN_LEVEL) + @test_logs (:info,) (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + krylovdim=n, maxiter=2, + rtol=tolerance(T), verbosity=STARTSTOP_LEVEL) + @test_logs min_level = Logging.Warn linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)); + krylovdim=n, maxiter=2, + rtol=tolerance(T), + verbosity=EACHITERATION_LEVEL) alg = GMRES(; - krylovdim = n, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = SILENT_LEVEL - ) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, - alg - ) + krylovdim=n, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=SILENT_LEVEL) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, + alg) @test info.numops == 1 @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) alg = GMRES(; - krylovdim = n, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = WARN_LEVEL - ) + krylovdim=n, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=WARN_LEVEL) @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) alg = GMRES(; - krylovdim = n, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = STARTSTOP_LEVEL - ) + krylovdim=n, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=STARTSTOP_LEVEL) @test_logs (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) alg = GMRES(; - krylovdim = n, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = SILENT_LEVEL - ) + krylovdim=n, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=SILENT_LEVEL) nreal = (T <: Real) ? n : 2n algr = GMRES(; - krylovdim = nreal, maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = SILENT_LEVEL - ) - xr, infor = @constinferred reallinsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - zerovector(x), algr - ) + krylovdim=nreal, maxiter=2, tol=tolerance(T) * norm(b), + verbosity=SILENT_LEVEL) + xr, infor = @constinferred reallinsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + zerovector(x), algr) @test infor.converged == 1 @test unwrapvec(x) ≈ unwrapvec(xr) @@ -215,61 +173,48 @@ end # Test GMRES with restart @testset "GMRES with restarts ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (N, N)) .- one(T) / 2 A = I - T(9 / 10) * A / maximum(abs, eigvals(A)) b = rand(T, N) x₀ = rand(T, N) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - krylovdim = 3 * n, - maxiter = 50, rtol = tolerance(T) - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + krylovdim=3 * n, + maxiter=50, rtol=tolerance(T)) @test unwrapvec(b) ≈ A * unwrapvec(x) + unwrapvec(info.residual) if info.converged == 0 - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - krylovdim = 3 * n, - maxiter = 50, rtol = tolerance(T), verbosity = SILENT_LEVEL - ) - @test_logs (:warn,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - krylovdim = 3 * n, - maxiter = 50, rtol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:info,) (:warn,) linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)), - wrapvec(x₀, Val(mode)); - krylovdim = 3 * n, - maxiter = 50, rtol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + krylovdim=3 * n, + maxiter=50, rtol=tolerance(T), verbosity=SILENT_LEVEL) + @test_logs (:warn,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + krylovdim=3 * n, + maxiter=50, rtol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:info,) (:warn,) linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)), + wrapvec(x₀, Val(mode)); + krylovdim=3 * n, + maxiter=50, rtol=tolerance(T), + verbosity=STARTSTOP_LEVEL) end alg = GMRES(; - krylovdim = 3 * n, maxiter = 50, tol = tolerance(T) * norm(b), - verbosity = SILENT_LEVEL - ) - xr, infor = @constinferred reallinsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - zerovector(x), alg - ) + krylovdim=3 * n, maxiter=50, tol=tolerance(T) * norm(b), + verbosity=SILENT_LEVEL) + xr, infor = @constinferred reallinsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + zerovector(x), alg) @test unwrapvec(b) ≈ A * unwrapvec(xr) + unwrapvec(infor.residual) A = rand(T, (N, N)) .- one(T) / 2 α₀ = maximum(abs, eigvals(A)) α₁ = -9 * rand(T) / 10 - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), α₀, - α₁; krylovdim = 3 * n, - maxiter = 50, rtol = tolerance(T) - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), α₀, + α₁; krylovdim=3 * n, + maxiter=50, rtol=tolerance(T)) @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) + unwrapvec(info.residual) if mode == :vector && T <: Complex @@ -287,63 +232,48 @@ end # Test BiCGStab @testset "BiCGStab small problem ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (n, n)) .- one(T) / 2 A = I - T(9 / 10) * A / maximum(abs, eigvals(A)) b = rand(T, n) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg - ) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg) @test info.converged > 0 @test unwrapvec(b) ≈ A * unwrapvec(x) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg - ) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = WARN_LEVEL) - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg - ) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = STARTSTOP_LEVEL) - @test_logs (:info,) (:info,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg - ) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=WARN_LEVEL) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg) alg = BiCGStab(; - maxiter = 4n, tol = tolerance(T) * norm(b), - verbosity = EACHITERATION_LEVEL - ) - @test_logs min_level = Logging.Warn linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg - ) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) + maxiter=4n, tol=tolerance(T) * norm(b), + verbosity=EACHITERATION_LEVEL) + @test_logs min_level = Logging.Warn linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, - alg - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, + alg) @test info.numops == 1 - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = WARN_LEVEL) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=WARN_LEVEL) @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = STARTSTOP_LEVEL) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=STARTSTOP_LEVEL) @test_logs (:info,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, alg) - alg = BiCGStab(; maxiter = 4n, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) + alg = BiCGStab(; maxiter=4n, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) α₀ = rand(real(T)) + 1 α₁ = rand(real(T)) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁ - ) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁) @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) @test info.converged > 0 end @@ -351,53 +281,40 @@ end @testset "BiCGStab large problem ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (N, N)) .- one(T) / 2 b = rand(T, N) α₀ = maximum(abs, eigvals(A)) α₁ = -9 * rand(real(T)) / 10 - alg = BiCGStab(; maxiter = 2, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, α₀, - α₁ - ) + alg = BiCGStab(; maxiter=2, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, α₀, + α₁) @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) + unwrapvec(info.residual) if info.converged == 0 - @test_logs linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁ - ) - alg = BiCGStab(; maxiter = 2, tol = tolerance(T) * norm(b), verbosity = WARN_LEVEL) - @test_logs (:warn,) linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁ - ) + @test_logs linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁) + alg = BiCGStab(; maxiter=2, tol=tolerance(T) * norm(b), verbosity=WARN_LEVEL) + @test_logs (:warn,) linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, α₀, α₁) alg = BiCGStab(; - maxiter = 2, tol = tolerance(T) * norm(b), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) (:warn,) linsolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)), - wrapvec(zerovector(b), Val(mode)), alg, - α₀, α₁ - ) + maxiter=2, tol=tolerance(T) * norm(b), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) (:warn,) linsolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)), + wrapvec(zerovector(b), Val(mode)), alg, + α₀, α₁) end - alg = BiCGStab(; maxiter = 10 * N, tol = tolerance(T) * norm(b), verbosity = SILENT_LEVEL) - x, info = @constinferred linsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, - alg, α₀, α₁ - ) + alg = BiCGStab(; maxiter=10 * N, tol=tolerance(T) * norm(b), verbosity=SILENT_LEVEL) + x, info = @constinferred linsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), x, + alg, α₀, α₁) @test info.converged > 0 @test unwrapvec(b) ≈ (α₀ * I + α₁ * A) * unwrapvec(x) - xr, infor = @constinferred reallinsolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - zerovector(x), alg, α₀, α₁ - ) + xr, infor = @constinferred reallinsolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + zerovector(x), alg, α₀, α₁) @test infor.converged > 0 @test unwrapvec(xr) ≈ unwrapvec(x) diff --git a/test/lssolve.jl b/test/lssolve.jl index 07254412..c5573b6c 100644 --- a/test/lssolve.jl +++ b/test/lssolve.jl @@ -1,7 +1,7 @@ # Test LSMR complete @testset "LSMR small problem ($mode)" for mode in (:vector, :inplace, :outplace, :mixed) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) @testset for T in scalartypes A = rand(T, (2 * n, n)) U, S, V = svd(A) @@ -12,28 +12,20 @@ b = rand(T, 2 * n) tol = tol = 10 * n * eps(real(T)) - x, info = @constinferred lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - maxiter = 3, krylovdim = 1, verbosity = SILENT_LEVEL - ) # no reorthogonalization + x, info = @constinferred lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + maxiter=3, krylovdim=1, verbosity=SILENT_LEVEL) # no reorthogonalization r = b - A * unwrapvec(x) @test unwrapvec(info.residual) ≈ r @test info.normres ≈ norm(A' * r) @test info.converged == 0 - @test_logs lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); maxiter = 3, - verbosity = SILENT_LEVEL - ) - @test_logs (:warn,) lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); maxiter = 3, - verbosity = WARN_LEVEL - ) - @test_logs (:info,) (:warn,) lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - maxiter = 3, verbosity = STARTSTOP_LEVEL - ) + @test_logs lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); maxiter=3, + verbosity=SILENT_LEVEL) + @test_logs (:warn,) lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); maxiter=3, + verbosity=WARN_LEVEL) + @test_logs (:info,) (:warn,) lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + maxiter=3, verbosity=STARTSTOP_LEVEL) - alg = LSMR(; maxiter = n, tol = tol, verbosity = SILENT_LEVEL, krylovdim = n) + alg = LSMR(; maxiter=n, tol=tol, verbosity=SILENT_LEVEL, krylovdim=n) # reorthogonalisation is essential here to converge in exactly n iterations x, info = @constinferred lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), alg) @@ -41,27 +33,21 @@ @test abs(inner(V[:, end], unwrapvec(x))) < alg.tol @test unwrapvec(x) ≈ V * Diagonal(invS) * U' * b @test_logs lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), alg) - alg = LSMR(; maxiter = 2 * n, tol = tol, verbosity = WARN_LEVEL) + alg = LSMR(; maxiter=2 * n, tol=tol, verbosity=WARN_LEVEL) @test_logs lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), alg) - alg = LSMR(; maxiter = 2 * n, tol = tol, verbosity = STARTSTOP_LEVEL) - @test_logs (:info,) (:info,) lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), - alg - ) - alg = LSMR(; maxiter = 2 * n, tol = tol, verbosity = EACHITERATION_LEVEL) - @test_logs min_level = Logging.Warn lssolve( - wrapop(A, Val(mode)), - wrapvec(b, Val(mode)), - alg - ) + alg = LSMR(; maxiter=2 * n, tol=tol, verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) (:info,) lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), + alg) + alg = LSMR(; maxiter=2 * n, tol=tol, verbosity=EACHITERATION_LEVEL) + @test_logs min_level = Logging.Warn lssolve(wrapop(A, Val(mode)), + wrapvec(b, Val(mode)), + alg) λ = rand(real(T)) - alg = LSMR(; maxiter = n, tol = tol, verbosity = SILENT_LEVEL, krylovdim = n) + alg = LSMR(; maxiter=n, tol=tol, verbosity=SILENT_LEVEL, krylovdim=n) # reorthogonalisation is essential here to converge in exactly n iterations - x, info = @constinferred lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)), alg, - λ - ) + x, info = @constinferred lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)), alg, + λ) r = b - A * unwrapvec(x) @test info.converged > 0 @@ -72,7 +58,7 @@ B = rand(T, (2 * n, n)) .- one(T) / 2 f = buildrealmap(A, B) # the effective linear problem has twice the size, so 4n x 2n - alg = LSMR(; maxiter = 2 * n, tol = tol, verbosity = SILENT_LEVEL, krylovdim = 2 * n) + alg = LSMR(; maxiter=2 * n, tol=tol, verbosity=SILENT_LEVEL, krylovdim=2 * n) xr, infor = @constinferred reallssolve(f, b, alg) @test infor.converged > 0 y = (A * xr + B * conj(xr)) @@ -88,11 +74,9 @@ end b = rand(T, 2 * N) .- (one(T) / 2) tol = 10 * N * eps(real(T)) - x, info = @constinferred lssolve( - wrapop(A, Val(mode)), wrapvec(b, Val(mode)); - maxiter = N, tol = tol, verbosity = SILENT_LEVEL, - krylovdim = 5 - ) + x, info = @constinferred lssolve(wrapop(A, Val(mode)), wrapvec(b, Val(mode)); + maxiter=N, tol=tol, verbosity=SILENT_LEVEL, + krylovdim=5) r = b - A * unwrapvec(x) @test info.converged > 0 @@ -102,7 +86,7 @@ end A = rand(T, (2 * N, N)) .- one(T) / 2 B = rand(T, (2 * N, N)) .- one(T) / 2 f = buildrealmap(A, B) - alg = LSMR(; maxiter = N, tol = tol, verbosity = SILENT_LEVEL, krylovdim = 5) + alg = LSMR(; maxiter=N, tol=tol, verbosity=SILENT_LEVEL, krylovdim=5) xr, infor = @constinferred reallssolve(f, b, alg) @test infor.converged > 0 y = (A * xr + B * conj(xr)) diff --git a/test/nestedtuple.jl b/test/nestedtuple.jl index fda1872a..43d981b6 100644 --- a/test/nestedtuple.jl +++ b/test/nestedtuple.jl @@ -5,7 +5,7 @@ A = rand(T, (n, n)) v = rand(T, (n,)) v2 = (v, zero(v)) - alg = Lanczos(; orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T)) + alg = Lanczos(; orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T)) D, V, info = eigsolve(v2, n, :LR, alg) do x x1, x2 = x y1 = A * x2 @@ -29,7 +29,7 @@ end v = rand(T, (N,)) w = rand(T, (2 * N,)) v2 = (v, w) - alg = Lanczos(; orth = orth, krylovdim = n, maxiter = 300, tol = tolerance(T)) + alg = Lanczos(; orth=orth, krylovdim=n, maxiter=300, tol=tolerance(T)) n1 = div(n, 2) D, V, info = eigsolve(v2, n1, :LR, alg) do x x1, x2 = x diff --git a/test/runtests.jl b/test/runtests.jl index 8268ef79..f242590e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -79,10 +79,10 @@ end println("Tests finished in $t seconds") module AquaTests - using KrylovKit - using Aqua - Aqua.test_all(KrylovKit; ambiguities = false) - # treat ambiguities special because of ambiguities between ChainRulesCore and Base - Aqua.test_ambiguities([KrylovKit, Base, Core]; exclude = [Base.:(==)]) +using KrylovKit +using Aqua +Aqua.test_all(KrylovKit; ambiguities=false) +# treat ambiguities special because of ambiguities between ChainRulesCore and Base +Aqua.test_ambiguities([KrylovKit, Base, Core]; exclude=[Base.:(==)]) end diff --git a/test/schursolve.jl b/test/schursolve.jl index b3dd9b90..bc0d4c7a 100644 --- a/test/schursolve.jl +++ b/test/schursolve.jl @@ -1,57 +1,42 @@ @testset "Arnoldi - schursolve full ($mode)" for mode in (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = rand(T, (n, n)) .- one(T) / 2 v = rand(T, (n,)) - alg = Arnoldi(; orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T)) + alg = Arnoldi(; orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T)) n1 = div(n, 2) - T1, V1, D1, info1 = @constinferred schursolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n1, :SR, - alg - ) - @test_logs schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, - alg - ) + T1, V1, D1, info1 = @constinferred schursolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n1, :SR, + alg) + @test_logs schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, + alg) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, - alg - ) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, :SR, + alg) alg = Arnoldi(; - orth = orth, krylovdim = n1 + 1, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, - :SR, - alg - ) + orth=orth, krylovdim=n1 + 1, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, + :SR, + alg) alg = Arnoldi(; - orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, - :SR, - alg - ) - - alg = Arnoldi(; orth = orth, krylovdim = n, maxiter = 1, tol = tolerance(T)) + orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n1, + :SR, + alg) + + alg = Arnoldi(; orth=orth, krylovdim=n, maxiter=1, tol=tolerance(T)) n2 = n - n1 - T2, V2, D2, info2 = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n2, - :LR, alg - ) - D = sort(sort(eigvals(A); by = imag, rev = true); alg = MergeSort, by = real) - D2′ = sort(sort(D2; by = imag, rev = true); alg = MergeSort, by = real) + T2, V2, D2, info2 = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n2, + :LR, alg) + D = sort(sort(eigvals(A); by=imag, rev=true); alg=MergeSort, by=real) + D2′ = sort(sort(D2; by=imag, rev=true); alg=MergeSort, by=real) @test vcat(D1[1:n1], D2′[(end - n2 + 1):end]) ≈ D @@ -64,15 +49,11 @@ @test A * U2 ≈ U2 * T2 if T <: Complex - T1, V1, D1, info = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - n1, :SI, alg - ) - T2, V2, D2, info = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - n2, :LI, alg - ) - D = sort(eigvals(A); by = imag) + T1, V1, D1, info = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + n1, :SI, alg) + T2, V2, D2, info = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + n2, :LI, alg) + D = sort(eigvals(A); by=imag) @test vcat(D1[1:n1], reverse(D2[1:n2])) ≈ D @@ -89,39 +70,32 @@ end @testset "Arnoldi - schursolve iteratively ($mode)" for mode in - (:vector, :inplace, :outplace) + (:vector, :inplace, :outplace) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = rand(T, (N, N)) .- one(T) / 2 v = rand(T, (N,)) alg = Arnoldi(; - orth = orth, krylovdim = 3 * n, maxiter = 10, tol = tolerance(T), - verbosity = SILENT_LEVEL - ) - T1, V1, D1, info1 = @constinferred schursolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), n, :SR, - alg - ) - T2, V2, D2, info2 = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :LR, alg - ) - T3, V3, D3, info3 = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, - :LM, alg - ) - D = sort(eigvals(A); by = imag, rev = true) + orth=orth, krylovdim=3 * n, maxiter=10, tol=tolerance(T), + verbosity=SILENT_LEVEL) + T1, V1, D1, info1 = @constinferred schursolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), n, :SR, + alg) + T2, V2, D2, info2 = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :LR, alg) + T3, V3, D3, info3 = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), n, + :LM, alg) + D = sort(eigvals(A); by=imag, rev=true) l1 = info1.converged l2 = info2.converged l3 = info3.converged - @test D1[1:l1] ≊ sort(D; alg = MergeSort, by = real)[1:l1] - @test D2[1:l2] ≊ sort(D; alg = MergeSort, by = real, rev = true)[1:l2] - @test D3[1:l3] ≊ sort(D; alg = MergeSort, by = abs, rev = true)[1:l3] + @test D1[1:l1] ≊ sort(D; alg=MergeSort, by=real)[1:l1] + @test D2[1:l2] ≊ sort(D; alg=MergeSort, by=real, rev=true)[1:l2] + @test D3[1:l3] ≊ sort(D; alg=MergeSort, by=abs, rev=true)[1:l3] U1 = stack(unwrapvec, V1) U2 = stack(unwrapvec, V2) @@ -138,20 +112,16 @@ end @test A * U3 ≈ U3 * T3 + R3 if T <: Complex - T1, V1, D1, info1 = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - n, :SI, alg - ) - T2, V2, D2, info2 = schursolve( - wrapop(A, Val(mode)), wrapvec(v, Val(mode)), - n, :LI, alg - ) + T1, V1, D1, info1 = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + n, :SI, alg) + T2, V2, D2, info2 = schursolve(wrapop(A, Val(mode)), wrapvec(v, Val(mode)), + n, :LI, alg) D = eigvals(A) l1 = info1.converged l2 = info2.converged - @test D1[1:l1] ≊ sort(D; by = imag)[1:l1] - @test D2[1:l2] ≊ sort(D; by = imag, rev = true)[1:l2] + @test D1[1:l1] ≊ sort(D; by=imag)[1:l1] + @test D2[1:l2] ≊ sort(D; by=imag, rev=true)[1:l2] U1 = stack(unwrapvec, V1) U2 = stack(unwrapvec, V2) diff --git a/test/svdsolve.jl b/test/svdsolve.jl index be4c69b5..db094c46 100644 --- a/test/svdsolve.jl +++ b/test/svdsolve.jl @@ -1,60 +1,44 @@ @testset "GKL - svdsolve full ($mode)" for mode in (:vector, :inplace, :outplace, :mixed) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths A = rand(T, (n, n)) - alg = GKL(; orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T)) - S, lvecs, rvecs, info = @constinferred svdsolve( - wrapop(A, Val(mode)), - wrapvec(A[:, 1], Val(mode)), n, - :LR, alg - ) + alg = GKL(; orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T)) + S, lvecs, rvecs, info = @constinferred svdsolve(wrapop(A, Val(mode)), + wrapvec(A[:, 1], Val(mode)), n, + :LR, alg) @test S ≈ svdvals(A) @test info.converged == n n1 = div(n, 2) - @test_logs svdsolve( - wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), n1, :LR, - alg - ) + @test_logs svdsolve(wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), n1, :LR, + alg) alg = GKL(; - orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs svdsolve( - wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), n1, :LR, - alg - ) + orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs svdsolve(wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), n1, :LR, + alg) alg = GKL(; - orth = orth, krylovdim = n1 + 1, maxiter = 1, tol = tolerance(T), - verbosity = WARN_LEVEL - ) - @test_logs (:warn,) svdsolve( - wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), - n1, :LR, - alg - ) + orth=orth, krylovdim=n1 + 1, maxiter=1, tol=tolerance(T), + verbosity=WARN_LEVEL) + @test_logs (:warn,) svdsolve(wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), + n1, :LR, + alg) alg = GKL(; - orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T), - verbosity = STARTSTOP_LEVEL - ) - @test_logs (:info,) svdsolve( - wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), - n1, :LR, - alg - ) + orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T), + verbosity=STARTSTOP_LEVEL) + @test_logs (:info,) svdsolve(wrapop(A, Val(mode)), wrapvec(A[:, 1], Val(mode)), + n1, :LR, + alg) alg = GKL(; - orth = orth, krylovdim = 2 * n, maxiter = 1, tol = tolerance(T), - verbosity = EACHITERATION_LEVEL + 1 - ) - @test_logs min_level = Logging.Warn svdsolve( - wrapop(A, Val(mode)), - wrapvec(A[:, 1], Val(mode)), - n1, :LR, - alg - ) + orth=orth, krylovdim=2 * n, maxiter=1, tol=tolerance(T), + verbosity=EACHITERATION_LEVEL + 1) + @test_logs min_level = Logging.Warn svdsolve(wrapop(A, Val(mode)), + wrapvec(A[:, 1], Val(mode)), + n1, :LR, + alg) U = stack(unwrapvec, lvecs) V = stack(unwrapvec, rvecs) @@ -66,9 +50,9 @@ end @testset "GKL - svdsolve iteratively ($mode)" for mode in - (:vector, :inplace, :outplace, :mixed) + (:vector, :inplace, :outplace, :mixed) scalartypes = mode === :vector ? (Float32, Float64, ComplexF32, ComplexF64) : - (ComplexF64,) + (ComplexF64,) orths = mode === :vector ? (cgs2, mgs2, cgsr, mgsr) : (mgsr,) @testset for T in scalartypes @testset for orth in orths @@ -76,14 +60,11 @@ end v = rand(T, (2 * N,)) n₁ = div(n, 2) alg = GKL(; - orth = orth, krylovdim = n, maxiter = 10, tol = tolerance(T), eager = true, - verbosity = SILENT_LEVEL - ) - S, lvecs, rvecs, info = @constinferred svdsolve( - wrapop(A, Val(mode)), - wrapvec(v, Val(mode)), - n₁, :LR, alg - ) + orth=orth, krylovdim=n, maxiter=10, tol=tolerance(T), eager=true, + verbosity=SILENT_LEVEL) + S, lvecs, rvecs, info = @constinferred svdsolve(wrapop(A, Val(mode)), + wrapvec(v, Val(mode)), + n₁, :LR, alg) l = info.converged @test S[1:l] ≈ svdvals(A)[1:l] diff --git a/test/testsetup.jl b/test/testsetup.jl index c214bbc3..973a5e41 100644 --- a/test/testsetup.jl +++ b/test/testsetup.jl @@ -64,10 +64,10 @@ using VectorInterface: MinimalSVec, MinimalMVec, MinimalVec function wrapvec(v, ::Val{mode}) where {mode} return mode === :vector ? v : - mode === :inplace ? MinimalMVec(v) : - mode === :outplace ? MinimalSVec(v) : - mode === :mixed ? MinimalSVec(v) : - throw(ArgumentError("invalid mode ($mode)")) + mode === :inplace ? MinimalMVec(v) : + mode === :outplace ? MinimalSVec(v) : + mode === :mixed ? MinimalSVec(v) : + throw(ArgumentError("invalid mode ($mode)")) end function wrapvec2(v, ::Val{mode}) where {mode} return mode === :mixed ? MinimalMVec(v) : wrapvec(v, mode) @@ -80,7 +80,7 @@ function wrapop(A, ::Val{mode}) where {mode} if mode === :vector return A elseif mode === :inplace || mode === :outplace - return function (v, flag = Val(false)) + return function (v, flag=Val(false)) if flag === Val(true) return wrapvec(A' * unwrapvec(v), Val(mode)) else @@ -88,10 +88,8 @@ function wrapop(A, ::Val{mode}) where {mode} end end elseif mode === :mixed - return ( - x -> wrapvec(A * unwrapvec(x), Val(mode)), - y -> wrapvec2(A' * unwrapvec(y), Val(mode)), - ) + return (x -> wrapvec(A * unwrapvec(x), Val(mode)), + y -> wrapvec2(A' * unwrapvec(y), Val(mode))) else throw(ArgumentError("invalid mode ($mode)")) end