-
Notifications
You must be signed in to change notification settings - Fork 89
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
Pymbar4 bootstrapping #448
Conversation
@mrshirts : I should be able to test this out in the next couple days. What's your expected timeline for getting this merged and pymbar 4 released? The reason I ask is because I am generating data for a paper and ideally I would be able to use a stable version of pymbar to do the bootstrapping. @mikemhenry is planning on fixing the pymbar 3 unit tests (which will take about a day) so that we can get #302 merged in and then put out a minor release, but if you're planning on having pymbar 4 released very soon (this week), then maybe its not worth his effort. |
I would like to do pymbar4 in the next ~10 days or so - depends mostly on other stakeholders signing on. I would not like to extend pymbar3 much longer. I have already identified a problem in pymbar3 bootstrapping in #302 (it bootstraps among ALL samples, rather than bootstrapping among samples at each K), so I don't know how much to trust the #302 version exactly. Additional testing of this branch by people would be useful and ensure that it gets out soon! |
@mrshirts : I had a discussion today with @jchodera and the perses / openmmtools developers -- since our tools depend on pymbar3, we are willing to take over maintaining pymbar3, so I think @mikemhenry will work on cutting a bugfix release that fixes the CI and contains the bootstrap branch you wrote awhile ago (#302). I'll make sure to carefully compare the code in this branch with that of the older branch and will fix any bugs in the old branch. I will also test both branches to make sure they yield similar results. Will post an update once I've done this! |
I have concerns about maintaining multiple branches. I think that it's fine to keep pymbar3 alive for a while to make sure that toolchains have time to adapt, and port any bugfixes back, but I think it's better to keep new development linear. The output formats of pymbar3 are really restrictive in addition. Note that #302 is incorrect in at least one way (randomly selects from all samples, instead of ones from one point), and it doesn't support bootstrapping of expectations, etc. @zhang-ivy, shoot me an email at Colorado address and we can set up a time to talk over things with @jchodera / perses developers? I'd rather allocate time in helping people to move forward rather than supporting old, out-of-date code. |
@zhang-ivy are you going to make a new PR with just the bootstrapping stuff, or take overt this one? I just want to make sure either on this PR or a future PR, the correct branch |
@mikemhenry : This branch is for pymbar4, the one that we want to merge into the pymbar3 long term maintenance branch is in #302 |
Yes, so what we want is once #302 is merged in to |
The failing histogram test just seems to be that a histogram with random samples is not getting data in that histogram. I will check over that problem again - maybe the way it's handled needs to be tweaked so this is less likely to happen. |
Yes! |
@Lnaden, thoughts about adjusting the pymbar tests relating to uncertainties? Specifically, what would you look at for the fact that we can't just add 'bootstrap' as an uncertainty_method variable, since MBAR needs to be initialized with bootstraps for this to happen? So therefore, when |
pymbar/fes.py
Outdated
@@ -2415,16 +2416,20 @@ def ddexpf(x, k_inner): | |||
h[i, j] += pE / pF[k] | |||
|
|||
elif spline_weights == "unbiasedstate": | |||
|
|||
def ddexpf(x, index_i, index_j): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the function is supposed to be inside of the loop, as the function (if I recall!) need to be redefined with the loop. I remember struggling with the right way to do this. Now, there's probably a better way to do it than I did, but I will need to go back and make sure the behavior hasn't changed. Before release at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see what you did. OK, I think that will work, I will double check to make sure. It probably improves performance . . .
A couple of options. The simplest is to do nothing special and just initialize the There is a separate option where we can rebuild the tests to pass bootstrap parameters, but I think that adds far more complexity than is needed here. Let me do a quick tinkering to test bootstrap uncertainty method and to add bad initialization options to make sure things fail correctly as well. |
Ah, I didn't realize that! And 1000 bootstraps are not needed. 200 should be plenty for decent uncertainties (so 3 seconds?) - maybe even 50, but then you might get a few statistically off samples. |
Because the mbar_and_test fixture is module scope, the bootstrap happens once per parameterization. This adds a couple seconds total to the whole test suite at 200.
…into pymbar4_bootstrapping
Is it returning a successful result in the end? Then it is working as intended (it tried 'hybr', then something else). Maybe there should be an additional message saying what method succeeded? |
One other question that would be great to know on a hard system - how does the pymbar4 bootstrapping work for entropy and enthalply errors? (n_bootstraps in initializations and uncertainty_method = "bootstrap" in compute_entropy_and_enthalpy). |
Thanks for checking these! |
Yes, its returning a successful result. I think adding an additional message with the method that succeeded would be nice!
Just saw this comment: https://github.com/choderalab/pymbar/pull/441/files#diff-39154a13f665187718c90bf1b7a14a9edb282028dd0fbd94a8dfb02168e42580R648-R652
Do you think we should change
Sure I can check this later tonight! |
In the current code, it's something like 4x(the smallest mantissa accessible to numpy, like 1e-15). That has worked so far. 0.001 is not so good, since then numbers near in magnitude 0.001 can't be averaged. |
Great! |
@mrshirts : I just tested bootstrapping for # Test compute_free_energy_differences()
decorrelated_u_ln, decorrelated_N_l = analyzer._compute_mbar_decorrelated_energies()
mbar = MBAR(decorrelated_u_ln, decorrelated_N_l)
results = mbar.compute_free_energy_differences(compute_uncertainty=True)
print("compute_free_energy_differences()", results['Delta_f'][0, -1], results['dDelta_f'][0, -1])
# Test compute_entropy_and_enthalpy()
results = mbar.compute_entropy_and_enthalpy()
print("compute_entropy_and_enthalpy()", results['Delta_f'][0, -1], results['dDelta_f'][0, -1], results['Delta_u'][0, -1], results['dDelta_u'][0, -1], results['Delta_s'][0, -1], results['dDelta_s'][0, -1])
# Test compute_free_energy_differences() with bootstrapping
mbar = MBAR(decorrelated_u_ln, decorrelated_N_l, n_bootstraps=200)
results = mbar.compute_free_energy_differences(compute_uncertainty=True, uncertainty_method='bootstrap')
print("compute_free_energy_differences() bootstrapped", results['Delta_f'][0, -1], results['dDelta_f'][0, -1])
# Test compute_entropy_and_enthalpy() with bootstrapping
results = mbar.compute_entropy_and_enthalpy(uncertainty_method='bootstrap')
print("compute_entropy_and_enthalpy() bootstrapped", results['Delta_f'][0, -1], results['dDelta_f'][0, -1], results['Delta_u'][0, -1], results['dDelta_u'][0, -1], results['Delta_s'][0, -1], results['dDelta_s'][0, -1])
Here are the results. For case 1 (barnase:barstar mutation):
For case 2 (ala dipeptide mutation):
Bootstrapping for free energy differences looks good (as I already found in my tests last week), but the bootstrapped uncertainty for the entropy and enthalpy looks much larger than expected... |
I just tried copying this line from pymbar4 mbar.py: logfactor = 4.0 * np.finfo(np.float64).eps
# make sure all results are larger than this number.
# We tried 1 before, but expecations that are all very small (like
# fraction folded when it is low) cannot be computed accurately.
# 0 causes warnings in the test with divide by zero, as does 1*eps (though fewer),
# and even occasionally 2*eps, so we chooose 4*eps to replace But I'm still seeing the same warning with pymbar3:
I'm not seeing this error when running pymbar4. I can take a closer look at the diff for |
Interesting, can you post the data set somewhere I can look at it? (like google drive?) It was looking OK for my tests, but maybe there's an error that didn't show up with that relatively easy test. |
Yeah, I'd have to take a look at the data set to see. |
The data for these experiments isn't too big, so i'm just going to attach them here. I included the |
Note that barnase:barstar and ala dipeptide have 24 and 12 thermodynamic states, respectively, but I've added unsampled endstates for lambda = 0 and lambda = 1 such that the states are ordered like [unsampled endstate for lambda 0, sampled endstate for lambda 0, ... sampled endstate for lambda = 1, unsampled endstate for lambda = 1], so the matrices have K = 26 and 14, respectively. Not sure this matters, but just noted it in case it does. |
Hmm. I can't seem to load them. When I unzip and run I get: with open("u_kn.npy", "rb") as f: I get: ValueError: Cannot load file containing pickled data when allow_pickle=False And when I try I get: Any thoughts? The size of u_kn.npy is 10932608. |
Hmm, this is working for me: with open("bnbs/u_kn.npy", "rb") as f:
u_kn = np.load(f, allow_pickle=True)
with open("bnbs/n_k.npy", "rb") as f:
n_k = np.load(f, allow_pickle=True) |
Hah, figured out what was doing wrong. Back to work! |
Ah, I figured out what went wrong with the expectations bootstrapping. A couple of arrays that needed to be bootstrapped were not. It appears to give very close results now. |
@mrshirts : Nice! Did you have a chance to look into why this warning is appearing for pymbar3 and not pymbar4?
If not, no worries, I will look into that this week. |
I'll try to take a look later today. |
There's one really weird point - 24090 is much lower than the other points, and is giving problems. Not sure if that affects things. |
So it fails because with the logic, there always be a number that is zero. So the idea is when subtracting the minimum value for each row, you actually subtract (minimum_value - small amount), which will result of all entries in that line to be greater than zero. So setting a slightly larger value for logfactor should solve the problem. |
Can you clarify what you're referring to when you say |
Hmm I set logfactor to Update: if I set logfactor to 0.001, the warning goes away! But earlier in this thread, you said "0.001 is not so good, since then numbers near in magnitude 0.001 can't be averaged." Also, I should note that this warning only shows up when I call |
Ah, OK, I understand better now, and why it was solved in pymbar4. When we subtract out the minimum, in order for it not to be zero, we need to subtract np.min(A) + some factor such that (A-np.min(A))/np.min(A) is greater than eps. This logic is fixed in pymbar 4, but not pymbar 3 - i.e. the factor we subtract is not too small in absolute terms, but not in relative terms; so technically A-(A_min-eps) = eps for the lowest value, but that is not enough, since if A is too big, then A-(A_min-eps) will round to 0. See improved logic in pymbar 4 on Line 872 in e8a68ff
I don't fully understand why the code was not working for very small expectations (expectations of occupation probabilities that were much lower than 1), but it clearly was not, and the new changes seem to fix this. I will continue to test with the new code to see if there are any issues we didn't get to. |
Note that the particular data set is not great, because there's one data point that is 1400 units below the rest of the data points, leading to highly likely overflow. So I guess it's a hard case! But at least you see why the pymbar 4 one does work, and can decide how much to port over. |
Thanks for the explanation! I ported the pymbar4 changes you mentioned to my local branch of pymbar3 and the divide by 0 warning goes away.
It seems like its a pretty easy fix to port the changes over (only involves changing a few lines), but I'm curious how bad is it to see the divide by 0 warning? In other words, are the returned expectations wrong when we see the warning? I would assume they aren't, but i'm not sure what the implications of dividing by 0 are on the resulting expectations. |
My understanding is that give the error, the point that is zero (which is the minimum) is left out, which may or many not affect the results. If the lowest point is very non-representative it would affect it a lot, if it was relatively representative then it would not affect things much. |
Ah I see. It sounds like it would definitely be good to port the changes to pymbar3 then. I'll create a new branch for that fix. |
Adding support for bootstrapped errors to all expectations.
Computes bootstrap uncertainties and covariances for:
These are accessed by using the uncertainty_method = "bootstrap". Note that MBAR needs to be initialized with "n_bootstrap=(some integer)" samples in order to do use this method - an error is omitted otherwise. Possibly could be done differently, but the logic is simpler this way for now.
I could use a little help in rewriting the pytests. Currently, to get bootstrap uncertainties from various functions, as one will now want to check both the default uncertainties and the bootstrap uncertainties, and MBAR needs to be initialized differently each way, which the pytests don't seem to handle.
Note this is built off of #444, so many of the differences are from that branch.