# \`SymPy.Sym\`s as keys in Dicts

**URL:** <https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995>\
**Category:** General Usage\
**Tags:** dictionary, potential-bug\
**Created:** [September 18, 2023, 4:42pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995 "2023-09-18T16:42:06Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [September 18, 2023, 4:42pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/1 "2023-09-18T16:42:06Z")

</div>

It seems that Tuples containting `SymPy.Sym`s don’t work as keys in julia’s standard dicts as expected. See the following MWE and its very counterintuitive behaviour.

```julia-repl
julia> using SymPy

julia> t = symbols("t")
t 

julia> = Dict()
Dict{Any, Any}()

julia> get!(d, (sin(t),), 0)
0

julia> get!(d, (sin(t),), 1)
1

julia> s = sin(t)
sin(t)

julia> get!(d, (s,), 2)
2

julia> get!(d, (s,), 3)
2

julia> haskey(d, (sin(t),))
false 

julia> haskey(d, (s,))
true

```

Is this a bug that should be reported on github, or is this behaviour expected? I want to use more complex SymPy expressions as keys to get some Memoization to work (see [this issue](https://github.com/marius311/Memoization.jl/issues/30) on github).

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [September 18, 2023, 6:51pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/2 "2023-09-18T18:51:42Z")

</div>

The issue is subtle. Hashing a tuple ends up calling `objectid` which differs for `s` and `sin(t)` (as `s === sin(t)` is false). Hashing `s` and `sin(t)` separately falls back to `hash(PyObject(s))` and `hash(PyObject(sin(t)))` which have the same hash. I think a change (a `hash(x::PyObject, h::UInt64)` method?) to `PyCall` would be needed here to get the same hash, though I’m not saying that is what the behaviour should be.

The related `SymPyPythonCall` package does hash the two tuples identically, so it does work there, so we have:

```julia
julia> @syms t; s = sin(t)
sin(t)

julia> d[(s,)] = s; d[(sin(t),)] = s^2
sin(t)^2

julia> d
Dict{Any, Any} with 1 entry:
  (sin(t),) => sin(t)^2

```

Under SymPy, the last command would print:

```julia
julia> d
Dict{Any, Any} with 2 entries:
  (sin(t),) => sin(t)^2
  (sin(t),) => sin(t)

```

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [September 19, 2023, 9:28am UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/3 "2023-09-19T09:28:21Z")

</div>

Thanks for the explanation! It turned out that the definition I needed to override was not only for `hash(::PyObject, ::UInt64)` but also for `hash(::Sym, ::UInt64)` to get it all working. So:

```julia
Base.hash(x::SymPy.PyObject, h::UInt64) = Base.hash_uint(3h - hash(x))
Base.hash(x::SymPy.Sym, h::UInt64) = Base.hash_uint(3h - hash(x))

```

I realise this is type piracy, but for little secret private code that I won’t share with anyone I am happy to have a bit of naughty type piracy.

---

<div class="post-metadata">

**Author:** ![alsaibie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alsaibie/32/23414_2.png) [@alsaibie](https://discourse.julialang.org/u/alsaibie)\
**Post date:** [December 27, 2023, 5:30pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/4 "2023-12-27T17:30:31Z")

</div>

Hello,

I’m having a related issue accessing values as keys in a Dictionary.

```julia
using SymPy

@vars ω positive = true
eq = 5^2 ~ ω^2
sol = solve(eq, ω, dict=true)
ω_v = sol[1][ω] # error

```

Error is

```bash
ERROR: MethodError: Eq(::Sym, ::Sym) is ambiguous.

Candidates:
  Eq(ex::Number, args...; kwargs...)
    @ SymPy C:\Users\USER\.julia\packages\SymPy\S5qKW\src\importexport.jl:145
  Eq(a::T, b::T) where T
    @ CommonEq C:\Users\USER\.julia\packages\CommonEq\DtDkN\src\CommonEq.jl:18

Possible fix, define
  Eq(::T, ::T) where T<:Number

Stacktrace:
 [1] isequal(a::Sym, b::Sym)
   @ SymPy C:\Users\USER\.julia\packages\SymPy\S5qKW\src\generic.jl:17
 [2] ht_keyindex(h::Dict{Any, Any}, key::Sym)
   @ Base .\dict.jl:275
 [3] getindex(h::Dict{Any, Any}, key::Sym)
   @ Base .\dict.jl:497
 [4] top-level scope
   @ _FILE_PATH_:6

```

However if I re-grab the key and use it, it works

```julia
ω_ = collect(keys(sol[1]))[1]
ω_v = sol[1][ω_] # success

```

And both `ω` and `ω_` have the same type:

```bash
julia> typeof(ω)
Sym

julia> typeof(ω_)
Sym

```

Tried it on Julia 1.9.0 and 1.10.0, SymPy v.1.2.0

This used to work on prior versions of Julia (1.6) and SymPy.

Any hints?

---

<div class="post-metadata">

**Author:** ![alsaibie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alsaibie/32/23414_2.png) [@alsaibie](https://discourse.julialang.org/u/alsaibie)\
**Post date:** [December 27, 2023, 5:37pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/5 "2023-12-27T17:37:06Z")

</div>

## Resolved on SymPy v2.0.1

And syntax for instantiating symbols is different.

```julia
using SymPy

@syms ω::positive
eq = 5^2 ~ ω^2
sol = solve(eq, ω, dict=true)
ω_v = sol[1][ω] # success

```

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [December 27, 2023, 7:04pm UTC](https://discourse.julialang.org/t/sympy-sym-s-as-keys-in-dicts/103995/6 "2023-12-27T19:04:45Z")

</div>

Yes, this was a hashing issue that got adressed not too long ago.
