-
Notifications
You must be signed in to change notification settings - Fork 16
Fix inverse_eltype
for ScalarTransform
s
#127
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
Conversation
c92a338
to
9ee0166
Compare
Can you please share an MWE? BTW, the primary use case of this package is And in any case, transformations are rarely a bottleneck in real life code. Maybe we can dispose of |
I added a few simple reproducers to the tests. My example was more complex but a simple cases that errors currently due to julia> d1 = ForwardDiff.derivative(5.3) do x
return only(inverse(as(Vector, as(Real, x, ∞), 1), [10]))
end
ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4", Float64}, Float64, 1})
The type `Float64` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
(::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat
@ Base rounding.jl:265
(::Type{T})(::T) where T<:Number
@ Core boot.jl:900
Float64(::IrrationalConstants.Logπ)
@ IrrationalConstants ~/.julia/packages/IrrationalConstants/lWTip/src/macro.jl:131
...
Stacktrace:
[1] convert(::Type{Float64}, x::ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4", Float64}, Float64, 1})
@ Base ./number.jl:7
[2] setindex!(A::Vector{Float64}, x::ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4", Float64}, Float64, 1}, i::Int64)
@ Base ./array.jl:987
[3] inverse_at!(x::Vector{Float64}, index::Int64, t::TransformVariables.ShiftedExp{true, ForwardDiff.Dual{…}}, y::Int64)
@ TransformVariables ~/.julia/packages/TransformVariables/9Eqxa/src/scalar.jl:25
[4] inverse_at!
@ ~/.julia/packages/TransformVariables/9Eqxa/src/aggregation.jl:224 [inlined]
[5] inverse!(x::Vector{…}, transformation::TransformVariables.ArrayTransformation{…}, y::Vector{…})
@ TransformVariables ~/.julia/packages/TransformVariables/9Eqxa/src/generic.jl:191
[6] inverse(t::TransformVariables.ArrayTransformation{TransformVariables.ShiftedExp{…}, 1}, y::Vector{Int64})
@ TransformVariables ~/.julia/packages/TransformVariables/9Eqxa/src/generic.jl:270
[7] (::var"#3#4")(x::ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4", Float64}, Float64, 1})
@ Main ./REPL[5]:2
[8] derivative(f::var"#3#4", x::Float64)
@ ForwardDiff ~/.julia/packages/ForwardDiff/UBbGT/src/derivative.jl:14
[9] top-level scope
@ REPL[5]:1
Some type information was truncated. Use `show(err)` to see complete types. |
Given your MWE, I think that the issue is that you are differentiating the transform, and I did not expect this use case, but it should be allowed. This is an issue only with the scalar transforms, since the rest have integer parameters. I think the quickest way to fix this may be
function inverse_eltype(t::ScalarTransform{S}, y::T) where {S,T <: Real}
promote_type(S, float(T))
end but I did not check this (not at the computer currently). |
Exactly, that's the problem I tried to address with the PR 🙂
I considered this, but I was worried that in general cases beyond the julia> using Unitful
julia> typeof(1.0u"s" * exp(1.0))
Quantity{Float64, 𝐓, Unitful.FreeUnits{(s,), 𝐓, nothing}}
julia> promote_type(typeof(1.0u"s"), Float64)
Quantity{Float64} |
Note that the unit types are not Is there a case where function promote_reals(::Type{T}, ::Type{S}) where {T,S}
typeof(exp(one(S) + zero(T)))
end and hope that the compiler elides this. |
Yes, this was just a hypothetical example to show limitations of such a default definition of My current feeling is that either the default should be based on |
First, note that Users may hook into the API of this package by defining their own methods (see the docstring of I am inclined to merge this as is. I am aware that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this and the thorough tests.
With julia> @b (as(Real, 0, 2), 1.0) inverse_eltype(_[1], _[2])
1.530 ns so performance-wise this is fine. |
Co-authored-by: Tamas K. Papp <tkpapp@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
I had a use case of TransformVariables where I wanted to differentiate with respect to the parameters of a (scalar) transform. Initially this failed due to the default definition of
inverse_eltype
forScalarTransform
s, which is based solely on the type of the input value.A safer but more complex - and hence in general slower - default definition would be based on
inverse
. AFAICT this would be the only way that would be guaranteed to be correct. Alternatively, one could require that users implementinverse_eltype
and not provide a default implementation.On the master branch
With this PR
Edit: I generally try to avoid
Base.promote_op
for the reasons listed in its docstring, but it seems to provide a fast alternative for the problematic transforms in TransformVariables. However, it means that the return type ofinverse_eltype(::ScalarTransform)
might change in different Julia versions, etc. The docstring ofinverse_eltype
only mentions that it should return a type that can be used forinverse!
, so it might not be a problem.