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

nonsquare (underdetermined) LQ solves #34350

Merged
merged 6 commits into from
Jan 17, 2020
Merged

Conversation

stevengj
Copy link
Member

@stevengj stevengj commented Jan 11, 2020

Fixes JuliaLang/LinearAlgebra.jl#689.

Seems to be about 2x faster than pivoted QR for a large underdetermined solve, with similar accuracy (assuming full row rank):

julia> A = rand(1000,1500); b = rand(size(A,1));

julia> @btime lq($A) \ $b;
  101.955 ms (14 allocations: 19.37 MiB)

julia> @btime qr($A, Val{true}()) \ $b;
  250.584 ms (8046 allocations: 31.98 MiB)

julia> x = lq(A) \ b; norm(x - qr(A, Val{true}()) \ b) / norm(x)
3.5357944054772334e-15

@ViralBShah ViralBShah added the linear algebra Linear algebra label Jan 11, 2020
Copy link
Member

@dkarrasch dkarrasch left a comment

Choose a reason for hiding this comment

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

One of us is getting over- and under-determined wrong, perhaps worth a second thought. The other issue I couldn't see resolved is ldiv!, which promises solve in-place of B, but must necessarily widen/stretch B to fit the longer X in A*X = B. It's not easy to track the changes in the test, but I couldn't find tests for ldiv!.

stdlib/LinearAlgebra/src/lq.jl Outdated Show resolved Hide resolved
stdlib/LinearAlgebra/src/lq.jl Outdated Show resolved Hide resolved
stdlib/LinearAlgebra/src/lq.jl Show resolved Hide resolved
@stevengj
Copy link
Member Author

stevengj commented Jan 12, 2020

For non-square problems with more cols n than rows m, ldiv! only looks at the first m rows of its input (which should have n rows to store the output solution)

(As noted above, ldiv! is exercised by the tests because it is actually called by \.)

You’re right that I was mixing up over/under determined some of the time — it's fixed now.

@stevengj
Copy link
Member Author

win32 CI failure seems to be an unrelated Error in testset SharedArrays

@stevengj
Copy link
Member Author

win32 failure is now another unrelated glitch cp: cannot stat 'dist-extras/7z.*': No such file or directory.

Should be good to merge?

@andreasnoack andreasnoack merged commit 40569c2 into master Jan 17, 2020
@andreasnoack andreasnoack deleted the sgj/lq_underdetermined branch January 17, 2020 07:40
KristofferC pushed a commit that referenced this pull request Apr 11, 2020
* nonsquare (underdetermined) LQ solves

* fix NEWS item

* fix NEWS item

* narrow method signature to eliminate ambiguity

* fix docs -- under/overdetermined terms were swapped

* Update stdlib/LinearAlgebra/test/lq.jl

Co-Authored-By: Stefan Karpinski <[email protected]>

Co-authored-by: Stefan Karpinski <[email protected]>
@stevengj
Copy link
Member Author

stevengj commented Nov 8, 2024

I wonder if we should change the default A \ b polyalgorithm to use LQ for undetermined problems ("wide" A)? It still seems to be twice as fast on Julia 1.11.

@dkarrasch
Copy link
Member

Currently, we only distinguish square from non-square, right? Sounds like a good idea.

@stevengj
Copy link
Member Author

stevengj commented Nov 8, 2024

Currently, we don't have row-pivoted lq, so using lq(A) \ b as-is might be problematic for badly conditioned "wide" A (i.e. nearly dependent rows).

However, we could equivalently use pivoted QR on $A^T$ via qr(A', ColumnNorm())' \ b. That seems to be the fastest option of all, actually:

julia> A = rand(100, 1000); b = rand(100);

julia> @btime $A \ $b; @btime qr($A, ColumnNorm()) \ $b; @btime lq($A) \ $b; @btime qr($A', ColumnNorm())' \ $b;
  6.802 ms (42 allocations: 1.89 MiB)
  6.851 ms (42 allocations: 1.89 MiB)
  3.847 ms (21 allocations: 926.41 KiB)
  2.454 ms (29 allocations: 1008.34 KiB)

julia> A \ b  qr(A, ColumnNorm()) \ b  lq(A) \ b  qr(A', ColumnNorm())' \ b  # same answer
true

Technically, the fastest option of all (by a factor of 10!) is to use $A^T (A A^T)^{-1} b$ via Cholesky, but this is potentially inaccurate because it squares the condition number:

julia> @btime (cholesky($A*$A') \ $b);
  287.375 μs (8 allocations: 157.31 KiB)

julia> A \ b  A' * (cholesky(A*A') \ b)
true

(which is also why we don't use the analogous Cholesky-based algorithm in the "tall" overdetermined case).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
linear algebra Linear algebra
Projects
None yet
Development

Successfully merging this pull request may close these issues.

lq(A) \ b unnecessarily requires square A
5 participants