> as far as I know we were the first people to release an open-source set of differentiable solvers using reverse-mode adjoint sensitivities
Would the DifferentialEquations.jl work in the Julia community qualify? As I understand it for the pure Julia solvers you can run them through autodiff to differentiate with respect to the parameters. Chris Rackauckas has talked a lot about how cool it is that he can take his solvers that were not written to be AD-aware and use somebody else’s AD package that’s not specialized for diff eqs, and combined you get differentialable diff eqs.
Yes, we've had this working in Julia for a few years now (with Flux.jl and ReverseDiff.jl tracing-based reverse-mode) and it's surprising that wasn't know because it's been pretty widely publicized (and mentioned to the authors). In fact, we even have a preprint showing that reverse-mode adjoint isn't what you want to use in most scenarios:
We identified that tracing-based reverse-mode adjoint + solving without mutation is not competitive with the other sensitivity analysis methods (this is the method which they have implemented in this repository) when comparing it to methods which are fully compiled and utilize mutation. Since the methods in SciPy don't utilize mutation (and add quite a bit of their own overhead, and don't necessarily work well with the JIT compilation being of context switching, as previously demonstrated in http://juliadiffeq.org/2018/04/30/Jupyter.html ), I wouldn't be surprised that they saw a speedup anyways over the odeint+adjoint method described in the paper, but that's mostly because the method from the paper is highly inefficient.
In terms of efficient implementations, the first ones which are equivalent to a reverse-mode adjoint were actually those of FATODE ( https://dl.acm.org/citation.cfm?id=2048596&dl=ACM&coll=DL ) which implemented quite a few Runge-Kutta and Rosenbrock methods with forward-mode and reverse-mode transforms of the solvers manually written in there (described as "discrete sensitivity analysis") for hooking into Fortran AD implementations. JuliaDiffEq's methods are based along this existing heritage and testing.
But sensitivity analysis goes back a lot further. In their paper though they utilize the adjoint sensitivity analysis, which the most common software one would point to for this is SUNDIALS CVODES (https://computation.llnl.gov/sites/default/files/public/cvs_... ). If you compare page 26 of the SUNDIALS manual to the Neural ODE paper, you'll notice that it's eerily similar because it's almost the same method. And actually, 2.7.1 describes an improvement to the method in the Neural ODE paper by using "checkpointing". In the Neural ODE paper, to do a reverse solve of the adjoint ODE it solve the forward ODE from the beginning time point until the point. Clearly, this is really slow because it requires a lot of forward solves over long intervals. You can instead save a few time points (checkpoints) along the forward solve and use those as the starting point, and this is the method implemented in CVODES and IDAS. In JuliaDiffEq, since we were focused on less memory intensive applications (CVODES did it for PDEs), we did it in a way where you get a continuous solution in one forward solve, and then just use the interpolation for an essentially free calculation for the Jacobian at a given time point. This of course goes from many ODE solves to a single ODE solve, at the cost of using more memory, and we proposed implementing checkpointing in a GSoC. But anyways, the derivation of the paper for the adjoint method wasn't new and it was actually already improved by 2005 in the TOMS paper on CVODES ( https://computation.llnl.gov/projects/sundials/toms_cvodes_w... ), with other methods for improving runtime done as well.
In a previous Twitter thread, the senior author also claimed that using the autodifferentiation to do the vector-Jacobian products was a first. For reference, what I mean is that there is a vjp in the adjoint ODE and this can be solved without explicitly building the Jacobian and seeding the backsolve of the derivative function f appropriately. I will admit that in JuliaDiffEq we weren't doing this, but we have since improved our implementation to make use of this trick (and the advantage of this method is shown in the Arxiv paper linked above). That said, the Neural ODE paper isn't the first group to do this either, with earlier work actually being traced back to CasADi (https://link.springer.com/chapter/10.1007/978-3-642-30023-3_...), and of course the author of CasADi was confused as well in the same Twitter thread about the novelty claim here.
And this is just the tip of the iceberg for schemes utilizing adjoint methods. There's entire pharmacometric software (NONMEM) and engineering software (Modelica) built back in the 80's for building the adjoint equations in ways to get these sensitivities for parameter estimation of methods.
So I honestly cannot find a single part of the adjoint method either as mentioned or implemented where this PyTorch implementation is the first. I think a lot of this comes from a lack of technical expertise in the area of numerical differential equations and just an honest mistake though. There are other glaring examples of this in the paper as well. For example, in the paper they mention:
>To solve ODE initial value problems numerically, we use the implicit Adams method implemented in LSODE and VODE and interfaced through the scipy.integrate package. Being an implicit method, it has better guarantees than explicit methods such as Runge-Kutta but requires solving a nonlinear optimization problem at every step.
However, it's well known that not all implicit methods have better stability guarantees than explicit methods. Specifically, the implicit Adams methods they mention are Adams-Bashforth-Moulton methods are not unconditionally stable and have stability similar to explicit methods (you can find this in a lot of numerical analysis course lecture notes, like this one: http://www.math.vt.edu/people/embree/math5466/lecture37_38.p... . The best resource on this is probably Hairer Solving Ordinary Differential Equations I: Non-stiff Problems). Because of this lack of stability, LSODE with Adams coefficients is only recommended non-stiff equations and common stiff test examples like the ROBER will cause it to fail. Instead, the backwards differentiation formula (BDF) is the similar method which is used for stiff equations (there are lots of sources on this. For example, ode113 is the Adams and ode15s is the BDF, and go look at MATLAB's documentation for how they do the recommendations. Another example is CVODE which implements both the Adams and BDF methods, and only recommends the Adams method for non-stiff equations). This also means that the current set of PyTorch differential equation solvers is only applicable to (some) non-stiff ODEs BTW.
I do want to end this critique though by saying that the paper itself describes a very new and novel application of differential equation solvers as part of a neural net in a way that seems very beneficial. So even though what was demonstrated is not technically sophisticated compared to current software and techniques, their application is a very interesting idea and a new research direction which is deserving of a best paper. I don't think we should lose track that the idea of an RNN as an Euler discretization and thus building the continuous one to get better training times is a very neat solution to a machine learning problem and I very much respect the authors for this advance.
I think that what we can learn from this case is that numerical differential equation and machine learning communities need to come together to do this in a way that pushes the research and the software forward. We are starting to do this in Julia, and that's why in our Arxiv paper you can see the JuliaDiffEq crew has teamed up with the machine learning and AD people (Jerret Revels and Mike Innes) to make sure we can get the full adaptive + events + stiffness handling etc. solves working with AD, and get these utilized in new applications for machine learning. With the help of one of the paper's authors we hope to put out a package in January for implementing said neural ODEs in Julia. From there we can all probe the method our own ways. Since my background is in numerical differential equation solvers, I plan to look at this to see what kinds of solvers are optimal in this application (I think some EPIRK type methods might be the right deal). The ML folks will likely want to actually use it as a model to train and predict. The AD folk see this as a good test case for source-to-source transformations, since building a tape a la Flux or PyTorch can be pretty inefficient for large nonlinear operations like an ODE solver (so we plan on seeing what happens with Zygote.jl on this. Right now it errors, but we'll see!).
I see a very nice future merging differential equations and machine learning in various ways, and this paper is a fantastic start in that direction. Let's just make sure we cite the relevant existing work from both communities.
Thanks for the detailed remarks, and for pointing out that my statement above about the novelty of our implementation was wrong - CasADi uses the same algorithm as was released in 2013. My apologies. Joel Andersson pointed this out to us and we added a cite to his thesis several months ago.
As for Sundials and FatODE, my understanding was that they used finite differences, forward mode, or differentiating the solver operations for at least some aspect of their sensitivity analysis.
On another topic, I think you might be misunderstanding how we're running our adjoint sensitivity analysis. You say
> In the Neural ODE paper, to do a reverse solve of the adjoint ODE it solve the forward ODE from the beginning time point until the point. Clearly, this is really slow because it requires a lot of forward solves over long intervals.
This isn't true - when we do the reverse solve, we get all gradients using a _single_ solve going backwards in time. I'm now realizing that the misunderstanding might be caused by our Fig. 2, which shows that multiple backward solves are necessary when the loss depends on the state at multiple time points.
I agree that our statement about the stability of implicit over explicit was overly broad, thanks for pointing that out. Can you suggest a more accurate statement about the advantages of implicit over explicit methods?
I also agree there is a lot of numerical work to be done in this area, and I'm glad that people more knowledgable than us (such as yourself) are looking at it too!
>As for Sundials and FatODE, my understanding was that they used finite differences, forward mode, or differentiating the solver operations for at least some aspect of their sensitivity analysis.
No, their sensitivity analysis doesn't use finite differences. They perform the sensitivity analysis as described in their documentation. If methods for the Jacobian calculation are not provided, then they utilize finite differences on the Jacobian calculation of course, using the same routine as for the stuff solver. But with a given Jacobian function there's no finite differences or forward mode.
>This isn't true - when we do the reverse solve, we get all gradients using a _single_ solve going backwards in time. I'm now realizing that the misunderstanding might be caused by our Fig. 2, which shows that multiple backward solves are necessary when the loss depends on the state at multiple time points.
No. Of course it's a single solve going backwards in time. I see what my misread was, but I'm surprised you'd attempt to solve the equation backwards like that because it's known to not be stable. Without a reversible integrator (implicit Adams is not reversible), it's a well-known result that the method drifts from the true solution doing a backwards integration, so the values so z(t) needs to computed with forward passes in order to be correct. A good test equation for this is probably the Lornez equation with standard parameters over a time like [0,300]. The backwards pass will diverge and not necessarily be on the same butterfly wing as the forwards pass. The Julia code using CVODE is shown here ( https://gist.github.com/ChrisRackauckas/fef4ae7778320530d44b... ) and you see that starting from [1.0,1.0,1.0] the result of going backwards is then off by [-17.5445, -14.7706, 39.7985]. So there you go, CVODE's Adams method ends up on a different "wing" of the butterfly when integrated backwards, ending up not even close to the actual initial point (CVODE is the successor to LSODE, both by Alan Hindmarsh, but utilizes constant leading coefficient forms to reduce computations. So not exactly the same as the paper, but very close). Thus to ensure correctness, existing sensitivity analysis packages only get Jacobians of f using data from forward passes. The Neural ODEs may have had a small enough Lyopunov coefficient or a short enough integration that this wasn't an issue, but it is in general something to note. Of course, if you are only doing this on Hamiltonian systems...
>I agree that our statement about the stability of implicit over explicit was overly broad, thanks for pointing that out. Can you suggest a more accurate statement about the advantages of implicit over explicit methods?
Any statement on it is too broad to be useful. Runge-Kutta Chebyshev methods are explicit methods for stiff systems. Implicit Adams is an implicit method for non-stiff systems. And there's many more examples. The stiffness handling also depends on implementation details. Using functional iteration on a BDF method reduces the region of stability, which is why BDF needs to use Newton's method for solving the implicit equation in order to be applicable to stiff ODEs. It's best to just talk about the stability of individual methods and their implementation.
> But with a given Jacobian function there's no finite differences or forward mode.
Right, but instantiating an entire Jacobian is always going to scale at least quadratically with time. The point I was trying to make is that the existing non-adjoint approaches were never going to scale to large systems with millions of parameters. This is the main attraction of reverse-mode, and it appeared to me that this was a major obstacle for fitting large models using existing packages (excepting CasADi).
> I'm surprised you'd attempt to solve the equation backwards like that because it's known to not be stable.
> it's a well-known result that the method drifts from the true solution doing a backwards integration, so the values so z(t) needs to computed with forward passes in order to be correct.
I agree that a purely reverse-mode gradient solve will diverge from the forward trajectory to some degree. But to say the gradients are 'correct' or not seems a bit strange to me. Every numerical solve introduces some degree of error, and I think the most useful discussion to have is the tradeoff between computation cost and numerical error. Re-solving the system forwards is one strategy to reduce error at the cost of computation. Another strategy would be reducing the error tolerance of the reverse solve. There are situations where our strategy might give worse precision wrt the parameter gradients for a given computational budget, but I wouldn't dismiss it out of hand. Especially since it's about as computationally cheap as one could hope for - O(1) memory and similar time cost as the forward solve. Also, it worked for our applications.
> Any statement on it is too broad to be useful.
I appreciate the detailed reply. But is there anything you can say about when to try implicit methods over explicit? What was the motivation for developing implicit methods in the first place?
Well, you can differentiate through the operations of ODE solvers written in any framework that has autodiff. But the whole idea of adjoint sensitivity analysis is to not differentiate through the operations of the solver, to save memory and to control numerical error more directly.
Would the DifferentialEquations.jl work in the Julia community qualify? As I understand it for the pure Julia solvers you can run them through autodiff to differentiate with respect to the parameters. Chris Rackauckas has talked a lot about how cool it is that he can take his solvers that were not written to be AD-aware and use somebody else’s AD package that’s not specialized for diff eqs, and combined you get differentialable diff eqs.