Source code for openpathsampling.pathsimulators.bootstrap_init_conds

import sys
import collections
import logging

import openpathsampling as paths

from openpathsampling.pathmover import SubPathMover
from .path_simulator import PathSimulator, MCStep
from ..ops_logging import initialization_logging

logger = logging.getLogger(__name__)
init_log = logging.getLogger('openpathsampling.initialization')

class BootstrapPromotionMove(SubPathMover):
    """
    Bootstrap promotion is the combination of an EnsembleHop (to the next
    ensemble up) with incrementing the replica ID.
    """
    def __init__(self, bias=None, shooters=None, ensembles=None):
        """
        Parameters
        ----------
        bias : None
            not used yet, only for API consistency and later implementation
        shooters : list of ShootingMovers
            list of ShootingMovers for each ensemble
        ensembles : list of Ensembles
            list of ensembles the move should act on

        Notes
        -----
        The bootstrapping will use the ensembles sequentially so it requires
        that all ensembles have a reasonable overlab using shooting moves.

        """
        self.shooters = shooters
        self.bias = bias
        self.ensembles = ensembles
        initialization_logging(logger=init_log, obj=self,
                               entries=['bias', 'shooters', 'ensembles'])

        ens_pairs = [[self.ensembles[i], self.ensembles[i+1]]
                     for i in range(len(self.ensembles)-1)]

        # Bootstrapping sets numeric replica IDs. If the user wants it done
        # differently, the user can change it.
        self._ensemble_dict = {ens: rep for rep, ens in enumerate(ensembles)}

        # Create all possible hoppers so we do not have to recreate these
        # every time which will result in more efficient storage
        mover = paths.LastAllowedMover([
            # writing an algorithm this convoluted can get you shot in Texas
            paths.PartialAcceptanceSequentialMover(
                movers=[
                    shoot,
                    paths.EnsembleHopMover(
                        ensemble=enss[0],
                        target_ensemble=enss[1],
                        change_replica=self._ensemble_dict[enss[1]]
                    )
                ]
            ) for (enss, shoot) in zip(ens_pairs, shooters)
        ])

        super(BootstrapPromotionMove, self).__init__(mover)


class Bootstrapping(PathSimulator):
    """Creates a SampleSet with one sample per ensemble.

    The ensembles for the Bootstrapping pathsimulator must be one ensemble
    set, in increasing order. Replicas are named numerically.
    """

    calc_name = "Bootstrapping"

    def __init__(
            self,
            storage,
            engine=None,
            movers=None,
            trajectory=None,
            ensembles=None
    ):
        """
        Parameters
        ----------
        storage : openpathsampling.storage.Storage
            the storage all results should be stored in
        engine : openpathsampling.DynamicsEngine
            the dynamics engine to be used
        movers : list of openpathsampling.PathMover
            list of shooters to be used in the BootstrapPromotionMove
        trajectory : openpathsampling.Trajectory
            an initial trajectory to be started from
        ensembles : nested list of openpathsampling.Ensemble
            the ensembles this move should act on
        """
        # TODO: Change input from trajectory to sample
        super(Bootstrapping, self).__init__(storage)
        self.engine = engine
        paths.EngineMover.default_engine = engine  # set the default
        self.ensembles = ensembles
        self.trajectory = trajectory

        sample = paths.Sample(
            replica=0,
            trajectory=trajectory,
            ensemble=self.ensembles[0]
        )

        self.sample_set = paths.SampleSet([sample])

        if movers is None:
            pass # TODO: implement defaults: one per ensemble, uniform sel
        else:
            self.movers = movers
        initialization_logging(init_log, self,
                               ['movers', 'ensembles'])
        init_log.info("Parameter: %s : %s", 'trajectory', str(trajectory))

        self._bootstrapmove = BootstrapPromotionMove(
            bias=None,
            shooters=self.movers,
            ensembles=self.ensembles
        )

    def run(self, n_steps):
        bootstrapmove = self._bootstrapmove

        cvs = []
        n_samples = 0

        if self.storage is not None:
            cvs = list(self.storage.cvs)
            n_samples = len(self.storage.snapshots)

        ens_num = len(self.sample_set)-1

        if self.step == 0:
            self.save_initial_step()

        failsteps = 0
        # if we fail n_steps times in a row, kill the job

        while ens_num < len(self.ensembles) - 1 and failsteps < n_steps:
            self.step += 1
            logger.info("Step: " + str(self.step)
                        + "   Ensemble: " + str(ens_num)
                        + "  failsteps = " + str(failsteps)
                       )
            paths.tools.refresh_output(
                ("Working on Bootstrapping cycle step %d" +
                 " in ensemble %d/%d .\n") %
                (self.step, ens_num + 1, len(self.ensembles)),
                output_stream=self.output_stream,
                refresh=self.allow_refresh
            )

            movepath = bootstrapmove.move(self.sample_set)
            samples = movepath.results
            new_sampleset = self.sample_set.apply_samples(samples)

#            samples = movepath.results
#            logger.debug("SAMPLES:")
#            for sample in samples:
#                logger.debug("(" + str(sample.replica)
#                             + "," + str(sample.trajectory)
#                             + "," + repr(sample.ensemble)
#                            )


            mcstep = MCStep(
                simulation=self,
                mccycle=self.step,
                previous=self.sample_set,
                active=new_sampleset,
                change=movepath
            )


