Reference

API documentation.

AI4BayesCode has two layers. You drive the package to turn a model description into a compiled sampler. The compiled sampler exposes a small stateful interface you call to run the chain and read it back. A third set of helpers sits between them, turning a sampler’s output into diagnostics and a DAG. The same names work from R and Python.


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(Sys.getenv("ANTHROPIC_API_KEY"))  # your own key (the library rejects placeholders)
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, 1L, TRUE)                      # data, then seed, then keep_history
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))                       # overwrite the current draw
m$readapt_NUTS(500, FALSE, -1)                         # re-tune NUTS (R needs all three)

# run several chains in parallel for cross-chain R-hat / ESS
runs <- ai4bayescode_run_chains(
  function(seed) new(MyModel, y, X, as.integer(seed), 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 and openai. The key lives in the process environment only. It is never written to disk and never printed in full. With check left on, an Anthropic key gets a short streaming self-check right away, so a bad key is reported here as a warning instead of surfacing part-way through a generate run.

R       ai4bayescode_set_key(key, provider = "anthropic", check = TRUE)
Python  AI4BayesCode.set_key(key, provider="anthropic", check=True)

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 = NULL, classname = NULL, LLM = NULL,
          effort = NULL, output_path = NULL, backend = NULL, API_key = NULL,
          interactive = interactive(), use_cli = FALSE, max_attempts = NULL,
          priors = NULL, confirm_model = NULL)
Python  AI4BayesCode.generate(model_description=None, *, classname=None, LLM=None,
          effort=None, output_path=None, backend=None, API_key=None,
          max_attempts=None, priors=None, confirm_model=None)
model_description
Plain-language model spec (text, or a path to a .txt).
classname
C++ class name for the generated sampler. Asked interactively if unset, otherwise derived from the description.
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 the code-generation prompt without calling the LLM. It is pure and offline, so nothing is sent anywhere and nothing is asked in the console. Use it to inspect or hand-edit exactly what ai4bayescode_generate would send. It returns the system prompt, the user prompt, the class name, the backend, and the list of skill files that went in.

R       ai4bayescode_prompt(model_description, backend = c("both", "R", "Python"),
          output_path = "./generated", classname = NULL, priors = "noninformative",
          max_attempts = 5L, include_skills = FALSE, skills = NULL,
          confirm_model = FALSE)
Python  AI4BayesCode.prompt(model_description, *, backend="both",
          output_path="./generated", classname=None, priors="noninformative",
          max_attempts=5, include_skills=False, skills=None, confirm_model=False)

ai4bayescode_source

Compile the generated .cpp against the bundled library and load it, with no paths to configure. code is either a path to a .cpp file or a string holding the source itself. R binds the sampler class into the calling environment, so new(MyModel, ...) works straight afterwards. Python returns the imported module, so keep it: mod = AI4BayesCode.source(...), then mod.MyModel(...). Set rebuild when you want to force a recompile.

R       ai4bayescode_source(code, rebuild = FALSE, verbose = FALSE,
          extra_cppflags = character(), extra_libs = character(),
          env = parent.frame(), quiet = FALSE)
Python  AI4BayesCode.source(code, *, rebuild=False, verbose=False, quiet=False)

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(x)
Python  AI4BayesCode.doc(x)

In Python the same cards are also browsable as an object. AI4BayesCode.blocks exposes every bundled example as an attribute, so tab completion lists them, AI4BayesCode.blocks.BartNoise? prints that sampler’s card in IPython or Jupyter, and dir(AI4BayesCode.blocks) shows everything available. It is the Python counterpart of R’s ?BartNoise.

x is the loaded class, the class name as a string, or the path to the .cpp source. Python also accepts the loaded module.

ai4bayescode_example

Compile and load a bundled example by name, a one-line way to see a working sampler. R binds the class into the calling environment, so new(GaussianLocationScale, ...) works afterwards. Python returns the module, so keep the return value: mod = AI4BayesCode.example("GaussianLocationScale"), then mod.GaussianLocationScale(...). See ai4bayescode_list_examples() for the full set.

R       ai4bayescode_example(name, env = parent.frame(), ...)
Python  AI4BayesCode.example(name, *, rebuild=False, quiet=False)

R passes ... on to ai4bayescode_source, so rebuild and quiet work there too.

ai4bayescode_list_examples

List the built-in example samplers that ship with the package. Load any of them by name with ai4bayescode_example().

R       ai4bayescode_list_examples()
Python  AI4BayesCode.list_examples()

ai4bayescode_list_skills

List the bundled skill files, the written instructions a coding agent reads to run the AI4BayesCode workflow.

R       ai4bayescode_list_skills()
Python  AI4BayesCode.list_skills()

ai4bayescode_version

Report the installed AI4BayesCode version. A generated runner records it, so you know which version produced a sampler.

R       ai4bayescode_version()
Python  AI4BayesCode.version()

ai4bayescode_stream_check

Send a short prompt to your LLM with streaming turned on and print the reply as it arrives, so you can confirm the connection and key work before a full generate run. Anthropic keys only.

R       ai4bayescode_stream_check(LLM = "claude-opus-4-8", API_key = NULL,
          effort = NULL, progress = TRUE)
Python  AI4BayesCode.stream_check(LLM="claude-opus-4-8", API_key=None,
          effort=None, progress=True)

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(name, force = FALSE, quiet = FALSE)
Python  AI4BayesCode.install_block(name, force=False, quiet=False)
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 the compiler can see, like installed.packages(). This covers both tiers. It lists the blocks you downloaded into your per-user library, and it also lists the blocks you are developing under ./blocks_local/ in the current project. A block that exists in both places is listed once, because the local copy shadows the download on the compile path.

R       ai4bayescode_installed_blocks(tier = c("all", "local", "download"))
Python  AI4BayesCode.installed_blocks(tier="all")
tier
Which tier to list. "all" is the default and covers both. "local" is ./blocks_local/ in the current project. "download" is the per-user library.

ai4bayescode_remove_block

Remove an installed contributed block, like remove.packages(). It deletes the block from your per-user library, the folder ai4bayescode_blocks_path() reports. A block you are developing under ./blocks_local/ is left alone, so delete that folder yourself when you want it gone.

R       ai4bayescode_remove_block(name)
Python  AI4BayesCode.remove_block(name)

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. The last two are always the same: an integer RNG seed, then keep_history, which records every draw when it is on and otherwise keeps only the latest. R matches constructor arguments by position, so pass them in order. In Python they are keyword arguments and the seed is called rng_seed.

R       m <- new(MyModel, y, X, 1L, TRUE)
Python  m = mod.MyModel(y, X, rng_seed=1, keep_history=True)

step

Run n sweeps in one call, advancing the chain. Called with no argument it 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()
        m$step(n)
Python  m.step(n_steps=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 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 mass matrix is carried through unchanged, it is not adapted here. max_tree_depth caps the tree-doubling depth per iteration, and target_accept overrides the block’s target acceptance rate for this readaptation. In both, -1 means leave the block’s own setting alone. Python fills those defaults in for you. R does not, so pass at least the first three arguments in order. This method exists only on models that contain a NUTS block.

R       m$readapt_NUTS(n, reset, max_tree_depth)
        m$readapt_NUTS(n, reset, max_tree_depth, target_accept)
Python  m.readapt_NUTS(n, reset=False, max_tree_depth=-1, target_accept=-1.0)

freeze / unfreeze / get_frozen

Hold part of a model fixed while the rest keeps sampling. freeze takes one or more block names, the same names get_current reports, and holds those blocks at their current values, so they do not change on the steps that follow. unfreeze returns them to being sampled, and with no name given it releases all of them. get_frozen lists the blocks that are currently frozen. This is useful when you want to condition on one part of a model, or hold some values fixed while you study the rest. Set the values you want with set_current first, then freeze.

R       m$freeze(c("sigma", "beta"))
        m$freeze("sigma", TRUE)
        m$unfreeze("sigma")
        m$unfreeze()
        m$get_frozen()
Python  m.freeze(["sigma", "beta"], quiet=False)
        m.unfreeze(["sigma"])
        m.unfreeze()
        m.get_frozen()

A name is matched first against the block names, then against a slot name when exactly one block exposes it, and you can always give the full "block.slot" path. Freezing something twice warns instead of failing, and quiet turns that warning off. In R it is positional, so pass TRUE in the second slot.

ai4bayescode_new_frozen

Do the set-then-freeze in one call. It builds the sampler, sets the blocks named in fixed to the values you give, and freezes them, so every following step samples the parameters left free. R takes fixed as a named list, Python as a dict. The names must be flat block names, the same ones set_current accepts. This exists in both languages.

R       ai4bayescode_new_frozen(module_class, ..., fixed = list(), quiet_freeze = TRUE)
        ai4bayescode_new_frozen(MyModel, y, X, fixed = list(sigma = 1.0))
Python  AI4BayesCode.new_frozen(module_class, *args, fixed=None, quiet_freeze=True, **kwargs)
        AI4BayesCode.new_frozen(mod.MyModel, y, X, fixed={"sigma": 1.0})

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, n_burn = 0, plot = TRUE, order_components = FALSE)
Python  AI4BayesCode.diagnose(hist, n_burn=0, plot=True, order_components=False)

ai4bayescode_plot_dag

Draw the model as a plate DAG straight from the sampler, reading its get_dag() under the hood. In R, leave out_path empty to draw to the active device, or pass a path to write a PNG. The Python helper always writes a PNG and returns its path, using a temporary directory when out_path is omitted. The plotting arguments follow each language’s own convention.

R       ai4bayescode_plot_dag(model, out_path = NULL, main = NULL, width = 1600,
          height = 1100, res = 150, plate = TRUE)
Python  AI4BayesCode.plot_dag(model, *, out_path=None, title=None, figsize=(12, 8),
          dpi=150, plate=True)

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.

R       ai4bayescode_run_chains(function(seed) new(MyModel, y, X, as.integer(seed), TRUE),
                                n_chains = 4, n_burn = 5000, n_keep = 5000)
Python  AI4BayesCode.run_chains(lambda s: mod.MyModel(y, X, rng_seed=s, keep_history=True),
                                n_chains=4, n_burn=5000, n_keep=5000)

ai4bayescode_rhat_summary

Take the chains that run_chains returns and report the split-R-hat and bulk-ESS convergence diagnostics for every parameter in one table. When a matrix parameter shows a high R-hat only because of label switching, it says so. This is the convergence companion to run_chains.

R       ai4bayescode_rhat_summary(run, keys = NULL, drop_burn = 0, order_components = FALSE)
Python  AI4BayesCode.rhat_summary(chains, keys=None, drop_burn=0, order_components=False)

rhat / ess_bulk / ess_tail / posterior_summary

The lower-level diagnostics behind those tables, computed one parameter at a time. rhat is the split-R-hat, where a value near 1.0 means the chains agree. ess_bulk and ess_tail are the effective sample sizes for the bulk and the tails of the distribution. posterior_summary returns the mean, median, standard deviation, MAD, a credible interval, R-hat, and ESS for one parameter. These are Python only.

Python  AI4BayesCode.rhat(samples)
Python  AI4BayesCode.ess_bulk(samples)
Python  AI4BayesCode.ess_tail(samples, quantile_lo=0.05, quantile_hi=0.95)
Python  AI4BayesCode.posterior_summary(samples, prob=0.90, *, chains=False)

ai4bayescode_perf_hint

Deprecated. It prints the per-sweep timing and suggests moving tightly-coupled parameters into a single joint NUTS block, but it asks you to compute and pass two numbers that run_chains already returns, and its fixed half-second-per-sweep threshold ignores the size of the model. It warns on every call and will be removed, and the generated runners no longer call it. Read the timing off the run object instead: sum(run$wall) in R, sum(c["wall"] for c in runs) in Python.

R       ai4bayescode_perf_hint(wall_sec, n_sweeps_total, uses_joint_nuts = FALSE,
          thresholds = list(slow_sweep_sec = 0.5))                       # deprecated
Python  AI4BayesCode.perf_hint(wall_sec, n_sweeps_total, uses_joint_nuts=False)    # deprecated

include_path / blocks_path / examples_path / skills_path

Find the files AI4BayesCode ships, and the blocks you have installed. include_path is the C++ header directory that goes on the compile path, and it takes no arguments. examples_path and skills_path point at the bundled examples and skill files, and give one a name to get the path to a single item. Those three return an empty string when the target is not there. blocks_path is the odd one out: it is your own per-user library of installed contributed blocks at ~/.AI4BayesCode/blocks_download/, which lives outside the package and is shared across R, Python, and C++. These are mainly for build tooling and coding agents.

R       ai4bayescode_include_path()
        ai4bayescode_blocks_path(name = NULL)
        ai4bayescode_examples_path(name = NULL)
        ai4bayescode_skills_path(name = NULL)
Python  AI4BayesCode.include_path()
        AI4BayesCode.blocks_path(name=None)
        AI4BayesCode.examples_path(name=None)
        AI4BayesCode.skills_path(name=None)
        AI4BayesCode.vendored_include_path()   # Python only