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 ...