def __init__(self, system, nreplicas, mc_runner, minima, energy_accuracy, compare_structures=None, mindist=None, copy_minima=True, config_tests=None, minimizer=None, debug=True, **kwargs): super(NestedSamplingSAExact, self).__init__(system, nreplicas, mc_runner, **kwargs) self.debug = debug if copy_minima: self.minima = [_UnboundMinimum(m) for m in minima] else: self.minima = minima if self.verbose: self._check_minima() self.compare_structures = compare_structures if compare_structures is None: try: self.compare_structures = self.system.get_compare_exact() except NotImplementedError or AttributeError: pass self.mindist = mindist if mindist is None: try: self.mindist = self.system.get_mindist() except NotImplementedError or AttributeError: pass self.config_tests = config_tests if config_tests is None: try: self.config_tests = self.system.get_config_tests() except NotImplementedError or AttributeError: pass self.minimizer = minimizer if self.minimizer is None: self.minimizer = self.system.get_minimizer() self.minima_searcher = _MinimaSearcher(self.minima, energy_accuracy, self.compare_structures) self.sa_sampler = SASampler(self.minima, self.system.k) self.count_sampled_minima = 0
def __init__(self, natoms=6): self.system = LJClusterSENS(natoms, 5.) self.ndof = natoms * 3 - 6 self.natoms = natoms self.database = build_database(self.system, nminima=2) self.sampler = SASampler(self.database.minima(), self.ndof)
def setUp(self): self.natoms = 6 self.system = LJCluster(self.natoms) # create a database self.database = self.system.create_database() # add some minima to the database bh = self.system.get_basinhopping(self.database, outstream=None) while self.database.number_of_minima() < 2: bh.run(1) get_thermodynamic_information(self.system, self.database) get_all_normalmodes(self.system, self.database) self.ndof = self.natoms * 3 - 6 self.sampler = SASampler(self.database.minima(), self.ndof)
def __init__(self, system, nreplicas, mc_runner, minima, minprob=None, energy_offset=None, **kwargs): super(NestedSamplingSA, self).__init__(system, nreplicas, mc_runner, **kwargs) self.minima = minima self.bh_sampler = SASampler(self.minima, self.system.k) if minprob is None: raise ValueError("minprob cannot be None") self.minprob = minprob if energy_offset is None: self.energy_offset = 2.5 else: self.energy_offset = energy_offset self.count_sampled_minima = 0
class SamplerSystem(object): def __init__(self, natoms=6): self.system = LJClusterSENS(natoms, 5.) self.ndof = natoms * 3 - 6 self.natoms = natoms self.database = build_database(self.system, nminima=2) self.sampler = SASampler(self.database.minima(), self.ndof) def sample_configuration(self, Emax): m, x = self.sampler.sample_coords(Emax) E = self.sampler.compute_energy(x, m) return E, x def __call__(self, *args, **kwargs): Emax = args[2] # print "walking with Emax", Emax E, x = self.sample_configuration(Emax) res = Result(x=x, energy=E, naccept=5, nsteps=10, Emax=Emax) return res
def __init__(self, replicas, mc_runner, minima, ndof, config_tests=None, debug=False, minprob=None, energy_offset=None, copy_minima=True, center_minima=False, energy_onset = None, **kwargs): super(NestedSamplingSA, self).__init__(replicas, mc_runner, **kwargs) self.minima = minima self.bh_sampler = SASampler(self.minima, ndof, copy_minima=copy_minima, center_minima=center_minima) if minprob is None: raise ValueError("minprob cannot be None") self.minprob = minprob if energy_offset is None: self.energy_offset = 2.5 else: self.energy_offset = energy_offset self.debug = debug self.config_tests = config_tests self._energy_max_database = energy_onset if self.debug and self.config_tests is None: print "warning, not using config tests" self.count_sampled_minima = 0
class NestedSamplingSA(NestedSampling): """overload get_starting_configuration() in order to introduce sampling from known minima Parameters ---------- system : pele system object nreplicas : int number of replicas takestep : callable object to do the step taking. must be callable and have attribute takestep.stepsize minima : list of Minimum objects """ def __init__(self, replicas, mc_runner, minima, ndof, config_tests=None, debug=False, minprob=None, energy_offset=None, copy_minima=True, center_minima=False, energy_onset = None, **kwargs): super(NestedSamplingSA, self).__init__(replicas, mc_runner, **kwargs) self.minima = minima self.bh_sampler = SASampler(self.minima, ndof, copy_minima=copy_minima, center_minima=center_minima) if minprob is None: raise ValueError("minprob cannot be None") self.minprob = minprob if energy_offset is None: self.energy_offset = 2.5 else: self.energy_offset = energy_offset self.debug = debug self.config_tests = config_tests self._energy_max_database = energy_onset if self.debug and self.config_tests is None: print "warning, not using config tests" self.count_sampled_minima = 0 # def get_starting_configuration_minima_HA(self, Emax): # """using the Harmonic Approximation sample a new configuration starting from a minimum sampled uniformly according to phase space volume # # Notes # ----- # displace minimum configuration along its eigenvectors # # """ # m = self.bh_sampler.sample_minimum(Emax) # x = self.bh_sampler.sample_coords_from_basin(m, Emax) # pot = self.system.get_potential() # e = pot.getEnergy(x) # return x, e def get_starting_configuration_minima_single(self, Emax): """return a minimum sampled uniformly according to phase space volume""" m = self.bh_sampler.sample_minimum(Emax) x, e = m.coords.copy(), m.energy # self.system.center_coords(x) if self.config_tests is not None: for test in self.config_tests: t = test(coords=x) if not t: print "warning: minimum from database failed configuration test", m.energy raise ConfigTestError() return x, e def get_starting_configuration_minima(self, Emax): """return a minimum sampled uniformly according to phase space volume Notes ----- some of the minima in the database don't satisfy the configuration checks. So we sample over and over again until we get one that passes """ self.count_sampled_minima += 1 while True: try: return self.get_starting_configuration_minima_single(Emax) except ConfigTestError: pass def onset_prob_func(self, Emax): """return the probability of sampling from a minimum The probability depends on Emax. For high Emax the probability is 0. the probability increases as Emax get's lower, reaching a maximum of self.minprob. value of Emax where the probabilty starts to get large is energy_max_database + energy_offset where energy_max_database is the maximum energy minimum in the database. This behaviour can be adjusted with parameter energy_offset. parameter <energy_onset_width> determines the speed at which it turns on. the optimal probability of sampling from the database scales approximately as 1/nreplicas, this has been shown analytically (see Stefano's thesis pp.29-32). """ #if not hasattr(self, "_energy_max_database"): if self._energy_max_database == None: self._energy_max_database = float(max([m.energy for m in self.minima])) max_prob = float(self.minprob) energy_onset_width = 1. dE = self._energy_max_database + self.energy_offset - Emax f = dE / energy_onset_width if np.log(np.finfo('d').max) <= (-f): onset_prob = 0. else: onset_prob = max_prob / (1. + np.exp(-f)) return float(onset_prob)/self.nreplicas def get_starting_configurations(self, Emax): """this function overloads the function in NestedSampling""" # choose a replica randomly configs = self.get_starting_configurations_from_replicas() # replace each starting configuration with a one chosen # from the minima with probability onset_prob onset_prob = self.onset_prob_func(Emax) for i in range(len(configs)): if np.random.uniform(0,1) < onset_prob: x, energy = self.get_starting_configuration_minima(Emax) configs[i] = Replica(x, energy, from_random=False) if self.verbose: print "sampling from minima, E minimum:", energy, "with probability:", onset_prob return configs
class TestBuildDatabase(unittest.TestCase): def setUp(self): self.natoms = 6 self.system = LJCluster(self.natoms) # create a database self.database = self.system.create_database() # add some minima to the database bh = self.system.get_basinhopping(self.database, outstream=None) while self.database.number_of_minima() < 2: bh.run(1) get_thermodynamic_information(self.system, self.database) get_all_normalmodes(self.system, self.database) self.ndof = self.natoms * 3 - 6 self.sampler = SASampler(self.database.minima(), self.ndof) # def test(self): # for m in self.database.minima(): # print m.energy # print self.sampler.compute_weights(-11.7) # for i in range(10): # m = self.sampler.sample_minimum(-11) # print m._id def compute_weights(self, Emax, k): print [m.energy for m in self.database.minima()], Emax lweights = [ - np.log(m.pgorder) + 0.5 * k * np.log(Emax - m.energy) - 0.5 * m.fvib for m in self.database.minima() if m.energy < Emax] lweights = np.array(lweights) if lweights.size <= 1: return lweights lwmax = lweights.max() lweights -= lwmax return np.exp(lweights) def test1(self): Emax = -11.7 minima, weights = self.sampler.compute_weights(0.) minima, weights = self.sampler.compute_weights(Emax) self.assertAlmostEqual(weights[0], 0.81256984, 3) self.assertAlmostEqual(weights[1], 1., 7) new_weights = self.compute_weights(Emax, self.ndof) print weights print new_weights def test2(self): Emax = -11.7 minima, weights = self.sampler.compute_weights(Emax) minima, weights = self.sampler.compute_weights(Emax) self.assertAlmostEqual(weights[0], 0.81256984, 3) self.assertAlmostEqual(weights[1], 1., 7) def test3(self): m = self.sampler.sample_minimum(-11.7) self.assertIn(m, self.database.minima()) def test4(self): """check that the HSA energy is computed correctly""" pot = self.system.get_potential() for m in self.database.minima(): x = m.coords.copy() x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) ecompare = (ehsa - ecalc) / (ecalc - m.energy) print ehsa - m.energy, ecalc - m.energy, m.energy, ecompare self.assertAlmostEqual(ecompare, 0., 1) def test_rotation(self): """assert that the HSA energy is *not* invariant under rotation""" pot = self.system.get_potential() aa = rotations.random_aa() rmat = rotations.aa2mx(aa) from pele.mindist import TransformAtomicCluster tform = TransformAtomicCluster(can_invert=True) for m in self.database.minima(): x = m.coords.copy() # randomly move the atoms by a small amount x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) # now rotate by a random matrix xnew = x.copy() tform.rotate(xnew, rmat) ehsa2 = self.sampler.compute_energy(xnew, m, x0=m.coords) ecalc2 = pot.getEnergy(xnew) self.assertAlmostEqual(ecalc, ecalc2, 5) self.assertNotAlmostEqual(ehsa1, ehsa2, 1) # def test_rotation_2(self): # """assert that the HSA energy *is* invariant under rotation *if* the initial coords are also rotated""" # pot = self.system.get_potential() # aa = rotations.random_aa() # rmat = rotations.aa2mx(aa) # from pele.mindist import TransformAtomicCluster # tform = TransformAtomicCluster(can_invert=True) # for m in self.database.minima(): # x = m.coords.copy() # # randomly move the atoms by a small amount # x += np.random.uniform(-1e-3, 1e-3, x.shape) # ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) # ecalc = pot.getEnergy(x) # # now rotate by a random matrix # xnew = x.copy() # tform.rotate(xnew, rmat) # xmnew = m.coords.copy() # tform.rotate(xmnew, rmat) # ehsa2 = self.sampler.compute_energy(xnew, m, x0=xmnew) # ecalc2 = pot.getEnergy(xnew) # self.assertAlmostEqual(ecalc, ecalc2, 5) # self.assertAlmostEqual(ehsa1, ehsa2, 3) def test_permutation(self): """assert that the HSA energy is not invariant under permutation""" pot = self.system.get_potential() perm = range(self.natoms) np.random.shuffle(perm) from pele.mindist import TransformAtomicCluster tform = TransformAtomicCluster(can_invert=True) for m in self.database.minima(): x = m.coords.copy() # randomly move the atoms by a small amount x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) # now rotate by a random matrix xnew = x.copy() xnew = tform.permute(xnew, perm) ehsa2 = self.sampler.compute_energy(xnew, m, x0=m.coords) ecalc2 = pot.getEnergy(xnew) self.assertAlmostEqual(ecalc, ecalc2, 5) self.assertNotAlmostEqual(ehsa1, ehsa2, 1)
class NestedSamplingSA(NestedSampling): """overload get_starting_configuration() in order to introduce sampling from known minima Parameters ---------- system : pele system object nreplicas : int number of replicas takestep : callable object to do the step taking. must be callable and have attribute takestep.stepsize minima : list of Minimum objects """ def __init__(self, system, nreplicas, mc_runner, minima, minprob=None, energy_offset=None, **kwargs): super(NestedSamplingSA, self).__init__(system, nreplicas, mc_runner, **kwargs) self.minima = minima self.bh_sampler = SASampler(self.minima, self.system.k) if minprob is None: raise ValueError("minprob cannot be None") self.minprob = minprob if energy_offset is None: self.energy_offset = 2.5 else: self.energy_offset = energy_offset self.count_sampled_minima = 0 def get_starting_configuration_minima_HA(self, Emax): """using the Harmonic Approximation sample a new configuration starting from a minimum sampled uniformly according to phase space volume Notes ----- displace minimum configuration along its eigenvectors """ m = self.bh_sampler.sample_minimum(Emax) x = self.bh_sampler.sample_coords_from_basin(m, Emax) pot = self.system.get_potential() e = pot.getEnergy(x) return x, e def get_starting_configuration_minima_single(self, Emax): """return a minimum sampled uniformly according to phase space volume""" m = self.bh_sampler.sample_minimum(Emax) x, e = m.coords, m.energy self.system.center_coords(x) if True: accept_tests = self.system.get_config_tests() for test in accept_tests: t = test(coords=x) if not t: print "warning: minimum from database failed configuration test", m._id, m.energy raise ConfigTestError() return x, e def get_starting_configuration_minima(self, Emax): """return a minimum sampled uniformly according to phase space volume Notes ----- some of the minima in the database don't satisfy the configuration checks. So we sample over and over again until we get one that passes """ self.count_sampled_minima += 1 while True: try: return self.get_starting_configuration_minima_single(Emax) except ConfigTestError: pass def onset_prob_func(self, Emax): """return the probability of sampling from a minimum The probability depends on Emax. For high Emax the probability is 0. the probability increases as Emax get's lower, reaching a maximum of self.minprob. value of Emax where the probabilty starts to get large is energy_max_database + energy_offset where energy_max_database is the maximum energy minimum in the database. This behavior can be adjusted with parameter energy_offset. parameter b determines the speed at which it turns on. """ if not hasattr(self, "_energy_max_database"): self._energy_max_database = float( max([m.energy for m in self.minima])) max_prob = float(self.minprob) energy_onset_width = 1. dE = self._energy_max_database + self.energy_offset - Emax f = dE / energy_onset_width if f > 100: onset_prob = 0. else: onset_prob = max_prob / (1. + np.exp(-f)) return onset_prob def get_starting_configurations(self, Emax): """this function overloads the function in NestedSampling""" # choose a replica randomly configs = self.get_starting_configurations_from_replicas() # replace each starting configuration with a one chosen # from the minima with probability prob onset_prob = self.onset_prob_func(Emax) prob = onset_prob / float(self.nreplicas) for i in range(len(configs)): if np.random.uniform(0, 1) < prob: x, energy = self.get_starting_configuration_minima(Emax) configs[i] = Replica(x, energy, from_random=False) if self.verbose: print "sampling from minima, E minimum:", energy, "with probability:", prob return configs
class TestBuildDatabase(unittest.TestCase): def setUp(self): self.natoms = 6 self.system = LJCluster(self.natoms) # create a database self.database = self.system.create_database() # add some minima to the database bh = self.system.get_basinhopping(self.database, outstream=None) while self.database.number_of_minima() < 2: bh.run(1) get_thermodynamic_information(self.system, self.database) get_all_normalmodes(self.system, self.database) self.ndof = self.natoms * 3 - 6 self.sampler = SASampler(self.database.minima(), self.ndof) # def test(self): # for m in self.database.minima(): # print m.energy # print self.sampler.compute_weights(-11.7) # for i in range(10): # m = self.sampler.sample_minimum(-11) # print m._id def test1(self): Emax = -11.7 minima, weights = self.sampler.compute_weights(0.) minima, weights = self.sampler.compute_weights(Emax) self.assertAlmostEqual(weights[0], 0.81256984, 3) self.assertAlmostEqual(weights[1], 1., 7) def test2(self): Emax = -11.7 minima, weights = self.sampler.compute_weights(Emax) minima, weights = self.sampler.compute_weights(Emax) self.assertAlmostEqual(weights[0], 0.81256984, 3) self.assertAlmostEqual(weights[1], 1., 7) def test3(self): m = self.sampler.sample_minimum(-11.7) self.assertIn(m, self.database.minima()) def test4(self): """check that the HSA energy is computed correctly""" pot = self.system.get_potential() for m in self.database.minima(): x = m.coords.copy() x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) ecompare = (ehsa - ecalc) / (ecalc - m.energy) print ehsa - m.energy, ecalc - m.energy, m.energy, ecompare self.assertAlmostEqual(ecompare, 0., 1) def test_rotation(self): """assert that the HSA energy is *not* invariant under rotation""" pot = self.system.get_potential() aa = rotations.random_aa() rmat = rotations.aa2mx(aa) from pele.mindist import TransformAtomicCluster tform = TransformAtomicCluster(can_invert=True) for m in self.database.minima(): x = m.coords.copy() # randomly move the atoms by a small amount x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) # now rotate by a random matrix xnew = x.copy() tform.rotate(xnew, rmat) ehsa2 = self.sampler.compute_energy(xnew, m, x0=m.coords) ecalc2 = pot.getEnergy(xnew) self.assertAlmostEqual(ecalc, ecalc2, 5) self.assertNotAlmostEqual(ehsa1, ehsa2, 1) # def test_rotation_2(self): # """assert that the HSA energy *is* invariant under rotation *if* the initial coords are also rotated""" # pot = self.system.get_potential() # aa = rotations.random_aa() # rmat = rotations.aa2mx(aa) # from pele.mindist import TransformAtomicCluster # tform = TransformAtomicCluster(can_invert=True) # for m in self.database.minima(): # x = m.coords.copy() # # randomly move the atoms by a small amount # x += np.random.uniform(-1e-3, 1e-3, x.shape) # ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) # ecalc = pot.getEnergy(x) # # now rotate by a random matrix # xnew = x.copy() # tform.rotate(xnew, rmat) # xmnew = m.coords.copy() # tform.rotate(xmnew, rmat) # ehsa2 = self.sampler.compute_energy(xnew, m, x0=xmnew) # ecalc2 = pot.getEnergy(xnew) # self.assertAlmostEqual(ecalc, ecalc2, 5) # self.assertAlmostEqual(ehsa1, ehsa2, 3) def test_permutation(self): """assert that the HSA energy is not invariant under permutation""" pot = self.system.get_potential() perm = range(self.natoms) np.random.shuffle(perm) from pele.mindist import TransformAtomicCluster tform = TransformAtomicCluster(can_invert=True) for m in self.database.minima(): x = m.coords.copy() # randomly move the atoms by a small amount x += np.random.uniform(-1e-3, 1e-3, x.shape) ehsa1 = self.sampler.compute_energy(x, m, x0=m.coords) ecalc = pot.getEnergy(x) # now rotate by a random matrix xnew = x.copy() xnew = tform.permute(xnew, perm) ehsa2 = self.sampler.compute_energy(xnew, m, x0=m.coords) ecalc2 = pot.getEnergy(xnew) self.assertAlmostEqual(ecalc, ecalc2, 5) self.assertNotAlmostEqual(ehsa1, ehsa2, 1)
class NestedSamplingSAExact(NestedSampling): """overload get_starting_configuration() in order to introduce sampling from known minima Parameters ---------- system : pele system object nreplicas : int number of replicas takestep : callable object to do the step taking. must be callable and have attribute takestep.stepsize minima : list of Minimum objects """ def __init__(self, system, nreplicas, mc_runner, minima, energy_accuracy, compare_structures=None, mindist=None, copy_minima=True, config_tests=None, minimizer=None, debug=True, **kwargs): super(NestedSamplingSAExact, self).__init__(system, nreplicas, mc_runner, **kwargs) self.debug = debug if copy_minima: self.minima = [_UnboundMinimum(m) for m in minima] else: self.minima = minima if self.verbose: self._check_minima() self.compare_structures = compare_structures if compare_structures is None: try: self.compare_structures = self.system.get_compare_exact() except NotImplementedError or AttributeError: pass self.mindist = mindist if mindist is None: try: self.mindist = self.system.get_mindist() except NotImplementedError or AttributeError: pass self.config_tests = config_tests if config_tests is None: try: self.config_tests = self.system.get_config_tests() except NotImplementedError or AttributeError: pass self.minimizer = minimizer if self.minimizer is None: self.minimizer = self.system.get_minimizer() self.minima_searcher = _MinimaSearcher(self.minima, energy_accuracy, self.compare_structures) self.sa_sampler = SASampler(self.minima, self.system.k) self.count_sampled_minima = 0 def _check_minima(self): for m in self.minima: assert m.energy is not None assert m.fvib is not None assert m.pgorder is not None assert m.normal_modes is not None def _compute_energy_in_SA(self, replica): """compute the energy of the coordinates in replica.coords in the harmonic superposition approximation This is where most of the difficulty is in the algorithm. This is also where all the system dependence is """ # quench to nearest minimum qresult = self.minimizer(replica.x) # check if that minimum is in the database. reject if not m, transformation = self.minima_searcher.get_minima( qresult.energy, qresult.coords) if m is None: return None # put replica.coords into best alignment with the structure stored in m.coords # this involves accounting for symmetries of the Hamiltonian like translational, # rotational and permutations symmetries. You can use the coordinates in qresult.coords # to help find the best permutation. Ultimately it must be aligned with the structure in m.coords # The hessian eigenvectors were computed with a given permutation # e.g. account for trivial translational and rotational degrees of freedom x = replica.x.copy() if transformation is not None: # transformation is the set of transformations that put qresult.coords into exact # alignment with m.coords. If we apply these transformations to replica.x then # replica.x will be in good (although not perfect) alignment already with m.coords x = self.compare_structures.apply_transformation(x, transformation) # Do a final round of optimization to further improve the alignment if self.mindist is not None: dist, x0, x = self.mindist(m.coords.copy(), x) if self.debug: diff = np.linalg.norm(x0 - m.coords) if diff > .01: with open("error.xyz", "w") as fout: from pele.utils.xyz import write_xyz write_xyz(fout, x0) write_xyz(fout, m.coords) raise Exception( "warning, mindist appears to have changed x0. the norm of the difference is %g" % diff) assert (x0 == m.coords).all() energy = self.sa_sampler.compute_energy(x, m) return energy def _attempt_swap(self, replica, Emax): # sample a configuration from the harmonic superposition approximation m, xsampled = self.sa_sampler.sample_coords(Emax) # if the configuration fails the config test then reject the swap # print "attempting swap" if self.config_tests is not None: for test in self.config_tests: if not test(coords=xsampled): return None # if the energy returned by full energy function is too high, then reject the swap Esampled = self.system.get_energy(xsampled) if Esampled >= Emax: return None # compute the energy of the replica within the superposition approximation. E_SA = self._compute_energy_in_SA(replica) # reject if the energy is too high if E_SA is None or E_SA >= Emax: # no swap done return None if self.verbose: print "accepting swap: Eold %g Enew %g Eold_SA %g Emax %g" % ( replica.energy, Esampled, E_SA, Emax) self.count_sampled_minima += 1 return Replica(xsampled, Esampled, from_random=False) def do_monte_carlo_chain(self, replicas, Emax): # replicas = super(NestedSamplingSAExact, self).do_monte_carlo_chain(replicas, Emax) # try to swap this configuration with one sampled from the HSA for i in xrange(len(replicas)): r = replicas[i] rnew = self._attempt_swap(r, Emax) if rnew is not None: replicas[i] = rnew # do a monte carlo walk replicas = super(NestedSamplingSAExact, self).do_monte_carlo_chain(replicas, Emax) return replicas
class NestedSamplingSAExact(NestedSampling): """overload get_starting_configuration() in order to introduce sampling from known minima Parameters ---------- system : pele system object nreplicas : int number of replicas takestep : callable object to do the step taking. must be callable and have attribute takestep.stepsize minima : list of Minimum objects """ def __init__(self, system, nreplicas, mc_runner, minima, energy_accuracy, compare_structures=None, mindist=None, copy_minima=True, config_tests=None, minimizer=None, debug=True, **kwargs): super(NestedSamplingSAExact, self).__init__(system, nreplicas, mc_runner, **kwargs) self.debug = debug if copy_minima: self.minima = [_UnboundMinimum(m) for m in minima] else: self.minima = minima if self.verbose: self._check_minima() self.compare_structures = compare_structures if compare_structures is None: try: self.compare_structures = self.system.get_compare_exact() except NotImplementedError or AttributeError: pass self.mindist = mindist if mindist is None: try: self.mindist = self.system.get_mindist() except NotImplementedError or AttributeError: pass self.config_tests = config_tests if config_tests is None: try: self.config_tests = self.system.get_config_tests() except NotImplementedError or AttributeError: pass self.minimizer = minimizer if self.minimizer is None: self.minimizer = self.system.get_minimizer() self.minima_searcher = _MinimaSearcher(self.minima, energy_accuracy, self.compare_structures) self.sa_sampler = SASampler(self.minima, self.system.k) self.count_sampled_minima = 0 def _check_minima(self): for m in self.minima: assert m.energy is not None assert m.fvib is not None assert m.pgorder is not None assert m.normal_modes is not None def _compute_energy_in_SA(self, replica): """compute the energy of the coordinates in replica.coords in the harmonic superposition approximation This is where most of the difficulty is in the algorithm. This is also where all the system dependence is """ # quench to nearest minimum qresult = self.minimizer(replica.x) # check if that minimum is in the database. reject if not m, transformation = self.minima_searcher.get_minima(qresult.energy, qresult.coords) if m is None: return None # put replica.coords into best alignment with the structure stored in m.coords # this involves accounting for symmetries of the Hamiltonian like translational, # rotational and permutations symmetries. You can use the coordinates in qresult.coords # to help find the best permutation. Ultimately it must be aligned with the structure in m.coords # The hessian eigenvectors were computed with a given permutation # e.g. account for trivial translational and rotational degrees of freedom x = replica.x.copy() if transformation is not None: # transformation is the set of transformations that put qresult.coords into exact # alignment with m.coords. If we apply these transformations to replica.x then # replica.x will be in good (although not perfect) alignment already with m.coords x = self.compare_structures.apply_transformation(x, transformation) # Do a final round of optimization to further improve the alignment if self.mindist is not None: dist, x0, x = self.mindist(m.coords.copy(), x) if self.debug: diff = np.linalg.norm(x0 - m.coords) if diff > .01: with open("error.xyz", "w") as fout: from pele.utils.xyz import write_xyz write_xyz(fout, x0) write_xyz(fout, m.coords) raise Exception("warning, mindist appears to have changed x0. the norm of the difference is %g" % diff) assert (x0 == m.coords).all() energy = self.sa_sampler.compute_energy(x, m) return energy def _attempt_swap(self, replica, Emax): # sample a configuration from the harmonic superposition approximation m, xsampled = self.sa_sampler.sample_coords(Emax) # if the configuration fails the config test then reject the swap # print "attempting swap" if self.config_tests is not None: for test in self.config_tests: if not test(coords=xsampled): return None # if the energy returned by full energy function is too high, then reject the swap Esampled = self.system.get_energy(xsampled) if Esampled >= Emax: return None # compute the energy of the replica within the superposition approximation. E_SA = self._compute_energy_in_SA(replica) # reject if the energy is too high if E_SA is None or E_SA >= Emax: # no swap done return None if self.verbose: print "accepting swap: Eold %g Enew %g Eold_SA %g Emax %g" % (replica.energy, Esampled, E_SA, Emax) self.count_sampled_minima += 1 return Replica(xsampled, Esampled, from_random=False) def do_monte_carlo_chain(self, replicas, Emax): # replicas = super(NestedSamplingSAExact, self).do_monte_carlo_chain(replicas, Emax) # try to swap this configuration with one sampled from the HSA for i in xrange(len(replicas)): r = replicas[i] rnew = self._attempt_swap(r, Emax) if rnew is not None: replicas[i] = rnew # do a monte carlo walk replicas = super(NestedSamplingSAExact, self).do_monte_carlo_chain(replicas, Emax) return replicas