Exponential mechanism

Privately pick the most-common value. Utility = histogram count; ε-DP via a probabilistic argmax.

Model note: this is central DP — a trusted curator sees the raw histogram and releases one answer under ε-DP. It's not the local LDP setting used elsewhere in this demo (where each user perturbs their own data). Included here because the mechanism shows up naturally alongside LDP primitives.
Initial design (current implementation)
ldp_exponential_mode(counts[1..d], ε):
  # utility u(r) = counts[r]        (mode estimation)
  # sensitivity Δu = 1              (±1 record changes any count by ≤1)

  for r in 1..d:
      score[r] = ε · counts[r] / (2 · Δu)     # = ε · counts[r] / 2

  # log-sum-exp for numerical stability
  m = max(score)
  w[r] = exp(score[r] - m)
  total = sum(w)

  # sample r with probability w[r] / total
  return weighted_pick(w)
Design critique: the design above is too tied to a single use-case. The exponential mechanism is a meta-mechanism — it should accept an arbitrary utility u(D, r) over an arbitrary candidate set R. Utility takes two arguments (true database + candidate output); for data synthesis the natural choice is 1 / L1 distance between the true histogram and the candidate output. Target shape: a generic core plus two worked instantiations (histogram and synthesis), with a small library of default utility functions users can pick from.
Proposed redesign (design only, not yet implemented)
# ──────────────────────────────────────────────────────────
# 1. GENERIC PRIMITIVE  (no use-case assumptions baked in)
# ──────────────────────────────────────────────────────────
dp_exponential(utilities[1..k], ε, Δu) -> int:
  # utilities[r] : score of candidate r ∈ R, computed by the caller
  # Δu          : sensitivity of the utility over neighbouring databases

  for r in 1..k:
      score[r] = ε · utilities[r] / (2 · Δu)

  m = max(score)                  # log-sum-exp for stability
  w = exp(score - m)
  return weighted_pick(w / sum(w))


# ──────────────────────────────────────────────────────────
# 2. DEFAULT UTILITY BUILDERS  (library helpers users can pick from)
# ──────────────────────────────────────────────────────────
utility_count(hist)                  -> hist::float[]                   # for mode
utility_inv_l1(true_hist, R[][])     -> [ 1 / l1(true_hist,  R[i])  ∀ i ]
utility_inv_linf(true_hist, R[][])   -> [ 1 / linf(true_hist, R[i]) ∀ i ]


# ──────────────────────────────────────────────────────────
# 3a. INSTANTIATION — histogram mode  (the existing demo)
# ──────────────────────────────────────────────────────────
true_hist = [c1, c2, …, cd]
mode = dp_exponential(
         utilities = utility_count(true_hist),
         ε         = 1.0,
         Δu        = 1                # one record changes a count by ≤ 1
       )


# ──────────────────────────────────────────────────────────
# 3b. INSTANTIATION — data synthesis  (new use-case)
# ──────────────────────────────────────────────────────────
true_hist  = histogram of D
candidates = [synthetic_hist_1, …, synthetic_hist_k]    # candidate pool

best = dp_exponential(
         utilities = utility_inv_l1(true_hist, candidates),
         ε         = 1.0,
         Δu        = sensitivity bound on 1/l1 over the candidate pool
       )
release(candidates[best])