-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
6 changed files
with
96 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import sys | ||
from collections import OrderedDict | ||
import numpy as np | ||
|
||
|
||
def _to_hashable(obj): | ||
"""Make unhashable objects hashable in a consistent manner.""" | ||
|
||
if isinstance(obj, (int, float, str)): | ||
# Strings and Numbers are hashed directly. | ||
return obj | ||
|
||
elif hasattr(obj, "__iter__"): | ||
# Encapsulate all the iterables to quickly discard as needed. | ||
|
||
if isinstance(obj, np.ndarray): | ||
# Numpy arrays: Convert the data buffer to a byte string. | ||
return obj.tobytes() | ||
|
||
elif isinstance(obj, dict): | ||
# Dictionaries: Build a tuple from key-value pairs, | ||
# where all values are converted to hashables. | ||
out = {key: _to_hashable(value) for key, value in obj.items()} | ||
# Sort unordered dictionaries for hash consistency. | ||
if isinstance(obj, OrderedDict): | ||
return tuple(out.items()) | ||
return tuple(sorted(out.items())) | ||
|
||
else: | ||
# Iterables: Build a tuple from values converted to hashables. | ||
out = [_to_hashable(item) for item in obj] | ||
return tuple(out) | ||
|
||
elif hasattr(obj, "__hash__"): | ||
# Hashables: Just return the object. | ||
return obj | ||
|
||
# NotImplemented: Can't hash safely, so raise TypeError. | ||
raise TypeError(f"Hashing for {type(obj)} not implemented.") | ||
|
||
|
||
def hash_(obj): | ||
"""Generic hash method, which changes between processes.""" | ||
digest = hash(repr(_to_hashable(obj))) + sys.maxsize + 1 | ||
return digest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
"""Test the hashing function of CCL.""" | ||
import pytest | ||
import pyccl as ccl | ||
import numpy as np | ||
from collections import OrderedDict | ||
|
||
OBJECTS = [ccl.Cosmology, # class | ||
(0, 1, 2), # tuple | ||
[0, 1, 2], # list | ||
set([0, 1, 2]), # set | ||
np.arange(3), # array | ||
{0: None, 1: None, 2: None}, # dict | ||
{0: None, 1: None, 2: {2.1: None, 2.2: None}}, # nested dict | ||
OrderedDict({0: None, 1: None, 2: None}), # OrderedDict | ||
ccl.CosmologyVanillaLCDM(), # something else | ||
None, # something else | ||
] | ||
|
||
|
||
@pytest.mark.parametrize("obj", OBJECTS) | ||
def test_hashing_smoke(obj): | ||
assert isinstance(ccl.hash_(obj), int) | ||
|
||
|
||
def test_hashing_large_array(): | ||
# Hashing ultimately uses the representation of the object. | ||
# The representation of large numpy arrays only contains the start | ||
# and the end. We check that the entire array is considered. | ||
array = np.random.random(64**3).reshape(64, 64, 64) | ||
array2 = array.copy() | ||
array2[31, 31, 31] += 1. # this is now the max value | ||
vmax = str(array2.max())[:6] | ||
assert vmax not in repr(array2) # make sure it doesn't show | ||
assert ccl.hash_(array) != ccl.hash_(array2) |