Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.

Commit 1cc9432

Browse files
author
ariddell
committed
Compile CVODES functions with each Stan model
In 2017, model compilation takes ~40-50 seconds. Compiling CVODES functions with each model adds about 7 seconds. Closes #209.
1 parent 5365e91 commit 1cc9432

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

pystan/model.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,45 @@ def __init__(self, file=None, charset='utf-8', model_name="anon_model",
269269
s = template.safe_substitute(model_cppname=self.model_cppname)
270270
outfile.write(s)
271271

272+
## cvodes sources
273+
274+
# cvodes sources are complied and linked together with the Stan model
275+
# extension module. This is not ideal. In theory, build_clib could be
276+
# used to build a library once and models would be complied and then
277+
# linked with this library. This would save 7 or more seconds from every build.
278+
# But such a strategy is frustrated by the
279+
# lack of ``install_clib`` functionality in Python's distutils.
280+
#
281+
# TODO: numpy provides install_clib functionality, use that.
282+
cvodes_src_path = os.path.join(pystan_dir, 'stan', 'lib', 'stan_math', 'lib', 'cvodes_2.9.0', 'src')
283+
cvodes_sources = [
284+
os.path.join(cvodes_src_path, 'cvodes', 'cvodea.c'),
285+
os.path.join(cvodes_src_path, 'cvodes', 'cvodea_io.c'),
286+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes.c'),
287+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_band.c'),
288+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_bandpre.c'),
289+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_bbdpre.c'),
290+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_dense.c'),
291+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_diag.c'),
292+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_direct.c'),
293+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_io.c'),
294+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_sparse.c'),
295+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_spbcgs.c'),
296+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_spgmr.c'),
297+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_spils.c'),
298+
os.path.join(cvodes_src_path, 'cvodes', 'cvodes_sptfqmr.c'),
299+
os.path.join(cvodes_src_path, 'nvec_ser', 'nvector_serial.c'),
300+
os.path.join(cvodes_src_path, 'sundials', 'sundials_band.c'),
301+
os.path.join(cvodes_src_path, 'sundials', 'sundials_dense.c'),
302+
os.path.join(cvodes_src_path, 'sundials', 'sundials_direct.c'),
303+
os.path.join(cvodes_src_path, 'sundials', 'sundials_iterative.c'),
304+
os.path.join(cvodes_src_path, 'sundials', 'sundials_math.c'),
305+
os.path.join(cvodes_src_path, 'sundials', 'sundials_nvector.c'),
306+
os.path.join(cvodes_src_path, 'sundials', 'sundials_spbcgs.c'),
307+
os.path.join(cvodes_src_path, 'sundials', 'sundials_spgmr.c'),
308+
os.path.join(cvodes_src_path, 'sundials', 'sundials_sptfqmr.c'),
309+
]
310+
272311
stan_macros = [
273312
('BOOST_RESULT_OF_USE_TR1', None),
274313
('BOOST_NO_DECLTYPE', None),
@@ -291,7 +330,7 @@ def __init__(self, file=None, charset='utf-8', model_name="anon_model",
291330
distutils.log.set_verbosity(verbose)
292331
extension = Extension(name=self.module_name,
293332
language="c++",
294-
sources=[pyx_file],
333+
sources=[pyx_file] + cvodes_sources,
295334
define_macros=stan_macros,
296335
include_dirs=include_dirs,
297336
extra_compile_args=extra_compile_args)

pystan/tests/test_cvodes.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import unittest
2+
3+
import pystan
4+
5+
6+
class TestCVODES(unittest.TestCase):
7+
8+
def test_cvodes_program(self):
9+
# from integrate_ode_bdf.stan
10+
model_code = """
11+
functions {
12+
real[] sho(real t,
13+
real[] y,
14+
real[] theta,
15+
real[] x,
16+
int[] x_int) {
17+
real dydt[2];
18+
dydt[1] = y[2];
19+
dydt[2] = -y[1] - theta[1] * y[2];
20+
return dydt;
21+
}
22+
}
23+
data {
24+
int<lower=1> T;
25+
real y0_d[2];
26+
real t0;
27+
real ts[T];
28+
real theta_d[1];
29+
real x[0];
30+
int x_int[0];
31+
}
32+
parameters {
33+
real y0_p[2];
34+
real theta_p[1];
35+
}
36+
model {
37+
real y_hat[T,2];
38+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_d, x, x_int);
39+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_p, x, x_int);
40+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_d, x, x_int);
41+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_p, x, x_int);
42+
43+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_d, x, x_int, 1e-10, 1e-10, 1e8);
44+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_p, x, x_int, 1e-10, 1e-10, 1e8);
45+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_d, x, x_int, 1e-10, 1e-10, 1e8);
46+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_p, x, x_int, 1e-10, 1e-10, 1e8);
47+
}
48+
generated quantities {
49+
real y_hat[T,2];
50+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_d, x, x_int);
51+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_p, x, x_int);
52+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_d, x, x_int);
53+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_p, x, x_int);
54+
55+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_d, x, x_int, 1e-10, 1e-10, 1e8);
56+
y_hat = integrate_ode_bdf(sho, y0_d, t0, ts, theta_p, x, x_int, 1e-10, 1e-10, 1e8);
57+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_d, x, x_int, 1e-10, 1e-10, 1e8);
58+
y_hat = integrate_ode_bdf(sho, y0_p, t0, ts, theta_p, x, x_int, 1e-10, 1e-10, 1e8);
59+
}
60+
"""
61+
model = pystan.StanModel(model_code=model_code, verbose=True)
62+
self.assertIsNotNone(model)

0 commit comments

Comments
 (0)