Describe your model in words, with its likelihood and priors. AI4BayesCode parses the description, picks the right built-in samplers, and hands you compiled code you can run from R, Python, or C++. You write the model, it writes the sampler.
Before writing any code, AI4BayesCode confirms it parsed the model you meant. After, it checks the sampler compiles, screens the code for silent bugs, and runs it on simulated data to confirm it recovers the right posterior, reporting R-hat, ESS, and posterior-predictive checks.
A recursively stateful coding paradigm is central to AI4BayesCode. Each block keeps its own state as the quantities around it change, so it keeps sampling inside a larger MCMC instead of starting over. This lets modular sampling components, even ones developed by different contributors, be composed coherently within larger MCMC procedures, and the same paradigm makes model development and exploration fast.
AI4BayesCode is built to be extended. New kinds of models can be supported quickly by adding a block, so the system keeps up with new methods. The block library is also open to contribution, so users can add their own blocks and grow the set of models the system can build.
Examples
From description to runnable chain.
You write the model in natural language, with priors and likelihood. AI4BayesCode builds the sampler, validates it, compiles it to C++, and ships R and Python bindings. Below are two short prompts and the samplers they produce.
Natural-language prompt
Bayesian linear regression, y ~ N(Xbeta, sigma^2). Normal prior on beta,
half-Normal on sigma.
Blocks selected
joint_nuts_block(beta, sigma)
Generated interface
library(AI4BayesCode)
ai4bayescode_generate(
"linear regression, y ~ N(Xbeta, sigma^2), normal prior on beta, half-normal on sigma",
classname = "BayesLinReg", output_path = "./generated")
# a sampler written by the coding agent is loaded the same wayai4bayescode_source("./generated/BayesLinReg.cpp")
ai4bayescode_doc(BayesLinReg) # what the constructor needs# run several chains and check convergence (the usual workflow)
run <- ai4bayescode_run_chains(
function(s) new(BayesLinReg, y, X,
seed = s, keep_history = TRUE),
n_chains = 4, n_burn = 5000, n_keep = 5000)
ai4bayescode_rhat_summary(run) # cross-chain R-hat and ESSai4bayescode_diagnose(run$histories[[1]]) # trace, ACF, density# optional: stateful usage, drive a single chain yourself
m <- new(BayesLinReg, y, X, seed = 1L, keep_history = TRUE)
m$step(10000)
hist <- m$get_history() # named list of matrices
cur <- m$get_current() # current draw
importAI4BayesCodeAI4BayesCode.generate(
"linear regression, y ~ N(Xbeta, sigma^2), normal prior on beta, half-normal on sigma",
classname="BayesLinReg", output_path="./generated")
# a sampler written by the coding agent is loaded the same way
mod = AI4BayesCode.source("./generated/BayesLinReg.cpp")
AI4BayesCode.doc(mod.BayesLinReg) # what the constructor needs# run several chains and check convergence (the usual workflow)
runs = AI4BayesCode.run_chains(
lambda s: mod.BayesLinReg(y, X,
seed=s, keep_history=True),
seeds=range(4), n_burn=5000, n_keep=5000)
AI4BayesCode.diagnose(runs[0]) # R-hat, ESS, trace, ACF, density# optional: stateful usage, drive a single chain yourself
m = mod.BayesLinReg(y, X, seed=1, keep_history=True)
m.step(10000)
hist = m.get_history() # dict of arrays
cur = m.get_current() # current draw
Natural-language prompt
Spike-and-slab regression, y ~ N(Xbeta, sigma^2). beta_j ~ Laplace(b*sigma) if
gamma_j = 1, else 0. gamma_j ~ Bernoulli(pi). pi ~ Beta(1, 1),
half-Normal priors on sigma and b.
Blocks selected
rjmcmc_block(beta, gamma)
joint_nuts_block(sigma, b)
beta_gibbs_block(pi)
Generated interface
library(AI4BayesCode)
ai4bayescode_generate(
"spike and slab, y ~ N(Xbeta, sigma^2), beta_j ~ Laplace(b*sigma) if gamma_j=1 else 0, gamma_j ~ Bernoulli(pi), pi ~ Beta(1,1), sigma ~ HalfNormal(0,1), b ~ HalfNormal(0,1)",
classname = "SpikeLaplace", output_path = "./generated")
# a sampler written by the coding agent is loaded the same wayai4bayescode_source("./generated/SpikeLaplace.cpp")
ai4bayescode_doc(SpikeLaplace) # what the constructor needs# run several chains and check convergence (the usual workflow)
run <- ai4bayescode_run_chains(
function(s) new(SpikeLaplace, y, X,
seed = s, keep_history = TRUE),
n_chains = 4, n_burn = 5000, n_keep = 5000)
ai4bayescode_rhat_summary(run)
# posterior inclusion probabilities
inc <- colMeans(run$histories[[1]]$gamma)
# optional: stateful usage, drive one chain with full control
m <- new(SpikeLaplace, y, X, seed = 1L, keep_history = TRUE)
m$set_current(list(sigma = 1.0)) # warm-start a parameter
m$step(2000); m$readapt_NUTS(500) # warm up, then re-tune NUTS
m$step(10000)
ai4bayescode_diagnose(m$get_history())
importAI4BayesCodeAI4BayesCode.generate(
"spike and slab, y ~ N(Xbeta, sigma^2), beta_j ~ Laplace(b*sigma) if gamma_j=1 else 0, gamma_j ~ Bernoulli(pi), pi ~ Beta(1,1), sigma ~ HalfNormal(0,1), b ~ HalfNormal(0,1)",
classname="SpikeLaplace", output_path="./generated")
# a sampler written by the coding agent is loaded the same way
mod = AI4BayesCode.source("./generated/SpikeLaplace.cpp")
AI4BayesCode.doc(mod.SpikeLaplace) # what the constructor needs# run several chains and check convergence (the usual workflow)
runs = AI4BayesCode.run_chains(
lambda s: mod.SpikeLaplace(y, X,
seed=s, keep_history=True),
seeds=range(4), n_burn=5000, n_keep=5000)
# posterior inclusion probabilities
inc = runs[0]["gamma"].mean(axis=0)
# optional: stateful usage, drive one chain with full control
m = mod.SpikeLaplace(y, X, seed=1, keep_history=True)
m.set_current({"sigma": 1.0}) # warm-start a parameter
m.step(2000); m.readapt_NUTS(500) # warm up, then re-tune NUTS
m.step(10000)
AI4BayesCode.diagnose(m.get_history())