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

Replace eigensolver code in LAMMPS with math_eigen.h and updated docs #2347

Merged
merged 31 commits into from
Sep 17, 2020

Conversation

jewettaij
Copy link
Collaborator

@jewettaij jewettaij commented Sep 6, 2020

Summary

This pull-request replaces all Numerical Recipes eigensolver code in LAMMPS with the code in "math_eigen.h", which contains a variety of eigensolvers for matrices of varying size and sparsity. It also moves "math_eigen.h" into the main "src" directory, and adds documentation (in pg_utils.rst and doxygen).

(The code in lib/colvars directory was not modified. However I have made a separate pull request to replace the Numerical Recipes code located there as well. If that pull request is accepted, hopefully that code will eventually migrate its way into LAMMPS later on.)

Related Issues

fixes #2321

Author(s)

Andrew Jewett, Scripps Research, https://moltemplate.org

Yuya Kurebayashi, Tohoku University, https://github.com/mrcdr

Licensing

By submitting this pull request, I agree, that my contribution will be included in LAMMPS and redistributed under either the GNU General Public License version 2 (GPL v2) or the GNU Lesser General Public License version 2.1 (LGPL v2.1). The code in this pull request is also compatible with the MIT license.

Backward Compatibility

Effort was made to make minimal modifications to the existing LAMMPS code. No header files were changed (other than "math_extra.h"). The "jacobi3()" functions are stand-in replacements for the "jacobi()" functions found in "math_extra.h/cpp" and "fix_poems.cpp".

As for the "math_eigen.h" file, the eigensolvers there have been tested, and their unit-tests are available at their respective github pages:

Based on that code, Yuya and I have written a (somewhat messy) rough draft of the unit test code for "math_eigen.h". But it is not included in this pull-request.

Implementation Notes

The largest bug risk comes from possible differences in the order in which the eigenvalues and eigenvectors are returned. In the new "jacobi3()" function, the eigenvalues (and eigenvectors) are sorted according to eigenvalue in decreasing order. In the original jacobi() functions, it seems that no attempt was made to sort the eigenvalues. I don't think that this discrepancy would lead to behavioral problems, but I point it out. (EDIT: Sorting the eigenvalues does indeed effect the behavior rigid-body simulations, but does not seem to cause any harm. See below.)

This is a template class library. All implementations are fully separated from their declarations, however both currently appear in the header file ("math_eigen.h"). To avoid bloat in the compiled binary, the most common template class instances for float and double are defined explicitly in the "math_eigen.cpp" file. When you wish to use one of these instances, you can avoid recompiling the template class again by declaring the instance you need using the "extern" keyword:

// --- hypothetical .cpp file ----
#include"math_eigen.h"
extern template class MathEigen::Jacobi<double, double*, double**, double const*const*>;

