Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ad extension [WIP] #85

Merged
merged 23 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,37 @@ authors = ["Jutho Haegeman"]
version = "0.7.1"

[deps]
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
VectorInterface = "409d34a3-91d5-4945-b6ec-7529ddf182d8"

[weakdeps]
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"

[extensions]
KrylovKitChainRulesCoreExt = "ChainRulesCore"

[compat]
Aqua = "0.6, 0.7, 0.8"
ChainRulesCore = "1"
ChainRulesTestUtils = "1"
FiniteDifferences = "0.12"
GPUArraysCore = "0.1"
VectorInterface = "0.4"
LinearAlgebra = "1"
Random = "1"
PackageExtensionCompat = "1"
Printf = "1"
Random = "1"
Test = "1"
TestExtras = "0.2"
VectorInterface = "0.4"
Zygote = "0.6"
julia = "1.6"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
ChainRulesTestUtils = "cdddcdb0-9152-4a09-a978-84456f9df70a"
FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand All @@ -35,4 +43,4 @@ TestExtras = "5ed8adda-3752-4e41-b88a-e8b09835ee3a"
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"

[targets]
test = ["Test", "Aqua", "Random", "TestExtras", "ChainRulesTestUtils", "FiniteDifferences", "Zygote"]
test = ["Test", "Aqua", "Random", "TestExtras", "ChainRulesTestUtils", "ChainRulesCore", "FiniteDifferences", "Zygote"]
15 changes: 15 additions & 0 deletions ext/KrylovKitChainRulesCoreExt/KrylovKitChainRulesCoreExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module KrylovKitChainRulesCoreExt

using KrylovKit
using ChainRulesCore
using LinearAlgebra
using VectorInterface

using KrylovKit: apply_normal, apply_adjoint

include("utilities.jl")
include("linsolve.jl")
include("eigsolve.jl")
include("svdsolve.jl")

end # module
322 changes: 322 additions & 0 deletions ext/KrylovKitChainRulesCoreExt/eigsolve.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
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)
T, fᴴ, construct∂f = _prepare_inputs(config, f, vecs, alg_primal)

function eigsolve_pullback(ΔX)
∂self = NoTangent()
∂x₀ = ZeroTangent()
∂howmany = NoTangent()
∂which = NoTangent()
∂alg = NoTangent()

_Δvals = unthunk(ΔX[1])
_Δvecs = unthunk(ΔX[2])

n = 0
while true
if !(_Δvals isa AbstractZero) &&
any(!iszero, view(_Δvals, (n + 1):length(_Δvals)))
n = n + 1
continue
end
if !(_Δvecs isa AbstractZero) &&
any(!Base.Fix2(isa, AbstractZero), view(_Δvecs, (n + 1):length(_Δvecs)))
n = n + 1
continue
end
break
end
@assert n <= length(vals)
Jutho marked this conversation as resolved.
Show resolved Hide resolved
if n == 0
∂f = ZeroTangent()
return ∂self, ∂f, ∂x₀, ∂howmany, ∂which, ∂alg

Check warning on line 44 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L43-L44

Added lines #L43 - L44 were not covered by tests
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a type instability (I assume this is what you were referring to this week?)
Do you think we can avoid this by:

  1. inserting pullback_eigsolve(ΔX::Tuple{AbstractZero, AbstractZero, Any}) = [...] (with ∂f = ZeroTangent())
  2. throwing a warning and explicitly computing the zero pullback

I am honestly not sure if the second case ever happens, as I think this implies that the dependence on the eigenvalues and eigenvectors is exactly zero, which sounds incredibly implausible with floating point accuracy. This would both mean that the regular (most common) case is now type-stable, and that the case where n = 0 gets handled properly when both inputs are AbstractZero (which afaik e.g. Zygote would never even generate either)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will get rid of this.

if _Δvals isa AbstractZero
Δvals = fill(zero(vals[1]), n)

Check warning on line 47 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L47

Added line #L47 was not covered by tests
else
@assert length(_Δvals) >= n
Δvals = view(_Δvals, 1:n)
end
if _Δvecs isa AbstractZero
Δvecs = fill(ZeroTangent(), n)

Check warning on line 53 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L53

Added line #L53 was not covered by tests
else
@assert length(_Δvecs) >= n
Δvecs = view(_Δvecs, 1:n)
end

