Skip to content

Commit b825314

Browse files
CopilotlkdvosJutho
authored
Add documentation for truncated decomposition keyword arguments (#71)
* Initial plan * Update docstrings for truncated decompositions with detailed keyword explanations Co-authored-by: lkdvos <37111893+lkdvos@users.noreply.github.com> * Rewrite Truncations documentation with comprehensive examples and usage guide Co-authored-by: lkdvos <37111893+lkdvos@users.noreply.github.com> * Convert examples to doctests and use hardcoded matrices for reproducibility Co-authored-by: lkdvos <37111893+lkdvos@users.noreply.github.com> * Use svd_vals to verify truncation error equals discarded singular values Co-authored-by: Jutho <4162102+Jutho@users.noreply.github.com> * Add workflow to comment the PR docs * update truncation docs * update namedtuple interface * mark `trunc` kwarg as optional * fix signature * change eig notes and outlining * some other doc corrections and additions * fix typo * add new file to toc --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lkdvos <37111893+lkdvos@users.noreply.github.com> Co-authored-by: Jutho <4162102+Jutho@users.noreply.github.com> Co-authored-by: Lukas Devos <ldevos98@gmail.com> Co-authored-by: Jutho Haegeman <jutho.haegeman@ugent.be>
1 parent bd96dfb commit b825314

File tree

12 files changed

+303
-66
lines changed

12 files changed

+303
-66
lines changed

.github/workflows/PR_comment.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Docs preview comment
2+
on:
3+
pull_request:
4+
types: [labeled]
5+
6+
permissions:
7+
pull-requests: write
8+
jobs:
9+
pr_comment:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Create PR comment
13+
if: github.event_name == 'pull_request' && github.repository == github.event.pull_request.head.repo.full_name && github.event.label.name == 'documentation'
14+
uses: thollander/actions-comment-pull-request@v3
15+
with:
16+
message: 'After the build completes, the updated documentation will be available [here](https://quantumkithub.github.io/MatrixAlgebraKit.jl/previews/PR${{ github.event.number }}/)'
17+
comment-tag: 'preview-doc'

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ makedocs(;
5252
"user_interface/compositions.md",
5353
"user_interface/decompositions.md",
5454
"user_interface/truncations.md",
55+
"user_interface/properties.md",
5556
"user_interface/matrix_functions.md",
5657
],
5758
"Developer Interface" => "dev_interface.md",

docs/src/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ These operations typically follow some common skeleton, and here we go into a li
2626

2727
```@contents
2828
Pages = ["user_interface/compositions.md", "user_interface/decompositions.md",
29-
"user_interface/truncations.md", "user_interface/matrix_functions.md"]
29+
"user_interface/truncations.md", "user_interface/properties.md",
30+
"user_interface/matrix_functions.md"]
3031
Depth = 2
3132
```

docs/src/user_interface/decompositions.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Not all matrices can be diagonalized, and some real matrices can only be diagona
5555
In particular, the resulting decomposition can only guaranteed to be real for real symmetric inputs `A`.
5656
Therefore, we provide `eig_` and `eigh_` variants, where `eig` always results in complex-valued `V` and `D`, while `eigh` requires symmetric inputs but retains the scalartype of the input.
5757

58+
The full set of eigenvalues and eigenvectors can be computed using the [`eig_full`](@ref) and [`eigh_full`](@ref) functions.
5859
If only the eigenvalues are required, the [`eig_vals`](@ref) and [`eigh_vals`](@ref) functions can be used.
5960
These functions return the diagonal elements of `D` in a vector.
6061

@@ -99,7 +100,7 @@ Filter = t -> t isa Type && t <: MatrixAlgebraKit.LAPACK_EigAlgorithm
99100

100101
The [Schur decomposition](https://en.wikipedia.org/wiki/Schur_decomposition) transforms a complex square matrix `A` into a product `Q * T * Qᴴ`, where `Q` is unitary and `T` is upper triangular.
101102
It rewrites an arbitrary complex square matrix as unitarily similar to an upper triangular matrix whose diagonal elements are the eigenvalues of `A`.
102-
For real matrices, the same decomposition can be achieved with `T` being quasi-upper triangular, ie triangular with blocks of size `(1, 1)` and `(2, 2)` on the diagonal.
103+
For real matrices, the same decomposition can be achieved in real arithmetic by allowing `T` to be quasi-upper triangular, i.e. triangular with blocks of size `(1, 1)` and `(2, 2)` on the diagonal.
103104

104105
This decomposition is also useful for computing the eigenvalues of a matrix, which is exposed through the [`schur_vals`](@ref) function.
105106

@@ -117,12 +118,12 @@ Filter = t -> t isa Type && t <: MatrixAlgebraKit.LAPACK_EigAlgorithm
117118

118119
## Singular Value Decomposition
119120

120-
The [Singular Value Decomposition](https://en.wikipedia.org/wiki/Singular_value_decomposition) transforms a matrix `A` into a product `U * Σ * Vᴴ`, where `U` and `V` are orthogonal, and `Σ` is diagonal, real and non-negative.
121-
For a square matrix `A`, both `U` and `V` are unitary, and if the singular values are distinct, the decomposition is unique.
121+
The [Singular Value Decomposition](https://en.wikipedia.org/wiki/Singular_value_decomposition) transforms a matrix `A` into a product `U * Σ * Vᴴ`, where `U` and `Vᴴ` are unitary, and `Σ` is diagonal, real and non-negative.
122+
For a square matrix `A`, both `U` and `Vᴴ` are unitary, and if the singular values are distinct, the decomposition is unique.
122123

123124
For rectangular matrices `A` of size `(m, n)`, there are two modes of operation, [`svd_full`](@ref) and [`svd_compact`](@ref).
124-
The former ensures that the resulting `U`, and `V` remain square unitary matrices, of size `(m, m)` and `(n, n)`, with rectangular `Σ` of size `(m, n)`.
125-
The latter creates an isometric `U` of size `(m, min(m, n))`, and `V` of size `(n, min(m, n))`, with a square `Σ` of size `(min(m, n), min(m, n))`.
125+
The former ensures that the resulting `U`, and `Vᴴ` remain square unitary matrices, of size `(m, m)` and `(n, n)`, with rectangular `Σ` of size `(m, n)`.
126+
The latter creates an isometric `U` of size `(m, min(m, n))`, and `V = (Vᴴ)'` of size `(n, min(m, n))`, with a square `Σ` of size `(min(m, n), min(m, n))`.
126127

127128
It is also possible to compute the singular values only, using the [`svd_vals`](@ref) function.
128129
This then returns a vector of the values on the diagonal of `Σ`.
@@ -147,21 +148,23 @@ Filter = t -> t isa Type && t <: MatrixAlgebraKit.LAPACK_SVDAlgorithm
147148

148149
The [Polar Decomposition](https://en.wikipedia.org/wiki/Polar_decomposition) of a matrix `A` is a factorization `A = W * P`, where `W` is unitary and `P` is positive semi-definite.
149150
If `A` is invertible (and therefore square), the polar decomposition always exists and is unique.
150-
For non-square matrices, the polar decomposition is not unique, but `P` is.
151-
In particular, the polar decomposition is unique if `A` is full rank.
151+
For non-square matrices `A` of size `(m, n)`, the decomposition `A = W * P` with `P` positive semi-definite of size `(n, n)` and `W` isometric of size `(m, n)` exists only if `m >= n`, and is unique if `A` and thus `P` is full rank.
152+
For `m <= n`, we can analoguously decompose `A` as `A = P * Wᴴ` with `P` positive semi-definite of size `(m, m)` and `Wᴴ` of size `(m, n)` such that `W = (Wᴴ)'` is isometric. Only in the case `m = n` do both decompositions exist.
152153

153-
This decomposition can be computed for both sides, resulting in the [`left_polar`](@ref) and [`right_polar`](@ref) functions.
154+
The decompositions `A = W * P` or `A = P * Wᴴ` can be computed with the [`left_polar`](@ref) and [`right_polar`](@ref) functions, respectively.
154155

155156
```@docs; canonical=false
156157
left_polar
157158
right_polar
158159
```
159160

160-
These functions are implemented by first computing a singular value decomposition, and then constructing the polar decomposition from the singular values and vectors.
161-
Therefore, the relevant LAPACK-based implementation is the one for the SVD:
161+
These functions can be implemented by first computing a singular value decomposition, and then constructing the polar decomposition from the singular values and vectors. Alternatively, the polar decomposition can be computed using an
162+
iterative method based on Newton's method, that can be more efficient for large matrices, especially if they are
163+
close to being isometric already.
162164

163165
```@docs; canonical=false
164166
PolarViaSVD
167+
PolarNewton
165168
```
166169

167170
## Orthogonal Subspaces
@@ -179,7 +182,7 @@ right_orth
179182
## Null Spaces
180183

181184
Similarly, it can be convenient to obtain an orthogonal basis for the kernel or cokernel of a matrix.
182-
These are the compliments of the image and coimage, and can be computed using the [`left_null`](@ref) and [`right_null`](@ref) functions.
185+
These are the compliments of the coimage and image, respectively, and can be computed using the [`left_null`](@ref) and [`right_null`](@ref) functions.
183186
Again, this is typically implemented through a combination of the decompositions mentioned above, and serves as a convenient interface to these operations.
184187

185188
```@docs; canonical=false
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
```@meta
2+
CurrentModule = MatrixAlgebraKit
3+
CollapsedDocStrings = true
4+
```
5+
6+
# Matrix Properties
7+
8+
MatrixAlgebraKit.jl provides a number of methods to check various properties of matrices.
9+
10+
```@docs; canonical=false
11+
isisometric
12+
isunitary
13+
ishermitian
14+
isantihermitian
15+
```
16+
17+
Furthermore, there are also methods to project a matrix onto the nearest matrix (in 2-norm distance) with a given property.
18+
19+
```@docs; canonical=false
20+
project_isometric
21+
project_hermitian
22+
project_antihermitian
23+
```

docs/src/user_interface/truncations.md

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,112 @@ CollapsedDocStrings = true
55

66
# Truncations
77

8-
Currently, truncations are supported through the following different methods:
8+
Truncation strategies allow you to control which eigenvalues or singular values to keep when computing partial or truncated decompositions. These strategies are used in the functions [`eigh_trunc`](@ref), [`eig_trunc`](@ref), and [`svd_trunc`](@ref) to reduce the size of the decomposition while retaining the most important information.
9+
10+
## Using Truncations in Decompositions
11+
12+
Truncation strategies can be used with truncated decomposition functions in two ways, as illustrated below.
13+
For concreteness, we use the following matrix as an example:
14+
15+
```jldoctest truncations
16+
using MatrixAlgebraKit
17+
using MatrixAlgebraKit: diagview
18+
19+
A = [2 1 0; 1 3 1; 0 1 4];
20+
D, V = eigh_full(A);
21+
22+
diagview(D) ≈ [3 - √3, 3, 3 + √3]
23+
24+
# output
25+
26+
true
27+
```
28+
29+
### 1. Using the `trunc` keyword with a `NamedTuple`
30+
31+
The simplest approach is to pass a `NamedTuple` with the truncation parameters.
32+
For example, keeping only the largest 2 eigenvalues:
33+
34+
```jldoctest truncations
35+
Dtrunc, Vtrunc = eigh_trunc(A; trunc = (maxrank = 2,));
36+
size(Dtrunc, 1) <= 2
37+
38+
# output
39+
40+
true
41+
```
42+
43+
Note however that there are no guarantees on the order of the output values:
44+
45+
```jldoctest truncations
46+
diagview(Dtrunc) ≈ diagview(D)[[3, 2]]
47+
48+
# output
49+
50+
true
51+
```
52+
53+
You can also use tolerance-based truncation or combine multiple criteria:
54+
55+
```jldoctest truncations
56+
Dtrunc, Vtrunc = eigh_trunc(A; trunc = (atol = 2.9,));
57+
all(>(2.9), diagview(Dtrunc))
58+
59+
# output
60+
61+
true
62+
```
63+
64+
```jldoctest truncations
65+
Dtrunc, Vtrunc = eigh_trunc(A; trunc = (maxrank = 2, atol = 2.9));
66+
size(Dtrunc, 1) <= 2 && all(>(2.9), diagview(Dtrunc))
67+
68+
# output
69+
true
70+
```
71+
72+
In general, the keyword arguments that are supported can be found in the `TruncationStrategy` docstring:
73+
74+
```@docs; canonical = false
75+
TruncationStrategy
76+
```
77+
78+
79+
### 2. Using explicit `TruncationStrategy` objects
80+
81+
For more control, you can construct [`TruncationStrategy`](@ref) objects directly.
82+
This is also what the previous syntax will end up calling.
83+
84+
```jldoctest truncations
85+
Dtrunc, Vtrunc = eigh_trunc(A; trunc = truncrank(2))
86+
size(Dtrunc, 1) <= 2
87+
88+
# output
89+
90+
true
91+
```
92+
93+
```jldoctest truncations
94+
Dtrunc, Vtrunc = eigh_trunc(A; trunc = truncrank(2) & trunctol(; atol = 2.9))
95+
size(Dtrunc, 1) <= 2 && all(>(2.9), diagview(Dtrunc))
96+
97+
# output
98+
true
99+
```
100+
101+
## Truncation with SVD vs Eigenvalue Decompositions
102+
103+
When using truncations with different decomposition types, keep in mind:
104+
105+
- **`svd_trunc`**: Singular values are always real and non-negative, sorted in descending order. Truncation by value typically keeps the largest singular values.
106+
107+
- **`eigh_trunc`**: Eigenvalues are real but can be negative for symmetric matrices. By default, `truncrank` sorts by absolute value, so `truncrank(k)` keeps the `k` eigenvalues with largest magnitude (positive or negative).
108+
109+
- **`eig_trunc`**: For general (non-symmetric) matrices, eigenvalues can be complex. Truncation by absolute value considers the complex magnitude.
110+
111+
## Truncation Strategies
112+
113+
MatrixAlgebraKit provides several built-in truncation strategies:
9114

10115
```@docs; canonical=false
11116
notrunc
@@ -15,11 +120,10 @@ truncfilter
15120
truncerror
16121
```
17122

18-
It is additionally possible to combine truncation strategies by making use of the `&` operator.
19-
For example, truncating to a maximal dimension `10`, and discarding all values below `1e-6` would be achieved by:
123+
Truncation strategies can be combined using the `&` operator to create intersection-based truncation.
124+
When strategies are combined, only the values that satisfy all conditions are kept.
20125

21126
```julia
22-
maxdim = 10
23-
atol = 1e-6
24-
combined_trunc = truncrank(maxdim) & trunctol(; atol)
127+
combined_trunc = truncrank(10) & trunctol(; atol = 1e-6);
25128
```
129+

src/interface/eig.jl

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
# TODO: kwargs for sorting eigenvalues?
44

55
docs_eig_note = """
6-
Note that [`eig_full`](@ref) and its variants do not assume additional structure on the input,
7-
and therefore will always return complex eigenvalues and eigenvectors. For the real
8-
eigenvalue decomposition of symmetric or hermitian operators, see [`eigh_full`](@ref).
6+
Note that [`eig_full`](@ref) and its variants do not assume any symmetry structure on
7+
the input matrices, and therefore will always return complex eigenvalues and eigenvectors
8+
for reasons of type stability. For the eigenvalue decomposition of symmetric or hermitian
9+
operators, see [`eigh_full`](@ref).
910
"""
1011

1112
"""
@@ -24,32 +25,53 @@ and the diagonal matrix `D` contains the associated eigenvalues.
2425
as it may not always be possible to use the provided `DV` as output.
2526
2627
!!! note
27-
$(docs_eig_note)
28+
$(docs_eig_note)
2829
2930
See also [`eig_vals(!)`](@ref eig_vals) and [`eig_trunc(!)`](@ref eig_trunc).
3031
"""
3132
@functiondef eig_full
3233

3334
"""
34-
eig_trunc(A; kwargs...) -> D, V
35+
eig_trunc(A; [trunc], kwargs...) -> D, V
3536
eig_trunc(A, alg::AbstractAlgorithm) -> D, V
36-
eig_trunc!(A, [DV]; kwargs...) -> D, V
37+
eig_trunc!(A, [DV]; [trunc], kwargs...) -> D, V
3738
eig_trunc!(A, [DV], alg::AbstractAlgorithm) -> D, V
3839
3940
Compute a partial or truncated eigenvalue decomposition of the matrix `A`,
4041
such that `A * V ≈ V * D`, where the (possibly rectangular) matrix `V` contains
4142
a subset of eigenvectors and the diagonal matrix `D` contains the associated eigenvalues,
4243
selected according to a truncation strategy.
4344
45+
## Keyword arguments
46+
The behavior of this function is controlled by the following keyword arguments:
47+
48+
- `trunc`: Specifies the truncation strategy. This can be:
49+
- A `NamedTuple` with fields `atol`, `rtol`, and/or `maxrank`, which will be converted to
50+
a [`TruncationStrategy`](@ref). For details on available truncation strategies, see
51+
[Truncations](@ref).
52+
- A `TruncationStrategy` object directly (e.g., `truncrank(10)`, `trunctol(atol=1e-6)`, or
53+
combinations using `&`).
54+
- `nothing` (default), which keeps all eigenvalues.
55+
56+
- Other keyword arguments are passed to the algorithm selection procedure. If no explicit
57+
`alg` is provided, these keywords are used to select and configure the algorithm through
58+
[`MatrixAlgebraKit.select_algorithm`](@ref). The remaining keywords after algorithm
59+
selection are passed to the algorithm constructor. See [`MatrixAlgebraKit.default_algorithm`](@ref)
60+
for the default algorithm selection behavior.
61+
62+
When `alg` is a [`TruncatedAlgorithm`](@ref), the `trunc` keyword cannot be specified as the
63+
truncation strategy is already embedded in the algorithm.
64+
4465
!!! note
4566
The bang method `eig_trunc!` optionally accepts the output structure and
4667
possibly destroys the input matrix `A`. Always use the return value of the function
4768
as it may not always be possible to use the provided `DV` as output.
4869
4970
!!! note
50-
$(docs_eig_note)
71+
$(docs_eig_note)
5172
52-
See also [`eig_full(!)`](@ref eig_full) and [`eig_vals(!)`](@ref eig_vals).
73+
See also [`eig_full(!)`](@ref eig_full), [`eig_vals(!)`](@ref eig_vals), and
74+
[Truncations](@ref) for more information on truncation strategies.
5375
"""
5476
@functiondef eig_trunc
5577

@@ -67,7 +89,7 @@ Compute the list of eigenvalues of `A`.
6789
as it may not always be possible to use the provided `D` as output.
6890
6991
!!! note
70-
$(docs_eig_note)
92+
$(docs_eig_note)
7193
7294
See also [`eig_full(!)`](@ref eig_full) and [`eig_trunc(!)`](@ref eig_trunc).
7395
"""

0 commit comments

Comments
 (0)