Source code for bayesflow.simulators.benchmark_simulators.bernoulli_glm
import numpy as np
from scipy.special import expit
from .benchmark_simulator import BenchmarkSimulator
[docs]
class BernoulliGLM(BenchmarkSimulator):
def __init__(self, T: int = 100, scale_by_T: bool = True, rng: np.random.Generator = None):
"""Bernoulli GLM simulated benchmark
See: https://arxiv.org/pdf/2101.04653.pdf, Task T.5
Important: `scale_sum` should be set to False if the simulator is used
with variable `T` during training, otherwise the information of `T` will
be lost.
Parameters
----------
T: int, optional, default: 100
The simulated duration of the task (eq. the number of Bernoulli draws).
scale_by_T: bool, optional, default: True
A flag indicating whether to scale the summayr statistics by T.
rng: np.random.Generator or None, optional, default: None
An optional random number generator to use.
"""
self.T = T
self.scale_by_T = scale_by_T
self.rng = rng
if self.rng is None:
self.rng = np.random.default_rng()
# Covariance matrix computed once for efficiency
F = np.zeros((9, 9))
i = np.arange(9)
F[i, i] = 1 + np.sqrt(i / 9)
F[i[1:], i[:-1]] = -2
F[i[2:], i[:-2]] = 1
self.cov = np.linalg.inv(F.T @ F)
[docs]
def prior(self):
"""Generates a random draw from the custom prior over the 10
Bernoulli GLM parameters (1 intercept and 9 weights). Uses a
global covariance matrix `Cov` for the multivariate Gaussian prior
over the model weights, which is pre-computed for efficiency.
Returns
-------
params: np.ndarray of shape (10, )
A single draw from the prior.
"""
beta = self.rng.normal(0, 2)
f = self.rng.multivariate_normal(np.zeros(9), self.cov)
return np.append(beta, f)
[docs]
def observation_model(self, params: np.ndarray):
"""Simulates data from the custom Bernoulli GLM likelihood.
Parameters
----------
params: np.ndarray of shape (10, )
The vector of model parameters (`params[0]` is intercept, `params[i], i > 0` are weights).
Returns
-------
x: np.ndarray of shape (10, )
The vector of sufficient summary statistics of the data.
"""
# Unpack parameters
beta, f = params[0], params[1:]
# Generate design matrix
V = self.rng.normal(size=(9, self.T))
# Draw from Bernoulli GLM
z = self.rng.binomial(n=1, p=expit(V.T @ f + beta))
# Compute and return (scaled) sufficient summary statistics
x1 = np.sum(z)
x_rest = V @ z
x = np.append(x1, x_rest)
if self.scale_by_T:
x /= self.T
return x