-
-
Notifications
You must be signed in to change notification settings - Fork 187
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
Feature/issue 310 coupled ode system refactor #312
Conversation
Hi @wds15, sorry it's taken so long to review. Since it's changing things around, I had to get a pen and paper to track through the changes. |
The biggest design issue is that I think if you changed the code to work that way and moved |
} | ||
|
||
inline void | ||
rhs_sens(const std::vector<stan::math::var>& initial, |
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.
These rhs_sens()
functions are what are forcing this class to be in rev
instead of in prim
. I would suggest moving the bulk of the class back to prim
and templated appropriately. Then have a template instantiation in rev
like it was done before.
@wds15, I don't think we've been clear about how our math library works. We want there to be versions that work with This pull request is removing functionality from The code looks solid, but I believe the way we need to move forward with this is to:
I think that's the way forward with this pull request, if that makes sense. Definitely open to having a design discussion if you think there's a different way to do this. |
@syclik Thanks for taking the time and sorry to confuse things. I wasn't aware of the need to have something working in "prim". BTW, will fvar ever work with ODEs? As I understand the main point is to restore the functionality in "prim" for the double only case (along with moving tests back). From what I understand I need to
I hope I got that right, we can discuss tomorrow as needed. BTW, I hope that the refactoring makes the ODE code easier to follow, but the main advantage is that execution flows through the ode_system which makes the ODE subsystem amenable to providing analytic Jacobians. Thanks for your time. |
@syclik I hope I understood things right as to how the design is intended. As such I went ahead and enabled a prim functionality for coupled_ode_system, decoupled_ode_states and integrate_ode_rk45. This should get integrate_ode_rk45 and all its utils in line with what you described to me. I am not sure where the merge conflicts come from, I need to follow those up (possible one of the new merges). |
…coupled_ode_system-refactor
Ok, now things should be good. So what I did essentially is to place under prim a forward declaration of the objects needed along with the template specialization for double, double. Then in rev I give a definition of the "general" template which uses internally is_var meta programs (and this rhs_sens logic thing) in order to provide the correct implementation for any of the cases whenever at least one var is involved. I also moved around tests, etc.. So I hope this is fine now as it is and brings back the old design idea of integrate_ode_rk45 living in prim and having special templates in rev for var types. |
So, go back to what I suggested a few comments ago. Those are still the correct comments. In Does that make sense? |
This is somewhat against my intuition of abstracting code - but I certainly don't have the bigger picture in mind. So what you suggest applies to
Right? |
I want to make sure we're all on the same page here and I'm not prim <- fwd <- mixed Wherever possible, we want prim-only versions of functions. In general, we prefer C++ code that doesn't have things like branching
|
…template specializations in rev
the approach to have a prim version with few dependencies makes sense, sure. I figured doing the same logic as for CVODES stuff would be fine in the first place, but it looks like we now return to the very original design for good reasons. Anyway, I hope that things are now as you expect them to be. |
@wds15, there's a branch conflict. Mind looking into that? |
@wds15, I'm taking another look at this now. Additional questions / comments:
I just need help with outlining what the objects do and why you've split the functionality across 3 different places instead of keeping them within one object where everything is coherent. |
…coupled_ode_system-refactor
@syclik Thanks for taking a look. The original and main intent of this pull is to route all ODE integrators through the jacobin function of the ode_system object, yes. This abstracts out the Jacobian such that we can provide partial template specialisations to the ODE framework and enable analytic Jacobians (this gives 2-3x speedups, depending on the ODE system vs using AD). As I learned from you that integrate_ode_rk45 was for good reason under prim, I went ahead and moved it back there. The integrate_ode_bdf function must reside in rev, since the stiff solver always needs a jacobian (even for the double, double aka prim case). The decouple_ode_states function had been factored out from the CVODES codes as I was anticipating that this can be reused for the coupled_ode_system object; just like I did. The functionality which is needed is about:
I have to say, that I find the design nice and straightforward... maybe I need to explain some more bits and pieces to you such that it makes more sense? |
Here's where I'm confused. We now have two entry points into the ode systems (we used to only have one in the past):
In the first,
We construct a
We have 4 ways of instantiating The second use case, Instead of the abstraction above, we now have a separate I'm having trouble following the rest of the implementation here. Why isn't essentially all of Regarding what the integrator needs -- it's not clear to me why there are 3 different classes floating around when perhaps it should be 1? Or 0? Or 2? I'm having trouble keeping it straight. Perhaps this would be done quicker over a video chat. (Those are really questions; I don't know what's driving the decisions to split things into multiple classes or merge them.) Regarding What happens to the |
So an example of where I'm really confused is inside: Why doesn't
inside? Each instance of |
Maybe a hangout is really easier, but let me try in order:
Here is a proposal to move forward, let's split things into two pull requests:
The first pull is the critical one to get ODEs faster in Stan, the second may cost us a little bit of performance (I don't know how much, probably not too much), but it will shrink the code base a bit. |
Maybe the two pull separate pull requests will help me see what you're trying to do. When is the user able to specify analytic Jacobians? That isn't possible now, right? Maybe that's where I'm missing a key use of these classes. (I still don't understand what's changing and for what reason. If you can create a more narrow pull request that just addresses speed, that'd be great. And a separate one to change the code, I hope it's a lot easier to understand.) |
The user has to provide C++ code which is a template specialisation of ode_system. This specialisation then has to provide the analytic jacobians. I have examples which demonstrate this for the case of the bdf integrator. Doing my proposed pull 1 will enable the same speedup for the RK45 integrator. So the speedup is not for free - the extra C++ code which is needed has to be provided. In the long run it will make sense to have scripts generating this. Would such a working example help? And if yes, where should I put these files? |
Yes, a working example would help. Where depends on what it is. I'm not sure where. What does it touch? The math library? The stan library? CmdStan? A generated model? Are you creating a handful of specialized systems that have analytic Jacobians so you can call that from Stan math with some sort of name? If so, then the bulk would be a math change and putting that on a branch in math is the right thing. |
The main point of the design is that providing the analytic Jacobians requires very little C++ code, i.e. just a single specialization of the ode_system object. I create a branch which focuses on this aspect and I add a test which will run integrate_ode_rk45 without AD. This is more of an example rather than a real test, but you will see immediately how this is intended to work. |
Thanks. Can you send a message on the stan-dev mailing list and I'll take a On Thu, Aug 4, 2016 at 1:33 PM, wds15 notifications@github.com wrote:
|
Submisison Checklist
./runTests.py test/unit
make cpplint
Summary:
Refactor coupled_ode_system object to take advantage of ode_system and decoupled_ode_states which have been introduced lately.
Intended Effect:
How to Verify:
Run the ODE related tests which have almost all been moved now to
test/unit/math/rev/mat/functor/
.Side Effects:
Minor internal only change to intergate_ode_rk45 and minor generalization of the ode_system.jacobian member function. The generalization of the ode_system.jacobian function may require to write tests for the generalized functionality which I will provide once design is agreed on to go into Stan as is.
Documentation:
Doxygen comments are updated and should be complete.
Reviewer Suggestions:
@bob-carpenter @syclik @betanalpha
Copyright and Licensing
Please list the copyright holder for the work you are submitting (this will be you or your assignee, such as a university or company): Sebastian Weber
By submitting this pull request, the copyright holder is requiring to license the submitted work under the following licenses: