Adding Symplectic Integrators - #1411
Conversation
|
@JacobHass8 : I think a good test is to identify the conserved quantity you wish to be conserved, and show that quantity is much better preserved with a symplectic integrator than with (say) RK4. I would also recommend an API for event detection and return a solution skeleton that can be interpolated as a Hermite spline, i.e., return ${t_k, y_k, dot{y}k}{k=0}^{n-1}$ rather than the typical solution skeleton {t_k, y_k, }_{k=0}^{n-1}. |
I've tried this on a harmonic oscillator and the energy fluctuations seem to be of order 1e-11 (for the 6th order method). I haven't checked RK4 (or any other method though!).
I'm not sure I totally understand what you mean. Are you saying return a third object that could be used to interpolate between different |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1411 +/- ##
===========================================
+ Coverage 95.40% 95.41% +0.01%
===========================================
Files 828 831 +3
Lines 69072 69405 +333
===========================================
+ Hits 65896 66226 +330
- Misses 3176 3179 +3
... and 4 files with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Exactly. Say you have an However, you have to compute Note that this is still inferior to the "natural interpolant", but given there seems to be no hope to get every ODE stepper's interpolant into the standard graphics packages, I think this is a reasonable compromise. |
| } | ||
|
|
||
| // Check if method is available | ||
| std::vector<std::string> available_methods = {"Y6", "Y4", "Y2"}; |
There was a problem hiding this comment.
Should we do an enum of available methods rather than a vector of strings?
There was a problem hiding this comment.
I wasn't quite sure how to implement the enum of available methods. I'm just not very familiar with enum types. Instead, I used a map. Is this okay or is there an easier way with enum?
There was a problem hiding this comment.
Actually, you're targeting a scipy wrap correct? So this question should be answered in the context of "what is the most expressive way to do this in python via nanobind?"
There was a problem hiding this comment.
Scipy actually uses cython to bind c++. I think strings are probably okay for now. This is the same behavior as scipy.integrate.solve_ivp.
There was a problem hiding this comment.
Also highly recommend going the enum route instead of a vector of strings and then a map.
enum class available_methods : unsigned
{
Y6 = 0,
Y4 = 1,
Y2 = 2
}And then you update the signature:
template <typename RandomAccessContainer, typename RealType, class Func, class Policy>
std::pair<std::vector<RandomAccessContainer>, std::vector<RandomAccessContainer> > integrate_hamiltonian_imp(const RandomAccessContainer p0,
const RandomAccessContainer q0,
const RealType dt,
const unsigned steps,
Func dHdp,
Func dHdq,
available_methods method,
const Policy& pol)|
@NAThompson I created a .qbk file to add documentation for the functions I added. I couldn't figure out how to build the documentation though. I kept getting errors when trying to follow the boost math guide. I wanted to include an equation so I created a .svg and .png using latex. However, I noticed that all other equations have a .mml file. Do you know how to convert these? |
I just use this website and drop pngs in it. I also experienced errors while building the docs-I think @mborland may have to help based on your particular error. |
What are the errors you are getting? Unfortunately there is a rather long toolchain required for building the docs, we did have it all documented on the old wiki, but I have a hunch that's bitten the dust :(
Don't worry about .mml, these are all a "historical artefact": when we started there was no easy way to convert LaTex to SVG or something web friendly (other than blocky pngs) but MML was the new kid on the block and there was a nice GUI editor and some scripts to convert the mml to SVG so that was what we used. Nowadays those tools are all unmaintained, and LaTex is so much better... please just make sure that the original LaTex is archived somewhere (a comment in the docs next to the link including the SVG would be good) that way if someone later spots an error it's so much easier/quicker to fix! |
Actually, no errors. I found the .xml file but don't know how to convert this to html. I just ran
Sounds good! I added the latex command. |
|
@jzmaddock or @NAThompson do you think Kahan summation might help the accuracy of these algorithms? Essentially, the algorithm is just summing together a bunch of small steps so I thought it might help. Edit: I implemented kahan summation, but it didn't do anything. I could have done it wrong, but I don't think that's the case. I'm only testing against the simple harmonic oscillator example. Thus, the error term just oscillates from a small negative to a small positive value. |
|
@JacobHass8 : I personally don't see any reason Kahan summation is going to help here. I would try to compute the condition number of the ODE using the techniques in Corless, A Graduate Introduction to Numerical Methods. This will tell you whether it's the ODE itself that forces the inaccuracy, or it's the method that forces inaccuracy on the ODE. |
Unfortunately, I haven't been able to find a copy of that book. I think I'll leave Kahan summation out for the first implementation. After this ports to |
|
On another note, is there any need to do policy based up/down casting on the input/result? From my understanding, boost policy casting is only implemented numeric types and not containers. |
|
@NAThompson, @jzmaddock, @mborland if all the tests come back passing, I think this PR is ready for review. There are only two small issues that I can think of . First, if the timestep, |
|
@NAThompson, @jzmaddock or @mborland gentle ping to see if anyone would be interested in reviewing. I can also make plots of more systems with my algorithm vs scipy if that would be helpful. |
mborland
left a comment
There was a problem hiding this comment.
Your accuracy is clearly good based on the plots in the main thread. I think reducing re-allocation or heavy objects will help significantly with performance.
| template<typename...> | ||
| using void_t = void; |
There was a problem hiding this comment.
It is a redefinition but it was only defined in C++17. I duplicated it because I need it for C++14 test. Is there a better way to do this?
| Func dHdp, | ||
| Func dHdq) | ||
| { | ||
| RandomAccessContainer p = p0; |
There was a problem hiding this comment.
This is a copy of a copy, but p0 is never used. Same for q0.
There was a problem hiding this comment.
I think p0 is just copied once now when integrate_hamiltonian_imp is called. Should I pass everything by reference when I can? Most function signatures have the parameters const RealType dt, Func dHdp, Func dHdq. The timestep dt is easy to pass by reference. Should I also pass the functions dHdp/dq?
There was a problem hiding this comment.
Yes, to the extent possible either pass by reference either const or non-const to the extent you can when moving containers around. Like the in-place add I mentioned I think there are other calculations you can do in-place since you're not mutating the users data.
| RealType b1 = static_cast<RealType>(0.0829844064174052); | ||
| RealType b2 = static_cast<RealType>(0.396309801498368); | ||
| RealType b3 = static_cast<RealType>(-0.0390563049223486); | ||
| RealType b4 = 1. - 2. * (b1 + b2 + b3); | ||
|
|
||
| RealType a1 = static_cast<RealType>(0.245298957184271); | ||
| RealType a2 = static_cast<RealType>(0.604872665711080); | ||
| RealType a3 = 0.5 - (a1 + a2); |
There was a problem hiding this comment.
Can these be competed to higher precision like you did above?
There was a problem hiding this comment.
I'll have to reread the paper to see how they did it. Even for the above (simpler) case it was quite complicated. I'll get a better idea and then report how difficult it would be.
| } | ||
|
|
||
| // Check if method is available | ||
| std::vector<std::string> available_methods = {"Y6", "Y4", "Y2"}; |
There was a problem hiding this comment.
Also highly recommend going the enum route instead of a vector of strings and then a map.
enum class available_methods : unsigned
{
Y6 = 0,
Y4 = 1,
Y2 = 2
}And then you update the signature:
template <typename RandomAccessContainer, typename RealType, class Func, class Policy>
std::pair<std::vector<RandomAccessContainer>, std::vector<RandomAccessContainer> > integrate_hamiltonian_imp(const RandomAccessContainer p0,
const RandomAccessContainer q0,
const RealType dt,
const unsigned steps,
Func dHdp,
Func dHdq,
available_methods method,
const Policy& pol)|
The one other change now is to mark your public APIs with |



Adds symplectic solvers for ODE systems with a conserved quantity (i.e. energy). This was a requested scipy feature (see #303 and scipy/scipy#12690). I'd ultimately like to merge this into scipy using cython. I'm still working on this and have a couple of features I'd like to add:
Does this seem like a good plan? I'd appreciate any input.