Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two performance problems with the Q in QR factorizations #800

Closed
olof3 opened this issue Dec 23, 2020 · 3 comments · Fixed by JuliaLang/julia#44615
Closed

Two performance problems with the Q in QR factorizations #800

olof3 opened this issue Dec 23, 2020 · 3 comments · Fixed by JuliaLang/julia#44615
Labels
performance Must go faster

Comments

@olof3
Copy link
Contributor

olof3 commented Dec 23, 2020

n = 300
B = randn(n, n)
D = Diagonal(ones(n))
F = qr(randn(n, 1))
@time F.Q * B # Works as expected
@time F.Q * D # Slow
@time F.Q * I # Slow
@time F.Q[:, :] # Slow

gives

  0.000505 seconds (4 allocations: 705.734 KiB)
  0.251926 seconds (270.00 k allocations: 448.380 MiB, 10.12% gc time)
  0.246129 seconds (270.00 k allocations: 448.380 MiB, 7.77% gc time)
  0.239383 seconds (270.01 k allocations: 448.380 MiB, 8.09% gc time)

There seems to be two problems

  1. Multiplying various matrix types with QRCompactQ.
    The same issue was reported for multiplication with UniformScaling in Unexpected performance loss #758.
    I guess the solution is to define the proper methods. Perhaps this would also fix Missing element promotion in I(n)*F.Q' #801.
  2. getindex for QRCompactQ
    This problem could be solved by defining the getindex method properly.
    A bonus would be that one could use F.Q[:,:] to retrieve the full Q, rather than the cumbersome F.Q*Matrix(I,m,m) as suggested in the docstring.

For problem 2., the following method seems to work for me, but I guess it needs further consideration and testing.

function getindex(Q::Union{AbstractQ,Adjoint{<:Any,<:AbstractQ}}, ind1, ind2)
    if ind2 isa Colon
        ind2 = 1:size(Q,2)
    end

    X = zeros(eltype(Q), size(Q,1), length(ind2))
    for (j,i) in enumerate(ind2)
        X[i,j] = 1
    end

    lmul!(Q, X)
    
    if ind2 isa Integer
        return X[ind1, 1]
    else
        return X[ind1, :]
    end
end

Perhaps it would also be good to have

getindex(Q::Union{AbstractQ,Adjoint{<:Any,<:AbstractQ}}, ::Colon, ::Colon) = lmul!(Q, Matrix{eltype(Q)}(I, size(Q)))

If the above methods are introduced it seems like the existing method,

function getindex(Q::AbstractQ, i::Integer, j::Integer)
    x = zeros(eltype(Q), size(Q, 1))
    x[i] = 1
    y = zeros(eltype(Q), size(Q, 2))
    y[j] = 1
    return dot(x, lmul!(Q, y))
end

could be removed.

@dkarrasch
Copy link
Member

For multiplication, one can perhaps start from the strided matrix version:

# this needs to be special-cased for all specific types, to avoid method ambiguities :-(
(*)(A::AbstractQ, B::SpecialMatrix) = _qlmul(A, B)

function _qlmul(A::AbstractQ, B)
    TAB = promote_type(eltype(A), eltype(B))
    Anew = convert(AbstractMatrix{TAB}, A)
    if size(A.factors, 1) == size(B, 1)
        Bnew = Matrix{TAB}(B)
    elseif size(A.factors, 2) == size(B, 1)
        Bnew = [Matrix{TAB}(B); zeros(TAB, size(A.factors, 1) - size(B,1), size(B, 2))]
    else
        throw(DimensionMismatch("first dimension of matrix must have size either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
    end
    lmul!(Anew, Bnew)
end

The competing method is, for instance, (*)(A::AbstractMatrix, D::Diagonal), so just having (*)(A::AbstractQ, B::AbstractMatrix) will not work out of the box. Furthermore, we'll need the same methods for multiplying with Q from the right.

ranocha referenced this issue in ranocha/julia Feb 5, 2021
This fixes some performance problems reported in #38972 and #37102
(multiplying `Q` by a `Diagonal` or `UniformScaling`).
ranocha referenced this issue in ranocha/julia Feb 5, 2021
This fixes some performance problems reported in #38972 and #37102
(multiplying `Q` by a `Diagonal` or `UniformScaling`).
ranocha referenced this issue in ranocha/julia Feb 5, 2021
This fixes some performance problems reported in #38972 and #37102
(multiplying `Q` by a `Diagonal` or a `UniformScaling`). This improves
the performance of generating random orthogonal matrices as described
in https://discourse.julialang.org/t/random-orthogonal-matrices/9779/7
significantly.
ranocha referenced this issue in ranocha/julia Feb 5, 2021
This fixes two performance bugs reported in https://github.com/JuliaLang/julia/issues/38972
and https://github.com/JuliaLang/julia/issues/38972 (multiplication of `Q` from `qr` by a
`Diagonal` or `UniformScaling`). In particular, it improves the performance of generating
random orthogonal matrices as described in https://github.com/JuliaLang/julia/issues/38972.
ranocha referenced this issue in ranocha/julia Feb 5, 2021
This fixes two performance bugs reported in https://github.com/JuliaLang/julia/issues/38972
and https://github.com/JuliaLang/julia/issues/38972 (multiplication of `Q` from `qr` by a
`Diagonal` or `UniformScaling`). In particular, it improves the performance of generating
random orthogonal matrices as described in https://discourse.julialang.org/t/random-orthogonal-matrices/9779/7.
oscardssmith referenced this issue in JuliaLang/julia Feb 23, 2021
* specialize copyto! and multiplication by numbers for Q from qr

This fixes two performance bugs reported in https://github.com/JuliaLang/julia/issues/38972
and https://github.com/JuliaLang/julia/issues/38972 (multiplication of `Q` from `qr` by a
`Diagonal` or `UniformScaling`). In particular, it improves the performance of generating
random orthogonal matrices as described in https://discourse.julialang.org/t/random-orthogonal-matrices/9779/7.

* fix typo in new qr tests

* resolve mehod ambiguity of copyto!
ElOceanografo referenced this issue in ElOceanografo/julia May 4, 2021
…Lang#39533)

* specialize copyto! and multiplication by numbers for Q from qr

This fixes two performance bugs reported in https://github.com/JuliaLang/julia/issues/38972
and https://github.com/JuliaLang/julia/issues/38972 (multiplication of `Q` from `qr` by a
`Diagonal` or `UniformScaling`). In particular, it improves the performance of generating
random orthogonal matrices as described in https://discourse.julialang.org/t/random-orthogonal-matrices/9779/7.

* fix typo in new qr tests

* resolve mehod ambiguity of copyto!
antoine-levitt referenced this issue in antoine-levitt/julia May 9, 2021
…Lang#39533)

* specialize copyto! and multiplication by numbers for Q from qr

This fixes two performance bugs reported in https://github.com/JuliaLang/julia/issues/38972
and https://github.com/JuliaLang/julia/issues/38972 (multiplication of `Q` from `qr` by a
`Diagonal` or `UniformScaling`). In particular, it improves the performance of generating
random orthogonal matrices as described in https://discourse.julialang.org/t/random-orthogonal-matrices/9779/7.

* fix typo in new qr tests

* resolve mehod ambiguity of copyto!
@ViralBShah ViralBShah added the performance Must go faster label Feb 25, 2022
@ViralBShah
Copy link
Member

ViralBShah commented Mar 13, 2022

Only F.Q * D apears to be slow now, but it now takes 36 secs for me!

@dkarrasch
Copy link
Member

Uff, that may be the reason for the significantly longer runtimes of the linalg tests. I'll take care of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Must go faster
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants