Metadata-Version: 2.4
Name: diffpriv-pure
Version: 0.1.0
Summary: Zero-dependency pure-stdlib differential privacy: Laplace, Gaussian, Geometric mechanisms + PrivacyBudget accountant
Author-email: Prasad A <prasad@example.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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."*

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://pypi.org/project/diffpriv-pure/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

---

## Quick Start

```bash
pip install diffpriv-pure
```

```python
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

```bash
# 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):

```json
{
  "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 |

```bash
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 `bounds` or `sensitivity` manually.
- **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 return `float`).

## 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)

```bash
git clone https://github.com/prasad-a-abhishek/diffpriv-pure.git
cd diffpriv-pure
pip install -e .
```

Or install from source with zero dependencies:

```bash
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`](benchmarks/adversarial/FUZZING_REPORT.md)._
