1- [ ![ DOI ] ( https://zenodo.org/badge/DOI/10.5281/zenodo.4058330.svg )] ( https://doi.org/10.5281/zenodo.4058330 )
1+ # Sunode – Solving ODEs in python
22
3+ [ ![ DOI] ( https://zenodo.org/badge/DOI/10.5281/zenodo.4058330.svg )] ( https://doi.org/10.5281/zenodo.4058330 )
34
45You can find the documentation [ here] ( https://sunode.readthedocs.io/en/latest/index.html ) .
56
6- # Sunode – Solving ODEs in python
7-
87Sunode wraps the sundials solvers ADAMS and BDF, and their support for solving
98adjoint ODEs in order to compute gradients of the solutions. The required
109right-hand-side function and some derivatives are either supplied manually or
@@ -16,9 +15,11 @@ C-function is passed to sunode, so that there is no python overhead.
1615` sunode ` comes with an PyTensor wrapper so that parameters of an ODE can be estimated
1716using PyMC.
1817
19- ### Installation
18+ ## Installation
19+
2020sunode is available on conda-forge. Set up a conda environment to use conda-forge
2121and install sunode:
22+
2223``` bash
2324conda create -n sunode-env
2425conda activate sunode-env
@@ -30,17 +31,18 @@ conda install sunode
3031
3132You can also install the development version. On Windows you have to make
3233sure the correct Visual Studio version is installed and in the PATH.
33- ```
34- git clone [email protected] :aseyboldt/sunode 34+
35+ ``` bash
36+ git clone
[email protected] :pymc-devs/sunode
3537# Or if no ssh key is configured:
36- git clone https://github.com/aseyboldt /sunode
38+ git clone https://github.com/pymc-devs /sunode
3739
3840cd sunode
3941conda install --only-deps sunode
4042pip install -e .
4143```
4244
43- ### Solve an ODE outside a PyMC model
45+ ## Solve an ODE outside a PyMC model
4446
4547We will use the Lotka-Volterra equations as an example ODE:
4648
@@ -64,7 +66,8 @@ def lotka_volterra(t, y, p):
6466 }
6567
6668
67- problem = sunode.symode.SympyProblem(
69+ from sunode.symode import SympyProblem
70+ problem = SympyProblem(
6871 params = {
6972 # We need to specify the shape of each parameter.
7073 # Any empty tuple corresponds to a scalar value.
@@ -90,9 +93,11 @@ problem = sunode.symode.SympyProblem(
9093# The solver generates uses numba and sympy to generate optimized C functions
9194# for the right-hand-side and if necessary for the jacobian, adoint and
9295# quadrature functions for gradients.
93- solver = sunode.solver.Solver(problem, compute_sens = False , solver = ' BDF' )
96+ from sunode.solver import Solver
97+ solver = Solver(problem, sens_mode = None , solver = ' BDF' )
9498
9599
100+ import numpy as np
96101tvals = np.linspace(0 , 10 )
97102# We can use numpy structured arrays as input, so that we don't need
98103# to think about how the different variables are stored in the array.
@@ -116,7 +121,8 @@ solver.solve(t0=0, tvals=tvals, y0=y0, y_out=output)
116121solver.as_xarray(tvals, output).solution_hares.plot()
117122
118123# Or we can convert it to a numpy record array
119- plt.plot(output.view(problem.state_dtype)[' hares' ]
124+ from matplotlib import pyplot as plt
125+ plt.plot(output.view(problem.state_dtype)[' hares' ])
120126```
121127
122128For this example the BDF solver (which isn't the best solver for a small non-stiff
@@ -125,12 +131,13 @@ takes about 40ms at a tolerance of 1e-10, 1e-10. So we are faster by a factor of
125131This advantage will get somewhat smaller for large problems however, when the
126132Python overhead of the ODE solver has a smaller impact.
127133
128- # ## Usage in PyMC
134+ ## Usage in PyMC
129135
130136Let's use the same ODE, but fit the parameters using PyMC, and gradients
131137computed using ` sunode ` .
132138
133139We'll use some time artificial data:
140+
134141``` python
135142import numpy as np
136143import sunode
@@ -188,27 +195,27 @@ with pm.Model() as model:
188195
189196 y_hat, _, problem, solver, _, _ = sunode.wrappers.as_pytensor.solve_ivp(
190197 y0 = {
191- # The initial conditions of the ode. Each variable
192- # needs to specify a PyTensor or numpy variable and a shape.
193- # This dict can be nested.
198+ # The initial conditions of the ode. Each variable
199+ # needs to specify a PyTensor or numpy variable and a shape.
200+ # This dict can be nested.
194201 ' hares' : (hares_start, ()),
195202 ' lynx' : (lynx_start, ()),
196203 },
197204 params = {
198- # Each parameter of the ode. sunode will only compute derivatives
199- # with respect to PyTensor variables. The shape needs to be specified
200- # as well. It it infered automatically for numpy variables.
201- # This dict can be nested.
205+ # Each parameter of the ode. sunode will only compute derivatives
206+ # with respect to PyTensor variables. The shape needs to be specified
207+ # as well. It it infered automatically for numpy variables.
208+ # This dict can be nested.
202209 ' alpha' : (alpha, ()),
203210 ' beta' : (beta, ()),
204211 ' gamma' : (gamma, ()),
205212 ' delta' : (delta, ()),
206213 ' extra' : np.zeros(1 ),
207214 },
208- # A functions that computes the right-hand-side of the ode using
209- # sympy variables.
215+ # A functions that computes the right-hand-side of the ode using
216+ # sympy variables.
210217 rhs = lotka_volterra,
211- # The time points where we want to access the solution
218+ # The time points where we want to access the solution
212219 tvals = times,
213220 t0 = times[0 ],
214221 )
@@ -224,15 +231,16 @@ with pm.Model() as model:
224231```
225232
226233We can sample using PyMC (You need ` cores=1 ` on Windows for the moment):
227- ```
234+
235+ ``` python
228236with model:
229237 idata = pm.sample(tune = 1000 , draws = 1000 , chains = 6 , cores = 6 )
230238```
231239
232240Many solver options can not be specified with a nice interface for now,
233241we can call the raw sundials functions however:
234242
235- ```
243+ ``` python
236244lib = sunode._cvodes.lib
237245lib.CVodeSStolerances(solver._ode, 1e-10 , 1e-10 )
238246lib.CVodeSStolerancesB(solver._ode, solver._odeB, 1e-8 , 1e-8 )
0 commit comments