def estimate_sampling_bounds(
S: np.ndarray,
gamma: float = 1.05,
eta: float = 0.05,
rho: float = 0.95,
method: str = "dyson",
omega: float = 0.8,
eta_pmax: float = 1e-3,
jump_frac: float = 0.1,
tol: float = 1e-4,
gap: float = 0.05,
random_state: int = 31213,
) -> tuple[float, float, np.ndarray]:
pmin, _, _, _, _ = pmin_bound(
S,
gamma=gamma,
eta=eta,
rho=rho,
random_state=random_state,
)
eff_dim = np.ceil((np.linalg.norm(S, "fro") / np.linalg.norm(S, 2)) ** 2).astype(
int
)
pmax = p_upper_only_k(
S,
k=eff_dim,
method=method,
tol=tol,
omega=omega,
eta=eta_pmax,
jump_frac=jump_frac,
seed=random_state,
)
S_noise = S
if pmin > pmax - gap:
logger.info("Noise regime triggered, pmin=%.4f, pmax=%.4f", pmin, pmax)
epsilon = np.linalg.norm(S, 2) / np.sqrt(S.shape[0])
t_range = np.linspace(0.0, epsilon, 10)
eff_dim_list = []
pmin_list = []
pmax_list = []
A = np.random.rand(S.shape[0], S.shape[1])
t_threshold = 0
t_iter = iter(t_range)
t = next(t_iter)
while True:
S_noise = S + t * (A + A.T)
pmin, _, _, _, _ = pmin_bound(
S_noise,
gamma=gamma,
eta=eta,
rho=rho,
random_state=random_state,
)
eff_dim = np.ceil(
(np.linalg.norm(S_noise, "fro") / np.linalg.norm(S_noise, 2)) ** 2
).astype(int)
pmax = p_upper_only_k(
S_noise,
k=eff_dim,
method=method,
tol=tol,
omega=omega,
eta=eta_pmax,
jump_frac=jump_frac,
seed=random_state,
)
logger.info(
"t=%.4f, pmin=%.4f, eff_dim=%d, pmax=%.4f", t, pmin, eff_dim, pmax
)
eff_dim_list.append(eff_dim)
pmin_list.append(pmin)
pmax_list.append(pmax)
if pmin < pmax - gap:
t_threshold = t
break
try:
t = next(t_iter)
except StopIteration:
break
S_noise = S + t_threshold * (A + A.T)
return pmin, pmax, S_noise