Skip to content

Commit 9f34bdd

Browse files
committed
Merge pull request #350 from dwhswenson/sr_minus_mover
Single Replica Minus Mover
2 parents 7c4acd7 + aa12f9d commit 9f34bdd

5 files changed

Lines changed: 258 additions & 13 deletions

File tree

‎openpathsampling/__init__.py‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@
2424
CV_Volume, CollectiveVariable
2525

2626
from pathmover import (
27-
BackwardShootMover, MinusMover, RandomChoiceMover, ForwardShootMover,
28-
PathMover, PathMoverFactory, PathReversalMover,
29-
ReplicaExchangeMover, ConditionalSequentialMover, EnsembleHopMover,
30-
PartialAcceptanceSequentialMover, ReplicaIDChangeMover, SequentialMover,
31-
ConditionalMover, RestrictToLastSampleMover, PathSimulatorMover,
32-
PathReversalSet, NeighborEnsembleReplicaExchange, SampleMover,
33-
StateSwapMover, FinalSubtrajectorySelectMover, EngineMover,
27+
RandomChoiceMover, PathMover, ConditionalSequentialMover,
28+
PartialAcceptanceSequentialMover, BackwardShootMover, ForwardShootMover,
29+
ShootMover, BackwardExtendMover, ForwardExtendMover, MinusMover,
30+
SingleReplicaMinusMover, PathMoverFactory, PathReversalMover,
31+
ReplicaExchangeMover, EnsembleHopMover, ReplicaIDChangeMover,
32+
SequentialMover, ConditionalMover, RestrictToLastSampleMover,
33+
PathSimulatorMover, PathReversalSet, NeighborEnsembleReplicaExchange,
34+
SampleMover, StateSwapMover, FinalSubtrajectorySelectMover, EngineMover,
3435
SwappingMover, FirstSubtrajectorySelectMover, MultipleSetMinusMover,
35-
OneWayShootingMover, RandomSubtrajectorySelectMover, ShootMover,
36-
SubPathMover, BackwardExtendMover, EnsembleFilterMover,
37-
ForwardExtendMover, SelectionMover, FirstAllowedMover, LastAllowedMover,
38-
OneWayExtendMover
36+
OneWayShootingMover, RandomSubtrajectorySelectMover, SubPathMover,
37+
EnsembleFilterMover, SelectionMover, FirstAllowedMover,
38+
LastAllowedMover, OneWayExtendMover
3939
)
4040

4141
from shooting import ShootingPoint, ShootingPointSelector, UniformSelector, \

‎openpathsampling/analysis/move_strategy.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,22 @@ def make_movers(self, scheme):
459459
return movers
460460

461461
class SingleReplicaMinusMoveStrategy(MinusMoveStrategy):
462-
pass
462+
"""
463+
Takes a given scheme and makes a single-replica minus mover.
464+
"""
465+
def make_movers(self, scheme):
466+
network = scheme.network
467+
ensemble_list = self.get_ensembles(scheme, self.ensembles)
468+
ensembles = reduce(list.__add__, map(lambda x: list(x), ensemble_list))
469+
movers = []
470+
for ens in ensembles:
471+
innermosts = [t.ensembles[0]
472+
for t in network.special_ensembles['minus'][ens]]
473+
movers.append(paths.SingleReplicaMinusMover(
474+
minus_ensemble=ens,
475+
innermost_ensembles=innermosts
476+
))
477+
return movers
463478

464479

465480
class OrganizeByMoveGroupStrategy(MoveStrategy):

‎openpathsampling/pathmover.py‎

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,6 @@ class MinusMover(SubPathMover):
18631863
_is_canonical = True
18641864

18651865
def __init__(self, minus_ensemble, innermost_ensembles):
1866-
18671866
try:
18681867
innermost_ensembles = list(innermost_ensembles)
18691868
except TypeError:
@@ -1925,6 +1924,63 @@ def __init__(self, minus_ensemble, innermost_ensembles):
19251924

