Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ jobs:
command: upload
args: --non-interactive --skip-existing wheels-*/*
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
files: |
wasm-wheels/*.whl
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.4
rev: v0.15.10
hooks:
- id: ruff
args: ["--fix", "--output-format=full"]
Expand Down
47 changes: 39 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

All notable changes to this project will be documented in this file.

## [0.16.8] - 2026-03-11

### Documentation

- Fix docstring formatting (Eliot Carlson)


### Miscellaneous Tasks

- Bump quinn-proto in the cargo group across 1 directory (dependabot[bot])


## [0.16.7] - 2026-03-03

### Bug Fixes

- Limit to arviz < 1.0 (Adrian Seyboldt)


### Miscellaneous Tasks

- Update dependencies (Adrian Seyboldt)

- Update github ci actions (Adrian Seyboldt)


### Styling

- Update pre-commit ruff version (Adrian Seyboldt)


## [0.16.6] - 2026-02-18

### Miscellaneous Tasks
Expand Down Expand Up @@ -549,8 +580,6 @@ All notable changes to this project will be documented in this file.

### Ci

- Fix uploads of releases (Adrian Seyboldt)

- Fix architectures in CI (Adrian Seyboldt)


Expand All @@ -574,6 +603,8 @@ All notable changes to this project will be documented in this file.

### Miscellaneous Tasks

- Bump actions/download-artifact from 3 to 4 (dependabot[bot])

- Bump actions/setup-python from 4 to 5 (dependabot[bot])

- Bump uraimo/run-on-arch-action from 2.5.0 to 2.7.1 (dependabot[bot])
Expand All @@ -598,7 +629,12 @@ All notable changes to this project will be documented in this file.
- Set the number of parallel chains dynamically (Adrian Seyboldt)


## [0.9.2] - 2024-02-19
### Ci

- Fix uploads of releases (Adrian Seyboldt)


## [0.9.2] - 2023-10-24

### Bug Fixes

Expand All @@ -617,11 +653,6 @@ All notable changes to this project will be documented in this file.
- Handle missing libraries more robustly (#72) (Ben Mares)


### Miscellaneous Tasks

- Bump actions/download-artifact from 3 to 4 (dependabot[bot])


### Ci

- Make sure the local nutpie is installed (Adrian Seyboldt)
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nutpie"
version = "0.16.7"
version = "0.16.8"
authors = [
"Adrian Seyboldt <adrian.seyboldt@gmail.com>",
"PyMC Developers <pymc.devs@gmail.com>",
Expand Down
63 changes: 63 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Releasing nutpie

## Version source of truth

The Python package version is dynamic (`pyproject.toml` declares `dynamic = ["version"]`),
so **`Cargo.toml` is the single source of truth** for the version number. Maturin reads it
at build time and applies it to the Python wheel.

## Prerequisites

- Write access to `pymc-devs/nutpie` (needed to create a GitHub Release)
- [git-cliff](https://git-cliff.org) installed (configuration lives in `cliff.toml`)

## Release checklist

1. **Bump the version** in `Cargo.toml`.

2. **Update the changelog.**

```bash
git cliff --tag vX.Y.Z -o CHANGELOG.md
```

3. **Open a pull request.**

The release prep (version bump + changelog) can be its own PR or included in a PR
that also contains the fixes/features going into the release.

```bash
git checkout -b release-X.Y.Z
git add Cargo.toml CHANGELOG.md
git commit -m "chore(release): prepare for X.Y.Z"
git push -u origin release-X.Y.Z
gh pr create --title "Prepare vX.Y.Z"
```

If `Cargo.lock` changed as a side effect of the version bump, include it in the
same commit.

4. **Merge the PR, then create a GitHub Release** targeting `main` with tag `vX.Y.Z`.

Use the GitHub UI (**Releases → Draft a new release**) or the CLI:

```bash
gh release create vX.Y.Z --target main --generate-notes
```

Create the release promptly after merging so the tag lands on the merge commit
rather than on something unrelated that arrives later.

5. **Approve the deployment.** Once CI finishes building wheels and the sdist, the
`release` job will be pending review. A `pypi` environment reviewer must approve
the deployment from the Actions tab. Once approved, wheels and the sdist are
published to PyPI.

## Conventions

- **Commit messages** follow [Conventional Commits](https://www.conventionalcommits.org).
`git-cliff` groups the changelog based on prefixes like `feat:`, `fix:`, `chore:`, etc.
- Release-prep commits use `chore(release):` so they are skipped in the generated
changelog (see `cliff.toml`).
- **Pre-release versions** (e.g. `0.17.0-alpha.1`) are supported; the CI will mark the
GitHub Release accordingly.
55 changes: 34 additions & 21 deletions docs/sampling-options.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -94,52 +94,65 @@ parameter space, where the model is most likely to diverge.
Nutpie offers several strategies for adapting the mass matrix, which determines
how the sampler navigates the parameter space.

### Standard Adaptation
The adaptation strategy is controlled by the `adaptation` argument, which
accepts one of four values:

By setting `use_grad_based_mass_matrix=False`, the sampling algorithm will more
closely resemble the algorithm in Stan and PyMC. Usually, this will result in
less efficient sampling, but the total number of effective samples is sometimes
higher. If this is set to `True` (the default), nutpie will use diagonal mass
matrix estimates that are based on the posterior draws and the scores at those
positions.
### `"diag"` (default)

The default strategy. Nutpie estimates a diagonal mass matrix from both draw
variance and gradient variance. This is usually the most efficient choice.

```python
trace = nutpie.sample(model)
# equivalent to:
trace = nutpie.sample(model, adaptation="diag")
```

### `"draw_diag"`

A diagonal mass matrix estimated from draw variance only, similar to the
adaptation used in Stan and PyMC. This will usually result in less efficient
sampling, but the total number of effective samples is occasionally higher.

```python
trace = nutpie.sample(
model,
use_grad_based_mass_matrix=False
adaptation="draw_diag"
)
```

### Low-Rank Updates
### `"low_rank"`

For models with strong parameter correlations you can enable a low rank modified
mass matrix. The `mass_matrix_gamma` parameter is a regularization parameter.
More regularization will lead to a smaller effect of the low-rank components,
but might work better for higher dimensional problems.
For models with strong parameter correlations you can enable a low-rank
modified mass matrix. This extends the diagonal mass matrix with a low-rank
update that can capture some posterior correlations. The `mass_matrix_gamma`
parameter is a regularization parameter — more regularization will lead to a
smaller effect of the low-rank components, but might work better for
higher-dimensional problems.

`mass_matrix_eigval_cutoff` should be greater than one, and controls how large
an eigenvalue of the full mass matrix has to be, to be included into the
low-rank mass matirx.
an eigenvalue of the full mass matrix has to be to be included in the low-rank
mass matrix.

```python
trace = nutpie.sample(
model,
low_rank_modified_mass_matrix=True,
adaptation="low_rank",
mass_matrix_eigval_cutoff=3,
mass_matrix_gamma=1e-5
)
```

### Experimental Features
### `"flow"` (Experimental)

`trasform_adapt` is an experimental feature that allows sampling from many
posteriors, where current methods diverge. It is described in more detail
[here](nf-adapt.qmd).
A normalizing-flow reparameterisation that is adapted during tuning. This
allows sampling from many posteriors where current methods diverge. It is
described in more detail [here](nf-adapt.qmd).

```python
trace = nutpie.sample(
model,
transform_adapt=True # Experimental reparameterization
adaptation="flow"
)
```

Expand Down
5 changes: 4 additions & 1 deletion python/nutpie/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from nutpie import _lib
from nutpie._lib import store as zarr_store
from nutpie.compile_pymc import compile_pymc_model
from nutpie.compile_stan import compile_stan_model
from nutpie.sample import sample
from nutpie._lib import store as zarr_store

ChainProgress = _lib.PyChainProgress

__version__: str = _lib.__version__
__all__ = [
"__version__",
"ChainProgress",
"compile_pymc_model",
"compile_stan_model",
"sample",
Expand Down
24 changes: 21 additions & 3 deletions python/nutpie/compile_pymc.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ def with_data(self, **updates):
raise ValueError(
f"Shared variable {name} must have rank {old_val.ndim}"
)
if old_val.shape != new_val.shape:
raise ValueError(
f"Shared variable '{name}' has shape {old_val.shape}, "
f"but the new value has shape {new_val.shape}. "
f"Changing the shape of shared data requires recompiling "
f"the model with compile_pymc_model()."
)
shared_data[name] = new_val
user_data = update_user_data(user_data, shared_data)

Expand All @@ -147,13 +154,24 @@ def with_data(self, **updates):
user_data=user_data,
)

def _make_sampler(self, settings, init_mean, cores, progress_type, store):
def _make_sampler(
self,
settings,
init_mean,
cores,
progress_type,
extra_callback,
extra_callback_rate,
store,
):
model = self._make_model(init_mean)
return _lib.PySampler.from_pymc(
settings,
cores,
model,
progress_type,
extra_callback,
extra_callback_rate,
store,
)

Expand Down Expand Up @@ -531,8 +549,8 @@ def compile_pymc_model(
if backend is not None:
backend = backend.lower() # type: ignore[assignment]

from pymc.model.transform.optimization import freeze_dims_and_data
from pymc.initial_point import make_initial_point_fn
from pymc.model.transform.optimization import freeze_dims_and_data

if freeze_model is None:
freeze_model = backend == "jax"
Expand Down Expand Up @@ -587,8 +605,8 @@ def wrapper(*args, **kwargs):

def _compute_shapes(model) -> dict[str, tuple[int, ...]]:
import pytensor
from pytensor.tensor import as_tensor
from pymc.initial_point import make_initial_point_fn
from pytensor.tensor import as_tensor

point = make_initial_point_fn(model=model, return_transformed=True)(0)

Expand Down
13 changes: 12 additions & 1 deletion python/nutpie/compile_stan.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,24 @@ def _make_model(self, init_mean):
return self.with_data().model
return self.model

def _make_sampler(self, settings, init_mean, cores, progress_type, store):
def _make_sampler(
self,
settings,
init_mean,
cores,
progress_type,
extra_callback,
extra_callback_rate,
store,
):
model = self._make_model(init_mean)
return _lib.PySampler.from_stan(
settings,
cores,
model,
progress_type,
extra_callback,
extra_callback_rate,
store,
)

Expand Down
Loading
Loading