Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
17 changes: 17 additions & 0 deletions .github/workflows/PR_comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Docs preview comment
on:
pull_request:
types: [labeled]

permissions:
pull-requests: write
jobs:
pr_comment:
runs-on: ubuntu-latest
steps:
- name: Create PR comment
if: github.event_name == 'pull_request' && github.repository == github.event.pull_request.head.repo.full_name && github.event.label.name == 'documentation'
uses: thollander/actions-comment-pull-request@v3
with:
message: 'After the build completes, the updated documentation will be available [here](https://quantumkithub.github.io/MatrixAlgebraKit.jl/previews/PR${{ github.event.number }}/)'
comment-tag: 'preview-doc'
180 changes: 173 additions & 7 deletions docs/src/user_interface/truncations.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ CollapsedDocStrings = true

# Truncations

Currently, truncations are supported through the following different methods:
Truncation strategies allow you to control which eigenvalues or singular values to keep when computing partial or truncated decompositions. These strategies are used in functions like [`eigh_trunc`](@ref), [`eig_trunc`](@ref), and [`svd_trunc`](@ref) to reduce the size of the decomposition while retaining the most important information.

## Truncation Strategies

MatrixAlgebraKit provides several built-in truncation strategies:

```@docs; canonical=false
notrunc
Expand All @@ -15,11 +19,173 @@ truncfilter
truncerror
```

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

```jldoctest
julia> using MatrixAlgebraKit

julia> combined_trunc = truncrank(10) & trunctol(; atol = 1e-6);

julia> typeof(combined_trunc)
MatrixAlgebraKit.TruncationIntersection{Tuple{MatrixAlgebraKit.TruncationByOrder{typeof(abs)}, MatrixAlgebraKit.TruncationByValue{Float64, Int64, typeof(abs)}}}
```

## Using Truncations in Decompositions

Truncation strategies can be used with truncated decomposition functions in two ways:

### 1. Using the `trunc` keyword with a `NamedTuple`

The simplest approach is to pass a `NamedTuple` with the truncation parameters:

```jldoctest truncations
julia> using MatrixAlgebraKit

julia> # Create a symmetric matrix with known values
A = [4.0 2.0 1.0; 2.0 5.0 3.0; 1.0 3.0 6.0];

julia> # Keep only the 2 largest eigenvalues
D, V = eigh_trunc(A; trunc = (maxrank = 2,));

julia> size(D)
(2, 2)

julia> size(V)
(3, 2)
```

You can also use tolerance-based truncation or combine multiple criteria:
Comment thread
lkdvos marked this conversation as resolved.
Outdated

```jldoctest truncations
julia> # Keep eigenvalues with absolute value above tolerance
D, V = eigh_trunc(A; trunc = (atol = 1e-6,));

julia> size(D, 1) # All eigenvalues are above 1e-6
3

julia> # Combine multiple criteria
D, V = eigh_trunc(A; trunc = (maxrank = 2, atol = 1e-10));

julia> size(D)
(2, 2)
```

### 2. Using explicit `TruncationStrategy` objects

For more control, you can construct `TruncationStrategy` objects directly:

```jldoctest truncations
julia> # Keep the 2 largest eigenvalues
strategy = truncrank(2);

julia> D, V = eigh_trunc(A; trunc = strategy);

julia> size(D)
(2, 2)

julia> # Combine strategies: keep at most 2 eigenvalues, all above 1e-8
strategy = truncrank(2) & trunctol(; atol = 1e-8);

julia> D, V = eigh_trunc(A; trunc = strategy);

julia> size(D)
(2, 2)
```

## Complete Example

Here's a complete example demonstrating different truncation approaches:

```jldoctest complete_example
julia> using MatrixAlgebraKit, LinearAlgebra

julia> # Create a symmetric test matrix with known spectrum
A = [10.0 2.0 1.0 0.5;
2.0 8.0 1.5 0.3;
1.0 1.5 6.0 0.2;
0.5 0.3 0.2 4.0];

julia> # 1. No truncation - keep all eigenvalues
D_full, V_full = eigh_trunc(A; trunc = nothing);

julia> size(D_full)
(4, 4)

julia> # 2. Keep only the 2 largest eigenvalues
D_rank, V_rank = eigh_trunc(A; trunc = (maxrank = 2,));

julia> size(D_rank)
(2, 2)

julia> size(V_rank)
(4, 2)

julia> # 3. Keep eigenvalues with absolute value above a threshold
D_tol, V_tol = eigh_trunc(A; trunc = (atol = 5.0,));

julia> size(D_tol, 1) >= 2 # At least 2 eigenvalues are above 5.0
true

julia> # 4. Combine rank and tolerance truncation
strategy = truncrank(3) & trunctol(; atol = 1e-8);

julia> D_combined, V_combined = eigh_trunc(A; trunc = strategy);

julia> size(D_combined, 1) <= 3
true

julia> # 5. Truncated SVD example
B = [3.0 2.0 1.0; 1.0 4.0 2.0; 2.0 1.0 5.0; 0.5 1.0 2.0];

julia> U, S, Vh = svd_trunc(B; trunc = (maxrank = 2,));

julia> size(S)
(2, 2)

julia> size(U)
(4, 2)

julia> size(Vh)
(2, 3)

julia> # Verify the truncated decomposition error equals the discarded singular values
norm(B - U * S * Vh) ≈ norm(svd_vals(B)[3:end])
true
```

