-
-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add hash function for AffineScalarFunc class #189
Changes from 15 commits
d90c541
5d429fe
40154ce
634db47
7cca18c
af3447c
f3cb615
5e40c49
cd3b7e0
a2d4bb1
d74a9d1
9a9d6d5
3e0b064
1e57a2f
e4ef9e1
4d8d268
605b5cd
82285df
3c64ae3
185aa0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1756,6 +1756,19 @@ def __bool__(self): | |
|
||
######################################## | ||
|
||
def __hash__(self): | ||
""" | ||
Calculates the hash for any AffineScalarFunc object. | ||
The hash is calculated from the nominal_value, and the derivatives. | ||
|
||
Returns: | ||
int: The hash of this object | ||
""" | ||
|
||
ids = [id(d) for d in self.derivatives.keys()] | ||
values_hash = tuple(self.derivatives.values()) | ||
return hash((self._nominal_value, tuple(ids), values_hash)) | ||
|
||
# Uncertainties handling: | ||
|
||
def error_components(self): | ||
|
@@ -2766,6 +2779,22 @@ def std_dev(self, std_dev): | |
|
||
self._std_dev = float(std_dev) | ||
|
||
def __hash__(self): | ||
""" | ||
Calculates the hash for any Variable object. | ||
This simply calls the __hash__ method of `AffineScalarFunc` in most cases. | ||
During initialization, the `_linear_part` and `_nominal_value` attributes does not exist. | ||
Because of that, the id returned in case this method is called during initialization. | ||
|
||
Returns: | ||
int: The hash of this object | ||
""" | ||
NelDav marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if hasattr(self, '_linear_part') and hasattr(self, '_nominal_value'): | ||
return super().__hash__() | ||
else: | ||
return id(self) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test failure is coming from this part.
and update It is tempting to try to remove the self-reference but I have seen another case in a symbolic algebra library where a set up similar to this one was the best option. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are totally right, the hash must be the same every time. Not sure, how I could miss that. |
||
|
||
# The following method is overridden so that we can represent the tag: | ||
def __repr__(self): | ||
|
||
|
@@ -2776,13 +2805,6 @@ def __repr__(self): | |
else: | ||
return "< %s = %s >" % (self.tag, num_repr) | ||
|
||
def __hash__(self): | ||
# All Variable objects are by definition independent | ||
# variables, so they never compare equal; therefore, their | ||
# id() are allowed to differ | ||
# (http://docs.python.org/reference/datamodel.html#object.__hash__): | ||
return id(self) | ||
|
||
def __copy__(self): | ||
""" | ||
Hook for the standard copy module. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add a case to
test_hash
likeThat case fails for me with this implementation but the implementation is dependent on the iteration order of
derivatives
which depends on in the insertion order, which can vary depending on the order in which operations are performed. Changing the tuples tofrozenset
or sorting the values before casting totuple
could remove the order dependence.Also, we could define
__hash__
onLinearCombination
and use that here instead of constructing it fromderivatives
directly. Perhaps there is no use for that though.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth trying implementations that cover obvious use cases (such as z_xy == z_yx) and using assertions to raise if we've gone past the limit of our assumptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm the problem of a forzenset might be, that the elements of a set must be unique right?
The
ids
are unique, but the values might not.I will try to create a test for that...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the example:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am using sorted tuples now, to avoid this problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a blocking problem for the implementation of the
__hash__
. It is okay if unequal objects hash to the same value. Usingfrozenset
would make it a little more likely to get hash collisions, but only in the case of multiple error terms with exactly the same value. Still, sorting should work just as well for the purpose of producing a deterministic hash. In the case of giant calculations with many error terms maybe sorting is slower than keeping a set, but in practice usually only a handful of variables are involved and sorting should be about as quick as creating a set.