|
1 | 1 | import numpy as np |
2 | 2 |
|
3 | | -from ..simulator import Simulator |
4 | | -from bayesflow.types import Shape |
| 3 | +from .benchmark_simulator import BenchmarkSimulator |
5 | 4 |
|
6 | 5 |
|
7 | | -class TwoMoons(Simulator): |
8 | | - def __init__(self, lower_bound: float = -1.0, upper_bound: float = 1.0, seed: int = None): |
| 6 | +class TwoMoons(BenchmarkSimulator): |
| 7 | + def __init__(self, lower_bound: float = -1.0, upper_bound: float = 1.0, rng: np.random.Generator = None): |
| 8 | + """Two moons simulated benchmark. |
| 9 | + See: https://arxiv.org/pdf/2101.04653.pdf, Task T.8 |
| 10 | +
|
| 11 | + Parameters |
| 12 | + ---------- |
| 13 | + lower_bound: float, optional, default: -1.0 |
| 14 | + The lower bound of the uniform prior |
| 15 | + upper_bound: float, optional, default: 1.0 |
| 16 | + The upper bound of the uniform prior |
| 17 | + rng: np.random.Generator or None, optional, default: None |
| 18 | + An option random number generator to use |
| 19 | + """ |
| 20 | + |
9 | 21 | self.lower_bound = lower_bound |
10 | 22 | self.upper_bound = upper_bound |
11 | | - self.rng = np.random.default_rng(seed) |
| 23 | + self.rng = rng |
| 24 | + if self.rng is None: |
| 25 | + self.rng = np.random.default_rng() |
| 26 | + |
| 27 | + def prior(self): |
| 28 | + """Generates a random draw from a 2-dimensional uniform prior bounded between |
| 29 | + `lower_bound` and `upper_bound` which represents the two parameters of the two moons simulator. |
| 30 | +
|
| 31 | + Returns |
| 32 | + ------- |
| 33 | + params: np.ndarray of shape (2, ) |
| 34 | + A single draw from the 2-dimensional uniform prior. |
| 35 | + """ |
| 36 | + |
| 37 | + return self.rng.uniform(low=self.lower_bound, high=self.upper_bound, size=2) |
| 38 | + |
| 39 | + def observation_model(self, params: np.ndarray): |
| 40 | + """Implements data generation from the two-moons model with a bimodal posterior. |
| 41 | +
|
| 42 | + Parameters |
| 43 | + ---------- |
| 44 | + params: np.ndarray of shape (2, ) |
| 45 | + The vector of two model parameters. |
12 | 46 |
|
13 | | - def sample(self, batch_shape: Shape, **kwargs) -> dict[str, np.ndarray]: |
14 | | - r = self.rng.normal(0.1, 0.01, size=batch_shape + (1,)) |
15 | | - alpha = self.rng.uniform(-0.5 * np.pi, 0.5 * np.pi, size=batch_shape + (1,)) |
16 | | - theta = self.rng.uniform(self.lower_bound, self.upper_bound, size=batch_shape + (2,)) |
| 47 | + Returns |
| 48 | + ------- |
| 49 | + observables: np.ndarray of shape (2, ) |
| 50 | + The 2D vector generated from the two moons simulator. |
| 51 | + """ |
17 | 52 |
|
18 | | - x1 = -np.abs(theta[..., :1] + theta[..., 1:]) / np.sqrt(2.0) + r * np.cos(alpha) + 0.25 |
19 | | - x2 = (-theta[..., :1] + theta[..., 1:]) / np.sqrt(2.0) + r * np.sin(alpha) |
| 53 | + # Generate noise |
| 54 | + alpha = self.rng.uniform(low=-0.5 * np.pi, high=0.5 * np.pi) |
| 55 | + r = self.rng.normal(loc=0.1, scale=0.01) |
20 | 56 |
|
21 | | - x = np.concatenate([x1, x2], axis=-1) |
| 57 | + # Forward process |
| 58 | + rhs1 = np.array([r * np.cos(alpha) + 0.25, r * np.sin(alpha)]) |
| 59 | + rhs2 = np.array([-np.abs(params[0] + params[1]) / np.sqrt(2.0), (-params[0] + params[1]) / np.sqrt(2.0)]) |
22 | 60 |
|
23 | | - return dict(r=r, alpha=alpha, theta=theta, x=x) |
| 61 | + return rhs1 + rhs2 |
0 commit comments