import marimo as mocloudposterior: Monitoring
Monitor MCMC sampling remotely with a live dashboard or push notifications.
dashboard=True(default for remote) – live web dashboard with convergence diagnostics, trace plots, and a stop button. Open on your phone or any browser via QR code.notify=True– push notifications via ntfy when sampling starts and completes.
Run this notebook locally (Jupyter or marimo) to see the dashboard links and QR codes, plus the in-cell live progress.
import numpy as np
import pandas as pd
import pymc as pm
import arviz as azStart fresh
Clear any cached results and this project’s Modal volume so the notebook runs cold and reproducibly. (Shown in marimo; hidden in the rendered notebook.)
import shutil
from pathlib import Path
import cloudposterior as cp
# Wipe the local result cache + this project's Modal volume so the example
# starts cold. The sampling cells below use `cp`, so marimo runs this first.
shutil.rmtree(Path(".cloudposterior"), ignore_errors=True)
cp.cleanup_volumes()/Users/spencerboucher/Projects/cloudposterior-map-dashboard/cloudposterior/backends/modal_backend.py:790: AsyncUsageWarning: A blocking Modal interface is being used in an async context.
This may cause performance issues or bugs. Consider rewriting to use Modal's async interfaces:
https://modal.com/docs/guide/async
Suggested rewrite:
await modal.Volume.objects.delete.aio(volume_name)
Original line:
modal.Volume.objects.delete(volume_name)
df = pd.read_csv(pm.get_data("radon.csv"))
with pm.Model(name="radon_intercepts", coords={"county": df.county.unique()}) as radon:
mu_a = pm.Normal("mu_a", mu=0, sigma=5)
sigma_a = pm.HalfNormal("sigma_a", sigma=2)
a_raw = pm.Normal("a_raw", mu=0, sigma=1, dims="county")
a = pm.Deterministic("a", mu_a + sigma_a * a_raw, dims="county")
b_floor = pm.Normal("b_floor", mu=0, sigma=5)
mu = a[df.county_code.values] + b_floor * df.floor.values
sigma_y = pm.HalfNormal("sigma_y", sigma=2)
pm.Normal("obs", mu=mu, sigma=sigma_y, observed=df.log_radon.values)Live dashboard
The dashboard is on by default for remote runs. Scan the QR code or open the URL to see:
- Per-chain progress bars with speed, divergences, and ETA
- Live convergence diagnostics (R-hat, ESS) with color-coded status
- Live trace plots and posterior KDE per parameter
- A stop button to end sampling early if convergence looks good
with cp.cloud(radon, remote=True, cache=False):
idata = pm.sample(draws=10000, tune=1000, chains=4)az.summary(idata, filter_vars="like", var_names=["mu_a", "sigma_a", "b_floor", "sigma_y"])| mean | sd | hdi_3% | hdi_97% | mcse_mean | mcse_sd | ess_bulk | ess_tail | r_hat | |
|---|---|---|---|---|---|---|---|---|---|
| radon_intercepts::mu_a | 1.492 | 0.051 | 1.398 | 1.589 | 0.001 | 0.0 | 9767.0 | 15649.0 | 1.0 |
| radon_intercepts::b_floor | -0.663 | 0.068 | -0.788 | -0.532 | 0.000 | 0.0 | 35031.0 | 29989.0 | 1.0 |
| radon_intercepts::sigma_a | 0.321 | 0.045 | 0.240 | 0.408 | 0.000 | 0.0 | 9285.0 | 14580.0 | 1.0 |
| radon_intercepts::sigma_y | 0.727 | 0.018 | 0.694 | 0.760 | 0.000 | 0.0 | 40116.0 | 27917.0 | 1.0 |
az.plot_trace(idata, filter_vars="like", var_names=["mu_a", "sigma_a", "b_floor", "sigma_y"])[0, 0].figurePush notifications
Pass notify=True to get push notifications when sampling starts and completes. Works for both local and remote runs. Scan the QR code with the ntfy app to subscribe.
For private notifications, point to your own ntfy server with notify={"server": "https://ntfy.example.com"}.
with cp.cloud(radon, cache=False, notify=True):
idata_1 = pm.sample(draws=2000, tune=1000, chains=4)Sampler Progress
Total Chains: 4
Active Chains: 0
Finished Chains: 4
Sampling for now
Estimated Time to Completion: now
| Progress | Draws | Divergences | Step Size | Gradients/Draw |
|---|---|---|---|---|
| 3000 | 0 | 0.52 | 7 | |
| 3000 | 0 | 0.48 | 7 | |
| 3000 | 0 | 0.45 | 7 | |
| 3000 | 0 | 0.48 | 7 |
Both
Use both together to get the live dashboard AND push notifications.
with cp.cloud(radon, remote=True, cache=False, notify=True):
idata_2 = pm.sample(draws=1000, tune=1000, chains=4)az.summary(idata_2, filter_vars='like', var_names=['mu_a', 'sigma_a', 'b_floor', 'sigma_y'])| mean | sd | hdi_3% | hdi_97% | mcse_mean | mcse_sd | ess_bulk | ess_tail | r_hat | |
|---|---|---|---|---|---|---|---|---|---|
| radon_intercepts::mu_a | 1.491 | 0.052 | 1.393 | 1.586 | 0.002 | 0.001 | 852.0 | 1900.0 | 1.0 |
| radon_intercepts::b_floor | -0.663 | 0.068 | -0.794 | -0.541 | 0.001 | 0.001 | 3537.0 | 3034.0 | 1.0 |
| radon_intercepts::sigma_a | 0.322 | 0.045 | 0.238 | 0.405 | 0.002 | 0.001 | 786.0 | 1174.0 | 1.0 |
| radon_intercepts::sigma_y | 0.727 | 0.018 | 0.695 | 0.762 | 0.000 | 0.000 | 3523.0 | 2458.0 | 1.0 |