From b2d5263ea3828acbee793af9debfd248c745b7d8 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Fri, 1 Jul 2022 16:53:08 +0100 Subject: [PATCH 1/3] feat: hash method for Obs objects added. --- pyerrors/obs.py | 6 ++++++ tests/obs_test.py | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index df22a36c..80b0b090 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -684,6 +684,12 @@ class Obs: else: return '{:.0f}({:2.0f})'.format(self.value, self._dvalue) + def __hash__(self): + return hash((np.array([self.value]).astype(np.float32)[0], + tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]), + tuple(np.array([o.errsq() for o in self.covobs.values()]).astype(np.float32).data.tobytes()), + tuple(self.names))) + # Overload comparisons def __lt__(self, other): return self.value < other diff --git a/tests/obs_test.py b/tests/obs_test.py index 1665ef62..d58933c9 100644 --- a/tests/obs_test.py +++ b/tests/obs_test.py @@ -950,3 +950,16 @@ def test_cobs_array(): cobs * np.identity(4) np.identity(4) / cobs cobs / np.ones((4, 4)) + + +def test_hash(): + obs = pe.pseudo_Obs(0.3, 0.1, "test") + pe.Obs([np.random.normal(2.3, 0.2, 200)], ["test2"], [range(1, 400, 2)]) + o1 = obs + pe.cov_Obs(0.0, 0.1, "co") + pe.cov_Obs(0.0, 0.8, "co2") + o2 = obs + pe.cov_Obs(0.0, 0.2, "co") + pe.cov_Obs(0.0, 0.8, "co2") + + for i_obs in [obs, o1, o2]: + assert hash(i_obs) == hash(i_obs ** 2 / i_obs) == hash(1 * i_obs) + assert hash(i_obs) == hash((1 + 1e-16) * i_obs) + assert hash(i_obs) != hash((1 + 1e-7) * i_obs) + assert hash(obs) != hash(o1) + assert hash(o1) != hash(o2) From ded21e792f3e6766c78e85733a3cbbf467c408a0 Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 5 Jul 2022 10:19:49 +0100 Subject: [PATCH 2/3] feat: hashing algorithm changed to md5 --- pyerrors/obs.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 80b0b090..6be18c0b 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -1,4 +1,5 @@ import warnings +import hashlib import pickle from math import gcd from functools import reduce @@ -685,10 +686,13 @@ class Obs: return '{:.0f}({:2.0f})'.format(self.value, self._dvalue) def __hash__(self): - return hash((np.array([self.value]).astype(np.float32)[0], - tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]), - tuple(np.array([o.errsq() for o in self.covobs.values()]).astype(np.float32).data.tobytes()), - tuple(self.names))) + hash_tuple = (np.array([self.value]).astype(np.float32).data.tobytes(),) + hash_tuple += tuple([o.astype(np.float32).data.tobytes() for o in self.deltas.values()]) + hash_tuple += tuple([np.array([o.errsq()]).astype(np.float32).data.tobytes() for o in self.covobs.values()]) + hash_tuple += tuple([o.encode() for o in self.names]) + m = hashlib.md5() + [m.update(o) for o in hash_tuple] + return int(m.hexdigest(), 16) # Overload comparisons def __lt__(self, other): From 572309c800be5329134fb6846b684239d8b2788c Mon Sep 17 00:00:00 2001 From: Fabian Joswig Date: Tue, 5 Jul 2022 10:53:31 +0100 Subject: [PATCH 3/3] feat: truncate hash to 32bit to deal with automatic truncation of python dunder hash method. --- pyerrors/obs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyerrors/obs.py b/pyerrors/obs.py index 6be18c0b..24ef3720 100644 --- a/pyerrors/obs.py +++ b/pyerrors/obs.py @@ -692,7 +692,7 @@ class Obs: hash_tuple += tuple([o.encode() for o in self.names]) m = hashlib.md5() [m.update(o) for o in hash_tuple] - return int(m.hexdigest(), 16) + return int(m.hexdigest(), 16) & 0xFFFFFFFF # Overload comparisons def __lt__(self, other):