-
-
Notifications
You must be signed in to change notification settings - Fork 205
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
Additional functionalities for exp utility: error estimate and IOP #372
Additional functionalities for exp utility: error estimate and IOP #372
Conversation
Not very sure about the interface for the error estimate at the moment. Do we just add an output argument |
Codecov Report
@@ Coverage Diff @@
## master #372 +/- ##
==========================================
- Coverage 83.64% 83.61% -0.03%
==========================================
Files 75 75
Lines 21639 21652 +13
==========================================
+ Hits 18099 18104 +5
- Misses 3540 3548 +8
Continue to review full report at Codecov.
|
So I've probably overcomplicated myself yesterday about error estimates... In both phipm and KIOPS the only necessary error estimate to have is for the highest order phi (in the case of KIOPS it's the exp). So there's no need to return a separate error estimate for all the outputs of Error estimates in action: using OrdinaryDiffEq: phi, phiv n = 100
m = 15
K = 4
A = diagm(-2 * ones(n)) + diagm(ones(n - 1), -1) + diagm(ones(n - 1), 1)
b = randn(n)
t = 5.0; P = phi(t*A, K)
w_exact = P[K] * b
w, errest = phiv(t, A, b, K; m=m, errest=true)
err = norm(w[:,K] - w_exact)
println("Error: $err, error estimate: $errest")
|
phiv
phiv
phiv
IOP is easier than I thought to implement so I'll just append the commit here. Because I'm not using a dedicated data structure for Turns out As a reference, some papers about using IOP to approximate matrix exponential-vector products: https://onlinelibrary.wiley.com/doi/pdf/10.1002/nla.2090 (Algorithm 1 gives a clear definition of IOP). |
Sanity checks: using OrdinaryDiffEq: arnoldi, getH srand(0)
n = 100
m = 15
A = diagm(-2 * ones(n)) + diagm(ones(n - 1), -1) + diagm(ones(n - 1), 1)
Aperm = A + 1e-5 * randn(n, n)
b = randn(n); For symmetric matrices, IOP(2) should be almost the same as Lanczos: Ks = arnoldi(A, b; m=m) # Lanczos
Ksperm = arnoldi(Aperm, b; m=m) # full Arnoldi
Ksperm2 = arnoldi(Aperm, b; m=m, iop=2); # IOP(2) getH(Ks)
getH(Ksperm)
getH(Ksperm2)
I'll do an detailed performance-cost analysis for IOP in the afternoon with large sparse |
Some test results for IOP: The test case is an As a comparison, the Laplacian is permuted to get a dense version. They differ a lot in their @time A_mul_B!(w, Asparse, b);
@time A_mul_B!(w, Adense, b);
This means that Because IOP saves the number of dot products and axpys, we expect to see a large increase in performance for the sparse case but not for the dense case: @time profile_arnoldi(Asparse, 0);
@time profile_arnoldi(Asparse, 2);
@time profile_arnoldi(Adense, 0);
@time profile_arnoldi(Adense, 2);
(Here an IOP value of 0 indicates full Arnoldi). Finally, the work-precision diagrams for the sparse case (note I didn't use a lot of trials so the results jiggle a bit): It is safe to say that in this case using IOP is a strictly better choice. If Full test script: |
Looks great! |
end | ||
# TODO: switch to overload `getproperty` in v0.7 | ||
getH(Ks::KrylovSubspace) = @view(Ks.H[1:Ks.m, 1:Ks.m]) | ||
getV(Ks::KrylovSubspace) = @view(Ks.V[:, 1:Ks.m]) | ||
getH(Ks::KrylovSubspace) = @view(Ks.H[1:Ks.m + 1, 1:Ks.m]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is everything +1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to get the "extended" H
and V
matrix from Arnoldi. Originally I just left out the last terms to make the outputs of getH
and getV
square for easier coding.
Since error estimate & correction is a need now the extra terms are required, so I added them back and also changed the "happy-breakdown" termination logic of the Arnoldi loop so that they are always calculated.
@@ -228,6 +238,9 @@ function arnoldi!(Ks::KrylovSubspace{B, T}, A, b::AbstractVector{T}; tol=1e-7, | |||
end | |||
V, H = getV(Ks), getH(Ks) | |||
vtol = tol * norm(A, Inf) | |||
if iop == 0 | |||
iop = m |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the PR you only show for 2? What does higher look like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the output of iop=p
, the Heisenberg matrix H
will have a length-p upper band (i.e. only p non-zero diagonals for the upper part). I checked this before running the performance tests and it's working as intended.
Performance & accuracy wise, haven't really tested yet but the idea is by increasing p
you get better accuracy and worse performance. For the almost symmetric operators that I showed in the tests, since IOP(2) is already good enough there's no need to check higher p
s. For general operators I would expect a more visible performance-accuracy tradeoff for adjusting p
.
One thing I forgot to mention is that: from the profiling results it is clear that At best it saves some space when the OK to merge this PR now? |
The aim of this PR is to address the second point of #243 (comment).
Some numerical results for the corrector:
So the corrector works best when the error is already small but actually amplifies error when it is big. IMO this is not very practical in real use... on the other hand, using the correction term purely as an error estimate should work well.