Skip to content

Add parameterhandler.hash #2077

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions openff/toolkit/_tests/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,53 @@ class MyParameterHandler(ParameterHandler):
):
handler.create_force()

def test_hash(self):
bh = BondHandler(skip_version_check=True, allow_cosmetic_attributes=True)

hash1 = hash(bh)
# Ensure hash changes when a new parameter is added
bh.add_parameter(
{
"smirks": "[*:1]-[*:2]",
"length": 1 * unit.angstrom,
"k": 10 * unit.kilocalorie / unit.mole / unit.angstrom ** 2,
"id": "b0",
}
)
hash2 = hash(bh)
assert hash1 != hash2

# Ensure hash changes when another parameter that differs only by SMIRKS is added
bh.add_parameter(
{
"smirks": "[C:1]-[C:2]",
"length": 1 * unit.angstrom,
"k": 10 * unit.kilocalorie / unit.mole / unit.angstrom ** 2,
"id": "b0",
}
)
hash3 = hash(bh)
assert hash2 != hash3

bh.add_cosmetic_attribute("fizz", "buzz")
hash3p5 = hash(bh)
assert hash3 != hash3p5

# Ensure hash changes when a cosmetic attribute is added
bh.parameters[0].add_cosmetic_attribute("foo", "bar")
hash4 = hash(bh)
assert hash3p5 != hash4

# Ensure hash changes when parameters are reordered
param = bh.parameters.pop(0)
bh.parameters.append(param)
hash5 = hash(bh)
assert hash4 != hash5

# Ensure hash doesn't change when the contents haven't changed
hash6 = hash(bh)
assert hash5 == hash6


class TestParameterList:
"""Test capabilities of ParameterList for accessing and manipulating SMIRNOFF parameter definitions."""
Expand Down
32 changes: 32 additions & 0 deletions openff/toolkit/typing/engines/smirnoff/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,38 @@ def __init__(
# Initialize ParameterAttributes and cosmetic attributes.
super().__init__(allow_cosmetic_attributes=allow_cosmetic_attributes, **kwargs)

def __hash__(self):
"""
Hash a ParameterHandler and all of its contents (INCLUDING cosmetic attributes).

This method does not attempt to return the same hash for ParameterHandlers with equivalent
physics/chemistry but different cosmetic attributes or units. Instead this is a hash of all
of the ParameterHandler's contents, even if they don't affect system creation in any way.
"""
handler_string = ''
attribute_dict = self.__dict__
for key, val in attribute_dict.items():
if isinstance(val, ParameterList):
handler_string += f'___{key}'
for parameter in val:
# print(parameter)
for attribute_name, attribute_val in parameter.__dict__.items():
# print(attribute_name, attribute_val)
if isinstance(attribute_val, list):
#print(attribute_val)
if len(attribute_val) > 0 and isinstance(attribute_val[0], Quantity):
attribute_val = tuple([(i.m, hash(i.units)) for i in attribute_val])
#else:
if isinstance(attribute_val, Quantity):
# print(dir(attribute_val))
attribute_val = (attribute_val.m, hash(attribute_val.units))
# break
handler_string += f'__{attribute_name}_{attribute_val}'
else:
handler_string += f'{key}__{val}'

return hash(handler_string)

def _add_parameters(self, section_dict, allow_cosmetic_attributes=False):
"""
Extend the ParameterList in this ParameterHandler using a SMIRNOFF data source.
Expand Down