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

Fix (l/r)mul! with Diagonal/Bidiagonal #55052

Merged
merged 8 commits into from
Jul 11, 2024
Merged

Fix (l/r)mul! with Diagonal/Bidiagonal #55052

merged 8 commits into from
Jul 11, 2024

Conversation

jishnub
Copy link
Contributor

@jishnub jishnub commented Jul 6, 2024

Currently, rmul!(A::AbstractMatirx, D::Diagonal) calls mul!(A, A, D), but this isn't a valid call, as mul! assumes no aliasing between the destination and the matrices to be multiplied. As a consequence,

julia> B = Bidiagonal(rand(4), rand(3), :L)
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.476892                      
 0.353756  0.139188             
          0.685839  0.309336    
                   0.369038  0.304273

julia> D = Diagonal(rand(size(B,2)));

julia> rmul!(B, D)
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.0            
 0.0  0.0        
     0.0  0.0    
         0.0  0.0

julia> B
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.0            
 0.0  0.0        
     0.0  0.0    
         0.0  0.0

This is clearly nonsense, and happens because the internal _mul! function assumes that it can safely overwrite the destination with zeros before carrying out the multiplication. This is fixed in this PR by using broadcasting instead. The current implementation is generally equally performant, albeit occasionally with a minor allocation arising from reshapeing an Array.

A similar problem also exists in l/rmul! with Bidiaognal, but that's a little harder to fix while remaining equally performant.

@jishnub jishnub added linear algebra Linear algebra arrays [a, r, r, a, y, s] backport 1.10 Change should be backported to the 1.10 release backport 1.11 Change should be backported to release-1.11 labels Jul 6, 2024
@dkarrasch
Copy link
Member

This is going back to where we came from, so it would be good to double check what the original concern was to make it call mul!(A, A, D). I guess this is a problem only with products of structured/banded matrices? One issue that I remember is that permuteddims allocates, and we didn't like that, so we wrote out the loops. Just to check: is there another option to fix this at the _mul! level?

@jishnub
Copy link
Contributor Author

jishnub commented Jul 6, 2024

The problem is that mul! is documented to assume no aliasing, so a call like mul!(A, A, D) will invariably lead to problems downstream. We may fix this for the methods in LinearAlgebra, but libraries that define more specific methods may still encounter errors.

One option might be to write out the loop here instead of broadcasting. My idea was that array types that specialise broadcasting would be able to access optimised methods.

@jishnub jishnub added bugfix This change fixes an existing bug and removed arrays [a, r, r, a, y, s] labels Jul 7, 2024
@dkarrasch
Copy link
Member

I found it: #42321.

@dkarrasch
Copy link
Member

This goes over the entire matrix and misses all the optimizations that we have in _mul! for the banded matrices. I guess that was (one of) the reason(s) to initially make [l/r]mul! run by mul!.

@jishnub
Copy link
Contributor Author

jishnub commented Jul 7, 2024

Yes, we would need to specialize lmul! and rmul! for banded matrices. The fallback would probably need to go over the entire matrix, although we could introduce a bandwidth function to limit the loop bounds. I don't think that we can reliably call mul! here, although we may reuse some of the code from the specialized methods.

@jishnub jishnub marked this pull request as draft July 7, 2024 14:36
@jishnub jishnub changed the title Fix (l/r)mul! with Diagonal Fix (l/r)mul! with Diagonal/Bidiagonal Jul 7, 2024
@jishnub jishnub marked this pull request as ready for review July 8, 2024 16:44
@jishnub jishnub merged commit 262b40a into master Jul 11, 2024
7 checks passed
@jishnub jishnub deleted the jishnub/diag_lrmul branch July 11, 2024 06:03
@KristofferC KristofferC mentioned this pull request Jul 12, 2024
42 tasks
KristofferC pushed a commit that referenced this pull request Jul 23, 2024
Currently, `rmul!(A::AbstractMatirx, D::Diagonal)` calls `mul!(A, A,
D)`, but this isn't a valid call, as `mul!` assumes no aliasing between
the destination and the matrices to be multiplied. As a consequence,
```julia
julia> B = Bidiagonal(rand(4), rand(3), :L)
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.476892   ⋅         ⋅         ⋅
 0.353756  0.139188   ⋅         ⋅
  ⋅        0.685839  0.309336   ⋅
  ⋅         ⋅        0.369038  0.304273

julia> D = Diagonal(rand(size(B,2)));

julia> rmul!(B, D)
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.0   ⋅    ⋅    ⋅
 0.0  0.0   ⋅    ⋅
  ⋅   0.0  0.0   ⋅
  ⋅    ⋅   0.0  0.0

julia> B
4×4 Bidiagonal{Float64, Vector{Float64}}:
 0.0   ⋅    ⋅    ⋅
 0.0  0.0   ⋅    ⋅
  ⋅   0.0  0.0   ⋅
  ⋅    ⋅   0.0  0.0
```
This is clearly nonsense, and happens because the internal `_mul!`
function assumes that it can safely overwrite the destination with zeros
before carrying out the multiplication. This is fixed in this PR by
using broadcasting instead. The current implementation is generally
equally performant, albeit occasionally with a minor allocation arising
from `reshape`ing an `Array`.