(Tiresome detail: This is not necessary if you are invoking the jacobi3() function on 3x3 matrices. Since most of the current LAMMPS files use jacobi3(), I haven't used the "extern" declaration in any of the existing cpp files that I modified so far. But this option exists for people who want to diagonalize larger matrices and avoid bloat.)

More details: Why this approach? (feel free to skip)

  1. If you move the implementation details into "math_eigen.cpp", it's a headache to get the PEigenDense and LambdaLanczos to work with complex valued matrices. (I was not able to get LAMMPS to compile if I simply add this line: "template class LambdaLanczos<std::complex<double>>;" to "math_eigen.cpp". SEE UPDATE BELOW.)

  2. Generally speaking, the ability for these template classes to work with a variety of exotic matrix implementations (eg double**, double [3][3], cvm::matrix2d, as well as complex-valued matrices...) is one of their strengths. For this reason, I actually prefer to keep the implementations in the header file instead of moving them to a cpp file. Keeping implementations in the .h file and using the "extern" keyword seems to be a good compromise that avoids most of the bloat, but keeps the option open in the future for people to use new matrix representations. (For example, fix_poems.cpp uses double**. fix_rigid.cpp uses double [3][3]. The colvars library defines its own "cvm::matrix2d" type. The new code from "math_eigen.h" accepts all of these matrices directly without asking the user to convert them to double** first.)

Incidentally, the "math_eigen.h" file is only included in a few cpp files that need to calculate eigenvalues. So keeping its implementation in the header file should not slow down compilation noticeably. (EDIT: Compiling LAMMPS takes about 3 seconds longer now using this new code.)

UPDATE: Declaring a variable of type LambdaLanczos<std::complex<double>> inside a function does work. I suppose I could define an empty function within "math_eigen.cpp" which contains a local variable of this type. But it's a confusing workaround, and I'm happy keeping everything in "math_eigen.h".

If you still want to move all implementation details into "math_eigen.cpp", just cut lines 423-1368 from "math_eigen.h" and paste them into "math_eigen.cpp".

Post Submission Checklist

  • The feature or features in this pull request is complete
  • Licensing information is complete
  • Corresponding author information is complete
  • The source code follows the LAMMPS formatting guidelines
  • Suitable new documentation files and/or updates to the existing docs are included
  • The added/updated documentation is integrated and tested with the documentation build system
  • The feature has been verified to work with the conventional build system
  • The feature has been verified to work with the CMake based build system
  • Suitable tests have been added to the unittest tree.
  • A package specific README file has been included or updated
  • One or more example input decks are included

Further Information, Files, and Links

As mentioned earlier, the two main eigensolvers in this pull request are from these github repositorries:

https://github.com/jewettaij/jacobi_pd

https://github.com/mrcdr/lambda-lanczos

…ead of "math_extra.h". Code in "lib" now uses its own abbreviated local version of the "math_eigen.h" file (which is named "jacobi_pd.h"), since it is not clear how code from "lib/" can access the code in "src/"
…en.h". moved "math_eigen.h" into the main "src" directory.
I replaced the 'Σ' character that was causing problems with PDF generation with the word "sum".  Hopefully PDF documentation generation works now.
@jewettaij
Copy link
Collaborator Author

The unit tests succeed but the compilation tests fail. Interesting. For what it's worth, the code and the documentation (HTML and PDF) build successfully on my computer (running ubuntu 18.04, using conventional make).

@akohlmey
Copy link
Member

akohlmey commented Sep 6, 2020

The unit tests succeed but the compilation tests fail. Interesting. For what it's worth, the code and the documentation (HTML and PDF) build successfully on my computer (running ubuntu 18.04, using conventional make).

They fail because of a deficiency in superpose3d.h which uses assertions without including the corresponding header.

https://ci.lammps.org/job/dev/job/pull_requests/job/centos7/job/cmake_mpi_openmp_bigbig_static/1233/console

@jewettaij
Copy link
Collaborator Author

Thanks Axel. I did not think to test the code with USER-REACTION enabled. I'm too sleepy to wait for the test results, but hopefully this fixes everything.

@akohlmey
Copy link
Member

akohlmey commented Sep 6, 2020

Thanks Axel. I did not think to test the code with USER-REACTION enabled. I'm too sleepy to wait for the test results, but hopefully this fixes everything.

You also have merge conflicts and whitespace/permission issues

…" to make sphinx happy. Fixed some new spelling mistakes.
@jewettaij
Copy link
Collaborator Author

jewettaij commented Sep 6, 2020

Thanks Axel. I did not think to test the code with USER-REACTION enabled. I'm too sleepy to wait for the test results, but hopefully this fixes everything.

You also have merge conflicts and whitespace/permission issues

...and some spelling errors I missed.
(...How did "peachpuff" end up in "false_positives.txt"?... no matter)

Also: When I run "make spelling" I also see that sphinx is complaining about line 830 of "pg_developer.rst" (which is in the "String processing" section). It seems that the real problem is that sphinx doesn't like the default string argument on line 239 of "utils.h". So I got around this problem this by editing "utils.h" and creating two different versions of the "trim_and_count_words()" function.

@jewettaij
Copy link
Collaborator Author

Oops. There are some linker errors. Hold on. I'll fix this in a few minutes. (Sorry. I got impatient waiting for LAMMPS to compile before pushing my commit because changing "utils.h" triggers a full recompilation.) Fixing this now.

@akohlmey
Copy link
Member

akohlmey commented Sep 6, 2020

Oops. There are some linker errors. Hold on. I'll fix this in a few minutes. (Sorry. I got impatient waiting for LAMMPS to compile before pushing my commit because changing "utils.h" triggers a full recompilation.) Fixing this now.

You are trying to fix things the wrong way around. You are 220 commits behind master and should merge in upstream first and with that resolve the merge conflicts. That would have likely removed the issue with the documentation build as well.

@akohlmey
Copy link
Member

akohlmey commented Sep 6, 2020

Oops. There are some linker errors. Hold on. I'll fix this in a few minutes. (Sorry. I got impatient waiting for LAMMPS to compile before pushing my commit because changing "utils.h" triggers a full recompilation.) Fixing this now.

This is not the fix we want. The current master can translate and link this just fine.

@jewettaij
Copy link
Collaborator Author

jewettaij commented Sep 6, 2020

Oops. There are some linker errors. Hold on. I'll fix this in a few minutes. (Sorry. I got impatient waiting for LAMMPS to compile before pushing my commit because changing "utils.h" triggers a full recompilation.) Fixing this now.

This is not the fix we want. The current master can translate and link this just fine.

I will revert "utils.h" and "utils.cpp".

@jewettaij
Copy link
Collaborator Author

current with remote, finally.

@jewettaij
Copy link
Collaborator Author

jewettaij commented Sep 6, 2020

Incidentally, after reverting to the previous version of "utils.h" and "utils.cpp", the message in big red letters returns. (But it looks like it's probably a warning message, not an error message, so no need to worry.)

doc/src/pg_developer.rst Outdated Show resolved Hide resolved
@akohlmey akohlmey added enhancement test_for_regression Enable to trigger regression tests maintenance labels Sep 6, 2020
@jewettaij
Copy link
Collaborator Author

Sounds good.
Let me know if you need anything.

@akohlmey akohlmey self-assigned this Sep 16, 2020
@akohlmey
Copy link
Member

@jewettaij I had a closer look at the code and the changes it introduces and thought about how to best address the concerns I have about all-header implementations versus the needs of LAMMPS. I think the best approach is to have the "math_eigen.h" header and the "math_eigen.cpp" file simply contain the prototypes for the two variants of the jacobi3 function with the optional arguments removed and applied directly. What is the "math_eigen.h" file will be renamed to "math_eigen_impl.h" and "math_eigen.cpp" (but not "math_eigen.h") will include it, same to "superpose3d.h". That should avoid major changes to the implementation and at the same time satisfy my desire to keep things clean and lean.

Thanks for all the effort with adding documentation, but I think for the LAMMPS purposes, it is sufficient to just document the jacobi3 function instead of the entire set of template classes, since they are currently not used anywhere else. I do want to keep mentioning them and the fact that they can be accessed in "math_eigen_impl.h", but for details I would prefer to point people to some webpage. Do you have a preference? The GitHub page where you maintain the code? or some other location?
Please let me know.

I'm also updating the unit tests and will take care of resetting the affected regression tests.

@akohlmey
Copy link
Member

@sjplimp @rbberger this is now satisfying my preferences and seems to be a decent replacement for the previous version. Please let me know if there is anything else you would like to discuss or change. Once this is merged we need to reset some of the regression tests. In the long run, this should be for the better, because the eigenvectors are now consistently sorted by magnitude of the eigenvalues. The old implementation would provide them in different order on some platforms when the values were close and thus making cross-platform testing a bit more difficult.

Copy link
Contributor

@sjplimp sjplimp left a comment

Choose a reason for hiding this comment

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

The math_eigen.cpp file needs the standard LAMMPS license info
and contributing authors entry.

@akohlmey akohlmey changed the title replaced all eigensolver code in LAMMPS with math_eigen.h and updated docs Replace eigensolver code in LAMMPS with math_eigen.h and updated docs Sep 16, 2020
@akohlmey
Copy link
Member

@sjplimp copyright and author attributes are added. I've also added URLs to the two projects on github where the code is maintained and integrated the existing tester tool (for the generic code, the specific functions are implicitly tested by the atom style tester and the fix rigid testers) into our unittest framework.

@jewettaij
Copy link
Collaborator Author

jewettaij commented Sep 17, 2020

@sjplimp @rbberger this is now satisfying my preferences and seems to be a decent replacement for the previous version.

Cool.

Please let me know if there is anything else you would like to discuss or change. Once this is merged we need to reset some of the regression tests. In the long run, this should be for the better, because the eigenvectors are now consistently sorted by magnitude of the eigenvalues.

To clarify: (by default) they are sorted by the eigenvalues. If you prefer to sort them by their absolute value (by default), then edit lines 45 and 69 of "math_eigen.cpp" and 181, 210 of "math_eigen_impl.h" and replace: "SORT_DECREASING_EVALS" with "SORT_DECREASING_ABS_EVALS".
(I'm happy with either choice. EDIT: I just remembered the eigenvalues are positive when calculating moments of inertia, so it doesn't matter.)

The old implementation would provide them in different order on some platforms when the values were close and thus making cross-platform testing a bit more difficult.

@akohlmey akohlmey merged commit e924fc6 into lammps:master Sep 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

please move "math_eigen.h" to "src" and accept a pull request from "mrcdr"
3 participants