Take care of coordinate modifcation in selector probability_ratio #1076
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1076 +/- ##
=======================================
Coverage 81.58% 81.59%
=======================================
Files 140 140
Lines 15416 15427 +11
=======================================
+ Hits 12577 12587 +10
- Misses 2839 2840 +1
Continue to review full report at Codecov.
|
This combination of facts seems like a bug to me, although I don't see them in the code. Where are you getting this from? I don't see a reimplementation of Before a detailed line-by-line review, I'd also like to see a test where you move the snapshot so that it does not cross the interface. In that case, you should get 0 acceptance probability (because there is no possibility of choosing this snapshot in the reverse move). I'm fine with this actually calculating the (wasted) dynamics and giving zero acceptance, because that's a use case the users have to seriously try hard to reach (choose interface-constrained shooting with two-way shooting with a modifier that changes coordinates). However, it's worth keeping this in mind in the context of follow-ups to #1075, in particular anything that deals with early rejection in the sense of #794 (comment). (My thought is to calculate the bias components in the |
I checked it and it is based on I definetly misremembered the hashes being determined by the coordinates for snapshot. Was this ever a plan in an early version of OPS? (Don't know where I got this from otherwise, might have dreamt it up 🤷♂️ ) I was also quite certain at least some fast things inside python were done based on hashes (why would for example >>> class b():
... def __hash__(self):
... return 1
...
>>> a = b()
>>> c = b()
>>> a == c
False
>>> hash(c)
1
>>> hash(a)
1
>>> d = {c, a}
>>> d
{<__main__.b object at 0x7f5d85599a90>, <__main__.b object at 0x7f5d85599b50>}
>>> d = {c: "foo", a: "bar"}
>>> d
{<__main__.b object at 0x7f5d85599a90>: 'foo', <__main__.b object at 0x7f5d85599b50>: 'bar'}
>>> d[c]
'foo'
>>> d[a]
'bar' |
I just posted a gist from some of my own experiments. See also https://hynek.me/articles/hashes-and-equality/. TL;DR: I'm planning to post an issue soon about hashing and equality changes for OPS 2.0, which need to happen for several reasons, but I'll be looking for feedback on details (and help catching edge cases I hadn't considered). |
Done
Sounds reasonable, we should probably add some |
Intriguing, thanks for the write up! |
dwhswenson
left a comment
There was a problem hiding this comment.
Should we update the call signature of this function to
(old_snapshot, old_trajectory, new_snapshot, new_trajectory)in 2.0
Yes (in a PR to 2.0, obviously)....
and raise a Deprecation warning if new_snapshot is not defined for 1.X?
... and yes (in this PR). That's the only change I see a need for; otherwise LGTM!
Done, please have another look |
dwhswenson
left a comment
There was a problem hiding this comment.
One question as an aside, but this looks good. Will merge!
| # TODO OPS 2.0: We should probabily alter the order and keyword names | ||
| # of this call | ||
| if new_snapshot is None: | ||
| NEW_SNAPSHOT_SELECTOR.warn(stacklevel=3) |
There was a problem hiding this comment.
I notice you've been setting stacklevel=3 in these -- should the default stacklevel=2 be changed?
There was a problem hiding this comment.
Maybe. When I run py.test on these with stacklevel=2 I get
openpathsampling/tests/test_external_engine.py::TestExternalEngine::test_in_shooting_move
/home/sander/github_files/openpathsampling/openpathsampling/shooting.py:41: DeprecationWarning: new_snapshot=None; If snapshot has been copied or modified we can't find it in trial_trajectory. This call signature will update to (old_snapshot, old_trajectory, new_snapshot, new_trajectory) in OpenPathSampling 2.0. Call with kwargs and use new_snapshot=old_snapshot if old_snapshot is not copied or modified in new_traj
NEW_SNAPSHOT_SELECTOR.warn(stacklevel=2)
which is a lot less useful (in my opinion) than with stacklevel=3:
openpathsampling/tests/test_external_engine.py::TestExternalEngine::test_in_shooting_move
/home/sander/github_files/openpathsampling/openpathsampling/pathmover.py:823: DeprecationWarning: new_snapshot=None; If snapshot has been copied or modified we can't find it in trial_trajectory. This call signature will update to (old_snapshot, old_trajectory, new_snapshot, new_trajectory) in OpenPathSampling 2.0. Call with kwargs and use new_snapshot=old_snapshot if old_snapshot is not copied or modified in new_traj
bias = self.selector.probability_ratio(
So we should definetly default to stacklevel=3 for deprecations were we want people to update their calls.
(As a side note, this is stacklevel=1, which is why the default might be slightly off:
/home/sander/github_files/openpathsampling/openpathsampling/deprecations.py:62: DeprecationWarning: new_snapshot=None; If snapshot has been copied or modified we can't find it in trial_trajectory. This call signature will update to (old_snapshot, old_trajectory, new_snapshot, new_trajectory) in OpenPathSampling 2.0. Call with kwargs and use new_snapshot=old_snapshot if old_snapshot is not copied or modified in new_traj
warnings.warn(self.message, category, stacklevel=stacklevel)
From writing down the code in this comment I noticed that selector.probability_ratio might not properly work for snapshots that get their coordinates altered.
How does this fail?
selector.probability_ratiotakes a frame, normally the original selected shooting point, the original trajectory, and a new trajectoryValueErrorif the coordinates were modifiedWhy is the ValueError raised when you alter the coordinates, but not with velocity modifications?
Trajectoryobjects are essentiallylists and inherits their way of checking for 'index'Trajectory.index(snapshot)looks at thehashes of all internalSnapshotsand compares them againsthash(snapshot)(Not true; in general it checks
==, which for all objects in OPS this checks their__uuid__(which is also used as part of the hash))(This is not true and based on me misremembering things)hash(snapshot)is determined by the coordinates -> identical coordinates generate identical hashesaltering the coordinates of a(This is not true and based on me misremembering things)snapshotalters thehashthe hash of original frame does therefor is not found in the altered trajectory, leading to a failure of theindexcallWhy is this only an issue for this selector
Solution
This PR adds a keyword argument
new_snapshottoprobability_ratio, which defaults tosnapshotif not given for all internal OPS selectors.Possible OPS 2.0 alterations
@dwhswenson Should we update the call signature of this function to
(old_snapshot, old_trajectory, new_snapshot, new_trajectory)in 2.0 and raise a Deprecation warning if new_snapshot is not defined for 1.X?The integration of calling the probability ratio with the new snapshot inside the
EngineMoverwill be done as part of #1075