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

Tests and Bugfixes for the GB potential #3091

Merged
merged 20 commits into from
Aug 23, 2019
Merged

Tests and Bugfixes for the GB potential #3091

merged 20 commits into from
Aug 23, 2019

Conversation

Kischy
Copy link
Contributor

@Kischy Kischy commented Aug 21, 2019

Fixes #2922

Description of changes:

  • Added tests for the gb potential for the force and torque
  • Fixed bugs in gay_berne.hpp
  • Removed condition in gay_berne.hpp

PR Checklist

  • Tests?
    • Interface
    • Core
  • Docs?

@Kischy Kischy changed the title Tests and Bugfixes for the GB potential #2922 Tests and Bugfixes for the GB potential Aug 21, 2019
@jngrad jngrad self-requested a review August 21, 2019 14:48
@codecov
Copy link

codecov bot commented Aug 21, 2019

Codecov Report

Merging #3091 into python will increase coverage by <1%.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##           python   #3091    +/-   ##
=======================================
+ Coverage      83%     83%   +<1%     
=======================================
  Files         530     530            
  Lines       26100   26102     +2     
=======================================
+ Hits        21915   21919     +4     
+ Misses       4185    4183     -2
Impacted Files Coverage Δ
src/core/nonbonded_interactions/gay_berne.hpp 98% <100%> (+2%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d231cd3...975bc21. Read the comment docs.

@jngrad
Copy link
Member

jngrad commented Aug 21, 2019

So far we've always used analytical solutions to calculate reference forces. Using a numerical approximation of the gradient is an interesting approach. I'm actually surprised that forces calculated with an integral step of 2e-7 can pass a self.assertAlmostEqual test at precision 1e-7. I've started the tests on non-amd64 architectures (pipeline 8879) to see if there is a risk of numerical instability.

After re-organizing my version of the force function to match the nomenclature of the variables in the energy function, I still can't follow the force code. For example, in the calculation of epsilon, at line:

auto const E = 4 * ia_params->gay_berne.eps *
pow(E1, ia_params->gay_berne.nu) *
pow(E2, ia_params->gay_berne.mu);

I would expect variable E2 to be raised to the power of mu - 1 in a subsequent variable to evaluate the derivative (d epsilon / d r), but I can't find it. It would be very helpful if we had the formula of the GB force, possibly with the intermediate steps to obtain it from the derivative of energy. We could write them with LaTeX in a Doxygen page, like we did for the derivation of the bond angle force.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

I've started the tests on non-amd64 architectures (pipeline 8879) to see if there is a risk of numerical instability.

I saw that two of them failed. This one failed because the reaction_ensemble test had a timeout. The second one failed because of different timeouts and fails, non of which was the interactions_non-bonded.py. Do you know why those failed?

So far we've always used analytical solutions to calculate reference forces. Using a numerical approximation of the gradient is an interesting approach. I'm actually surprised that forces calculated with an integral step of 2e-7 can pass a self.assertAlmostEqual test at precision 1e-7.

I think this way testing derivatives can be very powerful, if the analytical solution is long or complicated. Although it is much slower to calculate the derivative this way, it is not so prone to error. To advertise the approach a little bit more: If I had just implemented the same analytical solution used in the c++ code for testing I would not have found the error. I borrowed the idea from machine learning, see here and here.

I would expect variable E2 to be raised to the power of mu - 1 in a subsequent variable to evaluate the derivative (d epsilon / d r), but I can't find it.

It is a little bit confusing. The change in power comes with this line:

auto Koef1 = ia_params->gay_berne.mu / E2;

And with this line:

auto Koef2 = int_pow<3>(Sigma) * 0.5 / int_pow<3>(ia_params->gay_berne.sig);

And also with these lines:

auto const Bra12 = 6 * X6 * X * (2 * X6 - 1);
auto const Bra12Cut = 6 * Xcut6 * Xcut * (2 * Xcut6 - 1);

It would be very helpful if we had the formula of the GB force, possibly with the intermediate steps to obtain it from the derivative of energy.

If you want I can send you my jupyter notebook (using python 3) or the python script, where I did the calculations of the derivative.

We could write them with LaTeX in a Doxygen page, like we did for the derivation of the bond angle force.

The problem is that I do not know what paper the orginal author used, so I can't cite anything. I will try to work out a new nomenclatur for the problem and then we can put it in latex code, if you want.

* add extra intermediate variables
* use same variable names for identical or very similar
  intermediate quantities in the force/energy kernels
* simplify code logic with vector/scalar utility functions
@jngrad
Copy link
Member

jngrad commented Aug 22, 2019

The non-amd tests are known to sometimes false trigger, that's why we don't run them automatically. The gradient approach seems to work, though. Your UFLDL link states that an epsilon of 1e-4 will usually provide results at 4 digits precision, but in my experience a test is safer when the reference force is at least one order of magnitude more precise than the test threshold delta. Maybe @KaiSzuttor can comment on that.

The book or article used by the original author is unknown, all we know is this code is 16 years old. Given the notation in the energy function and the LaTeX formula in the docs, I would bet on Cleaver et al. 1996, equations 3-4 and 7-9.

Here is my modified version of the GB force function. Although it looks more verbose, when compiled with -O3 it generates 23 less lines of assembly code in gcc and 13 less in Clang (9% resp. 6% less). We could probably reduce the assembly code further with more refactoring based on the analytical expression of the force. I've looked at several sources on the GB potential, but none of them give the expression of its derivative in Cartesian space. If you already worked out the derivation, you can share it here by attaching a file.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

The non-amd tests are known to sometimes false trigger, that's why we don't run them automatically. The gradient approach seems to work, though. Your UFLDL link states that an epsilon of 1e-4 will usually provide results at 4 digits precision, but in my experience a test is safer when the reference force is at least one order of magnitude more precise than the test threshold delta. Maybe @KaiSzuttor can comment on that.

Okay, I will change the epsilon of my force calculation and update the pull request.

Here is my modified version of the GB force function. Although it looks more verbose, when compiled with -O3 it generates 23 less lines of assembly code in gcc and 13 less in Clang (9% resp. 6% less). We could probably reduce the assembly code further with more refactoring based on the analytical expression of the force. I've looked at several sources on the GB potential, but none of them give the expression of its derivative in Cartesian space. If you already worked out the derivation, you can share it here by attaching a file.

I worked on a better nomenclature just now too. If it is okay with you I would combine my version with yours and we see how it fits. What do you think?

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

I can't attach the files here, because the file type is not supported by github. I will send them to you by mail.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

Here is my modified version of the GB force function.

I think you're version is the best we can do. Can you just add the changed version to my pull request or should I do so?

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

Your UFLDL link states that an epsilon of 1e-4 will usually provide results at 4 digits precision, but in my experience a test is safer when the reference force is at least one order of magnitude more precise than the test threshold delta.

I added a commit that changes the delta value to 1x10E-6. For some reason it does not show here yet.

@jngrad
Copy link
Member

jngrad commented Aug 22, 2019

You can use my version. The simplest way to do it is to merge my branch into your branch (this is a trivial fast-forward), then add the new commit that changes the precision. Doing it the other way around (adding the precision commit, then merging my branch) would require a complex merge commit.

Also, I would not change delta to a bigger value, instead I would change epsilon to 1x10E-8.

@jngrad
Copy link
Member

jngrad commented Aug 22, 2019

You probably forgot to push the new commit to the GitHub repository. If it's ok with you, I can add my new version into your branch on GitHub. Then on you computer, you'll have to discard the precision commit (for example with git reset --soft HEAD^ to rewind the branch history to d79e9c1), then pull your GitHub branch into your local repository.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

No, it is already pushed. I'll merge your commit, change the delta and epsilon and push again.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

Also, I would not change delta to a bigger value, instead I would change epsilon to 1x10E-8.

The value of 1x10E-8 does not work, probably because of the numerical errors mentioned in the UFLDL link . I think 1x10E-7 is the lowest we can go here. Therefore I would recommend changing the delta value to 1x10E-6. Is that fine with you or not?

@@ -596,6 +596,20 @@ def gay_berne_potential(r_ij, u_i, u_j, epsilon_0, sigma_0, mu, nu, k_1, k_2):
return 4. * epsilon * (rr**-12 - rr**-6)


def calc_derivative(func, x, eps=1.0e-7, index=None):
Copy link
Member

Choose a reason for hiding this comment

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

could you please use scipy.misc.derivative. If that does not suit your needs you should definitely add a docstring to this function. Actually we could even use sympy. But we probably don't want to depend on that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add a docstring. scipy.misc.derivative does not allow for a multidimensional input.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does that help? I need to manipulate the element at a specific index while the rest stays the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a docstring. I could have thrown an error if someone gives a multidimensional input and index=None, but I think it would be a "overkill". In the docstring it is stated, that the index needs to be specified if x is an array.

@jngrad
Copy link
Member

jngrad commented Aug 22, 2019

I've just discussed it with Kai, and we're both fine with a larger delta.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 22, 2019

I've just discussed it with Kai, and we're both fine with a larger delta.

Thanks for the update.

Added docstring to calc_derivative
changed delta value in test_gb
@jngrad
Copy link
Member

jngrad commented Aug 23, 2019

bors r+

bors bot added a commit that referenced this pull request Aug 23, 2019
3091: Tests and Bugfixes for the GB potential r=jngrad a=Kischy

Fixes #2922

Description of changes:
 - Added tests for the gb potential for the force and torque
 - Fixed bugs in gay_berne.hpp
 - Removed condition in gay_berne.hpp

PR Checklist
------------
 - [x] Tests?
   - [ ] Interface
   - [x] Core 
 - [ ] Docs?


Co-authored-by: Christian Kischy Haege <[email protected]>
Co-authored-by: Jean-Noël Grad <[email protected]>
Co-authored-by: Christian Haege <[email protected]>
@bors
Copy link
Contributor

bors bot commented Aug 23, 2019

Canceled

@KaiSzuttor
Copy link
Member

I was not yet done...

@KaiSzuttor
Copy link
Member

I think we should have all reviewers approve the PR before merging

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

You changed the interface of the gradient function so that x0: (3,)? Does that matter that much now that it is specific to the gb_test?

Could you also update the Docstring?
Point in N-dimensional space where the derivatives are calculated
to
Point in 3-dimensional space where the derivatives are calculated

@KaiSzuttor
Copy link
Member

the scope of the function is limited to test_gb. Have you ever calculated a gradient in more than three dimensions?

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

the scope of the function is limited to test_gb.

That's why I thought that it doesn't matter.

Have you ever calculated a gradient in more than three dimensions?

No

@KaiSzuttor
Copy link
Member

I thought it's just more readable

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

Yeah, you are right.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

Can you exchange every "derivative" with "gradient" in the Docstring? I think than we are done. :)

