6 from numpy.random
import random, randint
7 kB = 1.3806503 * 6.0221415 / 4184.0
12 def __init__(self, nreps, inv_temps, grid, sfo_id,
13 rexlog=
'replicanums.txt', scheme=
'standard', xchg=
'random',
14 convectivelog=
'stirred.txt', tune_temps=
False,
15 tune_data={}, templog=
'temps.txt'):
18 self.replicanums = list(range(nreps))
20 self.statenums = list(range(nreps))
24 self.inv_temps = inv_temps
29 self.tune_temps = tune_temps
30 self.tune_data = tune_data
32 self.templog = templog
34 if scheme ==
"convective":
37 self.stirred[
'order'] = list(range(self.nreps))
38 self.stirred[
'order'].reverse()
40 self.stirred[
'replica'] = self.stirred[
'order'][0]
42 self.stirred[
'pos'] = 0
44 if self.stirred[
'replica'] != self.nreps - 1:
45 self.stirred[
'dir'] = 1
47 self.stirred[
'dir'] = 0
49 self.stirred[
'steps'] = 2 * (self.nreps - 1)
50 self.convectivelog = convectivelog
51 self.write_rex_stats()
53 def sort_per_state(self, inplist):
54 "sorts a replica list per state"
55 if len(inplist) != self.nreps:
56 raise ValueError(
'list has wrong size')
57 return [inplist[i]
for i
in self.replicanums]
59 def sort_per_replica(self, inplist):
60 "sorts a state list per replica number"
61 if len(inplist) != self.nreps:
62 raise ValueError(
'list has wrong size')
63 return [inplist[i]
for i
in self.statenums]
65 def get_energies(self):
66 "return replica-sorted energies"
67 return self.grid.gather(
68 self.grid.broadcast(self.sfo_id,
'm',
'evaluate',
False))
70 def gen_pairs_list_gromacs(self, direction):
71 """generate ordered list of exchange pairs based on direction.
72 direction == 0 : (0,1)(2,3)...
73 direction == 1 : (0(1,2)(3,4)...
77 if direction != 0
and direction != 1:
78 raise ValueError(direction)
81 ret = [(2 * i + direction, 2 * i + 1 + direction)
82 for i
in range(nreps // 2)]
87 def gen_pairs_list_rand(self, needed=[]):
88 "generate list of neighboring pairs of states"
92 init = [(i, i + 1)
for i
in range(nreps - 1)]
94 init.append((0, nreps - 1))
98 raise ValueError(
"wrong format for 'needed' list")
99 pairslist.append((i, i + 1))
100 init.remove((i - 1, i))
101 init.remove((i, i + 1))
102 init.remove((i + 1, i + 2))
105 i = randint(0, len(init))
110 init = [(r, q)
for (r, q)
in init
111 if (r
not in pair
and q
not in pair)]
114 if not pair == (0, nreps - 1):
115 pairslist.append(pair)
120 def gen_pairs_list_conv(self):
121 rep = self.stirred[
'replica']
122 state = self.statenums[rep]
123 pair = sorted([state, state + 2 * self.stirred[
'dir'] - 1])
124 self.stirred[
'pair'] = tuple(pair)
125 if self.xchg ==
'gromacs':
126 dir = (state + 1 + self.stirred[
'dir']) % 2
127 return self.gen_pairs_list_gromacs(dir)
128 elif self.xchg ==
'random':
129 return self.gen_pairs_list_rand(needed=[self.stirred[
'pair']])
131 raise NotImplementedError(
132 "Unknown exchange method: %s" %
135 def gen_pairs_list(self):
136 if self.scheme ==
'standard':
137 if self.xchg ==
'gromacs':
138 return self.gen_pairs_list_gromacs(self.stepno % 2)
139 elif self.xchg ==
'random':
140 return self.gen_pairs_list_rand()
142 raise NotImplementedError(
143 "unknown exchange method: %s" %
145 elif self.scheme ==
'convective':
146 return self.gen_pairs_list_conv()
148 raise NotImplementedError(
149 "unknown exchange scheme: %s" %
152 def get_cross_energies(self, pairslist):
153 "get energies assuming all exchanges have succeeded"
154 print(
"this is not needed for temperature replica-exchange")
155 raise NotImplementedError
157 def get_metropolis(self, pairslist, old_ene):
158 """compute metropolis criterion for temperature replica exchange
159 e.g. exp(Delta beta Delta E)
160 input: list of pairs, list of state-sorted energies
163 for (s1, s2)
in pairslist:
165 min(1, np.exp((old_ene[s2] - old_ene[s1])
166 * (self.inv_temps[s2] - self.inv_temps[s1])))
169 def try_exchanges(self, plist, metrop):
172 if (metrop[couple] >= 1)
or (random() < metrop[couple]):
173 accepted.append(couple)
176 def perform_exchanges(self, accepted):
177 "exchange given state couples both in local variables and on the grid"
179 for (i, j)
in accepted:
181 ri = self.replicanums[i]
182 rj = self.replicanums[j]
183 self.statenums[ri] = j
184 self.statenums[rj] = i
186 buf = self.replicanums[i]
187 self.replicanums[i] = self.replicanums[j]
188 self.replicanums[j] = buf
190 newtemps = self.sort_per_replica(self.inv_temps)
191 states = self.grid.gather(
192 self.grid.broadcast(self.sfo_id,
'get_state'))
193 for (i, j)
in accepted:
194 ri = self.replicanums[i]
195 rj = self.replicanums[j]
197 states[ri] = states[rj]
199 for temp, state
in zip(newtemps, states):
200 state[
'inv_temp'] = temp
202 self.grid.scatter(self.sfo_id,
'set_state', states))
204 def write_rex_stats(self):
205 "write replica numbers as a function of state"
206 fl = open(self.logfile,
'a')
207 fl.write(
'%8d ' % self.stepno)
208 fl.write(
' '.join([
'%2d' % (i + 1)
for i
in self.replicanums]))
211 if self.scheme ==
'convective':
212 fl = open(self.convectivelog,
'a')
213 fl.write(
'%5d ' % self.stepno)
214 fl.write(
'%2d ' % (self.stirred[
'replica'] + 1))
215 fl.write(
'%2d ' % self.stirred[
'dir'])
216 fl.write(
'%2d\n' % self.stirred[
'steps'])
218 if self.tune_temps
and len(self.rn_history) == 1:
219 fl = open(self.templog,
'a')
220 fl.write(
'%5d ' % self.stepno)
221 fl.write(
' '.join([
'%.3f' % i
for i
in self.inv_temps]))
226 """use TuneRex to optimize temp set. Temps are optimized every
227 'rate' steps and 'method' is used. Data is accumulated as long as
228 the temperatures weren't optimized.
229 td keys that should be passed to init:
230 rate : the rate at which to try tuning temps
232 targetAR : for ar only, target acceptance rate
233 alpha : Type I error to use.
237 self.rn_history.append([i
for i
in self.replicanums])
239 if len(self.rn_history) % td[
'rate'] == 0\
240 and len(self.rn_history) > 0:
241 temps = [1 / (kB * la)
for la
in self.inv_temps]
243 if td[
'method'] ==
'ar':
245 kwargs[
'targetAR'] = td[
'targetAR']
247 kwargs[
'alpha'] = td[
'alpha']
248 if 'dumb_scale' in td:
249 kwargs[
'dumb_scale'] = td[
'dumb_scale']
250 indicators = TuneRex.compute_indicators(
251 np.transpose(self.rn_history))
252 changed, newparams = TuneRex.tune_params_ar(
253 indicators, temps, **kwargs)
254 elif td[
'method'] ==
'flux':
256 kwargs[
'alpha'] = td[
'alpha']
257 changed, newparams = TuneRex.tune_params_flux(
258 np.transpose(self.rn_history),
263 self.inv_temps = [1 / (kB * t)
for t
in newparams]
265 def do_bookkeeping_before(self):
267 if self.scheme ==
'convective':
271 st[
'pos'] = (st[
'pos'] + 1) % self.nreps
272 st[
'replica'] = st[
'order'][st[
'pos']]
274 st[
'steps'] = 2 * (self.nreps - 1)
276 state = self.statenums[rep]
278 if state == self.nreps - 1:
284 def do_bookkeeping_after(self, accepted):
285 if self.scheme ==
'convective':
290 if self.stirred[
'pair']
in accepted:
291 self.stirred[
'steps'] -= 1
293 def replica_exchange(self):
294 "main entry point for replica-exchange"
296 self.do_bookkeeping_before()
301 energies = self.sort_per_state(self.get_energies())
303 plist = self.gen_pairs_list()
305 metrop = self.get_metropolis(plist, energies)
307 accepted = self.try_exchanges(plist, metrop)
309 self.perform_exchanges(accepted)
311 self.do_bookkeeping_after(accepted)
def __init__
input a list of particles, the slope and theta of the sigmoid potential theta is the cutoff distance ...