Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pymbar 4 #1173

Merged
merged 17 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion devtools/conda-envs/test_env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ dependencies:
- pdbfixer
- pip
- progressbar2
- pymbar <4
- pymbar >=4
- pytest
- pytest-attrib
- pytest-cov
Expand All @@ -47,3 +47,5 @@ dependencies:
- sphinx
- tinydb
- tqdm
- pip:
- git+https://github.com/choderalab/openmmtools.git
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to install openmmtools from pip anymore, do we? Latest openmmtools release should have what we need.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this was something temporary at the time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good catch!

mikemhenry marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions perses/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import numpy as np
import itertools
import pymbar
from openmmtools.multistate.pymbar import _pymbar_bar
from perses import storage
import seaborn as sns

Expand Down Expand Up @@ -203,7 +203,7 @@ def get_free_energies(self, environment):
resampled_w_F = np.random.choice(w_F, len(w_F), replace=True)
resampled_w_R = np.random.choice(w_R, len(w_R), replace=True)

[df, ddf] = pymbar.BAR(resampled_w_F, resampled_w_R)
[df, ddf] = _pymbar_bar(resampled_w_F, resampled_w_R)
bootstrapped_bar[i] = df

free_energies[state_pair] = [np.mean(bootstrapped_bar), np.std(bootstrapped_bar)]
Expand Down
6 changes: 3 additions & 3 deletions perses/analysis/fah_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import numpy as np
import pandas as pd
import seaborn as sns
from pymbar import BAR
from openmmtools.multistate.pymbar import _pymbar_bar
import matplotlib.pyplot as plt
import seaborn
from simtk.openmm import unit
Expand Down Expand Up @@ -160,7 +160,7 @@ def _bootstrap_BAR(run, phase, gen_id, n_bootstrap):
f"less than {min_num_work_values} reverse work values (got {len(r_works)})"
)

