import math
import logging
import numpy as np
from openpathsampling.netcdfplus import StorableNamedObject
logger = logging.getLogger(__name__)
init_log = logging.getLogger('openpathsampling.initialization')
class ShootingPointSelector(StorableNamedObject):
def __init__(self):
super(ShootingPointSelector, self).__init__()
@property
def identifier(self):
if hasattr(self, 'json'):
return self.json
else:
return None
def f(self, snapshot, trajectory):
'''
Returns the unnormalized proposal probability of a snapshot
Notes
-----
In principle this is an collectivevariable so we could easily add
caching if useful
'''
return 1.0
def probability(self, snapshot, trajectory):
sum_bias = self.sum_bias(trajectory)
if sum_bias > 0.0:
return self.f(snapshot, trajectory) / sum_bias
else:
return 0.0
def probability_ratio(self, snapshot, old_trajectory, new_trajectory):
p_old = self.probability(snapshot, old_trajectory)
p_new = self.probability(snapshot, new_trajectory)
return p_new / p_old
def _biases(self, trajectory):
'''
Returns a list of unnormalized proposal probabilities for all
snapshots in trajectory
'''
return [self.f(s, trajectory) for s in trajectory]
def sum_bias(self, trajectory):
'''
Returns the unnormalized probability probability of a trajectory.
This is just the sum of all proposal probabilities in a trajectory.
Notes
-----
For a uniform distribution this is proportional to the length of the
trajectory. In this case we can estimate the maximal accepted
trajectory length for a given acceptance probability.
After we have generated a new trajectory the acceptance probability
only for the non-symmetric proposal of different snapshots is given
by `probability(old_trajectory) / probability(new_trajectory)`
'''
return sum(self._biases(trajectory))
def pick(self, trajectory):
'''
Returns the index of the chosen snapshot within `trajectory`
Notes
-----
The native implementation is very slow. Simple picking algorithm
should override this function.
'''
prob_list = self._biases(trajectory)
sum_bias = sum(prob_list)
rand = np.random.random() * sum_bias
idx = 0
prob = prob_list[0]
while prob <= rand and idx < len(prob_list):
idx += 1
prob += prob_list[idx]
return idx
class GaussianBiasSelector(ShootingPointSelector):
def __init__(self, collectivevariable, alpha=1.0, l_0=0.5):
'''
A Selector that biases according to a specified CollectiveVariable
using a mean l_0 and a variance alpha
'''
super(GaussianBiasSelector, self).__init__()
self.collectivevariable = collectivevariable
self.alpha = alpha
self.l_0 = l_0
def f(self, snapshot, trajectory):
l_s = self.collectivevariable(snapshot)
return math.exp(-self.alpha * (l_s - self.l_0) ** 2)
[docs]class FinalFrameSelector(ShootingPointSelector):
'''
Pick final trajectory frame as shooting point.
This is used for "forward" extension in, e.g., the minus move.
'''
def f(self, frame, trajectory):
if trajectory.index(frame) == len(trajectory) - 1:
return 1.0
else:
return 0.0
def pick(self, trajectory):
return len(trajectory)-1
def probability(self, snapshot, trajectory):
return 1.0 # there's only one choice
def probability_ratio(self, snapshot, old_trajectory, new_trajectory):
# must be matched by a final-frame selector somewhere
return 1.0
[docs]class FirstFrameSelector(ShootingPointSelector):
'''
Pick first trajectory frame as shooting point.
This is used for "backward" extension in, e.g., the minus move.
'''
def f(self, frame, trajectory):
if trajectory.index(frame) == 0:
return 1.0
else:
return 0.0
def pick(self, trajectory):
return 0
def probability(self, snapshot, trajectory):
return 1.0 # there's only one choice
def probability_ratio(self, snapshot, old_trajectory, new_trajectory):
# must be matched by a first-frame selector somewhere
return 1.0