19261925
super(MinusMover, self).__init__(mover)
19271926

1927+
class SingleReplicaMinusMover(MinusMover):
1928+
"""
1929+
Minus mover for single replica TIS.
1930+
1931+
In SRTIS, the minus mover doesn't actually keep an active sample in the
1932+
minus interface. Instead, it just puts the newly generated segment into
1933+
the innermost ensemble.
1934+
"""
1935+
def __init__(self, minus_ensemble, innermost_ensembles, bias=None):
1936+
try:
1937+
innermost_ensembles = list(innermost_ensembles)
1938+
except TypeError:
1939+
innermost_ensembles = [innermost_ensembles]
1940+
1941+
# TODO: Until we have automated detailed balance calculations, I
1942+
# think this will only be valid in the case of only one innermost
1943+
# ensemble. But I think you only want to use it in the case of only
1944+
# one innermost ensemble anyway. The following warns us:
1945+
if len(innermost_ensembles) > 1:
1946+
logger.warning("Probably shouldn't use SingleReplicaMinusMover with MISTIS")
1947+
1948+
segment = minus_ensemble._segment_ensemble
1949+
1950+
hop_innermost_to_segment = RandomAllowedChoiceMover([
1951+
EnsembleHopMover(innermost, segment, bias=bias)
1952+
for innermost in innermost_ensembles
1953+
])
1954+
1955+
# TODO: again, works for single interface set, but there has to be a
1956+
# smarter way to do this in the MISTIS case
1957+
hop_segment_to_innermost = RandomChoiceMover([
1958+
EnsembleHopMover(segment, innermost, bias=bias)
1959+
for innermost in innermost_ensembles
1960+
])
1961+
1962+
forward_minus = ConditionalSequentialMover([
1963+
hop_innermost_to_segment,
1964+
ForwardExtendMover(segment, minus_ensemble),
1965+
FinalSubtrajectorySelectMover(minus_ensemble, segment),
1966+
hop_segment_to_innermost
1967+
])
1968+
1969+
backward_minus = ConditionalSequentialMover([
1970+
hop_innermost_to_segment,
1971+
BackwardExtendMover(segment, minus_ensemble),
1972+
FirstSubtrajectorySelectMover(minus_ensemble, segment),
1973+
hop_segment_to_innermost
1974+
])
1975+
1976+
mover = EnsembleFilterMover(RandomChoiceMover([backward_minus,
1977+
forward_minus]),
1978+
ensembles=innermost_ensembles)
1979+
1980+
# we skip MinusMover's init and go to the grandparent
1981+
super(MinusMover, self).__init__(mover)
1982+
1983+
19281984

