Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ makedocs(;
"User Interface" => [
"user_interface/compositions.md",
"user_interface/decompositions.md",
"user_interface/algorithms.md",
"user_interface/truncations.md",
"user_interface/properties.md",
"user_interface/matrix_functions.md",
Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ These operations typically follow some common skeleton, and here we go into a li

```@contents
Pages = ["user_interface/compositions.md", "user_interface/decompositions.md",
"user_interface/algorithms.md",
"user_interface/truncations.md", "user_interface/properties.md",
"user_interface/matrix_functions.md"]
Depth = 2
Expand Down
144 changes: 144 additions & 0 deletions docs/src/user_interface/algorithms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
```@meta
CurrentModule = MatrixAlgebraKit
CollapsedDocStrings = true
```

# [Algorithm Selection](@id sec_algorithmselection)

All factorization functions in MatrixAlgebraKit accept an optional `alg` keyword argument that controls which algorithm is used and how it is configured.
By default, an appropriate algorithm is selected automatically based on the function and the input types.
This page explains how to override that default, what algorithm types are available, and how to configure them.

## The `alg` Keyword

The `alg` keyword is interpreted by [`MatrixAlgebraKit.select_algorithm`](@ref), which accepts five different forms for specifying an algorithm and its configuration.
For example, for `qr_compact` these forms look like:

```julia
# Form 1: No alg — algorithm selected automatically based on function and input type.
Q, R = qr_compact(A);

# Form 2: Symbol — creates Algorithm{:Householder}(; positive=false).
Q, R = qr_compact(A; alg = :Householder, positive = false);

# Form 3: Algorithm type — calls Householder(; positive=false).
Q, R = qr_compact(A; alg = Householder, positive = false);

# Form 4: Algorithm instance — used as-is; no additional kwargs are allowed.
Q, R = qr_compact(A; alg = Householder(; positive = false));

# Form 5: NamedTuple — equivalent to qr_compact(A; positive=false).
Q, R = qr_compact(A; alg = (; positive = false));
```

!!! note
When passing an already-constructed algorithm instance (form 4), additional keyword arguments at the call site are not permitted and will throw an `ArgumentError`.
All configuration must go into the constructor in that case.

```@docs; canonical=false
MatrixAlgebraKit.select_algorithm
```

## Discovering the Default Algorithm

To check which algorithm is used by default for a given function and input type, call [`MatrixAlgebraKit.default_algorithm`](@ref).
The available keyword arguments depend on the algorithm type; refer to the docstrings listed in [Available Algorithm Types](@ref) below.

```@docs; canonical=false
MatrixAlgebraKit.default_algorithm
```

## Configuring Algorithms

Each algorithm accepts keyword arguments that control its behavior.
These can be provided either at the call site (forms 1–3) or inside the algorithm constructor:

```julia
# The following four calls are all equivalent:
U, S, Vᴴ = svd_compact(A; fixgauge = false)
U, S, Vᴴ = svd_compact(A; alg = :SafeDivideAndConquer, fixgauge = false)
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer, fixgauge = false)
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer(; fixgauge = false))
```

## The `DefaultAlgorithm` Sentinel

Package developers who want to store an algorithm configuration without committing to a specific algorithm can use `DefaultAlgorithm`.
It defers algorithm selection to call time, forwarding its stored keyword arguments to [`MatrixAlgebraKit.select_algorithm`](@ref):

```julia
# Store configuration without picking a specific algorithm:
alg = DefaultAlgorithm(; positive = true)

# Equivalent to qr_compact(A; positive = true):
Q, R = qr_compact(A; alg)
```

```@docs; canonical=false
DefaultAlgorithm
```

## Available Algorithm Types

The following high-level algorithm types are available.
They all accept an optional `driver` keyword to select the computational backend; see [Driver Selection](@ref sec_driverselection) for details.

| Algorithm | Applicable decompositions | Key keyword arguments |
Copy link
Member

Choose a reason for hiding this comment

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

Was "Key keyword arguments" intentional 😄 .

|:----------|:--------------------------|:----------------------|
| [`Householder`](@ref) | QR, LQ | `positive`, `pivoted`, `blocksize` |
| [`DivideAndConquer`](@ref) | SVD, eigh | `fixgauge` |
| [`SafeDivideAndConquer`](@ref) | SVD, eigh | `fixgauge` |
| [`QRIteration`](@ref) | SVD, eigh, eig, Schur | `fixgauge`, `expert`, `permute`, `scale` |
| [`Bisection`](@ref) | eigh, SVD | `fixgauge` |
| [`Jacobi`](@ref) | eigh, SVD | `fixgauge` |
| [`RobustRepresentations`](@ref) | eigh | `fixgauge` |
| [`SVDViaPolar`](@ref) | SVD | `fixgauge`, `tol` |
| [`PolarViaSVD`](@ref) | polar | positional `svd_alg` argument |
| [`PolarNewton`](@ref) | polar | `maxiter`, `tol` |

For full docstring details on each algorithm type, see the corresponding section in [Decompositions](@ref).

## [Driver Selection](@id sec_driverselection)

!!! note "Expert use case"
Selecting a specific driver is an advanced feature intended for users who need to target a specific computational backend, such as a GPU.
For most use cases, the default driver selection is sufficient.

Each algorithm in MatrixAlgebraKit can optionally accept a `driver` keyword argument to explicitly select the computational backend.
By default, the driver is set to `DefaultDriver()`, which automatically selects the most appropriate backend based on the input matrix type.
The available drivers are:

```@autodocs; canonical=false
Modules = [MatrixAlgebraKit]
Filter = t -> t isa Type && t <: MatrixAlgebraKit.Driver
```

For example, to force LAPACK for a generic matrix type, or to use a GPU backend:
Copy link
Member

Choose a reason for hiding this comment

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

This does sound like we support forcing e.g. the GPU driver on CPU matrices, which could be something people want to do (thinking that the data will automatically be transferred to the GPU, GPU does the computation, and data comes back), but is not something we (currently) support afaik.

Copy link
Member Author

Choose a reason for hiding this comment

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

That might actually be a very reasonable feature to add though, (mostly in the other direction, e.g. for fallback definitions of missing decompositions on GPUs)


```julia
using MatrixAlgebraKit
using MatrixAlgebraKit: LAPACK, CUSOLVER # driver types are not exported by default

