bamojax 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. bamojax-0.4.2/LICENSE +21 -0
  2. bamojax-0.4.2/MANIFEST.in +4 -0
  3. bamojax-0.4.2/PKG-INFO +264 -0
  4. bamojax-0.4.2/README.md +229 -0
  5. bamojax-0.4.2/bamojax/__init__.py +2 -0
  6. bamojax-0.4.2/bamojax/_version.py +21 -0
  7. bamojax-0.4.2/bamojax/base.py +632 -0
  8. bamojax-0.4.2/bamojax/examples/__init__.py +0 -0
  9. bamojax-0.4.2/bamojax/gwishart/__init__.py +6 -0
  10. bamojax-0.4.2/bamojax/gwishart/gwishart.py +361 -0
  11. bamojax-0.4.2/bamojax/inference.py +794 -0
  12. bamojax-0.4.2/bamojax/marginal_likelihoods/__init__.py +0 -0
  13. bamojax-0.4.2/bamojax/marginal_likelihoods/bayesian_quadrature.py +55 -0
  14. bamojax-0.4.2/bamojax/marginal_likelihoods/bridge_sampling.py +180 -0
  15. bamojax-0.4.2/bamojax/marginal_likelihoods/importance_sampling.py +82 -0
  16. bamojax-0.4.2/bamojax/marginal_likelihoods/laplace.py +40 -0
  17. bamojax-0.4.2/bamojax/marginal_likelihoods/naive_monte_carlo.py +55 -0
  18. bamojax-0.4.2/bamojax/marginal_likelihoods/thames.py +122 -0
  19. bamojax-0.4.2/bamojax/marginal_likelihoods/utility.py +54 -0
  20. bamojax-0.4.2/bamojax/model_search/__init__.py +1 -0
  21. bamojax-0.4.2/bamojax/model_search/anchored_rjsmc.py +236 -0
  22. bamojax-0.4.2/bamojax/modified_blackjax/__init__.py +0 -0
  23. bamojax-0.4.2/bamojax/modified_blackjax/modified_elliptical_slice_nd.py +323 -0
  24. bamojax-0.4.2/bamojax/modified_blackjax/more_proposals.py +66 -0
  25. bamojax-0.4.2/bamojax/more_distributions.py +556 -0
  26. bamojax-0.4.2/bamojax/samplers.py +435 -0
  27. bamojax-0.4.2/bamojax.egg-info/PKG-INFO +264 -0
  28. bamojax-0.4.2/bamojax.egg-info/SOURCES.txt +38 -0
  29. bamojax-0.4.2/bamojax.egg-info/dependency_links.txt +1 -0
  30. bamojax-0.4.2/bamojax.egg-info/requires.txt +20 -0
  31. bamojax-0.4.2/bamojax.egg-info/top_level.txt +1 -0
  32. bamojax-0.4.2/pyproject.toml +83 -0
  33. bamojax-0.4.2/setup.cfg +11 -0
  34. bamojax-0.4.2/setup.py +8 -0
  35. bamojax-0.4.2/tests/test_base.py +63 -0
  36. bamojax-0.4.2/tests/test_inference.py +214 -0
  37. bamojax-0.4.2/tests/test_more_distributions.py +42 -0
  38. bamojax-0.4.2/tests/test_samplers_and_engines.py +374 -0
  39. bamojax-0.4.2/versioneer.py +2277 -0
