Exemple #1
0
    def test_non_alchemical_protocol(self):
        """Any property of the ThermodynamicSystem can be specified in the protocol."""
        name, thermodynamic_state, sampler_state, topography = self.host_guest_implicit
        protocol = {
            'lambda_sterics': [0.0, 0.5, 1.0],
            'temperature': [300, 320, 300] * unit.kelvin,
            'update_alchemical_charges': [True, True, False]
        }
        alchemical_phase = AlchemicalPhase(sampler=ReplicaExchange())
        with self.temporary_storage_path() as storage_path:
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    protocol, storage_path, restraint=yank.restraints.Harmonic())
            self.check_protocol(alchemical_phase, protocol)

        # If temperatures of the end states is different, an error is raised.
        protocol['temperature'][-1] = 330 * unit.kelvin
        alchemical_phase = AlchemicalPhase(sampler=ReplicaExchange())
        with nose.tools.assert_raises(ValueError):
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    protocol, 'not_created.nc', restraint=yank.restraints.Harmonic())
Exemple #2
0
 def test_illegal_protocol(self):
     """An error is raised when the protocol parameters have a different number of states."""
     name, thermodynamic_state, sampler_state, topography = self.host_guest_implicit
     protocol = {
         'lambda_sterics': [0.0, 1.0],
         'lambda_electrostatics': [1.0]
     }
     alchemical_phase = AlchemicalPhase(sampler=ReplicaExchange())
     restraint = yank.restraints.Harmonic()
     with nose.tools.assert_raises(ValueError):
         alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                 protocol, 'not_created.nc', restraint=restraint)
Exemple #3
0
    def test_illegal_restraint(self):
        """Raise an error when restraint is handled incorrectly."""
        # An error is raised with ligand-receptor systems in implicit without restraint.
        test_name, thermodynamic_state, sampler_state, topography = self.host_guest_implicit
        alchemical_phase = AlchemicalPhase(sampler=ReplicaExchange())
        with nose.tools.assert_raises(ValueError):
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    self.protocol, 'not_created.nc')

        # An error is raised when trying to apply restraint to non ligand-receptor systems.
        restraint = yank.restraints.Harmonic()
        test_name, thermodynamic_state, sampler_state, topography = self.alanine_explicit
        with nose.tools.assert_raises(RuntimeError):
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    self.protocol, 'not_created.nc', restraint=restraint)
Exemple #4
0
    def test_from_storage(self):
        """When resuming, the AlchemicalPhase recover the correct sampler."""
        _, thermodynamic_state, sampler_state, topography = self.host_guest_implicit
        restraint = yank.restraints.Harmonic()

        with self.temporary_storage_path() as storage_path:
            alchemical_phase = AlchemicalPhase(ReplicaExchange())
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    self.protocol, storage_path, restraint=restraint)

            # Delete old alchemical phase to close storage file.
            del alchemical_phase

            # Resume, the sampler has the correct class.
            alchemical_phase = AlchemicalPhase.from_storage(storage_path)
            assert isinstance(alchemical_phase._sampler, ReplicaExchange)
Exemple #5
0
    def test_create(self):
        """Alchemical state correctly creates the simulation object."""
        available_restraints = list(yank.restraints.available_restraint_classes().values())

        for test_index, test_case in enumerate(self.all_test_cases):
            test_name, thermodynamic_state, sampler_state, topography = test_case

            # Add random restraint if this is ligand-receptor system in implicit solvent.
            if len(topography.ligand_atoms) > 0:
                restraint_cls = available_restraints[np.random.randint(0, len(available_restraints))]
                restraint = restraint_cls()
                protocol = self.restrained_protocol
                test_name += ' with restraint {}'.format(restraint_cls.__name__)
            else:
                restraint = None
                protocol = self.protocol

            # Add either automatic of fixed correction cutoff.
            if test_index % 2 == 0:
                correction_cutoff = 12 * unit.angstroms
            else:
                correction_cutoff = 'auto'

            # Replace the reaction field of the reference system to compare
            # also cutoff and switch width for the electrostatics.
            reference_system = thermodynamic_state.system
            mmtools.forcefactories.replace_reaction_field(reference_system, return_copy=False)

            alchemical_phase = AlchemicalPhase(sampler=ReplicaExchange())
            with self.temporary_storage_path() as storage_path:
                alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                        protocol, storage_path, restraint=restraint,
                                        anisotropic_dispersion_cutoff=correction_cutoff)

                yield prepare_yield(self.check_protocol, test_name, alchemical_phase, protocol)
                yield prepare_yield(self.check_standard_state_correction, test_name, alchemical_phase,
                                    topography)
                yield prepare_yield(self.check_expanded_states, test_name, alchemical_phase,
                                    protocol, correction_cutoff, reference_system)

                # Free memory.
                del alchemical_phase
