simplexuq-code / src /dgp /tail_drift.py
anonymous0523ly's picture
Initial anonymous code release
fc329a3 verified
raw
history blame
1.61 kB
"""Scale heterogeneity with mild tail/covariance drift."""
import numpy as np
from .base import DGPSample
from .pure_scale import PureScaleDGP
from ..utils.simplex import aitchison_dist, entropy, ilr, ilr_inv
class TailDriftDGP(PureScaleDGP):
"""Pure-scale DGP with entropy-dependent anisotropic ILR noise.
This auxiliary regime is not part of the main table, but it is useful for
stress-testing local normalization when the residual shape changes as well
as the scalar residual scale.
"""
def __init__(
self,
K: int = 3,
sigma_min: float = 0.1,
c: float = 0.5,
d_x: int = 2,
drift_strength: float = 0.5,
):
super().__init__(K=K, sigma_min=sigma_min, c=c, d_x=d_x)
self.drift_strength = drift_strength
def sample(self, n: int, rng: np.random.Generator) -> DGPSample:
self._init_weights(rng)
X = rng.standard_normal((n, self.d_x))
mu = self._mu(X)
sigma = self._sigma(mu)
Z_mu = ilr(mu)
H = entropy(mu) / np.log(self.K)
eps = rng.standard_normal((n, self.K - 1))
if self.K > 2:
# Low-entropy predictions get a mild directional covariance tilt.
eps[:, 0] *= 1.0 + self.drift_strength * (1.0 - H)
eps[:, 1:] *= 1.0 - 0.5 * self.drift_strength * (1.0 - H[:, None])
else:
eps[:, 0] *= 1.0 + self.drift_strength * (1.0 - H)
Y = ilr_inv(Z_mu + sigma[:, None] * eps, K=self.K)
U = mu
R = aitchison_dist(Y, U)
return DGPSample(X=X, Y=Y, U=U, R=R, sigma_true=sigma)