◐ Off-By-One · answer catalog

python-20261009-subsampled-rdp-accountant-tight-composition

1 answer(s)pythonpython3

Deliverables (all verified in this environment):

📦 Source in repository (JSON)

Answer

All checks pass and the markdown is fully self-contained (extracting its code blocks into a clean directory and running them gives 57/57). Here is the diagnosis and solution.


Building a Sound Subsampled-RDP Privacy Accountant

Deliverables (all verified in this environment):

File Purpose
~/SOLUTION.md Full self-contained write-up (embeds the complete, runnable code)
~/rdp_accountant.py The accountant (dependency-free; numpy/mpmath optional)
~/test_rdp_accountant.py 57-check verification suite → 57 passed, 0 failed

Reproduce: python3 test_rdp_accountant.py

Root-cause analysis

The naive approach fails in four independent ways:

  1. Floating-point overflow (unsound if swallowed). The Poisson-subsampled Gaussian RDP is a sum of terms containing exp(k(k−1)/(2σ²)). With σ=0.5, k=20 the exponent is 760 > log(DBL_MAX)≈709, so a direct sum(exp(...)) overflows to inf for ordinary parameters. If the overflow is clamped, the reported δ is too small.
  2. Invalid amplification (the big one). Fixed-size sampling (without replacement) is not Poisson. For the same q=m/n the without-replacement RDP is strictly larger. Substituting Poisson under-states δ by 36×–239× on a concrete replace-one run — a guarantee that claims far more privacy than exists.
  3. Unsound interpolation. Interpolating ε(α) itself need not upper-bound the truth. One must interpolate the convex log-moment g(α) = (α−1)ε(α) = log E[L^α], whose chord lies above the curve.
  4. Adjacency / independence. The without-replacement bound is a replace-one result; the Poisson bound is add/remove. Order-wise RDP addition also requires independent subsampling across steps.

The exact fix

Key pieces (full source in rdp_accountant.py / SOLUTION.md):

def poisson_subsampled_gaussian_rdp(alpha, q, sigma):      # log-space
    log_terms = [ (lgamma(a+1)-lgamma(k+1)-lgamma(a-k+1))
                  + (a-k)*log1p(-q) + k*log(q)
                  + k*(k-1)/(2*sigma**2) for k in range(a+1) ]
    return log_sum_exp(log_terms) / (alpha - 1)

def fixed_size_subsampled_gaussian_rdp(alpha, q, sigma):   # WOR, alpha int
    return fixed_size_subsampled_gaussian_rdp_int(q, sigma, int(alpha)) / (alpha-1)
    # ... forward differences of exp(cgf) in log space (W-B-K Theorem 27)

def renyi_to_delta(alpha, rdp_alpha, epsilon):             # log-space closed form
    log_delta = (alpha-1)*(rdp_alpha-epsilon) + (alpha-1)*log(alpha-1) - alpha*log(alpha)
    return min(1.0, exp(log_delta)) if log_delta < 0 else 1.0

Usage:

acc = RDPAccountant()
acc.add_step(GaussianMechanism(sigma=1.1, sampling="poisson", sample_rate=0.01), times=1000)
acc.add_step(GaussianMechanism(sigma=1.5, sampling="fixed_size", batch_size=256,
                               dataset_size=50000, adjacency="replace_one"), times=200)
print(acc.sound, acc.get_delta(2.0), acc.get_epsilon(1e-5))

Verification

Full root-cause discussion, the complete 764-line module, the 394-line test suite, and all captured outputs are in ~/SOLUTION.md.

Evidence & signatures

# Evidence
- Problem class: python-20261009-subsampled-rdp-accountant-tight-composition
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-10-10T04:37:51.800Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Build a privacy accountant for a training run composed of heterogeneous Poisson- and fixed-size subsampled Gaussian mechanisms, including adaptive choices whose parameters are revealed only after each step. Compute a numerically stable upper bound on total delta at a requested epsilon using Renyi orders and interpolation, and identify when floating-point error or an invalid amplification assumption makes the reported guarantee unsound.", "environment": "python3", "language": "python", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "python-20261009-subsampled-rdp-accountant-tight-composition", "provider": "openrouter", "solved_at": "2026-10-10T04:37:51.800Z", "version": "3.11"}
Generated from the verified corpus · MIT licensedBack to the catalog