Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
127 changes: 121 additions & 6 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,122 @@ 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:
## Combining Strategies
Comment thread
lkdvos marked this conversation as resolved.
Outdated

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.

For example, to keep at most 10 eigenvalues while also discarding all values below `1e-6`:

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

## 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:

```julia
maxdim = 10
atol = 1e-6
combined_trunc = truncrank(maxdim) & trunctol(; atol)
using MatrixAlgebraKit

# Create a symmetric matrix
A = randn(100, 100)
A = A + A' # Make symmetric

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

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

# Combine multiple criteria
D, V = eigh_trunc(A; trunc = (maxrank = 20, atol = 1e-10, rtol = 1e-8))
```

### 2. Using explicit `TruncationStrategy` objects

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

```julia
# Keep the 5 largest eigenvalues
strategy = truncrank(5)
D, V = eigh_trunc(A; trunc = strategy)

# Keep eigenvalues above an absolute tolerance
strategy = trunctol(; atol = 1e-6)
D, V = eigh_trunc(A; trunc = strategy)

# Combine strategies: keep at most 10 eigenvalues, all above 1e-8
strategy = truncrank(10) & trunctol(; atol = 1e-8)
D, V = eigh_trunc(A; trunc = strategy)
```

## Complete Example

Here's a complete example demonstrating different truncation approaches:

```julia
using MatrixAlgebraKit
using LinearAlgebra

# Generate a test matrix with known spectrum
n = 50
A = randn(n, n)
A = A + A' # Make symmetric

# 1. No truncation - keep all eigenvalues
D_full, V_full = eigh_trunc(A; trunc = nothing)
@assert size(D_full) == (n, n)

# 2. Keep only the 10 largest eigenvalues
D_rank, V_rank = eigh_trunc(A; trunc = (maxrank = 10,))
@assert size(D_rank) == (10, 10)
@assert size(V_rank) == (n, 10)

# 3. Keep eigenvalues with absolute value above a threshold
D_tol, V_tol = eigh_trunc(A; trunc = (atol = 1e-6,))
println("Kept $(size(D_tol, 1)) eigenvalues above tolerance")

# 4. Combine rank and tolerance truncation
strategy = truncrank(15) & trunctol(; atol = 1e-8)
D_combined, V_combined = eigh_trunc(A; trunc = strategy)
println("Kept $(size(D_combined, 1)) eigenvalues (max 15, all above 1e-8)")

# 5. Truncated SVD example
B = randn(100, 80)
U, S, Vh = svd_trunc(B; trunc = (maxrank = 20,))
@assert size(S) == (20, 20)
@assert size(U) == (100, 20)
@assert size(Vh) == (20, 80)

# Verify the truncated decomposition is accurate
@assert norm(B - U * S * Vh) ≈ norm(svd(B).S[21:end])
Comment thread
lkdvos marked this conversation as resolved.
Outdated
```

## 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:

```julia
# Keep only positive eigenvalues
strategy = truncfilter(x -> x > 0)
D_positive, V_positive = eigh_trunc(A; trunc = strategy)

# Keep eigenvalues in a specific range
strategy = truncfilter(x -> 0.1 < abs(x) < 10.0)
D_range, V_range = eigh_trunc(A; trunc = strategy)
```
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