def estimate_sampling_bounds_fast(
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,
verbose: bool = False,
random_state: int = 31213,
n_jobs: int = -1,
) -> tuple[float, float, np.ndarray]:
pmin, _, _, _, _ = pmin_bound(
S, gamma=gamma, eta=eta, rho=rho, random_state=random_state, verbose=verbose
)
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,
verbose=verbose,
seed=random_state,
)
S_noise = S
if pmin > pmax - gap:
epsilon = np.linalg.norm(S, 2) / np.sqrt(S.shape[0])
t_range = np.linspace(0.0, epsilon, 10)
A = np.random.rand(S.shape[0], S.shape[1])
AtA = A + A.T
def _eval_t(t):
S_t = S + t * AtA
pmin_t, _, _, _, _ = pmin_bound(
S_t,
gamma=gamma,
eta=eta,
rho=rho,
random_state=random_state,
verbose=verbose,
)
eff_dim_t = np.ceil(
(np.linalg.norm(S_t, "fro") / np.linalg.norm(S_t, 2)) ** 2
).astype(int)
pmax_t = p_upper_only_k(
S_t,
k=eff_dim_t,
method=method,
tol=tol,
omega=omega,
eta=eta_pmax,
jump_frac=jump_frac,
verbose=verbose,
seed=random_state,
)
return float(pmin_t), float(pmax_t)
results = Parallel(n_jobs=n_jobs)(delayed(_eval_t)(float(t)) for t in t_range)
idx = None
for i, (pm, px) in enumerate(results):
if pm < px - gap:
idx = i
break
if idx is not None:
t_threshold = float(t_range[idx])
pmin, pmax = results[idx]
else:
t_threshold = 0.0
pmin, pmax = results[-1]
S_noise = S + t_threshold * AtA
return pmin, pmax, S_noise