fe, err = bootstrap_uncorrelated(BAR, n_iters=n_bootstrap)(
fe, err = bootstrap_uncorrelated(_pymbar_bar, n_iters=n_bootstrap)(
f_works.values, r_works.values
)

Expand Down Expand Up @@ -227,7 +227,7 @@ def _process_phase(i, phase):
axes[i].set_title(phase)

# TODO add bootstrapping here
d[f"{phase}_fes"] = BAR(np.asarray(all_forward), np.asarray(all_reverse))
d[f"{phase}_fes"] = _pymbar_bar(np.asarray(all_forward), np.asarray(all_reverse))

for i, phase in enumerate(projects.keys()):
try:
Expand Down
6 changes: 3 additions & 3 deletions perses/app/relative_hydration.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ def check_alchemical_hybrid_elimination_bar(topology_proposal, old_positions, ne
bar.update(i)
del reverse_context, reverse_integrator

from pymbar import BAR
[df, ddf] = BAR(w_f, w_r)
from openmmtools.multistate.pymbar import _pymbar_bar
[df, ddf] = _pymbar_bar(w_f, w_r)
print("df = %12.6f +- %12.5f kT" % (df, ddf))


if __name__=="__main__":
topology_proposal, old_positions, new_positions = generate_solvated_hybrid_test_topology()
check_alchemical_hybrid_elimination_bar(topology_proposal, old_positions, new_positions, ncmc_nsteps=100, n_iterations=500)
check_alchemical_hybrid_elimination_bar(topology_proposal, old_positions, new_positions, ncmc_nsteps=100, n_iterations=500)
14 changes: 7 additions & 7 deletions perses/app/relative_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from openmmtools.states import ThermodynamicState, CompoundThermodynamicState, SamplerState

import pymbar
from openmmtools.multistate.pymbar import _pymbar_bar, _pymbar_exp, detect_equilibration
from cloudpathlib import AnyPath
import simtk.openmm as openmm
import simtk.openmm.app as app
Expand Down Expand Up @@ -1661,11 +1661,11 @@ def compute_sMC_free_energy(self):
"""
for _direction, works in self._nonequilibrium_cumulative_work.items():
if works is not None:
self.dg_EXP[_direction] = pymbar.EXP(works[:,-1])
self.dg_EXP[_direction] = _pymbar_exp(works[:,-1])

if all(work is not None for work in self._nonequilibrium_cumulative_work.values()):
#then we can compute a BAR estimate
self.dg_BAR = pymbar.BAR(self._nonequilibrium_cumulative_work['forward'][:,-1], self._nonequilibrium_cumulative_work['reverse'][:,-1])
self.dg_BAR = _pymbar_bar(self._nonequilibrium_cumulative_work['forward'][:,-1], self._nonequilibrium_cumulative_work['reverse'][:,-1])


def attempt_resample(self, observable = 'ESS', resampling_method = 'multinomial', resample_observable_threshold = 0.5):
Expand Down Expand Up @@ -2096,7 +2096,7 @@ def _adjust_for_correlation(self, timeseries_array: np.array):
Neff_max : float
Max number of uncorrelated samples
"""
[t0, g, Neff_max] = pymbar.timeseries.detectEquilibration(timeseries_array)
[t0, g, Neff_max] = detect_equilibration(timeseries_array)

return timeseries_array[t0:], g, Neff_max

Expand Down Expand Up @@ -2147,11 +2147,11 @@ def _endpoint_perturbations(self, direction = None, num_subsamples = 100):
self._nonalchemical_reduced_potential_differences[start_lambda] = np.array([nonalchemical_reduced_potential_differences[i] for i in nonalch_full_uncorrelated_indices])

#now to bootstrap results
alchemical_exp_results = np.array([pymbar.EXP(np.random.choice(self._alchemical_reduced_potential_differences[_direction], size = (len(self._alchemical_reduced_potential_differences[_direction]))), compute_uncertainty=False) for _ in range(num_subsamples)])
alchemical_exp_results = np.array([_pymbar_exp(np.random.choice(self._alchemical_reduced_potential_differences[_direction], size = (len(self._alchemical_reduced_potential_differences[_direction]))), compute_uncertainty=False) for _ in range(num_subsamples)])
self._EXP[_direction] = (np.average(alchemical_exp_results), np.std(alchemical_exp_results)/np.sqrt(num_subsamples))
_logger.debug(f"alchemical exp result for {_direction}: {self._EXP[_direction]}")

nonalchemical_exp_results = np.array([pymbar.EXP(np.random.choice(self._nonalchemical_reduced_potential_differences[start_lambda], size = (len(self._nonalchemical_reduced_potential_differences[start_lambda]))), compute_uncertainty=False) for _ in range(num_subsamples)])
nonalchemical_exp_results = np.array([_pymbar_exp(np.random.choice(self._nonalchemical_reduced_potential_differences[start_lambda], size = (len(self._nonalchemical_reduced_potential_differences[start_lambda]))), compute_uncertainty=False) for _ in range(num_subsamples)])
self._EXP[start_lambda] = (np.average(nonalchemical_exp_results), np.std(nonalchemical_exp_results)/np.sqrt(num_subsamples))
_logger.debug(f"nonalchemical exp result for {start_lambda}: {self._EXP[start_lambda]}")

Expand All @@ -2167,7 +2167,7 @@ def _alchemical_free_energy(self, num_subsamples = 100):
work_subsamples = {'forward': [np.random.choice(self._nonequilibrium_cum_work['forward'], size = (len(self._nonequilibrium_cum_work['forward']))) for _ in range(num_subsamples)],
'reverse': [np.random.choice(self._nonequilibrium_cum_work['reverse'], size = (len(self._nonequilibrium_cum_work['reverse']))) for _ in range(num_subsamples)]}

bar_estimates = np.array([pymbar.BAR(forward_sample, reverse_sample, compute_uncertainty=False) for forward_sample, reverse_sample in zip(work_subsamples['forward'], work_subsamples['reverse'])])
bar_estimates = np.array([_pymbar_bar(forward_sample, reverse_sample, compute_uncertainty=False) for forward_sample, reverse_sample in zip(work_subsamples['forward'], work_subsamples['reverse'])])
df, ddf = np.average(bar_estimates), np.std(bar_estimates) / np.sqrt(num_subsamples)
self._BAR = [df, ddf]
return (df, ddf)
Expand Down
6 changes: 3 additions & 3 deletions perses/dispersed/smc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from perses.annihilation.lambda_protocol import LambdaProtocol
from perses.annihilation.lambda_protocol import RelativeAlchemicalState
import random
import pymbar
from openmmtools.multistate.pymbar import _pymbar_bar, _pymbar_exp
from perses.dispersed.parallel import Parallelism
# Instantiate logger
_logger = logging.getLogger("sMC")
Expand Down Expand Up @@ -726,10 +726,10 @@ def compute_sMC_free_energy(self, cumulative_work_dict):
self.dg_EXP = {}
for _direction, _lst in cumulative_work_dict.items():
self.cumulative_work[_direction] = _lst
self.dg_EXP[_direction] = np.array([pymbar.EXP(_lst[:,i]) for i in range(_lst.shape[1])])
self.dg_EXP[_direction] = np.array([_pymbar_exp(_lst[:,i]) for i in range(_lst.shape[1])])
_logger.debug(f"cumulative_work for {_direction}: {self.cumulative_work[_direction]}")
if len(list(self.cumulative_work.keys())) == 2:
self.dg_BAR = pymbar.BAR(self.cumulative_work['forward'][:, -1], self.cumulative_work['reverse'][:, -1])
self.dg_BAR = _pymbar_bar(self.cumulative_work['forward'][:, -1], self.cumulative_work['reverse'][:, -1])

def minimize_sampler_states(self):
"""
Expand Down
6 changes: 3 additions & 3 deletions perses/dispersed/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,10 @@ def compute_timeseries(reduced_potentials):
uncorrelated indices

"""
from pymbar import timeseries
t0, g, Neff_max = timeseries.detectEquilibration(reduced_potentials) #computing indices of uncorrelated timeseries
from openmmtools.multistate.pymbar import detect_equilibration, subsample_correlated_data
t0, g, Neff_max = detect_equilibration(reduced_potentials) #computing indices of uncorrelated timeseries
A_t_equil = reduced_potentials[t0:]
uncorrelated_indices = timeseries.subsampleCorrelatedData(A_t_equil, g=g)
uncorrelated_indices = subsample_correlated_data(A_t_equil, g=g)
A_t = A_t_equil[uncorrelated_indices]
full_uncorrelated_indices = [i+t0 for i in uncorrelated_indices]

Expand Down
11 changes: 5 additions & 6 deletions perses/tests/test_relative.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@
from openmmtools.integrators import LangevinIntegrator
from unittest import skipIf

import pymbar.timeseries as timeseries
from openmmtools.multistate.pymbar import _pymbar_exp, detect_equilibration
import pytest
import pymbar

running_on_github_actions = os.environ.get('GITHUB_ACTIONS', None) == 'true'

Expand Down Expand Up @@ -100,9 +99,9 @@ def calculate_cross_variance(all_results):
non_b = all_results[2]
hybrid_b = all_results[3]
print('CROSS VALIDATION')
[df, ddf] = pymbar.EXP(non_a - hybrid_b)
[df, ddf] = _pymbar_exp(non_a - hybrid_b)
print('df: {}, ddf: {}'.format(df, ddf))
[df, ddf] = pymbar.EXP(non_b - hybrid_a)
[df, ddf] = _pymbar_exp(non_b - hybrid_a)
print('df: {}, ddf: {}'.format(df, ddf))
return

Expand Down Expand Up @@ -395,9 +394,9 @@ def run_endpoint_perturbation(lambda_thermodynamic_state, nonalchemical_thermody
md.Trajectory(nonalchemical_trajectory / unit.nanometers, nonalchemical_mdtraj_topology).save(f'nonalchemical{lambda_index}.pdb')

# Analyze data and return results
[t0, g, Neff_max] = timeseries.detectEquilibration(w)
[t0, g, Neff_max] = detect_equilibration(w)
w_burned_in = w[t0:]
[df, ddf] = pymbar.EXP(w_burned_in)
[df, ddf] = _pymbar_exp(w_burned_in)
ddf_corrected = ddf * np.sqrt(g)
results = [df, ddf_corrected, t0, Neff_max]

Expand Down
8 changes: 4 additions & 4 deletions perses/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ def validate_rjmc_work_variance(top_prop, positions, geometry_method = 0, num_it
md_steps: int
number of md_steps to run in each num_iteration
compute_timeseries = bool (default False)
whether to use pymbar detectEquilibration and subsampleCorrelated data from the MD run (the potential energy is the data)
whether to use pymbar detect_equilibration and subsample_correlated data from the MD run (the potential energy is the data)
prespecified_conformers = None or unit.Quantity(np.array([num_iterations, system.getNumParticles(), 3]), unit = unit.nanometers)
whether to input a unit.Quantity of conformers and bypass the conformer_generation/pymbar stage; None will default conduct this phase

Expand Down Expand Up @@ -562,9 +562,9 @@ def validate_rjmc_work_variance(top_prop, positions, geometry_method = 0, num_it

if compute_timeseries:
print(f"computing production and data correlation")
from pymbar import timeseries
t0, g, Neff = timeseries.detectEquilibration(rps)
series = timeseries.subsampleCorrelatedData(np.arange(t0, num_iterations), g = g)
from openmmtools.multistate.pymbar import detect_equilibration
t0, g, Neff = detect_equilibration(rps)
series = subsample_correlated_data(np.arange(t0, num_iterations), g = g)
print(f"production starts at index {t0} of {num_iterations}")
print(f"the number of effective samples is {Neff}")
indices = t0 + series
Expand Down