Exemple #6
0
    def test_randomize_ligand(self):
        """Test method AlchemicalPhase.randomize_ligand."""
        _, thermodynamic_state, sampler_state, topography = self.host_guest_implicit
        restraint = yank.restraints.Harmonic()

        ligand_atoms, receptor_atoms = topography.ligand_atoms, topography.receptor_atoms
        ligand_positions = sampler_state.positions[ligand_atoms]
        receptor_positions = sampler_state.positions[receptor_atoms]

        with self.temporary_storage_path() as storage_path:
            alchemical_phase = AlchemicalPhase(ReplicaExchange())
            alchemical_phase.create(thermodynamic_state, sampler_state, topography,
                                    self.protocol, storage_path, restraint=restraint)

            # Randomize ligand positions.
            alchemical_phase.randomize_ligand()

            # The new sampler states have the same receptor positions
            # but different ligand positions.
            for sampler_state in alchemical_phase._sampler.sampler_states:
                assert np.allclose(sampler_state.positions[receptor_atoms], receptor_positions)
                assert not np.allclose(sampler_state.positions[ligand_atoms], ligand_positions)
Exemple #7
0
    def test_minimize(self):
        """Test AlchemicalPhase minimization of positions in reference state."""
        # Ligand-receptor in implicit solvent.
        test_system = testsystems.AlanineDipeptideVacuum()
        thermodynamic_state = states.ThermodynamicState(test_system.system,
                                                        temperature=300*unit.kelvin)
        topography = Topography(test_system.topology)

        # We create 3 different sampler states that will be distributed over
        # replicas in a round-robin fashion.
        displacement_vector = np.ones(3) * unit.nanometer
        positions2 = test_system.positions + displacement_vector
        positions3 = positions2 + displacement_vector
        sampler_state1 = states.SamplerState(test_system.positions)
        sampler_state2 = states.SamplerState(positions2)
        sampler_state3 = states.SamplerState(positions3)
        sampler_states = [sampler_state1, sampler_state2, sampler_state3]

        with self.temporary_storage_path() as storage_path:
            # Create alchemical phase.
            alchemical_phase = AlchemicalPhase(ReplicaExchange())
            alchemical_phase.create(thermodynamic_state, sampler_states, topography,
                                    self.protocol, storage_path)

            # Measure the average distance between positions. This should be
            # maintained after minimization.
            sampler_states = alchemical_phase._sampler.sampler_states
            original_diffs = [np.average(sampler_states[i].positions - sampler_states[i+1].positions)
                              for i in range(len(sampler_states) - 1)]

            # Minimize.
            alchemical_phase.minimize()

            # The minimized positions should be still more or less
            # one displacement vector from each other.
            sampler_states = alchemical_phase._sampler.sampler_states
            new_diffs = [np.average(sampler_states[i].positions - sampler_states[i+1].positions)
                         for i in range(len(sampler_states) - 1)]
            assert np.allclose(original_diffs, new_diffs)
Exemple #8
0
def test_unknown_parameters():
    """Test ReplicaExchange raises exception on wrong initialization."""
    ReplicaExchange(store_filename='test', wrong_parameter=False)
Exemple #9
0
def test_parameters():
    """Test ReplicaExchange parameters initialization."""
    repex = ReplicaExchange(store_filename='test', nsteps_per_iteration=1e6)
    assert repex.nsteps_per_iteration == 1000000
    assert repex.collision_rate == repex.default_parameters['collision_rate']
