Using Walnuts from Python#
This notebook will show to how run the same model (a simple standard normal) implemented in Python, Numba (a just-in-time compiler package), and Stan.
[1]:
import walnutpie
def summarize(name, fit):
summarizer = walnutpie.Summarizer(fit)
mean = summarizer.mean()
std = summarizer.standard_deviation()
ess = summarizer.ess()
r_hat = summarizer.r_hat()
draws = summarizer._stacked.shape[0]
print(f"{name}\tdim\tmean\tstd\tess\trhat\tdraws")
for i in range(len(mean)):
print(
f"\t{i}\t{mean[i]:.4f}\t{std[i]:.4f}\t{ess[i]:.2f}\t{r_hat[i]:.4f}\t{draws}"
)
[2]:
import os
import bridgestan
stan_code = os.path.join(
bridgestan.compile.get_bridgestan_path(), "test_models/multi/multi.stan"
)
with open(stan_code, 'r') as f:
print(f.read())
m = bridgestan.StanModel(
stan_code,
{"M": 2, "N": 0, "P": 0},
make_args=["STAN_THREADS=1"],
)
data {
int<lower=0> M;
int<lower=0> N;
int<lower=0> P;
}
parameters {
vector[M] alpha;
}
model {
alpha ~ normal(0, 1);
}
[3]:
%%time
summarize("stan", walnutpie.walnuts_stan(m, seed=1234))
stan dim mean std ess rhat draws
0 0.0370 0.9487 1003.89 1.0058 949
1 0.0289 0.9732 943.73 1.0016 949
CPU times: user 12 ms, sys: 993 μs, total: 13 ms
Wall time: 4.71 ms
Python#
Defining a pure-python log density is simple and highly flexible, but will usually be slower than the other options due to the extra overhead of the Python language
[4]:
import numpy as np
import scipy.stats
def logp(x):
return np.sum(scipy.stats.norm.logpdf(x)), -x
[5]:
%%time
summarize("pyfunc", walnutpie.walnuts_pyfunc(logp, num_params=2))
pyfunc dim mean std ess rhat draws
0 0.0127 1.0221 1576.80 1.0016 2090
1 -0.0161 0.9791 1877.34 1.0021 2090
CPU times: user 1.63 s, sys: 494 ms, total: 2.12 s
Wall time: 1.52 s
Numba#
If we are willing to use `numba <https://numba.pydata.org/>`__, we can get much faster!
[6]:
import numba
from numba import types
from numba_stats import norm
@numba.cfunc(
types.intc(
types.size_t,
types.CPointer(types.double),
types.CPointer(types.double),
types.CPointer(types.double),
types.voidptr,
),
nopython=True,
)
def logp_numba(size, x_, grad_, lp, _):
x = numba.carray(x_, size)
lp[0] = norm.logpdf(x, 0.0, 1.0).sum()
grad = numba.carray(grad_, size)
grad[:] = -x
return 0
[7]:
%%time
summarize("numba", walnutpie.walnuts_pyfunc(logp_numba, num_params=2))
numba dim mean std ess rhat draws
0 0.0142 1.0107 2833.27 1.0002 3985
1 -0.0218 0.9991 2675.36 1.0002 3985
CPU times: user 20.4 ms, sys: 24 μs, total: 20.4 ms
Wall time: 6.97 ms