## Truncation with SVD vs Eigenvalue Decompositions

When using truncations with different decomposition types, keep in mind:

- **`svd_trunc`**: Singular values are always real and non-negative, sorted in descending order. Truncation by value typically keeps the largest singular values.

- **`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).

- **`eig_trunc`**: For general (non-symmetric) matrices, eigenvalues can be complex. Truncation by absolute value considers the complex magnitude.

## Advanced: Custom Truncation Filters

For specialized needs, you can use [`truncfilter`](@ref) to define custom selection criteria:

```jldoctest custom_filters
julia> using MatrixAlgebraKit

julia> A = [4.0 -1.0 2.0; -1.0 3.0 1.0; 2.0 1.0 -2.0];

julia> # Keep only positive eigenvalues
strategy = truncfilter(x -> x > 0);

julia> D_positive, V_positive = eigh_trunc(A; trunc = strategy);

julia> size(D_positive, 1) >= 2 # At least 2 positive eigenvalues
true

julia> # Keep eigenvalues in a specific range
strategy = truncfilter(x -> 1.0 < abs(x) < 5.0);

julia> D_range, V_range = eigh_trunc(A; trunc = strategy);

```julia
maxdim = 10
atol = 1e-6
combined_trunc = truncrank(maxdim) & trunctol(; atol)
julia> size(D_range, 1) >= 1 # At least 1 eigenvalue in range
true
```
27 changes: 24 additions & 3 deletions src/interface/eig.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,36 @@ See also [`eig_vals(!)`](@ref eig_vals) and [`eig_trunc(!)`](@ref eig_trunc).
@functiondef eig_full

"""
eig_trunc(A; kwargs...) -> D, V
eig_trunc(A; trunc, kwargs...) -> D, V
eig_trunc(A, alg::AbstractAlgorithm) -> D, V
eig_trunc!(A, [DV]; kwargs...) -> D, V
eig_trunc!(A, [DV]; trunc, kwargs...) -> D, V

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a default value that can be filled in here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I marked this as optional [trunc], and the explanation below already lists the default value nothing.

eig_trunc!(A, [DV], alg::AbstractAlgorithm) -> D, V

Compute a partial or truncated eigenvalue decomposition of the matrix `A`,
such that `A * V ≈ V * D`, where the (possibly rectangular) matrix `V` contains
a subset of eigenvectors and the diagonal matrix `D` contains the associated eigenvalues,
selected according to a truncation strategy.

## Keyword arguments
The behavior of this function is controlled by the following keyword arguments:

- `trunc`: Specifies the truncation strategy. This can be:
- A `NamedTuple` with fields `atol`, `rtol`, and/or `maxrank`, which will be converted to
a [`TruncationStrategy`](@ref). For details on available truncation strategies, see
[Truncations](@ref).
- A `TruncationStrategy` object directly (e.g., `truncrank(10)`, `trunctol(atol=1e-6)`, or
combinations using `&`).
- `nothing` (default), which keeps all eigenvalues.

- Other keyword arguments are passed to the algorithm selection procedure. If no explicit
`alg` is provided, these keywords are used to select and configure the algorithm through
[`MatrixAlgebraKit.select_algorithm`](@ref). The remaining keywords after algorithm
selection are passed to the algorithm constructor. See [`MatrixAlgebraKit.default_algorithm`](@ref)
for the default algorithm selection behavior.

When `alg` is a [`TruncatedAlgorithm`](@ref), the `trunc` keyword cannot be specified as the
truncation strategy is already embedded in the algorithm.

!!! note
The bang method `eig_trunc!` optionally accepts the output structure and
possibly destroys the input matrix `A`. Always use the return value of the function
Expand All @@ -49,7 +69,8 @@ selected according to a truncation strategy.
!!! note
$(docs_eig_note)

See also [`eig_full(!)`](@ref eig_full) and [`eig_vals(!)`](@ref eig_vals).
See also [`eig_full(!)`](@ref eig_full), [`eig_vals(!)`](@ref eig_vals), and
[Truncations](@ref) for more information on truncation strategies.
Comment thread
lkdvos marked this conversation as resolved.
"""
@functiondef eig_trunc

Expand Down
29 changes: 25 additions & 4 deletions src/interface/eigh.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,35 @@ See also [`eigh_vals(!)`](@ref eigh_vals) and [`eigh_trunc(!)`](@ref eigh_trunc)
@functiondef eigh_full

