diffpriv-pure
Zero-dependency pure-stdlib differential privacy for Python. Laplace, Gaussian, and Geometric mechanisms + a PrivacyBudget sequential accountant — no numpy, no scipy, no build step.
"Ship DP analytics to AWS Lambda in <100 ms cold-start, with zero C extensions."
Quick Start
pip install diffpriv-pure
from diffpriv_pure import laplace_noise, private_sum, private_mean, private_count, PrivacyBudget
# Pure ε-DP: Laplace noise for a sum query
noisy_sum = private_sum(values=[1.2, 3.4, 5.1], bounds=(0.0, 10.0), epsilon=0.5)
# Relaxed (ε, δ)-DP: Gaussian noise for a mean
noisy_mean = private_mean(values=[1.2, 3.4, 5.1], bounds=(0.0, 10.0), epsilon=0.5, delta=1e-6)
# Integer count with geometric (discrete Laplace) noise
noisy_count = private_count(condition_count=42, epsilon=0.5)
# Budget accountant — refuses to overspend ε or δ
budget = PrivacyBudget(total_epsilon=2.0, total_delta=1e-5)
budget.spend(epsilon=0.5, delta=1e-6, query="count_users")
budget.spend(epsilon=0.5, delta=1e-6, query="mean_age")
# budget.spend(epsilon=2.0) → raises PrivacyBudgetExhausted
CLI
# Laplace mechanism
diffpriv-pure laplace --epsilon 1.0 --sensitivity 1.0 --value 42.0
# Gaussian mechanism
diffpriv-pure gaussian --epsilon 0.5 --delta 1e-6 --sensitivity 1.0 --value 100.0
# Private count (geometric noise)
diffpriv-pure count --epsilon 0.5 --value 1000
# Private mean from a file
diffpriv-pure mean --epsilon 0.5 --bounds 0,100 --input values.txt
JSON output (all commands):
{
"value": 42.39635838696227,
"epsilon": 1.0,
"delta": 0.0,
"mechanism": "laplace"
}
Public API
| Function | Returns | Description |
|---|---|---|
laplace_noise(scale, epsilon) |
float |
Sample from Lap(0, b), b = scale |
gaussian_noise(sigma) |
float |
Sample from N(0, σ²) via Box–Muller |
gaussian_sigma(sensitivity, epsilon, delta) |
float |
Analytic Gaussian σ per Balle & Wang 2018 |
geometric_noise(epsilon) |
int |
Shifted geometric sample for integer counts |
private_sum(values, bounds, epsilon, delta=0) |
float |
Clamp values, add Laplace (δ=0) or Gaussian (δ>0) noise |
private_mean(values, bounds, epsilon, delta=0) |
float |
Clamp values, compute mean, add calibrated noise |
private_count(count, epsilon) |
int |
Add geometric noise to integer count |
PrivacyBudget(total_epsilon, total_delta=0) |
accountant | Sequential (ε, δ) accountant with hard depletion |
PrivacyBudgetExhausted |
Exception |
Raised when a query would exceed the budget |
⚡ Performance
No heavy dependencies means sub-100 ms cold-start on serverless:
| Package | Cold-start | Dependencies |
|---|---|---|
| diffpriv-pure | < 100 ms | none |
| diffprivlib (IBM) | 80–250 ms | numpy, scipy, scikit-learn, joblib |
| pydp (Google) | Fails | C++ FFI, numpy, protobuf |
| opendp (Harvard) | Fails | Rust FFI, numpy |
python3 benchmarks/run_benchmark.py
Limitations
- No DP-SGD / federated learning — this is a classical mechanism library, not a deep learning integration.
- No synthetic data generation — no GANs, VAEs, or marginal synthesis.
- No auto-sensitivity estimation — callers must supply
boundsorsensitivitymanually. - No async / parallel / vectorised operations — works on
list[float], not numpy arrays. - No advanced composition auto-application —
advanced_composition()method is exposed for manual use; basic sequential composition is applied automatically. - Does not guarantee exact integer output for
private_sum/private_mean(these returnfloat).
Non-Goals
This library does not provide:
- DP-SGD or federated learning integration (PyTorch / TensorFlow / JAX)
- Synthetic data or marginal synthesis (GANs, VAEs)
- Auto-sensitivity estimation
- SQL query rewriting
- Privacy-utility tradeoff optimization
- Support for negative δ
Install (clean clone)
git clone https://github.com/prasad-a-abhishek/diffpriv-pure.git
cd diffpriv-pure
pip install -e .
Or install from source with zero dependencies:
pip install --no-deps diffpriv-pure
pip check # shows no missing dependencies
Requires: Python 3.11+ — stdlib only (math, random, secrets, argparse, json).
Test Suite
pytest --collect-only -q | tail -1
# → 126 tests collected
pytest -v
# → 126 passed in ~0.5s
Coverage map (spec acceptance criteria → tests):
| AC | Criterion | Test(s) |
|---|---|---|
| AC-1 | Laplace mean/variance convergence | test_laplace.py::test_ac1_mean_variance |
| AC-2 | Gaussian sigma Balle-Wang formula | test_gaussian.py::test_ac2_approximate_formula |
| AC-3 | Gaussian mean/variance convergence | test_gaussian.py::test_ac3_mean_variance |
| AC-4 | private_sum returns float, reproducible |
test_laplace.py::test_private_sum_returns_float |
| AC-5 | private_sum clamps out-of-bounds values |
test_laplace.py::test_ac5_private_sum_clamping |
| AC-6 | private_mean sensitivity = (hi-lo)/n |
test_laplace.py::test_ac6_private_mean_divides_by_n |
| AC-7 | private_count returns int |
test_geometric.py::test_ac7_returns_int |
| AC-8 | Geometric noise concentrated for high ε | test_geometric.py::test_ac8_high_epsilon_near_zero |
| AC-9 | Budget exhaustion raises | test_budget.py::test_ac9_spend_exhausts |
| AC-10 | 4×(0.5, 1e-6) → remaining ε=0, δ=9.6e-6 | test_budget.py::test_ac10_four_spends |
| AC-11 | History dict shape | test_budget.py::test_ac11_history_is_list_of_dicts |
| AC-12 | Laplace validation | test_laplace.py::test_ac12_*_raises |
| AC-13 | Gaussian validation | test_gaussian.py::test_ac13_*_raises |
| AC-14 | CLI lapace/gaussian JSON | test_cli.py::test_laplace_subcommand |
| AC-15 | CLI count JSON | test_cli.py::test_count_subcommand |
| AC-16 | CLI invalid input exit code | test_cli.py::test_ac16_* |
| AC-17 | Zero dependencies | test_dependencies.py::test_ac17_dependencies_empty |
| AC-18 | LOC ≤ 380 | test_loc_budget.py::test_ac18_loc_budget |
| AC-20 | Seed reproducibility | test_laplace.py::test_ac20_reproducibility |
| AC-21 | No mutation on failed spend | test_budget.py::test_ac21_no_mutation_on_fail |
Adversarial QA
For the full adversarial QA report (methodology, surfaces, fuzz harnesses, 300k+ iters, 0 findings), see benchmarks/adversarial/FUZZING_REPORT.md.
Metadata
Release files for diffpriv-pure 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| diffpriv_pure-0.1.0.tar.gz | 18.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| diffpriv_pure-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.6 kB
Release files / diffpriv_pure-0.1.0.tar.gz
| Download URL | diffpriv_pure-0.1.0.tar.gz |
|---|---|
| Size | 18.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a160d13a7736f3f65bad105c07ad6796387016006dfc6baf798e346406d02a99
|
|
BLAKE2b-256 checksum How to use checksums |
a6600a7fc2bee01e3b4d4571ca3732d07692aabda96a21916c0cf84e10be537e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / diffpriv_pure-0.1.0-py3-none-any.whl
| Download URL | diffpriv_pure-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dcc149c29ddb39b6c10fefb3bd126a73f69551fd97ab0fea76b86476f543e815
|
|
BLAKE2b-256 checksum How to use checksums |
ea5d1e09791aa2e94fa8446d618f043d8cf35f1aee06362c3ea0cc0c1e591a81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|