#            logger.debug("GLOBALSTATE:")
#            for sample in self.sample_set:
#                logger.debug("(" + str(sample.replica)
#                             + "," + str(sample.trajectory)
#                             + "," + repr(sample.ensemble)
#                            )



            if self.storage is not None:
                # compute all cvs now
                for cv in cvs:
                    n_len = len(self.storage.snapshots)
                    cv(self.storage.snapshots[n_samples:n_len])
                    n_samples = n_len

                self.storage.steps.save(mcstep)

            self.sample_set = new_sampleset

            old_ens_num = ens_num
            ens_num = len(self.sample_set)-1
            if ens_num == old_ens_num:
                failsteps += 1

            if self.step % self.save_frequency == 0:
                self.sample_set.sanity_check()
                self.sync_storage()

        self.sync_storage()

        paths.tools.refresh_output(
            ("DONE! Completed Bootstrapping cycle step %d"
             + " in ensemble %d/%d.\n") %
            (self.step, ens_num + 1, len(self.ensembles)),
            output_stream=self.output_stream,
            refresh=self.allow_refresh
        )


[docs] class FullBootstrapping(PathSimulator): """ Takes a snapshot as input; gives you back a sampleset with trajectories for every ensemble in the transition. This includes Parameters ---------- transition : :class:`.TISTransition` the TIS transition to fill by bootstrapping snapshot : :class:`.Snapshot` the initial snapshot storage : :class:`.Storage` storage file to record the steps (optional) engine : :class:`.DynamicsEngine` MD engine to use for dynamics extra_interfaces : list of :class:`.Volume` additional interfaces to make into TIS ensembles (beyond those in the transition) extra_ensembles : list of :class:`.Ensemble` additional ensembles to sample after the TIS ensembles forbidden_states : list of :class:`.Volume` regions that are disallowed during the initial trajectory. Note that these region *are* allowed during the interface sampling initial_max_length : int maximum length of the initial A->A trajectory """ calc_name = "FullBootstrapping"
[docs] def __init__(self, transition, snapshot, storage=None, engine=None, extra_interfaces=None, extra_ensembles=None, forbidden_states=None, initial_max_length=None): super(FullBootstrapping, self).__init__(storage) self.engine = engine paths.EngineMover.default_engine = engine # set the default if extra_interfaces is None: extra_interfaces = list() if forbidden_states is None: forbidden_states = list() interface0 = transition.interfaces[0] ensemble0 = transition.ensembles[0] state = transition.stateA self.state = state self.first_traj_ensemble = paths.SequentialEnsemble([ paths.OptionalEnsemble(paths.AllOutXEnsemble(state)), paths.AllInXEnsemble(state), paths.OptionalEnsemble( paths.AllOutXEnsemble(state) & paths.AllInXEnsemble(interface0) ), paths.OptionalEnsemble(paths.AllInXEnsemble(interface0)), paths.AllOutXEnsemble(interface0), paths.OptionalEnsemble(paths.AllOutXEnsemble(state)), paths.SingleFrameEnsemble(paths.AllInXEnsemble(state)) ]) & paths.AllOutXEnsemble(paths.join_volumes(forbidden_states)) self.initial_max_length = initial_max_length if self.initial_max_length is not None: self.first_traj_ensemble = ( paths.LengthEnsemble(slice(0, self.initial_max_length)) & self.first_traj_ensemble ) if extra_ensembles is None: extra_ensembles = [] self.extra_ensembles = [ paths.TISEnsemble(transition.stateA, transition.stateB, iface, transition.orderparameter) for iface in extra_interfaces ] + extra_ensembles self.transition_shooters = [ paths.OneWayShootingMover(selector=paths.UniformSelector(), ensemble=ens, engine=self.engine) for ens in transition.ensembles ] self.extra_shooters = [ paths.OneWayShootingMover(selector=paths.UniformSelector(), ensemble=ens, engine=self.engine) for ens in self.extra_ensembles ] self.snapshot = snapshot.copy() self.ensemble0 = ensemble0 self.all_ensembles = transition.ensembles + self.extra_ensembles self.n_ensembles = len(self.all_ensembles) self.error_max_rounds = True
def run(self, max_ensemble_rounds=None, n_steps_per_round=20, build_attempts=20): #print first_traj_ensemble #DEBUG has_AA_path = False subtraj = None while not has_AA_path: self.engine.current_snapshot = self.snapshot.copy() self.engine.snapshot = self.snapshot.copy() self.output_stream.write("Building first trajectory\n") sys.stdout.flush() first_traj = self.engine.generate( self.engine.current_snapshot, [self.first_traj_ensemble.can_append] ) self.output_stream.write("Selecting segment\n") sys.stdout.flush() subtrajs = self.ensemble0.split(first_traj) if len(subtrajs) > 0: # if we have a short enough path go ahead subtraj = subtrajs[0] # check that this is A->A as well has_AA_path = self.state(subtraj[-1]) \ and self.state(subtraj[0]) build_attempts -= 1 if build_attempts == 0: raise RuntimeError( 'Too many attempts. Try another initial snapshot instead.') self.output_stream.write("Sampling " + str(self.n_ensembles) + " ensembles.\n") bootstrap = paths.Bootstrapping( storage=self.storage, ensembles=self.all_ensembles, movers=self.transition_shooters + self.extra_shooters, trajectory=subtraj ) bootstrap.output_stream = self.output_stream self.output_stream.write("Beginning bootstrapping\n") n_rounds = 0 n_filled = len(bootstrap.sample_set) while n_filled < self.n_ensembles: bootstrap.run(n_steps_per_round) if n_filled == len(bootstrap.sample_set): n_rounds += 1 else: n_rounds = 0 if n_rounds == max_ensemble_rounds: # hard equality instead of inequality so that None gives us # effectively infinite (rounds add one at a time msg = ("Too many rounds of bootstrapping: " + str(n_rounds) + " round of " + str(n_steps_per_round) + " steps.") if self.error_max_rounds: raise RuntimeError(msg) else: # pragma: no cover logger.warning(msg) break n_filled = len(bootstrap.sample_set) return bootstrap.sample_set