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

GlobalParameterState and changes for multi-restraint calculations #363

Merged
merged 35 commits into from
Sep 17, 2018

Conversation

andrrizzi
Copy link
Contributor

@andrrizzi andrrizzi commented Jul 21, 2018

  • Deprecated the signature of IComposableState._on_setattr to fix a bug where the objects were temporarily left in an inconsistent state when an exception was raised and caught.
  • Deprecated update_alchemical_charges in AlchemicalState in anticipation of the new implementation of the exact PME that will be based on the NonbondedForce offsets rather than updateParametersInContext().
  • Allow restraint force classes to be controlled by a parameter other than 'lambda_restraints'. This will enable multi-restraints simulations.
  • Implemented states.GlobalParametersState. This more or less generalize the logic of AlchemicalState and implement the "suffix" mechanism that we'll be able to use in multi-restraint calculations to control parameters such as lambda_restraints_restraint1, lambda_restraints_restraint2 and, in the future, in multiple alchemical region calculations. With this, the new RestraintState class in YANK can be simplified to:
class RestraintState(GlobalParameterState):
   lambda_restraints = GlobalParameterState.GlobalParameter('lambda_restraints', standard_value=1.0)

   @lambda_restraints.validator
   def lambda_restraints(self, instance, new_value):
       if not (0.0 <= new_value <= 1.0):
           raise ValueError('lambda_restraints must be between 0.0 and 1.0')
       return new_value

   # A constructor just to give parameters_name_suffix a more meaningful name.
   def __init__(restraint_name=None, **kwargs):
       super().__init__(parameters_name_suffix=restraint_name, **kwargs)

AlchemicalState will be similarly simplified, but we'll have to wait to do it until the new exact PME implementation will be in.

The default is still lambda_restraints, but this will enable multi-restraint simulations.
…callback.

This breaks backwards compatibility so the old signature is deprecated.
This change allows composable states to restore the old value if something goes wrong, thus leaving the object in a consistent state if eventual ComposableStateError are raised and catched.
In future versions, exact treatment of PME will be implemented with the NonbondedForce offset mechanism offered starting from OpenMM 7.3 instead of using updateParametersInContext so the argument will be meaningless.
This class will simplify a lot the implementation of AlchemicalState, RestraintState, and all other composable states that require controlling a global parameter.
This class also add the logic to control parameters that have a specific suffix appended to their name. This feature will enable multi-restraint and multi-alchemical-region simulations in the future.
@andrrizzi
Copy link
Contributor Author

The two failing tests (see #358) have been already fixed in #362 so we can ignore them here.

@Lnaden
Copy link
Contributor

Lnaden commented Jul 24, 2018

Is this ready for merging? If so, I am going to pull master into here to see if it fixes the tests. I want to make sure this is working as intended. Do you also want to try to fix some of the CodeClimate things its complaining about? I can take a pass through that if you have other things you are working on.

@andrrizzi
Copy link
Contributor Author

Whops! I didn't notice the failing doctests, I can fix it. I've already merged master to fix a conflict with the release history.

@andrrizzi
Copy link
Contributor Author

andrrizzi commented Jul 24, 2018

Tests finally pass! Hopefully, this is the last time I need to write special code for Python 2.

Copy link
Contributor

@Lnaden Lnaden left a comment

Choose a reason for hiding this comment

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

A couple small things I found and have questions on, if you can justify why you think they should not be changed, thats also fine

"""

def __init__(self, restraint_parameters, restrained_atom_indices1,
restrained_atom_indices2, *args, **kwargs):
restrained_atom_indices2, controlling_parameter_name,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make controlling_parameter_name have a default? E.g. controlling_parameter_name = 'lambda_restraints'? Or would we instead just want controlling_parameter_name = 'restraints' and the lambda_ prefix is assumed prepended?


"""

def __init__(self, energy_function, restraint_parameters,
restrained_atom_indices1, restrained_atom_indices2):
restrained_atom_indices1, restrained_atom_indices2,
controlling_parameter_name='lambda_restraints'):
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, see, here in this subclass, controlling_parameter_name an optional keyword arg, which changes the call from the parent function. We should make it consistent in the base class as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should make it consistent in the base class as well