19291985
class PathSimulatorMover(SubPathMover):
19301986
"""

‎openpathsampling/tests/testmovestrategy.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,22 @@ def test_make_movers(self):
431431
)
432432

433433

434+
class testSingleReplicaMinusMoveStrategy(MoveStrategyTestSetup):
435+
def test_make_movers(self):
436+
strategy = SingleReplicaMinusMoveStrategy()
437+
scheme = MoveScheme(self.network)
438+
movers = strategy.make_movers(scheme)
439+
assert_equal(len(movers), 2)
440+
441+
minuses = self.network.special_ensembles['minus']
442+
ens_minusA = minuses.keys()[0]
443+
ens_innerA = [t.ensembles[0] for t in minuses[ens_minusA]]
444+
sig_A = set([ens_minusA] + ens_innerA)
445+
ens_minusB = minuses.keys()[1]
446+
ens_innerB = [t.ensembles[0] for t in minuses[ens_minusB]]
447+
sig_B = set([ens_minusB] + ens_innerB)
448+
all_ens_sigs = [m.ensemble_signature_set for m in movers]
449+
434450

435451
class testOrganizeByMoveGroupStrategy(MoveStrategyTestSetup):
436452
def scheme_setup_shooting_repex(self):

‎openpathsampling/tests/testpathmover.py‎

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,3 +1092,161 @@ def test_extension_fails(self):
10921092
len(sub[-1][0].trials[0].trajectory),
10931093
len(traj_bad_extension)+self.dyn.n_frames_max-1
10941094
)
1095+
1096+
1097+
class testSingleReplicaMinusMover(object):
1098+
def setup(self):
1099+
op = CV_Function("myid", fcn=lambda snap :
1100+
snap.coordinates[0][0])
1101+
1102+
volA = CVRangeVolume(op, -100, 0.0)
1103+
volB = CVRangeVolume(op, 1.0, 100)
1104+
volX = CVRangeVolume(op, -100, 0.25)
1105+
self.dyn = CalvinistDynamics([
1106+
# successful move: (backward extension then forward)
1107+
-0.13, 0.13, 0.33, -0.11, -0.12, 0.12, 0.32, -0.131,
1108+
# never leaves state:
1109+
-0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15,
1110+
-0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15,
1111+
-0.25,
1112+
-0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15,
1113+
-0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15, -0.15,
1114+
# goes to other state:
1115+
1.16, 1.26, 1.16, -0.16, 1.16, 1.26, 1.16
1116+
])
1117+
SampleMover.engine = self.dyn
1118+
self.dyn.initialized = True
1119+
self.innermost = paths.TISEnsemble(volA, volB, volX)
1120+
self.minus = paths.MinusInterfaceEnsemble(volA, volX)
1121+
self.mover = SingleReplicaMinusMover(
1122+
minus_ensemble=self.minus,
1123+
innermost_ensembles=self.innermost
1124+
)
1125+
self.first_segment = [-0.1, 0.1, 0.3, 0.1, -0.15]
1126+
self.list_innermost = [-0.11, 0.11, 0.31, 0.11, -0.12]
1127+
self.second_segment = [-0.25, 0.2, 0.4, 0.2, -0.2]
1128+
init_minus = make_1d_traj(
1129+
coordinates=self.first_segment + [-0.35] + self.second_segment,
1130+
velocities=[1.0]*11
1131+
)
1132+
self.minus_sample = Sample(
1133+
replica=-1,
1134+
trajectory=init_minus,
1135+
ensemble=self.minus
1136+
)
1137+
1138+
def test_is_ensemble_change_mover(self):
1139+
assert_equal(self.mover.is_ensemble_change_mover, True)
1140+
1141+
def test_successful_move(self):
1142+
init_innermost = make_1d_traj(self.list_innermost, [1.0]*5)
1143+
init_sample = Sample(
1144+
replica=0,
1145+
trajectory=init_innermost,
1146+
ensemble=self.innermost
1147+
)
1148+
gs = SampleSet([init_sample, self.minus_sample])
1149+
1150+
extend_forward = self.list_innermost + [0.12, 0.32, -0.131]
1151+
extend_backward = [-0.13, 0.13, 0.33] + self.list_innermost
1152+
1153+
assert_equal(self.minus(make_1d_traj(extend_forward)), True)
1154+
assert_equal(self.minus(make_1d_traj(extend_backward)), True)
1155+
1156+
output_forward = [-0.12, 0.12, 0.32, -0.131]
1157+
output_backward = [-0.13, 0.13, 0.33, -0.11]
1158+
1159+
seg_dir = {}
1160+
for i in range(100):
1161+
change = self.mover.move(gs)
1162+
samples = change.results
1163+
sub_samples = change.subchange.subchange.results
1164+
assert_equal(len(samples), 1)
1165+
assert_equal(len(sub_samples), 4)
1166+
s_inner = [s for s in sub_samples if s.ensemble==self.innermost]
1167+
s_minus = [s for s in sub_samples if s.ensemble==self.minus]
1168+
s_seg = [s for s in sub_samples if s.ensemble==self.minus._segment_ensemble]
1169+
assert_equal(len(s_inner), 1) # this is the output
1170+
assert_equal(len(s_minus), 1) # this is the minus version
1171+
assert_equal(len(s_seg), 2) # first the selected, then the final
1172+
1173+
for c in change:
1174+
assert_equal(c.accepted, True)
1175+
1176+
assert_equal(change.canonical.mover, self.mover)
1177+
1178+
key = ""
1179+
s_seg0_xvals = [s.coordinates[0,0] for s in s_seg[0].trajectory]
1180+
if items_equal(s_seg0_xvals, self.list_innermost):
1181+
key += "0"
1182+
else:
1183+
print "s_seg0_xvals:", s_seg0_xvals
1184+
raise RuntimeError("Chosen segment neither first nor last!")
1185+
1186+
# s_minus is the intermediate
1187+
s_minus_xvals = [s.coordinates[0,0] for s in s_minus[-1].trajectory]
1188+
if items_equal(s_minus_xvals, extend_forward):
1189+
key += "f"
1190+
assert_equal(
1191+
[s.coordinates[0,0] for s in s_inner[0].trajectory],
1192+
output_forward
1193+
)
1194+
elif items_equal(s_minus_xvals, extend_backward):
1195+
key += "b"
1196+
assert_equal(
1197+
[s.coordinates[0,0] for s in s_inner[0].trajectory],
1198+
output_backward
1199+
)
1200+
else:
1201+
print "s_minus_xvals:", s_minus_xvals
1202+
raise RuntimeError("Unexpected minus extension result!")
1203+
1204+
1205+
try:
1206+
seg_dir[key] += 1
1207+
except KeyError:
1208+
seg_dir[key] = 1
1209+
assert_equal(len(seg_dir.keys()), 2)
1210+
1211+
def test_first_hop_fails(self):
1212+
crossing_traj = make_1d_traj([-0.11, 0.11, 0.31, 1.01], [1.0]*4)
1213+
crossing_samp = Sample(replica=0, trajectory=crossing_traj,
1214+
ensemble=self.innermost)
1215+
gs = SampleSet([crossing_samp])
1216+
gs.sanity_check()
1217+
1218+
change = self.mover.move(gs)
1219+
assert_equal(change.accepted, False)
1220+
assert_equal(len(change.results), 0)
1221+
sub_trials = change.subchange.subchange.subchange.trials
1222+
assert_equal(len(sub_trials), 1)
1223+
assert_equal(sub_trials[0].trajectory, crossing_traj)
1224+
assert_equal(sub_trials[0].ensemble, self.minus._segment_ensemble)
1225+
1226+
def test_extension_fails(self):
1227+
innermost_bad_extension = [-0.25, 0.1, 0.5, 0.1, -0.25]
1228+
traj_bad_extension = make_1d_traj(innermost_bad_extension, [1.0]*5)
1229+
samp_bad_extension = Sample(
1230+
replica=0,
1231+
trajectory=traj_bad_extension,
1232+
ensemble=self.innermost
1233+
)
1234+
1235+
assert_equal(self.innermost(traj_bad_extension), True)
1236+
1237+
gs = SampleSet([self.minus_sample, samp_bad_extension])
1238+
change = self.mover.move(gs)
1239+
assert_equal(change.accepted, False) # whole minus has failed
1240+
1241+
# Minus : Filter :ChooseFB : CondSeq
1242+
sub = change.subchange.subchange.subchange
1243+
assert_equal(len(sub.trials), 2)
1244+
assert_equal(len(change.trials), 0) # no trials survive filtering
1245+
assert_subchanges_set_accepted(sub, [True, False])
1246+
1247+
# first two work and the extention fails
1248+
# this only happens due to length
1249+
assert_equal(
1250+
len(sub[-1].trials[0].trajectory),
1251+
len(traj_bad_extension)+self.dyn.n_frames_max-1
1252+
)

0 commit comments

Comments
 (0)