[1]:
%load_ext autoreload
%autoreload 2

Waldo Posterior P-value Construction

INTRO & SETTINGS

The goal of this tutorial is to show how to construct confidence sets for a generative-model-based test statistic, using both Waldo (posterior-based) and Posterior, calibrated via p-values (monotonic probabilistic classification), on a two-component Gaussian-mixture location model (as used in the MLST paper).

The likelihood is a two-component Gaussian mixture:

\[X \mid \theta \sim w_0 \cdot \mathcal{N}(\theta, \sigma_0^2 I) + w_1 \cdot \mathcal{N}(\theta, \sigma_1^2 I)\]

where the mixture weights and component scales are fixed. The parameter of interest is the location \(\theta\). As in the other tutorials, we leverage a posterior estimator (SNPE from the sbi library) as the main underlying inferential model — both Waldo (with estimation_method='posterior') and Posterior build on it, just using it differently to form the test statistic.

[2]:
# SETTINGS

POI_DIM = 1
DATA_DIM = 1
BATCH_SIZE = 1  # assume we get to see only one observed sample for each "true" parameter
POI_SPACE_BOUNDS = {'low': -2.0, 'high': 2.0}
POI_GRID_SIZE = 1_000

CONFIDENCE_LEVEL = 0.90

B = 20_000       # simulations to train the posterior estimator
B_PRIME = 10_000  # simulations to train the p-values calibration model
NUM_POSTERIOR_SAMPLES = 20_000

SIMULATE

[3]:
import torch

from lf2i.simulator.gmm import GaussianMixtureLocation

gmm = GaussianMixtureLocation(
    poi_space_bounds=POI_SPACE_BOUNDS,
    poi_grid_size=POI_GRID_SIZE,
    poi_dim=POI_DIM,
    data_dim=DATA_DIM,
    batch_size=BATCH_SIZE,
)

Observations

[4]:
x_obs = gmm(param=torch.tensor([[0.75]]), batch_size=1).reshape(1, DATA_DIM)
x_obs
[4]:
tensor([[0.8363]])

CONFIDENCE SET via Posterior + p-values

Posterior uses the (log-)posterior density itself as the test statistic. Calibrating it via calibration_method='p-values' fits a monotonic probabilistic classifier that estimates the rejection probability \(P(T \le \tau \mid \theta)\) directly, which doubles as a p-value function usable for confidence sets.

[5]:
from lf2i.inference import LF2I
from lf2i.test_statistics import Posterior
from sbi.inference import SNPE

posterior_ts = Posterior(poi_dim=POI_DIM, estimator=SNPE())
lf2i_posterior = LF2I(test_statistic=posterior_ts)

posterior_region = lf2i_posterior.inference(
    x=x_obs,
    evaluation_grid=gmm.poi_grid.reshape(-1, 1),
    confidence_level=CONFIDENCE_LEVEL,
    calibration_method='p-values',
    calibration_model='nn',
    simulator=gmm,
    b=B,
    b_prime=B_PRIME,
)
/ocean/projects/mth260009p/jcarzon/conda/envs/tsi/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
Estimating test statistic ...
/ocean/projects/mth260009p/jcarzon/conda/envs/tsi/lib/python3.11/site-packages/sbi/neural_nets/net_builders/flow.py:149: UserWarning: In one-dimensional output space, this flow is limited to Gaussians
  x_numel = get_numel(
 Neural network successfully converged after 44 epochs.
Calibration ...
Evaluating posterior for 10000 points ...: 100%|██████████| 10000/10000 [02:45<00:00, 60.42it/s]

Retraining calibration...
  [calibration] CDF estimator — augment_kwargs ignored.
  [calibration] CDF estimator: fitting on 10000 raw (T, θ) pairs.

Constructing confidence sets ...
Evaluating posterior for 1 points ...: 100%|██████████| 1/1 [01:51<00:00, 111.04s/it]

Computing p-values...

Creating set 0...

CONFIDENCE SET via Waldo (posterior-based) + p-values

Waldo with estimation_method='posterior' instead centers the test statistic on the posterior mean and variance (estimated from posterior samples), rather than the density itself — a different way of using the same underlying posterior estimator to build a confidence set.

[6]:
from lf2i.test_statistics import Waldo

waldo_ts = Waldo(
    estimator=SNPE(),
    poi_dim=POI_DIM,
    estimation_method='posterior',
    num_posterior_samples=NUM_POSTERIOR_SAMPLES,
)
lf2i_waldo = LF2I(test_statistic=waldo_ts)

waldo_region = lf2i_waldo.inference(
    x=x_obs,
    evaluation_grid=gmm.poi_grid.reshape(-1, 1),
    confidence_level=CONFIDENCE_LEVEL,
    calibration_method='p-values',
    calibration_model='nn',
    simulator=gmm,
    b=B,
    b_prime=B_PRIME,
)
Estimating test statistic ...
 Neural network successfully converged after 47 epochs.
Calibration ...
Approximating conditional mean and covariance for 10000 points...: 100%|██████████| 10000/10000 [09:56<00:00, 16.75it/s]

Retraining calibration...
  [calibration] CDF estimator — augment_kwargs ignored.
  [calibration] CDF estimator: fitting on 10000 raw (T, θ) pairs.

Constructing confidence sets ...
Approximating conditional mean and covariance for 1 points...: 100%|██████████| 1/1 [00:00<00:00,  7.58it/s]

Computing p-values...

Creating set 0...

COMPARISON

Alongside the two LF2I-calibrated regions, we also plot the posterior’s own highest-posterior-density (HPD) credible region at the same level, as an (uncalibrated) reference.

[7]:
from lf2i.utils.other_methods import hpd_region
from lf2i.plot.parameter_regions import plot_parameter_regions

_, hpd_set = hpd_region(
    posterior=posterior_ts.estimator,
    param_grid=gmm.poi_grid.reshape(-1, 1),
    x=x_obs,
    credible_level=CONFIDENCE_LEVEL,
)

plot_parameter_regions(
    waldo_region[0], posterior_region[0], hpd_set,
    param_dim=POI_DIM,
    parameter_space_bounds=POI_SPACE_BOUNDS,
    region_names=['Waldo (posterior) + p-values', 'Posterior + p-values', 'Posterior HPD (uncalibrated)'],
    title=f'{int(CONFIDENCE_LEVEL*100)}% regions for theta',
)
../_images/examples_4_waldo_posterior_pvalue_construction_15_0.png