ws = compute_eigsolve_pullback_data(Δvals, Δvecs, view(vals, 1:n), view(vecs, 1:n),
info, which, fᴴ, T, alg_primal, alg_rrule)
∂f = construct∂f(ws)
return ∂self, ∂f, ∂x₀, ∂howmany, ∂which, ∂alg
end
return (vals, vecs, info), eigsolve_pullback
end

function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, T,
alg_primal, alg_rrule::Union{GMRES,BiCGStab})
ws = similar(vecs, length(Δvecs))
@inbounds for i in 1:length(Δvecs)
Δλ = Δvals[i]
Δv = Δvecs[i]
λ = vals[i]
v = vecs[i]

# First threat special cases
Jutho marked this conversation as resolved.
Show resolved Hide resolved
if isa(Δv, AbstractZero) && isa(Δλ, AbstractZero) # no contribution
ws[i] = zerovector(v)
continue

Check warning on line 79 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L78-L79

Added lines #L78 - L79 were not covered by tests
end
if isa(Δv, AbstractZero) && isa(alg_primal, Lanczos) # simple contribution
ws[i] = scale(v, Δλ)
continue

Check warning on line 83 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L82-L83

Added lines #L82 - L83 were not covered by tests
end

# General case :

# for the case where `f` is a real matrix, we can expect the following simplication
# TODO: can we implement this within our general approach, or generalise this to also
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that still has to be added? Do we have to check that eltype(A) is real, or do you think we can get away with just assuming that exact conjugate pairs only ever appear in that context?
conj(vec) is definitely not something that is part of our current interface for vectors, so in that sense it is also not ideal...

In principle I have nothing against just explicitly adding if f isa Matrix{<:Real} && i > 1 or something along these lines. I think the compiler is smart enough to just elide that whenever appropriate

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would indeed work.

# cover the case where `f` is a function?
# if i > 1 && eltype(A) <: Real &&
# vals[i] == conj(vals[i - 1]) && Δvals[i] == conj(Δvals[i - 1]) &&
# vecs[i] == conj(vecs[i - 1]) && Δvecs[i] == conj(Δvecs[i - 1])
# ws[i] = conj(ws[i - 1])
# continue
# end

if isa(Δv, AbstractZero)
b = (zerovector(v), convert(T, Δλ))

Check warning on line 99 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L99

Added line #L99 was not covered by tests
else
vdΔv = inner(v, Δv)
gaugeᵢ = abs(imag(vdΔv))
if gaugeᵢ > alg_primal.tol && alg_rrule.verbosity >= 1
@warn "`eigsolve` cotangent for eigenvector $i is sensitive to gauge choice: (|gaugeᵢ| = $gaugeᵢ)"

Check warning on line 104 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L104