bamojax-0.4.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Max Hinne, Uncertainty in complex systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include versioneer.py
2
+ include README.md
3
+ include LICENSE
4
+ recursive-include bamojax *.py
bamojax-0.4.2/PKG-INFO ADDED
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: bamojax
3
+ Version: 0.4.2
4
+ Summary: Bayesian Modelling with JAX
5
+ Author: Max Hinne
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/UncertaintyInComplexSystems/bamojax
8
+ Project-URL: Issues, https://github.com/UncertaintyInComplexSystems/bamojax/issues
9
+ Project-URL: Documentation, https://mhinne.github.io/uncertainty-in-complex-systems/bamojax_demo.html
10
+ Keywords: bayesian modelling,jax,probabilistic,mcmc,smc,inference
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: jax>=0.10.0
17
+ Requires-Dist: jaxlib>=0.10.0
18
+ Requires-Dist: blackjax>=1.6
19
+ Requires-Dist: numpyro>=0.22
20
+ Requires-Dist: jaxtyping
21
+ Requires-Dist: optax
22
+ Requires-Dist: tqdm
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == "dev"
25
+ Requires-Dist: pytest-cov>=5; extra == "dev"
26
+ Requires-Dist: ipython>=8.0; extra == "dev"
27
+ Provides-Extra: docs
28
+ Requires-Dist: mkdocs; extra == "docs"
29
+ Requires-Dist: mkdocs-material; extra == "docs"
30
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
31
+ Requires-Dist: pymdown-extensions; extra == "docs"
32
+ Requires-Dist: mkdocs-gen-files; extra == "docs"
33
+ Requires-Dist: mkdocs-literate-nav; extra == "docs"
34
+ Dynamic: license-file
35
+
36
+ [![Tests](https://github.com/uncertaintyincomplexsystems/bamojax/actions/workflows/test.yml/badge.svg)](https://github.com/uncertaintyincomplexsystems/bamojax/actions/workflows/test.yml) [![DOI](https://joss.theoj.org/papers/10.21105/joss.08642/status.svg)](https://doi.org/10.21105/joss.08642)
37
+
38
+ ![bamojax logo](https://github.com/uncertaintyincomplexsystems/bamojax/blob/main/figures/bamojax_logo_2x.png "bamojax")
39
+
40
+ # Welcome to bamojax!
41
+
42
+ Welcome to **bamojax**, the Bayesian modelling and model comparison toolbox implemented using the JAX coding universe. **bamojax** is a probabilistic programming language (PPL), similar to Numpyro, PyMC, Stan, JAGS, and BUGS, as well as a set of analyses for model comparison and averaging. **bamojax** relies on [Blackjax](https://blackjax-devs.github.io/blackjax/) for approximate inference, and on [Numpyro](https://num.pyro.ai/en/stable/) for probability distributions and their essential operations.
43
+
44
+ ## What sets bamojax apart?
45
+
46
+ Existing PPLs, such as PyMC, can export their log density function so it can be sampled using Blackjax. However, this has two downsides:
47
+
48
+ 1. Not all existing probabilistic programming easily allow you to update variables in your model using Gibbs Markov chain Monte Carlo (MCMC) algorithms. For example, if one wants to approximate the posterior over a latent Gaussian process (GP) and its hyperparameters, it can be more efficient to use elliptical slice sampling for the GP, than to apply the No-U-Turn Hamiltonian Monte Carlo Sampler [(Hoffman and Gelman, 2014)](https://jmlr.org/papers/v15/hoffman14a.html) to all variables at once. This effect is even more pronounced when embedding MCMC sampling within Sequential Monte Carlo (see [Hinne, 2025](https://link.springer.com/article/10.3758/s13428-025-02642-1) for more details).
49
+ 2. It is harder to embed this logdensity into tempered Sequential Monte Carlo algorithms, as for this one needs access to the prior and likelihood separately.
50
+
51
+ By implementing your own models and samplers using Blackjax, these problems can be circumvented. However, this is a labour-intensive and error-prone process. Therefore, **bamojax** provides a user-friendly interface around Blackjax, that allows for easy model construction and Gibbs sampling.
52
+
53
+ Once a Bayesian model has been determined, the next goal is often to compare it with competing hypotheses. This can, for example, be done by computing the marginal likelihoods of different models, and taking their ratio, a quantity known as the _Bayes factor_. However, computing marginal likelihoods and therefore Bayes factors is computationally challenging, and several approximation strategies exist. **bamojax** makes this process easier by implementing the following marginal likelihood approximations:
54
+
55
+ - Importance sampling
56
+ - Sequential Monte Carlo
57
+ - The Laplace approximation
58
+ - Bridge sampling (Gronau et al., 2017; Meng & Wong, 1996)
59
+ - The THAMES estimator (Metodiev et al., 2025)
60
+
61
+ More comparison features are being added to the toolbox continuously.
62
+
63
+ ## Installation
64
+
65
+ Install **bamojax** using:
66
+
67
+ ```
68
+ pip install git+https://github.com/UncertaintyInComplexSystems/bamojax#egg=bamojax
69
+ ```
70
+
71
+ Please note that if you want to use hardware acceleration, such as using GPUs, you might want to install `JAX` yourself. Please see https://docs.jax.dev/en/latest/installation.html for instructions. If no JAX installation is present, **bamojax** will install it as a dependency.
72
+
73
+
74
+ ## Quick tutorial
75
+
76
+ Let's estimate the latent probability $\theta=p(x_1=\text{heads} \mid \theta)$ that a coin lands up heads, given a set of observations $x_1, \ldots, x_n$:
77
+
78
+ #### Generate data
79
+ ```
80
+ import jax.numpy as jnp
81
+ import jax.random as jrnd
82
+ from bamojax.base import Model
83
+ from bamojax.inference import SMCInference
84
+ from bamojax.samplers import mcmc_sampler
85
+ import numpyro as npr
86
+ import numpyro.distributions as dist
87
+ import blackjax as bjx
88
+
89
+ key = jrnd.PRNGKey(0)
90
+ key, key_data, key_inference = jrnd.split(key, 3)
91
+
92
+ true_theta = 0.3
93
+ n = 10000
94
+ x = jrnd.bernoulli(key_data, p=true_theta, shape=(n, ))
95
+ ```
96
+
97
+ #### Define Bayesian generative model
98
+
99
+ Under the good, **bamojax** represents the a Bayesian model using a Directed Acyclic Graph (DAG) structure, and automatically derives model priors, likelihoods, and Gibbs inference schemes. These are then combined with the fast inference algorithms implemented in Blackjax. Bayesian models can be instantiated in **bamojax** using:
100
+
101
+ ```
102
+ from bamojax.base import Model
103
+ my_model = Model('The name of the model')
104
+ ```
105
+
106
+ Subsequently, variables can be added to this model using for example:
107
+
108
+ ```
109
+ def link_fn(probs):
110
+ return {'probs': probs}
111
+
112
+ #
113
+ latent_theta = my_model.add_node('theta',
114
+ distribution=dist.Beta(concentration1=1, concentration0=1))
115
+ observations = my_model.add_node('x',
116
+ distribution=dist.Bernoulli,
117
+ parents=dict(probs=latent_theta),
118
+ link_fn=link_fn,
119
+ observations=x)
120
+ ```
121
+
122
+ #### Perform approximate inference
123
+
124
+ The next step is to perform inference. Here, we use Adaptive-Tempered Sequential Monte Carlo, as implemented by Blackjax:
125
+
126
+ ```
127
+ num_particles = 1_000
128
+ num_mcmc_steps = 100
129
+ num_chains = 1
130
+
131
+ stepsize = 0.01
132
+
133
+ mcmc_params = dict(sigma=stepsize*jnp.eye(my_model.get_model_size()))
134
+ rmh = mcmc_sampler(my_model,
135
+ mcmc_kernel=bjx.normal_random_walk,
136
+ mcmc_parameters=mcmc_params)
137
+
138
+ key, key_inference = jrnd.split(key)
139
+
140
+ engine = SMCInference(model=my_model,
141
+ mcmc_kernel=rmh,
142
+ num_particles=num_particles,
143
+ num_mutations=num_mcmc_steps,
144
+ num_chains=num_chains)
145
+ result = engine.run(key_inference)
146
+
147
+
148
+ print(jnp.mean(result['final_state'].particles['theta']))
149
+
150
+ >>> 0.29956531188263924
151
+ ```
152
+
153
+ ## Modelling in bamojax
154
+
155
+ **bamojax** aims to make Bayesian modelling and inference with JAX and the Blackjax library more user-friendly, while retaining the speed and flexibility of the latter. To use the framework, a user is required to specify the directed acyclic graph that defines a Bayesian probabilistic model, as well as
156
+ * Distributions,
157
+ * Transformations,
158
+ * Link functions, and
159
+ * Observations.
160
+
161
+ ### The model
162
+
163
+ To perform these steps, one first initializes the `Model`:
164
+
165
+ ```
166
+ my_model = Model('The name of my model')
167
+ ```
168
+
169
+ This effectively creates an empty DAG.
170
+
171
+ ### The variables
172
+
173
+ Next, one adds nodes to the DAG one by one, or multiple at once in the case of iteration / plate notation: For instance, the following adds a latent random variable with a Gaussian distribution assigned to it, $x\sim \mathcal{N}(0,1)$:
174
+
175
+ ```
176
+ a_new_node = my_model.add_node('variable name', distribution=dist.Normal, parents=dict(loc=0.0, scale=1.0))
177
+ ```
178
+
179
+ Depending on whether a user provides values for the `distribution=`, and/or `observations=` arguments in the `add_node()` method, **bamojax** derives whether the variable is:
180
+ 1. Stochastic and latent,
181
+ 2. Stochastic and observed,
182
+ 3. Deterministic and observed.
183
+
184
+ The `parents` argument expects a dictionary, in which the keys must correspond to either the arguments of a Numpyro `dist.Distribution` object or the arguments of a link function.
185
+
186
+ Nodes can furthermore take any Numpyro bijector using the `bijector=` argument, for example to transform a real variable to a bounded domain or vice versa. In many cases, the same goal can be achieved by the link function, but sometimes a bijector is simpler to use.
187
+
188
+ ### Link functions
189
+
190
+ An important 'feature' of **bamojax** is that it is straightforward to add any deterministic transformation from the values of a parent variable to the inputs of a child variable. For example, imagine a variable $\theta$ representing a coin flip probability. A typical prior would be the beta distribution, typically parametrized with pseudo-counts $\alpha$ and $\beta$, so that $\theta \sim \text{Beta}(\alpha,\beta)$. However, we may want to specify a hierarchical prior on the _mode_ and _precision_ of this distribution, rather than on the pseudo-counts. With a link function we can express this:
191
+
192
+ ```
193
+ def beta_link_fn(mode, conc):
194
+ a = mode*(conc-2) + 1
195
+ b = (1 - mode)*(conc-2) + 1
196
+ return {'concentration1': a, 'concentration0': b}
197
+
198
+ omega = my_model.add_node('omega', distribution=dist.Beta, parents=dict(concentration1=1.0, concentration0=1.0))
199
+ theta = my_model.add_node('theta', distribution=dist.Beta, parents=dict(mode=omega, conc=15), link_fn=beta_link_fn)
200
+ ```
201
+
202
+ The link function `beta_link_fn` takes the mode (given by another node `omega`) and the concentration (given by a scalar, which is implicitly converted to a deterministic and observed node), and returns the standard arguments $\alpha$ and $\beta$ which the `dist.Beta` distribution object recognizes as valid parameters.
203
+
204
+ Because link functions are written in plain Python, they can be of arbitrary complexity. For example, in [bamojax/examples/bnn/bnn_mpl.ipynb](https://github.com/UncertaintyInComplexSystems/bamojax/blob/main/bamojax/examples/bnn/bnn_mpl.ipynb), we use [Flax Linen](https://flax.readthedocs.io/en/latest/) to set up a multilayer perceptron as a link function.
205
+
206
+ ### Inference
207
+
208
+ **bamojax** does not have its own inference engine, but provides an interface to [Blackjax](https://blackjax-devs.github.io/blackjax/), in addition to some quality-of-life features. Ultimately, control is left entirely to the user. Here is an example where we use Gibbs MCMC:
209
+
210
+ ```
211
+ step_fns = dict(beta=normal_random_walk, sigma=normal_random_walk)
212
+ step_fn_params = dict(omega=dict(sigma=0.05), theta=dict(sigma=0.5))
213
+ gibbs_kernel = gibbs_sampler(my_model, step_fns=step_fns, step_fn_params=step_fn_params)
214
+ ```
215
+
216
+ In Gibbs sampling, we specify a kernel for each variable. In this case, we use Gaussian proposal distributions for both $\omega$ and $\theta$.
217
+
218
+ Now we can set up our inference engine:
219
+
220
+ ```
221
+ engine = MCMCInference(model=my_model, num_chains=4, num_samples=100_000, num_burn=100_000, num_thin=50, mcmc_kernel=gibbs_kernel, return_diagnostics=True)
222
+ result = engine.run(jrnd.PRNGKey(0))
223
+ ```
224
+
225
+ This returns a dictionary with a `states` value containing a dictionary samples for each variable, for the requested number of chains and samples, discarding the specified number of burn-in samples, and storing only every 50th sample. By setting `return_diagnostics=True`, information such as acceptance rates are provided as well. For large models, turning this off can conserve memory consumption.
226
+
227
+ Alternative inference engines include Sequential Monte Carlo, Variational Inference, and Stochastic-Gradient MCMC methods. Examples on how to use these can be found in the `examples/` folder, as well as on the [Uncertainty in Complex Systems website](https://mhinne.github.io/uncertainty-in-complex-systems).
228
+
229
+ ### Predictions
230
+
231
+ **bamojax** supports each combination of sampling from the prior or posterior, and the latent variables or the predictive distribution, using any of the following:
232
+
233
+ | |Prior |Posterior |
234
+ |---------------------|----------------------------------------------|----------------------------------------------------------------------------------|
235
+ |**Latent variables** |`my_model.sample_prior(key)` |Using `InferenceEngine` |
236
+ |**Predictive** |`my_model.sample_prior_predictive(key)` |`my_model.sample_posterior_predictive(key, posterior_samples, input_variables)` |
237
+
238
+ When sampling from the posterior predictive, the parameter `input_variables=` can be used to provide for example predictor values, such as when sampling from a regression model or a neural network; $p(y^* \mid X, Y, x^*)$.
239
+
240
+ ## API
241
+
242
+ The full **bamojax** API can be found here: https://uncertaintyincomplexsystems.github.io/bamojax/.
243
+
244
+ ## Citing bamojax
245
+
246
+ If you use Bamojax in your work, please cite the following:
247
+
248
+ Max Hinne. (2025). Bamojax: Bayesian Modelling with JAX (Version 0.3.11). Journal of Open Source Software, 114. https://doi.org/10.21105/joss.08642.
249
+
250
+ BibTeX:
251
+
252
+ ```
253
+ @article{hinne2025bamojax,
254
+ author = {Hinne, Max},
255
+ title = {Bamojax: Bayesian Modelling with JAX},
256
+ journal = {Journal of Open Source Software},
257
+ volume = 114
258
+ year = 2025,
259
+ publisher = {Open Journals},
260
+ version = {0.3.11},
261
+ doi = {10.21105/joss.08642},
262
+ url = {https://joss.theoj.org/papers/10.21105/joss.08642}
263
+ }
264
+ ```
@@ -0,0 +1,229 @@
1
+ [![Tests](https://github.com/uncertaintyincomplexsystems/bamojax/actions/workflows/test.yml/badge.svg)](https://github.com/uncertaintyincomplexsystems/bamojax/actions/workflows/test.yml) [![DOI](https://joss.theoj.org/papers/10.21105/joss.08642/status.svg)](https://doi.org/10.21105/joss.08642)
2
+
3
+ ![bamojax logo](https://github.com/uncertaintyincomplexsystems/bamojax/blob/main/figures/bamojax_logo_2x.png "bamojax")
4
+
5
+ # Welcome to bamojax!
6
+
7
+ Welcome to **bamojax**, the Bayesian modelling and model comparison toolbox implemented using the JAX coding universe. **bamojax** is a probabilistic programming language (PPL), similar to Numpyro, PyMC, Stan, JAGS, and BUGS, as well as a set of analyses for model comparison and averaging. **bamojax** relies on [Blackjax](https://blackjax-devs.github.io/blackjax/) for approximate inference, and on [Numpyro](https://num.pyro.ai/en/stable/) for probability distributions and their essential operations.
8
+
9
+ ## What sets bamojax apart?
10
+
11
+ Existing PPLs, such as PyMC, can export their log density function so it can be sampled using Blackjax. However, this has two downsides:
12
+
13
+ 1. Not all existing probabilistic programming easily allow you to update variables in your model using Gibbs Markov chain Monte Carlo (MCMC) algorithms. For example, if one wants to approximate the posterior over a latent Gaussian process (GP) and its hyperparameters, it can be more efficient to use elliptical slice sampling for the GP, than to apply the No-U-Turn Hamiltonian Monte Carlo Sampler [(Hoffman and Gelman, 2014)](https://jmlr.org/papers/v15/hoffman14a.html) to all variables at once. This effect is even more pronounced when embedding MCMC sampling within Sequential Monte Carlo (see [Hinne, 2025](https://link.springer.com/article/10.3758/s13428-025-02642-1) for more details).
14
+ 2. It is harder to embed this logdensity into tempered Sequential Monte Carlo algorithms, as for this one needs access to the prior and likelihood separately.
15
+
16
+ By implementing your own models and samplers using Blackjax, these problems can be circumvented. However, this is a labour-intensive and error-prone process. Therefore, **bamojax** provides a user-friendly interface around Blackjax, that allows for easy model construction and Gibbs sampling.
17
+
18
+ Once a Bayesian model has been determined, the next goal is often to compare it with competing hypotheses. This can, for example, be done by computing the marginal likelihoods of different models, and taking their ratio, a quantity known as the _Bayes factor_. However, computing marginal likelihoods and therefore Bayes factors is computationally challenging, and several approximation strategies exist. **bamojax** makes this process easier by implementing the following marginal likelihood approximations:
19
+
20
+ - Importance sampling
21
+ - Sequential Monte Carlo
22
+ - The Laplace approximation
23
+ - Bridge sampling (Gronau et al., 2017; Meng & Wong, 1996)
24
+ - The THAMES estimator (Metodiev et al., 2025)
25
+
26
+ More comparison features are being added to the toolbox continuously.
27
+
28
+ ## Installation
29
+
30
+ Install **bamojax** using:
31
+
32
+ ```
33
+ pip install git+https://github.com/UncertaintyInComplexSystems/bamojax#egg=bamojax
34
+ ```
35
+
36
+ Please note that if you want to use hardware acceleration, such as using GPUs, you might want to install `JAX` yourself. Please see https://docs.jax.dev/en/latest/installation.html for instructions. If no JAX installation is present, **bamojax** will install it as a dependency.
37
+
38
+
39
+ ## Quick tutorial
40
+
41
+ Let's estimate the latent probability $\theta=p(x_1=\text{heads} \mid \theta)$ that a coin lands up heads, given a set of observations $x_1, \ldots, x_n$:
42
+
43
+ #### Generate data
44
+ ```
45
+ import jax.numpy as jnp
46
+ import jax.random as jrnd
47
+ from bamojax.base import Model
48
+ from bamojax.inference import SMCInference
49
+ from bamojax.samplers import mcmc_sampler
50
+ import numpyro as npr
51
+ import numpyro.distributions as dist
52
+ import blackjax as bjx
53
+
54
+ key = jrnd.PRNGKey(0)
55
+ key, key_data, key_inference = jrnd.split(key, 3)
56
+
57
+ true_theta = 0.3
58
+ n = 10000
59
+ x = jrnd.bernoulli(key_data, p=true_theta, shape=(n, ))
60
+ ```
61
+
62
+ #### Define Bayesian generative model
63
+
64
+ Under the good, **bamojax** represents the a Bayesian model using a Directed Acyclic Graph (DAG) structure, and automatically derives model priors, likelihoods, and Gibbs inference schemes. These are then combined with the fast inference algorithms implemented in Blackjax. Bayesian models can be instantiated in **bamojax** using:
65
+
66
+ ```
67
+ from bamojax.base import Model
68
+ my_model = Model('The name of the model')
69
+ ```
70
+
71
+ Subsequently, variables can be added to this model using for example:
72
+
73
+ ```
74
+ def link_fn(probs):
75
+ return {'probs': probs}
76
+
77
+ #
78
+ latent_theta = my_model.add_node('theta',
79
+ distribution=dist.Beta(concentration1=1, concentration0=1))
80
+ observations = my_model.add_node('x',
81
+ distribution=dist.Bernoulli,
82
+ parents=dict(probs=latent_theta),
83
+ link_fn=link_fn,
84
+ observations=x)
85
+ ```
86
+
87
+ #### Perform approximate inference
88
+
89
+ The next step is to perform inference. Here, we use Adaptive-Tempered Sequential Monte Carlo, as implemented by Blackjax:
90
+
91
+ ```
92
+ num_particles = 1_000
93
+ num_mcmc_steps = 100
94
+ num_chains = 1
95
+
96
+ stepsize = 0.01
97
+
98
+ mcmc_params = dict(sigma=stepsize*jnp.eye(my_model.get_model_size()))
99
+ rmh = mcmc_sampler(my_model,
100
+ mcmc_kernel=bjx.normal_random_walk,
101
+ mcmc_parameters=mcmc_params)
102
+
103
+ key, key_inference = jrnd.split(key)
104
+
105
+ engine = SMCInference(model=my_model,
106
+ mcmc_kernel=rmh,
107
+ num_particles=num_particles,
108
+ num_mutations=num_mcmc_steps,
109
+ num_chains=num_chains)
110
+ result = engine.run(key_inference)
111
+
112
+
113
+ print(jnp.mean(result['final_state'].particles['theta']))
114
+
115
+ >>> 0.29956531188263924
116
+ ```
117
+
118
+ ## Modelling in bamojax
119
+
120
+ **bamojax** aims to make Bayesian modelling and inference with JAX and the Blackjax library more user-friendly, while retaining the speed and flexibility of the latter. To use the framework, a user is required to specify the directed acyclic graph that defines a Bayesian probabilistic model, as well as
121
+ * Distributions,
122
+ * Transformations,
123
+ * Link functions, and
124
+ * Observations.
125
+
126
+ ### The model
127
+
128
+ To perform these steps, one first initializes the `Model`:
129
+
130
+ ```
131
+ my_model = Model('The name of my model')
132
+ ```
133
+
134
+ This effectively creates an empty DAG.
135
+
136
+ ### The variables
137
+
138
+ Next, one adds nodes to the DAG one by one, or multiple at once in the case of iteration / plate notation: For instance, the following adds a latent random variable with a Gaussian distribution assigned to it, $x\sim \mathcal{N}(0,1)$:
139
+
140
+ ```
141
+ a_new_node = my_model.add_node('variable name', distribution=dist.Normal, parents=dict(loc=0.0, scale=1.0))
142
+ ```
143
+
144
+ Depending on whether a user provides values for the `distribution=`, and/or `observations=` arguments in the `add_node()` method, **bamojax** derives whether the variable is:
145
+ 1. Stochastic and latent,
146
+ 2. Stochastic and observed,
147
+ 3. Deterministic and observed.
148
+
149
+ The `parents` argument expects a dictionary, in which the keys must correspond to either the arguments of a Numpyro `dist.Distribution` object or the arguments of a link function.
150
+
151
+ Nodes can furthermore take any Numpyro bijector using the `bijector=` argument, for example to transform a real variable to a bounded domain or vice versa. In many cases, the same goal can be achieved by the link function, but sometimes a bijector is simpler to use.
152
+
153
+ ### Link functions
154
+
155
+ An important 'feature' of **bamojax** is that it is straightforward to add any deterministic transformation from the values of a parent variable to the inputs of a child variable. For example, imagine a variable $\theta$ representing a coin flip probability. A typical prior would be the beta distribution, typically parametrized with pseudo-counts $\alpha$ and $\beta$, so that $\theta \sim \text{Beta}(\alpha,\beta)$. However, we may want to specify a hierarchical prior on the _mode_ and _precision_ of this distribution, rather than on the pseudo-counts. With a link function we can express this:
156
+
157
+ ```
158
+ def beta_link_fn(mode, conc):
159
+ a = mode*(conc-2) + 1
160
+ b = (1 - mode)*(conc-2) + 1
161
+ return {'concentration1': a, 'concentration0': b}
162
+
163
+ omega = my_model.add_node('omega', distribution=dist.Beta, parents=dict(concentration1=1.0, concentration0=1.0))
164
+ theta = my_model.add_node('theta', distribution=dist.Beta, parents=dict(mode=omega, conc=15), link_fn=beta_link_fn)
165
+ ```
166
+
167
+ The link function `beta_link_fn` takes the mode (given by another node `omega`) and the concentration (given by a scalar, which is implicitly converted to a deterministic and observed node), and returns the standard arguments $\alpha$ and $\beta$ which the `dist.Beta` distribution object recognizes as valid parameters.
168
+
169
+ Because link functions are written in plain Python, they can be of arbitrary complexity. For example, in [bamojax/examples/bnn/bnn_mpl.ipynb](https://github.com/UncertaintyInComplexSystems/bamojax/blob/main/bamojax/examples/bnn/bnn_mpl.ipynb), we use [Flax Linen](https://flax.readthedocs.io/en/latest/) to set up a multilayer perceptron as a link function.
170
+
171
+ ### Inference
172
+
173
+ **bamojax** does not have its own inference engine, but provides an interface to [Blackjax](https://blackjax-devs.github.io/blackjax/), in addition to some quality-of-life features. Ultimately, control is left entirely to the user. Here is an example where we use Gibbs MCMC:
174
+
175
+ ```
176
+ step_fns = dict(beta=normal_random_walk, sigma=normal_random_walk)
177
+ step_fn_params = dict(omega=dict(sigma=0.05), theta=dict(sigma=0.5))
178
+ gibbs_kernel = gibbs_sampler(my_model, step_fns=step_fns, step_fn_params=step_fn_params)
179
+ ```
180
+
181
+ In Gibbs sampling, we specify a kernel for each variable. In this case, we use Gaussian proposal distributions for both $\omega$ and $\theta$.
182
+
183
+ Now we can set up our inference engine:
184
+
185
+ ```
186
+ engine = MCMCInference(model=my_model, num_chains=4, num_samples=100_000, num_burn=100_000, num_thin=50, mcmc_kernel=gibbs_kernel, return_diagnostics=True)
187
+ result = engine.run(jrnd.PRNGKey(0))
188
+ ```
189
+
190
+ This returns a dictionary with a `states` value containing a dictionary samples for each variable, for the requested number of chains and samples, discarding the specified number of burn-in samples, and storing only every 50th sample. By setting `return_diagnostics=True`, information such as acceptance rates are provided as well. For large models, turning this off can conserve memory consumption.
191
+
192
+ Alternative inference engines include Sequential Monte Carlo, Variational Inference, and Stochastic-Gradient MCMC methods. Examples on how to use these can be found in the `examples/` folder, as well as on the [Uncertainty in Complex Systems website](https://mhinne.github.io/uncertainty-in-complex-systems).
193
+
194
+ ### Predictions
195
+
196
+ **bamojax** supports each combination of sampling from the prior or posterior, and the latent variables or the predictive distribution, using any of the following:
197
+
198
+ | |Prior |Posterior |
199
+ |---------------------|----------------------------------------------|----------------------------------------------------------------------------------|
200
+ |**Latent variables** |`my_model.sample_prior(key)` |Using `InferenceEngine` |
201
+ |**Predictive** |`my_model.sample_prior_predictive(key)` |`my_model.sample_posterior_predictive(key, posterior_samples, input_variables)` |
202
+
203
+ When sampling from the posterior predictive, the parameter `input_variables=` can be used to provide for example predictor values, such as when sampling from a regression model or a neural network; $p(y^* \mid X, Y, x^*)$.
204
+
205
+ ## API
206
+
207
+ The full **bamojax** API can be found here: https://uncertaintyincomplexsystems.github.io/bamojax/.
208
+
209
+ ## Citing bamojax
210
+
211
+ If you use Bamojax in your work, please cite the following:
212
+
213
+ Max Hinne. (2025). Bamojax: Bayesian Modelling with JAX (Version 0.3.11). Journal of Open Source Software, 114. https://doi.org/10.21105/joss.08642.
214
+
215
+ BibTeX:
216
+
217
+ ```
218
+ @article{hinne2025bamojax,
219
+ author = {Hinne, Max},
220
+ title = {Bamojax: Bayesian Modelling with JAX},
221
+ journal = {Journal of Open Source Software},
222
+ volume = 114
223
+ year = 2025,
224
+ publisher = {Open Journals},
225
+ version = {0.3.11},
226
+ doi = {10.21105/joss.08642},
227
+ url = {https://joss.theoj.org/papers/10.21105/joss.08642}
228
+ }
229
+ ```
@@ -0,0 +1,2 @@
1
+ from . import _version
2
+ __version__ = _version.get_versions()['version']
@@ -0,0 +1,21 @@
1
+
2
+ # This file was generated by 'versioneer.py' (0.29) from
3
+ # revision-control system data, or from the parent directory name of an
4
+ # unpacked source archive. Distribution tarballs contain a pre-generated copy
5
+ # of this file.
6
+
7
+ import json
8
+
9
+ version_json = '''
10
+ {
11
+ "date": "2026-10-26T21:35:56+0100",
12
+ "dirty": false,
13
+ "error": null,
14
+ "full-revisionid": "7b6576685f711fc94c568bb32813d45e67e2a861",
15
+ "version": "0.4.2"
16
+ }
17
+ ''' # END VERSION_JSON
18
+
19
+
20
+ def get_versions():
21
+ return json.loads(version_json)