Exemple #10
0
def test_replica_exchange(mpicomm=None, verbose=True):
    """
    Test that free energies and average potential energies of a 3D harmonic oscillator are correctly computed by parallel tempering.

    TODO

    * Test ParallelTempering and HamiltonianExchange subclasses as well.
    * Test with different combinations of input parameters.

    """

    if verbose and ((not mpicomm) or (mpicomm.rank==0)): sys.stdout.write("Testing replica exchange facility with harmonic oscillators: ")

    # Define mass of carbon atom.
    mass = 12.0 * units.amu

    # Define thermodynamic states.
    states = list() # thermodynamic states
    Ks = [500.00, 400.0, 300.0] * units.kilocalories_per_mole / units.angstroms**2 # spring constants
    temperatures = [300.0, 350.0, 400.0] * units.kelvin # temperatures
    seed_positions = list()
    analytical_results = list()
    f_i_analytical = list() # dimensionless free energies
    u_i_analytical = list() # reduced potential
    for (K, temperature) in zip(Ks, temperatures):
        # Create harmonic oscillator system.
        testsystem = testsystems.HarmonicOscillator(K=K, mass=mass, mm=openmm)
        [system, positions] = [testsystem.system, testsystem.positions]
        # Create thermodynamic state.
        state = ThermodynamicState(system=system, temperature=temperature)
        # Append thermodynamic state and positions.
        states.append(state)
        seed_positions.append(positions)
        # Store analytical results.
        results = computeHarmonicOscillatorExpectations(K, mass, temperature)
        analytical_results.append(results)
        f_i_analytical.append(results['f'])
        kT = kB * temperature # thermal energy
        reduced_potential = results['potential']['mean'] / kT
        u_i_analytical.append(reduced_potential)

    # Compute analytical Delta_f_ij
    nstates = len(f_i_analytical)
    f_i_analytical = numpy.array(f_i_analytical)
    u_i_analytical = numpy.array(u_i_analytical)
    s_i_analytical = u_i_analytical - f_i_analytical
    Delta_f_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_u_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    Delta_s_ij_analytical = numpy.zeros([nstates,nstates], numpy.float64)
    for i in range(nstates):
        for j in range(nstates):
            Delta_f_ij_analytical[i,j] = f_i_analytical[j] - f_i_analytical[i]
            Delta_u_ij_analytical[i,j] = u_i_analytical[j] - u_i_analytical[i]
            Delta_s_ij_analytical[i,j] = s_i_analytical[j] - s_i_analytical[i]

    # Define file for temporary storage.
    import tempfile # use a temporary file
    file = tempfile.NamedTemporaryFile(delete=False)
    store_filename = file.name
    #print("node %d : Storing data in temporary file: %s" % (mpicomm.rank, str(store_filename))) # DEBUG

    # Create and configure simulation object.
    simulation = ReplicaExchange(store_filename, mpicomm=mpicomm)
    simulation.create(states, seed_positions)
    simulation.platform = openmm.Platform.getPlatformByName('Reference')
    simulation.minimize = False
    simulation.number_of_iterations = 200
    simulation.nsteps_per_iteration = 500
    simulation.timestep = 2.0 * units.femtoseconds
    simulation.collision_rate = 20.0 / units.picosecond
    simulation.verbose = False
    simulation.show_mixing_statistics = False
    simulation.online_analysis = True

    # Run simulation.
    utils.config_root_logger(False)
    simulation.run() # run the simulation
    utils.config_root_logger(True)

    # Stop here if not root node.
    if mpicomm and (mpicomm.rank != 0): return

    # Retrieve extant analysis object.
    online_analysis = simulation.analysis

    # Analyze simulation to compute free energies.
    analysis = simulation.analyze()

    # Check if online analysis is close to final analysis.
    error = numpy.abs(online_analysis['Delta_f_ij'] - analysis['Delta_f_ij'])
    derror = (online_analysis['dDelta_f_ij']**2 + analysis['dDelta_f_ij']**2)
    indices = numpy.where(derror > 0.0)
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / derror[indices]
    MAX_SIGMA = 6.0 # maximum allowed number of standard errors
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_f_ij from online analysis")
        print(online_analysis['Delta_f_ij'])
        print("Delta_f_ij from final analysis")
        print(analysis['Delta_f_ij'])
        print("error")
        print(error)
        print("derror")
        print(derror)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless free energy differences between online and final analysis exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    # TODO: Check if deviations exceed tolerance.
    Delta_f_ij = analysis['Delta_f_ij']
    dDelta_f_ij = analysis['dDelta_f_ij']
    error = numpy.abs(Delta_f_ij - Delta_f_ij_analytical)
    indices = numpy.where(dDelta_f_ij > 0.0)
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    MAX_SIGMA = 6.0 # maximum allowed number of standard errors
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_f_ij")
        print(Delta_f_ij)
        print("Delta_f_ij_analytical")
        print(Delta_f_ij_analytical)
        print("error")
        print(error)
        print("stderr")
        print(dDelta_f_ij)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless free energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    error = analysis['Delta_u_ij'] - Delta_u_ij_analytical
    nsigma = numpy.zeros([nstates,nstates], numpy.float32)
    nsigma[indices] = error[indices] / dDelta_f_ij[indices]
    if numpy.any(nsigma > MAX_SIGMA):
        print("Delta_u_ij")
        print(analysis['Delta_u_ij'])
        print("Delta_u_ij_analytical")
        print(Delta_u_ij_analytical)
        print("error")
        print(error)
        print("nsigma")
        print(nsigma)
        raise Exception("Dimensionless potential energy difference exceeds MAX_SIGMA of %.1f" % MAX_SIGMA)

    # Clean up.
    del simulation

    if verbose: print("PASSED.")
    return