A similar problem also exists in `l/rmul!` with `Bidiaognal`, but that's
a little harder to fix while remaining equally performant.

(cherry picked from commit 262b40a)
@KristofferC
Copy link
Sponsor Member

This seems to fail on CI on 1.11:

Error During Test at /cache/build/tester-amdci5-15/julialang/julia-release-1-dot-11/julia-f4b43765c5/share/julia/stdlib/v1.11/LinearAlgebra/test/tridiag.jl:846
--
  | Test threw exception
  | Expression: rmul!(copy(T), D) ≈ T * D
  | MethodError: no method matching zero(::Type{Main.OffsetArrays.OffsetMatrix{Float64, Matrix{Float64}}})
  | The function `zero` exists, but no method is defined for this combination of argument types.
  |  
  | Closest candidates are:
  | zero(!Matched::Type{Union{}}, Any...)
  | @ Base number.jl:310
  | zero(!Matched::Type{Missing})
  | @ Base missing.jl:104
  | zero(!Matched::Type{Main.Test26Main_LinearAlgebra_structuredbroadcast.TestStructuredBroadcast.Zero36193})
  | @ Main.Test26Main_LinearAlgebra_structuredbroadcast /cache/build/tester-amdci5-15/julialang/julia-release-1-dot-11/julia-f4b43765c5/share/julia/stdlib/v1.11/LinearAlgebra/test/structuredbroadcast.jl:255
  | ...
  |  
  | Stacktrace:
  | [1] getindex
  | @ /cache/build/tester-amdci5-15/julialang/julia-release-1-dot-11/julia-f4b43765c5/share/julia/stdlib/v1.11/LinearAlgebra/src/tridiag.jl:689 [inlined]
  | [2] _getindex
  | @ ./abstractarray.jl:1361 [inlined]
  | [3] getindex
  | @ ./abstractarray.jl:1315 [inlined]
  | [4] iterate
  | @ ./abstractarray.jl:1212 [inlined]
  | [5] _foldl_impl(op::Base.MappingRF{typeof(LinearAlgebra.norm), Base.BottomRF{typeof(max)}}, init::Base._InitialValue, itr::LinearAlgebra.Tridiagonal{Main.OffsetArrays.OffsetMatrix{Float64, Matrix{Float64}}, Vector{Main.OffsetArrays.OffsetMatrix{Float64, Matrix{Float64}}}})