Sounds good! I'll move the default to the base class and use kwargs here. I'll have to keep it buried in kwargs in the base class though because Python 2 doesn't allow having both kwargs and keyword arguments.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh yeah... I forgot about that. Might be best to leave it then and it can be removed in 0.17 when we drop Py2 officially

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I just rememberd why I did this. It's because I need to use controlling_parameter_name in the subclass so I need the default to be specified there. I wouldn't write the default in two places as it's harder to keep track of (i.e., if a dev changes only one of the two the code may become inconsistent and unintuitive).

Copy link
Contributor

Choose a reason for hiding this comment

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

Also a good point

energy_function = 'lambda_restraints * ' + energy_function
super(RadiallySymmetricBondRestraintForce, self).__init__(restraint_parameters,
[restrained_atom_index1], [restrained_atom_index2], energy_function)
energy_function = controlling_parameter_name + ' * ' + energy_function
Copy link
Contributor

Choose a reason for hiding this comment

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

So I just noticed a potential bug here. If the user passes in an energy_function which has additive terms and not wrapped in ( ), this could lead to unintended behavior, which would NOT throw an error. Off the top of my head, the only practical thing I can think of is some kind of external field or shifted function, but this is also an easy enough fix it may be worth fixing now.

e.g.

>>> energy_function = '(K/2)*r^2 + 5*r'
>>> controlling_parameter_name = 'X`
>>> controlling_parameter_name + ' * ' + energy_function
X * (K/2)*r^2 + 5*r

the 5*r term won't be scaled.

Easy fix is just controlling_parameter_name + ' * (' + energy_function + ')'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Will change it.

@Lnaden
Copy link
Contributor

Lnaden commented Jul 25, 2018

Awesome, I think this is ready. Do we want to cut a release after this?

@andrrizzi
Copy link
Contributor Author

I'd wait to release until we're sure these changes will work for the multi-restriants feature in YANK. I don't think we have an openmmtools-dev version uploaded, but we could run the tests locally after we've implemented the new features in YANK before releasing this.

@Lnaden
Copy link
Contributor

Lnaden commented Jul 26, 2018

Okay we'll hold off on the release. I'll use this branch for developing the new restraints in YANK. I'm on the fence about whether we should merge this or not. If we plan on making changes to this, then I would be hesitant to do so.

@andrrizzi
Copy link
Contributor Author

That's fine I think! Let's remember to merge it before making other changes to openmmtools though.

Fix Python 2.7 ABC reference by just removing it.
Add a check to ensure the distance variable is in the energy_string
Expanded test to ensure deserialization correctly restores Python accessed properties

Changed name of the CV Distance from `Distance` to `symmetric_restraint_distance`

Removed the `restraint_parameters` property as it was not called by any test or implementation here or in YANK. It also would have been a pain to restore correctly.

Added helper function for the regex search of energy strings for implementations to follow.

Installed NetCDF4 from pip on windows due to odd bug in Conda's NetCDF4 on windows having DLL errors for... reasons?
Try again to fix windows netcdf. Pinning libnetcdf to the last good version the netcdf4 lib built against on CondaForge
Convert RadiallySymmetricRestraint Forces to CV Forces
@Lnaden
Copy link
Contributor

Lnaden commented Aug 8, 2018

HA! HAHAHAHAHAHA! Finally got the windows tests to pass!

@andrrizzi andrrizzi mentioned this pull request Sep 9, 2018
@andrrizzi
Copy link
Contributor Author

I've reverted the commits implementing the CV restraint conversion, which are blocking this PR. Once I merge this, I'll open a separate PR reverting the reverted commits that we can merge once we've figure out a way to solve the performance issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants