A worked example
The whole flow in R: set your key once, describe the model, compile, run, and read it back. Python is identical, with AI4BayesCode. in front of the package calls and m.method() on the sampler.
library(AI4BayesCode)
ai4bayescode_set_key("sk-ant-...") # your own LLM key here ("sk-ant-..." is a placeholder)
ai4bayescode_generate( # writes ./generated/MyModel.cpp
"y ~ N(X beta, sigma^2), beta ~ N(0, 10^2), sigma ~ HalfNormal(0, 1)",
output_path = "./generated")
ai4bayescode_source("./generated/MyModel.cpp") # compiles + loads MyModel
ai4bayescode_doc(MyModel) # what does the constructor want?
# keep_history = TRUE records every sweep, FALSE keeps only the latest draw
m <- new(MyModel, y, X, seed = 1L, keep_history = TRUE)
m$step(10000) # run the sampler (treat early draws as burn-in)
hist <- m$get_history() # named list of matrices, one per parameter
ai4bayescode_diagnose(hist) # R-hat / ESS table + trace, ACF, density
ai4bayescode_plot_dag(m) # model DAG
# optional: warm-start from chosen values, then re-tune NUTS if the geometry shifts
m$set_current(list(sigma = 1.0, beta = b0)) # overwrite the current draw
m$readapt_NUTS(500) # re-tune NUTS step size + mass matrix
# run several chains in parallel for cross-chain R-hat / ESS
runs <- ai4bayescode_run_chains(
function(seed) new(MyModel, y, X, seed = seed, keep_history = TRUE),
n_chains = 4, n_burn = 5000, n_keep = 5000)
ai4bayescode_rhat_summary(runs) # cross-chain R-hat and ESS
ai4bayescode_diagnose(runs$histories[[1]]) # full diagnostics on one chain
The package
These functions take you from a model description to a running sampler. Every R name carries the ai4bayescode_ prefix. The Python module mirrors them under AI4BayesCode..
ai4bayescode_set_key
Store your LLM provider key for the session. Providers are anthropic, openai, and google.
R ai4bayescode_set_key(key, provider = "anthropic")
Python AI4BayesCode.set_key(key, provider="anthropic")
ai4bayescode_key_status
Report which provider keys are set this session.
R ai4bayescode_key_status()
Python AI4BayesCode.key_status()
ai4bayescode_models
List the supported LLMs (name, provider, and effort levels) you can pass to ai4bayescode_generate.
R ai4bayescode_models()
Python AI4BayesCode.models()
ai4bayescode_generate
Parse and validate the model description, then write a sampler .cpp to output_path. A validate-then-repair loop (up to max_attempts) keeps going until the code compiles and passes the checks. Pass API_key here, or set it once with ai4bayescode_set_key.
R ai4bayescode_generate(model_description, LLM = NULL, effort = NULL,
backend = NULL, output_path = NULL, max_attempts = NULL, API_key = NULL)
Python AI4BayesCode.generate(model_description, LLM=None, effort=None,
backend=None, output_path=None, max_attempts=None, API_key=None)
- model_description
- Plain-language model spec (text, or a path to a
.txt).
- LLM, effort
- Which model writes the sampler, and how hard it thinks. Left unset (
NULL/None) they are asked interactively; see ai4bayescode_models().
- backend
- Target runtime:
"R", "Python", or "both" — one dual-module .cpp usable from both (the default when unset). Asked interactively if unset.
- output_path
- Where the generated
.cpp is written (default ./generated/).
- max_attempts
- Validate-then-repair budget (default 5). Asked interactively if unset.
- API_key
- Provider key for this call. Optional if you used
ai4bayescode_set_key.
ai4bayescode_prompt
Build (and, interactively, confirm) the code-generation prompt without calling the LLM. Useful to inspect or hand-edit what ai4bayescode_generate would send.
R ai4bayescode_prompt(model_description)
Python AI4BayesCode.prompt(model_description)
ai4bayescode_source
Compile the generated .cpp against the bundled library and load it, with no paths to configure. After it returns, the sampler class is available: new(MyModel, ...) in R, mod.MyModel(...) in Python.
R ai4bayescode_source("./generated/MyModel.cpp")
Python AI4BayesCode.source("./generated/MyModel.cpp")
ai4bayescode_doc
Print a usage card for a compiled sampler: the constructor arguments with their types and defaults, the model description, and the available methods. It answers “what does the constructor want?”.
R ai4bayescode_doc(MyModel)
Python AI4BayesCode.doc(MyModel)
ai4bayescode_example
Compile and load a bundled example by name, a one-line way to see a working sampler.
R ai4bayescode_example("GaussianLocationScale")
Python AI4BayesCode.example("GaussianLocationScale")
The remaining helpers list and report: ai4bayescode_list_examples(), ai4bayescode_list_skills(), ai4bayescode_version(), and ai4bayescode_stream_check() to confirm streaming works with your key. Each is mirrored under AI4BayesCode. in Python.
Contributed blocks
The core blocks ship with the package. Community blocks live in the hub registry and install on demand, the same way R installs a package from CRAN. Once a block is installed, its header is on the compile path and its skill is available to the generator, so you can use it like a built-in one. The Python module mirrors each name under AI4BayesCode..
ai4bayescode_available_blocks
List the contributed blocks you can install from the hub registry, like available.packages().
R ai4bayescode_available_blocks()
Python AI4BayesCode.available_blocks()
ai4bayescode_install_block
Install a contributed block into your library, like install.packages(). It downloads a reviewed bundle from the hub registry into a fixed per-user library at ~/.AI4BayesCode/blocks_download/, shared across R, Python, and C++, and adds the block, with any vendored dependencies, to the compile path, so the next ai4bayescode_source or ai4bayescode_generate can use it. The registry is curated and pre-validated, so installing is a download and version check, not a compile.
R ai4bayescode_install_block("nngp_gaussian_gibbs_block")
Python AI4BayesCode.install_block("nngp_gaussian_gibbs_block")
- name
- The block name, as listed by
ai4bayescode_available_blocks().
- force
- Reinstall even if it is already present. Off by default.
- quiet
- Skip the progress messages and the install summary. Off by default.
ai4bayescode_installed_blocks
List the contributed blocks already installed in your library, like installed.packages().
R ai4bayescode_installed_blocks()
Python AI4BayesCode.installed_blocks()
ai4bayescode_remove_block
Remove an installed contributed block, like remove.packages(). The library folder itself is ai4bayescode_blocks_path().
R ai4bayescode_remove_block("nngp_gaussian_gibbs_block")
Python AI4BayesCode.remove_block("nngp_gaussian_gibbs_block")
The generated sampler
Once ai4bayescode_source (or ai4bayescode_example) has loaded a sampler class, build it with your data and drive it through a small stateful interface. Every block, every composite, and the top-level sampler expose the same methods. In R they are m$method(). In Python, m.method(). Each signature below is the general form.
constructor
Build the sampler with your data. The arguments depend on the model, so run ai4bayescode_doc(MyModel) to see them. seed fixes the RNG. keep_history = TRUE records every draw, otherwise only the latest is kept.
R m <- new(MyModel, y, X, seed = 1L, keep_history = TRUE)
Python m = mod.MyModel(y, X, seed=1, keep_history=True)
step
Run n sweeps in one call, advancing the chain. n defaults to 1, so m$step() advances a single sweep. Every sweep is recorded when keep_history = TRUE, so there is no separate unrecorded burn-in. Treat the early draws as burn-in when you analyze.
R m$step(n = 1)
Python m.step(n=1)
get_current
The current draw as a named list (R) or dict (Python), keyed by parameter. Does not advance the chain.
R m$get_current()
Python m.get_current()
set_current
Overwrite current values from a named list, to warm-start from a previous run or a chosen starting point. Keys are parameter names (and, for some blocks, the data they condition on).
R m$set_current(list(sigma = 1.0, beta = b0))
Python m.set_current({"sigma": 1.0, "beta": b0})
get_history
All stored draws as a named list of matrices, each (n_draws × dim). Feed it straight to ai4bayescode_diagnose().
R m$get_history()
Python m.get_history()
predict_at
Posterior-predictive draws at new inputs, using the recorded chain. Pass the new data as a named list (column-major flattened where the constructor took a matrix). If you do not supply all of the inputs, it still returns a partial prediction of the intermediate quantities it can reach. Returns a named list and does not mutate state.
R m$predict_at(list(X = X_new))
Python m.predict_at({"X": X_new})
get_dag
The model’s dependency graph as data (a named list of edges). ai4bayescode_plot_dag() reads this to draw the figure.
R m$get_dag()
Python m.get_dag()
readapt_NUTS
Re-tune the NUTS step size and mass matrix for n iterations without advancing the chain. With reset = FALSE (the default) it continues adapting from the current state. With reset = TRUE it reinitializes dual-averaging and adapts from scratch. Continuing is what you want for online learning, where new data arrives and you keep refining. Resetting is for when the target geometry has changed and the old tuning no longer fits. The optional max_tree_depth caps the tree-doubling depth per iteration; -1 (the default) uses the block's configured depth. NUTS-family blocks only, others are skipped.
R m$readapt_NUTS(n, reset = FALSE, max_tree_depth = -1)
Python m.readapt_NUTS(n, reset=False, max_tree_depth=-1)
Diagnostics & DAG
These helpers sit between the two layers: they take what a sampler produces, its history and its structure, and turn it into convergence diagnostics and figures. They ship with the package, so a generated runner calls the same functions by name.
ai4bayescode_diagnose
From get_history(): a summary table (R-hat, bulk / tail ESS) plus a trace, autocorrelation, and density plot. The one call to sanity-check a run.
R ai4bayescode_diagnose(hist)
Python AI4BayesCode.diagnose(hist)
ai4bayescode_plot_dag
Draw the model as a plate DAG straight from the sampler, reading its get_dag() under the hood. Leave out_path empty to show it, or pass a path to write a PNG.
R ai4bayescode_plot_dag(m, out_path = NULL)
Python AI4BayesCode.plot_dag(m)
ai4bayescode_run_chains
Run several independent chains in parallel and collect them, for cross-chain R-hat and ESS. The first argument is a function that builds a fresh sampler from a seed. Convergence companion: ai4bayescode_rhat_summary() in R, AI4BayesCode.rhat_summary() in Python (or the lower-level rhat(), ess_bulk(), and ess_tail()).
R ai4bayescode_run_chains(function(seed) new(MyModel, y, X, seed = seed, keep_history = TRUE),
n_chains = 4, n_burn = 5000, n_keep = 5000)
Python AI4BayesCode.run_chains(lambda s: mod.MyModel(y, X, seed=s, keep_history=True),
seeds=range(4), n_burn=5000, n_keep=5000)