Added line #L104 was not covered by tests
end
Δv = add(Δv, v, -vdΔv)
b = (Δv, convert(T, Δλ))
end
w, reverse_info = let λ = λ, v = v
linsolve(b, zerovector(b), alg_rrule) do x
x1, x2 = x
Jutho marked this conversation as resolved.
Show resolved Hide resolved
y1 = VectorInterface.add!!(VectorInterface.add!!(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_rrule.verbosity >= 0
@warn "`eigsolve` cotangent linear problem ($i) did not converge, whereas the primal eigenvalue problem did: normres = $(reverse_info.normres)"

Check warning on line 119 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L119

Added line #L119 was not covered by tests
elseif abs(w[2]) > alg_rrule.tol && alg_rrule.verbosity >= 0
@warn "`eigsolve` cotangent linear problem ($i) returns unexpected result: error = $(w[2])"

Check warning on line 121 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L121

Added line #L121 was not covered by tests
end
ws[i] = w[1]
end
return ws
end

function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, T,
alg_primal::Arnoldi, alg_rrule::Arnoldi)
n = length(Δvecs)
G = zeros(T, n, n)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
G = zeros(T, n, n)
G = Matrix{T}(undef, n, n) # eigenvector overlap matrix

VdΔV = zeros(T, n, n)
for j in 1:n
for i in 1:n
lkdvos marked this conversation as resolved.
Show resolved Hide resolved
if i < j
G[i, j] = conj(G[j, i])
elseif i == j
G[i, i] = norm(vecs[i])^2
else
G[i, j] = inner(vecs[i], vecs[j])
end
if !(Δvecs[j] isa AbstractZero)
VdΔV[i, j] = inner(vecs[i], Δvecs[j])
end
end
end

# components along subspace spanned by current eigenvectors
tol = alg_primal.tol
mask = abs.(transpose(vals) .- vals) .< tol
gaugepart = VdΔV[mask] - Diagonal(real(diag(VdΔV)))[mask]
Δgauge = norm(gaugepart, Inf)
if Δgauge > tol && alg_rrule.verbosity >= 1
@warn "`eigsolve` cotangents sensitive to gauge choice: (|Δgauge| = $Δgauge)"

Check warning on line 154 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L154

Added line #L154 was not covered by tests
end
Jutho marked this conversation as resolved.
Show resolved Hide resolved
VdΔV′ = VdΔV - G * Diagonal(diag(VdΔV) ./ diag(G))
aVdΔV = VdΔV′ .* conj.(safe_inv.(transpose(vals) .- vals, tol))
for i in 1:n
aVdΔV[i, i] += Δvals[i]
end
Gc = cholesky!(G)
iGaVdΔV = Gc \ aVdΔV
iGVdΔV = Gc \ VdΔV

zs = similar(Δvecs)
for i in 1:n
z = scale(vecs[1], iGaVdΔV[1, i])
for j in 2:n
z = VectorInterface.add!!(z, vecs[j], iGaVdΔV[j, i])
end
zs[i] = z
end

# components in orthogonal subspace
sylvesterarg = similar(Δvecs)
for i in 1:n
y = fᴴ(zs[i])
if !(Δvecs[i] isa AbstractZero)
y = VectorInterface.add!!(y, Δvecs[i], +1)
Jutho marked this conversation as resolved.
Show resolved Hide resolved
for j in 1:n
y = VectorInterface.add!!(y, vecs[j], -iGVdΔV[j, i])
end
end
sylvesterarg[i] = y
end

W₀ = (zerovector(vecs[1]), one.(vals))
P = orthogonalprojector(vecs, n, Gc)
by, rev = KrylovKit.eigsort(which)
lkdvos marked this conversation as resolved.
Show resolved Hide resolved
if (rev ? (by(vals[n]) < by(zero(vals[n]))) : (by(vals[n]) > by(zero(vals[n]))))
shift = 2 * conj(vals[n])
else
shift = zero(vals[n])
end
rvals, Ws, reverse_info = let P = P, ΔV = sylvesterarg, shift = shift
eigsolve(W₀, n, reverse_wich(which), alg_rrule) do W
w, x = W
Jutho marked this conversation as resolved.
Show resolved Hide resolved
w₀ = P(w)
w′ = fᴴ(add(w, w₀, -1))
if !iszero(shift)
w′ = VectorInterface.add!!(w′, w₀, shift)
end
@inbounds for i in 1:length(x) # length(x) = n but let us not use outer variables
Jutho marked this conversation as resolved.
Show resolved Hide resolved
w′ = VectorInterface.add!!(w′, ΔV[i], -x[i])
end
return (w′, conj.(vals) .* x)
end
end
if info.converged >= n && reverse_info.converged < n && alg_rrule.verbosity >= 0
@warn "`eigsolve` cotangent problem did not converge, whereas the primal eigenvalue problem did"

Check warning on line 210 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L210

Added line #L210 was not covered by tests
end

# cleanup and construct final result
ws = zs
tol = alg_rrule.tol
Q = orthogonalcomplementprojector(vecs, n, Gc)
for i in 1:n
w, x = Ws[i]
_, ic = findmax(abs, x)
factor = 1 / x[ic]
x[ic] = zero(x[ic])
error = max(norm(x, Inf), abs(rvals[i] - conj(vals[ic])))
if error > 5 * tol && alg_rrule.verbosity >= 0
@warn "`eigsolve` cotangent linear problem ($ic) returns unexpected result: error = $error"

Check warning on line 224 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L224

Added line #L224 was not covered by tests
end
Jutho marked this conversation as resolved.
Show resolved Hide resolved
ws[ic] = VectorInterface.add!!(zs[ic], Q(w), -factor)
end
return ws
end

# several simplications happen in the case of a Hermitian eigenvalue problem
function compute_eigsolve_pullback_data(Δvals, Δvecs, vals, vecs, info, which, fᴴ, T,

Check warning on line 232 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L232

Added line #L232 was not covered by tests
alg_primal::Lanczos, alg_rrule::Arnoldi)
n = length(Δvecs)
VdΔV = zeros(T, n, n)
for j in 1:n
for i in 1:n
if !(Δvecs[j] isa AbstractZero)
VdΔV[i, j] = inner(vecs[i], Δvecs[j])

Check warning on line 239 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L234-L239

Added lines #L234 - L239 were not covered by tests
end
end
end

Check warning on line 242 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L241-L242

Added lines #L241 - L242 were not covered by tests

# components along subspace spanned by current eigenvectors
tol = alg_primal.tol
aVdΔV = rmul!(VdΔV - VdΔV', 1 / 2)
mask = abs.(transpose(vals) .- vals) .< tol
gaugepart = view(aVdΔV, mask)
Δgauge = norm(gaugepart, Inf)
if Δgauge > tol && alg_rrule.verbosity >= 1
@warn "`eigsolve` cotangents sensitive to gauge choice: (|Δgauge| = $Δgauge)"

Check warning on line 251 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L245-L251

Added lines #L245 - L251 were not covered by tests
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about avoiding computing the check when the verbosity is low

aVdΔV .= aVdΔV .* safe_inv.(transpose(vals) .- vals, tol)
for i in 1:n
aVdΔV[i, i] += real(Δvals[i])
end

Check warning on line 256 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L253-L256

Added lines #L253 - L256 were not covered by tests

zs = similar(Δvecs)
for i in 1:n
z = scale(vecs[1], aVdΔV[1, i])
for j in 2:n
z = VectorInterface.add!!(z, vecs[j], aVdΔV[j, i])
end
zs[i] = z
end

Check warning on line 265 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L258-L265

Added lines #L258 - L265 were not covered by tests

# components in orthogonal subspace
sylvesterarg = similar(Δvecs)
for i in 1:n
y = zerovector(vecs[1])
if !(Δvecs[i] isa AbstractZero)
y = VectorInterface.add!!(y, Δvecs[i], +1)
for j in 1:n
y = VectorInterface.add!!(y, vecs[j], -VdΔV[j, i])
end

Check warning on line 275 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L268-L275

Added lines #L268 - L275 were not covered by tests
end
sylvesterarg[i] = y
end

Check warning on line 278 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L277-L278

Added lines #L277 - L278 were not covered by tests

W₀ = (zerovector(vecs[1]), one.(vals))
P = orthogonalprojector(vecs, n)
by, rev = KrylovKit.eigsort(which)
if (rev ? (by(vals[n]) < by(zero(vals[n]))) : (by(vals[n]) > by(zero(vals[n]))))
shift = 2 * conj(vals[n])

Check warning on line 284 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L280-L284

Added lines #L280 - L284 were not covered by tests
else
shift = zero(vals[n])

Check warning on line 286 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L286

Added line #L286 was not covered by tests
end
rvals, Ws, reverse_info = let P = P, ΔV = sylvesterarg, shift = shift
eigsolve(W₀, n, reverse_wich(which), alg_rrule) do W
w, x = W
Jutho marked this conversation as resolved.
Show resolved Hide resolved
w₀ = P(w)
w′ = fᴴ(add(w, w₀, -1))
if !iszero(shift)
w′ = VectorInterface.add!!(w′, w₀, shift)

Check warning on line 294 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L288-L294

Added lines #L288 - L294 were not covered by tests
end
@inbounds for i in 1:length(x) # length(x) = n but let us not use outer variables
w′ = VectorInterface.add!!(w′, ΔV[i], -x[i])
end
return (w′, vals .* x)

Check warning on line 299 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L296-L299

Added lines #L296 - L299 were not covered by tests
end
end
if info.converged >= n && reverse_info.converged < n && alg_rrule.verbosity >= 0
@warn "`eigsolve` cotangent problem did not converge, whereas the primal eigenvalue problem did"

Check warning on line 303 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L302-L303

Added lines #L302 - L303 were not covered by tests
end

# cleanup and construct final result
ws = zs
tol = alg_rrule.tol
Q = orthogonalcomplementprojector(vecs, n)
for i in 1:n
w, x = Ws[i]
_, ic = findmax(abs, x)
factor = 1 / x[ic]
x[ic] = zero(x[ic])
error = max(norm(x, Inf), abs(rvals[i] - conj(vals[ic])))
if error > 5 * tol && alg_rrule.verbosity >= 0
@warn "`eigsolve` cotangent linear problem ($ic) returns unexpected result: error = $error"

Check warning on line 317 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L307-L317

Added lines #L307 - L317 were not covered by tests
end
ws[ic] = VectorInterface.add!!(zs[ic], Q(w), -factor)
end
return ws

Check warning on line 321 in ext/KrylovKitChainRulesCoreExt/eigsolve.jl

View check run for this annotation

Codecov / codecov/patch

ext/KrylovKitChainRulesCoreExt/eigsolve.jl#L319-L321

Added lines #L319 - L321 were not covered by tests
end
Loading
Loading