"""
eigh_trunc(A; kwargs...) -> D, V
eigh_trunc(A; trunc, kwargs...) -> D, V
eigh_trunc(A, alg::AbstractAlgorithm) -> D, V
eigh_trunc!(A, [DV]; kwargs...) -> D, V
eigh_trunc!(A, [DV]; trunc, kwargs...) -> D, V
eigh_trunc!(A, [DV], alg::AbstractAlgorithm) -> D, V

Compute a partial or truncated eigenvalue decomposition of the symmetric or hermitian matrix
`A`, such that `A * V ≈ V * D`, where the isometric matrix `V` contains a subset of the
orthogonal eigenvectors and the real diagonal matrix `D` contains the associated eigenvalues,
selected according to a truncation strategy.
selected according to a truncation strategy.

## Keyword arguments
The behavior of this function is controlled by the following keyword arguments:

- `trunc`: Specifies the truncation strategy. This can be:
- A `NamedTuple` with fields `atol`, `rtol`, and/or `maxrank`, which will be converted to
a [`TruncationStrategy`](@ref). For details on available truncation strategies, see
[Truncations](@ref).
- A `TruncationStrategy` object directly (e.g., `truncrank(10)`, `trunctol(atol=1e-6)`, or
combinations using `&`).
- `nothing` (default), which keeps all eigenvalues.

- Other keyword arguments are passed to the algorithm selection procedure. If no explicit
`alg` is provided, these keywords are used to select and configure the algorithm through
[`MatrixAlgebraKit.select_algorithm`](@ref). The remaining keywords after algorithm
selection are passed to the algorithm constructor. See [`MatrixAlgebraKit.default_algorithm`](@ref)
for the default algorithm selection behavior.

When `alg` is a [`TruncatedAlgorithm`](@ref), the `trunc` keyword cannot be specified as the
truncation strategy is already embedded in the algorithm.

!!! note
The bang method `eigh_trunc!` optionally accepts the output structure and
Expand All @@ -49,7 +69,8 @@ selected according to a truncation strategy.
!!! note
$(docs_eigh_note)

See also [`eigh_full(!)`](@ref eigh_full) and [`eigh_vals(!)`](@ref eigh_vals).
See also [`eigh_full(!)`](@ref eigh_full), [`eigh_vals(!)`](@ref eigh_vals), and
[Truncations](@ref) for more information on truncation strategies.
"""
@functiondef eigh_trunc

Expand Down
31 changes: 25 additions & 6 deletions src/interface/svd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,45 @@ See also [`svd_full(!)`](@ref svd_full), [`svd_vals(!)`](@ref svd_vals) and
"""
@functiondef svd_compact

# TODO: decide if we should have `svd_trunc!!` instead
"""
svd_trunc(A; kwargs...) -> U, S, Vᴴ
svd_trunc(A; trunc, kwargs...) -> U, S, Vᴴ
svd_trunc(A, alg::AbstractAlgorithm) -> U, S, Vᴴ
svd_trunc!(A, [USVᴴ]; kwargs...) -> U, S, Vᴴ
svd_trunc!(A, [USVᴴ]; trunc, kwargs...) -> U, S, Vᴴ
svd_trunc!(A, [USVᴴ], alg::AbstractAlgorithm) -> U, S, Vᴴ

Compute a partial or truncated singular value decomposition (SVD) of `A`, such that
`A * (Vᴴ)' = U * S`. Here, `U` is an isometric matrix (orthonormal columns) of size
`(m, k)`, whereas `Vᴴ` is a matrix of size `(k, n)` with orthonormal rows and `S` is a
square diagonal matrix of size `(k, k)`, with `k` is set by the truncation strategy.

## Keyword arguments
The behavior of this function is controlled by the following keyword arguments:

- `trunc`: Specifies the truncation strategy. This can be:
- A `NamedTuple` with fields `atol`, `rtol`, and/or `maxrank`, which will be converted to
a [`TruncationStrategy`](@ref). For details on available truncation strategies, see
[Truncations](@ref).
- A `TruncationStrategy` object directly (e.g., `truncrank(10)`, `trunctol(atol=1e-6)`, or
combinations using `&`).
- `nothing` (default), which keeps all singular values.

- Other keyword arguments are passed to the algorithm selection procedure. If no explicit
`alg` is provided, these keywords are used to select and configure the algorithm through
[`MatrixAlgebraKit.select_algorithm`](@ref). The remaining keywords after algorithm
selection are passed to the algorithm constructor. See [`MatrixAlgebraKit.default_algorithm`](@ref)
for the default algorithm selection behavior.

When `alg` is a [`TruncatedAlgorithm`](@ref), the `trunc` keyword cannot be specified as the
truncation strategy is already embedded in the algorithm.

!!! note
The bang method `svd_trunc!` optionally accepts the output structure and
possibly destroys the input matrix `A`. Always use the return value of the function
as it may not always be possible to use the provided `USVᴴ` as output.


See also [`svd_full(!)`](@ref svd_full), [`svd_compact(!)`](@ref svd_compact) and
[`svd_vals(!)`](@ref svd_vals).
See also [`svd_full(!)`](@ref svd_full), [`svd_compact(!)`](@ref svd_compact),
[`svd_vals(!)`](@ref svd_vals), and [Truncations](@ref) for more information on
truncation strategies.
"""
@functiondef svd_trunc

Expand Down
Loading