Closed andreasvarga closed 9 months ago
I fixed this issue by replacing mul!
with LinearMaps._unsafe_mul!
in the above definitions of methods for transposes. I wonder if this is the only way to fix this issue.
Sorry for my lack of response. That might be the right fix. For most cases, there's a fallback for _unsafe_mul!
that calls mul!
, but when the map is wrapped in a TransposeMap
, that doesn't seem to work. Perhaps we should state more clearly that it is actually _unsafe_mul!
that should be overloaded. In reward, one doesn't have to do size checking yourself.
I realized that we actually do recommend to implement LinearMaps._unsafe_mul!
for custom map types in our documentation: https://julialinearalgebra.github.io/LinearMaps.jl/stable/generated/custom/. Given those, the usual LinearAlgebra.mul!
interface just works.
As for the action of those operators, can't you make them in-place functions? Like
_unsafe_mul!(y::AbstractVector, A::matelimop, x::AbstractVector) =
triu2vec!(y, x)
etc.? You would then avoid intermediate allocations and extra copyto!
s.
The actual implementation (with changed operator name) is:
function LinearMaps._unsafe_mul!(y::AbstractVector, A::duplicationop, x::AbstractVector)
# y[:] = vec2triu(x, her = true)
n = A.dim
Y = reshape(y,n,n)
@inbounds begin
k = 1
for j = 1:n
for i = 1:j
Y[i,j] = x[k]
k += 1
end
for i = j+1:n
Y[i,j] = conj(Y[j,i])
end
end
end
end
I think this uses the least memory allocation.
In your case, the reshape is relevant only for the index reassignment. You could avoid it by doing the index manipulation manually:
function LinearMaps._unsafe_mul!(y::AbstractVector, A::duplicationop, x::AbstractVector)
# y[:] = vec2triu(x, her = true)
n = A.dim
@inbounds begin
k = 1
for j = 1:n
for i = 1:j
y[(i-1)*n + j] = x[k]
k += 1
end
for i = j+1:n
y[(i-1)*n + j] = conj(y[(j-1)*n + i])
end
end
end
return y
end
Perhaps it's worth benchmarking.
julia> n = 100;
julia> Dl = duplicationop(n);
julia> x = rand(ComplexF64,Int((n*(n+1)/2))); y = zeros(ComplexF64,n*n);
julia> @time LinearMaps._unsafe_mul!(y, Dl, x);
0.000022 seconds
Perfect!
I did similarly for the adjoint/transpose:
function LinearMaps._unsafe_mul!(x::AbstractVector, AT::LinearMaps.TransposeMap{T,<:duplicationop{T}}, y::AbstractVector) where {T}
n = AT.lmap.dim
#Y = reshape(y, n, n)
LinearMaps.check_dim_mul(x, AT, y)
# x[:] = triu2vec(Y+transpose(Y)-Diagonal(Y))
@inbounds begin
k = 1
for j = 1:n
for i = 1:j
#x[k] = i == j ? Y[j,j] : Y[i,j] + Y[j,i]
x[k] = i == j ? y[(j-1)*n + j] : y[(i-1)*n + j] + y[(j-1)*n + i]
k += 1
end
end
end
end
julia> n = 100;
julia> x = rand(ComplexF64,Int((n*(n+1)/2))); y = zeros(ComplexF64,n*n);
julia> Dl = duplicationop(n);
julia> @time LinearMaps._unsafe_mul!(x, transpose(Dl), y);
0.000024 seconds (1 allocation: 16 bytes)
You can also skip the check_dim_mul
. That is already taken care of by the generic mul!
implementation in LinearMaps.jl. Just have the naked code (+ potential require_one_based_indexing
if necessary).
Thanks for the hint. I implemented similarly the operations for the elimination operator.
Actually, I don't use these operators in MatrixEquations
, but they are very useful for testing purposes. Using them, I was able to fix the transpose/adjoint issues for half-vectorization based Lyapunov operators for real data. The complex case is still not fixed and I wonder if operations like complex conjugation are linear (I guess not), which could be the cause of my problems. I needed to fix the transpose issues, because I started to add several iterative solvers based on the conjugate gradient method and the operations with adjoint/transpose were not correctly implemented for half-vectorization based approaches. At a certain moment. before issuing a new minor release, I would be very pleased if you could have a look on these new developments.
One pleasant issue I would like to mention is that the defined operators works also for sparse matrices and can be also used with the two alternative solvers lsqr
and lsmr
available in the IterativeSolvers
package.
I am a little bit excited using the iterative methods, being able to solve now arbitrary linear matrix equations of the very general form ∑ A_i*X*B_i + ∑ C_j*transpose(X)*D_j = E
, where any of matrices can be equal toI
. I wonder if it would be possible to extend the definitions of some operators to also cover the case. when the above A_i, B_i, ...
are linear maps.
If you consider appropriate, you can close this issue.
I wonder if it would be possible to extend the definitions of some operators to also cover the case. when the above
A_i, B_i, ...
are linear maps.
It depends on which operations you use to create new operators. If those operations produce a new LinearMap
, then that should just work with IterativeSolvers.jl
, unless they require AbstractMatrix
.
I recently implemented two operators
Dl
andDe
, known in the literature as elimination operator and duplication operator, respectively. The matrices of these operators for 2-by-2 matrices can be constructed as follows:I also explicitly implemented the adjunct operators for which, as expected, I obtained
For the composed map
Dl*De
I obtainbut for its adjoint, explicitly computed, the generation of the matrix fails, with a strange error message:
I wonder if the error is mine or perhaps is a problem of
LinearMaps
.The code to define the above operators is given below: