This last weekend, I was finally able to fix the bug that had been eluding me for the past two weeks. Click here to read my previous blog post about this bug.
My approach to fixing the bug was to painstakingly keeping track of the reference states of the different variables after each deserialization. Eventually, I realized that the issue was there every single time it was deserialized so I started testing by serializing and deserializing manually, over and over again. I discovered that sympy Dummy is being deserialized differently from sympy Dummys wrapped inside sympy expression objects. For some reason, when a Dummy is part of an expression, it maintains the same reference as the original reference when it is deserialized (which is not guaranteed by picklers). On the other hand, when the Dummy is deserialized separately, the reference does change but the state is be the same as the original version. However, since the reference changes, the links between this Dummy and the Dummys inside expressions are broken. I am not sure if this is a feature or a bug.
I experimented a bit and found that sympy Symbols are being serialized and deserialized consistently, whether they are in expressions or not. For Symbols, the references are not the same as the original but, all of the Symbols deserialized together maintain the same reference to object relationship as the original Symbols. To fix our Variable class, I merely changed the __getstate__ function to append onto the state of Symbol, skipping the state of Dummy so that dummy_index is not serialized. This means that whenever an object is deserialized, the new Dummys do not have the same index as the original ones, but the reference relationship is maintained. Bypassing Dummy serialization seems to have fixed the bug and doesn't seem to have caused issues in other places, but more testing is needed to confirm that there are no side effects.