diff --git a/docs/src/literate/howtos/gpu.jl b/docs/src/literate/howtos/gpu.jl index 3d153fc..27830cb 100644 --- a/docs/src/literate/howtos/gpu.jl +++ b/docs/src/literate/howtos/gpu.jl @@ -20,7 +20,11 @@ LinearOperators.storage_type(fop) == typeof(storage) # ## Moving an Operator to Another Backend # Operators that support [Adapt.jl](https://github.com/JuliaGPU/Adapt.jl) can be moved to another array backend with `Adapt.adapt(target, op)`. -# Which backends are supported depends on the operator and the target, and not every operator in LinearOperatorCollection implements Adapt. +# Which backends are supported depends on the operator and the target. The following operators implement Adapt: +# * `FFTOp`, `NFFTOp` (including its Toeplitz normal operator) and `RadonOp` +# * `GradientOp`, `SamplingOp` and `WeightingOp` +# * `DiagOp`, `ProdOp`, `RowBlockOp` and `NormalOp`, and the normal operators of `DiagOp`, `ProdOp` and `RowBlockOp`. These are built from other operators and adapt them, so the target must also be supported by each of those operators. +# Other operators, such as `WaveletOp`, `DCTOp` and `DSTOp`, do not implement Adapt. # The target is the backing array type without element-type parameters, such as `Array`, `CuArray` or `ROCArray`. # LinearOperatorCollection does not support element-typed targets such as `Array{ComplexF64}` or `CuArray{Float32}`, even when the element type matches the operator. diff --git a/ext/LinearOperatorFFTWExt/FFTOp.jl b/ext/LinearOperatorFFTWExt/FFTOp.jl index be47e7c..7b20aca 100644 --- a/ext/LinearOperatorFFTWExt/FFTOp.jl +++ b/ext/LinearOperatorFFTWExt/FFTOp.jl @@ -43,7 +43,8 @@ returns an operator which performs an FFT on Arrays of type T `Adapt.adapt(target, op)` preserves the shape, shift, and normalization convention, planner options, and rebuilds the plans, workspaces, and closures for `target`. If `cache=false`, adapting the operator raises an explicit -`ArgumentError` because the planner options were not retained. +`ArgumentError` because the planner options were not retained. See +[Moving an Operator to Another Backend](@ref) for the supported targets. """ function LinearOperatorCollection.FFTOp(T::Type; shape::NTuple{D,Int64}, shift::Bool=true, unitary::Bool=true, S = Vector{Complex{real(T)}}, cache::Bool=true, kwargs...) where D diff --git a/ext/LinearOperatorNFFTExt/NFFTOp.jl b/ext/LinearOperatorNFFTExt/NFFTOp.jl index 838a695..e05239b 100644 --- a/ext/LinearOperatorNFFTExt/NFFTOp.jl +++ b/ext/LinearOperatorNFFTExt/NFFTOp.jl @@ -21,7 +21,8 @@ generates a `NFFTOpImpl` which evaluates the MRI Fourier signal encoding operato settings, and `toeplitz`; it replays the cached constructor arguments with target-backed nodes and rebuilds plans, workspaces, and closures for the target. If `cache=false`, adapting the operator raises an explicit -`ArgumentError` because the constructor arguments were not retained. +`ArgumentError` because the constructor arguments were not retained. See +[Moving an Operator to Another Backend](@ref) for the supported targets. """ function LinearOperatorCollection.NFFTOp(::Type{T}; shape::Tuple, nodes::AbstractMatrix{U}, toeplitz=false, oversamplingFactor=1.25, @@ -195,7 +196,8 @@ Construct a Toeplitz normal operator for an NFFT. `Adapt.adapt(target, op)` keeps the shape, weights, Toeplitz kernel `λ`, and FFT planner options, and rebuilds the FFT plans, workspaces, and closures on the target. Set `cache=false` when constructing the operator to opt out of retaining planner -kwargs; adapting such an operator raises an explicit `ArgumentError`. +kwargs; adapting such an operator raises an explicit `ArgumentError`. See +[Moving an Operator to Another Backend](@ref) for the supported targets. """ function LinearOperatorCollection.NFFTToeplitzNormalOp(shape, W, fftplan, ifftplan, λ, xL1::matT, xL2::matT, diff --git a/ext/LinearOperatorRadonKAExt/RadonOp.jl b/ext/LinearOperatorRadonKAExt/RadonOp.jl index 399b74b..ff40772 100644 --- a/ext/LinearOperatorRadonKAExt/RadonOp.jl +++ b/ext/LinearOperatorRadonKAExt/RadonOp.jl @@ -12,7 +12,8 @@ Generates a `RadonOp` which evaluates the Radon transform operator and its adjoi * `S` - storage type for internal vectors (default: `Vector{T}`) `Adapt.adapt(target, op)` rebuilds the operator for `target` with the same shape and -geometry; array `angles` and `μ` are adapted to `target`, ranges and scalars are kept. +geometry; array `angles` and `μ` are adapted to `target`, ranges and scalars are kept. See +[Moving an Operator to Another Backend](@ref) for the supported targets. """ function LinearOperatorCollection.RadonOp(::Type{T}; shape::NTuple{N, Int}, angles, geometry = RadonParallelCircle(shape[1], -(shape[1]-1)÷2:(shape[1]-1)÷2), μ = nothing, S = Vector{T}) where {T, N} diff --git a/src/GradientOp.jl b/src/GradientOp.jl index 2010c36..6b4d34a 100644 --- a/src/GradientOp.jl +++ b/src/GradientOp.jl @@ -15,20 +15,14 @@ directional gradient operator along the dimensions `dims` for an array of size ` # Moving to another array backend - Adapt.adapt(CuArray, op) - -returns a new gradient operator for the target backend, and `copy(op)` returns a new one with -the same storage type. The target is the backing array type without element-type parameters -(e.g. `Array`, `JLArray`, `CuArray` or `ROCArray`). Unlike `S`, which takes a parameterized type -such as `CuArray{T, 1}`, an element-typed target such as `Array{Float64}` is not supported, even -when its element type matches `op`. Adapting does not change the precision: choose `T` at -construction. Both rebuild the operator and its temporary storage from `shape` and the -directions taken from `dims` at construction; a later change of the vector passed as `dims`, or -an iterator consumed by the construction, has no effect. `op` itself is not modified. The -directions stay on the host. An element-typed target whose element type differs from `op`, such -as `CuArray{Float32}` for a `Float64` operator, throws an `ArgumentError`. An operator built with -an `S` of another element type cannot be adapted to any target and throws an `ArgumentError`; -`copy` keeps it as it is. +`Adapt.adapt(target, op)`, and `copy(op)` with the same storage type, rebuild the operator and +its temporary storage from `shape` and the directions taken from `dims` at construction; a later +change of the vector passed as `dims`, or an iterator consumed by the construction, has no +effect. `op` itself is not modified. The directions stay on the host. A target whose storage +element type differs from `op`, such as `CuArray{Float32}` for a `Float64` operator, throws an +`ArgumentError`. An operator built with an `S` of another element type cannot be adapted to any +target and throws an `ArgumentError`; `copy` keeps it as it is. See +[Moving an Operator to Another Backend](@ref) for the supported targets. """ function GradientOp(::Type{T}; shape::NTuple{N,Int}, dims=1:length(shape), kwargs...) where {T <: Number, N} return GradientOpImpl(T, shape, dims; kwargs...) diff --git a/src/RowBlockOp.jl b/src/RowBlockOp.jl index ef3d802..b23d4c3 100644 --- a/src/RowBlockOp.jl +++ b/src/RowBlockOp.jl @@ -38,8 +38,9 @@ of the blocks applied to their rows. The products of the blocks run in parallel the adjoint sums the blocks one after the other. Blocks with contiguous rows (`UnitRange`s) work on views of the data; other rows are gathered into and scattered -from a buffer per block (with index vectors on the device of the blocks, see `Adapt.adapt`). Reordering the data -such that every block is contiguous avoids these copies. +from a buffer per block (with index vectors on the device of the blocks, see +[Moving an Operator to Another Backend](@ref)). Reordering the data such that every block is contiguous avoids these +copies. The third form splits the rows of a matrix into `nblocks` blocks, as copies of the rows: `:interleaved` takes every `nblocks`-th row (`k:nblocks:m`), `:contiguous` consecutive rows, `:random` a random partition drawn with `rng`. diff --git a/src/SamplingOp.jl b/src/SamplingOp.jl index d48419a..aa848e4 100644 --- a/src/SamplingOp.jl +++ b/src/SamplingOp.jl @@ -13,9 +13,10 @@ Two modes are supported: * Boolean mask without `shape`: multiplies the input elementwise with a copy of `pattern` stored on the backend of `S`. -`Adapt.adapt(to, op)` rebuilds the operator for the storage type `to` from its +`Adapt.adapt(to, op)` rebuilds the operator for the target `to` from its effective pattern. Integer indices are copied and stay host arrays; the Boolean -mask is copied to the target backend. +mask is copied to the target backend. See +[Moving an Operator to Another Backend](@ref) for the supported targets. # Arguments * `pattern` - integer indices or Boolean mask