Skip to content

Instantly share code, notes, and snippets.

@Shoeboxam
Last active April 9, 2024 15:46
Show Gist options
  • Save Shoeboxam/543dad5a6e9f9a2deb71eb97b33875b8 to your computer and use it in GitHub Desktop.
Save Shoeboxam/543dad5a6e9f9a2deb71eb97b33875b8 to your computer and use it in GitHub Desktop.
OpenDP Quantile
import opendp.prelude as dp
dp.enable_features("contrib")
# define privacy guarantee
max_contributions = 1
epsilon = 0.1
# public information
candidates = [10, 30, 50, 70, 90]
space = dp.vector_domain(dp.atom_domain(T=int)), dp.symmetric_distance()
# create a dataset transformation that scores the utility of each candidate
t_median = space >> dp.t.then_quantile_score_candidates(candidates, alpha=0.5)
# utility of each candidate (lower is better)
print(t_median(list(range(100))))
# >>> [395000, 195000, 5000, 205000]
m_median = dp.binary_search_chain(
lambda s: t_median >> dp.m.then_report_noisy_max_gumbel(s, "min"),
d_in=max_contributions,
d_out=epsilon,
) >> (lambda i: candidates[i])
# check that median mechanism is (ε=.1)-dp
assert m_median.map(max_contributions) >= epsilon
# DP release
print(m_median(list(range(100))))
# >>> 50 (as expected)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment