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
11 changes: 9 additions & 2 deletions src/abstract.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,21 @@ LinearOperator{T}(
nctprod,
)

"""
LinearOperator{T, S}(nrow, ncol, symmetric, hermitian, prod!, tprod!, ctprod! = nothing) where {T, S}

Construct a linear operator with a specific temporary array storage type `S`, which should typically have element type `T`.

This is an inferrable variant of constructors that supply `S` as a keyword argument, and is recommended for performance-sensitive applications.
"""
LinearOperator{T, S}(
nrow::I,
ncol::I,
symmetric::Bool,
hermitian::Bool,
prod!,
tprod!,
ctprod!,
tprod! = nothing,
ctprod! = nothing,
Comment thread
timholy marked this conversation as resolved.
) where {T, S, I <: Integer} =
LinearOperator{T, S}(nrow, ncol, symmetric, hermitian, prod!, tprod!, ctprod!, 0, 0, 0)

Expand Down
71 changes: 31 additions & 40 deletions src/constructors.jl
Original file line number Diff line number Diff line change
@@ -1,57 +1,41 @@
# Constructors.
"""
LinearOperator(M::AbstractMatrix{T}; symmetric=false, hermitian=false, S = Vector{T}) where {T}
Construct a linear operator from a dense or sparse matrix.
Use the optional keyword arguments to indicate whether the operator
is symmetric and/or hermitian.
Change `S` to use LinearOperators on GPU.
LinearOperator(M::AbstractMatrix{T}; symmetric=defaultsymmetric(M), hermitian=defaulthermitian(M), S = Vector{T}) where {T}
Construct a linear operator from a dense or sparse matrix. Use the optional
keyword arguments to indicate whether the operator is symmetric and/or
hermitian. Change `S` to use LinearOperators on GPU.
Comment on lines +3 to +6

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

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

The documented default S = Vector{T} is imprecise. The actual default is S = storage_type(M) (line 17), which typically evaluates to Vector{T} for most matrices but can differ for specific types like Diagonal. For accuracy, the documentation should either state S = storage_type(M) or clarify that the default storage type is determined by storage_type(M).

Suggested change
LinearOperator(M::AbstractMatrix{T}; symmetric=defaultsymmetric(M), hermitian=defaulthermitian(M), S = Vector{T}) where {T}
Construct a linear operator from a dense or sparse matrix. Use the optional
keyword arguments to indicate whether the operator is symmetric and/or
hermitian. Change `S` to use LinearOperators on GPU.
LinearOperator(M::AbstractMatrix{T}; symmetric=defaultsymmetric(M), hermitian=defaulthermitian(M), S = storage_type(M)) where {T}
Construct a linear operator from a dense or sparse matrix. Use the optional
keyword arguments to indicate whether the operator is symmetric and/or
hermitian. The default storage type `S` is determined by `storage_type(M)`, which
typically evaluates to `Vector{T}` for standard matrix types. Change `S` to use
LinearOperators on GPU.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is inherited from previous code.

Comment thread
timholy marked this conversation as resolved.

!!! tip
In performance-sensitive applications, it may be advisable to use

LinearOperator{T, S}(M; kwargs...)

instead.
"""
function LinearOperator(
M::AbstractMatrix{T};
symmetric = false,
hermitian = false,
S = storage_type(M),
kwargs...,
) where {T}
return LinearOperator{T, S}(M; kwargs...)
end

function LinearOperator{T, S}(M::AbstractMatrix{T}; symmetric = defaultsymmetric(M), hermitian = defaulthermitian(M)) where {T, S}

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

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

This LinearOperator{T, S}(M::AbstractMatrix{T}; ...) constructor is missing documentation. Since the PR goal is to "Describe LinearOperator{T, S}(args...) in docs" and this is the main matrix constructor that implements the symmetric and hermitian defaults, it should have its own docstring explaining its purpose as the type-inferrable variant and documenting its parameters.

Copilot uses AI. Check for mistakes.
nrow, ncol = size(M)
prod! = @closure (res, v, α, β) -> mul!(res, M, v, α, β)
tprod! = @closure (res, u, α, β) -> mul!(res, transpose(M), u, α, β)
ctprod! = @closure (res, w, α, β) -> mul!(res, adjoint(M), w, α, β)
LinearOperator{T, S}(nrow, ncol, symmetric, hermitian, prod!, tprod!, ctprod!)
end

"""
LinearOperator(M::Symmetric{T}, S = Vector{T}) where {T <: Real} =

Construct a linear operator from a symmetric real square matrix `M`.
Change `S` to use LinearOperators on GPU.
"""
LinearOperator(M::Symmetric{T}, S = Vector{T}) where {T <: Real} =
LinearOperator(M, symmetric = true, hermitian = true, S = S)

"""
LinearOperator(M::SymTridiagonal{T}, S = Vector{T}) where {T}

Constructs a linear operator from a symmetric tridiagonal matrix. If
its elements are real, it is also Hermitian, otherwise complex
symmetric.
Change `S` to use LinearOperators on GPU.
"""
function LinearOperator(M::SymTridiagonal{T}, S = Vector{T}) where {T}
hermitian = eltype(M) <: Real
LinearOperator(M, symmetric = true, hermitian = hermitian, S = S)
end

"""
LinearOperator(M::Hermitian{T}, S = Vector{T}) where {T}

Constructs a linear operator from a Hermitian matrix. If
its elements are real, it is also symmetric.
Change `S` to use LinearOperators on GPU.
"""
function LinearOperator(M::Hermitian{T}, S = Vector{T}) where {T}
symmetric = eltype(M) <: Real
LinearOperator(M, symmetric = symmetric, hermitian = true, S = S)
end
defaultsymmetric(M) = false
defaulthermitian(M) = false
defaultsymmetric(M::Symmetric) = true

Copilot AI Feb 22, 2026

Copy link

Choose a reason for hiding this comment

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

This refactoring introduces a subtle behavioral change for complex Symmetric matrices. Previously, only real Symmetric matrices matched a specialized constructor (setting symmetric=true, hermitian=true), while complex Symmetric matrices fell back to the default constructor with symmetric=false, hermitian=false. Now, the defaultsymmetric(M::Symmetric) = true method applies to all Symmetric matrices, including complex ones. This is mathematically correct (a symmetric matrix is symmetric regardless of element type), but it is a breaking change that should be documented in release notes.

Suggested change
defaultsymmetric(M::Symmetric) = true
defaultsymmetric(M::Symmetric{<:Real}) = true

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it would be fair to call this a bugfix, not a breaking change.

defaulthermitian(M::Symmetric{<:Real}) = true
defaultsymmetric(M::Hermitian) = eltype(M) <: Real
defaulthermitian(M::Hermitian) = true
defaultsymmetric(M::SymTridiagonal) = true
defaulthermitian(M::SymTridiagonal{<:Real}) = true

"""
LinearOperator(type::Type{T}, nrow, ncol, symmetric, hermitian, prod!,
Expand Down Expand Up @@ -104,6 +88,13 @@ op = LinearOperator(Float64, 2, 2, false, false,
```

The 3-args `mul!` also works when applying the operator on a matrix.

!!! tip
In performance-sensitive applications, it may be advisable to use

LinearOperator{T, S}(nrow, ncol, symmetric, hermitian, prod!, tprod! = nothing, ctprod! = nothing)

instead.
"""
function LinearOperator(
::Type{T},
Expand Down
Loading