Uh oh!
There was an error while loading. Please reload this page.
Take is_inplaceable_destination seriously - #577
Conversation
Codecov Report
@@ Coverage Diff @@## main #577 +/- ##
==========================================
+ Coverage 93.15% 93.18% +0.03%
==========================================
Files 15 15 Lines 891 895 +4 ==========================================
+ Hits 830 834 +4
Misses 61 61
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
| is_inplaceable_destination(::DenseArray)= true | ||
| is_inplaceable_destination(::DenseArray{<:Integer}) = false |
There was a problem hiding this comment.
The intention was to allow for CuArrays. But perhaps this is unsafe, as:
julia> Base.Experimental.Const([1,2]) isa DenseArray
true
julia> subtypes(DenseArray)
13-element Vector{Any}:
Array
Base.CodeUnits
Base.Experimental.Const
CUDA.Const
CUDA.CuDeviceArray
GPUArrays.AbstractDeviceArray
GPUArraysCore.AbstractGPUArray
Metal.Const
Metal.MtlLargerDeviceArray
MtlDeviceArray
Random.UnsafeView
SharedArrays.SharedArray
SparseArrays.CHOLMOD.Dense
| is_inplaceable_destination(::DenseArray)=true | |
| is_inplaceable_destination(::DenseArray{<:Integer}) =false | |
| is_inplaceable_destination(::Array)=true | |
| is_inplaceable_destination(::Array{<:Integer}) =false |
There was a problem hiding this comment.
Or perhaps:
| is_inplaceable_destination(::DenseArray)=true | |
| is_inplaceable_destination(::DenseArray{<:Integer}) =false | |
| is_inplaceable_destination(x::DenseArray)=ismutable(x) | |
| is_inplaceable_destination(x::DenseArray{<:Integer}) =false |
There was a problem hiding this comment.
I think to be truly safe we much only accept Array
Since anyone can subtype DenseArray, and overload setindex however they want
There was a problem hiding this comment.
A pity to miss CuArrays. (Although previous version also marks them non-writable.) But agree that if the goal is always to fail on the safe side, then is has to be Array.
mzgubic
left a comment
There was a problem hiding this comment.
As far as I understand this PR will make some things work, and other things silently slower, because it will make them not inplaceable. Do we know how big the performance drop will be, and how widespread it will be? I imagine it should be fine as long as we cover most commonly used types here?
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Miha Zgubic <mzgubic@users.noreply.github.com>
mcabbott
commented
Aug 25, 2022
The cost is like I think its real intended use is for InplaceabeThunk which, as far as I know, is not in use at all, since The goal is more to make this function safe to use in code which may meet unknown types. For this it needs to fail to the side which doesn't error, that's much more important than guessing correctly more often. Some discussion of such use here: FluxML/Optimisers.jl#99 Instead of changing this function, it could be deprecated & replaced. |
mcabbott
commented
Aug 25, 2022
There is also a question of whether it treats eltypes and structure correctly. The present one is documented to allow The PR makes it return false on integers arrays, as you can't safely write a gradient into such a thing. But it won't stop you trying to write a complex array into a real one. |
oxinabox
commented
Aug 26, 2022
You know if you made this PR from a branch of this repo, rather than from your fork, you could just click accept on all the nagging from the autoformatter? |
oxinabox
commented
Aug 26, 2022
The intended use is for in I think this will have no performance impacts on anything real |
Uh oh!
There was an error while loading. Please reload this page.
| alpha = is_inplaceable_destination(parent(x)) | ||
| beta = x.indices isa Tuple{Vararg{ Union{Integer, Base.Slice, UnitRange}}} | ||
| return alpha && beta |
There was a problem hiding this comment.
Is it clearer to do
| alpha =is_inplaceable_destination(parent(x)) | |
| beta = x.indices isa Tuple{Vararg{ Union{Integer, Base.Slice, UnitRange}}} | |
| return alpha && beta | |
| is_inplaceable_destination(parent(x)) ||return falsse | |
| return x.indices isa Tuple{Vararg{Union{Integer,Base.Slice,UnitRange}}} |
I will trust your judgement
There was a problem hiding this comment.
I had a few variants. Maybe I don't like that this seems to imply you ought to check one before the other, but in fact it's just dealing with line length.
| @eval is_inplaceable_destination(x::Base.$T) = is_inplaceable_destination(parent(x)) | ||
| end | ||
| for T in [:Adjoint, :Transpose, :Diagonal, :UpperTriangular, :LowerTriangular] | ||
| @eval is_inplaceable_destination(x::LinearAlgebra.$T) = is_inplaceable_destination(parent(x)) |
There was a problem hiding this comment.
a bit sad that this won't catch NamedDimsArrays or KeyedArrays for free anymore.
But those need to overload ProjectTo anyway so its fine to make them do this
That wrapper types in Base is most of what we need to deal with
There was a problem hiding this comment.
Besides having something you have to opt into, the other approach I can think of would be to ask type inference. But... this doesn't seem like the package's style:
julia> Core.Compiler._return_type(setindex!, typeof(([1,2,3.0], 4.0, 2)))
Vector{Float64} (alias for Array{Float64, 1})
julia> Core.Compiler._return_type(setindex!, typeof((SA[1,2,3.0], 4.0, 2)))
Union{}
julia> Core.Compiler._return_type(setindex!, typeof((1:3.0, 4.0, 2)))
Union{}
julia> which(setindex!, typeof((SA[1,2,3.0], 4.0, 2))) # their own error
setindex!(a::StaticArray, value, i::Int64)
@ StaticArrays ~/.julia/packages/StaticArrays/6QFsp/src/indexing.jl:3
julia> which(setindex!, typeof((1:3.0, 4.0, 2))) # not useful
setindex!(A::AbstractArray, v, I...)
@ Base abstractarray.jl:1374
Uh oh!
There was an error while loading. Please reload this page.
oxinabox
left a comment
There was a problem hiding this comment.
Sensible reallly.
Only handle that which we know about.
Address remaining comments and then merge when happy
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Frames Catherine White <oxinabox@ucc.asn.au>
| # TODO: uncomment this once https://github.com/JuliaLang/julia/issues/35516 | ||
| if VERSION >= v"1.8-" | ||
| @test haskey(Tangent{Tuple{Float64}}(2.0), 1) == true | ||
| else | ||
| @test_broken haskey(Tangent{Tuple{Float64}}(2.0), 1) == true | ||
| end | ||
| # if VERSION >= v"1.8-" | ||
| # @test haskey(Tangent{Tuple{Float64}}(2.0), 1) == true | ||
| # else | ||
| # @test_broken haskey(Tangent{Tuple{Float64}}(2.0), 1) == true | ||
| # end | ||
| @test_broken hasproperty(Tangent{Tuple{Float64}}(2.0), 2) == false |
There was a problem hiding this comment.
This is unrelated, but was needed to make tests pass on 1.9. I don't know why it says "uncomment" but now that things are commented, it may make sense.
| @test_throws MethodError reverse(Tangent{Foo}(; x=1.0, y=2.0)) | ||
| if VERSION < v"1.9-" | ||
| # can't reverse a named tuple or a dict | ||
| @test_throws MethodError reverse(Tangent{Foo}(; x=1.0, y=2.0)) |
There was a problem hiding this comment.
Likewise needed for 1.9
This tests that you can't reverse a Tangent, but do we care that you can't? We know that it's meaningless, like the order of fields in a struct. But if it used to be an error and now works, why is that a failure?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
To be useful, this function should return true only when it is certain that the array can be written into. A false
falsecosts one allocation, but a falsetruewill lead to an error. At present, it guesses wrong for wrappers like ReadOnlyArrays.Commit c3bf4b0 is just fixing tests to pass on 1.9, could be pulled out.