Error when hashing Measurement{Float64}
jwscook opened this issue · comments
Hello,
I've encountered this error:
julia> hash(1.0 ± 0.1)
ERROR: MethodError: no method matching decompose(::Measurement{Float64})
Closest candidates are:
decompose(::Integer) at float.jl:555
decompose(::Float16) at float.jl:557
decompose(::Float32) at float.jl:568
...
Stacktrace:
[1] hash(x::Measurement{Float64}, h::UInt64)
@ Base ./float.jl:491
[2] hash(x::Measurement{Float64})
@ Base ./hashing.jl:18
[3] top-level scope
@ REPL[6]:1
What would the fix be? Declare hash
by hand or implement decompose
?
Something like:
Base.hash(m::Measurement) = foldr(hash, (m.val, m.err, m.der, m.tag))
or maybe if tag
can be hijacked:
Base.hash(m::Measurement) = foldr(hash, (m.val, m.err, m.der); init=m.tag)
Versions:
[eff96d63] Measurements v2.6.0
Version 1.6.1-pre.39 (2021-04-17)
Frankly, I've never implemented hashing because
- I don't understand how it's supposed to work for non-standard numbers (and lack of documentation about this doesn't help much)
- so far I never had to implement them in practice.
I'm happy to do it if someone can help in particular with point 1, or if they open a pull request 🙂
Taking inspiration from other packages:
- AutoHashEquals.jl hashes all the contents together like in my first example.
- DualNumbers.jl uses
bitmix
, but I'm not sure wherebitmix
comes from.
Out of curiosity, did you find a case in practice where the hash
method was needed? I'm aware the method isn't implemented, but I couldn't find a real-world case in my applications where this would be needed.
One instance it using a Measurement as a key in a Dict:
julia> using Measurements
julia> m = 1.0 ± 0.1
1.0 ± 0.1
julia> Dict(m=>"anything else here")
ERROR: MethodError: no method matching decompose(::Measurement{Float64})
Closest candidates are:
decompose(::Integer) at float.jl:555
decompose(::Float16) at float.jl:557
decompose(::Float32) at float.jl:568
...
Stacktrace:
[1] hash(x::Measurement{Float64}, h::UInt64)
@ Base ./float.jl:491
[2] hash(x::Measurement{Float64})
@ Base ./hashing.jl:18
[3] hashindex(key::Measurement{Float64}, sz::Int64)
@ Base ./dict.jl:169
[4] ht_keyindex2!(h::Dict{Measurement{Float64}, String}, key::Measurement{Float64})
@ Base ./dict.jl:310
[5] setindex!(h::Dict{Measurement{Float64}, String}, v0::String, key::Measurement{Float64})
@ Base ./dict.jl:383
[6] Dict{Measurement{Float64}, String}(kv::Tuple{Pair{Measurement{Float64}, String}})
@ Base ./dict.jl:104
[7] Dict(ps::Pair{Measurement{Float64}, String})
@ Base ./dict.jl:124
[8] top-level scope
@ REPL[3]:1
julia> Base.hash(m::Measurement) = foldr(hash, (m.val, m.err, m.der, m.tag))
julia> Dict(m=>"anything else here")
Dict{Measurement{Float64}, String} with 1 entry:
1.0±0.1 => "anything else here"
I've added the hash
method in #104. However, due to the fact isequal(x, y)
must imply hash(x) == hash(y)
we have this weird situation:
julia> x = 3 ± 0.1
3.0 ± 0.1
julia> a = 3 ± 0.1
3.0 ± 0.1
julia> x === a
false
julia> d = Dict(x => 42)
Dict{Measurement{Float64}, Int64} with 1 entry:
3.0±0.1 => 42
julia> d[x]
42
julia> d[a]
42
julia> d[a] = 0
0
julia> d[a]
0
julia> d[x]
0
I don't think this can be avoided given the requirement on the relation between isequal
and hash