# Default: driver is selected automatically based on the input type
U, S, Vᴴ = svd_compact(A)
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer())

# Expert: explicitly select LAPACK
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer(; driver = LAPACK()))

# Expert: use a GPU backend (requires loading the appropriate extension)
U, S, Vᴴ = svd_compact(A; alg = QRIteration(; driver = CUSOLVER()))
```

Similarly, for QR decompositions:

```julia
using MatrixAlgebraKit: LAPACK # driver types are not exported by default

# Default: driver is selected automatically
Q, R = qr_compact(A)
Q, R = qr_compact(A; alg = Householder())

# Expert: explicitly select a driver
Q, R = qr_compact(A; alg = Householder(; driver = LAPACK()))
```
57 changes: 5 additions & 52 deletions docs/src/user_interface/decompositions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ f!(A, [F]; kwargs...) -> F...

Here, the input matrix is always the first argument, and optionally the output can be provided as well.
The keywords are algorithm-specific, and can be used to influence the behavior of the algorithms.
To check what algorithm is used by default for a given factorization `f` and input `A`, and by extension which keyword arguments it takes, you can call [`MatrixAlgebraKit.default_algorithm(f, A)`](@ref) and check the documentation of resulting algorithm type.
For a full description of how to select and configure algorithms, see [Algorithm Selection](@ref sec_algorithmselection).
Importantly, for generic code patterns it is recommended to always use the output `F` explicitly, since some implementations may not be able to reuse the provided memory.
Additionally, the `f!` method typically assumes that it is allowed to destroy the input `A`, and making use of the contents of `A` afterwards should be deemed as undefined behavior.

Expand All @@ -40,10 +40,11 @@ lq_full
lq_compact
```

The following algorithm is available for QR and LQ decompositions:
The following algorithms are available for QR and LQ decompositions:

```@docs; canonical=false
Householder
```@autodocs; canonical=false
Modules = [MatrixAlgebraKit]
Filter = t -> t isa Type && (t <: MatrixAlgebraKit.QRAlgorithms || t <: MatrixAlgebraKit.LQAlgorithms)
```

## Eigenvalue Decomposition
Expand Down Expand Up @@ -387,54 +388,6 @@ norm(A * N1') < 1e-14 && norm(A * N2') < 1e-14 &&
true
```

## [Driver Selection](@id sec_driverselection)

!!! note "Expert use case"
Selecting a specific driver is an advanced feature intended for users who need to target a specific computational backend, such as a GPU. For most use cases, the default driver selection is sufficient.

Each algorithm in MatrixAlgebraKit can optionally accept a `driver` keyword argument to explicitly select the computational backend.
By default, the driver is set to `DefaultDriver()`, which automatically selects the most appropriate backend based on the input matrix type.
The available drivers are:

```@docs; canonical=false
MatrixAlgebraKit.DefaultDriver
MatrixAlgebraKit.LAPACK
MatrixAlgebraKit.CUSOLVER
MatrixAlgebraKit.ROCSOLVER
MatrixAlgebraKit.GLA
MatrixAlgebraKit.Native
```

For example, to force LAPACK for a generic matrix type, or to use a GPU backend:

```julia
using MatrixAlgebraKit
using MatrixAlgebraKit: LAPACK, CUSOLVER # driver types are not exported by default

# Default: driver is selected automatically based on the input type
U, S, Vᴴ = svd_compact(A)
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer())

# Expert: explicitly select LAPACK
U, S, Vᴴ = svd_compact(A; alg = SafeDivideAndConquer(; driver = LAPACK()))

# Expert: use a GPU backend (requires loading the appropriate extension)
U, S, Vᴴ = svd_compact(A; alg = QRIteration(; driver = CUSOLVER()))
```

Similarly, for QR decompositions:

```julia
using MatrixAlgebraKit: LAPACK # driver types are not exported by default

# Default: driver is selected automatically
Q, R = qr_compact(A)
Q, R = qr_compact(A; alg = Householder())

# Expert: explicitly select a driver
Q, R = qr_compact(A; alg = Householder(; driver = LAPACK()))
```

## [Gauge choices](@id sec_gaugefix)

Both eigenvalue and singular value decompositions have residual gauge degrees of freedom even when the eigenvalues or singular values are unique.
Expand Down
Loading