@KristofferC KristofferC mentioned this pull request Jul 24, 2024
46 tasks
KristofferC added a commit that referenced this pull request Jul 26, 2024
Backported PRs:
- [x] #54201 <!-- Fix generic triangular solves with empty matrices -->
- [x] #54358 <!-- Create `jl_clear_coverage_data` to dynamically reset
coverage -->
- [x] #54908 <!-- LazyString in interpolated error messages in
threadingconstructs -->
- [x] #54952 <!-- LAPACK: Avoid repr call in `chkvalidparam` -->
- [x] #54898 <!-- fix concurrent module loading return value -->
- [x] #55082 <!-- Add fast method for copyto!(::Memory, ::Memory) -->
- [x] #55084 <!-- Use triple quotes in TOML.print when string contains
newline -->
- [x] #55141 <!-- Update the aarch64 devdocs to reflect the current
state of its support -->
- [x] #54955 <!-- don't throw EOFError from sleep -->
- [x] #54871 <!-- Make warn missed transformations pass optional -->
- [x] #55178 <!-- Compat for `Base.@nospecializeinfer` -->
- [x] #55197 <!-- compat notice for a[begin] indexing -->
- [x] #54917 <!-- Fix potential underrun with annotation merging -->
- [x] #55209 <!-- correction to compat notice for a[begin] -->
- [x] #55203 <!-- document mutable struct const fields -->
- [x] #54791 <!-- Bump libblastrampoline to v5.10.1 -->
- [x] #54950 <!-- SuiteSparse: Bump version -->
- [x] #54956 <!-- Fix accidental early evaluation of imported `using`
binding -->
- [x] #54996 <!-- inference: add missing `MustAlias` widening in
`_getfield_tfunc` -->
- [x] #55070 <!-- LinearAlgebra: LazyString in error messages for
Diagonal/Bidiagonal -->
- [x] #54574 <!-- Make ScopedValues public -->
- [x] #54739 <!-- finish implementation of upgradable stdlibs -->
- [x] #54965 <!-- RFC: Make `include_dependency(path;
track_content=true)` the default -->
- [x] #53286 <!-- Raise an error when using `include_dependency` with
non-existent file or directory -->
- [x] #55066 <!-- fix loading of repeated/concurrent modules -->
- [x] #52694 <!-- Reinstate similar for AbstractQ for backward
compatibility -->
- [x] #55218 <!-- Artifacts: use a different way of getting the UUID of
a module -->
- [x] #54891 <!-- #54739-related fixes for loading stdlibs -->
- [x] #55072 <!-- trace-compile: don't generate `precompile` statements
for OpaqueClosure methods -->
- [x] #55188 <!-- Make Core.TypeofUnion use the type method table -->

Need manual backport:
- [ ] #55052 <!-- Fix `(l/r)mul!` with `Diagonal`/`Bidiagonal` -->


Contains multiple commits, manual intervention needed:

Non-merged PRs with backport label:
- [ ] #55169 <!-- `propertynames` for SVD respects private argument -->
- [ ] #55148 <!-- Random: Mark unexported public symbols as public -->
- [ ] #55017 <!-- TOML: Make `Dates` a type parameter -->
- [ ] #55013 <!-- [docs] change docstring to match code -->
- [ ] #54919 <!-- Fix annotated join with non-concrete eltype iters -->
- [ ] #54457 <!-- Make `String(::Memory)` copy -->
- [ ] #53957 <!-- tweak how filtering is done for what packages should
be precompiled -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
- [ ] #50813 <!-- More doctests for Sockets and capitalization fix -->
- [ ] #50157 <!-- improve docs for `@inbounds` and
`Base.@propagate_inbounds` -->
- [ ] #41244 <!-- Fix shell `cd` error when working dir has been deleted
-->
@KristofferC KristofferC mentioned this pull request Aug 2, 2024
68 tasks
KristofferC added a commit that referenced this pull request Aug 13, 2024
Backported PRs:
- [x] #51351 <!-- Remove boxing in pinv -->
- [x] #52678 <!-- Profile: Improve module docstring -->
- [x] #54201 <!-- Fix generic triangular solves with empty matrices -->
- [x] #54605 <!-- Allow libquadmath to also fail as it is not available
on all systems -->
- [x] #54634 <!-- Fix trampoline assembly for build on clang 18 on apple
silicon -->
- [x] #54635 <!-- Aggressive constprop in trevc! to stabilize triangular
eigvec -->
- [x] #54645 <!-- ensure we set the right value to gc_first_tid -->
- [x] #54671 <!-- Add boundscheck in bindingkey_eq to avoid OOB access
due to data race -->
- [x] #54672 <!-- make: Fix `sed` command for LLVM libraries with no
symbol versioning -->
- [x] #54704 <!-- LazyString in reinterpretarray error messages -->
- [x] #54713 <!-- make: use `readelf` for LLVM symbol version detection
-->
- [x] #54781 <!-- [LinearAlgebra] Improve resilience to unknown
libblastrampoline flags -->
- [x] #54837 <!-- Do not add type tag size to the `alloc_typed` lowering
for GC allocations -->
- [x] #54815 <!-- add sticky task warning to `@task` and `schedule` -->
- [x] #55141 <!-- Update the aarch64 devdocs to reflect the current
state of its support -->
- [x] #55178 <!-- Compat for `Base.@nospecializeinfer` -->
- [x] #55197 <!-- compat notice for a[begin] indexing -->
- [x] #55209 <!-- correction to compat notice for a[begin] -->
- [x] #55203 <!-- document mutable struct const fields -->
- [x] #54769 <!-- add missing compat entry to edit -->
- [x] #54791 <!-- Bump libblastrampoline to v5.10.1 -->
- [x] #55070 <!-- LinearAlgebra: LazyString in error messages for
Diagonal/Bidiagonal -->
- [x] #54624 <!-- more precise aliasing checks for SubArray -->
- [x] #54690 <!-- Fix assertion/crash when optimizing function with dead
basic block -->
- [x] #55084 <!-- Use triple quotes in TOML.print when string contains
newline -->


Need manual backport:
- [ ] #52505 <!-- fix alignment of emit_unbox_store copy -->
- [ ] #53373 <!-- fix sysimage-native-code=no option with pkgimages -->
- [ ] #53984 <!-- Profile: fix heap snapshot is valid char check -->
- [ ] #54276 <!-- Fix solve for complex `Hermitian` with non-vanishing
imaginary part on diagonal -->
- [ ] #54669 <!-- Improve error message in inplace transpose -->
- [ ] #54871 <!-- Make warn missed transformations pass optional -->

Contains multiple commits, manual intervention needed:
- [ ] #52854 <!-- Change to streaming out the heap snapshot data -->
- [ ] #53218 <!-- Fix interpreter_exec.jl test -->
- [ ] #53833 <!-- Profile: make heap snapshots viewable in vscode viewer
-->
- [ ] #54303 <!-- LinearAlgebra: improve type-inference in
Symmetric/Hermitian matmul -->
- [ ] #52694 <!-- Reinstate similar for AbstractQ for backward
compatibility -->
- [ ] #54737 <!-- LazyString in interpolated error messages involving
types -->
- [ ] #54738 <!-- serialization: fix relocatability bug -->
- [ ] #55052 <!-- Fix `(l/r)mul!` with `Diagonal`/`Bidiagonal` -->

Non-merged PRs with backport label:
- [ ] #55220 <!-- `isfile_casesensitive` fixes on Windows -->
- [ ] #55169 <!-- `propertynames` for SVD respects private argument -->
- [ ] #55013 <!-- [docs] change docstring to match code -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
- [ ] #50813 <!-- More doctests for Sockets and capitalization fix -->
- [ ] #50157 <!-- improve docs for `@inbounds` and
`Base.@propagate_inbounds` -->
- [ ] #41244 <!-- Fix shell `cd` error when working dir has been deleted
-->
KristofferC added a commit that referenced this pull request Aug 19, 2024
…5359)

This should hopefully fix the failing tests.

Co-authored-by: Kristoffer Carlsson <kcarlsson89@gmail.com>
@KristofferC KristofferC removed the backport 1.11 Change should be backported to release-1.11 label Aug 19, 2024
KristofferC added a commit that referenced this pull request Aug 26, 2024
Backported PRs:
- [x] #54962 <!-- Add timing to precompile trace compile -->
- [x] #55180 <!-- compress jit debuginfo for easy memory savings -->
- [x] #54919 <!-- Fix annotated join with non-concrete eltype iters -->
- [x] #55013 <!-- [docs] change docstring to match code -->
- [x] #55017 <!-- TOML: Make `Dates` a type parameter -->
- [x] #54033 <!-- Fix a bug in `stack`'s DimensionMismatch error message
-->
- [x] #55242 <!-- fix at-main docstring to not code quote a compat box
-->
- [x] #55261 <!-- Make `jl_*affinity` tests more portable -->
- [x] #54736 <!-- specificity: ensure fast-path in `sub/eq_msp` handle
missing `UnionAll` wrapper correctly. -->
- [x] #55299 <!-- typeintersect: fix bounds merging during inner
`intersect_all`. -->
- [x] #55302 <!-- Add `lbt_forwarded_funcs()` to debug LBT forwarding
issues -->
- [x] #55148 <!-- Random: Mark unexported public symbols as public -->
- [x] #55303 <!-- avoid overflowing show for OffsetArrays around typemax
-->
- [x] #55317 <!-- Restrict argument to `isleapyear(::Integer)` -->
- [x] #55327 <!-- Profile: Fix stdlib paths -->
- [x] #55330 <!-- [libblastrampoline] Bump to v5.11.0 -->
- [x] #55310 <!-- Preserve structure in scaling triangular matrices by
NaN -->
- [x] #55329 <!-- mapreduce: don't inbounds unknown functions -->
- [x] #55356 <!-- Profile: close files when assembling heap snapshot -->
- [x] #55371 <!-- Fix tr for block SymTridiagonal -->
- [x] #55307 <!-- Make REPL.TerminalMenus public -->
- [x] #55362 <!-- inference: fix missing LimitedAccuracy markers -->
- [x] #55306 <!-- AllocOpt: Fix stack lowering where alloca continas
boxed and unboxed data -->
- [x] #55395 <!-- fix #55389: type-unstable `join` -->
- [x] #55226 <!-- re-add `unsafe_convert` for Reinterpret and Reshaped
array -->
- [x] #55405 <!-- handle unbound vars in NTuple fields -->
- [x] #55365 <!-- ml-matches: ensure all methods are included -->
- [x] #55428 <!-- codegen: move undef freeze before promotion point -->
- [x] #55419 <!-- `stale_cachefile`: handle if the expected cache file
is missing -->
- [x] #55470 <!-- Add push! implementation for AbstractArray depending
only on resize! -->
- [x] #55483 <!-- fix hierarchy level of "API reference" in `Dates`
documentation -->
- [x] #55268 <!-- simplify complex atanh and remove singularity
perturbation -->
- [x] #55441 <!-- fix Event to use normal Condition variable -->
- [x] #55413 <!-- subtyping: fast path for lhs union and rhs typevar -->
- [x] #55492 <!-- build: add missing dependencies for expmap -->
- [x] #55507 <!-- Fix fast getptls ccall lowering. -->
- [x] #55424 <!-- add missing clamp function for IOBuffer -->
- [x] #55504 <!-- Update symmetric docstring to reflect the type of uplo
-->
- [x] #55107 <!-- Make the memory GEP an inbounds GEP since the bounds
check has happened somewhere else -->
- [x] #55411 <!-- Vendor the terminfo database for use with
base/terminfo.jl -->
- [x] #55452 <!-- Do not load `ScopedValues` with `using` -->
- [x] #55407 <!-- Remove deprecated non string API for LLVM pass
pipeline and parse all options -->
- [x] #55461 <!-- 🤖 [master] Bump the StyledStrings stdlib from d7496d2
to f6035eb -->
- [x] #55433 <!-- Backport #55407
to 1.11 -->
- [x] #55225 <!-- [1.11 backport] trace-compile: don't generate
`precompile` statements for OpaqueClosure methods (#55072) -->
- [x] #55212 <!-- Make `Base.depwarn()` public -->
- [x] #552
- [x] #55052 <!-- Fix `(l/r)mul!` with `Diagonal`/`Bidiagonal` -->
- [x] #55251 <!-- Restrict binary ops for Diagonal and Symmetric to
Number eltypes -->95 <!-- LAPACK: Aggressive constprop to concretely
infer syev!/syevd! -->
- [x] #55522 <!-- Fix tr for Symmetric/Hermitian block matrices -->

Need manual backport:
- [x] #55342 <!-- Ensure bidiagonal setindex! does not read indices in
error message -->

Contains multiple commits, manual intervention needed:

- [ ] #55336 <!-- codegen: take gc roots (and alloca alignment) more
seriously -->


Non-merged PRs with backport label:
- [ ] #55506 <!-- Fix indexing in _mapreducedim for OffsetArrays -->
- [ ] #55500 <!-- make jl_thread_suspend_and_get_state safe -->
- [ ] #55499 <!-- propagate the terminal's `displaysize` to the
`IOContext` used by the REPL -->
- [ ] #55458 <!-- Allow for generically extracting unannotated string
-->
- [ ] #55457 <!-- Make AnnotateChar equality consider annotations -->
- [ ] #55453 <!-- Privatise the annotations API, for StyledStrings -->
- [ ] #55443 <!-- Add test for upper/lower/titlecase and fix call -->
- [ ] #55355 <!-- relocation: account for trailing path separator in
depot paths -->
- [ ] #55220 <!-- `isfile_casesensitive` fixes on Windows -->
- [ ] #55169 <!-- `propertynames` for SVD respects private argument -->
- [ ] #54457 <!-- Make `String(::Memory)` copy -->
- [ ] #53957 <!-- tweak how filtering is done for what packages should
be precompiled -->
- [ ] #51479 <!-- prevent code loading from lookin in the versioned
environment when building Julia -->
- [ ] #50813 <!-- More doctests for Sockets and capitalization fix -->
- [ ] #50157 <!-- improve docs for `@inbounds` and
`Base.@propagate_inbounds` -->
- [ ] #41244 <!-- Fix shell `cd` error when working dir has been deleted
-->
@KristofferC KristofferC mentioned this pull request Sep 12, 2024
46 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport 1.10 Change should be backported to the 1.10 release bugfix This change fixes an existing bug linear algebra Linear algebra
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants