IMP  2.3.0
The Integrative Modeling Platform
samplers.py
1 """@namespace IMP.pmi.samplers
2  Sampling of the system.
3 """
4 
5 import IMP
6 import IMP.core
7 
8 class _SerialReplicaExchange(object):
9  """Dummy replica exchange class used in non-MPI builds.
10  It should act similarly to IMP.mpi.ReplicaExchange on a single processor.
11  """
12  def __init__(self):
13  self.__params = {}
14  def get_number_of_replicas(self):
15  return 1
16  def create_temperatures(self, tmin, tmax, nrep):
17  return [tmin]
18  def get_my_index(self):
19  return 0
20  def set_my_parameter(self, key, val):
21  self.__params[key] = val
22  def get_my_parameter(self, key):
23  return self.__params[key]
24  def get_friend_index(self, step):
25  return 0
26  def get_friend_parameter(self, key, findex):
27  return self.get_my_parameter(key)
28  def do_exchange(self, myscore, fscore, findex):
29  return False
30 
31 
32 class MonteCarlo(object):
33 
34  # check that isd is installed
35  try:
36  import IMP.isd
37  isd_available = True
38  except ImportError:
39  isd_available = False
40 
41  def __init__(self, m, objects, temp, filterbyname=None):
42  '''
43  check that the objects containts get_particles_to_sample methods
44  and the particle type is supported
45  list of particles to sample self.losp
46  '''
47 
48  self.losp = [
49  "Rigid_Bodies",
50  "Floppy_Bodies",
51  "Nuisances",
52  "X_coord",
53  "Weights"]
54  self.simulated_annealing = False
55  self.selfadaptive = False
56  # that is -1 because mc has not yet run
57  self.nframe = -1
58  self.temp = temp
59  self.mvs = []
60  self.mvslabels = []
61  self.label = "None"
62  self.m = m
63 
64  for ob in objects:
65  try:
66  ob.get_particles_to_sample()
67  except:
68  print "MonteCarlo: object ", ob, " doesn't have get_particles_to_sample() method"
69 
70  pts = ob.get_particles_to_sample()
71  for k in pts.keys():
72 
73  if "Rigid_Bodies" in k:
74  mvs = self.get_rigid_body_movers(
75  pts[k][0],
76  pts[k][1],
77  pts[k][2])
78  for mv in mvs:
79  mv.set_name(k)
80  self.mvs += mvs
81 
82  if "SR_Bodies" in k:
83  print len(pts[k])
84  mvs = self.get_super_rigid_body_movers(
85  pts[k][0],
86  pts[k][1],
87  pts[k][2])
88  for mv in mvs:
89  mv.set_name(k)
90  self.mvs += mvs
91 
92  if "Floppy_Bodies" in k:
93  mvs = self.get_floppy_body_movers(pts[k][0], pts[k][1])
94  for mv in mvs:
95  mv.set_name(k)
96  self.mvs += mvs
97 
98  if "X_coord" in k:
99  mvs = self.get_X_movers(pts[k][0], pts[k][1])
100  for mv in mvs:
101  mv.set_name(k)
102  self.mvs += mvs
103 
104  if "Nuisances" in k:
105  if not self.isd_available:
106  print "MonteCarlo: isd module needed to use nuisances"
107  exit()
108  mvs = self.get_nuisance_movers(pts[k][0], pts[k][1])
109  for mv in mvs:
110  mv.set_name(k)
111  self.mvs += mvs
112 
113  if "Weights" in k:
114  if not self.isd_available:
115  print "MonteCarlo: isd module needed to use weights"
116  exit()
117  mvs = self.get_weight_movers(pts[k][0], pts[k][1])
118  for mv in mvs:
119  mv.set_name(k)
120  self.mvs += mvs
121 
122  # SerialMover
123  self.smv = IMP.core.SerialMover(self.mvs)
124 
125  self.mc = IMP.core.MonteCarlo(self.m)
126  self.mc.set_return_best(False)
127  self.mc.set_kt(self.temp)
128  self.mc.add_mover(self.smv)
129 
130  def set_kt(self, temp):
131  self.temp = temp
132  self.mc.set_kt(temp)
133 
134  def get_mc(self):
135  return self.mc
136 
137  def set_scoring_function(self, objectlist):
138  rs = IMP.RestraintSet(self.m, 1.0, 'sfo')
139  for ob in objectlist:
140  rs.add_restraint(ob.get_restraint())
142  self.mc.set_scoring_function(sf)
143 
144  def set_simulated_annealing(
145  self,
146  min_temp,
147  max_temp,
148  min_temp_time,
149  max_temp_time):
150  self.simulated_annealing = True
151  self.tempmin = min_temp
152  self.tempmax = max_temp
153  self.timemin = min_temp_time
154  self.timemax = max_temp_time
155 
156  def set_self_adaptive(self, isselfadaptive=True):
157  self.selfadaptive = isselfadaptive
158 
159  def get_nuisance_movers_parameters(self):
160  '''returns a dictionary with the mover parameters
161  for nuisance parameters'''
162  output = {}
163  for i in range(self.get_number_of_movers()):
164  mv = self.smv.get_mover(i)
165  name = mv.get_name()
166  if "Nuisances" in name:
167  stepsize = IMP.core.NormalMover.get_from(mv).get_sigma()
168  output[name] = stepsize
169  return output
170 
171  def get_number_of_movers(self):
172  return len(self.smv.get_movers())
173 
174  def get_particle_types():
175  return self.losp
176 
177  def optimize(self, nstep):
178  self.nframe += 1
179  self.mc.optimize(nstep * self.get_number_of_movers())
180 
181  # apply simulated annealing protocol
182  if self.simulated_annealing:
183  self.temp = self.temp_simulated_annealing()
184  self.mc.set_kt(self.temp)
185 
186  # apply self adaptive protocol
187  if self.selfadaptive:
188  for i, mv in enumerate(self.smv.get_movers()):
189  name = mv.get_name()
190 
191  if "Nuisances" in name:
192  mvacc = mv.get_number_of_accepted()
193  mvprp = mv.get_number_of_proposed()
194  accept = float(mvacc) / float(mvprp)
195  nmv = IMP.core.NormalMover.get_from(mv)
196  stepsize = nmv.get_sigma()
197 
198  if 0.4 > accept or accept > 0.6:
199  nmv.set_sigma(stepsize * 2 * accept)
200  if accept < 0.05:
201  accept = 0.05
202  nmv.set_sigma(stepsize * 2 * accept)
203  if accept > 1.0:
204  accept = 1.0
205  nmv.set_sigma(stepsize * 2 * accept)
206 
207  if "Weights" in name:
208 
209  mvacc = mv.get_number_of_accepted()
210  mvprp = mv.get_number_of_proposed()
211  accept = float(mvacc) / float(mvprp)
212  wmv = IMP.isd.WeightMover.get_from(mv)
213  stepsize = wmv.get_radius()
214 
215  if 0.4 > accept or accept > 0.6:
216  wmv.set_radius(stepsize * 2 * accept)
217  if accept < 0.05:
218  accept = 0.05
219  wmv.set_radius(stepsize * 2 * accept)
220  if accept > 1.0:
221  accept = 1.0
222  wmv.set_radius(stepsize * 2 * accept)
223 
224  def run(self, *args, **kwargs):
225  IMP.pmi.tools.print_deprecation_warning(
226  "MonteCarlo.run",
227  "MonteCarlo.optimize")
228  self.optimize(*args, **kwargs)
229 
230  def get_nuisance_movers(self, nuisances, maxstep):
231  mvs = []
232  for nuisance in nuisances:
233  print nuisance, maxstep
234  mvs.append(
235  IMP.core.NormalMover([nuisance],
236  IMP.FloatKeys([IMP.FloatKey("nuisance")]),
237  maxstep))
238  return mvs
239 
240  def get_rigid_body_movers(self, rbs, maxtrans, maxrot):
241  mvs = []
242  for rb in rbs:
243  mvs.append(IMP.core.RigidBodyMover(rb, maxtrans, maxrot))
244  return mvs
245 
246  def get_super_rigid_body_movers(self, rbs, maxtrans, maxrot):
247  mvs = []
248  for rb in rbs:
249  if len(rb) == 2:
250  # normal Super Rigid Body
251  srbm = IMP.pmi.TransformMover(self.m, maxtrans, maxrot)
252  if len(rb) == 3:
253  # super rigid body with 2D rotation, rb[2] is the axis
254  srbm = IMP.pmi.TransformMover(
255  self.m,
256  IMP.algebra.Vector3D(rb[2]),
257  maxtrans,
258  maxrot)
259  for xyz in rb[0]:
260  srbm.add_xyz_particle(xyz)
261  for rb in rb[1]:
262  srbm.add_rigid_body_particle(rb)
263  mvs.append(srbm)
264  return mvs
265 
266  def get_floppy_body_movers(self, fbs, maxtrans):
267  mvs = []
268  for fb in fbs:
269  # check is that is a rigid body member:
271  # if so force the particles to move anyway
272  floatkeys = [IMP.FloatKey(4), IMP.FloatKey(5), IMP.FloatKey(6)]
273  for fk in floatkeys:
274  fb.set_is_optimized(fk, True)
275  mvs.append(
276  IMP.core.BallMover([fb],
277  IMP.FloatKeys(floatkeys),
278  maxtrans))
279  else:
280  # otherwise use the normal ball mover
281  mvs.append(IMP.core.BallMover([fb], maxtrans))
282  return mvs
283 
284  def get_X_movers(self, fbs, maxtrans):
285  mvs = []
286  Xfloatkey = IMP.core.XYZ.get_xyz_keys()[0]
287  for fb in fbs:
288  # check is that is a rigid body member:
290  print "particle is part of a rigid body"
291  exit()
292  else:
293  # otherwise use the normal ball mover
294  mvs.append(IMP.core.NormalMover([fb], [Xfloatkey], maxtrans))
295  return mvs
296 
297  def get_weight_movers(self, weights, maxstep):
298  mvs = []
299  for weight in weights:
300  if(weight.get_number_of_states() > 1):
301  mvs.append(IMP.isd.WeightMover(weight, maxstep))
302  return mvs
303 
304  def temp_simulated_annealing(self):
305  if self.nframe % (self.timemin + self.timemax) < self.timemin:
306  value = 0.0
307  else:
308  value = 1.0
309  temp = self.tempmin + (self.tempmax - self.tempmin) * value
310  return temp
311 
312  def set_label(self, label):
313  self.label = label
314 
315  def get_frame_number(self):
316  return self.nframe
317 
318  def get_output(self):
319  output = {}
320  acceptances = []
321  for i, mv in enumerate(self.smv.get_movers()):
322  mvname = mv.get_name()
323  mvacc = mv.get_number_of_accepted()
324  mvprp = mv.get_number_of_proposed()
325  try:
326  mvacr = float(mvacc) / float(mvprp)
327  except:
328  mvacr = 0.0
329  output["MonteCarlo_Acceptance_" +
330  mvname + "_" + str(i)] = str(mvacr)
331  if "Nuisances" in mvname:
332  output["MonteCarlo_StepSize_" + mvname + "_" +
333  str(i)] = str(IMP.core.NormalMover.get_from(mv).get_sigma())
334  if "Weights" in mvname:
335  output["MonteCarlo_StepSize_" + mvname + "_" +
336  str(i)] = str(IMP.isd.WeightMover.get_from(mv).get_radius())
337  output["MonteCarlo_Temperature"] = str(self.mc.get_kt())
338  output["MonteCarlo_Nframe"] = str(self.nframe)
339  return output
340 
341 
342 class MolecularDynamics(object):
343  def __init__(self,m,objects,kt,gamma=0.01,maximum_time_step=1.0):
344  self.m=m
345  to_sample=[]
346  for obj in objects:
347  to_sample+=obj.get_particles_to_sample()['Floppy_Bodies_SimplifiedModel'][0]
348  self.ltstate=IMP.atom.LangevinThermostatOptimizerState(self.m,to_sample,
349  kt/0.0019872041,
350  gamma)
351  self.md = IMP.atom.MolecularDynamics(self.m)
352  self.md.set_maximum_time_step(maximum_time_step)
353  self.md.add_optimizer_state(self.ltstate)
354 
355  def set_kt(self,kt):
356  temp=kt/0.0019872041
357  self.ltstate.set_temperature(temp)
358  self.md.assign_velocities(temp)
359 
360  def set_gamma(self,gamma):
361  self.ltstate.set_gamma(gamma)
362 
363  def optimize(self,nsteps):
364  self.md.optimize(nsteps)
365 
366  def get_output(self):
367  output={}
368  output["MolecularDynamics_KineticEnergy"]=str(self.md.get_kinetic_energy())
369  return output
370 
371 class ConjugateGradients(object):
372 
373  def __init__(self, m, objects):
374  self.m = m
375  self.nframe = -1
376  self.cg = IMP.core.ConjugateGradients(self.m)
377 
378  def set_label(self, label):
379  self.label = label
380 
381  def get_frame_number(self):
382  return self.nframe
383 
384  def run(self, nstep):
385  self.nframe += 1
386  self.cg.optimize(nstep)
387 
388  def set_scoring_function(self, objectlist):
389  rs = IMP.RestraintSet(self.m, 1.0, 'sfo')
390  for ob in objectlist:
391  rs.add_restraint(ob.get_restraint())
393  self.cg.set_scoring_function(sf)
394 
395  def get_output(self):
396  output = {}
397  acceptances = []
398  output["ConjugatedGradients_Nframe"] = str(self.nframe)
399  return output
400 
401 
402 class ReplicaExchange(object):
403 
404  def __init__(
405  self,
406  model,
407  tempmin,
408  tempmax,
409  samplerobjects,
410  test=True,
411  replica_exchange_object=None):
412  '''
413  samplerobjects can be a list of MonteCarlo or MolecularDynamics
414  '''
415 
416 
417  self.m = model
418  self.samplerobjects = samplerobjects
419  # min and max temperature
420  self.TEMPMIN_ = tempmin
421  self.TEMPMAX_ = tempmax
422 
423  if replica_exchange_object is None:
424  # initialize Replica Exchange class
425  try:
426  import IMP.mpi
427  self.rem = IMP.mpi.ReplicaExchange()
428  except ImportError:
429  self.rem = _SerialReplicaExchange()
430 
431  else:
432  # get the replica exchange class instance from elsewhere
433  print 'got existing rex object'
434  self.rem = replica_exchange_object
435 
436  # get number of replicas
437  nproc = self.rem.get_number_of_replicas()
438 
439  if nproc % 2 != 0 and test == False:
440  raise Exception, "number of replicas has to be even. set test=True to run with odd number of replicas."
441  # create array of temperatures, in geometric progression
442  temp = self.rem.create_temperatures(
443  self.TEMPMIN_,
444  self.TEMPMAX_,
445  nproc)
446  # get replica index
447  self.temperatures = temp
448 
449  myindex = self.rem.get_my_index()
450  # set initial value of the parameter (temperature) to exchange
451  self.rem.set_my_parameter("temp", [self.temperatures[myindex]])
452  for so in self.samplerobjects:
453  so.set_kt(self.temperatures[myindex])
454  self.nattempts = 0
455  self.nmintemp = 0
456  self.nmaxtemp = 0
457  self.nsuccess = 0
458 
459  def get_temperatures(self):
460  return self.temperatures
461 
462  def get_my_temp(self):
463  return self.rem.get_my_parameter("temp")[0]
464 
465  def get_my_index(self):
466  return self.rem.get_my_index()
467 
468  def swap_temp(self, nframe, score=None):
469  if score is None:
470  score = self.m.evaluate(False)
471  # get my replica index and temperature
472  myindex = self.rem.get_my_index()
473  mytemp = self.rem.get_my_parameter("temp")[0]
474 
475  if mytemp == self.TEMPMIN_:
476  self.nmintemp += 1
477 
478  if mytemp == self.TEMPMAX_:
479  self.nmaxtemp += 1
480 
481  # score divided by kbt
482  myscore = score / mytemp
483 
484  # get my friend index and temperature
485  findex = self.rem.get_friend_index(nframe)
486  ftemp = self.rem.get_friend_parameter("temp", findex)[0]
487  # score divided by kbt
488  fscore = score / ftemp
489 
490  # try exchange
491  flag = self.rem.do_exchange(myscore, fscore, findex)
492 
493  self.nattempts += 1
494  # if accepted, change temperature
495  if (flag):
496  for so in self.samplerobjects:
497  so.set_kt(ftemp)
498  self.nsuccess += 1
499 
500  def get_output(self):
501  output = {}
502  acceptances = []
503  if self.nattempts != 0:
504  output["ReplicaExchange_SwapSuccessRatio"] = str(
505  float(self.nsuccess) / self.nattempts)
506  output["ReplicaExchange_MinTempFrequency"] = str(
507  float(self.nmintemp) / self.nattempts)
508  output["ReplicaExchange_MaxTempFrequency"] = str(
509  float(self.nmaxtemp) / self.nattempts)
510  else:
511  output["ReplicaExchange_SwapSuccessRatio"] = str(0)
512  output["ReplicaExchange_MinTempFrequency"] = str(0)
513  output["ReplicaExchange_MaxTempFrequency"] = str(0)
514  output["ReplicaExchange_CurrentTemp"] = str(self.get_my_temp())
515  return output
516 
517 
518 class PyMCMover(object):
519  # only works if the sampled particles are rigid bodies
520 
521  def __init__(self, representation, mcchild, n_mc_steps):
522 
523  # mcchild must be pmi.samplers.MonteCarlo
524  # representation must be pmi.representation
525 
526  self.rbs = representation.get_rigid_bodies()
527 
528  self.mc = mcchild
529  self.n_mc_steps = n_mc_steps
530 
531  def store_move(self):
532  # get all xyz coordinates of all rigid bodies of all copies
533  self.oldcoords = []
534  for copy in self.rbs:
535  crd = []
536  for rb in copy:
537  crd.append(rb.get_reference_frame())
538  self.oldcoords.append(crd)
539 
540  def propose_move(self, prob):
541  self.mc.run(self.n_mc_steps)
542 
543  def reset_move(self):
544  # reset each copy to crd
545  for copy, crd in zip(self.rbs, self.oldcoords):
546  for rb, ref in zip(copy, crd):
547  rb.set_reference_frame(ref)
548 
549  def get_number_of_steps(self):
550  return self.n_mc_steps
551 
552  def set_number_of_steps(self, nsteps):
553  self.n_mc_steps = nsteps
554 
555 
556 class PyMC(object):
557 
558  def __init__(self, model):
559  from math import exp
560  import random
561 
562  self.m = model
563  self.restraints = None
564  self.first_call = True
565  self.nframe = -1
566 
567  def add_mover(self, mv):
568  self.mv = mv
569 
570  def set_kt(self, kT):
571  self.kT = kT
572 
573  def set_return_best(self, thing):
574  pass
575 
576  def set_move_probability(self, thing):
577  pass
578 
579  def get_energy(self):
580  if self.restraints:
581  pot = sum([r.evaluate(False) for r in self.restraints])
582  else:
583  pot = self.m.evaluate(False)
584  return pot
585 
586  def metropolis(self, old, new):
587  deltaE = new - old
588  print ": old %f new %f deltaE %f new_epot: %f" % (old, new, deltaE,
589  self.m.evaluate(
590  False)),
591  kT = self.kT
592  if deltaE < 0:
593  return True
594  else:
595  return exp(-deltaE / kT) > random.uniform(0, 1)
596 
597  def optimize(self, nsteps):
598  self.naccept = 0
599  self.nframe += 1
600  print "=== new MC call"
601  # store initial coordinates
602  if self.first_call:
603  self.mv.store_move()
604  self.first_call = False
605  for i in xrange(nsteps):
606  print "MC step %d " % i,
607  # get total energy
608  old = self.get_energy()
609  # make a MD move
610  self.mv.propose_move(1)
611  # get new total energy
612  new = self.get_energy()
613  if self.metropolis(old, new):
614  # move was accepted: store new conformation
615  self.mv.store_move()
616  self.naccept += 1
617  print "accepted "
618  else:
619  # move rejected: restore old conformation
620  self.mv.reset_move()
621  print " "
622 
623  def get_number_of_forward_steps(self):
624  return self.naccept
625 
626  def set_restraints(self, restraints):
627  self.restraints = restraints
628 
629  def set_scoring_function(self, objects):
630  # objects should be pmi.restraints
631  rs = IMP.RestraintSet(self.m, 1.0, 'sfo')
632  for ob in objects:
633  rs.add_restraint(ob.get_restraint())
634  self.set_restraints([rs])
635 
636  def get_output(self):
637  output = {}
638  acceptances = []
639  output["PyMC_Temperature"] = str(self.kT)
640  output["PyMC_Nframe"] = str(self.nframe)
641  return output
642 
643 
644 # 3
A Monte Carlo optimizer.
Definition: MonteCarlo.h:45
A class to implement Hamiltonian Replica Exchange.
Maintains temperature during molecular dynamics.
Object used to hold a set of restraints.
Modify the transformation of a rigid body.
Simple conjugate gradients optimizer.
Modify a set of continuous variables by perturbing them within a ball.
static bool get_is_setup(const IMP::kernel::ParticleAdaptor &p)
Definition: rigid_bodies.h:500
Simple molecular dynamics optimizer.
Code that uses the MPI parallel library.
Modify the transformation of a rigid body.
Definition: WeightMover.h:24
Modify the transformation of a rigid body.
Modify a set of continuous variables using a normal distribution.
Definition: NormalMover.h:20
Basic functionality that is expected to be used by a wide variety of IMP users.
VectorD< 3 > Vector3D
Definition: VectorD.h:395
static const FloatKeys & get_xyz_keys()
Get a vector containing the keys for x,y,z.
Applies a list of movers one at a time.
Definition: SerialMover.h:23
Inferential scoring building on methods developed as part of the Inferential Structure Determination ...