[RFC] Symplectic Arnoldi - #151
simeonschaub wants to merge 15 commits into
Conversation
|
Your PR no longer requires formatting changes. Thank you for your contribution! |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #151 +/- ##
==========================================
- Coverage 88.42% 85.37% -3.05%
==========================================
Files 36 38 +2
Lines 3964 4219 +255
==========================================
+ Hits 3505 3602 +97
- Misses 459 617 +158 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
c68189d to
8525cbd
Compare
|
Ok, I think I am happy with the implementation now from my side. This should enable some initial experimentation with symplectic Gram-Schmidt methods. Future work might include blocked methods such as described in https://journal.austms.org.au/ojs/index.php/ANZIAMJ/article/view/9380/1920, but I don't want to make this PR much bigger than it already is. |
|
Ok, I will have to study this a bit further and let it sink in, but my initial impression is that I am not a big fan of "recycling" |
|
Sure, I can change the design! I wasn't too sure about extending |
| rayleighquotient(F::ArnoldiFactorization) = PackedHessenberg(F.H, F.k) | ||
| residual(F::ArnoldiFactorization) = F.r | ||
| @inbounds normres(F::ArnoldiFactorization) = abs(F.H[end]) | ||
| @inbounds normres(F::ArnoldiFactorization) = F.H[end] |
There was a problem hiding this comment.
What is the rationale behind this change? Can normres be negative in the skew-orthogonal case? In the orthogonal case, I agree that F.H[end] is positive by construction, but in the complex case, it is a positive number encoded in a complex data type, and I would like normres to return a real number. So is real(F.H[end]) a solution?
There was a problem hiding this comment.
Yes, for symplectic bases normres is allowed to be negative, since all the even vectors need to be scaled in a way such that ω(last(b), v) = +1. So if we don't allow normres to be negative ω(last(b), v) might come out to be -1 instead of +1, which is not what we want.
I haven't really played around with complex vectors yet, but I imagine the phase would be relevant there as well. Would a good compromise be to have two methods:
@inbounds normres(F::ArnoldiFactorization{<:Any, <:Any, <:OrthonormalBasis}) = abs(F.H[end])
@inbounds normres(F::ArnoldiFactorization{<:Any, <:Any, <:SymplecticBasis}) = F.H[end]?
|
Thanks for the changes; this is starting to look really nice. I have a few remaining comments and suggestions:
|
Did you want me to add some more docs regarding this to the docstrings of |
|
Did you have a chance to take another look? |
|
My apologies for the long silence. Overall I think this is a great PR with very nice and consistent coding patterns. The only part of which I am not convinced is including both the orthogonal and symplectic Arnoldi process in the same I would propose to have an explicit |
3623311 to
c7d0666
Compare
Instead of parameterizing `ArnoldiFactorization` on the basis type, the
symplectic Arnoldi process now uses a dedicated
`SymplecticArnoldiFactorization`. Methods that are identical for both
structures use a `Union{ArnoldiFactorization, SymplecticArnoldiFactorization}`
argument, while `initialize(!)`, `expand!` and `shrink!` now have separate
method definitions for the orthogonal and symplectic case instead of
`if ... else ...` branches on the orthogonalizer type.
Assisted-By: Claude Fable 5 <noreply@anthropic.com>
- `shrink!` for `SymplecticArnoldiFactorization` rescaled the residual by `norm(r)` when shrinking to odd length, but the popped even-index basis vector is ω-normalized, so the stored coefficient `H[end]` is the correct scale. Since that coefficient is also what `norm(r)` evaluates to in the odd-index case, both `shrink!` and `expand!` now simply use `normres(state)` unconditionally, like their orthonormal counterparts. - the `Base.iterate` termination check now uses `abs(normres(state))`, as `normres` of a `SymplecticArnoldiFactorization` is a signed (or complex) scaling factor; a negative value previously terminated iteration immediately. - `expand!` now throws on symplectic breakdown (zero `normres`) instead of silently producing Inf/NaN basis vectors, matching the guard in `initialize`. - `skeworthonormalize!!` normalized the input binding instead of the vector returned by `skeworthogonalize!!`, which is wrong for vector types whose `!!` methods return new objects. - `skewproject!!` read `r[j + 1]` out of bounds under `@inbounds` for the default index range on an odd-length basis; the default now covers complete pairs and uneven ranges throw an `ArgumentError`. - `reskeworthogonalize!!` no longer allocates a fresh coefficient vector on every reorthogonalization pass of `ClassicalSymplecticGramSchmidtIR`. - add tests for `shrink!`, the `Base.iterate` protocol and `skeworthonormalize`. Assisted-By: Claude Fable 5 <noreply@anthropic.com>
c7d0666 to
d8267cd
Compare
|
Sorry, I was a little busy with other stuff these last few months, but finally had time to come back to this! I addressed your review comments. I also had claude review the existing code and it found some correctness issues I had it fix and add some additional tests. I reviewed the fixes and they all seemed sensible to me |
See the discussion in #150. On my test case, this already gives me
significantly better performance, and improved accuracy, if using
re-skew-orthogonalization, than my hand-rolled Arnoldi implementation. An initial sketch was written by Copilot, but
I revised and reviewed the result extensively.
closes #150