@KaiSzuttor
Copy link
Member

sorry for the long discussion, but I think we overall improved readability. Thanks for your contribution!

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

@KaiSzuttor I like your implementation of the gradiant function, it is quite compact. However it might be slower, which does not matter in this case. Just keep that in mind, if you ever use it heavily somewhere else.

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

sorry for the long discussion, but I think we overall improved readability. Thanks for your contribution!

Yes, it looks better now. Thank you. I'm glad I could help. And no worries about the discussion.

@KaiSzuttor
Copy link
Member

However it might be slower

Why?

@Kischy
Copy link
Contributor Author

Kischy commented Aug 23, 2019

However it might be slower

Why?

There will be one lambda for each call to the function. That will make it slower in comparison. I shouldn't have brought it up, because it does not matter.

@KaiSzuttor
Copy link
Member

bors r+

bors bot added a commit that referenced this pull request Aug 23, 2019
3091: Tests and Bugfixes for the GB potential r=KaiSzuttor a=Kischy

Fixes #2922

Description of changes:
 - Added tests for the gb potential for the force and torque
 - Fixed bugs in gay_berne.hpp
 - Removed condition in gay_berne.hpp

PR Checklist
------------
 - [x] Tests?
   - [ ] Interface
   - [x] Core 
 - [ ] Docs?


Co-authored-by: Christian Kischy Haege <[email protected]>
Co-authored-by: Jean-Noël Grad <[email protected]>
Co-authored-by: Christian Haege <[email protected]>
Co-authored-by: Kai Szuttor <[email protected]>
@bors
Copy link
Contributor

bors bot commented Aug 23, 2019

Build succeeded

@bors bors bot merged commit 975bc21 into espressomd:python Aug 23, 2019
@Kischy Kischy deleted the Bugfix_Tests_Gay_Berne_Issue_2922 branch August 23, 2019 11:15
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.

Gay-Berne has low test coverage
3 participants