`sympy` symbols end up in inconsistent internal state, causing problems in `Model`
Stumbled on this while investigating why some tests in kwant.tests.test_qsymm
fail, but it seems this problem only involves qsymm
.
Here is a minimal code segment that shows the problem, works with stable-1.2
. It seems fully reproducible on IO, but you might need to restart the python interpreter until you get an AssertionError
. This shows that one of the sympy symbols (c0
) got into a state where the internally cached hash is different from the hash that is recalculated using the same code that would be used if the hash wasn't cached.
This in itself wouldn't be a problem if it was consistent, but it is possible to end up with two symbols that look identical in every respect, but have different hash, causing Model
mishandling them by not combining the corresponding terms. Observe that c0
and c0_new
look identical for all purposes in the end, but they are different objects with different hash.
import numpy as np
import sympy
import qsymm
from qsymm.model import Model, _symbol_normalizer
# Define a symbol called c0
c0 = sympy.symbols('c0')
print('c0:')
print(c0, c0.assumptions0)
h = hash(c0)
h2 = hash((type(c0).__name__,) + c0._hashable_content())
print(h == h2, h, h2)
print('----')
# Define another symbol called c0, now make it real
c0_new = sympy.symbols('c0', real=True)
print('c0_new:')
print(c0_new, c0_new.assumptions0)
h = hash(c0_new)
h2 = hash((type(c0_new).__name__,) + c0_new._hashable_content())
print(h == h2, h, h2)
print('c0 is c0_new', c0 is c0_new)
print('----')
# Feed c0_new through some transformations in Model to get c
fam0 = Model({1: np.eye(1)}) * c0_new
c = list(fam0.keys())[0]
# c = _symbol_normalizer(c0_new)
print('c:')
print(c, c.assumptions0)
h = hash(c)
h2 = hash((type(c).__name__,) + c._hashable_content())
print(h == h2, h, h2)
print('c is c0_new', c is c0_new)
print('c is c0', c is c0)
# Now we have two screwed up symbols
print('----')
print('c0_new:')
print(c0_new, c0_new.assumptions0)
h = hash(c0_new)
h2 = hash((type(c0_new).__name__,) + c0_new._hashable_content())
print(h == h2, h, h2)
print('c0 is c0_new', c0 is c0_new)
print('----')
print('c0:')
print(c0, c0.assumptions0)
h = hash(c0)
h2 = hash((type(c0).__name__,) + c0._hashable_content())
print(h == h2, h, h2)
assert h == h2