diff --git a/sbi/inference/posteriors/base_posterior.py b/sbi/inference/posteriors/base_posterior.py
index 302764a51..f45f67115 100644
--- a/sbi/inference/posteriors/base_posterior.py
+++ b/sbi/inference/posteriors/base_posterior.py
@@ -37,6 +37,7 @@ def __init__(
Allows to perform, e.g. MCMC in unconstrained space.
device: Training device, e.g., "cpu", "cuda" or "cuda:0". If None,
`potential_fn.device` is used.
+ x_shape: Shape of the observed data.
"""
# Ensure device string.
@@ -132,6 +133,8 @@ def set_default_x(self, x: Tensor) -> "NeuralPosterior":
def _x_else_default_x(self, x: Optional[Array]) -> Tensor:
if x is not None:
+ # New x, reset posterior sampler.
+ self._posterior_sampler = None
return process_x(
x, x_shape=self._x_shape, allow_iid_x=self.potential_fn.allow_iid_x
)
diff --git a/sbi/inference/posteriors/mcmc_posterior.py b/sbi/inference/posteriors/mcmc_posterior.py
index 1c9dd5889..7b2dbb531 100644
--- a/sbi/inference/posteriors/mcmc_posterior.py
+++ b/sbi/inference/posteriors/mcmc_posterior.py
@@ -1,12 +1,16 @@
# This file is part of sbi, a toolkit for simulation-based inference. sbi is licensed
# under the Affero General Public License v3, see .
from functools import partial
-from typing import Any, Callable, Dict, Optional, Union
+from math import ceil
+from typing import Any, Callable, Dict, Optional, Tuple, Union
from warnings import warn
+import arviz as az
import torch
import torch.distributions.transforms as torch_tf
+from arviz.data import InferenceData
from joblib import Parallel, delayed
+from numpy import ndarray
from pyro.infer.mcmc import HMC, NUTS
from pyro.infer.mcmc.api import MCMC
from torch import Tensor
@@ -17,14 +21,15 @@
from sbi.samplers.mcmc import (
IterateParameters,
Slice,
+ SliceSamplerSerial,
+ SliceSamplerVectorized,
proposal_init,
resample_given_potential_fn,
sir_init,
- slice_np_parallized,
)
from sbi.simulators.simutils import tqdm_joblib
from sbi.types import Shape, TorchTransform
-from sbi.utils import pyro_potential_wrapper, transformed_potential
+from sbi.utils import pyro_potential_wrapper, tensor2numpy, transformed_potential
from sbi.utils.torchutils import ensure_theta_batched
@@ -102,6 +107,9 @@ def __init__(
self.init_strategy = init_strategy
self.init_strategy_parameters = init_strategy_parameters
self.num_workers = num_workers
+ self._posterior_sampler = None
+ # Hardcode parameter name to reduce clutter kwargs.
+ self.param_name = "theta"
if init_strategy_num_candidates is not None:
warn(
@@ -130,6 +138,11 @@ def mcmc_method(self, method: str) -> None:
"""See `set_mcmc_method`."""
self.set_mcmc_method(method)
+ @property
+ def posterior_sampler(self):
+ """Returns sampler created by `sample`."""
+ return self._posterior_sampler
+
def set_mcmc_method(self, method: str) -> "NeuralPosterior":
"""Sets sampling method to for MCMC and returns `NeuralPosterior`.
@@ -185,7 +198,7 @@ def sample(
sample_with: Optional[str] = None,
num_workers: Optional[int] = None,
show_progress_bars: bool = True,
- ) -> Tensor:
+ ) -> Union[Tensor, Tuple[Tensor, InferenceData]]:
r"""Return samples from posterior distribution $p(\theta|x)$ with MCMC.
Check the `__init__()` method for a description of all arguments as well as
@@ -291,11 +304,12 @@ def sample(
warmup_steps=warmup_steps, # type: ignore
num_chains=num_chains,
show_progress_bars=show_progress_bars,
- ).detach()
+ )
else:
raise NameError
samples = self.theta_transform.inv(transformed_samples)
+
return samples.reshape((*sample_shape, -1)) # type: ignore
def _build_mcmc_init_fn(
@@ -419,6 +433,7 @@ def _slice_np_mcmc(
warmup_steps: int,
vectorized: bool = False,
num_workers: int = 1,
+ init_width: Union[float, ndarray] = 0.01,
show_progress_bars: bool = True,
) -> Tensor:
"""Custom implementation of slice sampling using Numpy.
@@ -429,32 +444,47 @@ def _slice_np_mcmc(
initial_params: Initial parameters for MCMC chain.
thin: Thinning (subsampling) factor.
warmup_steps: Initial number of samples to discard.
- vectorized: Whether to use a vectorized implementation of
- the Slice sampler (still experimental).
- num_workers: number of CPU cores to use
- seed: seed that will be used to generate sub-seeds for each worker
+ vectorized: Whether to use a vectorized implementation of the Slice sampler.
+ num_workers: Number of CPU cores to use.
+ init_width: Inital width of brackets.
show_progress_bars: Whether to show a progressbar during sampling;
can only be turned off for vectorized sampler.
- Returns: Tensor of shape (num_samples, shape_of_single_theta).
+ Returns:
+ Tensor of shape (num_samples, shape_of_single_theta).
+ Arviz InferenceData object.
"""
num_chains, dim_samples = initial_params.shape
- samples = slice_np_parallized(
- potential_function,
- initial_params,
- num_samples,
+ if not vectorized:
+ SliceSamplerMultiChain = SliceSamplerSerial
+ else:
+ SliceSamplerMultiChain = SliceSamplerVectorized
+
+ posterior_sampler = SliceSamplerMultiChain(
+ init_params=tensor2numpy(initial_params),
+ log_prob_fn=potential_function,
+ num_chains=num_chains,
thin=thin,
- warmup_steps=warmup_steps,
- vectorized=vectorized,
+ verbose=show_progress_bars,
num_workers=num_workers,
- show_progress_bars=show_progress_bars,
+ init_width=init_width,
)
+ warmup_ = warmup_steps * thin
+ num_samples_ = ceil((num_samples * thin) / num_chains)
+ # Run mcmc including warmup
+ samples = posterior_sampler.run(warmup_ + num_samples_)
+ samples = samples[:, warmup_steps:, :] # discard warmup steps
+ samples = torch.from_numpy(samples) # chains x samples x dim
+
+ # Save posterior sampler.
+ self._posterior_sampler = posterior_sampler
# Save sample as potential next init (if init_strategy == 'latest_sample').
self._mcmc_init_params = samples[:, -1, :].reshape(num_chains, dim_samples)
+ # Collect samples from all chains.
samples = samples.reshape(-1, dim_samples)[:num_samples, :]
assert samples.shape[0] == num_samples
@@ -470,7 +500,7 @@ def _pyro_mcmc(
warmup_steps: int = 200,
num_chains: Optional[int] = 1,
show_progress_bars: bool = True,
- ):
+ ) -> Tensor:
r"""Return samples obtained using Pyro HMC, NUTS for slice kernels.
Args:
@@ -484,7 +514,9 @@ def _pyro_mcmc(
num_chains: Whether to sample in parallel. If None, use all but one CPU.
show_progress_bars: Whether to show a progressbar during sampling.
- Returns: Tensor of shape (num_samples, shape_of_single_theta).
+ Returns:
+ Tensor of shape (num_samples, shape_of_single_theta).
+ Arviz InferenceData object.
"""
num_chains = mp.cpu_count() - 1 if num_chains is None else num_chains
@@ -494,7 +526,7 @@ def _pyro_mcmc(
kernel=kernels[mcmc_method](potential_fn=potential_function),
num_samples=(thin * num_samples) // num_chains + num_chains,
warmup_steps=warmup_steps,
- initial_params={"": initial_params},
+ initial_params={self.param_name: initial_params},
num_chains=num_chains,
mp_context="spawn",
disable_progbar=not show_progress_bars,
@@ -505,10 +537,13 @@ def _pyro_mcmc(
-1, initial_params.shape[1] # .shape[1] = dim of theta
)
+ # Save posterior sampler.
+ self._posterior_sampler = sampler
+
samples = samples[::thin][:num_samples]
assert samples.shape[0] == num_samples
- return samples
+ return samples.detach()
def _prepare_potential(self, method: str) -> Callable:
"""Combines potential and transform and takes care of gradients and pyro.
@@ -612,6 +647,60 @@ def map(
force_update=force_update,
)
+ def get_arviz_inference_data(self) -> InferenceData:
+ """Returns arviz InferenceData object constructed most recent samples.
+
+ Note: the InferenceData is constructed using the posterior samples generated in
+ most recent call to `.sample(...)`.
+
+ For Pyro HMC and NUTS kernels InferenceData will contain diagnostics, for Pyro
+ Slice or sbi slice sampling samples, only the samples are added.
+
+ Returns:
+ inference_data: Arviz InferenceData object.
+ """
+ assert (
+ self._posterior_sampler is not None
+ ), """No samples have been generated, call .sample() first."""
+
+ sampler: Union[
+ MCMC, SliceSamplerSerial, SliceSamplerVectorized
+ ] = self._posterior_sampler
+
+ # If Pyro sampler and samples not transformed, use arviz' from_pyro.
+ # Exclude 'slice' kernel as it lacks the 'divergence' diagnostics key.
+ if isinstance(self._posterior_sampler, (HMC, NUTS)) and isinstance(
+ self.theta_transform, torch_tf.IndependentTransform
+ ):
+ inference_data = az.from_pyro(sampler)
+
+ # otherwise get samples from sampler and transform to original space.
+ else:
+ transformed_samples = sampler.get_samples(group_by_chain=True)
+ # Pyro samplers returns dicts, get values.
+ if isinstance(transformed_samples, Dict):
+ # popitem gets last items, [1] get the values as tensor.
+ transformed_samples = transformed_samples.popitem()[1]
+ # Our slice samplers return numpy arrays.
+ elif isinstance(transformed_samples, ndarray):
+ transformed_samples = torch.from_numpy(transformed_samples).type(
+ torch.float32
+ )
+ # For MultipleIndependent priors transforms first dim must be batch dim.
+ # thus, reshape back and forth to have batch dim in front.
+ num_chains, samples_per_chain, dim_params = transformed_samples.shape
+ samples = self.theta_transform.inv( # type: ignore
+ transformed_samples.reshape(-1, dim_params)
+ ).reshape( # type: ignore
+ num_chains, samples_per_chain, dim_params
+ )
+
+ inference_data = az.convert_to_inference_data(
+ {f"{self.param_name}": samples}
+ )
+
+ return inference_data
+
def _maybe_use_dict_entry(default: Any, key: str, dict_to_check: Dict) -> Any:
"""Returns `default` if `key` is not in the dict and otherwise the dict entry.
diff --git a/sbi/samplers/mcmc/__init__.py b/sbi/samplers/mcmc/__init__.py
index 125cc5f86..7d3abe146 100644
--- a/sbi/samplers/mcmc/__init__.py
+++ b/sbi/samplers/mcmc/__init__.py
@@ -7,6 +7,6 @@
from sbi.samplers.mcmc.slice import Slice
from sbi.samplers.mcmc.slice_numpy import (
SliceSampler,
+ SliceSamplerSerial,
SliceSamplerVectorized,
- slice_np_parallized,
)
diff --git a/sbi/samplers/mcmc/slice_numpy.py b/sbi/samplers/mcmc/slice_numpy.py
index a0e90cf94..7924fa671 100644
--- a/sbi/samplers/mcmc/slice_numpy.py
+++ b/sbi/samplers/mcmc/slice_numpy.py
@@ -5,6 +5,7 @@
import sys
from math import ceil
from typing import Callable, Optional, Union
+from warnings import warn
import numpy as np
import torch
@@ -55,23 +56,34 @@ def gen(self, n_samples):
class SliceSampler(MCMCSampler):
def __init__(
- self, x, lp_f, max_width=float("inf"), thin=None, verbose: bool = False
+ self,
+ x,
+ lp_f,
+ max_width=float("inf"),
+ init_width: Union[float, np.ndarray] = 0.01,
+ thin=None,
+ tuning: int = 50,
+ verbose: bool = False,
):
"""Slice sampling for multivariate continuous probability distributions.
It cycles sampling from each conditional using univariate slice sampling.
Args:
- x: initial state
+ x: Initial state.
lp_f: Function that returns the log prob.
- max_width: maximum bracket width
- thin: amount of thinning; if None, no thinning.
+ max_width: maximum bracket width.
+ init_width: Inital width of brackets.
+ thin: Amount of thinning; if None, no thinning.
+ tuning: Number of tuning steps for brackets.
verbose: Whether to show progress bars (False).
"""
MCMCSampler.__init__(self, x, lp_f, thin, verbose=verbose)
self.max_width = max_width
+ self.init_width = init_width
self.width = None
+ self.tuning = tuning
def gen(
self,
@@ -141,15 +153,14 @@ def _tune_bracket_width(self, rng):
rng: Random number generator to use.
"""
- n_samples = 50
order = list(range(self.n_dims))
x = self.x.copy()
- self.width = np.full(self.n_dims, 0.01)
+ self.width = np.full(self.n_dims, self.init_width)
- tbar = trange(n_samples, miniters=10, disable=not self.verbose)
+ tbar = trange(self.tuning, miniters=10, disable=not self.verbose)
tbar.set_description("Tuning bracket width...")
for n in tbar:
- # for n in range(int(n_samples)):
+ # for n in range(int(self.tuning)):
rng.shuffle(order)
for i in range(self.n_dims):
@@ -203,31 +214,171 @@ def _sample_from_conditional(self, i: int, cxi, rng):
return xi, ux - lx
+class SliceSamplerSerial:
+ def __init__(
+ self,
+ log_prob_fn: Callable,
+ init_params: np.ndarray,
+ num_chains: int = 1,
+ thin: Optional[int] = None,
+ tuning: int = 50,
+ verbose: bool = True,
+ init_width: Union[float, np.ndarray] = 0.01,
+ max_width: float = float("inf"),
+ num_workers: int = 1,
+ ):
+ """Slice sampler in pure Numpy, running for each chain in serial.
+
+ Parallelization across CPUs is possible by setting num_workers > 1.
+
+ Args:
+ log_prob_fn: Log prob function.
+ init_params: Initial parameters.
+ num_chains: Number of MCMC chains to run in parallel
+ thin: amount of thinning; if None, no thinning.
+ tuning: Number of tuning steps for brackets.
+ verbose: Show/hide additional info such as progress bars.
+ init_width: Inital width of brackets.
+ max_width: Maximum width of brackets.
+ num_workers: Number of parallel workers to use.
+ """
+ self._log_prob_fn = log_prob_fn
+
+ self.x = init_params
+ self.num_chains = num_chains
+ self.thin = thin
+ self.tuning = tuning
+ self.verbose = verbose
+
+ self.init_width = init_width
+ self.max_width = max_width
+
+ self.n_dims = self.x.size
+ self.num_workers = num_workers
+ self._samples = None
+
+ def run(self, num_samples: int) -> np.ndarray:
+ """Runs MCMC and returns thinned samples.
+
+ Sampling is performed parallelized across CPUs if self.num_workers > 1.
+ Parallelization is seeded across workers.
+
+ Note: Thinning is performed internally.
+
+ Args:
+ num_samples: Number of samples to generate
+ Returns:
+ MCMC samples in shape (num_chains, num_samples_per_chain, num_dim)
+ """
+
+ num_chains, dim_samples = self.x.shape
+
+ # Generate seeds for workers from current random state.
+ seeds = torch.randint(high=2**31, size=(num_chains,))
+
+ with tqdm_joblib(
+ tqdm(
+ range(num_chains), # type: ignore
+ disable=not self.verbose or self.num_workers == 1,
+ desc=f"""Running {self.num_chains} MCMC chains with
+ {self.num_workers} worker{"s" if self.num_workers>1 else ""}.""",
+ total=self.num_chains,
+ )
+ ):
+ all_samples = Parallel(n_jobs=self.num_workers)(
+ delayed(self.run_fun)(num_samples, initial_params_batch, seed)
+ for initial_params_batch, seed in zip(self.x, seeds)
+ )
+
+ samples = np.stack(all_samples).astype(np.float32)
+ samples = samples.reshape(num_chains, -1, dim_samples) # chains, samples, dim
+ samples = samples[:, :: self.thin, :] # thin chains
+
+ # save samples
+ self._samples = samples
+
+ return samples
+
+ def run_fun(self, num_samples, inits, seed) -> np.ndarray:
+ """Runs MCMC for a given number of samples starting at inits."""
+ np.random.seed(seed)
+ posterior_sampler = SliceSampler(
+ inits,
+ lp_f=self._log_prob_fn,
+ max_width=self.max_width,
+ init_width=self.init_width,
+ thin=self.thin,
+ tuning=self.tuning,
+ # turn off pbars in parallel mode.
+ verbose=self.num_workers == 1 and self.verbose,
+ )
+ return posterior_sampler.gen(num_samples)
+
+ def get_samples(
+ self, num_samples: Optional[int] = None, group_by_chain: bool = True
+ ) -> np.ndarray:
+ """Returns samples from last call to self.run.
+
+ Raises ValueError if no samples have been generated yet.
+
+ Args:
+ num_samples: Number of samples to return (for each chain if grouped by
+ chain), if too large, all samples are returned (no error).
+ group_by_chain: Whether to return samples grouped by chain (chain x samples
+ x dim_params) or flattened (all_samples, dim_params).
+
+ Returns:
+ samples
+ """
+ if self._samples is None:
+ raise ValueError("No samples found from MCMC run.")
+ # if not grouped by chain, flatten samples into (all_samples, dim_params)
+ if not group_by_chain:
+ samples = self._samples.reshape(-1, self._samples.shape[2])
+ else:
+ samples = self._samples
+
+ # if not specified return all samples
+ if num_samples is None:
+ return samples
+ # otherwise return last num_samples (for each chain when grouped).
+ elif group_by_chain:
+ return samples[:, -num_samples:, :]
+ else:
+ return samples[-num_samples:, :]
+
+
class SliceSamplerVectorized:
def __init__(
self,
log_prob_fn: Callable,
init_params: np.ndarray,
num_chains: int = 1,
+ thin: Optional[int] = None,
tuning: int = 50,
verbose: bool = True,
init_width: Union[float, np.ndarray] = 0.01,
max_width: float = float("inf"),
+ num_workers: int = 1,
):
"""Slice sampler in pure Numpy, vectorized evaluations across chains.
Args:
log_prob_fn: Log prob function.
init_params: Initial parameters.
- verbose: Show/hide additional info such as progress bars.
+ num_chains: Number of MCMC chains to run in parallel
+ thin: amount of thinning; if None, no thinning.
tuning: Number of tuning steps for brackets.
+ verbose: Show/hide additional info such as progress bars.
init_width: Inital width of brackets.
max_width: Maximum width of brackets.
+ num_workers: Number of parallel workers to use (not implemented.)
"""
self._log_prob_fn = log_prob_fn
self.x = init_params
self.num_chains = num_chains
+ self.thin = 1 if thin is None else thin
self.tuning = tuning
self.verbose = verbose
@@ -236,6 +387,14 @@ def __init__(
self.n_dims = self.x.size
+ self._samples = None
+
+ # TODO: implement parallelization across batches of chains.
+ if num_workers > 1:
+ warn(
+ """Parallelization of vectorized slice sampling not implement, running
+ serially."""
+ )
self._reset()
def _reset(self):
@@ -425,109 +584,41 @@ def run(self, num_samples: int) -> np.ndarray:
samples = np.stack([self.state[c]["samples"] for c in range(self.num_chains)])
+ samples = samples[:, :: self.thin, :] # thin chains
+
+ self._samples = samples
+
return samples
+ def get_samples(
+ self, num_samples: Optional[int] = None, group_by_chain: bool = True
+ ) -> np.ndarray:
+ """Returns samples from last call to self.run.
-def slice_np_parallized(
- potential_function: Callable,
- initial_params: torch.Tensor,
- num_samples: int,
- thin: int,
- warmup_steps: int,
- vectorized: bool,
- num_workers: int = 1,
- show_progress_bars: bool = False,
-):
- """Run slice np (vectorized) parallized over CPU cores.
-
- In case of the vectorized version of slice np parallization happens over batches of
- chains to still exploit vectorization.
-
- MCMC progress bars are omitted if num_workers > 1 to reduce clutter. Instead the
- progress over chains is shown.
-
- Args:
- potential_function: potential function
- initial_params: initital parameters, one for each chain
- num_samples: number of MCMC samples to produce
- thin: thinning factor
- warmup_steps: number of warmup / burnin steps
- vectorized: whether to use the vectorized version
- num_workers: number of CPU cores to use
- show_progress_bars: whether to show progress bars
-
- Returns:
- Tensor: final MCMC samples of each chain (num_chains, num_samples, dim_samples)
- """
- num_chains, dim_samples = initial_params.shape
-
- # Generate seeds for workers from current random state.
- seeds = torch.randint(high=2**31, size=(num_chains,))
-
- if not vectorized:
- # Define run function for given input.
- def run_slice_np(inits, seed):
- # Seed current job.
- np.random.seed(seed)
- posterior_sampler = SliceSampler(
- tensor2numpy(inits).reshape(-1),
- lp_f=potential_function,
- thin=thin,
- # Show pbars of workers only for single worker
- verbose=show_progress_bars and num_workers == 1,
- )
- if warmup_steps > 0:
- posterior_sampler.gen(int(warmup_steps))
- return posterior_sampler.gen(ceil(num_samples / num_chains))
-
- # For sequential chains each batch has only a single chain.
- batch_size = 1
- run_fun = run_slice_np
-
- else: # Sample all chains at the same time
-
- # Define local function to run a batch of chains vectorized.
- def run_slice_np_vectorized(inits, seed):
- # Seed current job.
- np.random.seed(seed)
- posterior_sampler = SliceSamplerVectorized(
- init_params=tensor2numpy(inits),
- log_prob_fn=potential_function,
- num_chains=inits.shape[0],
- # Show pbars of workers only for single worker
- verbose=show_progress_bars and num_workers == 1,
- )
- warmup_ = warmup_steps * thin
- num_samples_ = ceil((num_samples * thin) / num_chains)
- samples = posterior_sampler.run(warmup_ + num_samples_)
- samples = samples[:, warmup_:, :] # discard warmup steps
- samples = samples[:, ::thin, :] # thin chains
- samples = torch.from_numpy(samples) # chains x samples x dim
- return samples
+ Raises ValueError if no samples have been generated yet.
- # For vectorized case a batch contains multiple chains to exploit vectorization.
- batch_size = ceil(num_chains / num_workers)
- run_fun = run_slice_np_vectorized
-
- # Parallize over batch of chains.
- initial_params_in_batches = torch.split(initial_params, batch_size, dim=0)
- num_batches = len(initial_params_in_batches)
-
- # Show progress bars over batches.
- with tqdm_joblib(
- tqdm(
- range(num_batches), # type: ignore
- disable=not show_progress_bars or num_workers == 1,
- desc=f"""Running {num_chains} MCMC chains with {num_workers} worker{"s" if
- num_workers>1 else ""} (batch_size={batch_size}).""",
- total=num_chains,
- )
- ):
- all_samples = Parallel(n_jobs=num_workers)(
- delayed(run_fun)(initial_params_batch, seed)
- for initial_params_batch, seed in zip(initial_params_in_batches, seeds)
- )
- all_samples = np.stack(all_samples).astype(np.float32)
- samples = torch.from_numpy(all_samples)
+ Args:
+ num_samples: Number of samples to return (for each chain if grouped by
+ chain), if too large, all samples are returned (no error).
+ group_by_chain: Whether to return samples grouped by chain (chain x samples
+ x dim_params) or flattened (all_samples, dim_params).
- return samples.reshape(num_chains, -1, dim_samples) # chains x samples x dim
+ Returns:
+ samples
+ """
+ if self._samples is None:
+ raise ValueError("No samples found from MCMC run.")
+ # if not grouped by chain, flatten samples into (all_samples, dim_params)
+ if not group_by_chain:
+ samples = self._samples.reshape(-1, self._samples.shape[2])
+ else:
+ samples = self._samples
+
+ # if not specified return all samples
+ if num_samples is None:
+ return samples
+ # otherwise return last num_samples (for each chain when grouped).
+ elif group_by_chain:
+ return samples[:, -num_samples:, :]
+ else:
+ return samples[-num_samples:, :]
diff --git a/sbi/utils/sbiutils.py b/sbi/utils/sbiutils.py
index bde9d7375..0dc2dd811 100644
--- a/sbi/utils/sbiutils.py
+++ b/sbi/utils/sbiutils.py
@@ -6,9 +6,12 @@
from math import pi
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union
+import arviz as az
import pyknos.nflows.transforms as transforms
import torch
import torch.distributions.transforms as torch_tf
+from arviz.data import InferenceData
+from numpy import ndarray
from pyro.distributions import Empirical
from torch import Tensor
from torch import nn as nn
diff --git a/setup.py b/setup.py
index f997f840e..6064d0cba 100644
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,7 @@
REQUIRES_PYTHON = ">=3.6.0"
REQUIRED = [
+ "arviz",
"joblib>=1.0.0",
"matplotlib",
"numpy",
diff --git a/tests/mcmc_test.py b/tests/mcmc_test.py
index c9035f2d4..f68717b13 100644
--- a/tests/mcmc_test.py
+++ b/tests/mcmc_test.py
@@ -3,19 +3,32 @@
from __future__ import annotations
-from typing import Union
+from math import ceil
+import arviz as az
import numpy as np
import pytest
import torch
from torch import eye, ones, zeros
-
+from torch.distributions import Uniform
+
+from sbi.inference import (
+ SNLE,
+ MCMCPosterior,
+ likelihood_estimator_based_potential,
+ prepare_for_sbi,
+ simulate_for_sbi,
+)
from sbi.samplers.mcmc.slice_numpy import (
SliceSampler,
+ SliceSamplerSerial,
SliceSamplerVectorized,
- slice_np_parallized,
)
-from sbi.simulators.linear_gaussian import true_posterior_linear_gaussian_mvn_prior
+from sbi.simulators.linear_gaussian import (
+ diagonal_linear_gaussian,
+ true_posterior_linear_gaussian_mvn_prior,
+)
+from sbi.utils import likelihood_nn, tensor2numpy
from tests.test_utils import check_c2st
@@ -43,17 +56,25 @@ def test_c2st_slice_np_on_Gaussian(num_dim: int):
def lp_f(x):
return target_distribution.log_prob(torch.as_tensor(x, dtype=torch.float32))
- sampler = SliceSampler(lp_f=lp_f, x=np.zeros((num_dim,)).astype(np.float32))
- _ = sampler.gen(warmup)
+ sampler = SliceSampler(
+ lp_f=lp_f,
+ x=np.zeros((num_dim,)).astype(np.float32),
+ tuning=warmup,
+ )
+ warmup_samples = sampler.gen(warmup)
+ assert warmup_samples.shape == (warmup, num_dim)
+
samples = sampler.gen(num_samples)
+ assert samples.shape == (num_samples, num_dim)
samples = torch.as_tensor(samples, dtype=torch.float32)
- check_c2st(samples, target_samples, alg=f"slice_np")
+ check_c2st(samples, target_samples, alg="slice_np")
@pytest.mark.parametrize("num_dim", (1, 2))
-def test_c2st_slice_np_vectorized_on_Gaussian(num_dim: int):
+@pytest.mark.parametrize("slice_sampler", (SliceSamplerVectorized, SliceSamplerSerial))
+def test_c2st_slice_np_vectorized_on_Gaussian(num_dim: int, slice_sampler):
"""Test MCMC on Gaussian, comparing to ground truth target via c2st.
Args:
@@ -63,6 +84,7 @@ def test_c2st_slice_np_vectorized_on_Gaussian(num_dim: int):
num_samples = 500
warmup = 500
num_chains = 5
+ thin = 2
likelihood_shift = -1.0 * ones(num_dim)
likelihood_cov = 0.3 * eye(num_dim)
@@ -77,7 +99,7 @@ def test_c2st_slice_np_vectorized_on_Gaussian(num_dim: int):
def lp_f(x):
return target_distribution.log_prob(torch.as_tensor(x, dtype=torch.float32))
- sampler = SliceSamplerVectorized(
+ sampler = slice_sampler(
log_prob_fn=lp_f,
init_params=np.zeros(
(
@@ -85,23 +107,32 @@ def lp_f(x):
num_dim,
)
).astype(np.float32),
+ tuning=warmup,
+ thin=thin,
num_chains=num_chains,
)
- samples = sampler.run(warmup + int(num_samples / num_chains))
+ samples = sampler.run(thin * (warmup + int(num_samples / num_chains)))
+ assert samples.shape == (
+ num_chains,
+ warmup + int(num_samples / num_chains),
+ num_dim,
+ )
samples = samples[:, warmup:, :]
samples = samples.reshape(-1, num_dim)
samples = torch.as_tensor(samples, dtype=torch.float32)
- check_c2st(samples, target_samples, alg="slice_np_vectorized")
+ alg = {
+ SliceSamplerVectorized: "slice_np_vectorized",
+ SliceSamplerSerial: "slice_np",
+ }[slice_sampler]
+
+ check_c2st(samples, target_samples, alg=alg)
@pytest.mark.parametrize("vectorized", (False, True))
@pytest.mark.parametrize("num_workers", (1, 10))
-@pytest.mark.parametrize("seed", (None, 42))
-def test_c2st_slice_np_parallelized(
- vectorized: bool, num_workers: int, seed: Union[None, int]
-):
+def test_c2st_slice_np_parallelized(vectorized: bool, num_workers: int):
"""Test MCMC on Gaussian, comparing to ground truth target via c2st.
Args:
@@ -110,8 +141,9 @@ def test_c2st_slice_np_parallelized(
"""
num_dim = 2
num_samples = 500
- warmup = 500
- num_chains = 5
+ warmup = 100
+ num_chains = 10
+ thin = 2
likelihood_shift = -1.0 * ones(num_dim)
likelihood_cov = 0.3 * eye(num_dim)
@@ -128,37 +160,73 @@ def lp_f(x):
initial_params = torch.zeros((num_chains, num_dim))
- # Maybe test seeding.
- if seed is not None:
- torch.manual_seed(seed)
-
- samples = slice_np_parallized(
- lp_f,
- initial_params,
- num_samples,
- thin=1,
- warmup_steps=warmup,
- vectorized=vectorized,
+ if not vectorized:
+ SliceSamplerMultiChain = SliceSamplerSerial
+ else:
+ SliceSamplerMultiChain = SliceSamplerVectorized
+
+ posterior_sampler = SliceSamplerMultiChain(
+ init_params=tensor2numpy(initial_params),
+ log_prob_fn=lp_f,
+ num_chains=num_chains,
+ thin=thin,
+ verbose=False,
num_workers=num_workers,
- show_progress_bars=False,
)
- # Repeat to test seeding.
- if seed is not None:
- torch.manual_seed(seed)
- samples_2 = slice_np_parallized(
- lp_f,
- initial_params,
- num_samples,
- thin=1,
- warmup_steps=warmup,
- vectorized=vectorized,
- num_workers=num_workers,
- )
- # Test seeding.
- assert torch.allclose(samples, samples_2)
-
+ warmup_ = warmup * thin
+ num_samples_ = ceil((num_samples * thin) / num_chains)
+ samples = posterior_sampler.run(warmup_ + num_samples_) # chains x samples x dim
+ samples = samples[:, warmup:, :] # discard warmup steps
samples = torch.as_tensor(samples, dtype=torch.float32).reshape(-1, num_dim)
check_c2st(
samples, target_samples, alg=f"slice_np {'vectorized' if vectorized else ''}"
)
+
+
+@pytest.mark.parametrize(
+ "method",
+ (
+ "nuts",
+ "hmc",
+ "slice",
+ "slice_np",
+ "slice_np_vectorized",
+ ),
+)
+def test_getting_inference_diagnostics(method):
+
+ num_samples = 100
+ num_dim = 2
+ num_chains = 2
+
+ # Use composed prior to test MultipleIndependent case.
+ prior = [
+ Uniform(low=-ones(1), high=ones(1)),
+ Uniform(low=-ones(1), high=ones(1)),
+ ]
+
+ simulator, prior = prepare_for_sbi(diagonal_linear_gaussian, prior)
+ density_estimator = likelihood_nn("maf", num_transforms=3)
+ inference = SNLE(density_estimator=density_estimator, show_progress_bars=False)
+
+ theta, x = simulate_for_sbi(simulator, prior, 1000, simulation_batch_size=50)
+ likelihood_estimator = inference.append_simulations(theta, x).train(
+ training_batch_size=100
+ )
+
+ x_o = zeros((1, num_dim))
+ potential_fn, theta_transform = likelihood_estimator_based_potential(
+ prior=prior, likelihood_estimator=likelihood_estimator, x_o=x_o
+ )
+ posterior = MCMCPosterior(
+ proposal=prior,
+ potential_fn=potential_fn,
+ theta_transform=theta_transform,
+ thin=3,
+ num_chains=num_chains,
+ )
+ posterior.sample(sample_shape=(num_samples,), method=method)
+ idata = posterior.get_arviz_inference_data()
+
+ az.plot_trace(idata)
diff --git a/tests/posterior_sampler_test.py b/tests/posterior_sampler_test.py
new file mode 100644
index 000000000..b397ee23f
--- /dev/null
+++ b/tests/posterior_sampler_test.py
@@ -0,0 +1,80 @@
+# This file is part of sbi, a toolkit for simulation-based inference. sbi is licensed
+# under the Affero General Public License v3, see .
+
+from __future__ import annotations
+
+import pytest
+from pyro.infer.mcmc import MCMC
+from torch import eye, zeros
+from torch.distributions import MultivariateNormal
+
+from sbi import utils as utils
+from sbi.inference import (
+ SNL,
+ MCMCPosterior,
+ likelihood_estimator_based_potential,
+ prepare_for_sbi,
+ simulate_for_sbi,
+)
+from sbi.samplers.mcmc import SliceSamplerSerial, SliceSamplerVectorized
+from sbi.simulators.linear_gaussian import diagonal_linear_gaussian
+
+
+@pytest.mark.parametrize(
+ "sampling_method",
+ (
+ "slice_np",
+ "slice_np_vectorized",
+ "slice",
+ "nuts",
+ "hmc",
+ ),
+)
+def test_api_posterior_sampler_set(sampling_method: str, set_seed):
+ """Runs SNL and checks that posterior_sampler is correctly set.
+
+ Args:
+ mcmc_method: which mcmc method to use for sampling
+ set_seed: fixture for manual seeding
+ """
+
+ num_dim = 2
+ num_samples = 10
+ num_trials = 2
+ num_simulations = 10
+ x_o = zeros((num_trials, num_dim))
+ # Test for multiple chains is cheap when vectorized.
+ num_chains = 3 if sampling_method in "slice_np_vectorized" else 1
+
+ prior = MultivariateNormal(loc=zeros(num_dim), covariance_matrix=eye(num_dim))
+ simulator, prior = prepare_for_sbi(diagonal_linear_gaussian, prior)
+ inference = SNL(prior, show_progress_bars=False)
+
+ theta, x = simulate_for_sbi(
+ simulator, prior, num_simulations, simulation_batch_size=10
+ )
+ estimator = inference.append_simulations(theta, x).train(max_num_epochs=5)
+ potential_fn, transform = likelihood_estimator_based_potential(
+ estimator, prior, x_o
+ )
+ posterior = MCMCPosterior(
+ potential_fn, theta_transform=transform, method=sampling_method, proposal=prior
+ )
+
+ assert posterior.posterior_sampler is None
+ posterior.sample(
+ sample_shape=(num_samples, num_chains),
+ x=x_o,
+ mcmc_parameters={
+ "thin": 3,
+ "num_chains": num_chains,
+ "init_strategy": "prior",
+ },
+ )
+
+ if sampling_method in ["slice", "hmc", "nuts"]:
+ assert type(posterior.posterior_sampler) is MCMC
+ elif sampling_method == "slice_np":
+ assert type(posterior.posterior_sampler) is SliceSamplerSerial
+ else: # sampling_method == "slice_np_vectorized"
+ assert type(posterior.posterior_sampler) is SliceSamplerVectorized
diff --git a/tutorials/14_multi-trial-data-and-mixed-data-types.ipynb b/tutorials/14_multi-trial-data-and-mixed-data-types.ipynb
index 2af57ffac..efba7bcc2 100644
--- a/tutorials/14_multi-trial-data-and-mixed-data-types.ipynb
+++ b/tutorials/14_multi-trial-data-and-mixed-data-types.ipynb
@@ -43,7 +43,6 @@
"outputs": [],
"source": [
"import torch\n",
- "import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from torch import zeros, ones, eye\n",
@@ -793,7 +792,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "Python 3.8.13 ('sbi')",
"language": "python",
"name": "python3"
},
@@ -807,7 +806,12 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.11"
+ "version": "3.8.13"
+ },
+ "vscode": {
+ "interpreter": {
+ "hash": "9ef9b53a5ce850816b9705a866e49207a37a04a71269aa157d9f9ab944ea42bf"
+ }
}
},
"nbformat": 4,
diff --git a/tutorials/15_mcmc_diagnostics_with_arviz.ipynb b/tutorials/15_mcmc_diagnostics_with_arviz.ipynb
new file mode 100644
index 000000000..7d85c0b63
--- /dev/null
+++ b/tutorials/15_mcmc_diagnostics_with_arviz.ipynb
@@ -0,0 +1,1218 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# MCMC diagnostics with [Arviz](https://python.arviz.org/)\n",
+ "\n",
+ "This tutorial shows how to evaluate the quality of MCMC samples generated via `sbi` using the `arviz` package. \n",
+ "\n",
+ "We demonstrate this case using the trial-based simulator presented in Tutorial 14: A toy simulator mimicking the drift-diffusion model of decision-making. \n",
+ "\n",
+ "Outline:\n",
+ "\n",
+ "1) Train MNLE to approximate the likelihood underlying the simulator\n",
+ "2) Run MCMC using `pyro` MCMC samplers via `sbi` interface\n",
+ "3) Use `arviz` to visualize the posterior, predictive distributions and MCMC diagnostics. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import arviz as az\n",
+ "import torch\n",
+ "\n",
+ "from sbi.inference import MNLE, likelihood_estimator_based_potential\n",
+ "from pyro.distributions import InverseGamma\n",
+ "from torch.distributions import Beta, Binomial, Gamma\n",
+ "from sbi.utils import MultipleIndependent\n",
+ "\n",
+ "from sbi.inference import MCMCPosterior\n",
+ "\n",
+ "# Seeding\n",
+ "torch.manual_seed(1);"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Toy simulator for mixed data\n",
+ "def mixed_simulator(theta):\n",
+ " beta, ps = theta[:, :1], theta[:, 1:]\n",
+ "\n",
+ " choices = Binomial(probs=ps).sample()\n",
+ " rts = InverseGamma(concentration=2 * torch.ones_like(beta), rate=beta).sample()\n",
+ "\n",
+ " return torch.cat((rts, choices), dim=1)\n",
+ "\n",
+ "\n",
+ "# Define independent priors for each dimension.\n",
+ "prior = MultipleIndependent(\n",
+ " [\n",
+ " Gamma(torch.tensor([1.0]), torch.tensor([0.5])),\n",
+ " Beta(torch.tensor([2.0]), torch.tensor([2.0])),\n",
+ " ],\n",
+ " validate_args=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Train MNLE to approximate the likelihood\n",
+ "\n",
+ "For details see [tutorial 14](https://www.mackelab.org/sbi/tutorial/14_multi-trial-data-and-mixed-data-types/). \n",
+ "\n",
+ "Here, we pass `mcmc_method=\"nuts\"` in order to use the underlying [`pyro` No-U-turn sampler](https://docs.pyro.ai/en/1.8.1/mcmc.html#nuts), but it would work as well with other samplers (e.g. \"slice_np_vectorized\", \"hmc\"). \n",
+ "\n",
+ "Additionally, when calling `posterior.sample(...)` we pass `return_arviz=True` so that the [`Arviz InferenceData`](https://arviz-devs.github.io/arviz/api/generated/arviz.InferenceData.html#arviz.InferenceData) object is returned. This object gives us access to the wealth of MCMC diagnostics tool provided by `arviz`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/janbolts/qode/sbi/sbi/neural_nets/mnle.py:60: UserWarning: The mixed neural likelihood estimator assumes that x contains\n",
+ " continuous data in the first n-1 columns (e.g., reaction times) and\n",
+ " categorical data in the last column (e.g., corresponding choices). If\n",
+ " this is not the case for the passed `x` do not use this function.\n",
+ " warnings.warn(\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Neural network successfully converged after 65 epochs."
+ ]
+ }
+ ],
+ "source": [
+ "# Generate training data and train MNLE.\n",
+ "num_simulations = 10000\n",
+ "theta = prior.sample((num_simulations,))\n",
+ "x = mixed_simulator(theta)\n",
+ "\n",
+ "trainer = MNLE(prior)\n",
+ "likelihood_estimator = trainer.append_simulations(theta, x).train()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Run Pyro NUTS MCMC and obtain `arviz InferenceData` object"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/janbolts/qode/sbi/sbi/utils/sbiutils.py:280: UserWarning: An x with a batch size of 100 was passed. It will be interpreted as a batch of independent and identically\n",
+ " distributed data X={x_1, ..., x_n}, i.e., data generated based on the\n",
+ " same underlying (unknown) parameter. The resulting posterior will be with\n",
+ " respect to entire batch, i.e,. p(theta | X).\n",
+ " warnings.warn(\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Simulate \"observed\" data x_o\n",
+ "torch.manual_seed(42)\n",
+ "num_trials = 100\n",
+ "theta_o = prior.sample((1,))\n",
+ "x_o = mixed_simulator(theta_o.repeat(num_trials, 1))\n",
+ "\n",
+ "# Set MCMC parameters and run Pyro NUTS.\n",
+ "mcmc_parameters = dict(\n",
+ " num_chains=4,\n",
+ " thin=5,\n",
+ " warmup_steps=50,\n",
+ " init_strategy=\"proposal\",\n",
+ " method=\"nuts\",\n",
+ ")\n",
+ "num_samples = 1000\n",
+ "\n",
+ "# get the potential function and parameter transform for constructing the posterior\n",
+ "potential_fn, parameter_transform = likelihood_estimator_based_potential(\n",
+ " likelihood_estimator, prior, x_o\n",
+ ")\n",
+ "mnle_posterior = MCMCPosterior(\n",
+ " potential_fn, proposal=prior, theta_transform=parameter_transform, **mcmc_parameters\n",
+ ")\n",
+ "\n",
+ "mnle_samples = mnle_posterior.sample(\n",
+ " (num_samples,), x=x_o, show_progress_bars=False\n",
+ ")\n",
+ "# get arviz InferenceData object from posterior\n",
+ "inference_data = mnle_posterior.get_arviz_inference_data()\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Generate `arviz` plots\n",
+ "\n",
+ "The resulting `InferenceData` object can be passed to most `arviz` plotting functions, and there are plenty see [here](https://arviz-devs.github.io/arviz/examples/index.html#) for an overview.\n",
+ "\n",
+ "To get a better understanding of the `InferenceData` object see [here](https://arviz-devs.github.io/arviz/schema/schema.html). \n",
+ "\n",
+ "Below and overview of common MCMC diagnostics plot, see the corresponding `arviz` documentation for interpretation of the plots. \n",
+ "\n",
+ "We will a full use-case using the SBI-MCMC-arviz workflow soon."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ " \n",
+ " - \n",
+ " \n",
+ " \n",
+ " \n",
+ "
\n",
+ "
\n",
+ " \n",
+ "
<xarray.Dataset>\n",
+ "Dimensions: (chain: 4, draw: 1254, theta_dim_0: 2)\n",
+ "Coordinates:\n",
+ " * chain (chain) int64 0 1 2 3\n",
+ " * draw (draw) int64 0 1 2 3 4 5 6 ... 1248 1249 1250 1251 1252 1253\n",
+ " * theta_dim_0 (theta_dim_0) int64 0 1\n",
+ "Data variables:\n",
+ " theta (chain, draw, theta_dim_0) float32 2.125 0.8092 ... 0.8088\n",
+ "Attributes:\n",
+ " created_at: 2022-08-10T14:02:41.300799\n",
+ " arviz_version: 0.11.2
- chain: 4
- draw: 1254
- theta_dim_0: 2
theta
(chain, draw, theta_dim_0)
float32
2.125 0.8092 2.267 ... 1.848 0.8088
array([[[2.1245914 , 0.8092162 ],\n",
+ " [2.266512 , 0.8164251 ],\n",
+ " [2.036817 , 0.79519475],\n",
+ " ...,\n",
+ " [1.8315581 , 0.7797423 ],\n",
+ " [2.050106 , 0.7541253 ],\n",
+ " [1.9130744 , 0.7940467 ]],\n",
+ "\n",
+ " [[1.8672262 , 0.79227704],\n",
+ " [1.9084876 , 0.87156725],\n",
+ " [1.9282253 , 0.89998335],\n",
+ " ...,\n",
+ " [1.966494 , 0.7684441 ],\n",
+ " [1.9171734 , 0.76520354],\n",
+ " [1.9165115 , 0.8100004 ]],\n",
+ "\n",
+ " [[2.1789386 , 0.92230934],\n",
+ " [2.2388353 , 0.8388026 ],\n",
+ " [2.2388353 , 0.8388026 ],\n",
+ " ...,\n",
+ " [2.2749808 , 0.8510151 ],\n",
+ " [2.207828 , 0.843363 ],\n",
+ " [1.9331468 , 0.7714892 ]],\n",
+ "\n",
+ " [[2.0355892 , 0.902892 ],\n",
+ " [2.1481078 , 0.76634836],\n",
+ " [2.0999866 , 0.8813891 ],\n",
+ " ...,\n",
+ " [1.8813787 , 0.85843307],\n",
+ " [1.9198259 , 0.727575 ],\n",
+ " [1.8484387 , 0.80877745]]], dtype=float32)
- created_at :
- 2022-08-10T14:02:41.300799
- arviz_version :
- 0.11.2
\n",
+ "
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
\n",
+ " "
+ ],
+ "text/plain": [
+ "Inference data with groups:\n",
+ "\t> posterior"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "inference_data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Diagnostic plots"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([,\n",
+ " ],\n",
+ " dtype=object)"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAABoMAAAIzCAYAAADLfWB5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAA9hAAAPYQGoP6dpAACUdklEQVR4nOzdeWAU9f3/8deeOTEsh0UhHMohXiBFrYIniFYRqbWKWsUCKlW01CpapS36pZWCt1ip/rRiWxVRWoGiFbSKAioqoIgE8OAWMIQQcu/x+2PJJiE7m91kd2d39vn4BzZ5Z9+fST7z2fnMe+YztkAgEBAAAAAAAAAAAAAsyW52AwAAAAAAAAAAAJA4FIMAAAAAAAAAAAAsjGIQAAAAAAAAAACAhVEMAgAAAAAAAAAAsDCKQQAAAAAAAAAAABZGMQgAAAAAAAAAAMDCKAYBAAAAAAAAAABYGMUgAAAAAAAAAAAAC6MYBAAAAAAAAAAAYGEUgwAApvvwww/Vp08fXXPNNWY3BQAAAAASjjkQACDZnGY3AACQGZ577jmVlZVp9OjROuyww0xty4cffqiPPvpIp5xyik499VRT2wIAAADAmpgDAQBSCXcGAQCS4vnnn9fMmTO1f/9+s5uijz76SDNnztRHH31kdlMAAAAAWBRzIABAKqEYBAAAAAAAAAAAYGEUgwAAAAAAAAAAACzMFggEAmY3AgBgXfPmzdNvf/tbw+8///zzkqRrr71Wp5xyimbPnq2///3vmjt3rjZv3qw2bdro7LPP1u2336527dqFfY99+/bpb3/7m9566y1t27ZNNptNvXr10mWXXabLLrtMdnv9tQ99+vQxbMtPfvITTZs2TZK0detWvf7663rvvfe0ZcsWFRcXKz8/X8cdd5yuueYanX322S34bQAAAACwOuZAAIBU5DS7AQAAa2vfvr0GDBigtWvXqqamRscff7zcbnfo+23atFFZWVno9R133KGFCxeqe/fu6tatm7755hu9+uqr+uyzzzRv3rxGPytJGzdu1NixY7Vr1y65XC5169ZNNTU1+uyzz7RmzRq9//77evTRR2Wz2SRJAwYM0M6dO7Vz504dccQROuKII0Lv1b1799D/Z82apVdeeUW5ubk6/PDD1adPH+3atUvvv/++3n//ff3mN7/RDTfckKDfGgAAAIB0xRwIAJCKuDMIAJAU5557rrZv36633npLXbp0afS9Dz/8UNdee61cLpc8Ho+eeOIJnXjiiZKkb775Rtddd52+++47TZkyRVdeeWXo5yoqKnTJJZdoy5YtuuaaazRx4kTl5+dLkjZt2qSJEydq48aN+v3vf6+rr7469HOPP/64Zs6cqQkTJuiWW24J2953331Xbdu21YknnhiaREnSxx9/rIkTJ2rv3r1644031LVr17j9jgAAAABYB3MgAEAq4ZlBAICUUVtbq9/97nehSZAk9ejRQ+PGjZMkLV26tFH8q6++qi1btui8887T5MmTQ5MgSerZs6ceeOAB2Ww2/e1vf4u5LWeddZb69evXaBIkSQMHDtSvfvUr+Xw+LVq0KOb3BQAAAIA6zIEAAMnCMnEAgJRRUFCgYcOGNfn6CSecIEnatm1bo6+/+eabkqSf/exnYd/vmGOOUefOnbV161Z999136tSpU0zt2bt3rxYsWKDPPvtMxcXFqq6uliQdOHBAkrR+/fqY3g8AAAAAGmIOBABIFopBAICUUVhYGPbr7du3lySVl5c3+vqGDRskSY8++qhmzZoV9mdLSkokSbt27YppIvT+++9r4sSJjdbyPlRpaWnU7wcAAAAAh2IOBABIFopBAICUkZubG/brhy5TUKfu6rQvvvii2feuqqqKuh379+/XbbfdprKyMo0cOVJXXXWVevToofz8fNntdi1fvly/+MUv5PV6o35PAAAAADgUcyAAQLJQDAIApK3c3Fzt379fb775prp16xa39126dKlKS0t10kknadq0aU0mYjt37oxbLgAAAACIFnMgAEBL2c1uAAAALXX00UdLkjZu3BjTzxldZVdn+/btkqT+/fuHjWWdbAAAAABmYA4EAGgpikEAgKTIzs6WpNADSOOh7kGrzz//vAKBQNQ/l5WVJcl42YS673///fdNvldSUqJXXnkl1qYCAAAAyDDMgQAAqYRiEAAgKeoejPrRRx/F7T2vuOIKFRYW6sMPP9Ttt9+u3bt3N/p+eXm5Fi1apPvvvz9sW1atWhV2zeuBAwdKkt544w0tX7489PXdu3fr1ltvlc/ni9s2AAAAALAm5kAAgFTCM4MAAEnx4x//WO+8846mTJmiF154QW3btpUk3X333S1+z7y8PP31r3/VDTfcoIULF2rRokWhh5yWlpZq69at8vl86tevX6OfGzx4sAoK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",
+ "text/plain": [
+ "