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