From 923e9ab90d783580b5b0bafacb88fb57a06bc87a Mon Sep 17 00:00:00 2001 From: Ulysses Tiberious Date: Mon, 30 Mar 2026 00:39:00 +0800 Subject: [PATCH] Make Pdop and Sdop hashable and improve SymPy integration Add __hash__ and __ne__ methods to Pdop and Sdop classes so they can be used in sets, dictionaries, and other contexts that require hashable objects. This is a step toward better integration with SymPy's expression system. Fixes utiberious/galgebra#8 --- galgebra/dop.py | 15 +++++++++++++++ test/test_differential_ops.py | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/galgebra/dop.py b/galgebra/dop.py index 19f3226e..aa2f04d3 100644 --- a/galgebra/dop.py +++ b/galgebra/dop.py @@ -231,6 +231,15 @@ def __eq__(self, other): else: return NotImplemented + def __ne__(self, other): + result = self.__eq__(other) + if result is NotImplemented: + return result + return not result + + def __hash__(self): + return hash(self.terms) + def __add__(self, sdop): return Sdop.Add(self, sdop) @@ -312,6 +321,12 @@ def __eq__(self, A): return True return False + def __ne__(self, A): + return not self.__eq__(A) + + def __hash__(self): + return hash(tuple(sorted(self.pdiffs.items()))) + def __init__(self, __arg): """ The partial differential operator is a partial derivative with diff --git a/test/test_differential_ops.py b/test/test_differential_ops.py index 33a848fc..dcf2ea91 100644 --- a/test/test_differential_ops.py +++ b/test/test_differential_ops.py @@ -311,6 +311,32 @@ def test_multiply(self): with pytest.raises(TypeError): op(p, ex) + def test_hash_and_eq(self): + """Test Pdop/Sdop are hashable and support ne (issue 234).""" + x, y = symbols('x y', real=True) + p1 = Pdop(x) + p2 = Pdop(y) + p1b = Pdop(x) + p0 = Pdop({}) + + # Pdop is hashable + assert hash(p1) == hash(p1b) + assert {p1, p2, p1b} == {p1, p2} + + # ne works + assert p1 != p2 + assert not (p1 != p1b) + + # identity pdop + assert p0 == S.One + assert p0 != p1 + + # Sdop is hashable + s1 = Sdop([(x, p1)]) + s2 = Sdop([(y, p2)]) + assert hash(s1) is not None + assert {s1, s2} == {s1, s2} + def test_constructor_errors(self): # not a symbol or dict with pytest.raises(TypeError, match='dictionary or symbol is required'):