-
-
Notifications
You must be signed in to change notification settings - Fork 188
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 1071 ode time varying #1072
Conversation
…ting for coupled_ode_observer
… decoupling tests
class ops_partials_edge<double, std::vector<var> > { | ||
public: | ||
typedef std::vector<var> Op; | ||
typedef std::vector<double> partials_t; |
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.
Note: This used to be a Eigen type (a vector_d) and I changed it to be a std::vector to make things work with in the arr
area of this specialization.
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.
I just moved it back as otherwise tests fail big time.
@wds15, can you describe the overall strategy before I dig in? (I took a quick scan, but it's shifting bits of code to different files in something where I'll need a little more time to review properly.) Is there a good description / doc of the architecture of the internals of integrate_ode? If not, I think it's about time we put one together because it'll save us time in the future as we continue to work on this. |
The main shift in this PR is moving the decoupling operation out of What is always useful for me to have around is the document here: The math there is how we coded things (as documented in the |
This PR is ready for review. Should it help to have a video chat on this piece then I can jump on a hangout call. I know that this is dense material, but the ODE system is tested decently which should ease the review, hopefully. These changes should be helpful for inference in pharmacokinetic ODE systems with delays (and I am sure there are more applications). |
@wds15, thanks for putting up the link to the ode_system.pdf. That's not exactly what I'm looking for. It would really help if you described how the different things interacted together. What does |
Once I have some sense of what's going on, I'll review it. |
Ah... that's what you are asking for.
I hope this makes sense now. Let me know if something is unclear or if we should shortly have a hangout. |
@wds15, I think it'd help if we chatted about your changes. First off, this shouldn't have passed tests -- you're bringing in Let me know when you have some time. |
Hi, I have time this week to check the doc and do one more pass of the PR. Hopefully we can then merge the PR if everything goes well. |
…com/stan-dev/math into feature/issue-1071-ode-time-varying
…-ode-time-varying
…server and move this to a wrapping functor of the observer lambda in integrate_ode_rk45
This reverts commit d9de61e.
…-ode-time-varying
@charlesm93, @syclik Do you think to have time to finalize this PR at some point? It would be good to have a timeline for this. Otherwise we can either look for other reviewers or we just delete this PR. It's too much resource keep this hanging around. Tagging @seantalts or @bob-carpenter who may have ideas as well. |
One thing that would be helpful is if you were able to update the top level
description with relevant info. That's so anyone else could pick up the PR
and review it. As it stands, the top level info barely has the requisite
information and the review takes a lot of time to do.
There were a few points that were addressed in comments that should be
pulled up top and a few that weren't addressed at all. I could go an
rereview if you think it's ready for that.
…On Wed, May 8, 2019 at 8:00 AM wds15 ***@***.***> wrote:
@charlesm93 <https://github.com/charlesm93>, @syclik
<https://github.com/syclik> Do you think to have time to finalize this PR
at some point? It would be good to have a timeline for this. Otherwise we
can either look for other reviewers or we just delete this PR. It's too
much resource keep this hanging around.
Tagging @seantalts <https://github.com/seantalts> or @bob-carpenter
<https://github.com/bob-carpenter> who may have ideas as well.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1072 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AADH6F7L4BVR7BDP7Z6PZNTPUK6G5ANCNFSM4GHT645Q>
.
|
I'll go through this today and do a full review of the PR. I'll rely on the info in the issue and the summary of the PR (first comment on this thread) to gage if the documentation is sufficient. I'll also look whether the comments in @syclik 's review were addressed. |
Thanks! Feel free to dismiss my review if it's irrelevant.
…On Thu, May 9, 2019 at 11:48 AM Charles Margossian ***@***.***> wrote:
I'll go through this today and do a full review of the PR. I'll rely on
the info in the issue and the summary of the PR (first comment on this
thread) to gage if the documentation is sufficient. I'll also look whether
the comments in @syclik <https://github.com/syclik> 's review were
addressed.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1072 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AADH6FZXB2WTUMO7NBZ3HA3PURBRZANCNFSM4GHT645Q>
.
|
@charlesm93 great...thanks. I slightly updated the PR description with things I saw being relevant to change. If more should go to the PR description, let me know, before you waste time. |
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.
Overall this looks good to me. I think the documentation is good, and the code reads well. There are some minor details and questions that should be addressed before merging. I have considered @syclik 's comment while going through the code, so addressing my comments should resolve both reviews.
* | ||
* @param coupled_state solution at the specified time. | ||
* @param t time of solution. | ||
* @param t time of solution. The time must correspond to the ts | ||
* vector, respectively. |
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.
Not quite sure what this means. Should it be the elements in time correspond to elements in the ts vector?
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.
Yes, correct. You must call the operator only for those times which are in the vector passed in at construction.
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.
Ok. I think this would be worth rephrasing a little then.
const std::size_t M_; | ||
const std::size_t index_offset_theta_; | ||
// counter pointing to the element in the ts vector which is | ||
// currently being referred to. Initialized to -1. | ||
int n_; |
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.
I realize this came up in the PR comments, but it should be justified here too. Why do we initialize n_
at -1? To be used, n_
should at least be 0. Would it be possible to initialize it at 0?
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.
No. It points to the current element which is one before 0 at construction. The last time this came up things were more complicated as i needed to do branching due to different ode solver conventions. This branching was moved into the ode solver functions which is where they belong, i think. Right now i increase n when you enter the function for recording a state. If you like 0 for the init i would need to move that increment to the end of the function. I donˋt mind either convention.
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.
I also don't mind either convention, but I slightly prefer the second option. First, initializing n_
at 0 seems more natural and makes the code clearer. Secondly, for future development, we may add other functions that use n_
, and we don't want to impose the requirement that n_
needs to be incremented.
So either move the increment function or add one line of comment in the construct explaining the choice of starting at -1.
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.
have a look at what is there now. Hopefully easier to digest than before.
*/ | ||
template <typename F, typename T1, typename T2, typename T_t0, typename T_ts> |
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.
Just a suggestion: I like the type names T_t0
and T_ts
, more than T1
and T2
. I realize numbering types is consistent with what's done in Stan, but in the future, it would be nice to have something like T_init
and T_parm
, or T_y0
and T_theta
.
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.
I agree...and we both flavors floating around. Older code is usually numbered an newer is not...this also depends on who is writing the code. I am not sure if we have a convetion. I tried to stay consistent with the rest of the code.
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.
Fair enough. This doesn't need to be addressed in this PR. I'll bring it up on the discussion forum, and maybe raise an issue on the subject. This applies to a lot of functions in Stan.
check_finite("integrate_ode_rk45", "initial state", y0); | ||
check_finite("integrate_ode_rk45", "initial time", t0); | ||
check_finite("integrate_ode_rk45", "times", ts); | ||
check_finite("integrate_ode_rk45", "initial time", t0_dbl); |
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.
This looks fine to me. And yes, this should be faster because we're bringing a smaller object in memory.
@@ -31,7 +32,7 @@ class ops_partials_edge<double, std::vector<var> > { | |||
|
|||
void dump_partials(double* partials) { | |||
for (int i = 0; i < this->partials_.size(); ++i) { | |||
partials[i] = this->partials_[i]; | |||
partials[i] = this->partials_(i); |
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.
Maybe don't use []
on the L.H.S and ()
on the R.H.S ^^
I think []
is fine for Eigen structures. Since we know i
will not be out of bound, we can use the coeff
method, which doesn't check whether i
is in bound and is therefore slightly faster.
|
||
ASSERT_EQ(T, y.size()); | ||
for (int t = 0; t < T; t++) | ||
ASSERT_EQ(2, y[t].size()); |
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.
To be more consistent with other tests in math, shouldn't we use EXPECT_EQ
?
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.
Apply this comment to all the unit tests.
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.
I updated this, but GitHub still shows this outdated thing. I just double checked and this is aligned to EXPECT_EQ .
std::stringstream msgs; | ||
std::vector<double> x; | ||
std::vector<int> x_int; | ||
}; |
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.
What is this structure for?
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.
It's being used in a number of tests. We need that as dummy argument to all the observer instances created.
std::vector<int> x_int; | ||
}; | ||
|
||
TEST_F(StanMathOde, observe_states_dd) { |
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.
Can we change the name of the test? There are now 4 input that can be passed as dbl or var. So here, a title such as observe_states_dddd
may be more appropriate.
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.
Let me have a look...but i would like to avoid having to change all our test names....
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.
I suggest either using the dddd
suffix, or something more specific like all_double
. I get what you're doing, but some fresh eyes may not.
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.
sure... let's do this convention in the observer tests.
|
||
// here we only test first & last steps, and rely on the | ||
// fact that results in-between affect the initial | ||
// condition of the last step to check their validity. |
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.
I think that's fine, though it shouldn't be too difficult to write the test and loop through all the states to make sure the correct answer is always attained.
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.
Our grad tests for the ode solvers essentially work the same way as this one.
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.
Ok, just a suggestion. Though I wonder if there was an initial motivation for doing this. Maybe cutting down the time of the unit test.
Other note: this is an issue that needs to be addressed separately, not in this PR, but the code for |
The change to operands and partials from me in this pr is really cosmetic. I am using () to index Eigen structures and [] to index std vectors. This is how it should be, i think. If you think there is more doc needed on that, then that should be a new issue. Thanks for your eyes on it... i will try to address the rest soon. |
I would leave operands and partials as it is. We can raise another issue and fix the problem. I really think In general, I think it might help to make more incremental changes, though, of course, some balance must be found. |
The comment
refers to an outdated piece of code. Maybe GitHub does not propagate updates correctly through? In any case... this -2 / -1 thing is gone now. |
@charlesm93 I hope that I have caught all your comments and addressed them. Please be careful when using the GitHub review feature - it seems that it shows you outdated code. Maybe we can finish this really soon...it looks like we are close. |
Great. All the comments in my review have been addressed. We're good to merge. |
The review is out-of-date. The comments in this first review have been addressed when @wds15 went through my new review.
Awesome! Can't wait to hit the "merge" once Jenkins comes back (hopefully without hickups). Thanks. (next we need to make the parser aware of this change) |
Summary
This PR achieves two goals:
coupled_ode_system
and instead placed in thecoupled_ode_observer
function object.Note that this PR supersedes PR #857 in that this one also allows the initial time to be varying which I think is key in making it a useful thing to have time varying. Moreover, this PR has a different approach by refactoring things a bit. In particular, the decoupling operation is moved into the
coupled_ode_observer
function object which now handles all relevant cases usingoperands_and_partials
.A reason to pull out the decoupling operation out of theThis isn't the motivation anymore... the code makes more sense to be organized this way from my perspective. The thing is that the sensitivites wrt to time always work the same and the specialization's by thecoupled_ode_system
is to prepare for a future integration of acoupled_ode_system
for the case whenever the user provides analytic Jacobians. In this case it make sense to have the decoupling operation as a separate module.coupled_ode_system
of the decoupling operation can easily be integrated in a single function which usesoperands_and_partials
.Finally, this refactor makes the code use less temporaries since the coupled states are directly written into the AD tree (instead of all being stored and then moved to the AD tree).
This PR is somewhat larger than #857 one as this one tests the decoupling of the ode observer specifically which was not really tested for as I can see it (the respective decoupling operation).
Tests
The additional tests introduced in PR #857 have been put into this PR as well and have been extended even further to also cover the case for the varying initial time. These additional tests are in
test/unit/math/rev/mat/functor/integrate_ode_adams_rev_test.cpp
test/unit/math/rev/mat/functor/integrate_ode_bdf_rev_test.cpp
test/unit/math/rev/arr/functor/integrate_ode_rk45_test.cpp
The tests of the integrate functions now use a forced harmonic oscillator which involves explicitly time on the ODE RHS. This is to ensure that we get the right time-points aligned (which we do now).
Moreover, the decoupling tests which were part of
coupled_ode_system
have been moved to respective tests for thecoupled_ode_observer
:test/unit/math/rev/arr/functor/coupled_ode_observer_test.cpp
test/unit/math/prim/arr/functor/coupled_ode_observer_test.cpp
Side Effects
I had to pull in the specialization for
ops_partial_edge
forstd::vector<var>
from themat
branch in thearr
folder.Decoupling operation is now moved from the
coupled_ode_system
to thecoupled_ode_observer
.The rough flow of the
integrate_ode
calls changes now slightly:old behavior:
y_coupled
buffer for the coupled outputsy_coupled
new:
coupled_ode_observer
which decouples the state on the spot and adds it to the AD stackChecklist
Math issue allow time in integrate_ode calls to be autodiff variables #1071
Copyright holder: Sebastian Weber
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit
)make test-headers
)make doxygen
)make cpplint
)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested