Source code for src.Risk

# The bisection compares two betainc terms whose difference is ~1e-8 near the
# upper root; in JAX's default float32 that difference is pure noise, which
# flips the comparison and can leave epsU understated by up to ~0.01 at
# beta=1e-6 (the high-confidence regime this tool targets). float64 makes the
# root agree with a float64 SciPy reference to ~1e-10. It is switched on only
# inside quantify_risk (enable_x64), not for the whole Python process.
from jax.experimental import enable_x64
from jax.scipy.special import betainc

[docs] def quantify_risk(k,N,beta): r"""Compute scenario approach risk bounds on constraint violation probability. Uses the theory of Campi and Garatti with the regularized incomplete beta function to compute distribution-free bounds on the probability of out-of-sample constraint violation via bisection. The true violation probability satisfies: .. math:: \varepsilon \;\in\; [\varepsilon_L,\; \varepsilon_U] \quad \text{with confidence at least } 1 - \beta Parameters ---------- k : int Cardinality of the support list from the scenario optimization. N : int Number of sampled scenarios. beta : float Confidence parameter (e.g. ``1e-6`` for high confidence). Returns ------- epsL : float Lower bound on the constraint violation probability. epsU : float Upper bound on the constraint violation probability. Raises ------ ValueError If ``N < 1``, ``k`` is not between 0 and ``N``, or ``beta`` is not strictly between 0 and 1. Notes ----- The incomplete beta function ``betainc`` used here follows the JAX/SciPy argument convention, which differs from MATLAB (the first argument appears last in JAX). """ if int(N) != N or N < 1: raise ValueError(f"N must be a positive integer (got {N}).") if int(k) != k or not 0 <= k <= N: raise ValueError(f"k must be an integer between 0 and N = {N} (got {k}).") if not 0 < beta < 1: raise ValueError(f"beta must lie strictly between 0 and 1 (got {beta}).") with enable_x64(): return _risk_bounds(int(k), int(N), beta)
def _risk_bounds(k, N, beta): """Bisection for the lower and upper risk bounds (see quantify_risk).""" t1 = 0.0 t2 = k/N threshold = 1e-10 while (t2 - t1) > threshold: t = (t1+t2)/2 left = beta/3*betainc(k+1,N-k,t)+beta/6*betainc(k+1,4*N+1-k,t) #print("left = ",left) right = (1+beta/6/N)*t*N*(betainc(k,N-k+1,t)-betainc(k+1,N-k,t)) #print("right= ", right) if left > right: #added threshold t1 = t else: t2 = t epsL = t1 #set lower bound #print("epsL = ", epsL) if (k==N): epsU = 1.0 #set upper bound else: t1 = k/N t2 = 1 while (t2 - t1) > threshold: t = (t1 + t2) / 2 left = (beta / 2 - beta / 6) * betainc(k + 1, N - k,t) + beta / 6 * betainc(k + 1, 4 * N + 1 - k,t) #print(left) right = (1 + beta / 6 / N) * t * N * (betainc( k, N - k + 1,t) - betainc( k + 1, N - k,t)) #print(right) if left > right: t2 = t else: t1 = t epsU = t2 #set upper bound #print("epsU = ", epsU) # Return results return float(epsL), float(epsU)