Exemple #11
0
    def setup_class(cls):
        """Shared test cases and variables."""
        n_states = 3
        n_steps = 5
        checkpoint_interval = 2

        # Test case with host guest in vacuum at 3 different positions and alchemical parameters.
        # -----------------------------------------------------------------------------------------
        hostguest_test = testsystems.HostGuestVacuum()
        factory = mmtools.alchemy.AbsoluteAlchemicalFactory()
        alchemical_region = mmtools.alchemy.AlchemicalRegion(
            alchemical_atoms=range(126, 156))
        hostguest_alchemical = factory.create_alchemical_system(
            hostguest_test.system, alchemical_region)

        # Translate the sampler states to be different one from each other.
        hostguest_sampler_states = [
            mmtools.states.SamplerState(hostguest_test.positions +
                                        10 * i * unit.nanometers)
            for i in range(n_states)
        ]

        # Create the three basic thermodynamic states.
        hostguest_thermodynamic_states = [
            mmtools.states.ThermodynamicState(hostguest_alchemical,
                                              300 * unit.kelvin)
            for i in range(n_states)
        ]

        # Create alchemical states at different parameter values.
        alchemical_states = [
            mmtools.alchemy.AlchemicalState.from_system(hostguest_alchemical)
            for _ in range(n_states)
        ]
        for i, alchemical_state in enumerate(alchemical_states):
            alchemical_state.set_alchemical_parameters(
                float(i) / (n_states - 1))

        # Create compound states.
        hostguest_compound_states = list()
        for i in range(n_states):
            hostguest_compound_states.append(
                mmtools.states.CompoundThermodynamicState(
                    thermodynamic_state=hostguest_thermodynamic_states[i],
                    composable_states=[alchemical_states[i]]))

        # Unsampled states.
        nonalchemical_state = mmtools.states.ThermodynamicState(
            hostguest_test.system, 300 * unit.kelvin)
        hostguest_unsampled_states = [
            copy.deepcopy(nonalchemical_state),
            copy.deepcopy(nonalchemical_state)
        ]

        cls.hostguest_test = (hostguest_compound_states,
                              hostguest_sampler_states,
                              hostguest_unsampled_states)

        # Run a quick simulation
        thermodynamic_states, sampler_states, unsampled_states = copy.deepcopy(
            cls.hostguest_test)
        n_states = len(thermodynamic_states)

        # Remove one sampler state to verify distribution over states.
        sampler_states = sampler_states[:-1]

        # Prepare metadata for analysis.
        reference_state = mmtools.states.ThermodynamicState(
            hostguest_test.system, 300 * unit.kelvin)
        topography = Topography(hostguest_test.topology,
                                ligand_atoms=range(126, 156))
        metadata = {
            'standard_state_correction': 4.0,
            'reference_state': mmtools.utils.serialize(reference_state),
            'topography': mmtools.utils.serialize(topography)
        }
        analysis_atoms = topography.ligand_atoms

        # Create simulation and storage file.
        cls.tmp_dir = tempfile.mkdtemp()
        storage_path = os.path.join(cls.tmp_dir, 'test_analyze.nc')
        move = mmtools.mcmc.LangevinDynamicsMove(n_steps=1)
        cls.repex = ReplicaExchange(mcmc_moves=move,
                                    number_of_iterations=n_steps)
        cls.reporter = Reporter(storage_path,
                                checkpoint_interval=checkpoint_interval,
                                analysis_particle_indices=analysis_atoms)
        cls.repex.create(thermodynamic_states,
                         sampler_states,
                         storage=cls.reporter,
                         unsampled_thermodynamic_states=unsampled_states,
                         metadata=metadata)
        # run some iterations
        cls.n_states = n_states
        cls.n_steps = n_steps
        cls.checkpoint_interval = checkpoint_interval
        cls.analysis_atoms = analysis_atoms
        cls.repex.run(cls.n_steps - 1)  # Initial config
        cls.repex_name = "RepexAnalyzer"