From 99ce2f300cbe5ff2670b89df626180759e2d9411 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Mon, 30 Jun 2025 10:22:09 -0700 Subject: [PATCH 001/216] RandomizedResponse --- SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean new file mode 100644 index 00000000..e69de29b From 772ddbe98fa9740678284161e4c549993780a7ef Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 30 Jun 2025 10:43:24 -0700 Subject: [PATCH 002/216] rrdraft --- .../Local/RandomizedResponse.lean | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean index e69de29b..c2803bf2 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean @@ -0,0 +1,46 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert + +/- For now, we assume that our databases consist of a single element of type T, + representing a single user (i.e., a database of size one) -/ + +/- Implementation of randomized response for a single user with rational parameter 0 ≤ lambda ≤ 1/2 -/ + +def RRRandomizer (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : SLang Bool := do + let result1 ← SLang.BernoulliSample (2 * num + den) (2 * den) (by + linarith) -- note (2 * num + den) / (2 * den) = 1/2 + num/den + match result1 with + | true => return (query l) -- happens with probability (1/2 + num/den) + | false => return ¬(query l) -- happens with probability (1/2 - num/den) + + +lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h : 2 * num < den): + HasSum (RRRandomizer query l num den h) 1 := by + simp [RRRandomizer] + simp [(Summable.hasSum_iff ENNReal.summable), tsum_bool, add_tsub_cancel_iff_le] + aesop + { sorry } + { sorry } + +def RRRandomizerPMF (query : List T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : PMF Bool := + ⟨RRRandomizer query l num den h, RRRandomizerpmf_lemma query l num den h⟩ + +/-- THERE ARE STILL ISSUES BELOW THIS LINE. DO NOT EDIT YET. -/ + +/- Implementation of Randomized Response. Applies local randomizer to each user's data. -/ +def RandomizedResponseSample (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (List (SLang Bool)) := + do List.map (λ l => RRRandomizer query l num den h) l + +/-- lemma DP_RandomizedResponse (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : + sorry --/ + +/- Here is a special case of Randomized Response, with parameter 1/4-/ + +def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do + RRRandomizer query l 1 4 (by decide) /- Randomized Response with parameter 1/4 -/ + +lemma coinflippmf_lemma (query: T -> Bool) (l: T) : HasSum (CoinFlipSample query l) 1 := + RRRandomizerpmf_lemma query l 1 4 (by decide) + +def CoinFlipPMF (query : T -> Bool) (l : T) : PMF Bool := + ⟨CoinFlipSample query l, coinflippmf_lemma query l⟩ From 75a9f04f1bbe2133309bbf543ba1fd7d5c32fbe3 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 1 Jul 2025 09:00:56 -0700 Subject: [PATCH 003/216] coercions --- .../Local/ENNRealCoercions.lean | 52 +++++++++++++++++++ .../Local/RandomizedResponse.lean | 50 ++++++++++++++++-- SampCert/Samplers/Bernoulli/Code.lean | 1 + ffi.cpp | 2 +- 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean diff --git a/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean b/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean new file mode 100644 index 00000000..eb8a17c1 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean @@ -0,0 +1,52 @@ +import SampCert + +lemma real_to_ennreal_eq (a b : Real): a = b -> ENNReal.ofReal a = ENNReal.ofReal b := by + intro h + exact congrArg ENNReal.ofReal h + +#check ENNReal.ofReal_lt_ofReal_iff_of_nonneg +#check ENNReal.ofReal_le_ofReal_iff +#check ENNReal.mul_ne_top +#check ENNReal.add_ne_top + + +lemma div_not_top (a b : ENNReal) (h0: a ≠ ⊤) (h1: b ≠ 0): a/b ≠ ⊤ := by + rw [@ENNReal.div_eq_inv_mul] + apply ENNReal.mul_ne_top + {apply ENNReal.inv_ne_top.mpr h1} + {exact h0} + +lemma arith_0 (a : ENNReal) (h0 : a ≠ ⊤) (h1: a < 1) : 1 - a + a = 1 := by + rw [← ENNReal.toReal_eq_one_iff] + have h2: (1 - a + a).toReal = 1 - a.toReal + a.toReal := by + rw [@tsub_add_eq_max] + rw[max_eq_left_of_lt h1] + rw [ENNReal.one_toReal] + ring + rw[h2] + ring + +lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): + 1 - ((2 : ENNReal) * num + den) / (2 * den) + (2 * num + den) / (2 * den) = 1 := + by + apply arith_0 + apply div_not_top + { + simp + apply ENNReal.mul_ne_top + simp + simp + } + { + simp + exact Nat.not_eq_zero_of_lt h + } + { refine ENNReal.div_lt_of_lt_mul' ?h1.h + aesop + have h1 : ENNReal.ofReal (2 * num) < den := by + refine (ENNReal.ofReal_lt_iff_lt_toReal ?ha ?hb).mpr ?_ + simp + simp + simp + sorry + } diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean index c2803bf2..f3ad29e6 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean @@ -13,7 +13,6 @@ def RRRandomizer (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * nu | true => return (query l) -- happens with probability (1/2 + num/den) | false => return ¬(query l) -- happens with probability (1/2 - num/den) - lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h : 2 * num < den): HasSum (RRRandomizer query l num den h) 1 := by simp [RRRandomizer] @@ -22,19 +21,58 @@ lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) { sorry } { sorry } -def RRRandomizerPMF (query : List T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : PMF Bool := +def RRRandomizerPMF (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : PMF Bool := ⟨RRRandomizer query l num den h, RRRandomizerpmf_lemma query l num den h⟩ /-- THERE ARE STILL ISSUES BELOW THIS LINE. DO NOT EDIT YET. -/ +/- def mapper (n : Nat) (num : Nat) (den : PNat) (h : 2 * num < den) (_ : Fin n) : SLang Bool := do + let result1 ← SLang.BernoulliSample (2 * num + den) (2 * den) (by linarith) + return result1 + +def matcher (query : T -> Bool) (val_prob : T × Bool): Bool := do + let a ← val_prob.2 + + /- Implementation of Randomized Response. Applies local randomizer to each user's data. -/ -def RandomizedResponseSample (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (List (SLang Bool)) := - do List.map (λ l => RRRandomizer query l num den h) l +def RandomizedResponseSample (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (SLang (List Bool)) := do + let a ← List.replicate l.length 0 + let a ← List.map (SLang.BernoulliSample (2 * num + den) (2 * den) (by linarith)) a + let a ← List.zip l a + let a ← List.map (matcher query) a + return a + /-- lemma DP_RandomizedResponse (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : sorry --/ -/- Here is a special case of Randomized Response, with parameter 1/4-/ +/- Here is a special case of Randomized Response, with parameter 1/4-/ -/ + +def count_differences {T : Type} [DecidableEq T]: List T -> List T -> ℕ + | [], l => l.length + | l, [] => l.length + | x :: xs, y :: ys => + if x = y then + count_differences xs ys + else 1 + count_differences xs ys + +noncomputable def output_probabilities {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : List T → ENNReal := + fun l' => + let diff := count_differences (List.map (fun x => x) l) l' + let same := l.length - diff + ENNReal.ofReal (((2 * num + den : ℝ) / (2 * den : ℝ)) ^ diff * ((den - 2 * num : ℝ) / (2 * den : ℝ)) ^ same) + /-- The output_probabilities function describes a function from List Bool to ENNReal, + but SLang (List Bool) is a function List Bool → ENNReal (i.e., a probability mass function). + To return SLang (List T), you should define a function of type List T → ENNReal. + If you want to return SLang (List T), you can define: -/ +noncomputable def output_probabilities_SLang {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : SLang (List T) := + fun l' => + let diff := count_differences l l' + let same := l.length - diff + ENNReal.ofReal (((2 * num + den : ℝ) / (2 * den : ℝ)) ^ diff * ((den - 2 * num : ℝ) / (2 * den : ℝ)) ^ same) + +noncomputable def RandomizedResponseSample {T : Type} [DecidableEq T] (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (SLang (List Bool)) := + output_probabilities num den h (List.map query l) def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do RRRandomizer query l 1 4 (by decide) /- Randomized Response with parameter 1/4 -/ @@ -44,3 +82,5 @@ lemma coinflippmf_lemma (query: T -> Bool) (l: T) : HasSum (CoinFlipSample query def CoinFlipPMF (query : T -> Bool) (l : T) : PMF Bool := ⟨CoinFlipSample query l, coinflippmf_lemma query l⟩ + +/- need randomized response as a mechanism... -/ diff --git a/SampCert/Samplers/Bernoulli/Code.lean b/SampCert/Samplers/Bernoulli/Code.lean index 10c3bad6..0b1712f6 100644 --- a/SampCert/Samplers/Bernoulli/Code.lean +++ b/SampCert/Samplers/Bernoulli/Code.lean @@ -24,4 +24,5 @@ def BernoulliSample (num : Nat) (den : PNat) (_ : num ≤ den) : SLang Bool := d let d ← UniformSample den return d < num +#check BernoulliSample 1 2 (by decide) end SLang diff --git a/ffi.cpp b/ffi.cpp index 8b3775b3..ac08dfdc 100644 --- a/ffi.cpp +++ b/ffi.cpp @@ -47,7 +47,7 @@ extern "C" lean_object * prob_While(lean_object * condition, lean_object * body, extern "C" lean_object * my_run(lean_object * a) { if (urandom == -1) { - urandom = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + urandom = open("/dev/urandom", O_RDONLY); if (urandom == -1) { lean_internal_panic("prob_UniformByte: /dev/urandom cannot be opened"); } From 4be0701110b6b1b61d742b6e3b4a7c7288f2b867 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 1 Jul 2025 14:06:59 -0700 Subject: [PATCH 004/216] forethan --- .../Local/RandomizedResponse.lean | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean index f3ad29e6..1ed49556 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean @@ -6,14 +6,32 @@ import SampCert /- Implementation of randomized response for a single user with rational parameter 0 ≤ lambda ≤ 1/2 -/ -def RRRandomizer (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : SLang Bool := do +/- def RRRandomizer (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : SLang Bool := do let result1 ← SLang.BernoulliSample (2 * num + den) (2 * den) (by linarith) -- note (2 * num + den) / (2 * den) = 1/2 + num/den match result1 with | true => return (query l) -- happens with probability (1/2 + num/den) | false => return ¬(query l) -- happens with probability (1/2 - num/den) -lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h : 2 * num < den): +-/ + +/- What return actually does is that it returns that Dirac distribution that is either all true or all false... + So this is wrong? +-/ + +/- Note that this doesn't work: -/ + +/- def RRRandomizertest (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : Bool -> ENNReal := + fun b => + let result1 := SLang.BernoulliSample (2 * num + den) (2 * den) (by + linarith) + match b with + | true => query l + | false => ¬ query l + +-/ + +/- lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h : 2 * num < den): HasSum (RRRandomizer query l num den h) 1 := by simp [RRRandomizer] simp [(Summable.hasSum_iff ENNReal.summable), tsum_bool, add_tsub_cancel_iff_le] @@ -24,6 +42,8 @@ lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) def RRRandomizerPMF (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : PMF Bool := ⟨RRRandomizer query l num den h, RRRandomizerpmf_lemma query l num den h⟩ +-/ + /-- THERE ARE STILL ISSUES BELOW THIS LINE. DO NOT EDIT YET. -/ /- def mapper (n : Nat) (num : Nat) (den : PNat) (h : 2 * num < den) (_ : Fin n) : SLang Bool := do @@ -60,27 +80,34 @@ noncomputable def output_probabilities {T : Type} [DecidableEq T] (num : ℕ) (d fun l' => let diff := count_differences (List.map (fun x => x) l) l' let same := l.length - diff - ENNReal.ofReal (((2 * num + den : ℝ) / (2 * den : ℝ)) ^ diff * ((den - 2 * num : ℝ) / (2 * den : ℝ)) ^ same) - /-- The output_probabilities function describes a function from List Bool to ENNReal, - but SLang (List Bool) is a function List Bool → ENNReal (i.e., a probability mass function). - To return SLang (List T), you should define a function of type List T → ENNReal. - If you want to return SLang (List T), you can define: -/ + (((2 * num + den) / (2 * den)) ^ diff * ((den - 2 * num) / (2 * den)) ^ same : ENNReal) + noncomputable def output_probabilities_SLang {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : SLang (List T) := fun l' => let diff := count_differences l l' let same := l.length - diff ENNReal.ofReal (((2 * num + den : ℝ) / (2 * den : ℝ)) ^ diff * ((den - 2 * num : ℝ) / (2 * den : ℝ)) ^ same) -noncomputable def RandomizedResponseSample {T : Type} [DecidableEq T] (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (SLang (List Bool)) := - output_probabilities num den h (List.map query l) +noncomputable def RandomizedResponseSample {T : Type} [DecidableEq T] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := + ⟨output_probabilities num den h (List.map query l), by sorry⟩ -def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do - RRRandomizer query l 1 4 (by decide) /- Randomized Response with parameter 1/4 -/ +lemma log_arith: Real.log 3 ≥ 0 := Real.log_nonneg (by linarith) + +lemma RRPureDP {T : Type} [DecidableEq T] (query : T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den): SLang.PureDP (RandomizedResponseSample query num den h) ((Real.log 3).toNNReal) := + by + rw[SLang.PureDP] + rw[SLang.DP] + intro l₁ l₂ hl₁l₂ S + /- Study SampCert PureDP proofs -/ + sorry -lemma coinflippmf_lemma (query: T -> Bool) (l: T) : HasSum (CoinFlipSample query l) 1 := - RRRandomizerpmf_lemma query l 1 4 (by decide) + + +/- def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do + RRRandomizer query l 1 4 (by decide) /- Randomized Response with parameter 1/4 -/ def CoinFlipPMF (query : T -> Bool) (l : T) : PMF Bool := - ⟨CoinFlipSample query l, coinflippmf_lemma query l⟩ + ⟨CoinFlipSample query l, RRRandomizerpmf_lemma query l 1 4 (by decide)⟩ /- need randomized response as a mechanism... -/ +-/ From 4c21f3bc0f8167a703e37386746417c661f13aeb Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 2 Jul 2025 13:55:44 -0700 Subject: [PATCH 005/216] newRR --- .../Local/RandomizedResponse.lean | 18 +++++++------ .../Local/RandomizedResponseAlt.lean | 26 +++++++++++++++++++ SampCert/Samplers/Bernoulli/Code.lean | 1 - 3 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean index 1ed49556..b12b881a 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean @@ -76,31 +76,33 @@ def count_differences {T : Type} [DecidableEq T]: List T -> List T -> ℕ count_differences xs ys else 1 + count_differences xs ys -noncomputable def output_probabilities {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : List T → ENNReal := +noncomputable def output_probabilities {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : SLang (List T) := fun l' => let diff := count_differences (List.map (fun x => x) l) l' let same := l.length - diff - (((2 * num + den) / (2 * den)) ^ diff * ((den - 2 * num) / (2 * den)) ^ same : ENNReal) - -noncomputable def output_probabilities_SLang {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : SLang (List T) := - fun l' => - let diff := count_differences l l' - let same := l.length - diff - ENNReal.ofReal (((2 * num + den : ℝ) / (2 * den : ℝ)) ^ diff * ((den - 2 * num : ℝ) / (2 * den : ℝ)) ^ same) + (((2 * num + den) / (2 * den)) ^ same * ((2 * num - den) / (2 * den)) ^ diff : ENNReal) noncomputable def RandomizedResponseSample {T : Type} [DecidableEq T] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨output_probabilities num den h (List.map query l), by sorry⟩ lemma log_arith: Real.log 3 ≥ 0 := Real.log_nonneg (by linarith) +lemma log_arith1: Real.exp (Real.log 3) = 3 := Real.exp_log (by linarith) + +lemma log_arith2: ENNReal.ofReal (Real.exp ↑(Real.log 3).toNNReal) = ENNReal.ofReal 3 := by sorry + lemma RRPureDP {T : Type} [DecidableEq T] (query : T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den): SLang.PureDP (RandomizedResponseSample query num den h) ((Real.log 3).toNNReal) := by rw[SLang.PureDP] rw[SLang.DP] intro l₁ l₂ hl₁l₂ S + /- Current bound is not correct of course, should depend on num/den, this is just for illustration -/ /- Study SampCert PureDP proofs -/ sorry +noncomputable def RRImplementation {T : Type} [DecidableEq T] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T): SLang (List Bool) := do + let r ← output_probabilities num den h (List.map query l) + return r /- def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean new file mode 100644 index 00000000..69ba04ce --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -0,0 +1,26 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert + +lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by + simp_all only [tsub_le_iff_right] + linarith + +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do +/- RRSingleSample takes in a single user and produces a sample according to the distribution + induced by the user's actual response. + If the user's actual response to the query is "true", then RRSingleSample samples "true" + with probability 1/2 + num/den. If the user's actual response to the query is "false," then RRSingleSample + samples true with probability 1/2 - num/den. +-/ + match query l with + | true => let r ← SLang.BernoulliSample (den + 2 * num) (2 * den) (by linarith) + return r + | false => let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) + return r + +def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ + l.mapM (fun x => RRSingleSample query num den h x) + +/- At this point, we should be set to prove that RRSample is normalized and that it is + differentially private. The definition is computable, as we need. -/ diff --git a/SampCert/Samplers/Bernoulli/Code.lean b/SampCert/Samplers/Bernoulli/Code.lean index 0b1712f6..10c3bad6 100644 --- a/SampCert/Samplers/Bernoulli/Code.lean +++ b/SampCert/Samplers/Bernoulli/Code.lean @@ -24,5 +24,4 @@ def BernoulliSample (num : Nat) (den : PNat) (_ : num ≤ den) : SLang Bool := d let d ← UniformSample den return d < num -#check BernoulliSample 1 2 (by decide) end SLang From 0bdc19ee8e3aa6fd1563592b18dc18cffa07f20d Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 3 Jul 2025 11:13:53 -0700 Subject: [PATCH 006/216] 73 --- .../Local/ENNRealCoercions.lean | 24 +++++++ .../Local/RandomizedResponseAlt.lean | 62 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean b/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean index eb8a17c1..5ad7628f 100644 --- a/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean +++ b/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean @@ -50,3 +50,27 @@ lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): simp sorry } + + +lemma arith_3b (num : Nat) (den : PNat) (h : 2 * num < den) : + (2 : ENNReal) * num < den := by + +lemma arith_3c (num : Nat) (den : PNat) (h : 2 * num < den): + (den - (2 : ENNReal) * num) / (2 * den) < 1 := by + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] + apply arith_3b at h + norm_num at h + sorry + +lemma arith_3 (num: Nat) (den: PNat) (h : 2 * num < den): +1 - ((den : ENNReal) - 2 * num) / (2 * den) + (den - 2 * num) / (2 * den) = 1 := + by + apply arith_0 + apply div_not_top + {simp} + {simp + exact Nat.not_eq_zero_of_lt h + } + { apply arith_3c + exact h + } diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean index 69ba04ce..0530f798 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -1,5 +1,7 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert +import SampCert.Samplers.Bernoulli.Properties +/- import SampCert.DifferentialPrivacy.Local.ENNRealCoercions -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] @@ -18,9 +20,69 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 | false => let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return r +def RRSingleSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do + let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) + return Bool.xor (query l) r + def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) +def RRSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ + l.mapM (fun x => RRSingleSample2 query num den h x) + +lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : + HasSum (RRSingleSample2 query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw [@tsum_bool] + rw[RRSingleSample2] + cases query l + { + simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + } + { + simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', + mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + rw [@AddCommMonoidWithOne.add_comm] + } + +lemma nil_case {T : Type} (query : T -> Bool) (num : Nat) (den: PNat) (h: 2 * num ≤ den): + ∑' (b : List Bool), (RRSample2 query num den h) [] b = 1 := by + have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample2 query num den h x) [] b = + mapM (fun x => RRSingleSample2 query num den h x) [] [] := by + rw [@List.mapM_nil] + simp_all only [pure, SLang.pure_apply, ↓reduceIte] + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] + rw[RRSample2] + rw[h1] + rw [@List.mapM_nil] + simp + +lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * num ≤ den) + (hd : T) (tl : List T): ∑' (b : List Bool), RRSample2 query num den h (hd :: tl) b = + ∑' (b : List Bool), RRSample2 query num den h (hd :: tl) b = 1 + sorry + + +lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : + HasSum (RRSample2 query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + induction l.length with + | zero => have h1: l.length = 0 := b + sorry + /- exact nil_case query num den h -/ + | succ n ha => exact ha + /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ +def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := + ⟨RRSample2 query num den h l, RRSample2_PMF_helper query num den h l⟩ From dc6b514ddc6f5356f34beffd6c44a3d9055aeb6c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 7 Jul 2025 13:20:24 -0700 Subject: [PATCH 007/216] generalbernoulli --- .../Local/MultiBernoulli.lean | 45 ++++++++++++++ .../Local/RandomizedResponseAlt.lean | 61 +++++++------------ 2 files changed, 68 insertions(+), 38 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean new file mode 100644 index 00000000..ecc42625 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -0,0 +1,45 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert + +namespace SLang + +structure SeedType where + n : Nat + d : PNat + h : n ≤ d + +def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := + seeds.mapM (fun s => SLang.BernoulliSample s.n s.d s.h) + +#check @ENNReal.tsum_eq_iSup_sum +#check BernoulliSample_apply +#check BernoulliSample_normalizes + +/- We'll need a proof that the MultiBernoulliSample applied to a single-element + list is the same thing as the usual BernoulliSample -/ + +lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by + rw [MultiBernoulliSample] + rw [ENNReal.tsum_eq_add_tsum_ite] + sorry + + +lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = + (∑' (b : List Bool), MultiBernoulliSample [hd] b) * ∑' (b : List Bool), MultiBernoulliSample tl b := by + sorry + +lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : + HasSum (MultiBernoulliSample seeds) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + induction seeds with + | nil => rw [MultiBernoulliSample] + rw [@List.mapM_nil] + simp[pure] + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] + | cons hd tl ih => + rw [MultiBernoulli_independence hd tl] + rw [ih] + rw [@CanonicallyOrderedCommSemiring.mul_one] + rw[MultiBernoulli_single_list] diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean index 0530f798..931ecb82 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -1,42 +1,31 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert -import SampCert.Samplers.Bernoulli.Properties /- import SampCert.DifferentialPrivacy.Local.ENNRealCoercions -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do -/- RRSingleSample takes in a single user and produces a sample according to the distribution - induced by the user's actual response. - If the user's actual response to the query is "true", then RRSingleSample samples "true" - with probability 1/2 + num/den. If the user's actual response to the query is "false," then RRSingleSample - samples true with probability 1/2 - num/den. --/ - match query l with - | true => let r ← SLang.BernoulliSample (den + 2 * num) (2 * den) (by linarith) - return r - | false => let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) - return r - -def RRSingleSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ +/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) -def RRSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ - l.mapM (fun x => RRSingleSample2 query num den h x) +def RRSample2 {T : Type} (query : T -> Bool) (s : SeedType /- fix -/): SLang (List Bool) := do + sorry +/- At this point, we should be set to prove that RRSample is normalized and that it is + differentially private. The definition is computable, as we need. -/ + +#check SLang.BernoulliSample_normalizes lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : - HasSum (RRSingleSample2 query num den h l) 1 := by + HasSum (RRSingleSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] rw [@tsum_bool] - rw[RRSingleSample2] + rw[RRSingleSample] cases query l { simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, @@ -55,34 +44,30 @@ lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den } lemma nil_case {T : Type} (query : T -> Bool) (num : Nat) (den: PNat) (h: 2 * num ≤ den): - ∑' (b : List Bool), (RRSample2 query num den h) [] b = 1 := by - have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample2 query num den h x) [] b = - mapM (fun x => RRSingleSample2 query num den h x) [] [] := by + ∑' (b : List Bool), (RRSample query num den h) [] b = 1 := by + have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample query num den h x) [] b = + mapM (fun x => RRSingleSample query num den h x) [] [] := by rw [@List.mapM_nil] simp_all only [pure, SLang.pure_apply, ↓reduceIte] rw [ENNReal.tsum_eq_add_tsum_ite []] simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] - rw[RRSample2] + rw[RRSample] rw[h1] rw [@List.mapM_nil] simp lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * num ≤ den) - (hd : T) (tl : List T): ∑' (b : List Bool), RRSample2 query num den h (hd :: tl) b = - ∑' (b : List Bool), RRSample2 query num den h (hd :: tl) b = 1 - sorry - + (l : List T) : ∑' (b : List Bool), RRSample query num den h l b = 1 := by + rw[RRSample] + sorry lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : - HasSum (RRSample2 query num den h l) 1 := by + HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] - induction l.length with - | zero => have h1: l.length = 0 := b - sorry - /- exact nil_case query num den h -/ - | succ n ha => exact ha + rw[RRSample] + induction l with + | nil => exact nil_case query num den h + | cons hd tl tail_ih => sorry -/- At this point, we should be set to prove that RRSample is normalized and that it is - differentially private. The definition is computable, as we need. -/ def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := - ⟨RRSample2 query num den h l, RRSample2_PMF_helper query num den h l⟩ + ⟨RRSample query num den h l, RRSample2_PMF_helper query num den h l⟩ From e2429d6636ea08efcc9600fb686eab43bd7f5d29 Mon Sep 17 00:00:00 2001 From: Renee Date: Tue, 8 Jul 2025 12:12:35 -0400 Subject: [PATCH 008/216] MultiBernoulli_single_list proof --- .../Local/MultiBernoulli.lean | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index ecc42625..0bcc3218 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -19,9 +19,20 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := list is the same thing as the usual BernoulliSample -/ lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by - rw [MultiBernoulliSample] - rw [ENNReal.tsum_eq_add_tsum_ite] - sorry + rw [MultiBernoulliSample] + rw [List.mapM_cons, List.mapM_nil] + rcases hd with ⟨n, d, h⟩ + simp only [pure, bind] + simp_all only [pure_bind, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] + rw [@ENNReal.tsum_comm] + rw [tsum_bool] + simp_all only [Bool.false_eq_true, ↓reduceIte, tsum_ite_eq] + rw[←tsum_bool] + rw [BernoulliSample_normalizes] + sorry + + + lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): @@ -30,8 +41,7 @@ lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): sorry lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : - HasSum (MultiBernoulliSample seeds) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] + ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by induction seeds with | nil => rw [MultiBernoulliSample] rw [@List.mapM_nil] @@ -41,5 +51,5 @@ lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : | cons hd tl ih => rw [MultiBernoulli_independence hd tl] rw [ih] - rw [@CanonicallyOrderedCommSemiring.mul_one] - rw[MultiBernoulli_single_list] + rw [MultiBernoulli_single_list hd] + rw [one_mul] From d7f6788bf9c5fd97f3b76841ba402d80377f38b8 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 8 Jul 2025 09:12:40 -0700 Subject: [PATCH 009/216] update --- .../Local/LawfulMonadSLang.lean | 34 ++++++++++ .../Local/MultiBernoulli.lean | 68 ++++++++++++++++--- .../Local/RandomizedResponseAlt.lean | 27 ++++++-- 3 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean diff --git a/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean b/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean new file mode 100644 index 00000000..b3f896a6 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean @@ -0,0 +1,34 @@ +import SampCert + +open SLang + +instance SLang.LawfulMonad : LawfulMonad SLang where + map_const := by + intro a b + rfl + id_map := by + intro u hu + rw [@Function.funext_iff] + intro a + unfold id + sorry + seqLeft_eq := by + intro α β x y + sorry + seqRight_eq := by + intro α β x y + sorry + pure_seq := by + intro α β g x + simp_all only [pure] + sorry + pure_bind := by simp + bind_pure_comp := by + intro α β f x + simp_all only [bind, pure] + apply Eq.refl + bind_assoc := by simp + bind_map := by + intro α β f x + simp_all only [bind] + apply Eq.refl diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index ecc42625..df8f0d9a 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -1,20 +1,34 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Probability.Independence.Basic import SampCert -namespace SLang +namespace MultiBernoulli structure SeedType where n : Nat d : PNat h : n ≤ d +def mapper_funct: SeedType -> SLang Bool := + fun s => SLang.BernoulliSample s.n s.d s.h + +noncomputable def explicit_sample (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := + match b with + | [] => 0 + | x :: xs => mapper_funct hd x * (mapM mapper_funct tl) xs + def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := - seeds.mapM (fun s => SLang.BernoulliSample s.n s.d s.h) + seeds.mapM mapper_funct + +#check MultiBernoulliSample [SeedType.mk 1 2 (by decide), SeedType.mk 1 2 (by decide)] [true, false] #check @ENNReal.tsum_eq_iSup_sum -#check BernoulliSample_apply -#check BernoulliSample_normalizes +#check SLang.BernoulliSample_apply +#check SLang.BernoulliSample_normalizes +#check List.mapM_cons +#check SLang +#check List.mapM_cons /- We'll need a proof that the MultiBernoulliSample applied to a single-element list is the same thing as the usual BernoulliSample -/ @@ -22,16 +36,39 @@ lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBer rw [MultiBernoulliSample] rw [ENNReal.tsum_eq_add_tsum_ite] sorry + sorry + +lemma mapper_funct_neq_iff (l : List SeedType) (b : List Bool) : + mapM mapper_funct l [] = + match l with + | [] => 1 + | _ => 0 := by + cases l with + | nil => simp[-mapM] + | cons hd tl => simp[-mapM] + sorry + +lemma MultiBernoulliSampler_recurrence (hd : SeedType) (tl : List SeedType) (b : List Bool): + mapM mapper_funct (hd :: tl) b += explicit_sample hd tl b := by + unfold explicit_sample + rw[List.mapM_cons] + aesop + sorry + +lemma test_2 (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), mapM mapper_funct (hd :: tl) b = ∑' (b : List Bool), explicit_sample hd tl b := by + sorry lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = (∑' (b : List Bool), MultiBernoulliSample [hd] b) * ∑' (b : List Bool), MultiBernoulliSample tl b := by + rw [MultiBernoulliSample] sorry lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : - HasSum (MultiBernoulliSample seeds) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] + ∑' (b : List Bool), MultiBernoulliSample seeds b = 1 := by induction seeds with | nil => rw [MultiBernoulliSample] rw [@List.mapM_nil] @@ -41,5 +78,20 @@ lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : | cons hd tl ih => rw [MultiBernoulli_independence hd tl] rw [ih] - rw [@CanonicallyOrderedCommSemiring.mul_one] - rw[MultiBernoulli_single_list] + rw[MultiBernoulli_single_list hd] + rw [one_mul] + + +noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := + fun s => ∑' (t : T), if f t = s then p t else 0 + +lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : + ∑' (s : S), (push_forward p f) s = 1 := by + simp [push_forward] + rw [@ENNReal.tsum_comm] + have h1: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by + intro b + sorry + simp_all + +end MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean index 931ecb82..8396c5fb 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -1,6 +1,9 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert -/- import SampCert.DifferentialPrivacy.Local.ENNRealCoercions -/ +import SampCert.DifferentialPrivacy.Local.MultiBernoulli + +open SLang +open MultiBernoulli lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] @@ -14,14 +17,15 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) -def RRSample2 {T : Type} (query : T -> Bool) (s : SeedType /- fix -/): SLang (List Bool) := do - sorry +def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do + let r ← MultiBernoulliSample seed_list + return List.zipWith (fun u s => Bool.xor (query u) s) l r /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ #check SLang.BernoulliSample_normalizes -lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : +lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : HasSum (RRSingleSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] rw [@tsum_bool] @@ -61,7 +65,7 @@ lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * n rw[RRSample] sorry -lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : +lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] rw[RRSample] @@ -69,5 +73,16 @@ lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat | nil => exact nil_case query num den h | cons hd tl tail_ih => sorry +lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : + HasSum (RRSample2 query s l) 1 := by + rw[RRSample2] + simp_all only [bind, pure] + rw[Summable.hasSum_iff ENNReal.summable] + rw[←MultiBernoulliSample_normalizes s] + simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] + sorry + + + def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := - ⟨RRSample query num den h l, RRSample2_PMF_helper query num den h l⟩ + ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ From fedde795366741ffdbce710d61346f170c29ca56 Mon Sep 17 00:00:00 2001 From: Renee Date: Tue, 8 Jul 2025 13:04:35 -0400 Subject: [PATCH 010/216] SLang is Lawful --- .../DifferentialPrivacy/Local/LawfulMonadSLang.lean | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean b/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean index b3f896a6..a2f96e32 100644 --- a/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean +++ b/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean @@ -8,20 +8,17 @@ instance SLang.LawfulMonad : LawfulMonad SLang where rfl id_map := by intro u hu - rw [@Function.funext_iff] - intro a - unfold id - sorry + simp [Functor.map] seqLeft_eq := by intro α β x y - sorry + simp [SeqLeft.seqLeft, Seq.seq, Functor.map] + apply Eq.refl seqRight_eq := by intro α β x y - sorry + simp [SeqRight.seqRight, Seq.seq, Functor.map] pure_seq := by intro α β g x - simp_all only [pure] - sorry + simp [Seq.seq, Functor.map] pure_bind := by simp bind_pure_comp := by intro α β f x From 25c764add9c30ce81c378c28274e3c5b736180d6 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 8 Jul 2025 10:14:06 -0700 Subject: [PATCH 011/216] lawfulmonadexcitement --- .../Local/MultiBernoulli.lean | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index a2289483..5664009a 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -9,16 +9,16 @@ structure SeedType where d : PNat h : n ≤ d -def mapper_funct: SeedType -> SLang Bool := +def bernoulli_mapper: SeedType -> SLang Bool := fun s => SLang.BernoulliSample s.n s.d s.h -noncomputable def explicit_sample (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := +noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := match b with | [] => 0 - | x :: xs => mapper_funct hd x * (mapM mapper_funct tl) xs + | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := - seeds.mapM mapper_funct + seeds.mapM bernoulli_mapper #check MultiBernoulliSample [SeedType.mk 1 2 (by decide), SeedType.mk 1 2 (by decide)] [true, false] @@ -27,8 +27,9 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := #check SLang.BernoulliSample_normalizes #check List.mapM_cons #check SLang +#check List.mapM_nil +#check tsum_eq_single -#check List.mapM_cons /- We'll need a proof that the MultiBernoulliSample applied to a single-element list is the same thing as the usual BernoulliSample -/ @@ -42,37 +43,56 @@ lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBer rw [tsum_bool] simp_all only [Bool.false_eq_true, ↓reduceIte, tsum_ite_eq] rw[←tsum_bool] - rw[mapper_funct] + rw[bernoulli_mapper] rw [SLang.BernoulliSample_normalizes] sorry -lemma mapper_funct_neq_iff (l : List SeedType) (b : List Bool) : - mapM mapper_funct l [] = +lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : + mapM bernoulli_mapper l [] = match l with | [] => 1 - | _ => 0 := by + | hd :: tl => 0 := by cases l with | nil => simp[-mapM] | cons hd tl => simp[-mapM] sorry - -lemma MultiBernoulliSampler_recurrence (hd : SeedType) (tl : List SeedType) (b : List Bool): - mapM mapper_funct (hd :: tl) b -= explicit_sample hd tl b := by - unfold explicit_sample +lemma multi_bernoulli_explicit (hd : SeedType) (tl : List SeedType) (b : List Bool): + mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by + unfold explicit_prob rw[List.mapM_cons] - aesop + simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] + split + next b => simp_all only [↓reduceIte, tsum_zero, mul_zero] + next b x xs => + simp_all only [List.cons.injEq] + rw[tsum_bool] + cases x with + | false => simp[-mapM] + rw[tsum_eq_single xs] + simp_all + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + | true => simp[-mapM] + rw[tsum_eq_single xs] + simp_all + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] sorry -lemma test_2 (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), mapM mapper_funct (hd :: tl) b = ∑' (b : List Bool), explicit_sample hd tl b := by - sorry +lemma multi_bernoulli_explicit_sum (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by + simp_all [multi_bernoulli_explicit, -mapM] lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = (∑' (b : List Bool), MultiBernoulliSample [hd] b) * ∑' (b : List Bool), MultiBernoulliSample tl b := by - rw [MultiBernoulliSample] sorry lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : @@ -99,7 +119,13 @@ lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : rw [@ENNReal.tsum_comm] have h1: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by intro b - sorry + rw [tsum_eq_single (f b)] + simp + intro b' a + simp_all only [ne_eq, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] simp_all end MultiBernoulli From 0cad6a275ef5fa1d2fa46d0da71918e6d5dc44e4 Mon Sep 17 00:00:00 2001 From: Renee Date: Tue, 8 Jul 2025 13:14:49 -0400 Subject: [PATCH 012/216] param Lawful --- SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index a2289483..7f79c28b 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -1,6 +1,7 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import Mathlib.Probability.Independence.Basic import SampCert +import SampCert.SLang namespace MultiBernoulli @@ -32,7 +33,7 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := /- We'll need a proof that the MultiBernoulliSample applied to a single-element list is the same thing as the usual BernoulliSample -/ -lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by +lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by rw [MultiBernoulliSample] rw [List.mapM_cons, List.mapM_nil] rcases hd with ⟨n, d, h⟩ @@ -44,7 +45,6 @@ lemma MultiBernoulli_single_list (hd : SeedType): ∑' (b : List Bool), MultiBer rw[←tsum_bool] rw[mapper_funct] rw [SLang.BernoulliSample_normalizes] - sorry lemma mapper_funct_neq_iff (l : List SeedType) (b : List Bool) : mapM mapper_funct l [] = From a4dbf28fd77053144d4390bcf8659a56daf6a669 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 8 Jul 2025 10:50:07 -0700 Subject: [PATCH 013/216] updatenames --- .../Local/MultiBernoulli.lean | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index a24a0923..3e77f0de 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -57,7 +57,7 @@ lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : | cons hd tl => simp[-mapM] sorry -lemma multi_bernoulli_explicit (hd : SeedType) (tl : List SeedType) (b : List Bool): +lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by unfold explicit_prob rw[List.mapM_cons] @@ -84,18 +84,33 @@ lemma multi_bernoulli_explicit (hd : SeedType) (tl : List SeedType) (b : List Bo intro a_1 subst a_1 simp_all only [not_true_eq_false] - sorry -lemma multi_bernoulli_explicit_sum (hd : SeedType) (tl : List SeedType): +lemma multi_bernoulli_explicit_sum [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by simp_all [multi_bernoulli_explicit, -mapM] -lemma MultiBernoulli_independence (hd : SeedType) (tl : List SeedType): +def simplify_expression (hd : SeedType) (tl : List SeedType) (x : List Bool): ENNReal := + if x = [] then (0 : ENNReal) + else + match x with + | [] => (0 : ENNReal) + | x :: xs => bernoulli_mapper hd x * mapM bernoulli_mapper tl xs + +lemma MultiBernoulli_independence [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = (∑' (b : List Bool), MultiBernoulliSample [hd] b) * ∑' (b : List Bool), MultiBernoulliSample tl b := by + unfold MultiBernoulliSample + rw [multi_bernoulli_explicit_sum] + unfold explicit_prob + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [zero_add] + rw? + + + sorry -lemma MultiBernoulliSample_normalizes (seeds : List SeedType) : +lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by induction seeds with | nil => rw [MultiBernoulliSample] From 63339f41ca3d71373ba94f4736f4bcd9b6b70e20 Mon Sep 17 00:00:00 2001 From: Renee Date: Tue, 8 Jul 2025 16:38:21 -0400 Subject: [PATCH 014/216] bernoulli_helper --- .../DifferentialPrivacy/Local/MultiBernoulli.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 3e77f0de..99cecf2b 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -3,6 +3,7 @@ import Mathlib.Probability.Independence.Basic import SampCert import SampCert.SLang + namespace MultiBernoulli structure SeedType where @@ -47,6 +48,18 @@ lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : rw[bernoulli_mapper] rw [SLang.BernoulliSample_normalizes] +lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : bernoulli_mapper hd_1 hd = mapM bernoulli_mapper [hd_1] [hd] := by + rw [List.mapM_cons, List.mapM_nil] + rcases hd_1 with ⟨n, d, h⟩ + simp only [pure, bind] + simp_all only [SLang.pure_bind, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] + simp_all only [List.cons.injEq, and_true] + cases hd with + | true => simp_all only [Bool.true_eq, tsum_ite_eq] + | false => simp_all only [Bool.false_eq, tsum_ite_eq, tsum_zero, mul_zero] + + + lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : mapM bernoulli_mapper l [] = match l with From 11f0e9c2025604e3c92dee6dfbf27b5a15367330 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 8 Jul 2025 16:10:25 -0700 Subject: [PATCH 015/216] struggling --- .../Local/MultiBernoulli.lean | 103 +++++++++++++++--- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 3e77f0de..19617f4d 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -13,11 +13,43 @@ structure SeedType where def bernoulli_mapper: SeedType -> SLang Bool := fun s => SLang.BernoulliSample s.n s.d s.h +lemma bernoulli_mapper_sums_to_1 (s : SeedType): ∑' (b : Bool), bernoulli_mapper s b = 1 := by + rw[bernoulli_mapper] + exact SLang.BernoulliSample_normalizes s.n s.d s.h + noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := match b with | [] => 0 | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs +lemma bernoulli_mapper_empty (b : List Bool): mapM bernoulli_mapper [] b = if b = [] then 1 else 0 := by + rw [@List.mapM_nil] + simp_all only [pure, SLang.pure_apply, ite_eq_right_iff, one_ne_zero, imp_false] + split + next h => + subst h + simp_all only + next h => simp_all only + +lemma explicit_prob_nonempty (hd : SeedType) (tl : List SeedType) (b : List Bool) (h : b ≠ []): + explicit_prob hd tl b = bernoulli_mapper hd (b.head h) * (mapM bernoulli_mapper tl b.tail) := by + rw[explicit_prob] + cases b with + | nil => contradiction + | cons => + rename_i head tail + simp_all only [List.head_cons, List.tail_cons] + +lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob hd tl b = 1 := by + induction tl with + | nil => sorry + | cons => sorry + + + + + def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := seeds.mapM bernoulli_mapper @@ -47,7 +79,7 @@ lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : rw[bernoulli_mapper] rw [SLang.BernoulliSample_normalizes] -lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : +/- lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : mapM bernoulli_mapper l [] = match l with | [] => 1 @@ -56,7 +88,7 @@ lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : | nil => simp[-mapM] | cons hd tl => simp[-mapM] sorry - +-/ lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by unfold explicit_prob @@ -89,40 +121,75 @@ lemma multi_bernoulli_explicit_sum [LawfulMonad SLang] (hd : SeedType) (tl : Lis ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by simp_all [multi_bernoulli_explicit, -mapM] -def simplify_expression (hd : SeedType) (tl : List SeedType) (x : List Bool): ENNReal := +/- noncomputable def unsimplified_expression (hd : SeedType) (tl : List SeedType) (x : List Bool): ENNReal := if x = [] then (0 : ENNReal) else match x with | [] => (0 : ENNReal) - | x :: xs => bernoulli_mapper hd x * mapM bernoulli_mapper tl xs - -lemma MultiBernoulli_independence [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): + | x :: xs => bernoulli_mapper hd x * mapM bernoulli_mapper tl xs -/ + +/- lemma simplify_unsimplified (hd : SeedType) (tl : List SeedType) (x : List Bool) (h : x ≠ []): + unsimplified_expression hd tl x = + bernoulli_mapper hd (x.head h) * mapM bernoulli_mapper tl x.tail := by + unfold unsimplified_expression + simp_all only [↓reduceIte, mapM] + split + next b => + simp_all only [List.tail_nil, zero_eq_mul] + apply Or.inr + simp_all only [ne_eq, not_true_eq_false] + next b x xs => simp_all only [List.head_cons, List.tail_cons] -/ + +/- lemma list_bool_tsum_rw {T : Type} [DecidableEq T] [AddCommMonoid T] [TopologicalSpace T] (f : List Bool -> T): + ∑' (b : List Bool), f b = f [] + ∑' (b : {b : List Bool // b ≠ []}), f b := by + sorry -/ + +lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f g : α -> β) (h: ∀i : α, f i = g i ): + ∑' (i : α), f i = ∑' (i : α), g i := by simp_all + +/- lemma multibernoulli_of_zero [LawfulMonad SLang] (seed: SeedType): + MultiBernoulliSample [seed] [] = 0 := by + rw [MultiBernoulliSample] + rw [List.mapM_cons] + simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, ↓reduceIte, mul_zero, tsum_zero] -/ + +/- lemma MultiBernoulli_independence_single [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): + MultiBernoulliSample (hd :: tl) b = if assm: b ≠ [] then MultiBernoulliSample [hd] [b.head (by assumption)] * MultiBernoulliSample tl (b.tail) else 0 := by + unfold MultiBernoulliSample + cases b with + | nil => simp [-mapM] + | cons hd tl => + rename_i inst hd_1 tl_1 + simp_all only [ne_eq, not_false_eq_true, ↓reduceDIte, List.head_cons, List.tail_cons] + sorry -/ + +/- lemma MultiBernoulli_independence [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = - (∑' (b : List Bool), MultiBernoulliSample [hd] b) * ∑' (b : List Bool), MultiBernoulliSample tl b := by + ∑' (b : List Bool), if assm: b ≠ [] then MultiBernoulliSample [hd] [b.head (by assumption)] * MultiBernoulliSample tl (b.tail) else 0 := by unfold MultiBernoulliSample rw [multi_bernoulli_explicit_sum] unfold explicit_prob rw [ENNReal.tsum_eq_add_tsum_ite []] simp_all only [zero_add] - rw? - - - - sorry + rw[tsum_equal_comp] + intro b + cases b with + | nil => simp [-mapM] + | cons hd tl => + rename_i inst hd_1 tl_1 + simp_all only [↓reduceIte, ne_eq, not_false_eq_true, ↓reduceDIte, List.head_cons, List.tail_cons] + sorry -/ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by + rw [MultiBernoulliSample] induction seeds with - | nil => rw [MultiBernoulliSample] - rw [@List.mapM_nil] + | nil => rw [@List.mapM_nil] simp[pure] rw [ENNReal.tsum_eq_add_tsum_ite []] simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] | cons hd tl ih => - rw [MultiBernoulli_independence hd tl] - rw [ih] - rw[MultiBernoulli_single_list hd] - rw [one_mul] + rw [@multi_bernoulli_explicit_sum] noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := From e9d93e2c0a6efe91792d3b395f8c4add312a2fe3 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 9 Jul 2025 10:11:20 -0700 Subject: [PATCH 016/216] tryingagain --- .../Local/MultiBernoulli.lean | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index f6468f67..b3c91319 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -23,6 +23,14 @@ noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List B | [] => 0 | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs +noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := + match b with + | [] => 0 + | x :: xs => bernoulli_mapper hd x * + match tl with + | [] => if xs = [] then 1 else 0 + | tl_hd :: tl_tl => explicit_prob2 tl_hd tl_tl xs + lemma bernoulli_mapper_empty (b : List Bool): mapM bernoulli_mapper [] b = if b = [] then 1 else 0 := by rw [@List.mapM_nil] simp_all only [pure, SLang.pure_apply, ite_eq_right_iff, one_ne_zero, imp_false] @@ -41,15 +49,23 @@ lemma explicit_prob_nonempty (hd : SeedType) (tl : List SeedType) (b : List Bool rename_i head tail simp_all only [List.head_cons, List.tail_cons] +lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob hd tl b = + ∑' (b : List Bool), if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * (mapM bernoulli_mapper tl) b.tail else 0 := by + unfold explicit_prob + simp_all only [ne_eq, dite_eq_ite, ite_not] + sorry + lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), explicit_prob hd tl b = 1 := by induction tl with | nil => sorry - | cons => sorry - - - + | cons tl_hd tl_tl ih => rw[explicit_prob_sum_except_empty] + rw[explicit_prob_sum_except_empty] at ih + rw [← ih] + simp_all only [ne_eq, dite_not] + sorry def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := seeds.mapM bernoulli_mapper @@ -63,6 +79,7 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := #check SLang #check List.mapM_nil #check tsum_eq_single +#check tsum_eq_tsum_diff_singleton /- We'll need a proof that the MultiBernoulliSample applied to a single-element list is the same thing as the usual BernoulliSample -/ @@ -90,8 +107,7 @@ lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : berno | true => simp_all only [Bool.true_eq, tsum_ite_eq] | false => simp_all only [Bool.false_eq, tsum_ite_eq, tsum_zero, mul_zero] - - +/- lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : mapM bernoulli_mapper l [] = match l with @@ -203,6 +219,7 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] | cons hd tl ih => rw [@multi_bernoulli_explicit_sum] + sorry noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := From 43e620cc0e6621cb2f97ab7e824a174380fd8685 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 9 Jul 2025 17:14:33 -0700 Subject: [PATCH 017/216] forethan --- .../Local/MultiBernoulli.lean | 87 ++++++++++++++++--- .../Local/RandomizedResponseAlt.lean | 9 +- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index b3c91319..7d52e725 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -6,11 +6,33 @@ import SampCert.SLang namespace MultiBernoulli +/- We define the multivariable Bernoulli distrbution (corresponding to n + independent coin flips) and prove that it normalizes. + The approach for this is to define a function "explicit_prob" that + encapsulates the multiplication of probabilities for the independent + coin flips, and then prove that "MultiBernoulliSample" is equivalent + to "explicit_prob," in the sense that the two have the same output on any + input. Given this, the proof of normalization for the multivariate + Bernoulli distribution reduces to a proof of normalization for the + explicit_prob function. +-/ + structure SeedType where n : Nat d : PNat h : n ≤ d + /- This is just handy shorthand for a rational parameter n/d in [0, 1]-/ + +/- IGNORE-/ +example (b : {b : List Bool // b ≠ []}): b.val.head b.property ∨ ¬ b.val.head b.property := by + cases b.val.head b.property with + | true => left; rfl + | false => right; simp + +/- IGNORE -/ +def silly: {b : List Bool // b ≠ []} := ⟨[true], by decide⟩ +/-IMPORTANT: -/ def bernoulli_mapper: SeedType -> SLang Bool := fun s => SLang.BernoulliSample s.n s.d s.h @@ -23,7 +45,25 @@ noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List B | [] => 0 | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs -noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := +/- EXAMPLE OF WHAT ISN'T WORKING: -/ +lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob hd tl b = 1 := by + induction tl with + | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] + rw[explicit_prob] + simp + unfold explicit_prob + /- FOR ETHAN: At this point I would love to say that + in the else case, the x ≠ [] and so we can just + pattern-match on it...but I don't know how to get Lean + to understand that. -/ + sorry + | cons tl_hd tl_tl ih => sorry +/- If we could get the above proof to work, we would be done...-/ + +/- IGNORE -- after getting stuck with the previous def, I tried to make it + explicitly recursive, and got stuck...but hopefully we don't need it. -/ + noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := match b with | [] => 0 | x :: xs => bernoulli_mapper hd x * @@ -55,23 +95,39 @@ lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): unfold explicit_prob simp_all only [ne_eq, dite_eq_ite, ite_not] sorry - -lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob hd tl b = 1 := by - induction tl with - | nil => sorry - | cons tl_hd tl_tl ih => rw[explicit_prob_sum_except_empty] - rw[explicit_prob_sum_except_empty] at ih - rw [← ih] - simp_all only [ne_eq, dite_not] +lemma tsum_zero (f : List Bool -> ENNReal): + ∑' (b : List Bool), (if b = [] then 0 else f b) = + ∑' (b : List Bool), if assm : b ≠ [] then f b else 0 := by simp_all [ite_not] + +lemma tsum_zero_subtype (f : List Bool -> ENNReal): + ∑' (b : List Bool), f b = f [] + ∑'(b : {b : List Bool // b ≠ []}), f b.val:= by + rw [ENNReal.tsum_eq_add_tsum_ite []] + sorry + +lemma explicit_prob2_sums_to_1 (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob2 hd tl b = 1 := by + induction tl with + | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] + rw[explicit_prob2] + simp + unfold explicit_prob2 + simp_all only [mul_ite, mul_one, mul_zero] + rw [tsum_zero] + sorry + | cons tl_hd tl_tl ih => rw [ENNReal.tsum_eq_add_tsum_ite []] + rw[explicit_prob2] + simp sorry + +/- IMPORTANT -/ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := seeds.mapM bernoulli_mapper #check MultiBernoulliSample [SeedType.mk 1 2 (by decide), SeedType.mk 1 2 (by decide)] [true, false] +/- USEFUL LEMMAS: -/ #check @ENNReal.tsum_eq_iSup_sum #check SLang.BernoulliSample_apply #check SLang.BernoulliSample_normalizes @@ -81,8 +137,7 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := #check tsum_eq_single #check tsum_eq_tsum_diff_singleton -/- We'll need a proof that the MultiBernoulliSample applied to a single-element - list is the same thing as the usual BernoulliSample -/ +/- The following theorems might be useful, but are not used in the current proof -/ lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by rw [MultiBernoulliSample] @@ -118,6 +173,8 @@ lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : | cons hd tl => simp[-mapM] sorry -/ + +/- IMPORTANT: Here we prove that explicit_prob is the same as MultiBernoulliSample-/ lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by unfold explicit_prob @@ -125,6 +182,7 @@ lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List Se simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] split next b => simp_all only [↓reduceIte, tsum_zero, mul_zero] + simp next b x xs => simp_all only [List.cons.injEq] rw[tsum_bool] @@ -209,6 +267,7 @@ lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f simp_all only [↓reduceIte, ne_eq, not_false_eq_true, ↓reduceDIte, List.head_cons, List.tail_cons] sorry -/ +/- This can be proven by showing that explicit_prob normalizes. -/ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by rw [MultiBernoulliSample] @@ -217,10 +276,14 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType simp[pure] rw [ENNReal.tsum_eq_add_tsum_ite []] simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] + simp | cons hd tl ih => rw [@multi_bernoulli_explicit_sum] sorry +/- The rest of this file can be ignored. It states that the push-forward + of a probability measure is a probability measure, which we don't + need for now. -/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := fun s => ∑' (t : T), if f t = s then p t else 0 diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean index 8396c5fb..99813aed 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -1,9 +1,9 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert -import SampCert.DifferentialPrivacy.Local.MultiBernoulli +/-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang -open MultiBernoulli +/- open MultiBernoulli -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] @@ -17,9 +17,10 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) -def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do +/- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do let r ← MultiBernoulliSample seed_list - return List.zipWith (fun u s => Bool.xor (query u) s) l r + return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ + /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ From ceb7c65720443459b82dab7ac56382475f3f9ae9 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 10 Jul 2025 09:58:50 -0700 Subject: [PATCH 018/216] progresswithsums --- .../Local/MultiBernoulli.lean | 33 +++++++++++++++---- .../Local/PatternMatchingSumsExamples.lean | 21 ++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 7d52e725..44487c41 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -17,6 +17,10 @@ namespace MultiBernoulli explicit_prob function. -/ +lemma tsum_zero (f : List Bool -> ENNReal): + ∑' (x : List Bool), (if x = [] then 0 else f x) = + ∑' (x : List Bool), if assm : x ≠ [] then f x else 0 := by simp_all [ite_not] + structure SeedType where n : Nat d : PNat @@ -45,6 +49,19 @@ noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List B | [] => 0 | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs +lemma ite_simplifier1 (b : List Bool) (hd : SeedType): +(if assm : b ≠ [] then explicit_prob hd [] b else 0) = +if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * mapM bernoulli_mapper [] b.tail else 0 := by + cases b with + | nil => simp + | cons x xs => simp_all [explicit_prob, -mapM] + +lemma ite_simplifier2 (b : List Bool) (hd : SeedType): +(if h : b = [] then 0 else if b.tail = [] then bernoulli_mapper hd (b.head h) else 0) = +(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) := by + cases b with + | nil => simp + | cons x xs => simp /- EXAMPLE OF WHAT ISN'T WORKING: -/ lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), explicit_prob hd tl b = 1 := by @@ -52,12 +69,20 @@ lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] rw[explicit_prob] simp - unfold explicit_prob + convert tsum_zero (explicit_prob hd []) + apply symm + conv => + enter [1, 1, b] + rw [ite_simplifier1 b hd] + convert show (1 + 0 : ENNReal) = 1 from add_zero 1 + simp [-mapM] + conv => + enter [1, 1, b] + sorry /- FOR ETHAN: At this point I would love to say that in the else case, the x ≠ [] and so we can just pattern-match on it...but I don't know how to get Lean to understand that. -/ - sorry | cons tl_hd tl_tl ih => sorry /- If we could get the above proof to work, we would be done...-/ @@ -96,10 +121,6 @@ lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): simp_all only [ne_eq, dite_eq_ite, ite_not] sorry -lemma tsum_zero (f : List Bool -> ENNReal): - ∑' (b : List Bool), (if b = [] then 0 else f b) = - ∑' (b : List Bool), if assm : b ≠ [] then f b else 0 := by simp_all [ite_not] - lemma tsum_zero_subtype (f : List Bool -> ENNReal): ∑' (b : List Bool), f b = f [] + ∑'(b : {b : List Bool // b ≠ []}), f b.val:= by rw [ENNReal.tsum_eq_add_tsum_ite []] diff --git a/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean b/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean new file mode 100644 index 00000000..02312dcb --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean @@ -0,0 +1,21 @@ +import SampCert + +noncomputable def f : List Bool -> ENNReal := fun b => + match b with + | [] => 1 + | x :: _ => if x then 0 else 0 + +lemma f_sums_to_1: ∑' (b : List Bool), (if assm: b ≠ [] then f b else 1) = 1 := by + rw [ENNReal.tsum_eq_add_tsum_ite []] + convert show (1 + 0 : ENNReal) = 1 from add_zero 1 + rw [ENNReal.tsum_eq_zero] + intro b + by_cases h : b = [] + · simp [h] + · unfold f; aesop + +lemma f_sums_to_1': ∑' (b : List Bool), (if assm: b ≠ [] then f b else 1) = 1 := by + conv => + enter [1, 1, b] + rw [← List.head_cons_tail b assm, f] + simp From 24dc88fdc1fdf4442924157268eb45bd25088f46 Mon Sep 17 00:00:00 2001 From: Renee Date: Thu, 10 Jul 2025 14:45:54 -0400 Subject: [PATCH 019/216] re_added stuff --- .../Local/MultiBernoulli.lean | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 44487c41..5eea6d6d 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -324,4 +324,33 @@ lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : simp_all only [not_true_eq_false] simp_all +lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool) : + explicit_prob hd tl b = explicit_prob2 hd tl b := by + induction b with + | nil => unfold explicit_prob explicit_prob2 + simp_all only [mapM, bernoulli_mapper, pure, SLang.pure_apply, zero_mul] + | cons n ns ih => simp [explicit_prob, -mapM] + unfold explicit_prob2 + split + next tl => + simp_all only [mul_ite, mul_one, mul_zero] + split + next h => + subst h + rw [List.mapM_nil] + simp [pure] + next h => + simp_all only [mul_eq_zero] + apply Or.inr + rw [List.mapM_nil] + simp [pure] + exact h + next tl tl_hd tl_tl => + simp[explicit_prob, -mapM] at ih + split at ih + next b => simp [explicit_prob2, -mapM] + next b x xs => simp [explicit_prob2, -mapM] at ih + simp [List.mapM_cons, -mapM] + sorry + end MultiBernoulli From 0a49f121a72da0801ee9d258b9b0b2d6927db74c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 10 Jul 2025 11:57:00 -0700 Subject: [PATCH 020/216] onceagain --- .../Local/MultiBernoulli.lean | 74 ++++++++++++++++++- .../Local/PatternMatchingSumsExamples.lean | 11 +-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 44487c41..ae47d7a6 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -62,6 +62,71 @@ lemma ite_simplifier2 (b : List Bool) (hd : SeedType): cases b with | nil => simp | cons x xs => simp + +lemma ite_simplifier3 (b : List Bool) (hd : SeedType): +(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) = +(if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) := by + simp + cases b with + | nil => simp + | cons x xs => simp + cases xs with + | nil => simp + cases x with + | true => simp + | false => simp + | cons => simp + +lemma list_bool_length_1 (b : List Bool): + b.length = 1 ↔ b = [true] ∨ b = [false] := by + apply Iff.intro + · intro a + have h : ∃s : Bool, b = [s] := by + apply List.length_eq_one.mp + exact a + cases h with + | intro s hs => + subst hs + simp_all only [List.length_singleton, List.cons.injEq, and_true, Bool.eq_true_or_eq_false_self] + · intro a + cases a with + | inl h => + subst h + simp_all only [List.length_singleton] + | inr h_1 => + subst h_1 + simp_all only [List.length_singleton] + +lemma sum_simplifier1 (hd : SeedType): + (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) + = if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by + split + next h => + subst h + simp_all only [List.length_singleton, ↓reduceDIte, List.head_cons] + next h => + split + next h_1 => + subst h_1 + simp_all only [List.cons.injEq, Bool.false_eq_true, and_true, not_false_eq_true, List.length_singleton, + ↓reduceDIte, List.head_cons] + next h_1 => + split + next h_2 => apply False.elim + have p: ¬ (b = [false] ∨ b = [true]) := by simp_all only [or_self, not_false_eq_true] + apply p + rw [Or.comm] + apply (list_bool_length_1 b).mp + exact h_2 + next h_2 => simp_all only + + lemma sum_simplifier2 (hd : SeedType): + ∑' (b : List Bool), (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) + = ∑' (b : List Bool), if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by + conv => + enter [1, 1, b] + rw [sum_simplifier1 hd] + /- EXAMPLE OF WHAT ISN'T WORKING: -/ lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): ∑' (b : List Bool), explicit_prob hd tl b = 1 := by @@ -78,6 +143,12 @@ lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): simp [-mapM] conv => enter [1, 1, b] + rw [ite_simplifier2 b hd] + rw [ite_simplifier3 b hd] + rw[sum_simplifier2 hd] + rw [←bernoulli_mapper_sums_to_1 hd] + rw [tsum_bool] + rw [@AddCommMonoidWithOne.add_comm] sorry /- FOR ETHAN: At this point I would love to say that in the else case, the x ≠ [] and so we can just @@ -155,8 +226,9 @@ def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := #check List.mapM_cons #check SLang #check List.mapM_nil -#check tsum_eq_single #check tsum_eq_tsum_diff_singleton +#check tsum_ite_eq +#check tsum_eq_single /- The following theorems might be useful, but are not used in the current proof -/ diff --git a/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean b/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean index 02312dcb..be77471c 100644 --- a/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean +++ b/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean @@ -14,8 +14,9 @@ lemma f_sums_to_1: ∑' (b : List Bool), (if assm: b ≠ [] then f b else 1) = 1 · simp [h] · unfold f; aesop -lemma f_sums_to_1': ∑' (b : List Bool), (if assm: b ≠ [] then f b else 1) = 1 := by - conv => - enter [1, 1, b] - rw [← List.head_cons_tail b assm, f] - simp +lemma silly: (∑' (b : List Bool), if b = [true] then 1 else 0) = 1 := by + rw [@tsum_ite_eq] + + +lemma silly2 (f : Bool -> ENNReal): (∑' (b : List Bool), if b = [true] then f true else if b = [false] then f false else 0) += f true + f false := by sorry From e8fffbdd596a2ee5fa96fe4ef5c94e957fd5a791 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 10 Jul 2025 15:58:55 -0700 Subject: [PATCH 021/216] almostthere --- .../Local/MultiBernoulli.lean | 114 +++++++++++++++++- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 8eba9619..9c8676e1 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -361,6 +361,37 @@ lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f sorry -/ /- This can be proven by showing that explicit_prob normalizes. -/ + +lemma simplifier1 (b : Bool): +(∑' (a : List Bool), bernoulli_mapper hd b * ∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = +(∑' (a : List Bool), if assm: a ≠ [] then bernoulli_mapper hd b * mapM bernoulli_mapper tl a.tail else 0) := by + simp_all only [ne_eq, ite_eq_right_iff, not_true_eq_false, tsum_zero] + apply tsum_equal_comp + intro i + cases i with + | nil => sorry + | cons => sorry + +lemma simplifier2 (a : List Bool): +(if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail) + + (if a = [] then 0 else bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) += (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail + + bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) := by aesop + +lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by + intro h + rw[h] + +lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring + +lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): + ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp [h] + apply tsum_equal_comp + intro i + aesop + lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by rw [MultiBernoulliSample] @@ -371,7 +402,29 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] simp | cons hd tl ih => - rw [@multi_bernoulli_explicit_sum] + simp [List.mapM_cons, -mapM] + rw [@ENNReal.tsum_comm] + rw [tsum_bool] + rw[simplifier1 false] + rw[simplifier1 true] + rw [← @ENNReal.tsum_add] + simp only [ne_eq, dite_eq_ite, ite_not] + conv => + enter [1, 1, a] + simp [-mapM] + rw [simplifier2] + rw [ennreal_mul_assoc] + rw[←tsum_bool] + rw[bernoulli_mapper_sums_to_1] + simp [-mapM] + + + + + + + + sorry /- The rest of this file can be ignored. It states that the push-forward @@ -398,7 +451,7 @@ lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool) : explicit_prob hd tl b = explicit_prob2 hd tl b := by - induction b with + induction b generalizing hd with | nil => unfold explicit_prob explicit_prob2 simp_all only [mapM, bernoulli_mapper, pure, SLang.pure_apply, zero_mul] | cons n ns ih => simp [explicit_prob, -mapM] @@ -421,8 +474,59 @@ lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List Seed simp[explicit_prob, -mapM] at ih split at ih next b => simp [explicit_prob2, -mapM] - next b x xs => simp [explicit_prob2, -mapM] at ih - simp [List.mapM_cons, -mapM] - sorry + next b x xs => apply ennreal_mul_eq + unfold explicit_prob2 + rw [List.mapM_cons] + simp [-mapM] + rw[tsum_bool] + cases tl_tl with + | nil => simp only [mul_ite, mul_one, mul_zero] + rename_i inst + split + next h => + subst h + simp_all only [↓reduceIte, _root_.tsum_zero, mul_zero] + cases x with + | true => + simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, + _root_.tsum_zero, mul_zero, true_and, zero_add] + simp [tsum_eq_single, -mapM] + simp_all [tsum_ite_eq] + rw [tsum_eq_single []] + simp + intro b hb + simp + exact id (Ne.symm hb) + | false => + simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, + _root_.tsum_zero, mul_zero, true_and, zero_add] + simp [tsum_eq_single, -mapM] + simp_all [tsum_ite_eq] + rw [tsum_eq_single []] + simp + intro b hb + simp + exact id (Ne.symm hb) + next + h => + simp_all only [add_eq_zero, mul_eq_zero, ENNReal.tsum_eq_zero, + ite_eq_right_iff, and_imp, forall_apply_eq_imp_iff] + apply And.intro + · apply Or.inr + intro hx + rw [List.mapM_nil] + simp [pure] + exact h + · apply Or.inr + intro hx + rw [List.mapM_nil] + simp [pure] + exact h + | cons => + + + + + sorry end MultiBernoulli From 34d34e8adf202bc8466c76234f3963a201fbf1c0 Mon Sep 17 00:00:00 2001 From: Renee Date: Thu, 10 Jul 2025 18:59:10 -0400 Subject: [PATCH 022/216] lemmas and lists --- .../Local/MultiBernoulli.lean | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 5eea6d6d..05f4e8bb 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -2,10 +2,35 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import Mathlib.Probability.Independence.Basic import SampCert import SampCert.SLang +import Mathlib.Data.Set.Basic namespace MultiBernoulli + +open Set + +lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by + ext l -- extensionality: sets equal iff same elements + constructor + · intro _ + -- show l is in the tail set: pick any Bool x and let xs := l + use true, l + rfl + · rintro ⟨x, xs, h⟩ + -- l = tail of some cons => l is a list => l ∈ univ + subst h + let ll := true::l + have h: ll.tail =l := by rfl + rw [← h] + exact mem_univ _ + +lemma sum_bool_eq_sum_tail {f : List Bool -> ENNReal} : +∑' (b : List Bool), f b = ∑' (b : List Bool), f b.tail := by + + +close Set + /- We define the multivariable Bernoulli distrbution (corresponding to n independent coin flips) and prove that it normalizes. The approach for this is to define a function "explicit_prob" that @@ -353,4 +378,15 @@ lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List Seed simp [List.mapM_cons, -mapM] sorry + + + + + + + + + + + end MultiBernoulli From 3613420d5f0f04c70527e13be8a0b735bd2dac89 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 08:59:21 -0700 Subject: [PATCH 023/216] almostt --- .../Local/MultiBernoulli.lean | 54 ++++++++----------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 9450102f..93c30379 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -24,11 +24,6 @@ lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l have h: ll.tail =l := by rfl rw [← h] exact mem_univ _ - -lemma sum_bool_eq_sum_tail {f : List Bool -> ENNReal} : -∑' (b : List Bool), f b = ∑' (b : List Bool), f b.tail := by - - close Set /- We define the multivariable Bernoulli distrbution (corresponding to n @@ -387,22 +382,20 @@ lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f /- This can be proven by showing that explicit_prob normalizes. -/ -lemma simplifier1 (b : Bool): -(∑' (a : List Bool), bernoulli_mapper hd b * ∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = -(∑' (a : List Bool), if assm: a ≠ [] then bernoulli_mapper hd b * mapM bernoulli_mapper tl a.tail else 0) := by - simp_all only [ne_eq, ite_eq_right_iff, not_true_eq_false, tsum_zero] - apply tsum_equal_comp - intro i - cases i with - | nil => sorry - | cons => sorry - -lemma simplifier2 (a : List Bool): +lemma simplifier1 (a : List Bool) (b : Bool): +(∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = +(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by sorry + +lemma simplifierNOTNEEDED (a : List Bool): (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail) + (if a = [] then 0 else bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) = (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail + bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) := by aesop +lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): +(∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = + ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by sorry + lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by intro h rw[h] @@ -428,26 +421,23 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType simp | cons hd tl ih => simp [List.mapM_cons, -mapM] - rw [@ENNReal.tsum_comm] + /- rw [@ENNReal.tsum_comm] rw [tsum_bool] - rw[simplifier1 false] - rw[simplifier1 true] - rw [← @ENNReal.tsum_add] - simp only [ne_eq, dite_eq_ite, ite_not] + rw [← @ENNReal.tsum_add] -/ conv => - enter [1, 1, a] + enter [1, 1, b, 1, a] simp [-mapM] + rw [simplifier1] + /- rewrite as a double sum, the first sum being over possible a.heads, and the second + some being over all list Bools, with the conditional now being on the Boolean + in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ + rw [@ENNReal.tsum_comm] + conv => + enter [1, 1, b] rw [simplifier2] - rw [ennreal_mul_assoc] - rw[←tsum_bool] - rw[bernoulli_mapper_sums_to_1] - simp [-mapM] - - - - - - + rw [@ENNReal.tsum_comm] + rw? + sorry sorry From 0849b1d68a0ded74fc00a5b974e8f3aadc7c177e Mon Sep 17 00:00:00 2001 From: Renee Date: Fri, 11 Jul 2025 12:42:30 -0400 Subject: [PATCH 024/216] simplifier1 --- .../Local/MultiBernoulli.lean | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 93c30379..738bea9b 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -384,7 +384,31 @@ lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f lemma simplifier1 (a : List Bool) (b : Bool): (∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = -(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by sorry +(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by + cases a with + | nil => simp + | cons ah atl => + simp [-mapM] + split + next h => + subst h + simp_all only [true_and] + rw [tsum_eq_single] + --simp_all only [mapM] + split + rename_i h + on_goal 2 => rename_i h + apply Eq.refl + simp_all only [not_true_eq_false] + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + next h => simp_all only [false_and, ↓reduceIte, _root_.tsum_zero] + + + lemma simplifierNOTNEEDED (a : List Bool): (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail) + From b60930d4bf14959925653a4e52ca39838f3819c7 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 09:58:41 -0700 Subject: [PATCH 025/216] donemodulo --- .../Local/MultiBernoulli.lean | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean index 93c30379..a197ad5c 100644 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean @@ -392,9 +392,22 @@ lemma simplifierNOTNEEDED (a : List Bool): = (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail + bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) := by aesop +lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): +∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by + apply Equiv.tsum_eq_tsum_of_support + { intro x + + } + { + + } + lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): (∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = - ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by sorry + ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by + simp_all only [mul_ite, mul_zero] + apply symm + apply list_bool_tsum_only_tl b lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by intro h @@ -402,6 +415,14 @@ lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring +lemma simplifier3: + ∑' (a : Bool), bernoulli_mapper hd a * mapM bernoulli_mapper tl b = mapM bernoulli_mapper tl b := by + rw [tsum_bool] + rw [ennreal_mul_assoc] + rw [←tsum_bool] + rw [bernoulli_mapper_sums_to_1] + rw [@CanonicallyOrderedCommSemiring.one_mul] + lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by rw [ENNReal.tsum_eq_add_tsum_ite []] @@ -421,9 +442,6 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType simp | cons hd tl ih => simp [List.mapM_cons, -mapM] - /- rw [@ENNReal.tsum_comm] - rw [tsum_bool] - rw [← @ENNReal.tsum_add] -/ conv => enter [1, 1, b, 1, a] simp [-mapM] @@ -436,12 +454,10 @@ lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType enter [1, 1, b] rw [simplifier2] rw [@ENNReal.tsum_comm] - rw? - sorry - - - sorry - + conv => + enter [1, 1, b] + rw [simplifier3] + apply ih /- The rest of this file can be ignored. It states that the push-forward of a probability measure is a probability measure, which we don't need for now. -/ From 101046a18378cdf0b00f84ec99ab849c3ccba7f3 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 11:58:12 -0700 Subject: [PATCH 026/216] reorganizedRobert --- .../Local/ENNRealLemmasSuite.lean | 26 + .../Local/MultiBernoulli.lean | 598 ------------------ .../Local/MultiBernoulli/Code.lean | 18 + .../Local/MultiBernoulli/Properties.lean | 125 ++++ .../{ => Playground}/ENNRealCoercions.lean | 0 .../Local/Playground/Junk.lean | 265 ++++++++ .../PatternMatchingSumsExamples.lean | 0 .../Local/Playground/UsefulLemmas.lean | 9 + .../Local/PushForward.lean | 24 + SampCert/DifferentialPrivacy/Neighbours.lean | 3 +- 10 files changed, 468 insertions(+), 600 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean delete mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean create mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean create mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean rename SampCert/DifferentialPrivacy/Local/{ => Playground}/ENNRealCoercions.lean (100%) create mode 100644 SampCert/DifferentialPrivacy/Local/Playground/Junk.lean rename SampCert/DifferentialPrivacy/Local/{ => Playground}/PatternMatchingSumsExamples.lean (100%) create mode 100644 SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean create mode 100644 SampCert/DifferentialPrivacy/Local/PushForward.lean diff --git a/SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean new file mode 100644 index 00000000..f2a2774f --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean @@ -0,0 +1,26 @@ +import SampCert + +namespace ENNRealLemmas + +lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f g : α -> β) (h: ∀i : α, f i = g i ): + ∑' (i : α), f i = ∑' (i : α), g i := by simp_all + +lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by + intro h + rw[h] + +lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring + +lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): + ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp [h] + apply tsum_equal_comp + intro i + aesop + +lemma tsum_ite_not (f : List Bool -> ENNReal): + ∑' (x : List Bool), (if x = [] then 0 else f x) = + ∑' (x : List Bool), if assm : x ≠ [] then f x else 0 := by simp_all [ite_not] + +end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean deleted file mode 100644 index bfb10bf4..00000000 --- a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean +++ /dev/null @@ -1,598 +0,0 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import Mathlib.Probability.Independence.Basic -import SampCert -import SampCert.SLang -import Mathlib.Data.Set.Basic - - -namespace MultiBernoulli - - -open Set - -lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by - ext l -- extensionality: sets equal iff same elements - constructor - · intro _ - -- show l is in the tail set: pick any Bool x and let xs := l - use true, l - rfl - · rintro ⟨x, xs, h⟩ - -- l = tail of some cons => l is a list => l ∈ univ - subst h - let ll := true::l - have h: ll.tail =l := by rfl - rw [← h] - exact mem_univ _ -close Set - -/- We define the multivariable Bernoulli distrbution (corresponding to n - independent coin flips) and prove that it normalizes. - The approach for this is to define a function "explicit_prob" that - encapsulates the multiplication of probabilities for the independent - coin flips, and then prove that "MultiBernoulliSample" is equivalent - to "explicit_prob," in the sense that the two have the same output on any - input. Given this, the proof of normalization for the multivariate - Bernoulli distribution reduces to a proof of normalization for the - explicit_prob function. --/ - -lemma tsum_zero (f : List Bool -> ENNReal): - ∑' (x : List Bool), (if x = [] then 0 else f x) = - ∑' (x : List Bool), if assm : x ≠ [] then f x else 0 := by simp_all [ite_not] - -structure SeedType where - n : Nat - d : PNat - h : n ≤ d - /- This is just handy shorthand for a rational parameter n/d in [0, 1]-/ - -/- IGNORE-/ -example (b : {b : List Bool // b ≠ []}): b.val.head b.property ∨ ¬ b.val.head b.property := by - cases b.val.head b.property with - | true => left; rfl - | false => right; simp - -/- IGNORE -/ -def silly: {b : List Bool // b ≠ []} := ⟨[true], by decide⟩ - -/-IMPORTANT: -/ -def bernoulli_mapper: SeedType -> SLang Bool := - fun s => SLang.BernoulliSample s.n s.d s.h - -lemma bernoulli_mapper_sums_to_1 (s : SeedType): ∑' (b : Bool), bernoulli_mapper s b = 1 := by - rw[bernoulli_mapper] - exact SLang.BernoulliSample_normalizes s.n s.d s.h - -noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := - match b with - | [] => 0 - | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs - -lemma ite_simplifier1 (b : List Bool) (hd : SeedType): -(if assm : b ≠ [] then explicit_prob hd [] b else 0) = -if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * mapM bernoulli_mapper [] b.tail else 0 := by - cases b with - | nil => simp - | cons x xs => simp_all [explicit_prob, -mapM] - -lemma ite_simplifier2 (b : List Bool) (hd : SeedType): -(if h : b = [] then 0 else if b.tail = [] then bernoulli_mapper hd (b.head h) else 0) = -(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) := by - cases b with - | nil => simp - | cons x xs => simp - -lemma ite_simplifier3 (b : List Bool) (hd : SeedType): -(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) = -(if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) := by - simp - cases b with - | nil => simp - | cons x xs => simp - cases xs with - | nil => simp - cases x with - | true => simp - | false => simp - | cons => simp - -lemma list_bool_length_1 (b : List Bool): - b.length = 1 ↔ b = [true] ∨ b = [false] := by - apply Iff.intro - · intro a - have h : ∃s : Bool, b = [s] := by - apply List.length_eq_one.mp - exact a - cases h with - | intro s hs => - subst hs - simp_all only [List.length_singleton, List.cons.injEq, and_true, Bool.eq_true_or_eq_false_self] - · intro a - cases a with - | inl h => - subst h - simp_all only [List.length_singleton] - | inr h_1 => - subst h_1 - simp_all only [List.length_singleton] - -lemma sum_simplifier1 (hd : SeedType): - (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) - = if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by - split - next h => - subst h - simp_all only [List.length_singleton, ↓reduceDIte, List.head_cons] - next h => - split - next h_1 => - subst h_1 - simp_all only [List.cons.injEq, Bool.false_eq_true, and_true, not_false_eq_true, List.length_singleton, - ↓reduceDIte, List.head_cons] - next h_1 => - split - next h_2 => apply False.elim - have p: ¬ (b = [false] ∨ b = [true]) := by simp_all only [or_self, not_false_eq_true] - apply p - rw [Or.comm] - apply (list_bool_length_1 b).mp - exact h_2 - next h_2 => simp_all only - - lemma sum_simplifier2 (hd : SeedType): - ∑' (b : List Bool), (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) - = ∑' (b : List Bool), if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by - conv => - enter [1, 1, b] - rw [sum_simplifier1 hd] - -/- EXAMPLE OF WHAT ISN'T WORKING: -/ -lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob hd tl b = 1 := by - induction tl with - | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] - rw[explicit_prob] - simp - convert tsum_zero (explicit_prob hd []) - apply symm - conv => - enter [1, 1, b] - rw [ite_simplifier1 b hd] - convert show (1 + 0 : ENNReal) = 1 from add_zero 1 - simp [-mapM] - conv => - enter [1, 1, b] - rw [ite_simplifier2 b hd] - rw [ite_simplifier3 b hd] - rw[sum_simplifier2 hd] - rw [←bernoulli_mapper_sums_to_1 hd] - rw [tsum_bool] - rw [@AddCommMonoidWithOne.add_comm] - sorry - /- FOR ETHAN: At this point I would love to say that - in the else case, the x ≠ [] and so we can just - pattern-match on it...but I don't know how to get Lean - to understand that. -/ - | cons tl_hd tl_tl ih => sorry -/- If we could get the above proof to work, we would be done...-/ - -/- IGNORE -- after getting stuck with the previous def, I tried to make it - explicitly recursive, and got stuck...but hopefully we don't need it. -/ - noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := - match b with - | [] => 0 - | x :: xs => bernoulli_mapper hd x * - match tl with - | [] => if xs = [] then 1 else 0 - | tl_hd :: tl_tl => explicit_prob2 tl_hd tl_tl xs - -lemma bernoulli_mapper_empty (b : List Bool): mapM bernoulli_mapper [] b = if b = [] then 1 else 0 := by - rw [@List.mapM_nil] - simp_all only [pure, SLang.pure_apply, ite_eq_right_iff, one_ne_zero, imp_false] - split - next h => - subst h - simp_all only - next h => simp_all only - -lemma explicit_prob_nonempty (hd : SeedType) (tl : List SeedType) (b : List Bool) (h : b ≠ []): - explicit_prob hd tl b = bernoulli_mapper hd (b.head h) * (mapM bernoulli_mapper tl b.tail) := by - rw[explicit_prob] - cases b with - | nil => contradiction - | cons => - rename_i head tail - simp_all only [List.head_cons, List.tail_cons] - -lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob hd tl b = - ∑' (b : List Bool), if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * (mapM bernoulli_mapper tl) b.tail else 0 := by - unfold explicit_prob - simp_all only [ne_eq, dite_eq_ite, ite_not] - sorry - -lemma tsum_zero_subtype (f : List Bool -> ENNReal): - ∑' (b : List Bool), f b = f [] + ∑'(b : {b : List Bool // b ≠ []}), f b.val:= by - rw [ENNReal.tsum_eq_add_tsum_ite []] - sorry - -lemma explicit_prob2_sums_to_1 (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob2 hd tl b = 1 := by - induction tl with - | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] - rw[explicit_prob2] - simp - unfold explicit_prob2 - simp_all only [mul_ite, mul_one, mul_zero] - rw [tsum_zero] - sorry - | cons tl_hd tl_tl ih => rw [ENNReal.tsum_eq_add_tsum_ite []] - rw[explicit_prob2] - simp - sorry - - -/- IMPORTANT -/ -def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := - seeds.mapM bernoulli_mapper - -#check MultiBernoulliSample [SeedType.mk 1 2 (by decide), SeedType.mk 1 2 (by decide)] [true, false] - -/- USEFUL LEMMAS: -/ -#check @ENNReal.tsum_eq_iSup_sum -#check SLang.BernoulliSample_apply -#check SLang.BernoulliSample_normalizes -#check List.mapM_cons -#check SLang -#check List.mapM_nil -#check tsum_eq_tsum_diff_singleton -#check tsum_ite_eq -#check tsum_eq_single - -/- The following theorems might be useful, but are not used in the current proof -/ - -lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by - rw [MultiBernoulliSample] - rw [List.mapM_cons, List.mapM_nil] - rcases hd with ⟨n, d, h⟩ - simp only [pure, bind] - simp_all only [SLang.pure_bind, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] - rw [@ENNReal.tsum_comm] - rw [tsum_bool] - simp_all only [Bool.false_eq_true, ↓reduceIte, tsum_ite_eq] - rw[←tsum_bool] - rw[bernoulli_mapper] - rw [SLang.BernoulliSample_normalizes] - -lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : bernoulli_mapper hd_1 hd = mapM bernoulli_mapper [hd_1] [hd] := by - rw [List.mapM_cons, List.mapM_nil] - rcases hd_1 with ⟨n, d, h⟩ - simp only [pure, bind] - simp_all only [SLang.pure_bind, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] - simp_all only [List.cons.injEq, and_true] - cases hd with - | true => simp_all only [Bool.true_eq, tsum_ite_eq] - | false => simp_all only [Bool.false_eq, tsum_ite_eq, tsum_zero, mul_zero] - -/- -lemma bernoulli_mapper_neq_iff (l : List SeedType) (b : List Bool) : - mapM bernoulli_mapper l [] = - match l with - | [] => 1 - | hd :: tl => 0 := by - cases l with - | nil => simp[-mapM] - | cons hd tl => simp[-mapM] - sorry --/ - -/- IMPORTANT: Here we prove that explicit_prob is the same as MultiBernoulliSample-/ -lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): - mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by - unfold explicit_prob - rw[List.mapM_cons] - simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] - split - next b => simp_all only [↓reduceIte, tsum_zero, mul_zero] - simp - next b x xs => - simp_all only [List.cons.injEq] - rw[tsum_bool] - cases x with - | false => simp[-mapM] - rw[tsum_eq_single xs] - simp_all - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - | true => simp[-mapM] - rw[tsum_eq_single xs] - simp_all - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - -lemma multi_bernoulli_explicit_sum [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by - simp_all [multi_bernoulli_explicit, -mapM] - -/- noncomputable def unsimplified_expression (hd : SeedType) (tl : List SeedType) (x : List Bool): ENNReal := - if x = [] then (0 : ENNReal) - else - match x with - | [] => (0 : ENNReal) - | x :: xs => bernoulli_mapper hd x * mapM bernoulli_mapper tl xs -/ - -/- lemma simplify_unsimplified (hd : SeedType) (tl : List SeedType) (x : List Bool) (h : x ≠ []): - unsimplified_expression hd tl x = - bernoulli_mapper hd (x.head h) * mapM bernoulli_mapper tl x.tail := by - unfold unsimplified_expression - simp_all only [↓reduceIte, mapM] - split - next b => - simp_all only [List.tail_nil, zero_eq_mul] - apply Or.inr - simp_all only [ne_eq, not_true_eq_false] - next b x xs => simp_all only [List.head_cons, List.tail_cons] -/ - -/- lemma list_bool_tsum_rw {T : Type} [DecidableEq T] [AddCommMonoid T] [TopologicalSpace T] (f : List Bool -> T): - ∑' (b : List Bool), f b = f [] + ∑' (b : {b : List Bool // b ≠ []}), f b := by - sorry -/ - -lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f g : α -> β) (h: ∀i : α, f i = g i ): - ∑' (i : α), f i = ∑' (i : α), g i := by simp_all - -/- lemma multibernoulli_of_zero [LawfulMonad SLang] (seed: SeedType): - MultiBernoulliSample [seed] [] = 0 := by - rw [MultiBernoulliSample] - rw [List.mapM_cons] - simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, ↓reduceIte, mul_zero, tsum_zero] -/ - -/- lemma MultiBernoulli_independence_single [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): - MultiBernoulliSample (hd :: tl) b = if assm: b ≠ [] then MultiBernoulliSample [hd] [b.head (by assumption)] * MultiBernoulliSample tl (b.tail) else 0 := by - unfold MultiBernoulliSample - cases b with - | nil => simp [-mapM] - | cons hd tl => - rename_i inst hd_1 tl_1 - simp_all only [ne_eq, not_false_eq_true, ↓reduceDIte, List.head_cons, List.tail_cons] - sorry -/ - -/- lemma MultiBernoulli_independence [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), MultiBernoulliSample (hd :: tl) b = - ∑' (b : List Bool), if assm: b ≠ [] then MultiBernoulliSample [hd] [b.head (by assumption)] * MultiBernoulliSample tl (b.tail) else 0 := by - unfold MultiBernoulliSample - rw [multi_bernoulli_explicit_sum] - unfold explicit_prob - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp_all only [zero_add] - rw[tsum_equal_comp] - intro b - cases b with - | nil => simp [-mapM] - | cons hd tl => - rename_i inst hd_1 tl_1 - simp_all only [↓reduceIte, ne_eq, not_false_eq_true, ↓reduceDIte, List.head_cons, List.tail_cons] - sorry -/ - -/- This can be proven by showing that explicit_prob normalizes. -/ - -lemma simplifier1 (a : List Bool) (b : Bool): -(∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = -(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by - cases a with - | nil => simp - | cons ah atl => - simp [-mapM] - split - next h => - subst h - simp_all only [true_and] - rw [tsum_eq_single] - --simp_all only [mapM] - split - rename_i h - on_goal 2 => rename_i h - apply Eq.refl - simp_all only [not_true_eq_false] - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - next h => simp_all only [false_and, ↓reduceIte, _root_.tsum_zero] - - - - -lemma simplifierNOTNEEDED (a : List Bool): -(if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail) + - (if a = [] then 0 else bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) -= (if a = [] then 0 else bernoulli_mapper hd false * mapM bernoulli_mapper tl a.tail + - bernoulli_mapper hd true * mapM bernoulli_mapper tl a.tail) := by aesop - -lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): -∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by - apply Equiv.tsum_eq_tsum_of_support - { intro x - - } - { - - } - -lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): -(∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = - ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by - simp_all only [mul_ite, mul_zero] - apply symm - apply list_bool_tsum_only_tl b - -lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by - intro h - rw[h] - -lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring - -lemma simplifier3: - ∑' (a : Bool), bernoulli_mapper hd a * mapM bernoulli_mapper tl b = mapM bernoulli_mapper tl b := by - rw [tsum_bool] - rw [ennreal_mul_assoc] - rw [←tsum_bool] - rw [bernoulli_mapper_sums_to_1] - rw [@CanonicallyOrderedCommSemiring.one_mul] - -lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): - ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp [h] - apply tsum_equal_comp - intro i - aesop - -lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : - ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by - rw [MultiBernoulliSample] - induction seeds with - | nil => rw [@List.mapM_nil] - simp[pure] - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] - simp - | cons hd tl ih => - simp [List.mapM_cons, -mapM] - conv => - enter [1, 1, b, 1, a] - simp [-mapM] - rw [simplifier1] - /- rewrite as a double sum, the first sum being over possible a.heads, and the second - some being over all list Bools, with the conditional now being on the Boolean - in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ - rw [@ENNReal.tsum_comm] - conv => - enter [1, 1, b] - rw [simplifier2] - rw [@ENNReal.tsum_comm] - conv => - enter [1, 1, b] - rw [simplifier3] - apply ih -/- The rest of this file can be ignored. It states that the push-forward - of a probability measure is a probability measure, which we don't - need for now. -/ - -noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := - fun s => ∑' (t : T), if f t = s then p t else 0 - -lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : - ∑' (s : S), (push_forward p f) s = 1 := by - simp [push_forward] - rw [@ENNReal.tsum_comm] - have h1: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by - intro b - rw [tsum_eq_single (f b)] - simp - intro b' a - simp_all only [ne_eq, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - simp_all - -lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool) : - explicit_prob hd tl b = explicit_prob2 hd tl b := by - induction b generalizing hd with - | nil => unfold explicit_prob explicit_prob2 - simp_all only [mapM, bernoulli_mapper, pure, SLang.pure_apply, zero_mul] - | cons n ns ih => simp [explicit_prob, -mapM] - unfold explicit_prob2 - split - next tl => - simp_all only [mul_ite, mul_one, mul_zero] - split - next h => - subst h - rw [List.mapM_nil] - simp [pure] - next h => - simp_all only [mul_eq_zero] - apply Or.inr - rw [List.mapM_nil] - simp [pure] - exact h - next tl tl_hd tl_tl => - simp[explicit_prob, -mapM] at ih - split at ih - next b => simp [explicit_prob2, -mapM] - next b x xs => apply ennreal_mul_eq - unfold explicit_prob2 - rw [List.mapM_cons] - simp [-mapM] - rw[tsum_bool] - cases tl_tl with - | nil => simp only [mul_ite, mul_one, mul_zero] - rename_i inst - split - next h => - subst h - simp_all only [↓reduceIte, _root_.tsum_zero, mul_zero] - cases x with - | true => - simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, - _root_.tsum_zero, mul_zero, true_and, zero_add] - simp [tsum_eq_single, -mapM] - simp_all [tsum_ite_eq] - rw [tsum_eq_single []] - simp - intro b hb - simp - exact id (Ne.symm hb) - | false => - simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, - _root_.tsum_zero, mul_zero, true_and, zero_add] - simp [tsum_eq_single, -mapM] - simp_all [tsum_ite_eq] - rw [tsum_eq_single []] - simp - intro b hb - simp - exact id (Ne.symm hb) - next - h => - simp_all only [add_eq_zero, mul_eq_zero, ENNReal.tsum_eq_zero, - ite_eq_right_iff, and_imp, forall_apply_eq_imp_iff] - apply And.intro - · apply Or.inr - intro hx - rw [List.mapM_nil] - simp [pure] - exact h - · apply Or.inr - intro hx - rw [List.mapM_nil] - simp [pure] - exact h - | cons => - - - - - - sorry - - - - - - - - - - - -end MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean new file mode 100644 index 00000000..d2b993c5 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean @@ -0,0 +1,18 @@ +import SampCert +open SLang + +namespace MultiBernoulli + +structure SeedType where + /- This is just handy shorthand for a rational parameter n/d in [0, 1]-/ + n : Nat + d : PNat + h : n ≤ d + +def bernoulli_mapper: SeedType -> SLang Bool := + fun s => SLang.BernoulliSample s.n s.d s.h + +def MultiBernoulliSample (seeds: List SeedType): SLang (List Bool) := + seeds.mapM bernoulli_mapper + +end MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean new file mode 100644 index 00000000..8a294cea --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean @@ -0,0 +1,125 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Probability.Independence.Basic +import SampCert +import SampCert.SLang +import Mathlib.Data.Set.Basic +import SampCert.DifferentialPrivacy.Local.ENNRealLemmasSuite +import SampCert.DifferentialPrivacy.Local.MultiBernoulli.Code + + +namespace MultiBernoulli +open SLang +open ENNRealLemmas + +lemma bernoulli_mapper_sums_to_1 (s : SeedType): ∑' (b : Bool), bernoulli_mapper s b = 1 := by + rw[bernoulli_mapper] + exact SLang.BernoulliSample_normalizes s.n s.d s.h + +lemma bernoulli_mapper_empty (b : List Bool): mapM bernoulli_mapper [] b = if b = [] then 1 else 0 := by + rw [@List.mapM_nil] + simp_all only [pure, SLang.pure_apply, ite_eq_right_iff, one_ne_zero, imp_false] + split + next h => + subst h + simp_all only + next h => simp_all only + +/- The following theorems might be useful, but are not used in the current proof -/ + +lemma MultiBernoulli_single_list [LawfulMonad SLang] (hd : SeedType): ∑' (b : List Bool), MultiBernoulliSample [hd] b = 1 := by + rw [MultiBernoulliSample] + rw [List.mapM_cons, List.mapM_nil] + rcases hd with ⟨n, d, h⟩ + simp only [pure, bind] + simp_all only [SLang.pure_bind, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] + rw [@ENNReal.tsum_comm] + rw [tsum_bool] + simp_all only [Bool.false_eq_true, ↓reduceIte, tsum_ite_eq] + rw[←tsum_bool] + rw[bernoulli_mapper] + rw [SLang.BernoulliSample_normalizes] + +lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : bernoulli_mapper hd_1 hd = mapM bernoulli_mapper [hd_1] [hd] := by + rw [List.mapM_cons, List.mapM_nil] + rcases hd_1 with ⟨n, d, h⟩ + simp only [pure, bind] + simp_all only [SLang.pure_bind, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] + simp_all only [List.cons.injEq, and_true] + cases hd with + | true => simp_all only [Bool.true_eq, tsum_ite_eq] + | false => simp_all only [Bool.false_eq, tsum_ite_eq, tsum_ite_not, mul_zero] + +lemma simplifier1 (a : List Bool) (b : Bool): +(∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = +(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by + cases a with + | nil => simp + | cons ah atl => + simp [-mapM] + split + next h => + subst h + simp_all only [true_and] + rw [tsum_eq_single] + split + rename_i h + on_goal 2 => rename_i h + apply Eq.refl + simp_all only [not_true_eq_false] + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + next h => simp_all only [false_and, ↓reduceIte] + simp [tsum_ite_not] + +lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): +∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by + apply Equiv.tsum_eq_tsum_of_support + sorry + +lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): +(∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = + ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by + simp_all only [mul_ite, mul_zero] + apply symm + apply list_bool_tsum_only_tl b + +lemma simplifier3: + ∑' (a : Bool), bernoulli_mapper hd a * mapM bernoulli_mapper tl b = mapM bernoulli_mapper tl b := by + rw [tsum_bool] + rw [ennreal_mul_assoc] + rw [←tsum_bool] + rw [bernoulli_mapper_sums_to_1] + rw [@CanonicallyOrderedCommSemiring.one_mul] + +lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : + ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by + rw [MultiBernoulliSample] + induction seeds with + | nil => rw [@List.mapM_nil] + simp[pure] + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [↓reduceIte, ite_self, tsum_ite_not, add_zero] + simp + | cons hd tl ih => + simp [List.mapM_cons, -mapM] + conv => + enter [1, 1, b, 1, a] + simp [-mapM] + rw [simplifier1] + /- rewrite as a double sum, the first sum being over possible a.heads, and the second + some being over all list Bools, with the conditional now being on the Boolean + in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ + rw [@ENNReal.tsum_comm] + conv => + enter [1, 1, b] + rw [simplifier2] + rw [@ENNReal.tsum_comm] + conv => + enter [1, 1, b] + rw [simplifier3] + apply ih + +end MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean b/SampCert/DifferentialPrivacy/Local/Playground/ENNRealCoercions.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/ENNRealCoercions.lean rename to SampCert/DifferentialPrivacy/Local/Playground/ENNRealCoercions.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/Junk.lean b/SampCert/DifferentialPrivacy/Local/Playground/Junk.lean new file mode 100644 index 00000000..ecdb0c80 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/Playground/Junk.lean @@ -0,0 +1,265 @@ +import SampCert + +open MultiBernoulli + +/- These is stuff that might be helpful for understanding + how to deal with some specific Lean issues, mainly + to do with manipulating tsums and List.mapM, but isn't + actually used for anything at this point. -/ + +/- WORKING WITH SUBTYPES: -/ +example (b : {b : List Bool // b ≠ []}): b.val.head b.property ∨ ¬ b.val.head b.property := by + cases b.val.head b.property with + | true => left; rfl + | false => right; simp + +def silly: {b : List Bool // b ≠ []} := ⟨[true], by decide⟩ + +/- SIMPLIFICATION LEMMAS FOR IF-THEN-ELSE STATEMENTS: -/ +noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := + match b with + | [] => 0 + | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs + +lemma ite_simplifier1 (b : List Bool) (hd : SeedType): +(if assm : b ≠ [] then explicit_prob hd [] b else 0) = +if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * mapM bernoulli_mapper [] b.tail else 0 := by + cases b with + | nil => simp + | cons x xs => simp_all [explicit_prob, -mapM] + +lemma ite_simplifier2 (b : List Bool) (hd : SeedType): +(if h : b = [] then 0 else if b.tail = [] then bernoulli_mapper hd (b.head h) else 0) = +(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) := by + cases b with + | nil => simp + | cons x xs => simp + +lemma ite_simplifier3 (b : List Bool) (hd : SeedType): +(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) = +(if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) := by + simp + cases b with + | nil => simp + | cons x xs => simp + cases xs with + | nil => simp + cases x with + | true => simp + | false => simp + | cons => simp + +lemma list_bool_length_1 (b : List Bool): + b.length = 1 ↔ b = [true] ∨ b = [false] := by + apply Iff.intro + · intro a + have h : ∃s : Bool, b = [s] := by + apply List.length_eq_one.mp + exact a + cases h with + | intro s hs => + subst hs + simp_all only [List.length_singleton, List.cons.injEq, and_true, Bool.eq_true_or_eq_false_self] + · intro a + cases a with + | inl h => + subst h + simp_all only [List.length_singleton] + | inr h_1 => + subst h_1 + simp_all only [List.length_singleton] + +lemma sum_simplifier1 (hd : SeedType): + (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) + = if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by + split + next h => + subst h + simp_all only [List.length_singleton, ↓reduceDIte, List.head_cons] + next h => + split + next h_1 => + subst h_1 + simp_all only [List.cons.injEq, Bool.false_eq_true, and_true, not_false_eq_true, List.length_singleton, + ↓reduceDIte, List.head_cons] + next h_1 => + split + next h_2 => apply False.elim + have p: ¬ (b = [false] ∨ b = [true]) := by simp_all only [or_self, not_false_eq_true] + apply p + rw [Or.comm] + apply (list_bool_length_1 b).mp + exact h_2 + next h_2 => simp_all only + + lemma sum_simplifier2 (hd : SeedType): + ∑' (b : List Bool), (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) + = ∑' (b : List Bool), if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by + conv => + enter [1, 1, b] + rw [sum_simplifier1 hd] + +/- EXAMPLE OF WHAT ISN'T WORKING: -/ +lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob hd tl b = 1 := by + induction tl with + | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] + rw[explicit_prob] + simp + convert tsum_zero (explicit_prob hd []) + apply symm + conv => + enter [1, 1, b] + rw [ite_simplifier1 b hd] + convert show (1 + 0 : ENNReal) = 1 from add_zero 1 + simp [-mapM] + conv => + enter [1, 1, b] + rw [ite_simplifier2 b hd] + rw [ite_simplifier3 b hd] + rw[sum_simplifier2 hd] + rw [←bernoulli_mapper_sums_to_1 hd] + rw [tsum_bool] + rw [@AddCommMonoidWithOne.add_comm] + sorry + /- FOR ETHAN: At this point I would love to say that + in the else case, the x ≠ [] and so we can just + pattern-match on it...but I don't know how to get Lean + to understand that. -/ + | cons tl_hd tl_tl ih => sorry +/- If we could get the above proof to work, we would be done...-/ + +/- IGNORE -- after getting stuck with the previous def, I tried to make it + explicitly recursive, and got stuck...but hopefully we don't need it. -/ + noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := + match b with + | [] => 0 + | x :: xs => bernoulli_mapper hd x * + match tl with + | [] => if xs = [] then 1 else 0 + | tl_hd :: tl_tl => explicit_prob2 tl_hd tl_tl xs + +lemma explicit_prob_nonempty (hd : SeedType) (tl : List SeedType) (b : List Bool) (h : b ≠ []): + explicit_prob hd tl b = bernoulli_mapper hd (b.head h) * (mapM bernoulli_mapper tl b.tail) := by + rw[explicit_prob] + cases b with + | nil => contradiction + | cons => + rename_i head tail + simp_all only [List.head_cons, List.tail_cons] + +lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), explicit_prob hd tl b = + ∑' (b : List Bool), if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * (mapM bernoulli_mapper tl) b.tail else 0 := by + unfold explicit_prob + simp_all only [ne_eq, dite_eq_ite, ite_not] + sorry + +lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): + mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by + unfold explicit_prob + rw[List.mapM_cons] + simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] + split + next b => simp_all only [↓reduceIte, tsum_zero, mul_zero] + simp + next b x xs => + simp_all only [List.cons.injEq] + rw[tsum_bool] + cases x with + | false => simp[-mapM] + rw[tsum_eq_single xs] + simp_all + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + | true => simp[-mapM] + rw[tsum_eq_single xs] + simp_all + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + +lemma multi_bernoulli_explicit_sum [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): + ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by + simp_all [multi_bernoulli_explicit, -mapM] + +lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool) : + explicit_prob hd tl b = explicit_prob2 hd tl b := by + induction b generalizing hd with + | nil => unfold explicit_prob explicit_prob2 + simp_all only [mapM, bernoulli_mapper, pure, SLang.pure_apply, zero_mul] + | cons n ns ih => simp [explicit_prob, -mapM] + unfold explicit_prob2 + split + next tl => + simp_all only [mul_ite, mul_one, mul_zero] + split + next h => + subst h + rw [List.mapM_nil] + simp [pure] + next h => + simp_all only [mul_eq_zero] + apply Or.inr + rw [List.mapM_nil] + simp [pure] + exact h + next tl tl_hd tl_tl => + simp[explicit_prob, -mapM] at ih + split at ih + next b => simp [explicit_prob2, -mapM] + next b x xs => apply ennreal_mul_eq + unfold explicit_prob2 + rw [List.mapM_cons] + simp [-mapM] + rw[tsum_bool] + cases tl_tl with + | nil => simp only [mul_ite, mul_one, mul_zero] + rename_i inst + split + next h => + subst h + simp_all only [↓reduceIte, _root_.tsum_zero, mul_zero] + cases x with + | true => + simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, + _root_.tsum_zero, mul_zero, true_and, zero_add] + simp [tsum_eq_single, -mapM] + simp_all [tsum_ite_eq] + rw [tsum_eq_single []] + simp + intro b hb + simp + exact id (Ne.symm hb) + | false => + simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, + _root_.tsum_zero, mul_zero, true_and, zero_add] + simp [tsum_eq_single, -mapM] + simp_all [tsum_ite_eq] + rw [tsum_eq_single []] + simp + intro b hb + simp + exact id (Ne.symm hb) + next + h => + simp_all only [add_eq_zero, mul_eq_zero, ENNReal.tsum_eq_zero, + ite_eq_right_iff, and_imp, forall_apply_eq_imp_iff] + apply And.intro + · apply Or.inr + intro hx + rw [List.mapM_nil] + simp [pure] + exact h + · apply Or.inr + intro hx + rw [List.mapM_nil] + simp [pure] + exact h + | cons => + sorry diff --git a/SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean b/SampCert/DifferentialPrivacy/Local/Playground/PatternMatchingSumsExamples.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/PatternMatchingSumsExamples.lean rename to SampCert/DifferentialPrivacy/Local/Playground/PatternMatchingSumsExamples.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean b/SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean new file mode 100644 index 00000000..0da21fda --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean @@ -0,0 +1,9 @@ +import SampCert + +/- USEFUL LEMMAS: -/ +#check SLang.BernoulliSample_apply +#check List.mapM_cons +#check List.mapM_nil +#check tsum_eq_tsum_diff_singleton +#check tsum_ite_eq +#check tsum_eq_single diff --git a/SampCert/DifferentialPrivacy/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Local/PushForward.lean new file mode 100644 index 00000000..63791a50 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/PushForward.lean @@ -0,0 +1,24 @@ +import SampCert + +open SLang + +/- This proves that the push-forward of a probability measure is a probability measure. +We do not actually use this anywhere yet.-/ + +noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := + fun s => ∑' (t : T), if f t = s then p t else 0 + +lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : + ∑' (s : S), (push_forward p f) s = 1 := by + simp [push_forward] + rw [@ENNReal.tsum_comm] + have h1: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by + intro b + rw [tsum_eq_single (f b)] + simp + intro b' a + simp_all only [ne_eq, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + simp_all diff --git a/SampCert/DifferentialPrivacy/Neighbours.lean b/SampCert/DifferentialPrivacy/Neighbours.lean index 1d556e5f..8cfeee25 100644 --- a/SampCert/DifferentialPrivacy/Neighbours.lean +++ b/SampCert/DifferentialPrivacy/Neighbours.lean @@ -16,14 +16,13 @@ variable {T : Type} /-- Lists which differ by the inclusion or modification of an element. -This is SampCert's private property. +This is SampCert's private property. -/ inductive Neighbour (l₁ l₂ : List T) : Prop where | Addition : l₁ = a ++ b → l₂ = a ++ [n] ++ b → Neighbour l₁ l₂ | Deletion : l₁ = a ++ [n] ++ b → l₂ = a ++ b → Neighbour l₁ l₂ | Update : l₁ = a ++ [n] ++ b → l₂ = a ++ [m] ++ b -> Neighbour l₁ l₂ - /-- Neighbour relation is symmetric. -/ From 52f44ded62155a6c14bba55ba5048b022c63ff9e Mon Sep 17 00:00:00 2001 From: PCChess Date: Fri, 11 Jul 2025 13:10:06 -0700 Subject: [PATCH 027/216] Create Definitions.lean --- .../DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean new file mode 100644 index 00000000..e69de29b From b5bdf8bc8405d172c1c85aa1af3295689d71f815 Mon Sep 17 00:00:00 2001 From: PCChess Date: Fri, 11 Jul 2025 13:12:14 -0700 Subject: [PATCH 028/216] reorganize --- .../RandomizedResponse/AccuracyProof.lean | 62 ++++++++++++++++ .../Local/RandomizedResponse/Basic.lean | 4 ++ .../Local/RandomizedResponse/DPProof.lean | 0 .../Local/RandomizedResponse/Definitions.lean | 25 +++++++ .../RandomizedResponse/PMFProperties.lean | 70 +++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse/DPProof.lean create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean new file mode 100644 index 00000000..2dd2e8b8 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean @@ -0,0 +1,62 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponseAlt +import Mathlib.Probability.ProbabilityMassFunction.Basic + +open PMF + +def toSingletonLists {α : Type u} (l : List α) : List (List α) := + l.map (fun x => [x]) + + + +def sum_list (Y : List ℕ) := Y.foldl (· + ·) 0 + +noncomputable def coeff {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := + (1.0 / (X.length : ℝ)) * ((den : ℝ) / (2.0 * (num : ℝ))) + +noncomputable def constants {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := + (- (X.length) / 2) + (num * X.length) / den + +def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : List (SLang Bool) := + X.map (fun x => RRSingleSample query num den h x) + + +def sumBernoulli (xs : List (SLang Bool)) : SLang Nat := + xs.foldlM (fun sum x => do + let b ← x + return sum + if b then 1 else 0 + ) 0 + +def addMulRealToRV (Y : SLang Nat) (R : Real) (S: Real): SLang Real := do + let n ← Y -- Sample a Nat from Y + return S * ((n : Real) + R) -- Convert to Real and add R +/- +variables {α : Type*} [AddMonoid α] + +instance : AddMonoid (List α) where + nsmul := fun n l => List.join (List.replicate n l) + add := (· ++ ·) + zero := [] + add_assoc := List.append_assoc + zero_add := List.nil_append + add_zero := List.append_nil + + + +noncomputable def pmf.add (X Y : PMF α) : PMF α := + X.bind (λ a => Y.map (λ b => a + b)) + +noncomputable def pmf.sum_list : List (PMF α) → PMF α +| [] => PMF.pure 0 +| (x::xs) => pmf.add x (pmf.sum_list xs) +-/ + +--def mean_of_X {T : Type} (query: T -> Bool) (X : T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : SLang ℕ := + + +noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den):= + let coef := coeff X num den + let cons := constants X num den + let s := applying_RR_individually query X num den h + let sum_of_ys := sumBernoulli s + let p_estimate := addMulRealToRV sum_of_ys cons coef + p_estimate diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean new file mode 100644 index 00000000..dbf45991 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean @@ -0,0 +1,4 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProof +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.AccuracyProof diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/DPProof.lean new file mode 100644 index 00000000..e69de29b diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean index e69de29b..1b1bcdd0 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean @@ -0,0 +1,25 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +/-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ + +open SLang +/- open MultiBernoulli -/ + +lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by + simp_all only [tsub_le_iff_right] + linarith + +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do + let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) + return Bool.xor (query l) r + +def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ + l.mapM (fun x => RRSingleSample query num den h x) + + /- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do + let r ← MultiBernoulliSample seed_list + return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ + +/- At this point, we should be set to prove that RRSample is normalized and that it is + differentially private. The definition is computable, as we need. -/ diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean new file mode 100644 index 00000000..ba3c0f53 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean @@ -0,0 +1,70 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert + + +open Slang + +#check SLang.BernoulliSample_normalizes + +lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : + HasSum (RRSingleSample query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw [@tsum_bool] + rw[RRSingleSample] + cases query l + { + simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + } + { + simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', + mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + rw [@AddCommMonoidWithOne.add_comm] + } + +lemma nil_case {T : Type} (query : T -> Bool) (num : Nat) (den: PNat) (h: 2 * num ≤ den): + ∑' (b : List Bool), (RRSample query num den h) [] b = 1 := by + have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample query num den h x) [] b = + mapM (fun x => RRSingleSample query num den h x) [] [] := by + rw [@List.mapM_nil] + simp_all only [pure, SLang.pure_apply, ↓reduceIte] + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] + rw[RRSample] + rw[h1] + rw [@List.mapM_nil] + simp + +lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * num ≤ den) + (l : List T) : ∑' (b : List Bool), RRSample query num den h l b = 1 := by + rw[RRSample] + sorry + +lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : + HasSum (RRSample query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw[RRSample] + induction l with + | nil => exact nil_case query num den h + | cons hd tl tail_ih => sorry + +lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : + HasSum (RRSample2 query s l) 1 := by + rw[RRSample2] + simp_all only [bind, pure] + rw[Summable.hasSum_iff ENNReal.summable] + rw[←MultiBernoulliSample_normalizes s] + simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] + sorry + + + +def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := + ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ + From 13f77854e9f67a329a4289626b01438a2a01d9bd Mon Sep 17 00:00:00 2001 From: PCChess Date: Fri, 11 Jul 2025 13:13:21 -0700 Subject: [PATCH 029/216] reorganize --- .../DifferentialPrivacy/{ => Pure}/Local/ENNRealLemmasSuite.lean | 0 .../DifferentialPrivacy/{ => Pure}/Local/LawfulMonadSLang.lean | 0 .../DifferentialPrivacy/{ => Pure}/Local/MultiBernoulli/Code.lean | 0 .../{ => Pure}/Local/MultiBernoulli/Properties.lean | 0 .../{ => Pure}/Local/Playground/ENNRealCoercions.lean | 0 .../DifferentialPrivacy/{ => Pure}/Local/Playground/Junk.lean | 0 .../{ => Pure}/Local/Playground/PatternMatchingSumsExamples.lean | 0 .../{ => Pure}/Local/Playground/UsefulLemmas.lean | 0 SampCert/DifferentialPrivacy/{ => Pure}/Local/PushForward.lean | 0 .../DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse.lean | 0 .../{ => Pure}/Local/RandomizedResponse/AccuracyProof.lean | 0 .../{ => Pure}/Local/RandomizedResponse/Basic.lean | 0 .../{ => Pure}/Local/RandomizedResponse/DPProof.lean | 0 .../{ => Pure}/Local/RandomizedResponse/Definitions.lean | 0 .../{ => Pure}/Local/RandomizedResponse/PMFProperties.lean | 0 .../{ => Pure}/Local/RandomizedResponseAlt.lean | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/ENNRealLemmasSuite.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/LawfulMonadSLang.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/MultiBernoulli/Code.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/MultiBernoulli/Properties.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/Playground/ENNRealCoercions.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/Playground/Junk.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/Playground/PatternMatchingSumsExamples.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/Playground/UsefulLemmas.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/PushForward.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse/AccuracyProof.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse/Basic.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse/DPProof.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse/Definitions.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponse/PMFProperties.lean (100%) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/RandomizedResponseAlt.lean (100%) diff --git a/SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/ENNRealLemmasSuite.lean rename to SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean diff --git a/SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean b/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/LawfulMonadSLang.lean rename to SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Code.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/MultiBernoulli/Code.lean rename to SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Code.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/MultiBernoulli/Properties.lean rename to SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/ENNRealCoercions.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/Playground/ENNRealCoercions.lean rename to SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/Junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/Playground/Junk.lean rename to SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/PatternMatchingSumsExamples.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/Playground/PatternMatchingSumsExamples.lean rename to SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean diff --git a/SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/Playground/UsefulLemmas.lean rename to SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean diff --git a/SampCert/DifferentialPrivacy/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/PushForward.lean rename to SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse/AccuracyProof.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse/Basic.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse/DPProof.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse/Definitions.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponse/PMFProperties.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean From 9175d5b7fc898c8432b7989ca65d5cf1233e9852 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 13:15:07 -0700 Subject: [PATCH 030/216] deletedwrongfile --- .../Pure/Local/RandomizedResponse.lean | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean deleted file mode 100644 index b12b881a..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse.lean +++ /dev/null @@ -1,115 +0,0 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert - -/- For now, we assume that our databases consist of a single element of type T, - representing a single user (i.e., a database of size one) -/ - -/- Implementation of randomized response for a single user with rational parameter 0 ≤ lambda ≤ 1/2 -/ - -/- def RRRandomizer (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : SLang Bool := do - let result1 ← SLang.BernoulliSample (2 * num + den) (2 * den) (by - linarith) -- note (2 * num + den) / (2 * den) = 1/2 + num/den - match result1 with - | true => return (query l) -- happens with probability (1/2 + num/den) - | false => return ¬(query l) -- happens with probability (1/2 - num/den) - --/ - -/- What return actually does is that it returns that Dirac distribution that is either all true or all false... - So this is wrong? --/ - -/- Note that this doesn't work: -/ - -/- def RRRandomizertest (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : Bool -> ENNReal := - fun b => - let result1 := SLang.BernoulliSample (2 * num + den) (2 * den) (by - linarith) - match b with - | true => query l - | false => ¬ query l - --/ - -/- lemma RRRandomizerpmf_lemma (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h : 2 * num < den): - HasSum (RRRandomizer query l num den h) 1 := by - simp [RRRandomizer] - simp [(Summable.hasSum_iff ENNReal.summable), tsum_bool, add_tsub_cancel_iff_le] - aesop - { sorry } - { sorry } - -def RRRandomizerPMF (query : T -> Bool) (l : T) (num : Nat) (den : PNat) (h: 2 * num < den) : PMF Bool := - ⟨RRRandomizer query l num den h, RRRandomizerpmf_lemma query l num den h⟩ - --/ - -/-- THERE ARE STILL ISSUES BELOW THIS LINE. DO NOT EDIT YET. -/ - -/- def mapper (n : Nat) (num : Nat) (den : PNat) (h : 2 * num < den) (_ : Fin n) : SLang Bool := do - let result1 ← SLang.BernoulliSample (2 * num + den) (2 * den) (by linarith) - return result1 - -def matcher (query : T -> Bool) (val_prob : T × Bool): Bool := do - let a ← val_prob.2 - - -/- Implementation of Randomized Response. Applies local randomizer to each user's data. -/ -def RandomizedResponseSample (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : (SLang (List Bool)) := do - let a ← List.replicate l.length 0 - let a ← List.map (SLang.BernoulliSample (2 * num + den) (2 * den) (by linarith)) a - let a ← List.zip l a - let a ← List.map (matcher query) a - return a - - -/-- lemma DP_RandomizedResponse (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den) : - sorry --/ - -/- Here is a special case of Randomized Response, with parameter 1/4-/ -/ - -def count_differences {T : Type} [DecidableEq T]: List T -> List T -> ℕ - | [], l => l.length - | l, [] => l.length - | x :: xs, y :: ys => - if x = y then - count_differences xs ys - else 1 + count_differences xs ys - -noncomputable def output_probabilities {T : Type} [DecidableEq T] (num : ℕ) (den : PNat) (_ : 2 * num < den) (l : List T) : SLang (List T) := - fun l' => - let diff := count_differences (List.map (fun x => x) l) l' - let same := l.length - diff - (((2 * num + den) / (2 * den)) ^ same * ((2 * num - den) / (2 * den)) ^ diff : ENNReal) - -noncomputable def RandomizedResponseSample {T : Type} [DecidableEq T] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨output_probabilities num den h (List.map query l), by sorry⟩ - -lemma log_arith: Real.log 3 ≥ 0 := Real.log_nonneg (by linarith) - -lemma log_arith1: Real.exp (Real.log 3) = 3 := Real.exp_log (by linarith) - -lemma log_arith2: ENNReal.ofReal (Real.exp ↑(Real.log 3).toNNReal) = ENNReal.ofReal 3 := by sorry - -lemma RRPureDP {T : Type} [DecidableEq T] (query : T -> Bool) (l : List T) (num : Nat) (den : PNat) (h: 2 * num < den): SLang.PureDP (RandomizedResponseSample query num den h) ((Real.log 3).toNNReal) := - by - rw[SLang.PureDP] - rw[SLang.DP] - intro l₁ l₂ hl₁l₂ S - /- Current bound is not correct of course, should depend on num/den, this is just for illustration -/ - /- Study SampCert PureDP proofs -/ - sorry - -noncomputable def RRImplementation {T : Type} [DecidableEq T] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T): SLang (List Bool) := do - let r ← output_probabilities num den h (List.map query l) - return r - - -/- def CoinFlipSample (query : T -> Bool) (l : T): SLang (Bool):= do - RRRandomizer query l 1 4 (by decide) /- Randomized Response with parameter 1/4 -/ - -def CoinFlipPMF (query : T -> Bool) (l : T) : PMF Bool := - ⟨CoinFlipSample query l, RRRandomizerpmf_lemma query l 1 4 (by decide)⟩ - -/- need randomized response as a mechanism... -/ --/ From 0868efce08a78b4c8a843ca9819716a76b7e0046 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 13:17:18 -0700 Subject: [PATCH 031/216] updated --- .../Pure/Local/RandomizedResponseAlt.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 99813aed..7cf5d09d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -9,6 +9,7 @@ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 simp_all only [tsub_le_iff_right] linarith +/- Eventually, we may want query to return more than just a Boolean -/ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r @@ -24,8 +25,6 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ -#check SLang.BernoulliSample_normalizes - lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : HasSum (RRSingleSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -66,6 +65,7 @@ lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * n rw[RRSample] sorry +/- This should now follow from Renee's abstraction of the MultiBernoulli proof -/ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -74,7 +74,7 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) | nil => exact nil_case query num den h | cons hd tl tail_ih => sorry -lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : +/- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : HasSum (RRSample2 query s l) 1 := by rw[RRSample2] simp_all only [bind, pure] @@ -82,7 +82,7 @@ lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l rw[←MultiBernoulliSample_normalizes s] simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] sorry - +-/ def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := From 22ab1616cf2eafc970f0020ed5dc0ad2ca04cbd7 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 13:28:59 -0700 Subject: [PATCH 032/216] allissuesfixed --- .../Pure/Local/MultiBernoulli/Properties.lean | 4 ++-- .../Pure/Local/RandomizedResponse/AccuracyProof.lean | 6 ++++-- .../Pure/Local/RandomizedResponse/Definitions.lean | 4 ++++ .../Pure/Local/RandomizedResponse/PMFProperties.lean | 11 ++++++----- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean index 8a294cea..abd22cec 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean @@ -3,8 +3,8 @@ import Mathlib.Probability.Independence.Basic import SampCert import SampCert.SLang import Mathlib.Data.Set.Basic -import SampCert.DifferentialPrivacy.Local.ENNRealLemmasSuite -import SampCert.DifferentialPrivacy.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code namespace MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index 2dd2e8b8..21fa41ac 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -1,13 +1,15 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponseAlt +import SampCert import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +open SLang open PMF +open RandomizedResponse def toSingletonLists {α : Type u} (l : List α) : List (List α) := l.map (fun x => [x]) - def sum_list (Y : List ℕ) := Y.foldl (· + ·) 0 noncomputable def coeff {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index 1b1bcdd0..7f2f7ec1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -2,6 +2,8 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ +namespace RandomizedResponse + open SLang /- open MultiBernoulli -/ @@ -23,3 +25,5 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ + +end RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index ba3c0f53..b8fab6ee 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -1,9 +1,11 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +open SLang +open RandomizedResponse -open Slang - +#check RandomizedResponse.RRSingleSample #check SLang.BernoulliSample_normalizes lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : @@ -54,7 +56,7 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) | nil => exact nil_case query num den h | cons hd tl tail_ih => sorry -lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : +/- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : HasSum (RRSample2 query s l) 1 := by rw[RRSample2] simp_all only [bind, pure] @@ -62,9 +64,8 @@ lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l rw[←MultiBernoulliSample_normalizes s] simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] sorry - +-/ def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ - From 08a661cc3a3946657d4ee7ecf34543a8c0b70eef Mon Sep 17 00:00:00 2001 From: Renee Date: Fri, 11 Jul 2025 17:10:54 -0400 Subject: [PATCH 033/216] norm file --- .../Local/Normalization.lean | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Local/Normalization.lean diff --git a/SampCert/DifferentialPrivacy/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Local/Normalization.lean new file mode 100644 index 00000000..8b90f3dd --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/Normalization.lean @@ -0,0 +1,82 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Probability.Independence.Basic +import SampCert +import SampCert.SLang +import Mathlib.Data.Set.Basic + +lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Bool)(tl : List α): +(∑' (a_1 : List Bool), if a = b :: a_1 then mapM f tl a_1 else 0) = +(if a.head? = b then mapM f tl a.tail else 0) := by + cases a with + | nil => simp + | cons ah atl => + simp [-mapM] + split + next h => + subst h + simp_all only [true_and] + rw [tsum_eq_single] + --simp_all only [mapM] + split + rename_i h + on_goal 2 => rename_i h + apply Eq.refl + simp_all only [not_true_eq_false] + intro b' a + simp_all only [ne_eq, mapM, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + next h => simp_all only [false_and, ↓reduceIte, _root_.tsum_zero] + +lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) (b : Bool): +(∑' (a : List Bool), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = + ∑' (a : List Bool), f hd b * mapM f tl a := by + simp_all only [mul_ite, mul_zero] + apply symm + sorry + +lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring + +lemma simp_4 [LawfulMonad SLang] (α : Type)(a : α)(f: α → SLang Bool): ∑' (i : Bool), f a i = ∑' (b : Bool), f a b := by + rfl + +lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd : α)(tl : List α) (h : ∑' (b : Bool), f hd b = 1): ∑' (a : Bool), f hd a * mapM f tl b = mapM f tl b := by + rw [tsum_bool] + rw [ennreal_mul_assoc] + rw [←tsum_bool] + rw [simp_4] + rw [h] + rw [@CanonicallyOrderedCommSemiring.one_mul] + + + +lemma Norm_func_norm_on_list [LawfulMonad SLang] (α : Type)(f: α → SLang Bool) (al: List α): + (∀ a : α, ∑' (b : Bool), f a b = 1) → ∑' (b : List Bool), mapM f al b = 1 := by + intro h + induction al with + | nil => + rw [@List.mapM_nil] + simp[pure] + rw [ENNReal.tsum_eq_add_tsum_ite []] + simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] + | cons hd tl ih => + simp [List.mapM_cons, -mapM] + conv => + enter [1, 1, b, 1, a] + simp [-mapM] + rw [simplifier1_gen] + /- rewrite as a double sum, the first sum being over possible a.heads, and the second + some being over all list Bools, with the conditional now being on the Boolean + in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ + rw [@ENNReal.tsum_comm] + conv => + enter [1, 1, b] + rw [simplifier2_gen] + rw [@ENNReal.tsum_comm] + conv => + enter [1, 1, b] + rw [simplifier3_gen] + rfl + rw[h hd] + apply ih From 4ee3680c5d89cc3d3f5726910f21e36335f34b08 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 16:24:22 -0700 Subject: [PATCH 034/216] beforepull --- .../Pure/Local/RandomizedResponse/Definitions.lean | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index 7f2f7ec1..ebb428b1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -15,6 +15,11 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r +def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (query l) r) +/- Y is a random variable which outputs the function measuring whether or not a given person + changes their answer. It is distributed according to the probability distribution + from which we sample r.-/ + def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) From 38884e39df37178793ab45266c2dc75372c84ab9 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 11 Jul 2025 19:36:48 -0400 Subject: [PATCH 035/216] normalization of MB and RR --- .../Pure/Local/MultiBernoulli/Properties.lean | 34 +++++-------------- .../{ => Pure}/Local/Normalization.lean | 0 .../RandomizedResponse/PMFProperties.lean | 13 +++++++ 3 files changed, 21 insertions(+), 26 deletions(-) rename SampCert/DifferentialPrivacy/{ => Pure}/Local/Normalization.lean (100%) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean index abd22cec..dc1438d4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean @@ -5,7 +5,7 @@ import SampCert.SLang import Mathlib.Data.Set.Basic import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code - +import SampCert.DifferentialPrivacy.Pure.Local.Normalization namespace MultiBernoulli open SLang @@ -86,6 +86,8 @@ lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): apply symm apply list_bool_tsum_only_tl b +lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring + lemma simplifier3: ∑' (a : Bool), bernoulli_mapper hd a * mapM bernoulli_mapper tl b = mapM bernoulli_mapper tl b := by rw [tsum_bool] @@ -96,30 +98,10 @@ lemma simplifier3: lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by - rw [MultiBernoulliSample] - induction seeds with - | nil => rw [@List.mapM_nil] - simp[pure] - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp_all only [↓reduceIte, ite_self, tsum_ite_not, add_zero] - simp - | cons hd tl ih => - simp [List.mapM_cons, -mapM] - conv => - enter [1, 1, b, 1, a] - simp [-mapM] - rw [simplifier1] - /- rewrite as a double sum, the first sum being over possible a.heads, and the second - some being over all list Bools, with the conditional now being on the Boolean - in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ - rw [@ENNReal.tsum_comm] - conv => - enter [1, 1, b] - rw [simplifier2] - rw [@ENNReal.tsum_comm] - conv => - enter [1, 1, b] - rw [simplifier3] - apply ih + unfold MultiBernoulliSample + apply Norm_func_norm_on_list + intro a + rw [bernoulli_mapper_sums_to_1] + end MultiBernoulli diff --git a/SampCert/DifferentialPrivacy/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Local/Normalization.lean rename to SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index b8fab6ee..8fa9b0c8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -1,6 +1,8 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.Samplers.Bernoulli.Properties open SLang open RandomizedResponse @@ -56,6 +58,8 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) | nil => exact nil_case query num den h | cons hd tl tail_ih => sorry + + /- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : HasSum (RRSample2 query s l) 1 := by rw[RRSample2] @@ -69,3 +73,12 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ + + +lemma RR_normalizes [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) + (h: 2 * num ≤ den) (l : List T) : ∑' (x : List Bool), RRSample query num den h l x = 1 := by + unfold RRSample + apply Norm_func_norm_on_list + intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSingleSample_PMF_helper From 117f109f149951a145a6ff1aaa86eb81b28f4dae Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 16:38:43 -0700 Subject: [PATCH 036/216] Ydef --- .../Pure/Local/MultiBernoulli/Properties.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean index abd22cec..a5eabe2d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean @@ -76,8 +76,9 @@ lemma simplifier1 (a : List Bool) (b : Bool): lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): ∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by - apply Equiv.tsum_eq_tsum_of_support - sorry + apply Equiv.tsum_eq_tsum_of_support + sorry + sorry lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): (∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = From 08fc08e550a7cbd405df278705b3d92bcc95947a Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 11 Jul 2025 16:58:25 -0700 Subject: [PATCH 037/216] neighbourdef --- .../Local/LocalDP/DPwithGeneralNeighbour.lean | 15 +++++++++++++++ .../Local/LocalDP/DPwithUpdateNeighbour.lean | 13 +++++++++++++ .../Pure/Local/LocalDP/UpdateNeighbour.lean | 17 +++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean new file mode 100644 index 00000000..50394836 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean @@ -0,0 +1,15 @@ +import SampCert + +namespace SLang + +open SLang +open Classical + +def DP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour: List T -> List T -> Prop) (ε : ℝ): Prop := + ∀ l₁ l₂ : List T, VariableNeighbour l₁ l₂ → ∀ S : Set U, + (∑' x : U, if x ∈ S then m l₁ x else 0) / (∑' x : U, if x ∈ S then m l₂ x else 0) ≤ ENNReal.ofReal (Real.exp ε) + +def PureDP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour : List T -> List T -> Prop) (ε : NNReal) : Prop := + DP_withGeneralNeighbour m VariableNeighbour ε + +end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean new file mode 100644 index 00000000..845f8c20 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -0,0 +1,13 @@ +import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithGeneralNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.UpdateNeighbour + +open SLang +open Classical + +namespace SLang + +def DP_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := + DP_withGeneralNeighbour m (UpdateNeighbour) ε + +end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean new file mode 100644 index 00000000..b541fe8a --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean @@ -0,0 +1,17 @@ +import SampCert + +namespace SLang + +/- Lists which differ by update of a single entry. -/ +inductive UpdateNeighbour (l₁ l₂ : List T) : Prop where + | Update : l₁ = a ++ [n] ++ b → l₂ = a ++ [m] ++ b -> UpdateNeighbour l₁ l₂ + +/- +UpdateNeighbour relation is symmetric. +-/ +def UpdateNeighbour_symm (l₁ l₂ : List T) (H : UpdateNeighbour l₁ l₂) : UpdateNeighbour l₂ l₁ := by + cases H + · rename_i _ _ _ _ Hl1 Hl2 + exact UpdateNeighbour.Update Hl2 Hl1 + +end SLang From 66c1637c709adb0462adbd9513ed25f93317d7b2 Mon Sep 17 00:00:00 2001 From: PCChess Date: Fri, 11 Jul 2025 17:07:42 -0700 Subject: [PATCH 038/216] Update AccuracyProof.lean --- .../Pure/Local/RandomizedResponse/AccuracyProof.lean | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index 21fa41ac..b20558e1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -52,7 +52,10 @@ noncomputable def pmf.sum_list : List (PMF α) → PMF α | (x::xs) => pmf.add x (pmf.sum_list xs) -/ ---def mean_of_X {T : Type} (query: T -> Bool) (X : T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : SLang ℕ := +def p {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : ℚ := + let bool_lst := X.map query + let true_count := (bool_lst.filter (fun b => b)).length + (true_count) / X.length noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den):= From 6e88b001014df02deedef1c709820a7c0de2987f Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Fri, 11 Jul 2025 17:08:34 -0700 Subject: [PATCH 039/216] Co-authored-by: perrynchang --- .../DifferentialPrivacy/Local/PureDPGen.lean | 0 .../Local/RandomizedResponseAlt.lean | 172 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Local/PureDPGen.lean create mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean diff --git a/SampCert/DifferentialPrivacy/Local/PureDPGen.lean b/SampCert/DifferentialPrivacy/Local/PureDPGen.lean new file mode 100644 index 00000000..e69de29b diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean new file mode 100644 index 00000000..b2f4a0c6 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -0,0 +1,172 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +import SampCert.DifferentialPrivacy.Pure.DP +import SampCert.Samplers.Bernoulli.Properties +import SampCert.DifferentialPrivacy.Local.LawfulMonadSLang + + +lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by + simp_all only [tsub_le_iff_right] + linarith + +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do +/- RRSingleSample takes in a single user and produces a sample according to the distribution + induced by the user's actual response. + If the user's actual response to the query is "true", then RRSingleSample samples "true" + with probability 1/2 + num/den. If the user's actual response to the query is "false," then RRSingleSample + samples true with probability 1/2 - num/den. +-/ + match query l with + | true => let r ← SLang.BernoulliSample (den + 2 * num) (2 * den) (by linarith) + return r + | false => let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) + return r + +def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ + l.mapM (fun x => RRSingleSample query num den h x) + +def RRSingleSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do + let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) + return Bool.xor (query l) r + +def RRSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ + l.mapM (fun x => RRSingleSample2 query num den h x) + + lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : + HasSum (RRSingleSample2 query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw [@tsum_bool] + rw[RRSingleSample2] + cases query l + { + simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + } + { + simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, + Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', + mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] + rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] + rw[tsum_bool] + rw [@AddCommMonoidWithOne.add_comm] + } + +lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : + HasSum (RRSample2 query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + induction l.length with + | zero => have h1: l.length = 0 := by + sorry + /- exact nil_case query num den h -/ + | succ n ha => exact ha +/- At this point, we should be set to prove that RRSample is normalized and that it is + differentially private. The definition is computable, as we need. -/ +def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := + ⟨RRSample2 query num den h l, RRSample2_PMF_helper query num den h l⟩ +-- namespace SLang + + +namespace SLang +lemma simplifier_1 (f : T -> SLang Bool): +(∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by +rw[tsum_eq_single c] +aesop +intro b h +simp_all only [ne_eq, mapM, ite_eq_right_iff] +intro a +subst a +simp_all only [not_true_eq_false] + + + + + + + +lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): +mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by +rw[List.mapM_cons] +simp[-mapM] +rw [@tsum_bool] +cases b with +| true => +simp[-mapM] +conv => + enter [1, 2] + rw [simplifier_1] +| false => +simp [-mapM] +conv => + enter [1, 2] + rw [simplifier_1] + +lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): +RRSample2 query num den h (hd::tl) (b::c) = RRSingleSample2 query num den h hd b * RRSample2 query num den h tl c := by +unfold RRSample2 +set f := fun x => RRSingleSample2 query num den h x +rw[mapM_dist_cons f b c hd tl] + + + + + +lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +RRSample2 query num den h l a = (∏'(i: Fin l.length), RRSingleSample2 query num den h (l.get i) (a.get (Fin.cast k i ))):= by +induction l generalizing a with +| nil => + simp + rw[List.length_nil] at k + symm at k + apply List.eq_nil_of_length_eq_zero at k + rw[k] + unfold RRSample2 + rw [List.mapM_nil] + simp [pure] + +| cons hd tl ih => + simp + simp at ih + cases a with + | nil => + simp at k + | cons b c => + rw[RRSample_rec query num den h] + rw[ih c] + rw [@tprod_fintype] + rw [@tprod_fintype] + + rw[Fin.prod_univ_succ] + simp + simp at k + exact k + +theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample2 query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob + +namespace SLang + + +theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : +PureDP (RRSample_PMF query num den h) ((num: NNReal) / den) := by +-- let ε := ↑num / NNReal.ofPNat den +apply singleton_to_event +intros l₁ l₂ h_adj x +rw[prod_of_ind_prob_PMF query num den h x l₁] +rw[prod_of_ind_prob_PMF query num den h x l₂] +cases h_adj with +| Addition hl₁ hl₂ => sorry +| Deletion a b => sorry +| Update a b => +have h1: l₁.length = l₂.length := by aesop +have h2: Fin (l₁.length) = Fin (l₂.length) := by rw[h1] +rw[h2] + + + +lemma differs_in_one_row {T: Type}(l₁ l₂: List T)(adj: Neighbour l₁ l₂)(h: l₁.length = l₂.length): +∃ From 53b440df8684105dc22d80d2d32c2e4b441b12ca Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Fri, 11 Jul 2025 21:32:58 -0700 Subject: [PATCH 040/216] DP Proof Updates --- .../DifferentialPrivacy/Local/PureDPGen.lean | 0 .../Local/RandomizedResponseAlt.lean | 15 +-- .../Pure/Local/RandomizedResponseAlt.lean | 94 +++++++++++++++++++ 3 files changed, 96 insertions(+), 13 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Local/PureDPGen.lean diff --git a/SampCert/DifferentialPrivacy/Local/PureDPGen.lean b/SampCert/DifferentialPrivacy/Local/PureDPGen.lean deleted file mode 100644 index e69de29b..00000000 diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean index b2f4a0c6..9bce62d7 100644 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean @@ -2,7 +2,7 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties -import SampCert.DifferentialPrivacy.Local.LawfulMonadSLang +import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by @@ -158,15 +158,4 @@ apply singleton_to_event intros l₁ l₂ h_adj x rw[prod_of_ind_prob_PMF query num den h x l₁] rw[prod_of_ind_prob_PMF query num den h x l₂] -cases h_adj with -| Addition hl₁ hl₂ => sorry -| Deletion a b => sorry -| Update a b => -have h1: l₁.length = l₂.length := by aesop -have h2: Fin (l₁.length) = Fin (l₂.length) := by rw[h1] -rw[h2] - - - -lemma differs_in_one_row {T: Type}(l₁ l₂: List T)(adj: Neighbour l₁ l₂)(h: l₁.length = l₂.length): -∃ +sorry diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 7cf5d09d..8a692ecc 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -1,5 +1,8 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert +import SampCert.DifferentialPrivacy.Pure.DP +import SampCert.Samplers.Bernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang @@ -87,3 +90,94 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ + + + +namespace SLang +lemma simplifier_1 (f : T -> SLang Bool): +(∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by +rw[tsum_eq_single c] +aesop +intro b h +simp_all only [ne_eq, mapM, ite_eq_right_iff] +intro a +subst a +simp_all only [not_true_eq_false] + + + + + + + +lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): +mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by +rw[List.mapM_cons] +simp[-mapM] +rw [@tsum_bool] +cases b with +| true => +simp[-mapM] +conv => + enter [1, 2] + rw [simplifier_1] +| false => +simp [-mapM] +conv => + enter [1, 2] + rw [simplifier_1] + +lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): +RRSample query num den h (hd::tl) (b::c) = RRSingleSample query num den h hd b * RRSample query num den h tl c := by +unfold RRSample +set f := fun x => RRSingleSample query num den h x +rw[mapM_dist_cons f b c hd tl] + + + + + +lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +RRSample query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by +induction l generalizing a with +| nil => + simp + rw[List.length_nil] at k + symm at k + apply List.eq_nil_of_length_eq_zero at k + rw[k] + unfold RRSample + rw [List.mapM_nil] + simp [pure] + +| cons hd tl ih => + simp + simp at ih + cases a with + | nil => + simp at k + | cons b c => + rw[RRSample_rec query num den h] + rw[ih c] + rw [@tprod_fintype] + rw [@tprod_fintype] + + rw[Fin.prod_univ_succ] + simp + simp at k + exact k + +theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob + +namespace SLang + + +theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : +PureDP (RRSample_PMF query num den h) ((num: NNReal) / den) := by +-- let ε := ↑num / NNReal.ofPNat den +apply singleton_to_event +intros l₁ l₂ h_adj x +rw[prod_of_ind_prob_PMF query num den h x l₁] +rw[prod_of_ind_prob_PMF query num den h x l₂] +sorry From f54f6420f2250b69ab7a8826a11feeddb4507e2e Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Mon, 14 Jul 2025 09:07:20 -0700 Subject: [PATCH 041/216] DP proof updates and new definitions --- .../Local/LocalDP/DPwithGeneralNeighbour.lean | 4 ++ .../Local/LocalDP/DPwithUpdateNeighbour.lean | 2 + .../Pure/Local/LocalDP/UpdateNeighbour.lean | 2 +- .../Pure/Local/RandomizedResponseAlt.lean | 50 +++++++++++++++++-- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean index 50394836..5b5a6b51 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean @@ -12,4 +12,8 @@ def DP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour: List T -> Li def PureDP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour : List T -> List T -> Prop) (ε : NNReal) : Prop := DP_withGeneralNeighbour m VariableNeighbour ε +def DP_singleton_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour: List T -> List T -> Prop) (ε : ℝ): Prop := + ∀ l₁ l₂ : List T, VariableNeighbour l₁ l₂ → ∀ x : U, + (m l₁ x) / (m l₂ x) ≤ ENNReal.ofReal (Real.exp ε) + end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index 845f8c20..4fd6f6db 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -10,4 +10,6 @@ namespace SLang def DP_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_withGeneralNeighbour m (UpdateNeighbour) ε +def DP_singleton_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := + DP_singleton_withGeneralNeighbour m (UpdateNeighbour) ε end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean index b541fe8a..f5e2721f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean @@ -4,7 +4,7 @@ namespace SLang /- Lists which differ by update of a single entry. -/ inductive UpdateNeighbour (l₁ l₂ : List T) : Prop where - | Update : l₁ = a ++ [n] ++ b → l₂ = a ++ [m] ++ b -> UpdateNeighbour l₁ l₂ + | Update: l₁ = a ++ [n] ++ b → l₂ = a ++ [m] ++ b -> UpdateNeighbour l₁ l₂ /- UpdateNeighbour relation is symmetric. diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 8a692ecc..eb1f1022 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -3,6 +3,7 @@ import SampCert import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang @@ -170,14 +171,55 @@ induction l generalizing a with theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -namespace SLang + +open Classical + +theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_withUpdateNeighbour m ε) : + DP_withUpdateNeighbour m ε := by + simp [DP_withUpdateNeighbour] + simp [DP_singleton_withUpdateNeighbour] at h + intros l₁ l₂ h1 S + replace h1 := h l₁ l₂ h1 + have A : ∀ (x : U), (if x ∈ S then m l₁ x else 0) / (if x ∈ S then m l₂ x else 0) ≤ ENNReal.ofReal ε.exp := by + aesop + have B : ∀ b : ENNReal, b ≠ 0 ∨ ENNReal.ofReal ε.exp ≠ ⊤ := by + aesop + have C : ∀ b : ENNReal, b ≠ ⊤ ∨ ENNReal.ofReal ε.exp ≠ 0 := by + intro b + right + simp + exact Real.exp_pos ε + have D := fun { x : U } => @ENNReal.div_le_iff_le_mul (if x ∈ S then m l₁ x else 0) (if x ∈ S then m l₂ x else 0) (ENNReal.ofReal ε.exp) (B (if x ∈ S then m l₂ x else 0)) (C (if x ∈ S then m l₂ x else 0)) + have E := fun x : U => D.1 (A x) + have F := ENNReal.tsum_le_tsum E + rw [ENNReal.tsum_mul_left] at F + rw [← ENNReal.div_le_iff_le_mul] at F + · clear h1 A B C D + trivial + · aesop + · right + simp + exact Real.exp_pos ε +lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → Bool → ENNReal)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁.get i) (x[↑i])) / + ∏' (i : Fin l₂.length), f (l₂.get i) (x[↑i]) = f (l₁[i]) (x[↑i]) := by sorry + + +lemma prod_split (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den)(l : List T)(x: List Bool)(hl: l = a++b)(h1: l.length = x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): + ∏' (i : Fin l.length), RRSingleSample query num den h (l[↑i]) (x[↑i]) = (∏'(i : Fin a.length), RRSingleSample query num den h (a[i]) (x[↑i]))*(∏'(i : Fin b.length), RRSingleSample query num den h (b[i]) (x[↑i])) := by sorry + theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : -PureDP (RRSample_PMF query num den h) ((num: NNReal) / den) := by +DP_withUpdateNeighbour (RRSample_PMF query num den h) ((num: NNReal) / den) := by -- let ε := ↑num / NNReal.ofPNat den -apply singleton_to_event +apply singleton_to_event2 intros l₁ l₂ h_adj x rw[prod_of_ind_prob_PMF query num den h x l₁] rw[prod_of_ind_prob_PMF query num den h x l₂] -sorry + +cases h_adj with +| Update hl₁ hl₂ => +have hlen: l₁.length = l₂.length := by aesop +simp +rename_i a n b m +rw[hl₁] From de2b8bbc08c8d47a25e13f3c05aa648bfa4ae52f Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 14 Jul 2025 09:49:38 -0700 Subject: [PATCH 042/216] monday --- lakefile.lean | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lakefile.lean b/lakefile.lean index f918d059..8dac9cf5 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -3,10 +3,16 @@ open Lake DSL package «sampcert» where -- add any package configuration options here + /--/ moreLinkArgs := #[ + "-L./.lake/packages/LeanCopilot/.lake/build/lib", + "-lctranslate2" + ] -/ require mathlib from git "https://github.com/leanprover-community/mathlib4.git" @ "v4.10.0" +-- require LeanCopilot from git "https://github.com/lean-dojo/LeanCopilot.git" @ "49ca8d6" + @[default_target] lean_lib «SampCert» where extraDepTargets := #[`libleanffi,`libleanffidyn] From 1fd25fa51f8ad12fa364da0c33dd4cd51ba99232 Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 14 Jul 2025 09:59:32 -0700 Subject: [PATCH 043/216] Update DPProof.lean --- .../Pure/Local/RandomizedResponse/DPProof.lean | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index e69de29b..e1ebfd6b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -0,0 +1,17 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.Samplers.Bernoulli.Properties + +lemma temp {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : + (1/2 + num/den) / (1/2 - num/den) = (1 + (2 * num)/den) / (1 - (2*num) / den) := by + have hden : den ≠ (0 : ℕ+) := by exact PNat.ne_zero den + + + +lemma final_step {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : + (1/2 + num/den) / (1/2 - num/den) ≤ Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den))) := by + rw [Real.exp_log] + + apply le_of_eq From 0b67adf3ad625ec62abf2eea733f16266c2522e5 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 14 Jul 2025 13:49:55 -0700 Subject: [PATCH 044/216] rrbasicproofs --- .../Pure/Local/LocalDP/UpdateNeighbour.lean | 2 +- .../RandomizedResponse/PMFProperties.lean | 43 ++----- .../Pure/Local/RandomizedResponseAlt.lean | 114 ++++++++++++++---- 3 files changed, 100 insertions(+), 59 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean index f5e2721f..97462d9c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean @@ -3,7 +3,7 @@ import SampCert namespace SLang /- Lists which differ by update of a single entry. -/ -inductive UpdateNeighbour (l₁ l₂ : List T) : Prop where +inductive UpdateNeighbour {T : Type} (l₁ l₂ : List T) : Prop where | Update: l₁ = a ++ [n] ++ b → l₂ = a ++ [m] ++ b -> UpdateNeighbour l₁ l₂ /- diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index 8fa9b0c8..d32ca560 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -32,33 +32,14 @@ lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : rw [@AddCommMonoidWithOne.add_comm] } -lemma nil_case {T : Type} (query : T -> Bool) (num : Nat) (den: PNat) (h: 2 * num ≤ den): - ∑' (b : List Bool), (RRSample query num den h) [] b = 1 := by - have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample query num den h x) [] b = - mapM (fun x => RRSingleSample query num den h x) [] [] := by - rw [@List.mapM_nil] - simp_all only [pure, SLang.pure_apply, ↓reduceIte] - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] - rw[RRSample] - rw[h1] - rw [@List.mapM_nil] - simp - -lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * num ≤ den) - (l : List T) : ∑' (b : List Bool), RRSample query num den h l b = 1 := by - rw[RRSample] - sorry - -lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : +lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] - rw[RRSample] - induction l with - | nil => exact nil_case query num den h - | cons hd tl tail_ih => sorry - - + unfold RRSample + apply Norm_func_norm_on_list + intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSingleSample_PMF_helper /- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : HasSum (RRSample2 query s l) 1 := by @@ -70,15 +51,5 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) sorry -/ - -def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := +def RRSample_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ - - -lemma RR_normalizes [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) - (h: 2 * num ≤ den) (l : List T) : ∑' (x : List Bool), RRSample query num den h l x = 1 := by - unfold RRSample - apply Norm_func_norm_on_list - intro a - rw [← Summable.hasSum_iff ENNReal.summable] - apply RRSingleSample_PMF_helper diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index eb1f1022..c55b2b6c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -18,6 +18,40 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r +lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): + RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ + +lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): + RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): + RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): + RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + /- This is the same state as the first lemma that's not working, + again it's just annoying arithmetic. -/ + sorry + def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -105,12 +139,6 @@ intro a subst a simp_all only [not_true_eq_false] - - - - - - lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by rw[List.mapM_cons] @@ -135,9 +163,6 @@ set f := fun x => RRSingleSample query num den h x rw[mapM_dist_cons f b c hd tl] - - - lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by induction l generalizing a with @@ -201,25 +226,70 @@ theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_wit simp exact Real.exp_pos ε - -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → Bool → ENNReal)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁.get i) (x[↑i])) / - ∏' (i : Fin l₂.length), f (l₂.get i) (x[↑i]) = f (l₁[i]) (x[↑i]) := by sorry - +lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b) +(hx: l₁.length = x.length)(hy: l₂.length = x.length): + (∏' (i : Fin l₁.length), RRSingleSample query num den h l₁[i] x[i]) / + ∏' (i : Fin l₂.length), RRSingleSample query num den h l₂[i] x[i] += f (l₁[a.length]'(by sorry)) (x[a.length]' (by sorry)) := by sorry lemma prod_split (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den)(l : List T)(x: List Bool)(hl: l = a++b)(h1: l.length = x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): - ∏' (i : Fin l.length), RRSingleSample query num den h (l[↑i]) (x[↑i]) = (∏'(i : Fin a.length), RRSingleSample query num den h (a[i]) (x[↑i]))*(∏'(i : Fin b.length), RRSingleSample query num den h (b[i]) (x[↑i])) := by sorry - -theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : + ∏' (i : Fin l.length), RRSingleSample query num den h (l[↑i]) (x[↑i]) + = (∏'(i : Fin a.length), RRSingleSample query num den h (a[i]) (x[↑i]))*(∏'(i : Fin b.length), RRSingleSample query num den h (b[i]) (x[↑i])) := by sorry + +lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): + RRSingleSample query num den h a b / RRSingleSample query num den h a' b + ≤ (den + 2 * num) / (den - 2 * num) := by + cases b with + | true => + cases hqa : query a with + | true => + rw [RRSingleSample_true_true _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => + rw [RRSingleSample_false_true _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] + sorry + -- arithmetic now + | false => + cases hqa : query a with + | true => + rw [RRSingleSample_true_false _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => + rw [RRSingleSample_false_false _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + +theorem RRSample_is_DP {T : Type} (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : DP_withUpdateNeighbour (RRSample_PMF query num den h) ((num: NNReal) / den) := by -- let ε := ↑num / NNReal.ofPNat den apply singleton_to_event2 intros l₁ l₂ h_adj x rw[prod_of_ind_prob_PMF query num den h x l₁] rw[prod_of_ind_prob_PMF query num den h x l₂] - cases h_adj with -| Update hl₁ hl₂ => -have hlen: l₁.length = l₂.length := by aesop -simp -rename_i a n b m -rw[hl₁] +| Update hl₁ hl₂ => sorry + -- have hlen: l₁.length = l₂.length := by aesop + -- simp + -- rename_i a n b m From e8cc097229d243534f819dd18e67f35c9c1e37a4 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Mon, 14 Jul 2025 17:49:21 -0400 Subject: [PATCH 045/216] ethan's code --- .../Pure/Local/Normalization.lean | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index 8b90f3dd..ca91b85e 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -3,6 +3,8 @@ import Mathlib.Probability.Independence.Basic import SampCert import SampCert.SLang import Mathlib.Data.Set.Basic +import Mathlib.Data.Set.Basic + lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Bool)(tl : List α): (∑' (a_1 : List Bool), if a = b :: a_1 then mapM f tl a_1 else 0) = @@ -29,14 +31,66 @@ lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Boo simp_all only [not_true_eq_false] next h => simp_all only [false_and, ↓reduceIte, _root_.tsum_zero] + +open Set + +lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by + ext l -- extensionality: sets equal iff same elements + constructor + · intro _ + -- show l is in the tail set: pick any Bool x and let xs := l + use true, l + rfl + · rintro ⟨x, xs, h⟩ + -- l = tail of some cons => l is a list => l ∈ univ + subst h + let ll := true::l + have h: ll.tail =l := by rfl + rw [← h] + exact mem_univ _ + +lemma all_list_eq_all_true_tails (b: Bool):(univ : Set (List Bool)) = { l | ∃ xs, l = (b :: xs).tail } := by + ext l + constructor + intro a + simp_all only [mem_univ, List.tail_cons, exists_eq', setOf_true] + intro b + simp_all only [List.tail_cons, exists_eq', setOf_true, mem_univ] + +lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): +∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by + apply Equiv.tsum_eq_tsum_of_support + intro x + + case e => + let e₁ := Equiv.Set.image (List.cons b) (Function.support f) (fun _ _ h => List.cons_injective h) + + have h_eq : (List.cons b) '' (Function.support f) = + Function.support (fun y => if y.head? = some b then f y.tail else 0) := by + sorry + + let e₂ := Equiv.Set.ofEq h_eq + exact e₁.trans e₂ + + case he => simp + + + + + + + + + + lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) (b : Bool): (∑' (a : List Bool), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = ∑' (a : List Bool), f hd b * mapM f tl a := by simp_all only [mul_ite, mul_zero] apply symm - sorry + apply list_bool_tsum_only_tl b -lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring +lemma f (b :: c).tail = (if ((b::c).head? = b) then f c else 0)ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring lemma simp_4 [LawfulMonad SLang] (α : Type)(a : α)(f: α → SLang Bool): ∑' (i : Bool), f a i = ∑' (b : Bool), f a b := by rfl From 494dd545722029844ab53d6802962e54c798c262 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 14 Jul 2025 15:53:23 -0700 Subject: [PATCH 046/216] listlemmaproven --- .../Pure/Local/Normalization.lean | 50 ++++++++++++------- .../Pure/Local/RandomizedResponseAlt.lean | 13 +++-- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index ca91b85e..7fce5479 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -61,28 +61,44 @@ lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): ∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by apply Equiv.tsum_eq_tsum_of_support intro x - case e => let e₁ := Equiv.Set.image (List.cons b) (Function.support f) (fun _ _ h => List.cons_injective h) - have h_eq : (List.cons b) '' (Function.support f) = Function.support (fun y => if y.head? = some b then f y.tail else 0) := by - sorry - + rw [@Set.ext_iff] + intro L + apply Iff.intro + { intro hx + have lhead: L.head? = some b := by + simp_all only [mem_image, Function.mem_support, ne_eq] + obtain ⟨w, h⟩ := hx + obtain ⟨_, right⟩ := h + subst right + simp_all only [List.head?_cons] + rw[Function.support] + rw [@mem_setOf] + rw [lhead] + simp + simp_all only [mem_image, Function.mem_support, ne_eq] + obtain ⟨w, h⟩ := hx + obtain ⟨left, right⟩ := h + subst right + simp_all only [List.head?_cons, List.tail_cons, not_false_eq_true] + } + { intro hx + rw [Function.support] at hx + rw [@mem_setOf] at hx + have lhead: L.head? = some b := by simp_all only [ne_eq, ite_eq_right_iff, Classical.not_imp] + simp_all only [↓reduceIte, ne_eq, mem_image, Function.mem_support] + apply Exists.intro L.tail + apply And.intro + exact hx + rw [List.cons_head?_tail lhead] + } let e₂ := Equiv.Set.ofEq h_eq exact e₁.trans e₂ - case he => simp - - - - - - - - - lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) (b : Bool): (∑' (a : List Bool), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = ∑' (a : List Bool), f hd b * mapM f tl a := by @@ -90,11 +106,11 @@ lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) apply symm apply list_bool_tsum_only_tl b -lemma f (b :: c).tail = (if ((b::c).head? = b) then f c else 0)ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring - lemma simp_4 [LawfulMonad SLang] (α : Type)(a : α)(f: α → SLang Bool): ∑' (i : Bool), f a i = ∑' (b : Bool), f a b := by rfl +lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring + lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd : α)(tl : List α) (h : ∑' (b : Bool), f hd b = 1): ∑' (a : Bool), f hd a * mapM f tl b = mapM f tl b := by rw [tsum_bool] rw [ennreal_mul_assoc] @@ -103,8 +119,6 @@ lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd rw [h] rw [@CanonicallyOrderedCommSemiring.one_mul] - - lemma Norm_func_norm_on_list [LawfulMonad SLang] (α : Type)(f: α → SLang Bool) (al: List α): (∀ a : α, ∑' (b : Bool), f a b = 1) → ∑' (b : List Bool), mapM f al b = 1 := by intro h diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index c55b2b6c..edeba026 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -288,8 +288,11 @@ apply singleton_to_event2 intros l₁ l₂ h_adj x rw[prod_of_ind_prob_PMF query num den h x l₁] rw[prod_of_ind_prob_PMF query num den h x l₂] -cases h_adj with -| Update hl₁ hl₂ => sorry - -- have hlen: l₁.length = l₂.length := by aesop - -- simp - -- rename_i a n b m +{ cases h_adj with + | Update hl₁ hl₂ => have hlen: l₁.length = l₂.length := by aesop + simp + rename_i a n b + sorry +} +{ sorry } +{ sorry } From d1a9ca99761e85f5e27c13e301949e33d9a249ed Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 14 Jul 2025 16:53:47 -0700 Subject: [PATCH 047/216] normalizationcleaned --- .../Pure/Local/MultiBernoulli/Properties.lean | 48 ------------------- .../Pure/Local/Normalization.lean | 35 +++----------- .../Pure/Local/Playground/Junk.lean | 25 ++++++++++ 3 files changed, 31 insertions(+), 77 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean index 27f9b76d..8d50b4bd 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean @@ -49,54 +49,6 @@ lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : berno | true => simp_all only [Bool.true_eq, tsum_ite_eq] | false => simp_all only [Bool.false_eq, tsum_ite_eq, tsum_ite_not, mul_zero] -lemma simplifier1 (a : List Bool) (b : Bool): -(∑' (a_1 : List Bool), if a = b :: a_1 then mapM bernoulli_mapper tl a_1 else 0) = -(if a.head? = b then mapM bernoulli_mapper tl a.tail else 0) := by - cases a with - | nil => simp - | cons ah atl => - simp [-mapM] - split - next h => - subst h - simp_all only [true_and] - rw [tsum_eq_single] - split - rename_i h - on_goal 2 => rename_i h - apply Eq.refl - simp_all only [not_true_eq_false] - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - next h => simp_all only [false_and, ↓reduceIte] - simp [tsum_ite_not] - -lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): -∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by - apply Equiv.tsum_eq_tsum_of_support - sorry - sorry - -lemma simplifier2 (hd : SeedType) (tl : List SeedType) (b : Bool): -(∑' (a : List Bool), bernoulli_mapper hd b * if a.head? = some b then mapM bernoulli_mapper tl a.tail else 0) = - ∑' (a : List Bool), bernoulli_mapper hd b * mapM bernoulli_mapper tl a := by - simp_all only [mul_ite, mul_zero] - apply symm - apply list_bool_tsum_only_tl b - -lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring - -lemma simplifier3: - ∑' (a : Bool), bernoulli_mapper hd a * mapM bernoulli_mapper tl b = mapM bernoulli_mapper tl b := by - rw [tsum_bool] - rw [ennreal_mul_assoc] - rw [←tsum_bool] - rw [bernoulli_mapper_sums_to_1] - rw [@CanonicallyOrderedCommSemiring.one_mul] - lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by unfold MultiBernoulliSample diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index 7fce5479..32ebfce8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -5,6 +5,12 @@ import SampCert.SLang import Mathlib.Data.Set.Basic import Mathlib.Data.Set.Basic +/- In this file, we show that if a function f : α -> SLang Bool + normalizes, in the sense that ∑' (b : List Bool) f a b = 1 + for any fixed a : α, + then the function obtained by applying monadic map + to f also normalizes. +-/ lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Bool)(tl : List α): (∑' (a_1 : List Bool), if a = b :: a_1 then mapM f tl a_1 else 0) = @@ -18,7 +24,6 @@ lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Boo subst h simp_all only [true_and] rw [tsum_eq_single] - --simp_all only [mapM] split rename_i h on_goal 2 => rename_i h @@ -31,32 +36,8 @@ lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Boo simp_all only [not_true_eq_false] next h => simp_all only [false_and, ↓reduceIte, _root_.tsum_zero] - open Set -lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by - ext l -- extensionality: sets equal iff same elements - constructor - · intro _ - -- show l is in the tail set: pick any Bool x and let xs := l - use true, l - rfl - · rintro ⟨x, xs, h⟩ - -- l = tail of some cons => l is a list => l ∈ univ - subst h - let ll := true::l - have h: ll.tail =l := by rfl - rw [← h] - exact mem_univ _ - -lemma all_list_eq_all_true_tails (b: Bool):(univ : Set (List Bool)) = { l | ∃ xs, l = (b :: xs).tail } := by - ext l - constructor - intro a - simp_all only [mem_univ, List.tail_cons, exists_eq', setOf_true] - intro b - simp_all only [List.tail_cons, exists_eq', setOf_true, mem_univ] - lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): ∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by apply Equiv.tsum_eq_tsum_of_support @@ -106,16 +87,12 @@ lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) apply symm apply list_bool_tsum_only_tl b -lemma simp_4 [LawfulMonad SLang] (α : Type)(a : α)(f: α → SLang Bool): ∑' (i : Bool), f a i = ∑' (b : Bool), f a b := by - rfl - lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd : α)(tl : List α) (h : ∑' (b : Bool), f hd b = 1): ∑' (a : Bool), f hd a * mapM f tl b = mapM f tl b := by rw [tsum_bool] rw [ennreal_mul_assoc] rw [←tsum_bool] - rw [simp_4] rw [h] rw [@CanonicallyOrderedCommSemiring.one_mul] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean index ecdb0c80..e4cccd6d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean @@ -263,3 +263,28 @@ lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List Seed exact h | cons => sorry + +open Set + +lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by + ext l -- extensionality: sets equal iff same elements + constructor + · intro _ + -- show l is in the tail set: pick any Bool x and let xs := l + use true, l + rfl + · rintro ⟨x, xs, h⟩ + -- l = tail of some cons => l is a list => l ∈ univ + subst h + let ll := true::l + have h: ll.tail =l := by rfl + rw [← h] + exact mem_univ _ + +lemma all_list_eq_all_true_tails (b: Bool):(univ : Set (List Bool)) = { l | ∃ xs, l = (b :: xs).tail } := by + ext l + constructor + intro _ + simp_all only [mem_univ, List.tail_cons, exists_eq', setOf_true] + intro _ + simp_all only [List.tail_cons, exists_eq', setOf_true, mem_univ] From 5fb89c6ea453a8b54f5771b00bbcebafc2b6da62 Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 14 Jul 2025 19:56:26 -0700 Subject: [PATCH 048/216] DPProof final step --- .../Local/RandomizedResponse/DPProof.lean | 71 +++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index e1ebfd6b..d7896ed4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -4,14 +4,75 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties -lemma temp {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : - (1/2 + num/den) / (1/2 - num/den) = (1 + (2 * num)/den) / (1 - (2*num) / den) := by - have hden : den ≠ (0 : ℕ+) := by exact PNat.ne_zero den +/- +lemma final_bound {γ: ℝ} (h0: 0 ≤ γ) (h1: 1/2 > γ) : +ENNReal.ofReal ((1/2 + γ) / (1/2 - γ) )≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + γ) / (1/2 - γ)))) := by + rw [] + have h_pos : 0 < (1 / 2 : ℝ) - γ := sub_pos.mpr h1 + have num_pos : 0 < 1 / 2 + γ := by linarith + exact div_pos num_pos h_pos +-/ +lemma bruh (num : Nat) (den : PNat): + 0 < ((1:ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := by + apply add_pos_of_pos_of_nonneg + norm_num + apply div_nonneg + exact Nat.cast_nonneg num + exact NNReal.coe_nonneg (NNReal.ofPNat den) +--lemma num_div_lt_one_half (num : Nat) (den : PNat) (h : 2 * num < den) : + -- ↑num / ↑↑↑den < (2⁻¹ : ℝ) := by -lemma final_step {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : +--lemma nnreal_coe_ineq (n : Nat) (d : PNat) (h : n < d.val) : n < NNReal.ofPNat d := by aesop + +lemma t (num : ℕ) (den : PNat) (h : 2 * num < den) : 2 * ↑num < NNReal.ofPNat den := by + simp + norm_cast + +lemma tr (num : ℕ) (den : ℕ+) (h : 2 * num < den) : + (1 / 2 : NNReal) * (2 * ↑num) < (1 / 2 : NNReal) * NNReal.ofPNat den := by + rw [← mul_assoc] + rw [← inv_eq_one_div] + rw [inv_mul_cancel] + rw [one_mul] + rw [← mul_lt_mul_iff_of_pos_left two_pos] + rw [← mul_assoc] + rw [mul_inv_cancel] + rw [one_mul] + {exact t num den h} + {norm_num} + {norm_num} + +lemma temp (num : Nat) (den : PNat) (h : 2 * num < den) : (num : ℝ) / den < (1 : ℝ) / 2:= by + rw [div_lt_iff] + { + have h2: 1/2 * (2 * ↑num) < 1/2 * NNReal.ofPNat den := tr num den h + have h3: 1/2 * (2 * (num : NNReal)) = (1/2 * 2) * ↑num := by aesop + have h4: (1/2 * 2) * (num : NNReal) = num := by aesop + rw [h3] at h2 + rw [h4] at h2 + exact h2 + } + {simp [den.pos] + exact den.2} + +lemma bruh1 (num : Nat) (den : PNat) (h: 2 * num < den) : + 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := by + have h1 : 1/2 > (num : ℝ) / den := temp num den h + have h_pos : 0 < (1 / 2 : ℝ) - (num / den) := sub_pos.mpr h1 + exact h_pos + + +lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): (1/2 + num/den) / (1/2 - num/den) ≤ Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den))) := by rw [Real.exp_log] + rw [@div_pos_iff] + apply Or.inl + apply And.intro + {apply bruh} + {apply bruh1 num den h} - apply le_of_eq + /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l + have h2 : 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := bruh1 query num den h l + apply div_pos h1 h2 -/ From 60beee93ab15d076b9bf10976f0394d433f1abc0 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 15 Jul 2025 10:39:07 -0700 Subject: [PATCH 049/216] someENNRlemmas --- .../Pure/Local/RandomizedResponseAlt.lean | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index edeba026..ff58b8df 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -52,6 +52,20 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den again it's just annoying arithmetic. -/ sorry +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + cases hb: b with + | true => cases hq: query l with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] + sorry + | false => cases hq: query l with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] + sorry + def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -236,9 +250,31 @@ lemma prod_split (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den ∏' (i : Fin l.length), RRSingleSample query num den h (l[↑i]) (x[↑i]) = (∏'(i : Fin a.length), RRSingleSample query num den h (a[i]) (x[↑i]))*(∏'(i : Fin b.length), RRSingleSample query num den h (b[i]) (x[↑i])) := by sorry +#check ENNReal.div_self + +lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by + intro h + cases hb: b == 0 with + | true => simp at hb + sorry + | false => sorry + + +lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by + sorry + +lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by + sorry + +lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by + intro h1 + sorry + +lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b - ≤ (den + 2 * num) / (den - 2 * num) := by + < (den + 2 * num) / (den - 2 * num) := by cases b with | true => cases hqa : query a with @@ -246,10 +282,30 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ rw [RRSingleSample_true_true _ _ _ _ _ hqa] cases hqa' : query a' with | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] - -- arithmetic now - sorry + rw[ENNReal.div_self] + {sorry} + {rw [@ENNReal.div_ne_zero] + apply And.intro + simp + intro hd + apply False.elim + apply pnat_zero_imp_false den hd + apply mult_ne_top + simp + simp + } + { apply div_ne_top + simp + apply mult_ne_top + simp + simp + simp + rw[Not] + apply pnat_zero_imp_false den + } | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] -- arithmetic now + aesop sorry | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa] @@ -258,7 +314,22 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ -- arithmetic now sorry | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] - sorry + rw[ENNReal.div_self] + {sorry} + {rw [@ENNReal.div_ne_zero] + apply And.intro + simp + intro hd + sorry /- For this sorry, we need the h hypothesis to be a strict inequality -/ + apply mult_ne_top + simp + simp + } + { apply div_ne_top + simp + aesop + apply pnat_zero_imp_false den a_1 + } -- arithmetic now | false => cases hqa : query a with @@ -292,7 +363,7 @@ rw[prod_of_ind_prob_PMF query num den h x l₂] | Update hl₁ hl₂ => have hlen: l₁.length = l₂.length := by aesop simp rename_i a n b - sorry + sorry } { sorry } { sorry } From bc2754a45bb9e8d313987aecf4e6575d0d0b4094 Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 15 Jul 2025 10:41:46 -0700 Subject: [PATCH 050/216] Update DPProof.lean --- .../Pure/Local/RandomizedResponse/DPProof.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index d7896ed4..2613bd58 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -4,6 +4,9 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties +lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ENNReal.ofReal ((1/2 + num/den) / (1/2 - num/den)) := by + sorry + /- lemma final_bound {γ: ℝ} (h0: 0 ≤ γ) (h1: 1/2 > γ) : ENNReal.ofReal ((1/2 + γ) / (1/2 - γ) )≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + γ) / (1/2 - γ)))) := by @@ -76,3 +79,13 @@ lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l have h2 : 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := bruh1 query num den h l apply div_pos h1 h2 -/ + +lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): + ENNReal.ofReal ((1/2 + num/den) / (1/2 - num/den)) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by + apply ENNReal.ofReal_le_ofReal + exact final_step num den h + +lemma final_step_combined (num : Nat) (den: PNat) (h: 2 * num < den): + (den + (2 : ENNReal) * num) / (den - 2 * num) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by + rw [final_transition num den h] + exact ENNReal_final_step num den h From 7697873b8894cc403ba32fc9860f93faab11b0f2 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 15 Jul 2025 13:33:15 -0700 Subject: [PATCH 051/216] ubuybg --- .../Pure/Local/RandomizedResponseAlt.lean | 281 ++++++++---------- 1 file changed, 117 insertions(+), 164 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index ff58b8df..247a8282 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -18,54 +18,6 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r -lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): - RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ - -lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): - RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): - RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): - RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - /- This is the same state as the first lemma that's not working, - again it's just annoying arithmetic. -/ - sorry - -lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ 0 := by - cases hb: b with - | true => cases hq: query l with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] - sorry - | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] - sorry - | false => cases hq: query l with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] - sorry - | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] - sorry - def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -153,6 +105,12 @@ intro a subst a simp_all only [not_true_eq_false] + + + + + + lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by rw[List.mapM_cons] @@ -177,6 +135,9 @@ set f := fun x => RRSingleSample query num den h x rw[mapM_dist_cons f b c hd tl] + + + lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by induction l generalizing a with @@ -240,130 +201,122 @@ theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_wit simp exact Real.exp_pos ε -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b) -(hx: l₁.length = x.length)(hy: l₂.length = x.length): - (∏' (i : Fin l₁.length), RRSingleSample query num den h l₁[i] x[i]) / - ∏' (i : Fin l₂.length), RRSingleSample query num den h l₂[i] x[i] -= f (l₁[a.length]'(by sorry)) (x[a.length]' (by sorry)) := by sorry -lemma prod_split (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den)(l : List T)(x: List Bool)(hl: l = a++b)(h1: l.length = x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): - ∏' (i : Fin l.length), RRSingleSample query num den h (l[↑i]) (x[↑i]) - = (∏'(i : Fin a.length), RRSingleSample query num den h (a[i]) (x[↑i]))*(∏'(i : Fin b.length), RRSingleSample query num den h (b[i]) (x[↑i])) := by sorry -#check ENNReal.div_self +lemma prod_split (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(hl: l = a++b)(h1: l.length ≤ x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): + ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by rw[hl] at h1, simp h1)))) := by sorry + +lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / + ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by + rw[List.append_assoc] at h1 + rw[prod_split f l₁ x a ([n]++b) h1 (by aesop)] + rw[prod_split f ([n]++b) x [n] b (by aesop)] + case h1 => + rw[← hy] + rw[h2] + simp + case h2 => + rw[← hy] + rw[h2] + simp + rw[← add_assoc] + aesop + + case h3 => + rw[← hy] + rw[h2] + simp + rw[← add_assoc] + rw[add_comm a.length b.length] + rw[add_assoc] + aesop + + case h2 => + rw[← hy] + rw[h2] + simp + + case h3 => + rw[←hy] + rw[h2] + simp + + rw[List.append_assoc] at h2 + rw[prod_split f l₂ x a ([m]++b) h2 (by aesop)] + + case h2 => + rw[← hy] + rw[h2] + simp + + case h3 => + rw[← hy] + rw[h2] + simp + + + + + rw[prod_split f ([m]++b) x [m] b (by rfl)] + + case h1 => + rw[← hy] + rw[h2] + simp + + case h2 => + rw[← hy] + rw[h2] + simp + rw[← add_assoc] + simp + + case h3 => + rw[← hy] + rw[h2] + simp + rw[add_comm] + rw[add_assoc] + aesop + + rw[ENNReal.mul_div_mul_left] + + rw[ENNReal.mul_div_mul_right] + simp + --- + -lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by - intro h - cases hb: b == 0 with - | true => simp at hb - sorry - | false => sorry -lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by - sorry -lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by - sorry -lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by - intro h1 - sorry -lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop - -lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): - RRSingleSample query num den h a b / RRSingleSample query num den h a' b - < (den + 2 * num) / (den - 2 * num) := by - cases b with - | true => - cases hqa : query a with - | true => - rw [RRSingleSample_true_true _ _ _ _ _ hqa] - cases hqa' : query a' with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] - rw[ENNReal.div_self] - {sorry} - {rw [@ENNReal.div_ne_zero] - apply And.intro - simp - intro hd - apply False.elim - apply pnat_zero_imp_false den hd - apply mult_ne_top - simp - simp - } - { apply div_ne_top - simp - apply mult_ne_top - simp - simp - simp - rw[Not] - apply pnat_zero_imp_false den - } - | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] - -- arithmetic now - aesop - sorry - | false => - rw [RRSingleSample_false_true _ _ _ _ _ hqa] - cases hqa' : query a' with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] - -- arithmetic now - sorry - | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] - rw[ENNReal.div_self] - {sorry} - {rw [@ENNReal.div_ne_zero] - apply And.intro - simp - intro hd - sorry /- For this sorry, we need the h hypothesis to be a strict inequality -/ - apply mult_ne_top - simp - simp - } - { apply div_ne_top - simp - aesop - apply pnat_zero_imp_false den a_1 - } - -- arithmetic now - | false => - cases hqa : query a with - | true => - rw [RRSingleSample_true_false _ _ _ _ _ hqa] - cases hqa' : query a' with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] - -- arithmetic now - sorry - | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] - -- arithmetic now - sorry - | false => - rw [RRSingleSample_false_false _ _ _ _ _ hqa] - cases hqa' : query a' with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] - -- arithmetic now - sorry - | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] - -- arithmetic now - sorry - -theorem RRSample_is_DP {T : Type} (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) ((num: NNReal) / den) := by +open Finset +open scoped BigOperators + + +theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : +DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((1/2 + num / den) / (1/2 - num / den))) := by -- let ε := ↑num / NNReal.ofPNat den apply singleton_to_event2 intros l₁ l₂ h_adj x -rw[prod_of_ind_prob_PMF query num den h x l₁] -rw[prod_of_ind_prob_PMF query num den h x l₂] -{ cases h_adj with - | Update hl₁ hl₂ => have hlen: l₁.length = l₂.length := by aesop - simp - rename_i a n b - sorry -} -{ sorry } -{ sorry } +cases xlen1 : l₁.length == x.length with +| true => + rw[prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] + rw[prod_of_ind_prob_PMF query num den h x l₂ (by sorry)] + cases h_adj with + | Update hl₁ hl₂ => + rename_i a n b m + have hlen: l₁.length = l₂.length := by aesop + have xlen2 : l₂.length = x.length := by aesop + simp + have xlen3 : l₁.length = x.length := by aesop + rw[reduction l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + + + + +| false => sorry + + +-- rw[reduction l₁ l₂ x (RRSingleSample query num den h) hl₁ hl₂ xlen xlen2] From 102c8b5188705119aa6fab06cded5696ceca1540 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 15 Jul 2025 13:53:25 -0700 Subject: [PATCH 052/216] Ruthless Robert + Aras code --- .../Pure/Local/RandomizedResponseAlt.lean | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 247a8282..00d4a2f1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -18,6 +18,54 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r +lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): + RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ + +lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): + RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): + RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): + RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + /- This is the same state as the first lemma that's not working, + again it's just annoying arithmetic. -/ + sorry + +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + cases hb: b with + | true => cases hq: query l with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] + sorry + | false => cases hq: query l with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] + sorry + def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -206,6 +254,109 @@ theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_wit lemma prod_split (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(hl: l = a++b)(h1: l.length ≤ x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by rw[hl] at h1, simp h1)))) := by sorry +#check ENNReal.div_self + +lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by + intro h + cases hb: b == 0 with + | true => simp at hb + sorry + | false => sorry + + +lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by + sorry + +lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by + sorry + +lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by + intro h1 + sorry + +lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + +lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): + RRSingleSample query num den h a b / RRSingleSample query num den h a' b + < (den + 2 * num) / (den - 2 * num) := by + cases b with + | true => + cases hqa : query a with + | true => + rw [RRSingleSample_true_true _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] + rw[ENNReal.div_self] + {sorry} + {rw [@ENNReal.div_ne_zero] + apply And.intro + simp + intro hd + apply False.elim + apply pnat_zero_imp_false den hd + apply mult_ne_top + simp + simp + } + { apply div_ne_top + simp + apply mult_ne_top + simp + simp + simp + rw[Not] + apply pnat_zero_imp_false den + } + | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] + -- arithmetic now + aesop + sorry + | false => + rw [RRSingleSample_false_true _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] + rw[ENNReal.div_self] + {sorry} + {rw [@ENNReal.div_ne_zero] + apply And.intro + simp + intro hd + sorry /- For this sorry, we need the h hypothesis to be a strict inequality -/ + apply mult_ne_top + simp + simp + } + { apply div_ne_top + simp + aesop + apply pnat_zero_imp_false den a_1 + } + -- arithmetic now + + | false => + cases hqa : query a with + | true => + rw [RRSingleSample_true_false _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => + rw [RRSingleSample_false_false _ _ _ _ _ hqa] + cases hqa' : query a' with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] + -- arithmetic now + sorry + lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by rw[List.append_assoc] at h1 From 5fcd682930e6057383195d978bc1a2d9ab9d68fd Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 15 Jul 2025 13:56:07 -0700 Subject: [PATCH 053/216] new --- .../Pure/Local/RandomizedResponseAlt.lean | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index ff58b8df..d9a0e2ca 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -13,6 +13,8 @@ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 simp_all only [tsub_le_iff_right] linarith +lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + /- Eventually, we may want query to return more than just a Boolean -/ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) @@ -57,6 +59,8 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P cases hb: b with | true => cases hq: query l with | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + aesop + apply pnat_zero_imp_false den a sorry | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] sorry @@ -259,19 +263,34 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by sorry | false => sorry +lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop + +lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + sorry + +lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry + lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [← @ENNReal.inv_ne_zero] at h2 sorry lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [@ENNReal.div_eq_inv_mul] + sorry lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by intro h1 sorry -lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop - lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b < (den + 2 * num) / (den - 2 * num) := by From e32e934a452e04a8998372b204f937ee5093c6e3 Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 15 Jul 2025 14:24:03 -0700 Subject: [PATCH 054/216] Update DPProof.lean --- .../Local/RandomizedResponse/DPProof.lean | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 2613bd58..b74fea59 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -4,7 +4,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties -lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ENNReal.ofReal ((1/2 + num/den) / (1/2 - num/den)) := by +lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ((1/2 + num/den) / (1/2 - num/den)) := by + rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] sorry /- @@ -80,10 +81,20 @@ lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): have h2 : 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := bruh1 query num den h l apply div_pos h1 h2 -/ +lemma ennreal_pres_ineq (a b : Real): a ≤ b -> ENNReal.ofReal a ≤ ENNReal.ofReal b := by sorry + +lemma q (num : Nat) (den : PNat) (h : 2 * num < den) : + (1/(2 : ENNReal) + num/den) / (1/2 - num/den) = ENNReal.ofReal (((1 / 2 + num / den)) / (1 / 2 - num / den)) := by + + lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): - ENNReal.ofReal ((1/2 + num/den) / (1/2 - num/den)) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - apply ENNReal.ofReal_le_ofReal - exact final_step num den h + (1/(2 : ENNReal) + num/den) / (1/2 - num/den) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by + have h1 : 0 < (1/2 : ℝ) - (num : ℝ)/den := by + exact bruh1 num den h + have h₂ : 0 < (1/2 : ℝ) + (num : ℝ)/den := by + sorry + have h3: ((1/2) + num/den) / (1/2 - num/den) = ENNReal.ofReal ((1/2) + (num/den : ℝ)) / (1/2 - num/den) := by sorry + simp_rw [ENNReal.ofReal_sub, ENNReal.ofReal_div] lemma final_step_combined (num : Nat) (den: PNat) (h: 2 * num < den): (den + (2 : ENNReal) * num) / (den - 2 * num) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by From cbe95c255e639fdf166876f111dd7e0b1d5ad48f Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 15 Jul 2025 14:27:46 -0700 Subject: [PATCH 055/216] mergewperryn --- .../Pure/Local/LocalDP/UpdateNeighbour.lean | 7 +++++++ .../Pure/Local/RandomizedResponseAlt.lean | 10 +++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean index 97462d9c..dc7b92ae 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean @@ -14,4 +14,11 @@ def UpdateNeighbour_symm (l₁ l₂ : List T) (H : UpdateNeighbour l₁ l₂) : · rename_i _ _ _ _ Hl1 Hl2 exact UpdateNeighbour.Update Hl2 Hl1 +lemma UpdateNeighbour_length {T : Type} {l₁ l₂ : List T} (H : UpdateNeighbour l₁ l₂) : + l₁.length = l₂.length := by + cases H + rename_i _ _ _ _ Hl1 Hl2 + rw[Hl1, Hl2] + simp + end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index a70be833..8ff2e7c0 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -274,7 +274,7 @@ lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by rw [@inv_eq_one_div] rw [@inv_eq_one_div] sorry - + lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry @@ -464,7 +464,6 @@ lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ open Finset open scoped BigOperators - theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((1/2 + num / den) / (1/2 - num / den))) := by -- let ε := ↑num / NNReal.ofPNat den @@ -473,7 +472,10 @@ intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with | true => rw[prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] - rw[prod_of_ind_prob_PMF query num den h x l₂ (by sorry)] + rw[prod_of_ind_prob_PMF query num den h x l₂ + (by rw[←UpdateNeighbour_length h_adj] + simp at xlen1 + exact xlen1)] cases h_adj with | Update hl₁ hl₂ => rename_i a n b m @@ -482,6 +484,8 @@ cases xlen1 : l₁.length == x.length with simp have xlen3 : l₁.length = x.length := by aesop rw[reduction l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + + From 349497c297adb9cbe14a5400d0a66494cababaec Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 16 Jul 2025 11:33:27 -0700 Subject: [PATCH 056/216] reduction2 --- .../Local/MultiBernoulli.lean | 0 .../Pure/Local/ENNRealLemmasSuite.lean | 28 ++ .../Local/RandomizedResponse/DPProof.lean | 7 +- .../Pure/Local/RandomizedResponseAlt.lean | 276 +++++++----------- 4 files changed, 130 insertions(+), 181 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean new file mode 100644 index 00000000..e69de29b diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index f2a2774f..e05e86d5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -11,6 +11,34 @@ lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring +lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop + +lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + sorry + +lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry + + +lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [← @ENNReal.inv_ne_zero] at h2 + sorry + +lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [@ENNReal.div_eq_inv_mul] + + sorry + +lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by + intro h1 + sorry + lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by rw [ENNReal.tsum_eq_add_tsum_ite []] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index b74fea59..5d616d29 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -5,7 +5,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ((1/2 + num/den) / (1/2 - num/den)) := by - rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] + -- rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] sorry /- @@ -85,7 +85,7 @@ lemma ennreal_pres_ineq (a b : Real): a ≤ b -> ENNReal.ofReal a ≤ ENNReal.of lemma q (num : Nat) (den : PNat) (h : 2 * num < den) : (1/(2 : ENNReal) + num/den) / (1/2 - num/den) = ENNReal.ofReal (((1 / 2 + num / den)) / (1 / 2 - num / den)) := by - + sorry lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): (1/(2 : ENNReal) + num/den) / (1/2 - num/den) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by @@ -94,7 +94,8 @@ lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): have h₂ : 0 < (1/2 : ℝ) + (num : ℝ)/den := by sorry have h3: ((1/2) + num/den) / (1/2 - num/den) = ENNReal.ofReal ((1/2) + (num/den : ℝ)) / (1/2 - num/den) := by sorry - simp_rw [ENNReal.ofReal_sub, ENNReal.ofReal_div] + -- simp_rw [ENNReal.ofReal_sub, ENNReal.ofReal_div] + sorry lemma final_step_combined (num : Nat) (den: PNat) (h: 2 * num < den): (den + (2 : ENNReal) * num) / (den - 2 * num) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 8ff2e7c0..c06dda2f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -4,23 +4,25 @@ import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite- /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang /- open MultiBernoulli -/ -lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by +lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop /- Eventually, we may want query to return more than just a Boolean -/ -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r -lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): +lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by rw[RRSingleSample] simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, @@ -28,7 +30,7 @@ lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ -lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = true): +lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by rw[RRSingleSample] simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, @@ -36,7 +38,7 @@ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] apply Eq.refl -lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): +lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by rw[RRSingleSample] simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, @@ -44,7 +46,7 @@ lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] apply Eq.refl -lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (hq : query l = false): +lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by rw[RRSingleSample] simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, @@ -54,7 +56,7 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den again it's just annoying arithmetic. -/ sorry -lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) (b : Bool): +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ 0 := by cases hb: b with | true => cases hq: query l with @@ -69,8 +71,23 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P sorry | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] sorry +lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ ⊤ := by + cases hb: b with + | true => cases hq: query l with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + aesop + sorry + | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] + sorry + | false => cases hq: query l with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] + sorry + | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] + sorry + -def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -81,7 +98,7 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- At this point, we should be set to prove that RRSample is normalized and that it is differentially private. The definition is computable, as we need. -/ -lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : +lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : HasSum (RRSingleSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] rw [@tsum_bool] @@ -103,49 +120,16 @@ lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : rw [@AddCommMonoidWithOne.add_comm] } -lemma nil_case {T : Type} (query : T -> Bool) (num : Nat) (den: PNat) (h: 2 * num ≤ den): - ∑' (b : List Bool), (RRSample query num den h) [] b = 1 := by - have h1: ∑' (b : List Bool), mapM (fun x => RRSingleSample query num den h x) [] b = - mapM (fun x => RRSingleSample query num den h x) [] [] := by - rw [@List.mapM_nil] - simp_all only [pure, SLang.pure_apply, ↓reduceIte] - rw [ENNReal.tsum_eq_add_tsum_ite []] - simp_all only [↓reduceIte, ite_self, tsum_zero, add_zero] - rw[RRSample] - rw[h1] - rw [@List.mapM_nil] - simp - -lemma cons_case {T: Type} (query : T -> Bool) (num : Nat) (den: PNat) (h : 2 * num ≤ den) - (l : List T) : ∑' (b : List Bool), RRSample query num den h l b = 1 := by - rw[RRSample] - sorry - /- This should now follow from Renee's abstraction of the MultiBernoulli proof -/ -lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : +lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] - rw[RRSample] - induction l with - | nil => exact nil_case query num den h - | cons hd tl tail_ih => sorry - -/- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : - HasSum (RRSample2 query s l) 1 := by - rw[RRSample2] - simp_all only [bind, pure] - rw[Summable.hasSum_iff ENNReal.summable] - rw[←MultiBernoulliSample_normalizes s] - simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] - sorry --/ - + sorry + /- See Renee's proof in the PMFProperties file-/ -def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := +def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ - - namespace SLang lemma simplifier_1 (f : T -> SLang Bool): (∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by @@ -157,12 +141,6 @@ intro a subst a simp_all only [not_true_eq_false] - - - - - - lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by rw[List.mapM_cons] @@ -180,17 +158,14 @@ conv => enter [1, 2] rw [simplifier_1] -lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): +lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): RRSample query num den h (hd::tl) (b::c) = RRSingleSample query num den h hd b * RRSample query num den h tl c := by unfold RRSample set f := fun x => RRSingleSample query num den h x rw[mapM_dist_cons f b c hd tl] - - - -lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by induction l generalizing a with | nil => @@ -220,7 +195,7 @@ induction l generalizing a with simp at k exact k -theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): +theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob @@ -267,37 +242,10 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by sorry | false => sorry -lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop - -lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - sorry - -lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry - -lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [← @ENNReal.inv_ne_zero] at h2 - sorry - -lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [@ENNReal.div_eq_inv_mul] - - sorry - -lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by - intro h1 - sorry - -lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ den) (a a' : T) (b : Bool): +lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b - < (den + 2 * num) / (den - 2 * num) := by + ≤ (den + 2 * num) / (den - 2 * num) := by cases b with | true => cases hqa : query a with @@ -376,97 +324,55 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ -- arithmetic now sorry -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / +/- ROBERT'S VERSION OF REDUCTION: -/ +lemma prod_over_prod (f : T -> SLang Bool) (l₁ l₂: List T) (x : List Bool) (hx : l₁.length = x.length) (hy : l₂.length = x.length): +(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / (∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val])) += ∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val]) / f (l₂[i.val]) (x[i.val]) := by sorry + +lemma simplifier1 (f : T -> SLang Bool) (l₁ l₂ : List T) (x : List Bool) (hx : l₁.length = x.length) (hy : l₂.length = x.length) (i : Fin l₁.length) +(hnz: f l₂[↑i] x[↑i] ≠ 0) (hnt: f l₂[↑i] x[↑i] ≠ ⊤): +(if assm : f l₁[↑i.val] x[↑i.val] = f l₂[↑i.val] x[↑i.val] then (1 : ENNReal) else f l₁[↑i.val] x[↑i.val] / f l₂[↑i.val] x[↑i.val]) = f l₁[↑i.val] x[↑i.val] / f l₂[↑i.val] x[↑i.val] := by + simp_all only [Fin.getElem_fin, dite_eq_ite, ite_eq_right_iff] + intro _ + rw [ENNReal.div_self] + exact hnz + exact hnt + +lemma prod_simp +(f : T -> SLang Bool) (l₁ l₂ : List T) (x : List Bool) (hx : l₁.length = x.length) (hy : l₂.length = x.length) (i : Fin l₁.length) +(hnz: ∀i : Fin l₁.length, f l₂[i] x[i] ≠ 0 ) (hnt: ∀i : Fin l₁.length, f l₂[i] x[i] ≠ ⊤): +∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val]) / f (l₂[i.val]) (x[i.val]) += ∏' (i : Fin l₁.length), if assm: (f (l₁[i.val]) (x[i.val]) = f (l₂[i.val]) (x[i.val])) then 1 else f (l₁[i.val]) (x[i.val]) / f (l₂[i.val]) (x[i.val]) := by + conv => + enter [2, 1, i] + rw[simplifier1 f l₁ l₂ x hx hy i (hnz i) (hnt i)] + +lemma prod_split (g : U -> ℕ -> ENNReal) (l : ℕ) (a : ℕ) (b : ℕ) (h1 : a + b = l): + ∏' (i : Fin l), g u i = (∏' (i : Fin a), g u i) * (∏' (i : Fin b), g u (a + i)) := by + rw [@tprod_fintype, @tprod_fintype, @tprod_fintype] + subst h1 + rw [@Fin.prod_univ_add] + simp_all only [Fin.coe_castAdd, Fin.coe_natAdd] + +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[List.append_assoc] at h1 - rw[prod_split f l₁ x a ([n]++b) h1 (by aesop)] - rw[prod_split f ([n]++b) x [n] b (by aesop)] - case h1 => - rw[← hy] - rw[h2] - simp - case h2 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - aesop - - case h3 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - rw[add_comm a.length b.length] - rw[add_assoc] - aesop - - case h2 => - rw[← hy] - rw[h2] - simp - - case h3 => - rw[←hy] - rw[h2] - simp - - rw[List.append_assoc] at h2 - rw[prod_split f l₂ x a ([m]++b) h2 (by aesop)] - - case h2 => - rw[← hy] - rw[h2] - simp - - case h3 => - rw[← hy] - rw[h2] - simp - - - - - rw[prod_split f ([m]++b) x [m] b (by rfl)] - - case h1 => - rw[← hy] - rw[h2] - simp - - case h2 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - simp - - case h3 => - rw[← hy] - rw[h2] - simp - rw[add_comm] - rw[add_assoc] - aesop - - rw[ENNReal.mul_div_mul_left] - - rw[ENNReal.mul_div_mul_right] - simp - --- - - - - - + rw [prod_over_prod f l₁ l₂ x hx hy] + rw [prod_simp f l₁ l₂ x hx hy] /- Note that we now have 4 goals. These goals can be solved in our ultimate proof by application + of the lemma that says that RRSample is non-zero on lists of the same length and is always finite. -/ + have ha (i : Fin a.length): f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry)) = f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry)) := by + sorry + set g: List T -> List T -> List Bool -> ℕ -> ENNReal := fun l₁ l₂ x i => + if f (l₁[↑i]'(by sorry)) (x[↑i]'(by sorry)) = f (l₂[↑i]'(by sorry)) (x[↑i]'(by sorry)) then 1 else f (l₁[↑i]'(by sorry)) (x[↑i]'(by sorry)) / f (l₂[↑i]'(by sorry)) (x[↑i]'(by sorry)) + /- Now would love to rewrite product split ... -/ + /- Need to write the correct g-/ + sorry open Finset open scoped BigOperators -theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : +theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((1/2 + num / den) / (1/2 - num / den))) := by --- let ε := ↑num / NNReal.ofPNat den apply singleton_to_event2 intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with @@ -484,13 +390,27 @@ cases xlen1 : l₁.length == x.length with simp have xlen3 : l₁.length = x.length := by aesop rw[reduction l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] - - - - - - -| false => sorry + have i1: a.length < x.length := by + rw[←xlen3] + subst hl₁ hl₂ + simp_all only [List.append_assoc, List.singleton_append, List.length_append, + List.length_cons, beq_iff_eq] + rw[←xlen1] + rw [@Nat.lt_add_right_iff_pos] + simp + calc + RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) + / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by + apply final_step_combined + exact h + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop + +| false => simp at xlen1 + /- We should have a lemma that says that RRSample_PMF applied to a list of length + different than the length of l₁ gives zero. -/ + /- We should also clarify with Ethan about why the PureDP definition seems to allow division by zero...-/ + sorry -- rw[reduction l₁ l₂ x (RRSingleSample query num den h) hl₁ hl₂ xlen xlen2] From 65e05deb1445ce382db9421588bddbf1cd333f85 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Thu, 17 Jul 2025 17:16:34 -0400 Subject: [PATCH 057/216] head_tail_prod --- .../Pure/Local/RandomizedResponseAlt.lean | 208 ++++++++++++------ 1 file changed, 141 insertions(+), 67 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 00d4a2f1..f762c8d0 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -1,9 +1,9 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.SLang /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang @@ -249,11 +249,23 @@ theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_wit simp exact Real.exp_pos ε +lemma list_index_trans (T : Type)(l a b : List T)(x: List Bool)(i : Fin b.length)(h1 : l = a++b)(h2 : l.length ≤ x.length) +(h3: a.length ≤ x.length)(h4: b.length ≤ x.length): a.length + ↑i < x.length := by + subst h1 + simp_all only [List.length_append] + have htrans : ↑i < b.length := by simp + have htrans2 : a.length + ↑i < a.length + b.length := by simp_all only [Fin.is_lt, add_lt_add_iff_left] + rw [] + sorry + lemma prod_split (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(hl: l = a++b)(h1: l.length ≤ x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): - ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by rw[hl] at h1, simp h1)))) := by sorry + ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by sorry)))) := by sorry + +lemma prod_split_left (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(hl: l = b++a)(h1: l.length ≤ x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): + ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by sorry)))) := by sorry #check ENNReal.div_self lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by @@ -349,7 +361,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ sorry | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa] - cases hqa' : query a' with + cases hqa' : query a' with(l : T)(ls: List T | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] -- arithmetic now sorry @@ -357,84 +369,148 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ -- arithmetic now sorry -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / - ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[List.append_assoc] at h1 - rw[prod_split f l₁ x a ([n]++b) h1 (by aesop)] - rw[prod_split f ([n]++b) x [n] b (by aesop)] - case h1 => - rw[← hy] - rw[h2] - simp - case h2 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - aesop +open Finset +open scoped BigOperators - case h3 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - rw[add_comm a.length b.length] - rw[add_assoc] - aesop +lemma append_empty_eq_og (l: List T) : l++[]=l:= by simp +lemma empty_append_eq_og (l: List T) : []++l=l:= by simp +lemma prod_of_one (x: Bool)(f: T → SLang Bool)(l : T) : ∏' (i : Fin ([l].length)), f [l][i.val] [x][i.val] = f l x := by + simp_all only [List.length_singleton, Fin.coe_fin_one, List.getElem_cons_zero] + rw [tprod_fintype] + rw [@Fin.prod_univ_one] +lemma list_fin (ls: List T)(xs : List Bool) (i : Fin ls.length)(h : ls.length = xs.length) : i.val < xs.length :=by + rw[← h] + simp - case h2 => - rw[← hy] - rw[h2] - simp +lemma list_len_0_then_empty (l : List T) (h: l.length = 0) : l =[] :=by simp_all only [List.length_eq_zero] +lemma list_head_nonempty (l : List T)(h1: l ≠ []) : 0 < l.length :=by + rw [List.length_pos_iff_ne_nil] + exact h1 - case h3 => - rw[←hy] - rw[h2] - simp - rw[List.append_assoc] at h2 - rw[prod_split f l₂ x a ([m]++b) h2 (by aesop)] - case h2 => - rw[← hy] - rw[h2] - simp +lemma prod_ENNReal (a : List ENNReal)(h : a≠ []) : ∏' (b: Fin a.length), a[b.val] = (a[0]'(by sorry)) * ∏' (b : Fin (a.tail).length), (a.tail[b.val]'(by sorry)) := by + induction a with + | nil => simp_all only [ne_eq, not_true_eq_false] + | cons ah t x => + rw[tprod_fintype] + rw[tprod_fintype] + simp_all only [List.getElem_cons_zero, List.tail_cons, + Fin.prod_univ_get] + simp_all only [ne_eq, not_false_eq_true, List.prod_cons] - case h3 => - rw[← hy] - rw[h2] - simp +lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.length = x.length)(h1: l ≠ [])(h2 : x≠ []) : + (∏' (i: Fin l.length), f l[i.val] x[i.val]) = f (l[0]'(by sorry)) (x[0]'(by sorry )) * ∏' (i : Fin l.tail.length), f (l.tail)[i.val] (x.tail[i.val]'(by sorry)):= by + induction l generalizing x with + | nil => aesop + | cons l ls hl => + rw [tprod_fintype] + rw[tprod_fintype] + simp only [List.getElem_cons_zero] + simp only [List.tail_cons] + simp [Finset.prod_eq_multiset_prod] + rw [@Fin.prod_ofFn] + rw[@Fin.prod_ofFn] + have h3 : ∀(i: Fin ls.length), (x[i.val+1]'(by sorry)) = (x.tail[i.val]'(by sorry)) := by + intro i + induction x with + | nil => simp_all only [ne_eq, not_false_eq_true, List.length_cons, List.length_nil, add_eq_zero, + List.length_eq_zero, one_ne_zero, and_false] + | cons xh xt x => simp + simp_all only [ne_eq, not_false_eq_true] - rw[prod_split f ([m]++b) x [m] b (by rfl)] - case h1 => - rw[← hy] - rw[h2] - simp + /-ls with + | nil => + induction xs with + | nil => + simp_all only [List.getElem_cons_zero, List.length_nil, tprod_empty, + mul_one] + rw [prod_of_one] + | cons => sorry + | cons lh lt h1 => + induction xs with + | nil => sorry + |cons xh xt h2 => + rw [] + list_index_tran -/ + + - case h2 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - simp +lemma ennreal_mul_div_mul_comm (a b c d: ENNReal) : a*b/(c*d) = (a/c) *(b/d) := by sorry - case h3 => - rw[← hy] - rw[h2] - simp - rw[add_comm] - rw[add_assoc] + +lemma prod_div (x: List Bool)(f: T → SLang Bool)(l₁ l₂ : List T) (hx: l₁.length = x.length)(hy: l₂.length = x.length) : +(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) += ∏' (i : Fin l₂.length), f (l₁[i.val]) (x[i.val]) / f (l₂[i.val]) (x[i.val]) := by + have hw : l₁.length = l₂.length := by simp_all only + induction l₁ with + | nil => + induction l₂ with + | nil => simp_all only [List.length_nil, tprod_empty, div_one] + | cons h tl hz => + apply False.elim + have rw_false: (([] : List T).length = (h :: tl).length) ↔ (([] : List T).length ≠ (h :: tl).length -> False) := by aesop + rw[rw_false] at hw + apply hw + have lhs_empty: ([] : List T).length = 0 := by rw [@List.length_eq_zero] + rw[lhs_empty] aesop + | cons ah t a => + induction l₂ with + | nil => + apply False.elim + have rw_false: ((ah::t).length = ([]: List T).length) ↔ ((ah::t).length ≠ ([]: List T).length -> False) := by aesop + rw[rw_false] at hw + apply hw + have rhs_empty: ([] : List T).length = 0 := by rw [@List.length_eq_zero] + rw[rhs_empty] + aesop + | cons bh bt b => + induction x with + | nil => sorry + | cons x xt h => + rw [head_tail_prod x xt bh bt f hy] + rw [head_tail_prod x xt ah t f hx] + rw [ENNReal.mul_div_right_comm] - rw[ENNReal.mul_div_mul_left] - rw[ENNReal.mul_div_mul_right] - simp - --- + + + + + + sorry + + + + + + + + | cons h t h3 => + + + +#check Finset.prod + + +lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(nonzero : f (k:T) (bo : Bool) ≠ 0) (noninf : f (k:T) (bo:Bool) ≠ ⊤)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / + ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by + induction a with + | nil => + induction b with + | nil => + simp_all [append_empty_eq_og, empty_append_eq_og] + | cons bh bt b=> sorry + | cons h t a => + induction b with + | nil => sorry + | cons ba bt b => sorry + @@ -442,8 +518,6 @@ lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ -open Finset -open scoped BigOperators theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : From 7babcb583a2e9e753543f7654f0d18add9ec3792 Mon Sep 17 00:00:00 2001 From: perrynchang Date: Thu, 17 Jul 2025 15:35:41 -0700 Subject: [PATCH 058/216] bruh2 --- SampCert/DifferentialPrivacy/Approximate/DP.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/SampCert/DifferentialPrivacy/Approximate/DP.lean b/SampCert/DifferentialPrivacy/Approximate/DP.lean index 534248ee..ebbbb7f9 100644 --- a/SampCert/DifferentialPrivacy/Approximate/DP.lean +++ b/SampCert/DifferentialPrivacy/Approximate/DP.lean @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Jean-Baptiste Tristan -/ import SampCert.SLang +import s import SampCert.DifferentialPrivacy.Generic import SampCert.DifferentialPrivacy.Neighbours -- import SampCert.DifferentialPrivacy.Pure.DP From 2051bdc44c39c420f639500de9412467f41fa8d7 Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:37:34 -0700 Subject: [PATCH 059/216] Update DP.lean --- SampCert/DifferentialPrivacy/Approximate/DP.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Approximate/DP.lean b/SampCert/DifferentialPrivacy/Approximate/DP.lean index ebbbb7f9..534248ee 100644 --- a/SampCert/DifferentialPrivacy/Approximate/DP.lean +++ b/SampCert/DifferentialPrivacy/Approximate/DP.lean @@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Jean-Baptiste Tristan -/ import SampCert.SLang -import s import SampCert.DifferentialPrivacy.Generic import SampCert.DifferentialPrivacy.Neighbours -- import SampCert.DifferentialPrivacy.Pure.DP From 6add5c65a9cc1139cc059a6f74e356e8def5c554 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Mon, 21 Jul 2025 10:21:45 -0700 Subject: [PATCH 060/216] Reduction2 updates --- .../Pure/Local/RandomizedResponseAlt.lean | 265 ++++++++++++++---- 1 file changed, 204 insertions(+), 61 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 00d4a2f1..5f702d6e 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -251,9 +251,6 @@ theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_wit -lemma prod_split (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(hl: l = a++b)(h1: l.length ≤ x.length)(h2: a.length ≤ x.length)(h3: b.length ≤ x.length): - ∏' (i : Fin l.length), f (l[i.val]) (x[i.val]) = (∏'(i : Fin a.length), f (a[i.val]) (x[i.val]))*(∏'(i : Fin b.length), f (b[i.val]) ((x[(a.length+i.val)]'(by rw[hl] at h1, simp h1)))) := by sorry - #check ENNReal.div_self lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by @@ -357,84 +354,209 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ -- arithmetic now sorry -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / - ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[List.append_assoc] at h1 - rw[prod_split f l₁ x a ([n]++b) h1 (by aesop)] - rw[prod_split f ([n]++b) x [n] b (by aesop)] - case h1 => - rw[← hy] - rw[h2] - simp - case h2 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - aesop - case h3 => - rw[← hy] - rw[h2] - simp - rw[← add_assoc] - rw[add_comm a.length b.length] - rw[add_assoc] - aesop - - case h2 => - rw[← hy] - rw[h2] - simp - case h3 => - rw[←hy] - rw[h2] +lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.length = x.length)(h1: l ≠ [])(h2 : x≠ []) : + (∏' (i: Fin l.length), f l[i.val] x[i.val]) = f (l[0]'(by sorry)) (x[0]'(by sorry )) * ∏' (i : Fin l.tail.length), f (l.tail)[i.val] (x.tail[i.val]'(by sorry)):= by + induction l generalizing x with + | nil => aesop + | cons l ls hl => + rw [tprod_fintype] + rw[tprod_fintype] + simp only [List.getElem_cons_zero] + simp only [List.tail_cons] + simp [Finset.prod_eq_multiset_prod] + + rw [@Fin.prod_ofFn] + rw[@Fin.prod_ofFn] + have h3 : ∀(i: Fin ls.length), (x[i.val+1]'(by sorry)) = (x.tail[i.val]'(by sorry)) := by + intro i + induction x with + | nil => simp_all only [ne_eq, not_false_eq_true, List.length_cons, List.length_nil, add_eq_zero, + List.length_eq_zero, one_ne_zero, and_false] + | cons xh xt x => simp + simp_all only [ne_eq, not_false_eq_true] +lemma prod_split (f: T → SLang Bool)(l a b : List T)(x c d : List Bool)(hl: l = a++b)(hx: x = c++d)(xl : x.length = l.length)(ac: a.length = c.length) : +∏' (i: Fin l.length), f l[i.val] x[i.val] = (∏'(i :Fin a.length), f a[i.val] c[i.val]) * ∏'(i: Fin b.length), f b[i.val] (d[i.val]'(by sorry)) := by + rw[tprod_fintype] + +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / + (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by + rw[tprod_fintype] + rw[tprod_fintype] + have f₁ := fun(b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry)) + have f₂ := fun(b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry)) + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] + + have ind: a.length < x.length := by + rw[← hx] + rw[h1] simp + conv => + enter[1,2] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] + have helper: ∀i : Fin (l₁.length - 1), l₁[↑(Fin.succAbove a.length i)]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by sorry + have helper2: ∀i : Fin (l₁.length - 1), f₁ i = f₂ i := by sorry + have helper3: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by sorry + have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by + rw[Finset.prod_congr] + - rw[List.append_assoc] at h2 - rw[prod_split f l₂ x a ([m]++b) h2 (by aesop)] - case h2 => - rw[← hy] - rw[h2] - simp - case h3 => - rw[← hy] - rw[h2] + rw[hlp] + rw[ENNReal.mul_div_mul_right] + simp + have mod: a.length % (l₁.length-1+1) = a.length := by + rw[Nat.mod_eq_of_lt] + rw[hx] + rw[Nat.sub_add_cancel] + exact ind + rw[← hx] + rw[h1] simp + sorry + simp[mod] + rw[hx] at mod + rw[← hy] at mod + simp[mod] + + + + + + + + + + - rw[prod_split f ([m]++b) x [m] b (by rfl)] +lemma reduction_aux (a b c List T)(x: List Bool)(f: T → SLang Bool): ∏' (i: Fin (a++b).length) - case h1 => - rw[← hy] - rw[h2] +lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / + ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by + rw[List.append_assoc] at h1 + rw[List.append_assoc] at h2 + let c := List.take a.length x + have c_def : c = List.take a.length x := rfl + let d := List.drop a.length x + have d_def : d = List.drop a.length x := by rfl + have leq: a.length < x.length := by { + rw[← hx] + rw[h1] simp + } + have ac: a.length = c.length := by { + rw[c_def] + rw[List.length_take] + rw[min_eq_left_of_lt leq] + } + rw[prod_split f l₁ a ([n]++b) x c d (by exact h1) (by rw[List.take_append_drop a.length x]) (by simp[hx]) (by exact ac)] + rw[prod_split f l₂ a ([m]++b) x c d (by exact h2) (by rw[List.take_append_drop a.length x]) (by simp[hy]) (by exact ac)] + rw[@tprod_fintype] + rw[ENNReal.mul_div_mul_left] + let e := List.take ([n].length) d + have e_def : e = List.take [n].length d := by rfl + let g := List.drop ([n].length) d + rw[prod_split f ([n]++b) [n] b d e g (by rfl) (by rw[List.take_append_drop])] + rw[prod_split f ([m]++b) [m] b d e g (by rfl) (by rw[List.take_append_drop])] + rw[@tprod_fintype] + rw[ENNReal.mul_div_mul_right] + simp only [List.length_singleton, Fin.coe_fin_one, List.getElem_cons_zero] + rw [tprod_fintype] + rw [@Fin.prod_univ_one] + rw [@Fin.prod_univ_one] + rw [h1] + rw [h2] + rw[List.getElem_append_right] + case h => + simp + case h'' => + simp + rw[List.getElem_append_right a] + case h => + simp + case h'' => + simp + simp + have xx : x = c++(e++g) := by + { + rw[List.take_append_drop] + rw[List.take_append_drop] + } + rw[xx] + rw[List.getElem_append_right] + - case h2 => - rw[← hy] - rw[h2] + rw[List.getElem_append_left] + simp [ac] + + case h => + rw[ac] + simp + rw[e_def] simp - rw[← add_assoc] + rw[d_def] + simp + exact leq + case h' => + rw[ac] + simp + left + rw[e_def] + simp + rw[d_def] + simp + exact leq + case h => + rw[ac] simp - case h3 => - rw[← hy] - rw[h2] + case xl => + rw[d_def] + simp + rw[← hx] + rw[h1] simp - rw[add_comm] - rw[add_assoc] - aesop + case ac => + rw[e_def] + rw[d_def] + simp + have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] + simp[aux] + case xl => + rw[d_def] + simp + rw[← hx] + rw[h1] + simp + case ac => + rw[e_def] + simp + rw[d_def] + simp + have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] + simp[aux] + case hc => + + rw[@tprod_fintype] + rw[Nat.eq_zero_of_mul_eq_zero] + + + + + + + + + + + - rw[ENNReal.mul_div_mul_left] - rw[ENNReal.mul_div_mul_right] - simp - --- @@ -442,6 +564,26 @@ lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ + + + + + + + + + + + + + + --- + + + + + + open Finset open scoped BigOperators @@ -467,6 +609,7 @@ cases xlen1 : l₁.length == x.length with + | false => sorry From f5bd8fe177b7a2b58a19ddab152b2ac1a18f15a4 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 21 Jul 2025 10:29:49 -0700 Subject: [PATCH 061/216] rralt --- .../Pure/Local/ENNRealLemmasSuite.lean | 9 ++- .../Pure/Local/RandomizedResponseAlt.lean | 72 +++++++++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index e05e86d5..5c8bf030 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -32,13 +32,20 @@ lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ rw [← @ENNReal.inv_ne_zero] rw [← @ENNReal.inv_ne_zero] at h1 rw [@ENNReal.div_eq_inv_mul] - + sorry lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by intro h1 sorry +lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by + intro h + cases hb: b == 0 with + | true => simp at hb + sorry + | false => sorry + lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by rw [ENNReal.tsum_eq_add_tsum_ite []] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index c06dda2f..c82dff7c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -5,10 +5,11 @@ import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof -import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite- +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang +open ENNRealLemmas /- open MultiBernoulli -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by @@ -57,6 +58,13 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den sorry lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + simp [RRSingleSample] + cases hb : b == query l with + | true => + | false => sorry + +lemma RRSingleSample_non_zero2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ 0 := by cases hb: b with | true => cases hq: query l with @@ -73,24 +81,66 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P sorry lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ ⊤ := by + have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by + rw [@ne_iff_lt_or_gt] + apply Or.inr + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt, ENNReal.coe_pos, Nat.cast_pos] + apply den.2 cases hb: b with | true => cases hq: query l with | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + apply div_ne_top + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.true.h2.h1 ?true.true.h2.h2 aesop - sorry + exact hden | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] - sorry + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.false.h2.h1 ?true.false.h2.h2 + aesop + exact hden | false => cases hq: query l with | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] - sorry + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.true.h2.h1 ?false.true.h2.h2 + aesop + exact hden | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] - sorry - + apply div_ne_top + rw [@ENNReal.add_ne_top] + apply And.intro + aesop + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.false.h2.h1 ?false.false.h2.h2 + aesop + exact hden def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) + lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample query num den h l₁ l₂= 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSample, -mapM] + aesop + | cons hd tl ih => + simp [RRSample, -mapM] + simp [RRSample, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + /- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do let r ← MultiBernoulliSample seed_list return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ @@ -235,14 +285,6 @@ lemma prod_split (f: T → SLang Bool)(l : List T)(x: List Bool)(a b : List T)(h #check ENNReal.div_self -lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by - intro h - cases hb: b == 0 with - | true => simp at hb - sorry - | false => sorry - - lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b ≤ (den + 2 * num) / (den - 2 * num) := by @@ -359,7 +401,7 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ rw [prod_over_prod f l₁ l₂ x hx hy] rw [prod_simp f l₁ l₂ x hx hy] /- Note that we now have 4 goals. These goals can be solved in our ultimate proof by application - of the lemma that says that RRSample is non-zero on lists of the same length and is always finite. -/ + of the lemma that says that RRSample is non-zero on lists of the same length and is always finite. -/} have ha (i : Fin a.length): f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry)) = f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry)) := by sorry set g: List T -> List T -> List Bool -> ℕ -> ENNReal := fun l₁ l₂ x i => From bff22c08ae2360b9be12a8efa55f0e21b2bdac19 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 21 Jul 2025 15:11:02 -0700 Subject: [PATCH 062/216] proofupdates --- .../Pure/Local/ENNRealLemmasSuite.lean | 91 ++++++++- .../Pure/Local/RandomizedResponseAlt.lean | 183 ++++++++---------- 2 files changed, 164 insertions(+), 110 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 5c8bf030..91d6c3e3 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -13,6 +13,10 @@ lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by rin lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop +lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): +2 * (@Nat.cast ENNReal NonAssocSemiring.toNatCast num) < @Nat.cast ENNReal CanonicallyOrderedCommSemiring.toNatCast ↑den := + by norm_cast + lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by rw [@inv_eq_one_div] rw [@inv_eq_one_div] @@ -32,19 +36,96 @@ lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ rw [← @ENNReal.inv_ne_zero] rw [← @ENNReal.inv_ne_zero] at h1 rw [@ENNReal.div_eq_inv_mul] - sorry lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by intro h1 sorry +lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by + intro h1 + apply ENNReal.div_lt_of_lt_mul + rw [@ENNReal.mul_comm_div] + rw [ENNReal.div_self] + simp + exact h1 + exact h.left + exact h.right + +lemma quot_gt_one_rev (a b : ENNReal): b < a -> 1 < a/b := by + cases hb : b == 0 with + | true => simp at hb + subst hb + intro ha + have h1 : a / 0 = ⊤ := by + rw [@ENNReal.div_eq_top] + apply Or.inl + apply And.intro + aesop + trivial + rw [h1] + decide + | false => simp at hb + intro ha + have h1: b/b = 1 := by + rw [ENNReal.div_self] + aesop + aesop + cases hbT : b == ⊤ with + | true => + simp at hbT + aesop + | false => + simp at hbT + have h2: b/b < a/b := by + apply div_div_cancel_rev b a b + aesop + exact ha + rw[h1] at h2 + exact h2 + lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by intro h cases hb: b == 0 with | true => simp at hb - sorry - | false => sorry + cases ha: a == 0 with + | true => simp at ha + subst hb + subst ha + apply False.elim + have nh: ¬ (1 : ENNReal) < 0/0 := by simp + contradiction + | false => simp at ha + have ha1: a > 0 := by exact pos_iff_ne_zero.mpr ha + rw[←hb] at ha1 + exact ha1 + | false => simp at hb + cases hbT : b == ⊤ with + | true => simp at hbT + subst hbT + have h1 : ¬ 1 < a / ⊤ := by simp_all only [ENNReal.div_top, not_lt_zero'] + apply False.elim + apply h1 + exact h + | false => + simp at hbT + have h1: 1 * b < (a / b) * b := ENNReal.mul_lt_mul_right' hb hbT h + rw [@ENNReal.mul_comm_div] at h1 + rw [ENNReal.div_self] at h1 + rw [@CanonicallyOrderedCommSemiring.one_mul] at h1 + rw [@CanonicallyOrderedCommSemiring.mul_one] at h1 + exact h1 + apply hb + apply hbT + +lemma quot_lt_one (a b : ENNReal): a/b < 1 -> a < b := by sorry + +lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry + +lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by + intro h + apply div_ineq_flip + exact quot_gt_one_rev a b h lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by @@ -58,4 +139,8 @@ lemma tsum_ite_not (f : List Bool -> ENNReal): ∑' (x : List Bool), (if x = [] then 0 else f x) = ∑' (x : List Bool), if assm : x ≠ [] then f x else 0 := by simp_all [ite_not] +lemma tsum_ite_mult (f : T -> ENNReal) (P : T -> Bool): + (∑' (x : T), f x * if (P x) then 1 else 0) = ∑' (x : T), if (P x) then f x else 0 := by simp_all only [mul_ite, + mul_one, mul_zero] + end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 447a61a0..e5d09391 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -26,9 +26,7 @@ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by rw[RRSingleSample] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + aesop sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): @@ -61,24 +59,28 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P RRSingleSample query num den h l b ≠ 0 := by simp [RRSingleSample] cases hb : b == query l with - | true => - | false => sorry + | true => simp at hb + subst hb + simp + rw [@tsub_eq_zero_iff_le] + rw [@Mathlib.Tactic.PushNeg.not_le_eq] + apply quot_lt_one_rev + norm_cast + rw [PNat.mul_coe] + simp_all only [PNat.val_ofNat] + sorry + | false => simp at hb + rw [← Bool.eq_not_of_ne hb] + intro j + apply And.intro + trivial + apply And.intro + {norm_cast + rw [@Nat.sub_eq_zero_iff_le] + linarith + } + {exact ne_of_beq_false rfl} -lemma RRSingleSample_non_zero2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ 0 := by - cases hb: b with - | true => cases hq: query l with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] - aesop - apply pnat_zero_imp_false den a - sorry - | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] - sorry - | false => cases hq: query l with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] - sorry - | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] - sorry lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ ⊤ := by have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by @@ -121,26 +123,6 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) - lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSample query num den h l₁ l₂= 0 := by - induction l₁ generalizing l₂ with - | nil => simp [RRSample, -mapM] - aesop - | cons hd tl ih => - simp [RRSample, -mapM] - simp [RRSample, -mapM] at ih - apply And.intro - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - /- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do let r ← MultiBernoulliSample seed_list return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ @@ -180,6 +162,29 @@ lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ +lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample query num den h l₁ l₂= 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSample, -mapM] + aesop + | cons hd tl ih => + simp [RRSample, -mapM] + simp [RRSample, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen + namespace SLang lemma simplifier_1 (f : T -> SLang Bool): (∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by @@ -363,8 +368,6 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de -- arithmetic now sorry - - lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.length = x.length)(h1: l ≠ [])(h2 : x≠ []) : (∏' (i: Fin l.length), f l[i.val] x[i.val]) = f (l[0]'(by sorry)) (x[0]'(by sorry )) * ∏' (i : Fin l.tail.length), f (l.tail)[i.val] (x.tail[i.val]'(by sorry)):= by induction l generalizing x with @@ -387,7 +390,17 @@ lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.lengt simp_all only [ne_eq, not_false_eq_true] lemma prod_split (f: T → SLang Bool)(l a b : List T)(x c d : List Bool)(hl: l = a++b)(hx: x = c++d)(xl : x.length = l.length)(ac: a.length = c.length) : ∏' (i: Fin l.length), f l[i.val] x[i.val] = (∏'(i :Fin a.length), f a[i.val] c[i.val]) * ∏'(i: Fin b.length), f b[i.val] (d[i.val]'(by sorry)) := by - rw[tprod_fintype] + rw[@tprod_fintype, @tprod_fintype, @tprod_fintype] + have h1 : l.length = a.length + b.length := by aesop + have h2: Fin l.length = Fin (a.length + b.length) := by aesop + sorry + -- rw [@Fin.prod_univ_add] + -- simp_all only [Fin.coe_castAdd, Fin.coe_natAdd] + +lemma finprod_test (a b : ℕ) (h : a = b) (f : Fin a -> ENNReal) (g : Fin b -> ENNReal): ∏ (i : Fin a), f i = ∏ (i : Fin b), g i := by + rw [h] + simp only [Fin.prod_univ_castAdd, Fin.coe_castAdd, Fin.coe_natAdd] + sorry lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by @@ -406,13 +419,16 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] have helper: ∀i : Fin (l₁.length - 1), l₁[↑(Fin.succAbove a.length i)]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by sorry have helper2: ∀i : Fin (l₁.length - 1), f₁ i = f₂ i := by sorry - have helper3: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by sorry + have helper4: l₁.length - 1 = l₂.length - 1 := by + rw[h1] + rw[h2] + simp + have helper3: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by aesop have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by - rw[Finset.prod_congr] - - - - + have e: Fin (l₁.length - 1) ≃ Fin (l₂.length - 1) := by rw [helper3] + apply Fintype.prod_equiv (by rw[helper3]) + intro y + sorry rw[hlp] rw[ENNReal.mul_div_mul_right] simp @@ -432,18 +448,7 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ - - - - - - - - - - - -lemma reduction_aux (a b c List T)(x: List Bool)(f: T → SLang Bool): ∏' (i: Fin (a++b).length) +-- lemma reduction_aux (a b c List T)(x: List Bool)(f: T → SLang Bool): ∏' (i: Fin (a++b).length) lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by @@ -550,48 +555,10 @@ lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] simp[aux] case hc => - rw[@tprod_fintype] - rw[Nat.eq_zero_of_mul_eq_zero] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- - - - - - + rw [@Finset.prod_ne_zero_iff] + intro a ha + sorry open Finset open scoped BigOperators @@ -632,10 +599,12 @@ cases xlen1 : l₁.length == x.length with _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop | false => simp at xlen1 - /- We should have a lemma that says that RRSample_PMF applied to a list of length - different than the length of l₁ gives zero. -/ - /- We should also clarify with Ethan about why the PureDP definition seems to allow division by zero...-/ - sorry - + rw [←Ne.eq_def] at xlen1 + have numerator_zero: RRSample_PMF query num den h l₁ x = 0 := by + rw [RRSamplePMF_diff_lengths] + exact xlen1 + rw [numerator_zero] + rw [@ENNReal.zero_div] + simp -- rw[reduction l₁ l₂ x (RRSingleSample query num den h) hl₁ hl₂ xlen xlen2] From f50f17b36fd8c4e8a956025aa52dc3c991b0c9c3 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 21 Jul 2025 19:05:26 -0700 Subject: [PATCH 063/216] cleanup --- .../Local/LocalDP/DPwithUpdateNeighbour.lean | 27 +++++ .../Pure/Local/Normalization.lean | 8 +- .../Pure/Local/Playground/UsefulLemmas.lean | 1 + .../Local/RandomizedResponse/Definitions.lean | 10 +- .../RandomizedResponse/PMFProperties.lean | 16 +-- .../Pure/Local/RandomizedResponseAlt.lean | 108 ++---------------- 6 files changed, 47 insertions(+), 123 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index 4fd6f6db..8c58ee8f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -13,3 +13,30 @@ def DP_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := def DP_singleton_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_singleton_withGeneralNeighbour m (UpdateNeighbour) ε end SLang + +theorem singleton_to_event_update (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_withUpdateNeighbour m ε) : + DP_withUpdateNeighbour m ε := by + simp [DP_withUpdateNeighbour] + simp [DP_singleton_withUpdateNeighbour] at h + intros l₁ l₂ h1 S + replace h1 := h l₁ l₂ h1 + have A : ∀ (x : U), (if x ∈ S then m l₁ x else 0) / (if x ∈ S then m l₂ x else 0) ≤ ENNReal.ofReal ε.exp := by + aesop + have B : ∀ b : ENNReal, b ≠ 0 ∨ ENNReal.ofReal ε.exp ≠ ⊤ := by + aesop + have C : ∀ b : ENNReal, b ≠ ⊤ ∨ ENNReal.ofReal ε.exp ≠ 0 := by + intro b + right + simp + exact Real.exp_pos ε + have D := fun { x : U } => @ENNReal.div_le_iff_le_mul (if x ∈ S then m l₁ x else 0) (if x ∈ S then m l₂ x else 0) (ENNReal.ofReal ε.exp) (B (if x ∈ S then m l₂ x else 0)) (C (if x ∈ S then m l₂ x else 0)) + have E := fun x : U => D.1 (A x) + have F := ENNReal.tsum_le_tsum E + rw [ENNReal.tsum_mul_left] at F + rw [← ENNReal.div_le_iff_le_mul] at F + · clear h1 A B C D + trivial + · aesop + · right + simp + exact Real.exp_pos ε diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index 32ebfce8..b9375244 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -4,6 +4,7 @@ import SampCert import SampCert.SLang import Mathlib.Data.Set.Basic import Mathlib.Data.Set.Basic +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite /- In this file, we show that if a function f : α -> SLang Bool normalizes, in the sense that ∑' (b : List Bool) f a b = 1 @@ -87,11 +88,9 @@ lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) apply symm apply list_bool_tsum_only_tl b -lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring - lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd : α)(tl : List α) (h : ∑' (b : Bool), f hd b = 1): ∑' (a : Bool), f hd a * mapM f tl b = mapM f tl b := by rw [tsum_bool] - rw [ennreal_mul_assoc] + rw [ENNRealLemmas.ennreal_mul_assoc] rw [←tsum_bool] rw [h] rw [@CanonicallyOrderedCommSemiring.one_mul] @@ -111,9 +110,6 @@ lemma Norm_func_norm_on_list [LawfulMonad SLang] (α : Type)(f: α → SLang Boo enter [1, 1, b, 1, a] simp [-mapM] rw [simplifier1_gen] - /- rewrite as a double sum, the first sum being over possible a.heads, and the second - some being over all list Bools, with the conditional now being on the Boolean - in the first sum. Afterwards, it should be straightforward to use the inductive hypothesis -/ rw [@ENNReal.tsum_comm] conv => enter [1, 1, b] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean index 0da21fda..77ac44b7 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean @@ -7,3 +7,4 @@ import SampCert #check tsum_eq_tsum_diff_singleton #check tsum_ite_eq #check tsum_eq_single +#check ENNReal.div_self diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index ebb428b1..f1433331 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -5,13 +5,12 @@ import SampCert namespace RandomizedResponse open SLang -/- open MultiBernoulli -/ -lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by +lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (query l) r @@ -20,7 +19,7 @@ def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (q changes their answer. It is distributed according to the probability distribution from which we sample r.-/ -def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do +def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do /- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) @@ -28,7 +27,4 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num let r ← MultiBernoulliSample seed_list return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ -/- At this point, we should be set to prove that RRSample is normalized and that it is - differentially private. The definition is computable, as we need. -/ - end RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index d32ca560..643fd176 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -10,7 +10,7 @@ open RandomizedResponse #check RandomizedResponse.RRSingleSample #check SLang.BernoulliSample_normalizes -lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : +lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : HasSum (RRSingleSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] rw [@tsum_bool] @@ -32,7 +32,7 @@ lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : rw [@AddCommMonoidWithOne.add_comm] } -lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : +lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold RRSample @@ -41,15 +41,5 @@ lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num rw [← Summable.hasSum_iff ENNReal.summable] apply RRSingleSample_PMF_helper -/- lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (s : List SeedType) (l : List T) : - HasSum (RRSample2 query s l) 1 := by - rw[RRSample2] - simp_all only [bind, pure] - rw[Summable.hasSum_iff ENNReal.summable] - rw[←MultiBernoulliSample_normalizes s] - simp_all only [bind_apply, pure_apply, mul_ite, mul_one, mul_zero] - sorry --/ - -def RRSample_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := +def RRSample_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index e5d09391..39a9f72c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -5,24 +5,17 @@ import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite /-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang open ENNRealLemmas +open RandomizedResponse /- open MultiBernoulli -/ -lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by - simp_all only [tsub_le_iff_right] - linarith - lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop -/- Eventually, we may want query to return more than just a Boolean -/ -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do - let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) - return Bool.xor (query l) r - lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by rw[RRSingleSample] @@ -119,49 +112,6 @@ lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNa aesop exact hden -def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ - l.mapM (fun x => RRSingleSample query num den h x) - -/- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do - let r ← MultiBernoulliSample seed_list - return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ - -/- At this point, we should be set to prove that RRSample is normalized and that it is - differentially private. The definition is computable, as we need. -/ - -lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : - HasSum (RRSingleSample query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - rw [@tsum_bool] - rw[RRSingleSample] - cases query l - { - simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, - Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] - rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] - rw[tsum_bool] - } - { - simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, - Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', - mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] - rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] - rw[tsum_bool] - rw [@AddCommMonoidWithOne.add_comm] - } - -/- This should now follow from Renee's abstraction of the MultiBernoulli proof -/ -lemma RRSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : - HasSum (RRSample query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - sorry - /- See Renee's proof in the PMFProperties file-/ - -def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ - lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSample query num den h l₁ l₂= 0 := by induction l₁ generalizing l₂ with @@ -253,40 +203,6 @@ induction l generalizing a with theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob - -open Classical - -theorem singleton_to_event2 (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_withUpdateNeighbour m ε) : - DP_withUpdateNeighbour m ε := by - simp [DP_withUpdateNeighbour] - simp [DP_singleton_withUpdateNeighbour] at h - intros l₁ l₂ h1 S - replace h1 := h l₁ l₂ h1 - have A : ∀ (x : U), (if x ∈ S then m l₁ x else 0) / (if x ∈ S then m l₂ x else 0) ≤ ENNReal.ofReal ε.exp := by - aesop - have B : ∀ b : ENNReal, b ≠ 0 ∨ ENNReal.ofReal ε.exp ≠ ⊤ := by - aesop - have C : ∀ b : ENNReal, b ≠ ⊤ ∨ ENNReal.ofReal ε.exp ≠ 0 := by - intro b - right - simp - exact Real.exp_pos ε - have D := fun { x : U } => @ENNReal.div_le_iff_le_mul (if x ∈ S then m l₁ x else 0) (if x ∈ S then m l₂ x else 0) (ENNReal.ofReal ε.exp) (B (if x ∈ S then m l₂ x else 0)) (C (if x ∈ S then m l₂ x else 0)) - have E := fun x : U => D.1 (A x) - have F := ENNReal.tsum_le_tsum E - rw [ENNReal.tsum_mul_left] at F - rw [← ENNReal.div_le_iff_le_mul] at F - · clear h1 A B C D - trivial - · aesop - · right - simp - exact Real.exp_pos ε - - - -#check ENNReal.div_self - lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b ≤ (den + 2 * num) / (den - 2 * num) := by @@ -334,6 +250,8 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de {rw [@ENNReal.div_ne_zero] apply And.intro simp + norm_cast + simp [Nat.cast_mul] intro hd sorry /- For this sorry, we need the h hypothesis to be a strict inequality -/ apply mult_ne_top @@ -397,11 +315,6 @@ lemma prod_split (f: T → SLang Bool)(l a b : List T)(x c d : List Bool)(hl: l -- rw [@Fin.prod_univ_add] -- simp_all only [Fin.coe_castAdd, Fin.coe_natAdd] -lemma finprod_test (a b : ℕ) (h : a = b) (f : Fin a -> ENNReal) (g : Fin b -> ENNReal): ∏ (i : Fin a), f i = ∏ (i : Fin b), g i := by - rw [h] - simp only [Fin.prod_univ_castAdd, Fin.coe_castAdd, Fin.coe_natAdd] - sorry - lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by rw[tprod_fintype] @@ -447,7 +360,6 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp[mod] - -- lemma reduction_aux (a b c List T)(x: List Bool)(f: T → SLang Bool): ∏' (i: Fin (a++b).length) lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / @@ -565,7 +477,7 @@ open scoped BigOperators theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((1/2 + num / den) / (1/2 - num / den))) := by -apply singleton_to_event2 +apply singleton_to_event_update intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with | true => @@ -590,13 +502,17 @@ cases xlen1 : l₁.length == x.length with rw[←xlen1] rw [@Nat.lt_add_right_iff_pos] simp - calc + {calc RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by apply final_step_combined exact h - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop} + {apply RRSingleSample_non_zero query num den h} + {apply RRSingleSample_finite query num den h} + {aesop} + {aesop} | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 @@ -606,5 +522,3 @@ cases xlen1 : l₁.length == x.length with rw [numerator_zero] rw [@ENNReal.zero_div] simp - --- rw[reduction l₁ l₂ x (RRSingleSample query num den h) hl₁ hl₂ xlen xlen2] From 99bfa27880865875d790dadd572cefebce86b8b5 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 21 Jul 2025 19:16:52 -0700 Subject: [PATCH 064/216] moreorganization --- .../Local/RandomizedResponse/BasicLemmas.lean | 135 ++++++++++++++++++ .../Pure/Local/RandomizedResponseAlt.lean | 123 +--------------- 2 files changed, 136 insertions(+), 122 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean new file mode 100644 index 00000000..2815fb2c --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -0,0 +1,135 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +import SampCert.DifferentialPrivacy.Pure.DP +import SampCert.Samplers.Bernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite + +namespace RandomizedResponse + +open SLang +open ENNRealLemmas + +lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + +lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): + RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + aesop + sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ + +lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): + RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): + RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): + RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + /- This is the same state as the first lemma that's not working, + again it's just annoying arithmetic. -/ + sorry + +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + simp [RRSingleSample] + cases hb : b == query l with + | true => simp at hb + subst hb + simp + rw [@tsub_eq_zero_iff_le] + rw [@Mathlib.Tactic.PushNeg.not_le_eq] + apply quot_lt_one_rev + norm_cast + rw [PNat.mul_coe] + simp_all only [PNat.val_ofNat] + sorry + | false => simp at hb + rw [← Bool.eq_not_of_ne hb] + intro j + apply And.intro + trivial + apply And.intro + {norm_cast + rw [@Nat.sub_eq_zero_iff_le] + linarith + } + {exact ne_of_beq_false rfl} + +lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ ⊤ := by + have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by + rw [@ne_iff_lt_or_gt] + apply Or.inr + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt, ENNReal.coe_pos, Nat.cast_pos] + apply den.2 + cases hb: b with + | true => cases hq: query l with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + apply div_ne_top + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.true.h2.h1 ?true.true.h2.h2 + aesop + exact hden + | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.false.h2.h1 ?true.false.h2.h2 + aesop + exact hden + | false => cases hq: query l with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.true.h2.h1 ?false.true.h2.h2 + aesop + exact hden + | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] + apply div_ne_top + rw [@ENNReal.add_ne_top] + apply And.intro + aesop + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.false.h2.h1 ?false.false.h2.h2 + aesop + exact hden + +lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample query num den h l₁ l₂= 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSample, -mapM] + aesop + | cons hd tl ih => + simp [RRSample, -mapM] + simp [RRSample, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 39a9f72c..e730ee9a 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -4,6 +4,7 @@ import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite @@ -12,128 +13,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite open SLang open ENNRealLemmas open RandomizedResponse -/- open MultiBernoulli -/ - -lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop - -lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): - RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] - aesop - sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ - -lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): - RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): - RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): - RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - /- This is the same state as the first lemma that's not working, - again it's just annoying arithmetic. -/ - sorry - -lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ 0 := by - simp [RRSingleSample] - cases hb : b == query l with - | true => simp at hb - subst hb - simp - rw [@tsub_eq_zero_iff_le] - rw [@Mathlib.Tactic.PushNeg.not_le_eq] - apply quot_lt_one_rev - norm_cast - rw [PNat.mul_coe] - simp_all only [PNat.val_ofNat] - sorry - | false => simp at hb - rw [← Bool.eq_not_of_ne hb] - intro j - apply And.intro - trivial - apply And.intro - {norm_cast - rw [@Nat.sub_eq_zero_iff_le] - linarith - } - {exact ne_of_beq_false rfl} - -lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ ⊤ := by - have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by - rw [@ne_iff_lt_or_gt] - apply Or.inr - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt, ENNReal.coe_pos, Nat.cast_pos] - apply den.2 - cases hb: b with - | true => cases hq: query l with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] - apply div_ne_top - exact Ne.symm (ne_of_beq_false rfl) - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.true.h2.h1 ?true.true.h2.h2 - aesop - exact hden - | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] - apply div_ne_top - aesop - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.false.h2.h1 ?true.false.h2.h2 - aesop - exact hden - | false => cases hq: query l with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] - apply div_ne_top - aesop - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.true.h2.h1 ?false.true.h2.h2 - aesop - exact hden - | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] - apply div_ne_top - rw [@ENNReal.add_ne_top] - apply And.intro - aesop - exact Ne.symm (ne_of_beq_false rfl) - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.false.h2.h1 ?false.false.h2.h2 - aesop - exact hden - -lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSample query num den h l₁ l₂= 0 := by - induction l₁ generalizing l₂ with - | nil => simp [RRSample, -mapM] - aesop - | cons hd tl ih => - simp [RRSample, -mapM] - simp [RRSample, -mapM] at ih - apply And.intro - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - -lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen namespace SLang lemma simplifier_1 (f : T -> SLang Bool): From d3edd55abd02e3cd7f0f5fea4fc60448e64e270a Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Mon, 21 Jul 2025 23:50:10 -0700 Subject: [PATCH 065/216] proof of DP reduction step --- .../Pure/Local/RandomizedResponseAlt.lean | 116 +++++++++++++++--- 1 file changed, 100 insertions(+), 16 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 5f702d6e..5733ff31 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -376,16 +376,11 @@ lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.lengt List.length_eq_zero, one_ne_zero, and_false] | cons xh xt x => simp simp_all only [ne_eq, not_false_eq_true] -lemma prod_split (f: T → SLang Bool)(l a b : List T)(x c d : List Bool)(hl: l = a++b)(hx: x = c++d)(xl : x.length = l.length)(ac: a.length = c.length) : -∏' (i: Fin l.length), f l[i.val] x[i.val] = (∏'(i :Fin a.length), f a[i.val] c[i.val]) * ∏'(i: Fin b.length), f b[i.val] (d[i.val]'(by sorry)) := by - rw[tprod_fintype] -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo < ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by rw[tprod_fintype] rw[tprod_fintype] - have f₁ := fun(b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry)) - have f₂ := fun(b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry)) rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] have ind: a.length < x.length := by @@ -395,15 +390,100 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ conv => enter[1,2] rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] - have helper: ∀i : Fin (l₁.length - 1), l₁[↑(Fin.succAbove a.length i)]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by sorry - have helper2: ∀i : Fin (l₁.length - 1), f₁ i = f₂ i := by sorry - have helper3: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by sorry + have helper: ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by + intro i + simp only [h1,h2] + by_cases i < a.length + case pos h => + unfold Fin.succAbove + have h' : i.castSucc < ↑a.length := by + rw [@Fin.castSucc_lt_iff_succ_le] + rw [@Fin.le_iff_val_le_val] + simp + have mod: a.length % (l₁.length-1+1) = a.length := by + rw[Nat.mod_eq_of_lt] + rw[hx] + rw[Nat.sub_add_cancel] + exact ind + rw[← hx] + rw[h1] + simp + linarith + rw[mod] + simp[Nat.succ_le_of_lt h] + + + + simp only[h'] + simp only [↓reduceIte, Fin.coe_castSucc, + Fin.getElem_fin] + rw[List.getElem_append_left] + rw[List.getElem_append_left] + rw[List.getElem_append_left] + rw[List.getElem_append_left] + exact h + simp[h] + linarith + simp[h] + linarith + case neg h => + unfold Fin.succAbove + have h' : ¬ i.castSucc < ↑a.length := by + simp at h + simp + rw [@Fin.le_castSucc_iff] + apply Nat.lt_succ_of_le + simp + have mod: a.length % (l₁.length-1+1) = a.length := by + rw[Nat.mod_eq_of_lt] + rw[hx] + rw[Nat.sub_add_cancel] + exact ind + rw[← hx] + rw[h1] + simp + linarith + rw[mod] + exact h + + + simp only[h'] + simp only [↓reduceIte, Fin.coe_castSucc, + Fin.getElem_fin] + rw[List.getElem_append_right] + rw[List.getElem_append_right] + simp + simp + linarith + simp + + have ile : i < l₁.length - 1 := by exact i.is_lt + simp[h1] at ile + rw[tsub_lt_iff_left] + exact ile + simp at h + exact h + + simp + linarith + simp + + have ile : i < l₁.length - 1 := by exact i.is_lt + simp[h1] at ile + rw[tsub_lt_iff_left] + exact ile + simp at h + exact h + + have helper2: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by aesop + have helper3: l₁.length - 1 = l₂.length - 1 := by aesop have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by - rw[Finset.prod_congr] - - - - + apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper3)) + simp[helper] + intro i + congr + rw [← propext cast_eq_iff_heq] + rw [← propext cast_eq_iff_heq] rw[hlp] rw[ENNReal.mul_div_mul_right] simp @@ -415,13 +495,18 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ rw[← hx] rw[h1] simp - sorry + linarith simp[mod] rw[hx] at mod rw[← hy] at mod simp[mod] + rw[Finset.prod_ne_zero_iff] + intro i + simp[nonzero] + rw[← lt_top_iff_ne_top] + sorry @@ -434,7 +519,6 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ -lemma reduction_aux (a b c List T)(x: List Bool)(f: T → SLang Bool): ∏' (i: Fin (a++b).length) lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by From 56f176de484b72252ea561e1ba8723aba697c7b3 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 22 Jul 2025 00:07:19 -0700 Subject: [PATCH 066/216] reduction proof --- .../Pure/Local/RandomizedResponseAlt.lean | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 5733ff31..56226f60 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -377,7 +377,7 @@ lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.lengt | cons xh xt x => simp simp_all only [ne_eq, not_false_eq_true] -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo < ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by rw[tprod_fintype] rw[tprod_fintype] @@ -506,7 +506,9 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp[nonzero] rw[← lt_top_iff_ne_top] - sorry + apply ENNReal.prod_lt_top + intro i + simp[noninf] From 5a79b5e6e3b426aa58db6d2458e683ce0aa1ef83 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 22 Jul 2025 12:13:53 -0700 Subject: [PATCH 067/216] nochange --- .../Pure/Local/ENNRealLemmasSuite.lean | 1 + .../Pure/Local/LawfulMonadSLang.lean | 2 ++ .../Pure/Local/RandomizedResponseAlt.lean | 20 ++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 91d6c3e3..82746fc9 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -118,6 +118,7 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hb apply hbT + lemma quot_lt_one (a b : ENNReal): a/b < 1 -> a < b := by sorry lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean b/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean index a2f96e32..c56db4d4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean @@ -2,6 +2,8 @@ import SampCert open SLang +/- In this file, we instantiate SLang as a LawfulMonad. This makes simp much stronger.-/ + instance SLang.LawfulMonad : LawfulMonad SLang where map_const := by intro a b diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index e730ee9a..4ca614e7 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -125,7 +125,25 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de sorry | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] rw[ENNReal.div_self] - {sorry} + { rw [@Decidable.le_iff_lt_or_eq] + cases hnum : num == 0 with + | true => simp at hnum + apply Or.inr + subst hnum + simp + rw [ENNReal.div_self] + norm_num + apply pnat_zero_imp_false + simp + | false => simp at hnum + apply Or.inl + apply quot_gt_one_rev + simp + have h1: 0 < (2 : ENNReal) * num + 2 * num := by sorry + have h2: den < den + (2 : ENNReal) * num + 2 * num := by sorry + aesop + sorry + } {rw [@ENNReal.div_ne_zero] apply And.intro simp From ebd7e7316897d68ee0637dcc85763ee2f908fa1d Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 22 Jul 2025 15:55:13 -0700 Subject: [PATCH 068/216] updates --- .../Pure/Local/RandomizedResponseAlt.lean | 218 +++++------------- 1 file changed, 63 insertions(+), 155 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 56226f60..11bffa15 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -354,43 +354,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num ≤ -- arithmetic now sorry - - -lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.length = x.length)(h1: l ≠ [])(h2 : x≠ []) : - (∏' (i: Fin l.length), f l[i.val] x[i.val]) = f (l[0]'(by sorry)) (x[0]'(by sorry )) * ∏' (i : Fin l.tail.length), f (l.tail)[i.val] (x.tail[i.val]'(by sorry)):= by - induction l generalizing x with - | nil => aesop - | cons l ls hl => - rw [tprod_fintype] - rw[tprod_fintype] - simp only [List.getElem_cons_zero] - simp only [List.tail_cons] - simp [Finset.prod_eq_multiset_prod] - - rw [@Fin.prod_ofFn] - rw[@Fin.prod_ofFn] - have h3 : ∀(i: Fin ls.length), (x[i.val+1]'(by sorry)) = (x.tail[i.val]'(by sorry)) := by - intro i - induction x with - | nil => simp_all only [ne_eq, not_false_eq_true, List.length_cons, List.length_nil, add_eq_zero, - List.length_eq_zero, one_ne_zero, and_false] - | cons xh xt x => simp - simp_all only [ne_eq, not_false_eq_true] - -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / - (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[tprod_fintype] - rw[tprod_fintype] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] - - have ind: a.length < x.length := by - rw[← hx] - rw[h1] - simp - conv => - enter[1,2] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] - have helper: ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by +lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by intro i simp only [h1,h2] by_cases i < a.length @@ -402,10 +366,9 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp have mod: a.length % (l₁.length-1+1) = a.length := by rw[Nat.mod_eq_of_lt] - rw[hx] rw[Nat.sub_add_cancel] - exact ind - rw[← hx] + rw[h1] + simp rw[h1] simp linarith @@ -436,10 +399,9 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp have mod: a.length % (l₁.length-1+1) = a.length := by rw[Nat.mod_eq_of_lt] - rw[hx] rw[Nat.sub_add_cancel] - exact ind - rw[← hx] + rw[h1] + simp rw[h1] simp linarith @@ -475,11 +437,24 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp at h exact h +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / + (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by + rw[tprod_fintype] + rw[tprod_fintype] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] + + have ind: a.length < x.length := by + rw[← hx] + rw[h1] + simp + conv => + enter[1,2] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] have helper2: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by aesop have helper3: l₁.length - 1 = l₂.length - 1 := by aesop have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper3)) - simp[helper] + simp[succHelp l₁ l₂ h1 h2] intro i congr rw [← propext cast_eq_iff_heq] @@ -510,126 +485,59 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ intro i simp[noninf] - - - - - - - - - - - - -lemma reduction (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: f (k: T) (bo: Bool) ≠ 0)(noninf: f (k: T) (bo: Bool) ≠ ⊤):(∏' (i : Fin l₁.length), f (l₁[i.val]) (x[i.val])) / - ∏' (i : Fin l₂.length), f (l₂[i.val]) (x[i.val]) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[List.append_assoc] at h1 - rw[List.append_assoc] at h2 - let c := List.take a.length x - have c_def : c = List.take a.length x := rfl - let d := List.drop a.length x - have d_def : d = List.drop a.length x := by rfl - have leq: a.length < x.length := by { - rw[← hx] +theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by + have h_len : l₁.length - 1 + 1 = l₁.length := by + rw[Nat.sub_add_cancel] rw[h1] simp - } - have ac: a.length = c.length := by { - rw[c_def] - rw[List.length_take] - rw[min_eq_left_of_lt leq] - } - rw[prod_split f l₁ a ([n]++b) x c d (by exact h1) (by rw[List.take_append_drop a.length x]) (by simp[hx]) (by exact ac)] - rw[prod_split f l₂ a ([m]++b) x c d (by exact h2) (by rw[List.take_append_drop a.length x]) (by simp[hy]) (by exact ac)] - rw[@tprod_fintype] - rw[ENNReal.mul_div_mul_left] - let e := List.take ([n].length) d - have e_def : e = List.take [n].length d := by rfl - let g := List.drop ([n].length) d - rw[prod_split f ([n]++b) [n] b d e g (by rfl) (by rw[List.take_append_drop])] - rw[prod_split f ([m]++b) [m] b d e g (by rfl) (by rw[List.take_append_drop])] - rw[@tprod_fintype] - rw[ENNReal.mul_div_mul_right] - simp only [List.length_singleton, Fin.coe_fin_one, List.getElem_cons_zero] - rw [tprod_fintype] - rw [@Fin.prod_univ_one] - rw [@Fin.prod_univ_one] - rw [h1] - rw [h2] - rw[List.getElem_append_right] - case h => - simp - case h'' => - simp - rw[List.getElem_append_right a] - case h => - simp - case h'' => + linarith + have h_len2 : l₂.length - 1 + 1 = l₂.length := by + rw[Nat.sub_add_cancel] + rw[h2] simp + linarith + rw[tprod_fintype] + rw[tprod_fintype] + rw[Fintype.prod_equiv (Equiv.cast (congr_arg Fin h_len.symm))] + rw[Fintype.prod_equiv (Equiv.cast (congr_arg Fin h_len2.symm))] + rw[← tprod_fintype] + rw[← tprod_fintype] + rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] simp - have xx : x = c++(e++g) := by - { - rw[List.take_append_drop] - rw[List.take_append_drop] - } - rw[xx] - rw[List.getElem_append_right] + intro i + congr + rw[Nat.sub_add_cancel] + rw[h2] + simp + linarith + rw [← propext cast_eq_iff_heq] + rw[Nat.sub_add_cancel] + rw[h2] + simp + linarith - rw[List.getElem_append_left] - simp [ac] + rw [← propext cast_eq_iff_heq] + intro i + congr + rw[Nat.sub_add_cancel] + rw[h1] + simp + linarith + + simp + rw[← propext cast_eq_iff_heq] + rw[Nat.sub_add_cancel] + rw[h1] + simp + linarith + + simp + rw[← propext cast_eq_iff_heq] - case h => - rw[ac] - simp - rw[e_def] - simp - rw[d_def] - simp - exact leq - case h' => - rw[ac] - simp - left - rw[e_def] - simp - rw[d_def] - simp - exact leq - case h => - rw[ac] - simp - case xl => - rw[d_def] - simp - rw[← hx] - rw[h1] - simp - case ac => - rw[e_def] - rw[d_def] - simp - have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] - simp[aux] - case xl => - rw[d_def] - simp - rw[← hx] - rw[h1] - simp - case ac => - rw[e_def] - simp - rw[d_def] - simp - have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] - simp[aux] - case hc => - rw[@tprod_fintype] - rw[Nat.eq_zero_of_mul_eq_zero] From 3cd65ff52fd142c21b8c0905b934a9a9e7fffc8c Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 22 Jul 2025 15:55:58 -0700 Subject: [PATCH 069/216] Update --- .../DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 11bffa15..f8dcda59 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -598,7 +598,7 @@ cases xlen1 : l₁.length == x.length with have xlen2 : l₂.length = x.length := by aesop simp have xlen3 : l₁.length = x.length := by aesop - rw[reduction l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] From 8d68ac695068fede2c6c581f0a333e1571eda715 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 22 Jul 2025 17:14:42 -0700 Subject: [PATCH 070/216] nosorries --- .../Pure/Local/RandomizedResponseAlt.lean | 203 ++++-------------- 1 file changed, 45 insertions(+), 158 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean index 936f299b..dcd2da40 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean @@ -183,53 +183,32 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de -- arithmetic now sorry -<<<<<<< HEAD -lemma head_tail_prod (x: List Bool)(l : List T)(f: T → SLang Bool) (h: l.length = x.length)(h1: l ≠ [])(h2 : x≠ []) : - (∏' (i: Fin l.length), f l[i.val] x[i.val]) = f (l[0]'(by sorry)) (x[0]'(by sorry )) * ∏' (i : Fin l.tail.length), f (l.tail)[i.val] (x.tail[i.val]'(by sorry)):= by - induction l generalizing x with - | nil => aesop - | cons l ls hl => - rw [tprod_fintype] - rw[tprod_fintype] - simp only [List.getElem_cons_zero] - simp only [List.tail_cons] - simp [Finset.prod_eq_multiset_prod] - - rw [@Fin.prod_ofFn] - rw[@Fin.prod_ofFn] - have h3 : ∀(i: Fin ls.length), (x[i.val+1]'(by sorry)) = (x.tail[i.val]'(by sorry)) := by - intro i - induction x with - | nil => simp_all only [ne_eq, not_false_eq_true, List.length_cons, List.length_nil, add_eq_zero, - List.length_eq_zero, one_ne_zero, and_false] - | cons xh xt x => simp - simp_all only [ne_eq, not_false_eq_true] -lemma prod_split (f: T → SLang Bool)(l a b : List T)(x c d : List Bool)(hl: l = a++b)(hx: x = c++d)(xl : x.length = l.length)(ac: a.length = c.length) : -∏' (i: Fin l.length), f l[i.val] x[i.val] = (∏'(i :Fin a.length), f a[i.val] c[i.val]) * ∏'(i: Fin b.length), f b[i.val] (d[i.val]'(by sorry)) := by - rw[@tprod_fintype, @tprod_fintype, @tprod_fintype] - have h1 : l.length = a.length + b.length := by aesop - have h2: Fin l.length = Fin (a.length + b.length) := by aesop - sorry - -- rw [@Fin.prod_univ_add] - -- simp_all only [Fin.coe_castAdd, Fin.coe_natAdd] - -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / - (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by sorry)) (x[a.length]'(by sorry)) / f (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) := by - rw[tprod_fintype] - rw[tprod_fintype] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] - - have ind: a.length < x.length := by - rw[← hx] - rw[h1] +lemma valid_index0 (l₁ : List T)(h1: l₁ = a++[n]++b) (i : Fin (l₁.length - 1)): (Fin.succAbove (a.length) i).val < l₁.length := by + have hl : l₁.length - 1 + 1 = l₁.length := by + rw [Nat.sub_add_cancel] + rw [h1] simp - conv => - enter[1,2] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] - have helper: ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by -======= -lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(by sorry) = l₂[Fin.succAbove a.length i]'(by sorry) := by ->>>>>>> origin/main + linarith + simp [Fin.succAbove] + split + simp [Fin.castSucc] + {calc + i.val < l₁.length - 1 := i.2 + _ < l₁.length := by aesop} + { + calc + i.succ.val = i.val + 1 := by simp + _ < l₁.length - 1 + 1 := by linarith[i.2] + _ = l₁.length := by rw [hl] + } + +lemma valid_index1 (l₁ l₂ : List T)(h1: l₁ = a++[n]++b) (h2: l₂ = a++[m]++b) (i : Fin ((l₁.length - 1))): (Fin.succAbove (a.length) i).val < l₂.length := by + have hl: l₁.length = l₂.length := by aesop + rw[←hl] + apply valid_index0 + exact h1 + +lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(valid_index0 l₁ h1 i) = l₂[Fin.succAbove a.length i]'(valid_index1 l₁ l₂ h1 h2 i) := by intro i simp only [h1,h2] by_cases i < a.length @@ -250,8 +229,6 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw[mod] simp[Nat.succ_le_of_lt h] - - simp only[h'] simp only [↓reduceIte, Fin.coe_castSucc, Fin.getElem_fin] @@ -282,8 +259,6 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): linarith rw[mod] exact h - - simp only[h'] simp only [↓reduceIte, Fin.coe_castSucc, Fin.getElem_fin] @@ -312,11 +287,26 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): simp at h exact h -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry))) / - (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(by sorry)) (x[i.val]'(by sorry))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by +lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): + i.val < l₁.length := by + have hl1: l₁.length - 1 + 1 = l₁.length := by + rw [Nat.sub_add_cancel] + subst h1 + simp + linarith + exact Nat.lt_of_lt_of_eq i.2 hl1 + +lemma valid_index3 {l₁ : List T} {x : List Bool} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): + i.val < x.length := by + rw[←hx] + apply valid_index2 h1 i + + +lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / + (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by rw[tprod_fintype] rw[tprod_fintype] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(valid_index2 h1 b)) (x[b.val]'(valid_index3 h1 hx b))) a.length] have ind: a.length < x.length := by rw[← hx] @@ -324,7 +314,7 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp conv => enter[1,2] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(by sorry)) (x[b.val]'(by sorry))) a.length] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] have helper2: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by aesop have helper3: l₁.length - 1 = l₂.length - 1 := by aesop have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by @@ -411,102 +401,6 @@ theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h simp rw[← propext cast_eq_iff_heq] - - -<<<<<<< HEAD - case h => - rw[ac] - simp - rw[e_def] - simp - rw[d_def] - simp - exact leq - case h' => - rw[ac] - simp - left - rw[e_def] - simp - rw[d_def] - simp - exact leq - case h => - rw[ac] - simp - - case xl => - rw[d_def] - simp - rw[← hx] - rw[h1] - simp - case ac => - rw[e_def] - rw[d_def] - simp - have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] - simp[aux] - case xl => - rw[d_def] - simp - rw[← hx] - rw[h1] - simp - case ac => - rw[e_def] - simp - rw[d_def] - simp - have aux: 1 ≤ x.length - a.length := by linarith [Nat.sub_pos_of_lt leq] - simp[aux] - case hc => - rw[@tprod_fintype] - rw [@Finset.prod_ne_zero_iff] - intro a ha - sorry -======= - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --- - - - - - ->>>>>>> origin/main - open Finset open scoped BigOperators @@ -528,8 +422,7 @@ cases xlen1 : l₁.length == x.length with have xlen2 : l₂.length = x.length := by aesop simp have xlen3 : l₁.length = x.length := by aesop -<<<<<<< HEAD - rw[reduction l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] have i1: a.length < x.length := by rw[←xlen3] subst hl₁ hl₂ @@ -547,12 +440,6 @@ cases xlen1 : l₁.length == x.length with _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop} {apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} - {aesop} - {aesop} -======= - rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] ->>>>>>> origin/main - | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 have numerator_zero: RRSample_PMF query num den h l₁ x = 0 := by From a779b24e9a592937ee987a7e9bdcf9821b185c9c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 22 Jul 2025 18:58:26 -0700 Subject: [PATCH 071/216] nochange --- .../Pure/Local/RandomizedResponse/AccuracyProof.lean | 6 +++--- .../Pure/Local/RandomizedResponse/Basic.lean | 2 +- .../RandomizedResponseMain.lean} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename SampCert/DifferentialPrivacy/Pure/Local/{RandomizedResponseAlt.lean => RandomizedResponse/RandomizedResponseMain.lean} (100%) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index b20558e1..4dedae3d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -18,7 +18,7 @@ noncomputable def coeff {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ : noncomputable def constants {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := (- (X.length) / 2) + (num * X.length) / den -def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : List (SLang Bool) := +def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : List (SLang Bool) := X.map (fun x => RRSingleSample query num den h x) @@ -52,13 +52,13 @@ noncomputable def pmf.sum_list : List (PMF α) → PMF α | (x::xs) => pmf.add x (pmf.sum_list xs) -/ -def p {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den) : ℚ := +def p {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : ℚ := let bool_lst := X.map query let true_count := (bool_lst.filter (fun b => b)).length (true_count) / X.length -noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num ≤ den):= +noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den):= let coef := coeff X num den let cons := constants X num den let s := applying_RR_individually query X num den h diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean index dbf45991..9e6a21a3 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean @@ -1,4 +1,4 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProof +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.AccuracyProof diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponseAlt.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean From ee9573aa2be3a0bf6c2e12d1a0c03942c039a1b4 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 22 Jul 2025 19:04:28 -0700 Subject: [PATCH 072/216] fixedtypos --- .../Pure/Local/RandomizedResponse/AccuracyProof.lean | 1 + .../Pure/Local/RandomizedResponse/DPProof.lean | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index 4dedae3d..ef97dfcd 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -6,6 +6,7 @@ open SLang open PMF open RandomizedResponse +-- UNDER CONSTRUCTION -- def toSingletonLists {α : Type u} (l : List α) : List (List α) := l.map (fun x => [x]) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 5d616d29..3d9743d5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -4,6 +4,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties +-- UNDER CONSTRUCTION -- lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ((1/2 + num/den) / (1/2 - num/den)) := by -- rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] sorry @@ -17,7 +18,7 @@ ENNReal.ofReal ((1/2 + γ) / (1/2 - γ) )≤ ENNReal.ofReal (Real.exp (Real.log exact div_pos num_pos h_pos -/ -lemma bruh (num : Nat) (den : PNat): +lemma pos_helper (num : Nat) (den : PNat): 0 < ((1:ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := by apply add_pos_of_pos_of_nonneg norm_num @@ -85,7 +86,7 @@ lemma ennreal_pres_ineq (a b : Real): a ≤ b -> ENNReal.ofReal a ≤ ENNReal.of lemma q (num : Nat) (den : PNat) (h : 2 * num < den) : (1/(2 : ENNReal) + num/den) / (1/2 - num/den) = ENNReal.ofReal (((1 / 2 + num / den)) / (1 / 2 - num / den)) := by - sorry + sorry lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): (1/(2 : ENNReal) + num/den) / (1/2 - num/den) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by From b6c3da3852b10eeb876fd80c9127e12ce1f5ef32 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 24 Jul 2025 17:12:33 -0700 Subject: [PATCH 073/216] rappor --- .../Pure/Local/RAPPOR/Definitions.lean | 32 +++++++++++++++++ .../Local/RandomizedResponse/BasicLemmas.lean | 10 +++--- .../Local/RandomizedResponse/DPProof.lean | 2 +- .../Local/RandomizedResponse/Definitions.lean | 11 ++++-- .../RandomizedResponse/PMFProperties.lean | 35 +++++++++++++------ 5 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean new file mode 100644 index 00000000..24171cbf --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean @@ -0,0 +1,32 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties + +namespace RAPPOR + +open RandomizedResponse SLang + +def one_hot {T : Type} (n : Nat) (query : T -> Fin n) (v : T) : List Bool := List.ofFn (fun i => query v = i) + +def RAPPORSingleSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : SLang (List Bool) := do + RRSamplePushForward num den h (one_hot n query v) + +def RAPPORSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : SLang (List (List Bool)) := do + v.mapM (fun x => RAPPORSingleSample n query num den h x) + +lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : + HasSum (RAPPORSingleSample n query num den h v) 1 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_PMF_helper + +lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : + HasSum (RAPPORSample n query num den h v) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold RAPPORSample + sorry + /-- apply Norm_func_norm_on_list + /-- intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RAPPORSingleSample_PMF_helper --/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index 2815fb2c..40b182b2 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -17,13 +17,13 @@ lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] + rw[RRSingleSample, RRSinglePushForward] aesop sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] + rw[RRSingleSample, RRSinglePushForward] simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] @@ -31,7 +31,7 @@ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample] + rw[RRSingleSample, RRSinglePushForward] simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] @@ -39,7 +39,7 @@ lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample] + rw[RRSingleSample, RRSinglePushForward] simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] @@ -49,7 +49,7 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ 0 := by - simp [RRSingleSample] + simp [RRSingleSample, RRSinglePushForward] cases hb : b == query l with | true => simp at hb subst hb diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 3d9743d5..557666e5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -75,7 +75,7 @@ lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): rw [@div_pos_iff] apply Or.inl apply And.intro - {apply bruh} + {apply pos_helper} {apply bruh1 num den h} /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index f1433331..f8288c7d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -10,9 +10,12 @@ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * simp_all only [tsub_le_iff_right] linarith -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do + def RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) - return Bool.xor (query l) r + return Bool.xor (l) r + +def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do + RRSinglePushForward num den h (query l) def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (query l) r) /- Y is a random variable which outputs the function measuring whether or not a given person @@ -27,4 +30,8 @@ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num let r ← MultiBernoulliSample seed_list return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ +def RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) : SLang (List Bool) := do + /- For use in RAPPOR -/ + l.mapM (fun x => RRSinglePushForward num den h x) + end RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index 643fd176..9d97d996 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -10,27 +10,31 @@ open RandomizedResponse #check RandomizedResponse.RRSingleSample #check SLang.BernoulliSample_normalizes -lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : - HasSum (RRSingleSample query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - rw [@tsum_bool] - rw[RRSingleSample] - cases query l - { +lemma RRSinglePushForward_PMF (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : + HasSum (RRSinglePushForward num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw [@tsum_bool] + rw[RRSinglePushForward] + cases l + { simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] rw[tsum_bool] - } - { - simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, + } + { simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] rw[tsum_bool] rw [@AddCommMonoidWithOne.add_comm] - } + } + +lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : + HasSum (RRSingleSample query num den h l) 1 := by + rw [RRSingleSample] + apply RRSinglePushForward_PMF lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : HasSum (RRSample query num den h l) 1 := by @@ -41,5 +45,14 @@ lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num rw [← Summable.hasSum_iff ENNReal.summable] apply RRSingleSample_PMF_helper +lemma RRSamplePushForward_PMF_helper [LawfulMonad SLang] (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) : + HasSum (RRSamplePushForward num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold RRSamplePushForward + apply Norm_func_norm_on_list + intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSinglePushForward_PMF + def RRSample_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ From 823bf227793ce6a176ae0e8d599b4f7b1cd57688 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 25 Jul 2025 13:54:38 -0700 Subject: [PATCH 074/216] rappordef --- .../Pure/Local/Normalization.lean | 34 +++++++++---------- .../Pure/Local/RAPPOR/Definitions.lean | 26 +++++++------- .../Pure/Local/RAPPOR/Properties.lean | 27 +++++++++++++++ 3 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index b9375244..8a264fee 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -6,15 +6,15 @@ import Mathlib.Data.Set.Basic import Mathlib.Data.Set.Basic import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite -/- In this file, we show that if a function f : α -> SLang Bool - normalizes, in the sense that ∑' (b : List Bool) f a b = 1 +/- In this file, we show that if a function f : α -> SLang β + normalizes, in the sense that ∑' (b : List β) f a b = 1 for any fixed a : α, then the function obtained by applying monadic map to f also normalizes. -/ -lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Bool)(tl : List α): -(∑' (a_1 : List Bool), if a = b :: a_1 then mapM f tl a_1 else 0) = +lemma simplifier1_gen (α β : Type) [DecidableEq β](f: α → SLang β) (a : List β) (b : β)(tl : List α): +(∑' (a_1 : List β), if a = b :: a_1 then mapM f tl a_1 else 0) = (if a.head? = b then mapM f tl a.tail else 0) := by cases a with | nil => simp @@ -39,8 +39,8 @@ lemma simplifier1_gen (α : Type)(f: α → SLang Bool) (a : List Bool) (b : Boo open Set -lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): -∑' (a : List Bool), f a = ∑' (a : List Bool), if a.head? = some b then f a.tail else 0 := by +lemma list_beta_tsum_only_tl (β : Type) [DecidableEq β] (b : β) (f : List β -> ENNReal): +∑' (a : List β), f a = ∑' (a : List β), if a.head? = some b then f a.tail else 0 := by apply Equiv.tsum_eq_tsum_of_support intro x case e => @@ -81,22 +81,20 @@ lemma list_bool_tsum_only_tl (b : Bool) (f : List Bool -> ENNReal): exact e₁.trans e₂ case he => simp -lemma simplifier2_gen (α : Type)(f: α → SLang Bool)(hd : α) (tl : List α) (b : Bool): -(∑' (a : List Bool), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = - ∑' (a : List Bool), f hd b * mapM f tl a := by +lemma simplifier2_gen (α β : Type) [DecidableEq β] (f: α → SLang β)(hd : α) (tl : List α) (b : β): +(∑' (a : List β), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = + ∑' (a : List β), f hd b * mapM f tl a := by simp_all only [mul_ite, mul_zero] apply symm - apply list_bool_tsum_only_tl b + apply list_beta_tsum_only_tl β b -lemma simplifier3_gen [LawfulMonad SLang] (α : Type)(f : α → SLang Bool)(hd : α)(tl : List α) (h : ∑' (b : Bool), f hd b = 1): ∑' (a : Bool), f hd a * mapM f tl b = mapM f tl b := by - rw [tsum_bool] - rw [ENNRealLemmas.ennreal_mul_assoc] - rw [←tsum_bool] - rw [h] - rw [@CanonicallyOrderedCommSemiring.one_mul] +lemma simplifier3_gen [LawfulMonad SLang] (α β : Type)(f : α → SLang β)(hd : α)(tl : List α) (b : List β) (h : ∑' (b : β), f hd b = 1): ∑' (a : β), f hd a * mapM f tl b = mapM f tl b := by + have : ∑' (a : β), f hd a * mapM f tl b = (∑' (a : β), f hd a) * mapM f tl b := by + rw [ENNReal.tsum_mul_right] + rw [this, h, one_mul] -lemma Norm_func_norm_on_list [LawfulMonad SLang] (α : Type)(f: α → SLang Bool) (al: List α): - (∀ a : α, ∑' (b : Bool), f a b = 1) → ∑' (b : List Bool), mapM f al b = 1 := by +lemma Norm_func_norm_on_list [LawfulMonad SLang] (α β : Type) [DecidableEq β] (f: α → SLang β) (al: List α): + (∀ a : α, ∑' (b : β), f a b = 1) → ∑' (b : List β), mapM f al b = 1 := by intro h induction al with | nil => diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean index 24171cbf..0ca70c8c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean @@ -8,25 +8,23 @@ namespace RAPPOR open RandomizedResponse SLang +/- Definition of One-Time Basic RAPPOR. -/ + +/- One-hot encoding of a vector-/ def one_hot {T : Type} (n : Nat) (query : T -> Fin n) (v : T) : List Bool := List.ofFn (fun i => query v = i) +/- One-Time Basic RAPPOR for a single user. + We follow the description in "LDP Protocols for Frequency Estimation" + by Wang et al. + The rational privacy parameter lambda = num/den relates to the parameter + f in the paper via the equation lambda = 1/2 (1 - f). + +-/ def RAPPORSingleSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : SLang (List Bool) := do RRSamplePushForward num den h (one_hot n query v) +/- One-Time Basic RAPPOR for a dataset of users. -/ def RAPPORSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : SLang (List (List Bool)) := do v.mapM (fun x => RAPPORSingleSample n query num den h x) -lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : - HasSum (RAPPORSingleSample n query num den h v) 1 := by - rw [RAPPORSingleSample] - apply RRSamplePushForward_PMF_helper - -lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : - HasSum (RAPPORSample n query num den h v) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold RAPPORSample - sorry - /-- apply Norm_func_norm_on_list - /-- intro a - rw [← Summable.hasSum_iff ENNReal.summable] - apply RAPPORSingleSample_PMF_helper --/ +end RAPPOR diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean new file mode 100644 index 00000000..307567fe --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -0,0 +1,27 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions + +namespace RAPPOR + +lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : + HasSum (RAPPORSingleSample n query num den h v) 1 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_PMF_helper + +lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : + HasSum (RAPPORSample n query num den h v) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold RAPPORSample + apply Norm_func_norm_on_list + intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RAPPORSingleSample_PMF_helper query num den h a + +def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := + ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ + +end RAPPOR From c78060c65fd3d21e821f769956eb36fde7a578fa Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 25 Jul 2025 14:20:52 -0700 Subject: [PATCH 075/216] equivDPdefs --- .../Pure/Local/LocalDP/DPNeighbour.lean | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean new file mode 100644 index 00000000..601f90b6 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean @@ -0,0 +1,17 @@ +import SampCert +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithGeneralNeighbour +import SampCert.DifferentialPrivacy.Neighbours + +namespace SLang + +open SLang + +/- Abstraction of SampCert definition of DP-/ +def DP_withARUNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := + DP_withGeneralNeighbour m (Neighbour) ε + +/- Proof that our definitions are equivalent -/ +theorem DP_withARUNeighbour_isDP (m : Mechanism T U) (ε : ℝ) : + DP_withARUNeighbour m ε ↔ DP m ε := by simp [DP_withARUNeighbour, DP_withGeneralNeighbour, DP] + +end SLang From cd7ae95c5a60e4146ac0082886d3ef6cf9d1a7dd Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 25 Jul 2025 19:40:30 -0400 Subject: [PATCH 076/216] arithmetic --- .../Local/RandomizedResponse/DPProof.lean | 2 +- .../RandomizedResponseMain.lean | 332 ++++++++++++++++-- 2 files changed, 311 insertions(+), 23 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 3d9743d5..557666e5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -75,7 +75,7 @@ lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): rw [@div_pos_iff] apply Or.inl apply And.intro - {apply bruh} + {apply pos_helper} {apply bruh1 num den h} /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index dcd2da40..27055141 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -82,6 +82,9 @@ induction l generalizing a with theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob +lemma ennreal_div_one (a: ENNReal) : a / 1 = a := by aesop + + lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b ≤ (den + 2 * num) / (den - 2 * num) := by @@ -115,14 +118,82 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de } | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] -- arithmetic now - aesop - sorry + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [mul_assoc] + rw [mul_comm] + rw [mul_comm] + rw [← mul_assoc] + rw [← mul_assoc] + conv => + enter [1, 1] + rw [mul_comm] + rw [← mul_assoc] + enter [1] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + simp_all only [ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, + false_or] + apply Aesop.BuiltinRules.not_intro + intro a_1 + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + simp_all only [ne_eq] + apply Aesop.BuiltinRules.not_intro + intro a_1 + norm_cast | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa] cases hqa' : query a' with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] - -- arithmetic now - sorry + | true => + rw [RRSingleSample_true_true _ _ _ _ _ hqa'] + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [mul_assoc] + rw [mul_comm] + rw [mul_comm] + rw [← mul_assoc] + rw [← mul_assoc] + conv => + enter [1, 1] + rw [mul_comm] + rw [← mul_assoc] + enter [1] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + rw [← @ENNReal.div_eq_inv_mul] + rw [← @ENNReal.div_eq_inv_mul] + apply ENNReal.div_le_div + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + simp + rw [Not] + intro a + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + simp + rw [Not] + intro a + norm_cast + + + | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] rw[ENNReal.div_self] { rw [@Decidable.le_iff_lt_or_eq] @@ -139,25 +210,59 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de apply Or.inl apply quot_gt_one_rev simp - have h1: 0 < (2 : ENNReal) * num + 2 * num := by sorry - have h2: den < den + (2 : ENNReal) * num + 2 * num := by sorry - aesop - sorry + have h1: 0 < (2 : ENNReal) * num + 2 * num := by + simp + rw [@Nat.pos_iff_ne_zero] + simp + exact hnum + have h2: den < den + (2 : ENNReal) * num + 2 * num := by + simp + rw [add_assoc] + apply ENNReal.lt_add_right + simp + norm_num + exact hnum + + simp_all only [pos_add_self_iff, CanonicallyOrderedCommSemiring.mul_pos, Nat.ofNat_pos, + Nat.cast_pos, true_and, NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt] + apply ENNReal.sub_lt_of_lt_add + rw [@Decidable.le_iff_eq_or_lt] + right + rw [← ENNReal.coe_two] + exact_mod_cast h + + simp_all only + } - {rw [@ENNReal.div_ne_zero] + {simp apply And.intro - simp + rw [Not] + intro b + rw [@Nat.lt_iff_le_and_not_ge] at h + rw [@tsub_eq_zero_iff_le] at b + rcases h with ⟨hl, hr⟩ + rw [← ENNReal.coe_two] at b + have hh : den.val ≤ 2 * num := by + exact_mod_cast b + linarith + + rw [Not] + intro a_1 norm_cast - simp [Nat.cast_mul] - intro hd - sorry /- For this sorry, we need the h hypothesis to be a strict inequality -/ - apply mult_ne_top - simp - simp + + + + + + /- For this sorry, we need the h hypothesis to be a strict inequality -/ + } { apply div_ne_top simp - aesop + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, + ENNReal.coe_eq_zero, Nat.cast_eq_zero, false_or] + apply Aesop.BuiltinRules.not_intro + intro a_1 apply pnat_zero_imp_false den a_1 } -- arithmetic now @@ -169,19 +274,202 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de cases hqa' : query a' with | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] -- arithmetic now - sorry + simp + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [← mul_assoc] + conv => + enter [1,1] + rw [mul_comm] + rw [← mul_assoc] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + rw [ENNReal.inv_mul_cancel] + rw [ENNReal.le_div_iff_mul_le] + rw [one_mul] + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + + right + simp_all only [ne_eq, add_eq_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, mul_eq_zero, + OfNat.ofNat_ne_zero, false_or, not_and] + intro a_1 + apply Aesop.BuiltinRules.not_intro + intro a_2 + subst a_2 + simp_all only [mul_zero, PNat.pos] + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + + left + norm_num + + simp + rw [Not] + intro b + rw [@Nat.lt_iff_le_and_not_ge] at h + rw [@tsub_eq_zero_iff_le] at b + rcases h with ⟨hl, hr⟩ + rw [← ENNReal.coe_two] at b + have hh : den.val ≤ 2 * num := by + exact_mod_cast b + linarith + + norm_num + + simp_all only [ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, + false_or] + apply Aesop.BuiltinRules.not_intro + intro a_1 + have hh: ¬(den: Nat) = 0 := by simp + contradiction + + simp + rw[Not] + intro b + norm_cast + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] -- arithmetic now - sorry + simp + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [mul_assoc] + rw [mul_comm] + rw [mul_comm] + rw [← mul_assoc] + rw [← mul_assoc] + conv => + enter [1, 1] + rw [mul_comm] + rw [← mul_assoc] + enter [1] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + rw [← @ENNReal.div_eq_inv_mul] + rw [← @ENNReal.div_eq_inv_mul] + apply ENNReal.div_le_div + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + simp + rw [Not] + intro a + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + simp + rw [Not] + intro a + norm_cast + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa] cases hqa' : query a' with | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] -- arithmetic now - sorry + simp + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [mul_assoc] + rw [mul_comm] + rw [mul_comm] + rw [← mul_assoc] + rw [← mul_assoc] + conv => + enter [1, 1] + rw [mul_comm] + rw [← mul_assoc] + enter [1] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + simp + rw [Not] + intro b + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + simp + rw [Not] + intro b + norm_cast + + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] -- arithmetic now - sorry + simp + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mult_inv_dist] + simp + rw [← mul_assoc] + conv => + enter [1,1] + rw [mul_comm] + rw [← mul_assoc] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + rw [ENNReal.inv_mul_cancel] + rw [ENNReal.le_div_iff_mul_le] + rw [one_mul] + simp_all only [tsub_le_iff_right] + rw [add_assoc] + have hh (a b : ENNReal) : a ≤ a + b := by simp + apply hh + + right + simp_all only [ne_eq, add_eq_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, mul_eq_zero, + OfNat.ofNat_ne_zero, false_or, not_and] + intro a_1 + apply Aesop.BuiltinRules.not_intro + intro a_2 + subst a_2 + simp_all only [mul_zero, PNat.pos] + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + + left + norm_num + + simp + intro b + rw [Not] + intro c + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + + simp + rw[Not] + intro b + norm_cast + + simp + intro b + have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den + contradiction + + simp + rw[Not] + intro b + norm_cast + + + lemma valid_index0 (l₁ : List T)(h1: l₁ = a++[n]++b) (i : Fin (l₁.length - 1)): (Fin.succAbove (a.length) i).val < l₁.length := by have hl : l₁.length - 1 + 1 = l₁.length := by From 30c213a9c089e7733c46f40825e3fd53abf1358b Mon Sep 17 00:00:00 2001 From: PCChess Date: Sat, 26 Jul 2025 14:10:50 -0700 Subject: [PATCH 077/216] finish final step --- .../Local/RandomizedResponse/DPProof.lean | 71 ++++++++++++++++--- .../Local/RandomizedResponse/DPProof2.lean | 40 +++++++++++ 2 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 3d9743d5..c9c054f5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -5,18 +5,21 @@ import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties -- UNDER CONSTRUCTION -- +--DO NOT USE. USE DPProof2.lean-- + +/- lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ((1/2 + num/den) / (1/2 - num/den)) := by - -- rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] + rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] sorry -/- + lemma final_bound {γ: ℝ} (h0: 0 ≤ γ) (h1: 1/2 > γ) : ENNReal.ofReal ((1/2 + γ) / (1/2 - γ) )≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + γ) / (1/2 - γ)))) := by rw [] have h_pos : 0 < (1 / 2 : ℝ) - γ := sub_pos.mpr h1 have num_pos : 0 < 1 / 2 + γ := by linarith exact div_pos num_pos h_pos --/ + lemma pos_helper (num : Nat) (den : PNat): 0 < ((1:ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := by @@ -26,10 +29,10 @@ lemma pos_helper (num : Nat) (den : PNat): exact Nat.cast_nonneg num exact NNReal.coe_nonneg (NNReal.ofPNat den) ---lemma num_div_lt_one_half (num : Nat) (den : PNat) (h : 2 * num < den) : - -- ↑num / ↑↑↑den < (2⁻¹ : ℝ) := by +lemma num_div_lt_one_half (num : Nat) (den : PNat) (h : 2 * num < den) : + ↑num / ↑↑↑den < (2⁻¹ : ℝ) := by ---lemma nnreal_coe_ineq (n : Nat) (d : PNat) (h : n < d.val) : n < NNReal.ofPNat d := by aesop +lemma nnreal_coe_ineq (n : Nat) (d : PNat) (h : n < d.val) : n < NNReal.ofPNat d := by aesop lemma t (num : ℕ) (den : PNat) (h : 2 * num < den) : 2 * ↑num < NNReal.ofPNat den := by simp @@ -98,7 +101,57 @@ lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): -- simp_rw [ENNReal.ofReal_sub, ENNReal.ofReal_div] sorry +-/ +/- +lemma cat (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by + have den_pos : 0 < (den : ℕ) := den.property + have den_real_pos : (0 : ℝ) < ↑(den : ℕ) := Nat.cast_pos.mpr den_pos + have two_num_nonneg : 0 ≤ (2 * num : ℝ) := by + refine mul_nonneg ?_ (Nat.cast_nonneg num) + norm_num + exact add_pos_of_pos_of_nonneg den_real_pos two_num_nonneg + +lemma dog (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by + rw [sub_pos] + norm_cast + +lemma pig (num : ℕ) (den : ℕ+) (h : 2 * num < den) : (0 : ℝ) < (↑↑den + 2 * num) / (↑↑den - 2 * ↑num):= by + rw [div_pos_iff] + apply Or.inl + apply And.intro + exact cat num den + exact dog num den h + + +lemma cow (num : Nat) (den : PNat) (h : 2 * num < den): +(0 : ℝ) ≤ (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) := by + apply le_of_lt + exact pig num den h + + +lemma step1 (num : Nat) (den : PNat) (h : 2 * num < den): +ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by + congr + rw [eq_comm] + apply Real.exp_log + apply div_pos + {exact cat num den} + {exact dog num den h} + + + +lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): + (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by + rw [ENNReal.ofReal_div_of_pos] + · congr + norm_cast + · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h + exact sub_pos.mpr foo + + lemma final_step_combined (num : Nat) (den: PNat) (h: 2 * num < den): - (den + (2 : ENNReal) * num) / (den - 2 * num) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - rw [final_transition num den h] - exact ENNReal_final_step num den h +(den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by + rw [← step1 num den h] + exact step2 num den h + +-\ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean new file mode 100644 index 00000000..0ad43a2c --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean @@ -0,0 +1,40 @@ +import Mathlib.Topology.Basic +import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Data.Real.Basic +import Mathlib.Data.Complex.Exponential +import Mathlib.Analysis.SpecialFunctions.Exp +import Mathlib.Analysis.SpecialFunctions.Log.Basic + +lemma numerator_pos (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by + have den_pos : 0 < (den : ℕ) := den.property + have den_real_pos : (0 : ℝ) < ↑(den : ℕ) := Nat.cast_pos.mpr den_pos + have two_num_nonneg : 0 ≤ (2 * num : ℝ) := by + refine mul_nonneg ?_ (Nat.cast_nonneg num) + norm_num + exact add_pos_of_pos_of_nonneg den_real_pos two_num_nonneg + +lemma denominator_pos (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by + rw [sub_pos] + norm_cast + +lemma step1 (num : Nat) (den : PNat) (h : 2 * num < den): +ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by + congr + rw [eq_comm] + apply Real.exp_log + apply div_pos + {exact numerator_pos num den} + {exact denominator_pos num den h} + +lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): + (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by + rw [ENNReal.ofReal_div_of_pos] + · congr + norm_cast + · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h + exact sub_pos.mpr foo + +lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : +(den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by + rw [← step1 num den h] + exact step2 num den h From 4e21042c52ea5a0d6b80742ce6254f70877734cc Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 09:49:49 -0700 Subject: [PATCH 078/216] monday --- .../Pure/Local/LocalDP/DPNeighbour.lean | 5 ++++- .../Pure/Local/Normalization.lean | 15 ++++++++++++++- .../Pure/Local/RAPPOR/Definitions.lean | 2 +- .../Pure/Local/RAPPOR/Properties.lean | 7 +++++++ .../Local/RandomizedResponse/Definitions.lean | 6 +----- .../Local/RandomizedResponse/PMFProperties.lean | 1 + 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean index 601f90b6..7fa969f4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean @@ -6,7 +6,9 @@ namespace SLang open SLang -/- Abstraction of SampCert definition of DP-/ +/- Abstraction of SampCert definition of DP. + ARU stands for Add-Remove-Update Neighbour +-/ def DP_withARUNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_withGeneralNeighbour m (Neighbour) ε @@ -15,3 +17,4 @@ theorem DP_withARUNeighbour_isDP (m : Mechanism T U) (ε : ℝ) : DP_withARUNeighbour m ε ↔ DP m ε := by simp [DP_withARUNeighbour, DP_withGeneralNeighbour, DP] end SLang + diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index 8a264fee..adfc6ff9 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -10,9 +10,13 @@ import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite normalizes, in the sense that ∑' (b : List β) f a b = 1 for any fixed a : α, then the function obtained by applying monadic map - to f also normalizes. + to f and some list also normalizes. + This is valuable for local algorithms, where + a randomizer is first defined for a single user and then + applied to a dataset of users. -/ +/- Helper lemma to simplify a if-then-else statement in a sum-/ lemma simplifier1_gen (α β : Type) [DecidableEq β](f: α → SLang β) (a : List β) (b : β)(tl : List α): (∑' (a_1 : List β), if a = b :: a_1 then mapM f tl a_1 else 0) = (if a.head? = b then mapM f tl a.tail else 0) := by @@ -39,6 +43,9 @@ lemma simplifier1_gen (α β : Type) [DecidableEq β](f: α → SLang β) (a : L open Set +/- This lemma shows that summing a function over all possible lists is the same as summing a function + over all tails of lists with a specified head. +-/ lemma list_beta_tsum_only_tl (β : Type) [DecidableEq β] (b : β) (f : List β -> ENNReal): ∑' (a : List β), f a = ∑' (a : List β), if a.head? = some b then f a.tail else 0 := by apply Equiv.tsum_eq_tsum_of_support @@ -81,6 +88,7 @@ lemma list_beta_tsum_only_tl (β : Type) [DecidableEq β] (b : β) (f : List β exact e₁.trans e₂ case he => simp +/- Another simplifying lemma for if-then-else statements within a sum.-/ lemma simplifier2_gen (α β : Type) [DecidableEq β] (f: α → SLang β)(hd : α) (tl : List α) (b : β): (∑' (a : List β), f hd b * if a.head? = some b then mapM f tl a.tail else 0) = ∑' (a : List β), f hd b * mapM f tl a := by @@ -88,11 +96,16 @@ lemma simplifier2_gen (α β : Type) [DecidableEq β] (f: α → SLang β)(hd : apply symm apply list_beta_tsum_only_tl β b +/- Simplifying lemma to pull a constant out of a sum. -/ lemma simplifier3_gen [LawfulMonad SLang] (α β : Type)(f : α → SLang β)(hd : α)(tl : List α) (b : List β) (h : ∑' (b : β), f hd b = 1): ∑' (a : β), f hd a * mapM f tl b = mapM f tl b := by have : ∑' (a : β), f hd a * mapM f tl b = (∑' (a : β), f hd a) * mapM f tl b := by rw [ENNReal.tsum_mul_right] rw [this, h, one_mul] +/- The key normalization lemma, which shows that if a function f + normalizes, then the function obtained applying monadic map to f and some list al + also normalizes. +-/ lemma Norm_func_norm_on_list [LawfulMonad SLang] (α β : Type) [DecidableEq β] (f: α → SLang β) (al: List α): (∀ a : α, ∑' (b : β), f a b = 1) → ∑' (b : List β), mapM f al b = 1 := by intro h diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean index 0ca70c8c..81fa7f64 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean @@ -18,8 +18,8 @@ def one_hot {T : Type} (n : Nat) (query : T -> Fin n) (v : T) : List Bool := Lis by Wang et al. The rational privacy parameter lambda = num/den relates to the parameter f in the paper via the equation lambda = 1/2 (1 - f). - -/ + def RAPPORSingleSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : SLang (List Bool) := do RRSamplePushForward num den h (one_hot n query v) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 307567fe..a8aaefd3 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -7,11 +7,17 @@ import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions namespace RAPPOR +/- In this file, we show normalization for the One-Time Basic RAPPOR Algorithm. +-/ + +/- Normalization of the single-user RAPPOR, which essentially relies on the normalization property + of randomized response. -/ lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : HasSum (RAPPORSingleSample n query num den h v) 1 := by rw [RAPPORSingleSample] apply RRSamplePushForward_PMF_helper +/- Extension to the multi-user RAPPOR, which follows from our normalization lemma. -/ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : HasSum (RAPPORSample n query num den h v) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -21,6 +27,7 @@ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) rw [← Summable.hasSum_iff ENNReal.summable] apply RAPPORSingleSample_PMF_helper query num den h a +/- Instantiation of RAPPOR as a PMF-/ def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index f8288c7d..102ba9b2 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -23,13 +23,9 @@ def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (q from which we sample r.-/ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ +/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) - /- def RRSample2 {T : Type} (query : T -> Bool) (seed_list : List SeedType) (l : List T): SLang (List Bool) := do - let r ← MultiBernoulliSample seed_list - return List.zipWith (fun u s => Bool.xor (query u) s) l r -/ - def RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) : SLang (List Bool) := do /- For use in RAPPOR -/ l.mapM (fun x => RRSinglePushForward num den h x) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean index 9d97d996..23a1f2ac 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean @@ -4,6 +4,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties + open SLang open RandomizedResponse From cd4da210df588d18a4b87ead00a66f3b4dc1b9c4 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 12:04:49 -0700 Subject: [PATCH 079/216] difflengthsproof --- .../Pure/Local/RAPPOR/Properties.lean | 7 ++++ .../Local/RandomizedResponse/BasicLemmas.lean | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index a8aaefd3..bbb17c28 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -3,9 +3,11 @@ import SampCert import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions namespace RAPPOR +open RandomizedResponse /- In this file, we show normalization for the One-Time Basic RAPPOR Algorithm. -/ @@ -31,4 +33,9 @@ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ +lemma RRSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): + RAPPORSingleSample n query num den h l₁ l₂= 0 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen + end RAPPOR diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index 40b182b2..2759b972 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -111,6 +111,26 @@ lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNa aesop exact hden +lemma RRSamplePushForward_diff_lengths (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List Bool) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSamplePushForward num den h l₁ l₂ = 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSamplePushForward, -mapM] + aesop + | cons hd tl ih => + simp [RRSamplePushForward, -mapM] + simp [RRSamplePushForward, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSample query num den h l₁ l₂= 0 := by induction l₁ generalizing l₂ with @@ -133,3 +153,15 @@ lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNa lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen + +lemma mwi1 (n : Nat) (f : Fin n -> Real): ∏ (i : Fin n), f i = ∏ (i : Fin (n + 1 - 1)), f i := by congr + +lemma mwi2 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by sorry)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by sorry)) := by congr + +lemma valid_index1 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin n): i.val < l.length := by + sorry + +lemma valid_index2 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin (n + 1 - 1)): i.val < l.length := by + sorry + +lemma mwi3 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by apply valid_index1; apply h)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by apply valid_index2; apply h)) := by congr From 3ecd46457cf21bca7f35fb6314da1ad4e43daac1 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 12:30:19 -0700 Subject: [PATCH 080/216] meow --- .../RandomizedResponseMain.lean | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index dcd2da40..affe8d1d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -15,8 +15,8 @@ open ENNRealLemmas open RandomizedResponse namespace SLang -lemma simplifier_1 (f : T -> SLang Bool): -(∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by +lemma simplifier_1 {β : Type} [DecidableEq β] (f : T -> SLang β) (c : List β): +(∑' (a : List β), if c = a then mapM f tl a else 0) = mapM f tl c := by rw[tsum_eq_single c] aesop intro b h @@ -79,6 +79,52 @@ induction l generalizing a with simp at k exact k +lemma mapM_dist_cons_general {β : Type} (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): +mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by + rw[List.mapM_cons] + simp[-mapM] + conv => + enter [1, 1, a, 2, 1, a_1] + /- cases b with + | true => + simp[-mapM] + conv => + enter [1, 2] + rw [simplifier_1] + | false => + simp [-mapM] + conv => + enter [1, 2] + rw [simplifier_1] -/ + sorry + +lemma prod_of_ind_prob_general (β : Type) (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : + mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by + induction l generalizing a with + | nil => + simp[-mapM] + rw[List.length_nil] at k + symm at k + apply List.eq_nil_of_length_eq_zero at k + rw[k] + | cons hd tl ih => + cases a with + | nil => + simp at k + | cons b c => + rw [mapM_dist_cons_general] + rw [ih c] + rw [@tprod_fintype] + rw [@tprod_fintype] + simp + rw[Fin.prod_univ_succ] + simp at k + apply Eq.refl + aesop + +end SLang + + theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob From 54ab220a2868a737689d3ef44638cf9f48bc86c2 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Mon, 28 Jul 2025 16:54:23 -0400 Subject: [PATCH 081/216] removedunsolvedlemmas --- .../Pure/Local/ENNRealLemmasSuite.lean | 31 +-- .../RandomizedResponseMain.lean | 183 +++++++++++++----- 2 files changed, 135 insertions(+), 79 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 82746fc9..c95b7fc0 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -17,30 +17,7 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): 2 * (@Nat.cast ENNReal NonAssocSemiring.toNatCast num) < @Nat.cast ENNReal CanonicallyOrderedCommSemiring.toNatCast ↑den := by norm_cast -lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - sorry -lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry - - -lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [← @ENNReal.inv_ne_zero] at h2 - sorry - -lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [@ENNReal.div_eq_inv_mul] - sorry - -lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by - intro h1 - sorry lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by intro h1 @@ -119,14 +96,7 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hbT -lemma quot_lt_one (a b : ENNReal): a/b < 1 -> a < b := by sorry - -lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry -lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by - intro h - apply div_ineq_flip - exact quot_gt_one_rev a b h lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by @@ -144,4 +114,5 @@ lemma tsum_ite_mult (f : T -> ENNReal) (P : T -> Bool): (∑' (x : T), f x * if (P x) then 1 else 0) = ∑' (x : T), if (P x) then f x else 0 := by simp_all only [mul_ite, mul_one, mul_zero] + end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 27055141..c04ea438 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -96,25 +96,67 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de cases hqa' : query a' with | true => rw [RRSingleSample_true_true _ _ _ _ _ hqa'] rw[ENNReal.div_self] - {sorry} + {rw [@Decidable.le_iff_lt_or_eq] + cases hnum : num == 0 with + | true => simp at hnum + apply Or.inr + subst hnum + simp + rw [ENNReal.div_self] + norm_num + apply pnat_zero_imp_false + simp + | false => simp at hnum + apply Or.inl + apply quot_gt_one_rev + simp + have h1: 0 < (2 : ENNReal) * num + 2 * num := by + simp + rw [@Nat.pos_iff_ne_zero] + simp + exact hnum + have h2: den < den + (2 : ENNReal) * num + 2 * num := by + simp + rw [add_assoc] + apply ENNReal.lt_add_right + simp + norm_num + exact hnum + + simp_all only [pos_add_self_iff, CanonicallyOrderedCommSemiring.mul_pos, Nat.ofNat_pos, + Nat.cast_pos, true_and, NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt] + apply ENNReal.sub_lt_of_lt_add + rw [@Decidable.le_iff_eq_or_lt] + right + rw [← ENNReal.coe_two] + exact_mod_cast h + + simp_all only} {rw [@ENNReal.div_ne_zero] apply And.intro simp intro hd apply False.elim apply pnat_zero_imp_false den hd - apply mult_ne_top - simp simp + rw[Not] + intro b + norm_cast } - { apply div_ne_top - simp - apply mult_ne_top - simp - simp - simp + { simp + rw [@ENNReal.div_eq_top] rw[Not] - apply pnat_zero_imp_false den + intro b + rcases b with ⟨_, br⟩ + have hh : ¬ den.val = 0 := by simp + simp at br + rw [Not] at hh + apply hh at br + exact br + rename_i h_1 + simp_all only [ENNReal.add_eq_top, ENNReal.coe_ne_top, false_or, ne_eq] + obtain ⟨left⟩ := h_1 + norm_cast } | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] -- arithmetic now @@ -123,7 +165,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [mul_assoc] rw [mul_comm] @@ -139,14 +181,19 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [one_mul] simp_all only [ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, false_or] - apply Aesop.BuiltinRules.not_intro - intro a_1 - have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den - contradiction + apply pnat_zero_imp_false simp_all only [ne_eq] apply Aesop.BuiltinRules.not_intro intro a_1 norm_cast + left + simp + rw[Not] + intro b + norm_cast + left + simp + apply pnat_zero_imp_false | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa] cases hqa' : query a' with @@ -157,7 +204,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [mul_assoc] rw [mul_comm] @@ -183,16 +230,19 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de have hh (a b : ENNReal) : a ≤ a + b := by simp apply hh simp - rw [Not] - intro a - have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den - contradiction + apply pnat_zero_imp_false simp rw [Not] intro a norm_cast - - + left + simp + rw[Not] + intro b + norm_cast + left + simp + apply pnat_zero_imp_false | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] rw[ENNReal.div_self] @@ -257,13 +307,20 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de /- For this sorry, we need the h hypothesis to be a strict inequality -/ } - { apply div_ne_top - simp - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, - ENNReal.coe_eq_zero, Nat.cast_eq_zero, false_or] - apply Aesop.BuiltinRules.not_intro - intro a_1 - apply pnat_zero_imp_false den a_1 + { simp + rw [@ENNReal.div_eq_top] + rw[Not] + intro b + rcases b with ⟨_, br⟩ + have hh : ¬ den.val = 0 := by simp + simp at br + rw [Not] at hh + apply hh at br + exact br + rename_i h_1 + simp_all only [ENNReal.add_eq_top, ENNReal.coe_ne_top, false_or, ne_eq] + obtain ⟨left⟩ := h_1 + norm_cast } -- arithmetic now @@ -277,7 +334,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de simp rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [← mul_assoc] conv => @@ -320,19 +377,25 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de linarith norm_num + simp + apply pnat_zero_imp_false - simp_all only [ne_eq, mul_eq_zero, OfNat.ofNat_ne_zero, ENNReal.coe_eq_zero, Nat.cast_eq_zero, - false_or] - apply Aesop.BuiltinRules.not_intro - intro a_1 - have hh: ¬(den: Nat) = 0 := by simp - contradiction + simp + rw[Not] + intro b + norm_cast + left simp rw[Not] intro b norm_cast + left + simp + apply pnat_zero_imp_false + + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] -- arithmetic now simp @@ -340,7 +403,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [mul_assoc] rw [mul_comm] @@ -366,15 +429,22 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de have hh (a b : ENNReal) : a ≤ a + b := by simp apply hh simp - rw [Not] - intro a - have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den - contradiction + apply pnat_zero_imp_false simp rw [Not] intro a norm_cast + left + simp + rw[Not] + intro b + norm_cast + + left + simp + apply pnat_zero_imp_false + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa] cases hqa' : query a' with @@ -385,7 +455,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [mul_assoc] rw [mul_comm] @@ -399,23 +469,31 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de enter [1] rw [ENNReal.inv_mul_cancel] rw [one_mul] + simp + apply pnat_zero_imp_false + simp rw [Not] intro b - have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den - contradiction + norm_cast + + left simp - rw [Not] + rw[Not] intro b norm_cast + left + simp + apply pnat_zero_imp_false + | false => rw [RRSingleSample_false_false _ _ _ _ _ hqa'] -- arithmetic now simp rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] - rw [mult_inv_dist] + rw [ENNReal.mul_inv] simp rw [← mul_assoc] conv => @@ -449,7 +527,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de simp intro b rw [Not] - intro c + intro have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den contradiction @@ -459,15 +537,22 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de norm_cast simp + apply pnat_zero_imp_false + + simp + rw[Not] intro b - have h' : ¬ (den : Nat) = 0:= PNat.ne_zero den - contradiction + norm_cast + left simp rw[Not] intro b norm_cast + left + simp + apply pnat_zero_imp_false From 431e2847de42e6707d2e15f21ba8d5377859ded0 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 14:03:05 -0700 Subject: [PATCH 082/216] generalprodthm --- .../RandomizedResponseMain.lean | 90 ++++--------------- 1 file changed, 16 insertions(+), 74 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index affe8d1d..fded316e 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -15,32 +15,24 @@ open ENNRealLemmas open RandomizedResponse namespace SLang -lemma simplifier_1 {β : Type} [DecidableEq β] (f : T -> SLang β) (c : List β): -(∑' (a : List β), if c = a then mapM f tl a else 0) = mapM f tl c := by + +lemma simplifier_3 {β : Type} [DecidableEq β] (f : T -> SLang β) (c : List β) (a b : β): +(∑' (a_1 : List β), if b = a ∧ c = a_1 then mapM f tl a_1 else 0) = if b = a then mapM f tl c else 0 := by rw[tsum_eq_single c] aesop -intro b h -simp_all only [ne_eq, mapM, ite_eq_right_iff] -intro a -subst a -simp_all only [not_true_eq_false] +aesop -lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): +lemma mapM_dist_cons {β : Type} [DecidableEq β] (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by -rw[List.mapM_cons] -simp[-mapM] -rw [@tsum_bool] -cases b with -| true => -simp[-mapM] -conv => - enter [1, 2] - rw [simplifier_1] -| false => -simp [-mapM] -conv => - enter [1, 2] - rw [simplifier_1] + rw[List.mapM_cons] + simp[-mapM] + conv => + enter [1, 1, a, 2] + simp[-mapM] + rw [simplifier_3] + rw [tsum_eq_single b] + aesop + aesop lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): RRSample query num den h (hd::tl) (b::c) = RRSingleSample query num den h hd b * RRSample query num den h tl c := by @@ -48,57 +40,7 @@ unfold RRSample set f := fun x => RRSingleSample query num den h x rw[mapM_dist_cons f b c hd tl] - -lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): -RRSample query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by -induction l generalizing a with -| nil => - simp - rw[List.length_nil] at k - symm at k - apply List.eq_nil_of_length_eq_zero at k - rw[k] - unfold RRSample - rw [List.mapM_nil] - simp [pure] - -| cons hd tl ih => - simp - simp at ih - cases a with - | nil => - simp at k - | cons b c => - rw[RRSample_rec query num den h] - rw[ih c] - rw [@tprod_fintype] - rw [@tprod_fintype] - - rw[Fin.prod_univ_succ] - simp - simp at k - exact k - -lemma mapM_dist_cons_general {β : Type} (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): -mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by - rw[List.mapM_cons] - simp[-mapM] - conv => - enter [1, 1, a, 2, 1, a_1] - /- cases b with - | true => - simp[-mapM] - conv => - enter [1, 2] - rw [simplifier_1] - | false => - simp [-mapM] - conv => - enter [1, 2] - rw [simplifier_1] -/ - sorry - -lemma prod_of_ind_prob_general (β : Type) (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : +lemma prod_of_ind_prob (β : Type) [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by induction l generalizing a with | nil => @@ -112,7 +54,7 @@ lemma prod_of_ind_prob_general (β : Type) (f : T -> SLang β) (a : List β) (l | nil => simp at k | cons b c => - rw [mapM_dist_cons_general] + rw [mapM_dist_cons] rw [ih c] rw [@tprod_fintype] rw [@tprod_fintype] From b1111ea24efafa1712b0245ad31a1003ebeef56d Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 14:20:00 -0700 Subject: [PATCH 083/216] ready to pull --- .../Pure/Local/RAPPOR/Properties.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index bbb17c28..82de5530 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -38,4 +38,20 @@ lemma RRSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T - rw [RAPPORSingleSample] apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen +lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): + (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by + simp_all only [RAPPORSingleSample] + set ohv := one_hot n query v + set ohu := one_hot n query u + cases hlen: ohv.length == b.length with + | true => sorry + | false => + simp at hlen + have h1: RRSamplePushForward num den h ohv b = 0 := RRSample_diff_lengths n query num den h v b hlen + rw [h1] + rw [@ENNReal.zero_div] + simp + + + end RAPPOR From 520d22a65f7d8aac077e0dd5cb56a457825268c4 Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 28 Jul 2025 14:21:02 -0700 Subject: [PATCH 084/216] Update DPProof.lean --- .../Local/RandomizedResponse/DPProof.lean | 137 ++---------------- 1 file changed, 10 insertions(+), 127 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 590dcce3..0ad43a2c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -1,109 +1,11 @@ +import Mathlib.Topology.Basic import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.Samplers.Bernoulli.Properties +import Mathlib.Data.Real.Basic +import Mathlib.Data.Complex.Exponential +import Mathlib.Analysis.SpecialFunctions.Exp +import Mathlib.Analysis.SpecialFunctions.Log.Basic --- UNDER CONSTRUCTION -- ---DO NOT USE. USE DPProof2.lean-- - -/- -lemma final_transition (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2 : ENNReal) * num) / (den - 2 * num) = ((1/2 + num/den) / (1/2 - num/den)) := by - rw [← mul_div_mul_left 2 (1/2 + num/den) (1/2 - num/den)] - sorry - - -lemma final_bound {γ: ℝ} (h0: 0 ≤ γ) (h1: 1/2 > γ) : -ENNReal.ofReal ((1/2 + γ) / (1/2 - γ) )≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + γ) / (1/2 - γ)))) := by - rw [] - have h_pos : 0 < (1 / 2 : ℝ) - γ := sub_pos.mpr h1 - have num_pos : 0 < 1 / 2 + γ := by linarith - exact div_pos num_pos h_pos - - -lemma pos_helper (num : Nat) (den : PNat): - 0 < ((1:ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := by - apply add_pos_of_pos_of_nonneg - norm_num - apply div_nonneg - exact Nat.cast_nonneg num - exact NNReal.coe_nonneg (NNReal.ofPNat den) - -lemma num_div_lt_one_half (num : Nat) (den : PNat) (h : 2 * num < den) : - ↑num / ↑↑↑den < (2⁻¹ : ℝ) := by - -lemma nnreal_coe_ineq (n : Nat) (d : PNat) (h : n < d.val) : n < NNReal.ofPNat d := by aesop - -lemma t (num : ℕ) (den : PNat) (h : 2 * num < den) : 2 * ↑num < NNReal.ofPNat den := by - simp - norm_cast - -lemma tr (num : ℕ) (den : ℕ+) (h : 2 * num < den) : - (1 / 2 : NNReal) * (2 * ↑num) < (1 / 2 : NNReal) * NNReal.ofPNat den := by - rw [← mul_assoc] - rw [← inv_eq_one_div] - rw [inv_mul_cancel] - rw [one_mul] - rw [← mul_lt_mul_iff_of_pos_left two_pos] - rw [← mul_assoc] - rw [mul_inv_cancel] - rw [one_mul] - {exact t num den h} - {norm_num} - {norm_num} - -lemma temp (num : Nat) (den : PNat) (h : 2 * num < den) : (num : ℝ) / den < (1 : ℝ) / 2:= by - rw [div_lt_iff] - { - have h2: 1/2 * (2 * ↑num) < 1/2 * NNReal.ofPNat den := tr num den h - have h3: 1/2 * (2 * (num : NNReal)) = (1/2 * 2) * ↑num := by aesop - have h4: (1/2 * 2) * (num : NNReal) = num := by aesop - rw [h3] at h2 - rw [h4] at h2 - exact h2 - } - {simp [den.pos] - exact den.2} - -lemma bruh1 (num : Nat) (den : PNat) (h: 2 * num < den) : - 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := by - have h1 : 1/2 > (num : ℝ) / den := temp num den h - have h_pos : 0 < (1 / 2 : ℝ) - (num / den) := sub_pos.mpr h1 - exact h_pos - - -lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): - (1/2 + num/den) / (1/2 - num/den) ≤ Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den))) := by - rw [Real.exp_log] - rw [@div_pos_iff] - apply Or.inl - apply And.intro - {apply pos_helper} - {apply bruh1 num den h} - - /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l - have h2 : 0 < ((1:ℝ) / 2 - ↑num / ↑(NNReal.ofPNat den)) := bruh1 query num den h l - apply div_pos h1 h2 -/ - -lemma ennreal_pres_ineq (a b : Real): a ≤ b -> ENNReal.ofReal a ≤ ENNReal.ofReal b := by sorry - -lemma q (num : Nat) (den : PNat) (h : 2 * num < den) : - (1/(2 : ENNReal) + num/den) / (1/2 - num/den) = ENNReal.ofReal (((1 / 2 + num / den)) / (1 / 2 - num / den)) := by - sorry - -lemma ENNReal_final_step (num : Nat) (den : PNat) (h: 2 * num < den): - (1/(2 : ENNReal) + num/den) / (1/2 - num/den) ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - have h1 : 0 < (1/2 : ℝ) - (num : ℝ)/den := by - exact bruh1 num den h - have h₂ : 0 < (1/2 : ℝ) + (num : ℝ)/den := by - sorry - have h3: ((1/2) + num/den) / (1/2 - num/den) = ENNReal.ofReal ((1/2) + (num/den : ℝ)) / (1/2 - num/den) := by sorry - -- simp_rw [ENNReal.ofReal_sub, ENNReal.ofReal_div] - sorry - --/ -/- -lemma cat (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by +lemma numerator_pos (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by have den_pos : 0 < (den : ℕ) := den.property have den_real_pos : (0 : ℝ) < ↑(den : ℕ) := Nat.cast_pos.mpr den_pos have two_num_nonneg : 0 ≤ (2 * num : ℝ) := by @@ -111,34 +13,18 @@ lemma cat (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by norm_num exact add_pos_of_pos_of_nonneg den_real_pos two_num_nonneg -lemma dog (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by +lemma denominator_pos (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by rw [sub_pos] norm_cast -lemma pig (num : ℕ) (den : ℕ+) (h : 2 * num < den) : (0 : ℝ) < (↑↑den + 2 * num) / (↑↑den - 2 * ↑num):= by - rw [div_pos_iff] - apply Or.inl - apply And.intro - exact cat num den - exact dog num den h - - -lemma cow (num : Nat) (den : PNat) (h : 2 * num < den): -(0 : ℝ) ≤ (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) := by - apply le_of_lt - exact pig num den h - - lemma step1 (num : Nat) (den : PNat) (h : 2 * num < den): ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by congr rw [eq_comm] apply Real.exp_log apply div_pos - {exact cat num den} - {exact dog num den h} - - + {exact numerator_pos num den} + {exact denominator_pos num den h} lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by @@ -148,10 +34,7 @@ lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h exact sub_pos.mpr foo - -lemma final_step_combined (num : Nat) (den: PNat) (h: 2 * num < den): +lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by rw [← step1 num den h] exact step2 num den h - --\ From 5165f399726d08df4aa658be8988ae7e304f2b63 Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 28 Jul 2025 14:28:39 -0700 Subject: [PATCH 085/216] Update RandomizedResponseMain.lean --- .../Pure/Local/RandomizedResponse/RandomizedResponseMain.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index c04ea438..3240ae12 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -778,7 +778,7 @@ open Finset open scoped BigOperators theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((1/2 + num / den) / (1/2 - num / den))) := by +DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((den + 2 * num) / (den - 2 * num))) := by apply singleton_to_event_update intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with From 94961e3d7ee778d90fb63a48bb540deb3a970374 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 14:34:05 -0700 Subject: [PATCH 086/216] nan --- .../DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean | 4 ---- .../Pure/Local/RandomizedResponse/BasicLemmas.lean | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index c95b7fc0..81bf9065 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -17,8 +17,6 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): 2 * (@Nat.cast ENNReal NonAssocSemiring.toNatCast num) < @Nat.cast ENNReal CanonicallyOrderedCommSemiring.toNatCast ↑den := by norm_cast - - lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by intro h1 apply ENNReal.div_lt_of_lt_mul @@ -96,8 +94,6 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hbT - - lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by rw [ENNReal.tsum_eq_add_tsum_ite []] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index 2759b972..e6148d30 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -154,7 +154,7 @@ lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNa lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen -lemma mwi1 (n : Nat) (f : Fin n -> Real): ∏ (i : Fin n), f i = ∏ (i : Fin (n + 1 - 1)), f i := by congr +/- lemma mwi1 (n : Nat) (f : Fin n -> Real): ∏ (i : Fin n), f i = ∏ (i : Fin (n + 1 - 1)), f i := by congr lemma mwi2 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by sorry)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by sorry)) := by congr @@ -165,3 +165,4 @@ lemma valid_index2 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin (n + 1 sorry lemma mwi3 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by apply valid_index1; apply h)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by apply valid_index2; apply h)) := by congr +-/ From 0f300b429268941c90e079e39a6dfb2ccbbf8569 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Mon, 28 Jul 2025 17:34:25 -0400 Subject: [PATCH 087/216] re-added lemmas --- .../Pure/Local/ENNRealLemmasSuite.lean | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index c95b7fc0..920f9fc4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -17,7 +17,30 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): 2 * (@Nat.cast ENNReal NonAssocSemiring.toNatCast num) < @Nat.cast ENNReal CanonicallyOrderedCommSemiring.toNatCast ↑den := by norm_cast +lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + rw [@inv_eq_one_div] + sorry + lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry + + +lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [← @ENNReal.inv_ne_zero] at h2 + sorry + +lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by + rw [← @ENNReal.inv_ne_zero] + rw [← @ENNReal.inv_ne_zero] at h1 + rw [@ENNReal.div_eq_inv_mul] + sorry + +lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by + intro h1 + sorry lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by intro h1 @@ -96,7 +119,14 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hbT +lemma quot_lt_one (a b : ENNReal): a/b < 1 -> a < b := by sorry + +lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry +lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by + intro h + apply div_ineq_flip + exact quot_gt_one_rev a b h lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by From 3afda8b53dcafce1edf10b4a5768da32eae0e609 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 28 Jul 2025 14:54:01 -0700 Subject: [PATCH 088/216] slightlycleanedwithrappor --- .../Pure/Local/ENNRealLemmasSuite.lean | 6 +++ .../Local/RandomizedResponse/DPProof2.lean | 40 ------------------- .../RandomizedResponseMain.lean | 15 ++----- 3 files changed, 9 insertions(+), 52 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index c6efdb03..1eb02322 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -119,6 +119,12 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hb apply hbT +lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry + +lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by + intro h + apply div_ineq_flip + exact quot_gt_one_rev a b h lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean deleted file mode 100644 index 0ad43a2c..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof2.lean +++ /dev/null @@ -1,40 +0,0 @@ -import Mathlib.Topology.Basic -import Mathlib.Probability.ProbabilityMassFunction.Basic -import Mathlib.Data.Real.Basic -import Mathlib.Data.Complex.Exponential -import Mathlib.Analysis.SpecialFunctions.Exp -import Mathlib.Analysis.SpecialFunctions.Log.Basic - -lemma numerator_pos (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by - have den_pos : 0 < (den : ℕ) := den.property - have den_real_pos : (0 : ℝ) < ↑(den : ℕ) := Nat.cast_pos.mpr den_pos - have two_num_nonneg : 0 ≤ (2 * num : ℝ) := by - refine mul_nonneg ?_ (Nat.cast_nonneg num) - norm_num - exact add_pos_of_pos_of_nonneg den_real_pos two_num_nonneg - -lemma denominator_pos (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by - rw [sub_pos] - norm_cast - -lemma step1 (num : Nat) (den : PNat) (h : 2 * num < den): -ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by - congr - rw [eq_comm] - apply Real.exp_log - apply div_pos - {exact numerator_pos num den} - {exact denominator_pos num den h} - -lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): - (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by - rw [ENNReal.ofReal_div_of_pos] - · congr - norm_cast - · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h - exact sub_pos.mpr foo - -lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : -(den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by - rw [← step1 num den h] - exact step2 num den h diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index f7fce9f4..30e85cc5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -8,7 +8,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite -/-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ open SLang open ENNRealLemmas @@ -287,13 +286,6 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [Not] intro a_1 norm_cast - - - - - - /- For this sorry, we need the h hypothesis to be a strict inequality -/ - } { simp rw [@ENNReal.div_eq_top] @@ -310,7 +302,6 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de obtain ⟨left⟩ := h_1 norm_cast } - -- arithmetic now | false => cases hqa : query a with @@ -318,7 +309,6 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de rw [RRSingleSample_true_false _ _ _ _ _ hqa] cases hqa' : query a' with | true => rw [RRSingleSample_true_false _ _ _ _ _ hqa'] - -- arithmetic now simp rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] @@ -796,8 +786,9 @@ cases xlen1 : l₁.length == x.length with RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - apply final_step_combined - exact h + /- apply final_step_combined + exact h --/ + sorry _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop} {apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} From d40f0c1b49ebe51fb11b43aaccbecd869698d61e Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 28 Jul 2025 16:47:01 -0700 Subject: [PATCH 089/216] Update DPProof.lean --- .../Pure/Local/RandomizedResponse/DPProof.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 0ad43a2c..3c905ae1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -38,3 +38,4 @@ lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by rw [← step1 num den h] exact step2 num den h + From edc04f0c62e7430d21b9c63db1b4a8b7afa041ce Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 29 Jul 2025 09:58:35 -0700 Subject: [PATCH 090/216] Update DPProof.lean --- .../Pure/Local/RandomizedResponse/DPProof.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 3c905ae1..0ad43a2c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -38,4 +38,3 @@ lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by rw [← step1 num den h] exact step2 num den h - From 4fa8d49914352e021df93b6b1a89a078ad2389bc Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 10:18:36 -0700 Subject: [PATCH 091/216] beginnings --- .../Pure/Local/RAPPOR/Definitions.lean | 1 + .../Pure/Local/RAPPOR/Properties.lean | 102 +++++++++++++++++- .../Local/RandomizedResponse/BasicLemmas.lean | 3 +- .../RandomizedResponseMain.lean | 12 +-- 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean index 81fa7f64..9f7c0909 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean @@ -11,6 +11,7 @@ open RandomizedResponse SLang /- Definition of One-Time Basic RAPPOR. -/ /- One-hot encoding of a vector-/ +@[simp] def one_hot {T : Type} (n : Nat) (query : T -> Fin n) (v : T) : List Bool := List.ofFn (fun i => query v = i) /- One-Time Basic RAPPOR for a single user. diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 82de5530..87829ed8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -4,10 +4,13 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.RandomizedResponseMain import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions namespace RAPPOR + open RandomizedResponse +open SLang /- In this file, we show normalization for the One-Time Basic RAPPOR Algorithm. -/ @@ -33,25 +36,116 @@ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ -lemma RRSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): +lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): RAPPORSingleSample n query num den h l₁ l₂= 0 := by rw [RAPPORSingleSample] apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen +lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): + RAPPORSample n query num den h l₁ x = 0 := by + induction l₁ generalizing x with + | nil => simp [RAPPORSample, -mapM] + aesop + | cons hd tl ih => + simp [RAPPORSample, -mapM] + simp [RAPPORSample, -mapM] at ih + intro b + apply Or.inr + intro y hy + subst hy + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +#check List.ofFn_eq_map + +lemma List.ofFn_rw {T : Type} (n : Nat) (f : Fin n -> T) (i : Fin n): + (List.ofFn f)[i] = f i := by + simp [List.ofFn_eq_map] + +lemma one_hot_same_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query v = query u) : + one_hot n query v = one_hot n query u := by + simp + rw [h] + +lemma one_hot_same_answer_index {T : Type} (n : Nat) (query: T -> Fin n) (v : T) (j : Fin n) : + (one_hot n query v)[j]'(by simp) = true ↔ query v = j := by + simp [one_hot] + +lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query u ≠ query v): + (one_hot n query v)[query v]'(by simp) ≠ (one_hot n query u)[query v]'(by simp) := by + simp + rw [← @Ne.eq_def] + exact h + +lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : + RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob + +lemma RRSamplePushForward_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): + RRSamplePushForward num den h l b ≠ 0 := by + rw [RRSamplePushForward] + rw [prod_of_ind_prob _ _ _ _ k] + rw [@tprod_fintype] + rw [@Finset.prod_ne_zero_iff] + intro a ha + sorry -- Need a proof that RRSinglePushForward is non-zero, should be easy + +lemma RRSamplePushForward_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): + RRSamplePushForward num den h l b ≠ ⊤ := by + unfold RRSamplePushForward + sorry -- Need a proof that RRSinglePushForward is finite, and that this works well with mapM... + lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by simp_all only [RAPPORSingleSample] set ohv := one_hot n query v set ohu := one_hot n query u cases hlen: ohv.length == b.length with - | true => sorry + | true => + cases h_eq: query v == query u with + | true => simp at h_eq + have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq + rw [same_answer] + rw [@ENNReal.div_self] + {sorry} + {sorry} + {sorry} + | false => + simp at hlen + simp_all only [RRSamplePushForward] + have hlen2: ohu.length = b.length := by + rw [←hlen] + simp [ohu, ohv] + rw [prod_of_ind_prob _ _ _ _ hlen] + rw [prod_of_ind_prob _ _ _ _ hlen2] + sorry | false => simp at hlen - have h1: RRSamplePushForward num den h ohv b = 0 := RRSample_diff_lengths n query num den h v b hlen + have h1: RRSamplePushForward num den h ohv b = 0 := RAPPORSingleSample_diff_lengths n query num den h v b hlen rw [h1] rw [@ENNReal.zero_div] simp - +lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): + DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) -- placeholder + := by + apply singleton_to_event_update + intros l₁ l₂ h_adj x + cases xlen1 : l₁.length == x.length with + | true => simp at xlen1 + have xlen2: l₂.length = x.length := by + rw [←xlen1] + rw[←UpdateNeighbour_length h_adj] + rw[RAPPOR_prob_of_ind_prob_PMF n query num den h l₁ x xlen1] + rw[RAPPOR_prob_of_ind_prob_PMF n query num den h l₂ x xlen2] + cases h_adj with + | Update hl₁ hl₂ => + rename_i a y b z + simp + sorry + | false => simp at xlen1 + rw [←Ne.eq_def] at xlen1 + have numerator_zero: RAPPORSample_PMF n query num den h l₁ x = 0 := RAPPORSample_diff_lengths n query num den h l₁ x xlen1 + rw [numerator_zero] + rw [@ENNReal.zero_div] + simp end RAPPOR diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index e6148d30..d0a24409 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -73,6 +73,7 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P } {exact ne_of_beq_false rfl} + lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ ⊤ := by have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by @@ -165,4 +166,4 @@ lemma valid_index2 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin (n + 1 sorry lemma mwi3 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by apply valid_index1; apply h)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by apply valid_index2; apply h)) := by congr --/ +-/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 30e85cc5..2deca518 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -65,8 +65,7 @@ lemma prod_of_ind_prob (β : Type) [DecidableEq β] (f : T -> SLang β) (a : Lis end SLang - -theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): +theorem RRSample_prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob lemma ennreal_div_one (a: ENNReal) : a / 1 = a := by aesop @@ -761,8 +760,8 @@ apply singleton_to_event_update intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with | true => - rw[prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] - rw[prod_of_ind_prob_PMF query num den h x l₂ + rw[RRSample_prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] + rw[RRSample_prod_of_ind_prob_PMF query num den h x l₂ (by rw[←UpdateNeighbour_length h_adj] simp at xlen1 exact xlen1)] @@ -782,14 +781,15 @@ cases xlen1 : l₁.length == x.length with rw[←xlen1] rw [@Nat.lt_add_right_iff_pos] simp - {calc + {/- calc RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by /- apply final_step_combined exact h --/ sorry - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop} + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop -/ + sorry} {apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} | false => simp at xlen1 From 642f7725517f20a96cf76b02ecf2528be52f0be5 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 10:41:47 -0700 Subject: [PATCH 092/216] someupdates --- .../Pure/Local/RAPPOR/Properties.lean | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 87829ed8..b27917c6 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -79,7 +79,7 @@ lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -lemma RRSamplePushForward_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): +lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): RRSamplePushForward num den h l b ≠ 0 := by rw [RRSamplePushForward] rw [prod_of_ind_prob _ _ _ _ k] @@ -88,7 +88,7 @@ lemma RRSamplePushForward_non_zero {T : Type} (query: T -> Bool) (num : Nat) (de intro a ha sorry -- Need a proof that RRSinglePushForward is non-zero, should be easy -lemma RRSamplePushForward_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): +lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): RRSamplePushForward num den h l b ≠ ⊤ := by unfold RRSamplePushForward sorry -- Need a proof that RRSinglePushForward is finite, and that this works well with mapM... @@ -98,24 +98,33 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den simp_all only [RAPPORSingleSample] set ohv := one_hot n query v set ohu := one_hot n query u + have oh_len: ohu.length = ohv.length := by simp[ohv, ohu] cases hlen: ohv.length == b.length with | true => + simp at hlen cases h_eq: query v == query u with | true => simp at h_eq have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq rw [same_answer] rw [@ENNReal.div_self] - {sorry} - {sorry} - {sorry} + {rw [@sq] + aesop + sorry + } + { + apply RRSamplePushForward_non_zero + exact T + rw[←hlen] + exact oh_len + } + { apply RRSamplePushForward_finite + exact T + } | false => - simp at hlen + simp at h_eq simp_all only [RRSamplePushForward] - have hlen2: ohu.length = b.length := by - rw [←hlen] - simp [ohu, ohv] rw [prod_of_ind_prob _ _ _ _ hlen] - rw [prod_of_ind_prob _ _ _ _ hlen2] + rw [prod_of_ind_prob _ _ _ _ oh_len] sorry | false => simp at hlen From f7cd8cc91e17749ed9c027fc4c506d29d164c07a Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 10:48:24 -0700 Subject: [PATCH 093/216] Simplification of Reduction final --- .../Pure/Local/Playground/test.lean | 25 +++++++ .../Local/RandomizedResponse/DPProof.lean | 2 +- .../RandomizedResponseMain.lean | 67 ++++++------------- 3 files changed, 45 insertions(+), 49 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean new file mode 100644 index 00000000..749783da --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean @@ -0,0 +1,25 @@ + +import Mathlib.Data.ENNReal.Basic + + + +lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): + i.val < l₁.length := by + have hl1: l₁.length - 1 + 1 = l₁.length := by + rw [Nat.sub_add_cancel] + rw[h1] + simp + linarith + exact Nat.lt_of_lt_of_eq i.2 hl1 + +lemma conversion2 (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hx: l.length = x.length)(f: T → Bool → ENNReal): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(by sorry)) (x[i.val]'(by rw[← hx];sorry))) := by + rw[Nat.sub_add_cancel] + rw[h1] + simp + linarith + +lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hx: l.length = x.length)(f: T → Bool → ENNReal): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(by rw[← hx];sorry))) := by + rw[Nat.sub_add_cancel] + rw[h1] + simp + linarith diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 3d9743d5..557666e5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -75,7 +75,7 @@ lemma final_step (num : Nat) (den : PNat) (h: 2 * num < den): rw [@div_pos_iff] apply Or.inl apply And.intro - {apply bruh} + {apply pos_helper} {apply bruh1 num den h} /- have h1 : 0 < ((1: ℝ) / 2 + ↑num / ↑(NNReal.ofPNat den)) := bruh query num den h l diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index dcd2da40..58aa0742 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -115,7 +115,6 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de } | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa'] -- arithmetic now - aesop sorry | false => rw [RRSingleSample_false_true _ _ _ _ _ hqa] @@ -291,7 +290,7 @@ lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length i.val < l₁.length := by have hl1: l₁.length - 1 + 1 = l₁.length := by rw [Nat.sub_add_cancel] - subst h1 + rw[h1] simp linarith exact Nat.lt_of_lt_of_eq i.2 hl1 @@ -350,56 +349,28 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ intro i simp[noninf] -theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / - (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by - have h_len : l₁.length - 1 + 1 = l₁.length := by - rw[Nat.sub_add_cancel] - rw[h1] - simp - linarith - have h_len2 : l₂.length - 1 + 1 = l₂.length := by - rw[Nat.sub_add_cancel] - rw[h2] - simp - linarith - rw[tprod_fintype] - rw[tprod_fintype] - rw[Fintype.prod_equiv (Equiv.cast (congr_arg Fin h_len.symm))] - rw[Fintype.prod_equiv (Equiv.cast (congr_arg Fin h_len2.symm))] - rw[← tprod_fintype] - rw[← tprod_fintype] - rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] - simp - intro i - congr - rw[Nat.sub_add_cancel] - rw[h2] - simp - linarith - rw [← propext cast_eq_iff_heq] +lemma fin_prod_cast {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : + ∏ i : Fin n, f i = ∏ i : Fin m, f (Fin.cast h.symm i) := by + subst h + simp - rw[Nat.sub_add_cancel] - rw[h2] - simp - linarith +lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang Bool): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by + rw[tprod_fintype] + rw[tprod_fintype] + rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] + simp - rw [← propext cast_eq_iff_heq] - intro i - congr - rw[Nat.sub_add_cancel] - rw[h1] - simp - linarith - simp - rw[← propext cast_eq_iff_heq] - rw[Nat.sub_add_cancel] - rw[h1] - simp - linarith - simp - rw[← propext cast_eq_iff_heq] + + +theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by + have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith + have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith + rw[conversion l₂ x h2 hl2 hy f] + rw[conversion l₁ x h1 hl1 hx f] + rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] open Finset open scoped BigOperators From 403d1e1b58a80dcded1aaf80c47cd0ef6efcd7cd Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 10:58:45 -0700 Subject: [PATCH 094/216] more updates --- .../Pure/Local/RandomizedResponse/RandomizedResponseMain.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 58aa0742..c5583196 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -350,13 +350,11 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ simp[noninf] lemma fin_prod_cast {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : - ∏ i : Fin n, f i = ∏ i : Fin m, f (Fin.cast h.symm i) := by + ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by subst h simp lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang Bool): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by - rw[tprod_fintype] - rw[tprod_fintype] rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] simp From d116cadc8708420b1d20bbdde2270024f22b4e6b Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 11:38:26 -0700 Subject: [PATCH 095/216] More cleaning on succHelp --- .../RandomizedResponseMain.lean | 70 ++++++------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index c5583196..42f71555 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -210,6 +210,14 @@ lemma valid_index1 (l₁ l₂ : List T)(h1: l₁ = a++[n]++b) (h2: l₂ = a++[m] lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): ∀i : Fin (l₁.length - 1), l₁[(Fin.succAbove a.length i).val]'(valid_index0 l₁ h1 i) = l₂[Fin.succAbove a.length i]'(valid_index1 l₁ l₂ h1 h2 i) := by intro i simp only [h1,h2] + have mod: a.length % (l₁.length-1+1) = a.length := by + rw[Nat.mod_eq_of_lt] + rw[Nat.sub_add_cancel] + rw[h1] + simp + rw[h1] + simp + linarith by_cases i < a.length case pos h => unfold Fin.succAbove @@ -217,30 +225,23 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw [@Fin.castSucc_lt_iff_succ_le] rw [@Fin.le_iff_val_le_val] simp - have mod: a.length % (l₁.length-1+1) = a.length := by - rw[Nat.mod_eq_of_lt] - rw[Nat.sub_add_cancel] - rw[h1] - simp - rw[h1] - simp - linarith rw[mod] simp[Nat.succ_le_of_lt h] simp only[h'] - simp only [↓reduceIte, Fin.coe_castSucc, - Fin.getElem_fin] + simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] + rw[List.getElem_append_left (a++[n]) b (by simp[h];linarith)] + rw[List.getElem_append_left a [n] h] + rw[List.getElem_append_left (a++[m]) b (by simp[h];linarith)] rw[List.getElem_append_left] - rw[List.getElem_append_left] - rw[List.getElem_append_left] - rw[List.getElem_append_left] - exact h - simp[h] - linarith - simp[h] - linarith case neg h => + have iab: i.val - a.length < b.length := by + have ile : i < l₁.length - 1 := i.is_lt + simp[h1] at ile + rw[tsub_lt_iff_left] + exact ile + simp at h + exact h unfold Fin.succAbove have h' : ¬ i.castSucc < ↑a.length := by simp at h @@ -248,43 +249,19 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw [@Fin.le_castSucc_iff] apply Nat.lt_succ_of_le simp - have mod: a.length % (l₁.length-1+1) = a.length := by - rw[Nat.mod_eq_of_lt] - rw[Nat.sub_add_cancel] - rw[h1] - simp - rw[h1] - simp - linarith rw[mod] exact h simp only[h'] simp only [↓reduceIte, Fin.coe_castSucc, Fin.getElem_fin] - rw[List.getElem_append_right] - rw[List.getElem_append_right] - simp - simp - linarith + rw[List.getElem_append_right (a++[n]) b (by simp;linarith)] + rw[List.getElem_append_right (a++[m]) b (by simp;linarith)] simp - - have ile : i < l₁.length - 1 := by exact i.is_lt - simp[h1] at ile - rw[tsub_lt_iff_left] - exact ile - simp at h - exact h - simp linarith simp + exact iab - have ile : i < l₁.length - 1 := by exact i.is_lt - simp[h1] at ile - rw[tsub_lt_iff_left] - exact ile - simp at h - exact h lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): i.val < l₁.length := by @@ -359,9 +336,6 @@ lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hl : l.length ≥ simp - - - theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith From 6d6d80ca9dfbe1ba741807f0ad465aeed594d771 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 12:04:50 -0700 Subject: [PATCH 096/216] progress --- .../Pure/Local/ENNRealLemmasSuite.lean | 62 +++++++++++++++++-- .../Pure/Local/RAPPOR/Properties.lean | 37 ++++++++--- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 1eb02322..61f71c20 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -27,11 +27,65 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry +#check ENNReal.mul_eq_top + lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [← @ENNReal.inv_ne_zero] at h2 - sorry + rw [@Ne.eq_def] + intro h + rw [ENNReal.mul_eq_top] at h + cases h with + | inl h => + apply And.right at h + contradiction + | inr h => + apply And.left at h + contradiction + +lemma Finset.prod_ne_top (n : Nat) (f : ℕ -> ENNReal) (h : ∀ i, f i ≠ ⊤): + ∏ (i : Fin n), f i.val ≠ ⊤ := by + induction n with + | zero => simp + | succ m ih => + rw [@Fin.prod_univ_add] + apply mult_ne_top + apply ih + rw [@Fin.prod_univ_one] + apply h + +lemma Finset.prod_ne_top_fin (n : Nat) (f : Fin n -> ENNReal) (h : ∀ i, f i ≠ ⊤): + ∏ (i : Fin n), f i ≠ ⊤ := by + cases hn : n == 0 with +| false => + have hn1: n > 0 := by + simp at hn + exact Nat.zero_lt_of_ne_zero hn + + let g : Nat -> ENNReal := fun i => f (Fin.ofNat' i hn1) + have mod: ∀ i : Fin n, i % n = i := by + intro i + rw [Nat.mod_eq] + simp + have h0: ∀ i : Fin n, f i = g i := by + intro i + simp_all [g] + have eq: Fin.ofNat' (i.val) hn1 = i := by + simp_all [Fin.ofNat'] + rw [eq] + have h1: ∏ i : Fin n, f i = ∏ i : Fin n, g i := by + conv => + enter [2, 2, i] + rw [←h0 i] + have h2: ∀ i : ℕ, g i ≠ ⊤ := by + intro i + apply h + rw [h1] + apply Finset.prod_ne_top n g + exact h2 + | true => + simp at hn + subst hn + simp + lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by rw [← @ENNReal.inv_ne_zero] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index b27917c6..812f3811 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -32,15 +32,16 @@ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) rw [← Summable.hasSum_iff ENNReal.summable] apply RAPPORSingleSample_PMF_helper query num den h a -/- Instantiation of RAPPOR as a PMF-/ +/- Promotion of RAPPOR to a PMF-/ def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ +/- In the RAPPOR algorithm with n possible responses, the probability of an output of different length than n is zero.-/ lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): RAPPORSingleSample n query num den h l₁ l₂= 0 := by rw [RAPPORSingleSample] apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen - +/- The same as above, but extended to the entire dataset. -/ lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): RAPPORSample n query num den h l₁ x = 0 := by induction l₁ generalizing x with @@ -55,8 +56,8 @@ lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: subst hy simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] -#check List.ofFn_eq_map - +/- The next few lemmas are helper lemmas to simplify proofs involving one-hot encodings. +-/ lemma List.ofFn_rw {T : Type} (n : Nat) (f : Fin n -> T) (i : Fin n): (List.ofFn f)[i] = f i := by simp [List.ofFn_eq_map] @@ -76,9 +77,13 @@ lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T rw [← @Ne.eq_def] exact h +/- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob +/- RRSamplePushForward gives a non-zero probability for an output of the same length. + This is needed in the DP proof. +-/ lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): RRSamplePushForward num den h l b ≠ 0 := by rw [RRSamplePushForward] @@ -88,11 +93,26 @@ lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * n intro a ha sorry -- Need a proof that RRSinglePushForward is non-zero, should be easy +/- RRSamplePushForward is always finite. This is needed in the DP proof. -/ lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): RRSamplePushForward num den h l b ≠ ⊤ := by - unfold RRSamplePushForward - sorry -- Need a proof that RRSinglePushForward is finite, and that this works well with mapM... + cases hlen: l.length == b.length with + | true => + simp at hlen + unfold RRSamplePushForward + rw [prod_of_ind_prob _ _ _ _ hlen] + rw [@tprod_fintype] + apply ENNRealLemmas.Finset.prod_ne_top_fin + intro i + -- Need a proof that RRSinglePushForward is finite + sorry + | false => + simp at hlen + have hzero: RRSamplePushForward num den h l b = 0 := RRSamplePushForward_diff_lengths num den h l b hlen + rw [hzero] + simp +/- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by simp_all only [RAPPORSingleSample] @@ -109,7 +129,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.div_self] {rw [@sq] aesop - sorry + sorry -- have a separate lemma that proves this } { apply RRSamplePushForward_non_zero @@ -133,6 +153,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.zero_div] simp +/- This extends the previous lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) -- placeholder := by @@ -149,6 +170,8 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d | Update hl₁ hl₂ => rename_i a y b z simp + /- Now we need to apply the generalized reduction lemma, + and then do some arithmetic. -/ sorry | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 From 1a9692d088b3c79843cd03b3bb04a2c7ffa43d57 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Tue, 29 Jul 2025 15:05:00 -0400 Subject: [PATCH 097/216] finished sorrys basic lemmas --- .../Pure/Local/ENNRealLemmasSuite.lean | 15 +++ .../Local/RandomizedResponse/BasicLemmas.lean | 112 +++++++++++++++++- 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 1eb02322..d5a4c47d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -142,5 +142,20 @@ lemma tsum_ite_mult (f : T -> ENNReal) (P : T -> Bool): (∑' (x : T), f x * if (P x) then 1 else 0) = ∑' (x : T), if (P x) then f x else 0 := by simp_all only [mul_ite, mul_one, mul_zero] +lemma sub_add_eq_add_sub (a b :ENNReal)(h : b ≤ a)(h1 : b ≠ ⊤): a - b+ b = a + b-b := by + rw [@AddCommMonoidWithOne.add_comm] + rw [add_tsub_cancel_of_le h] + rw [@AddCommMonoidWithOne.add_comm] + symm + apply ENNReal.sub_eq_of_add_eq + exact h1 + rw [add_comm] + +lemma sub_add_cancel_ennreal (a b :ENNReal)(h:b≤ a)(h1 : b ≠ ⊤): a -b +b =a := by + rw [sub_add_eq_add_sub] + rw [ENNReal.add_sub_cancel_right h1] + exact h + exact h1 + end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index e6148d30..f0ab120c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -15,11 +15,61 @@ open ENNRealLemmas lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + + + + lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by rw[RRSingleSample, RRSinglePushForward] - aesop - sorry /- This is arithmetically true, but proving arithmetic things is a mess -/ + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, + pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, reduceIte, mul_one, mul_zero, tsum_ite_eq] + simp + rw [ENNReal.sub_eq_of_add_eq] + simp + rw [@ENNReal.div_eq_top] + rw [Not] + intro A + rcases A with ⟨_,hb⟩ + simp at hb + rename_i h_1 + simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] + have h_le : (2:ENNReal) *num ≤ den.val := by + rw [@Nat.lt_iff_le_and_ne] at h + rcases h with ⟨hl,_⟩ + exact mod_cast hl + have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by + simp + rw [Not] + intro B + norm_cast + have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by + simp + rw [@ENNReal.div_add_div_same] + rw [add_comm] + conv => + enter [2,1,2] + rw [add_comm] + rw [← add_assoc] + rw [sub_add_cancel_ennreal] + have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by + rw[two_mul] + rw [ENNReal.div_self] + simp + simp + norm_cast + rw [@ENNReal.le_coe_iff] + simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] + apply h_le + simp_all only [WithTop.coe_natCast, Nat.cast_inj] + apply Eq.refl + exact two_num_fin + symm + exact hh + + + /- This is arithmetically true, but proving arithmetic things is a mess -/ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by @@ -45,7 +95,47 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] /- This is the same state as the first lemma that's not working, again it's just annoying arithmetic. -/ - sorry + rw [ENNReal.sub_eq_of_add_eq] + simp + rw [@ENNReal.div_eq_top] + rw [Not] + intro A + rcases A with ⟨_,hb⟩ + simp at hb + rename_i h_1 + simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] + have h_le : (2:ENNReal) *num ≤ den.val := by + rw [@Nat.lt_iff_le_and_ne] at h + rcases h with ⟨hl,_⟩ + exact mod_cast hl + have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by + simp + rw [Not] + intro B + norm_cast + have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by + simp + rw [@ENNReal.div_add_div_same] + rw [add_comm] + conv => + enter [2,1,2] + rw [add_comm] + rw [← add_assoc] + rw [sub_add_cancel_ennreal] + have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by + rw[two_mul] + rw [ENNReal.div_self] + simp + simp + norm_cast + rw [@ENNReal.le_coe_iff] + simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] + apply h_le + simp_all only [WithTop.coe_natCast, Nat.cast_inj] + apply Eq.refl + exact two_num_fin + symm + exact hh lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ 0 := by @@ -60,10 +150,20 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P norm_cast rw [PNat.mul_coe] simp_all only [PNat.val_ofNat] - sorry + have hh : den.val - 2*num ≤ den.val:= by simp + have gg : den.val < 2 *den.val := by simp + rw [@Nat.le_iff_lt_or_eq] at hh + cases hh with + | inl hl => + apply LT.lt.trans hl gg + | inr hr => + rw [hr] + simp + + | false => simp at hb rw [← Bool.eq_not_of_ne hb] - intro j + intro apply And.intro trivial apply And.intro @@ -165,4 +265,4 @@ lemma valid_index2 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin (n + 1 sorry lemma mwi3 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by apply valid_index1; apply h)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by apply valid_index2; apply h)) := by congr --/ +-/ From 50e27ab2a758d3d823e58375ee53e98fa829b73c Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 13:15:30 -0700 Subject: [PATCH 098/216] More simplification --- .../RandomizedResponseMain.lean | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 42f71555..e66c4330 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -207,17 +207,15 @@ lemma valid_index1 (l₁ l₂ : List T)(h1: l₁ = a++[n]++b) (h2: l₂ = a++[m] apply valid_index0 exact h1 +lemma mod_helper (a b: ℕ)(h1: b ≥ 1)(h2: a unfold Fin.succAbove @@ -225,7 +223,7 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw [@Fin.castSucc_lt_iff_succ_le] rw [@Fin.le_iff_val_le_val] simp - rw[mod] + rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] simp[Nat.succ_le_of_lt h] simp only[h'] @@ -249,7 +247,7 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw [@Fin.le_castSucc_iff] apply Nat.lt_succ_of_le simp - rw[mod] + rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] exact h simp only[h'] simp only [↓reduceIte, Fin.coe_castSucc, @@ -288,13 +286,10 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ rw[← hx] rw[h1] simp - conv => - enter[1,2] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] - have helper2: Fin (l₁.length - 1) = Fin (l₂.length - 1) := by aesop - have helper3: l₁.length - 1 = l₂.length - 1 := by aesop + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] + have helper: l₁.length - 1 = l₂.length - 1 := by aesop have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by - apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper3)) + apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper)) simp[succHelp l₁ l₂ h1 h2] intro i congr @@ -303,19 +298,9 @@ lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ rw[hlp] rw[ENNReal.mul_div_mul_right] simp - have mod: a.length % (l₁.length-1+1) = a.length := by - rw[Nat.mod_eq_of_lt] - rw[hx] - rw[Nat.sub_add_cancel] - exact ind - rw[← hx] - rw[h1] - simp - linarith - simp[mod] - rw[hx] at mod - rw[← hy] at mod - simp[mod] + + simp[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] + simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] rw[Finset.prod_ne_zero_iff] intro i From cfc108b3f8a50bb1d7f4779b5378e56f1e9ba2c2 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 13:16:38 -0700 Subject: [PATCH 099/216] more --- .../Pure/Local/RAPPOR/Properties.lean | 12 ++++--- .../Local/RandomizedResponse/BasicLemmas.lean | 33 ++++++++++++++----- .../Local/RandomizedResponse/Definitions.lean | 2 +- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 812f3811..ee5c9d4c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -90,8 +90,8 @@ lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * n rw [prod_of_ind_prob _ _ _ _ k] rw [@tprod_fintype] rw [@Finset.prod_ne_zero_iff] - intro a ha - sorry -- Need a proof that RRSinglePushForward is non-zero, should be easy + intro a _ + apply RRSinglePushForward_non_zero /- RRSamplePushForward is always finite. This is needed in the DP proof. -/ lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): @@ -104,8 +104,7 @@ lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num rw [@tprod_fintype] apply ENNRealLemmas.Finset.prod_ne_top_fin intro i - -- Need a proof that RRSinglePushForward is finite - sorry + apply RRSinglePushForward_finite | false => simp at hlen have hzero: RRSamplePushForward num den h l b = 0 := RRSamplePushForward_diff_lengths num den h l b hlen @@ -130,6 +129,8 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den {rw [@sq] aesop sorry -- have a separate lemma that proves this + /- Probably for this we need a version of + quot_gt_one_rev in ENNRealLemmasSuite-/ } { apply RRSamplePushForward_non_zero @@ -153,6 +154,9 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.zero_div] simp + +#check Real.log_rpow -- we'll need this later + /- This extends the previous lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) -- placeholder diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index f34e729a..c417ced8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -16,8 +16,16 @@ open ENNRealLemmas lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + /- RRSinglePushForward is like RRSingleSample, but with "query" taken to be the identity map-/ +lemma RRSingleSample_is_RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool): + RRSingleSample (fun x => x) num den h l = RRSinglePushForward num den h l := by + simp [RRSingleSample, RRSinglePushForward] - + /- RRSamplePushForward is like RRSample, but with "query" taken to be the identity map -/ +lemma RRSample_is_RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool): + RRSample (fun x => x) num den h l = RRSamplePushForward num den h l := by + simp [RRSample, RRSamplePushForward, -mapM] + rfl lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by @@ -68,7 +76,6 @@ lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : symm exact hh - /- This is arithmetically true, but proving arithmetic things is a mess -/ lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): @@ -93,8 +100,6 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - /- This is the same state as the first lemma that's not working, - again it's just annoying arithmetic. -/ rw [ENNReal.sub_eq_of_add_eq] simp rw [@ENNReal.div_eq_top] @@ -137,9 +142,9 @@ lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den symm exact hh -lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ 0 := by - simp [RRSingleSample, RRSinglePushForward] +lemma RRSinglePushForward_non_zero {T : Type} (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : T) (b : Bool): + RRSinglePushForward num den h (query l) b ≠ 0 := by + simp [RRSinglePushForward] cases hb : b == query l with | true => simp at hb subst hb @@ -159,8 +164,6 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P | inr hr => rw [hr] simp - - | false => simp at hb rw [← Bool.eq_not_of_ne hb] intro @@ -173,6 +176,10 @@ lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : P } {exact ne_of_beq_false rfl} +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + rw [RRSingleSample] + apply RRSinglePushForward_non_zero lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): RRSingleSample query num den h l b ≠ ⊤ := by @@ -212,6 +219,14 @@ lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNa aesop exact hden +/- Given what was already proved, the simplest way to prove the next lemma + is to note that RRSinglePushForward and RRSample with the identity query are the same -/ +lemma RRSinglePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) (b : Bool): + RRSinglePushForward num den h l b ≠ ⊤ := by + rw [←RRSingleSample_is_RRSinglePushForward] + apply RRSingleSample_finite + + lemma RRSamplePushForward_diff_lengths (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List Bool) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSamplePushForward num den h l₁ l₂ = 0 := by induction l₁ generalizing l₂ with diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index 102ba9b2..747d126d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -9,7 +9,7 @@ open SLang lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith - + def RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (l) r From 1700afc0fab231023fbbb8f910e55c31f0f63999 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Tue, 29 Jul 2025 16:18:00 -0400 Subject: [PATCH 100/216] div_ne_top --- .../Pure/Local/ENNRealLemmasSuite.lean | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index d5a4c47d..16115848 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -34,10 +34,17 @@ lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ sorry lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by - rw [← @ENNReal.inv_ne_zero] - rw [← @ENNReal.inv_ne_zero] at h1 - rw [@ENNReal.div_eq_inv_mul] - sorry + simp + rw [Not] + intro a + rw [@ENNReal.div_eq_top] at a + rcases a with ⟨_,ar⟩ + subst ar + simp_all only [ne_eq, not_true_eq_false] + rename_i h3 + rcases h3 with ⟨hl,_⟩ + subst hl + simp_all only [ne_eq, not_true_eq_false] lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by intro h1 From 887ad43fca593edec856f00bac7af80062b8142a Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 14:05:10 -0700 Subject: [PATCH 101/216] More cleaning --- .../Local/RandomizedResponse/RandomizedResponseMain.lean | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index e66c4330..e8d74d89 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -217,6 +217,7 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): intro i simp only [h1,h2] by_cases i < a.length + case pos h => unfold Fin.succAbove have h' : i.castSucc < ↑a.length := by @@ -232,6 +233,7 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw[List.getElem_append_left a [n] h] rw[List.getElem_append_left (a++[m]) b (by simp[h];linarith)] rw[List.getElem_append_left] + case neg h => have iab: i.val - a.length < b.length := by have ile : i < l₁.length - 1 := i.is_lt @@ -250,15 +252,14 @@ lemma succHelp (l₁ l₂ : List T)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b): rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] exact h simp only[h'] - simp only [↓reduceIte, Fin.coe_castSucc, - Fin.getElem_fin] + simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] rw[List.getElem_append_right (a++[n]) b (by simp;linarith)] rw[List.getElem_append_right (a++[m]) b (by simp;linarith)] simp simp linarith simp - exact iab + linarith lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): From b0fce85ceab8f48110334e2d0ba5c79b101ae423 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 29 Jul 2025 14:10:37 -0700 Subject: [PATCH 102/216] Beta generalization --- .../Local/RandomizedResponse/RandomizedResponseMain.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index e8d74d89..411f9e9c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -271,13 +271,13 @@ lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length linarith exact Nat.lt_of_lt_of_eq i.2 hl1 -lemma valid_index3 {l₁ : List T} {x : List Bool} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): +lemma valid_index3 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): i.val < x.length := by rw[←hx] apply valid_index2 h1 i -lemma reduction2 (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / +lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by rw[tprod_fintype] rw[tprod_fintype] @@ -317,12 +317,12 @@ lemma fin_prod_cast {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : subst h simp -lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang Bool): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by +lemma conversion {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang β): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] simp -theorem reduction_final (l₁ l₂: List T)(x: List Bool)(f: T → SLang Bool)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: Bool), f k bo ≠ 0)(noninf: ∀(k: T) (bo: Bool), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / +theorem reduction_final {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith From db60b18656a16a4e80e78d0ee42654754dd4f31f Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 14:11:30 -0700 Subject: [PATCH 103/216] robert --- .../Pure/Local/RAPPOR/Properties.lean | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index ee5c9d4c..5190a9f2 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -77,6 +77,28 @@ lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T rw [← @Ne.eq_def] exact h +lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j]'(by simp) ≠ (one_hot n query u)[j]'(by simp) ↔ query v = j ∨ query u = j := by + simp [one_hot] + apply Iff.intro + { intro ha + by_contra hb -- actually aesop can take it from here + rw [Mathlib.Tactic.PushNeg.not_or_eq] at hb + apply ha + apply Iff.intro + intro hc + apply And.left at hb + contradiction + intro hc + apply And.right at hb + contradiction + } + { intro ha + cases ha with + | inl h1 => aesop + | inr h1 => aesop + } + /- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob @@ -111,6 +133,14 @@ lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num rw [hzero] simp +lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): + (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by + sorry + +/- lemma RAPPOR_cancel {T : Type} (n : Nat) (query : T -> Fin n) (num : Nat) (den : PNat) (h : 2 * num < den) (v u : T) (len_eq: (one_hot n query v).length = (one_hot n query u).length) (b : List Bool) (hlen: (one_hot n query u).length = b.length): + ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) + / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ + /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by @@ -146,6 +176,12 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den simp_all only [RRSamplePushForward] rw [prod_of_ind_prob _ _ _ _ hlen] rw [prod_of_ind_prob _ _ _ _ oh_len] + simp_all [@tprod_fintype] + have len_eq: ohu.length = ohv.length := by aesop + have index_1: ∏ i : Fin ohv.length, RRSinglePushForward num den h ohv[i.val] b[i.val] = + ∏ i : Fin ohu.length, RRSinglePushForward num den h ohv[i.val] b[i.val] := by sorry + rw [index_1] + rw [prod_over_prod] -- this needs proving sorry | false => simp at hlen From 84bbf22385198a7d4b37b99178164a3d98b3b3ba Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 16:28:36 -0700 Subject: [PATCH 104/216] disgustingproof --- .../Pure/Local/RAPPOR/Properties.lean | 72 ++++++++++++++++++- .../RandomizedResponseMain.lean | 3 +- .../Local/RandomizedResponse/Reduction.lean | 57 +++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 5190a9f2..7ac8cba5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -6,6 +6,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.RandomizedResponseMain import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Reduction namespace RAPPOR @@ -144,6 +145,7 @@ lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by + -- probably want to restate the bound in an arithmetically equivalent way simp_all only [RAPPORSingleSample] set ohv := one_hot n query v set ohu := one_hot n query u @@ -195,7 +197,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den /- This extends the previous lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): - DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) -- placeholder + DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) := by apply singleton_to_event_update intros l₁ l₂ h_adj x @@ -208,11 +210,75 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d rw[RAPPOR_prob_of_ind_prob_PMF n query num den h l₂ x xlen2] cases h_adj with | Update hl₁ hl₂ => - rename_i a y b z + rename_i a y c z simp + cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) == true with + | true => + simp at x_indices /- Now we need to apply the generalized reduction lemma, and then do some arithmetic. -/ - sorry + rw [reduction_final_RAP n l₁ l₂ x (fun f => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] + { calc + RAPPORSingleSample n query num den h (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / + RAPPORSingleSample n query num den h (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) ≤ + ((1/2 + num / den) / (1/2 - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h + _ ≤ ENNReal.ofReal (Real.exp (2 * Real.log ((↑↑↑den + 2 * ↑num) / (↑↑↑den - 2 * ↑num)))) := by sorry + } + { + intro k bo hbo + rw [RAPPORSingleSample] + apply RRSamplePushForward_non_zero + exact T + aesop + } + { intro k bo + rw [RAPPORSingleSample] + apply RRSamplePushForward_finite + exact T + } + {apply x_indices} + | false => /- This part of the proof is completely disgusting, I am not proud of it -/ + simp at x_indices + cases x_indices with + | intro i hi => + have i_zero: RAPPORSingleSample n query num den h (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry)) = 0 := by + apply RAPPORSingleSample_diff_lengths n query num den h + simp + aesop + have len_sub_add: l₂.length - 1 + 1 = l₂.length := by + rw [Nat.sub_add_cancel] + rw [@Nat.succ_le_iff] + rw [hl₂] + rw [@List.length_append] + aesop + have numerator_zero: (∏' (i : Fin l₁.length), RAPPORSingleSample n query num den h l₁[i.val] x[i.val]) = 0 := by + rw [@tprod_fintype] + rw[Finset.prod_eq_zero_iff] + norm_num + have hl1len:l₁.length > 0 := by + rw[hl₁] + rw [@List.length_append] + aesop + use (Fin.ofNat' i.val (hl1len)) + apply RAPPORSingleSample_diff_lengths n query num den h + simp + have h_coe: i.val % l₁.length = i.val := by + rw [Nat.mod_eq] + have hival: i.val < l₁.length := by + conv => + enter [2] + rw [xlen1] + rw [←xlen2] + rw [←len_sub_add] + exact i.2 + aesop + conv => + enter[1, 2, 1, 2] + rw[h_coe] + aesop + rw [numerator_zero] + rw [@ENNReal.zero_div] + simp | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 have numerator_zero: RAPPORSample_PMF n query num den h l₁ x = 0 := RAPPORSample_diff_lengths n query num den h l₁ x xlen1 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 67abf565..09f68e55 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -628,7 +628,7 @@ lemma valid_index3 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b apply valid_index2 h1 i -lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / +lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by rw[tprod_fintype] rw[tprod_fintype] @@ -657,7 +657,6 @@ lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1 rw[Finset.prod_ne_zero_iff] intro i simp[nonzero] - rw[← lt_top_iff_ne_top] apply ENNReal.prod_lt_top intro i diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean new file mode 100644 index 00000000..7be4bc9f --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean @@ -0,0 +1,57 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.RandomizedResponseMain + +open RandomizedResponse + + +lemma fin_prod_cast_RAP {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : + ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by + subst h + simp + +lemma conversion_RAP {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang β): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by + rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] + simp + +lemma reduction2_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool))(h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) (hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0) (noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f n (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / + (∏' (i : Fin ((l₂.length-1)+1)), f n (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by + rw[tprod_fintype] + rw[tprod_fintype] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f n (l₁[b.val]'(valid_index2 h1 b)) (x[b.val]'(valid_index3 h1 hx b))) a.length] + have ind: a.length < x.length := by + rw[← hx] + rw[h1] + simp + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f n (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] + have helper: l₁.length - 1 = l₂.length - 1 := by aesop + have hlp: (∏ i : Fin (l₁.length - 1), f n l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f n l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by + apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper)) + simp[succHelp l₁ l₂ h1 h2] + intro i + congr + rw [← propext cast_eq_iff_heq] + rw [← propext cast_eq_iff_heq] + rw[hlp] + rw[ENNReal.mul_div_mul_right] + simp + + simp[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] + simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] + + rw[Finset.prod_ne_zero_iff] + intro i + aesop + rw[← lt_top_iff_ne_top] + apply ENNReal.prod_lt_top + intro i + simp[noninf] + +theorem reduction_final_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool)) (h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) (hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0)(noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f n (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f n (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by + have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith + have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith + rw[conversion_RAP l₂ x h2 hl2 hy (f n)] + rw[conversion_RAP l₁ x h1 hl1 hx (f n)] + rw [reduction2_RAP n l₁ l₂ x f h1 h2 hx hx2 hy nonzero noninf] + +open Finset +open scoped BigOperators From a232ea78dbb96ca7cda70401c0c3d1db9a47978f Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Tue, 29 Jul 2025 19:53:35 -0400 Subject: [PATCH 105/216] final bound sorry --- .../Pure/Local/ENNRealLemmasSuite.lean | 32 +++++++++++++++ .../Pure/Local/RAPPOR/Properties.lean | 39 ++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 6f356b31..ea2a54be 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -11,6 +11,36 @@ lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring +lemma le_add_non_zero (a b :ENNReal)(h: b ≠ 0)(h2: a ≠ ⊤): a < a+b := by + rw [@lt_iff_le_and_ne] + apply And.intro + simp_all + simp + rw [Not] + intro c + have gg : a + 0 =a +b := by + simp + exact c + rw [ENNReal.add_right_inj] at gg + symm at gg + subst gg + have hh: (0 ≠ 0) → False := by simp + apply hh + exact_mod_cast h + exact h2 + +lemma sub_le_add_ennreal (a b :ENNReal)(h1: b ≠ 0)(h3: b ≤ a)(h4: a ≠ ⊤): a -b < a +b := by + apply ENNReal.sub_lt_of_lt_add + exact h3 + rw [add_assoc] + apply le_add_non_zero + simp_all only [ne_eq, add_eq_zero, and_self, not_false_eq_true] + exact h4 + + + + + lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): @@ -114,6 +144,8 @@ lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> exact h.left exact h.right + + lemma quot_gt_one_rev (a b : ENNReal): b < a -> 1 < a/b := by cases hb : b == 0 with | true => simp at hb diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index ee5c9d4c..5a0cd284 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -127,8 +127,43 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [same_answer] rw [@ENNReal.div_self] {rw [@sq] - aesop - sorry -- have a separate lemma that proves this + simp + cases frac_zero : num/den.val == (0:ENNReal) with + | true => + simp_all only [beq_iff_eq] + rw [@Decidable.le_iff_lt_or_eq] + right + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp + rw [ENNReal.div_self] + simp + simp + | false => + rw [@Decidable.le_iff_lt_or_eq] + left + apply ENNRealLemmas.quot_gt_one_rev + apply ENNRealLemmas.sub_le_add_ennreal + aesop + rw [@ENNReal.le_inv_iff_mul_le] + rw [@ENNReal.div_eq_inv_mul] + rw [mul_assoc] + rw [mul_comm] + rw [← @ENNReal.le_inv_iff_mul_le] + simp + rw [@Decidable.le_iff_lt_or_eq] + left + rw [@Nat.cast_comm] + norm_cast + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp + + -- have a separate lemma that proves this /- Probably for this we need a version of quot_gt_one_rev in ENNRealLemmasSuite-/ } From d72dbae38d605f5b4ea2277ddd6b1d492f0d6bdf Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 16:53:50 -0700 Subject: [PATCH 106/216] readytopull --- .../Pure/Local/RAPPOR/Properties.lean | 21 ++++--------------- .../Local/RandomizedResponse/Reduction.lean | 17 +++++++++++++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 7ac8cba5..4752ad77 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -212,7 +212,7 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d | Update hl₁ hl₂ => rename_i a y c z simp - cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) == true with + cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4 _ hl₂; apply xlen2)).length = n) == true with | true => simp at x_indices /- Now we need to apply the generalized reduction lemma, @@ -241,16 +241,6 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d simp at x_indices cases x_indices with | intro i hi => - have i_zero: RAPPORSingleSample n query num den h (l₁[i.val]'(by sorry)) (x[i.val]'(by sorry)) = 0 := by - apply RAPPORSingleSample_diff_lengths n query num den h - simp - aesop - have len_sub_add: l₂.length - 1 + 1 = l₂.length := by - rw [Nat.sub_add_cancel] - rw [@Nat.succ_le_iff] - rw [hl₂] - rw [@List.length_append] - aesop have numerator_zero: (∏' (i : Fin l₁.length), RAPPORSingleSample n query num den h l₁[i.val] x[i.val]) = 0 := by rw [@tprod_fintype] rw[Finset.prod_eq_zero_iff] @@ -265,12 +255,9 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d have h_coe: i.val % l₁.length = i.val := by rw [Nat.mod_eq] have hival: i.val < l₁.length := by - conv => - enter [2] - rw [xlen1] - rw [←xlen2] - rw [←len_sub_add] - exact i.2 + rw [xlen1] + apply valid_index4 _ hl₂ + exact xlen2 aesop conv => enter[1, 2, 1, 2] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean index 7be4bc9f..89a44364 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean @@ -12,7 +12,20 @@ lemma conversion_RAP {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] simp -lemma reduction2_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool))(h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) (hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0) (noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f n (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / +lemma fin_conv_helper (l₂ : List T) (hl₂: l₂ = a++[m]++b): l₂.length - 1 + 1 = l₂.length := by --this is useful later + rw [Nat.sub_add_cancel] + rw [@Nat.succ_le_iff] + rw [hl₂] + rw [@List.length_append] + aesop +lemma valid_index4 (l₂ : List T) (hl₂ : l₂ = a++[m]++b)(x : List (List Bool)) (i : Fin (l₂.length - 1 + 1)) (xlen2 : l₂.length = x.length): i.val < x.length := by + conv => + enter [2] + rw [←xlen2] + rw [←fin_conv_helper _ hl₂] + exact i.2 + +lemma reduction2_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool))(h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4; apply h2; aesop)).length = n) (hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0) (noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f n (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / (∏' (i : Fin ((l₂.length-1)+1)), f n (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by rw[tprod_fintype] rw[tprod_fintype] @@ -45,7 +58,7 @@ lemma reduction2_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat intro i simp[noninf] -theorem reduction_final_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool)) (h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by sorry)).length = n) (hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0)(noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f n (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / +theorem reduction_final_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool)) (h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4; apply h2; aesop)).length = n) (hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0)(noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f n (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / (∏' (i : Fin (l₂.length)), f n (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith From 864b8dd99b0bf095ba484eb7156e0eb4e6cd7805 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 18:24:29 -0700 Subject: [PATCH 107/216] cleanedup --- .../Pure/Local/RAPPOR/Properties.lean | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index b6d498c5..d0ba4d2c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -142,23 +142,9 @@ lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ -/- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ -lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): - (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by - -- probably want to restate the bound in an arithmetically equivalent way - simp_all only [RAPPORSingleSample] - set ohv := one_hot n query v - set ohu := one_hot n query u - have oh_len: ohu.length = ohv.length := by simp[ohv, ohu] - cases hlen: ohv.length == b.length with - | true => - simp at hlen - cases h_eq: query v == query u with - | true => simp at h_eq - have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq - rw [same_answer] - rw [@ENNReal.div_self] - {rw [@sq] +lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): +(1 : ENNReal) ≤ ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by + rw [@sq] simp cases frac_zero : num/den.val == (0:ENNReal) with | true => @@ -195,10 +181,33 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den norm_cast simp - -- have a separate lemma that proves this - /- Probably for this we need a version of - quot_gt_one_rev in ENNRealLemmasSuite-/ - } +lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) + (f : α -> β -> ENNReal): + ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by + let e : Fin l.length ≃ Fin v.length := by + rw [h1] + apply Fintype.prod_equiv e + intro x + simp + sorry + +/- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ +lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): + (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by + -- probably want to restate the bound in an arithmetically equivalent way + simp_all only [RAPPORSingleSample] + set ohv := one_hot n query v + set ohu := one_hot n query u + have oh_len: ohu.length = ohv.length := by simp[ohv, ohu] + cases hlen: ohv.length == b.length with + | true => + simp at hlen + cases h_eq: query v == query u with + | true => simp at h_eq + have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq + rw [same_answer] + rw [@ENNReal.div_self] + {exact arith_1 num den h} /- The statement of arith_1 might have to change...-/ { apply RRSamplePushForward_non_zero exact T @@ -216,7 +225,11 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den simp_all [@tprod_fintype] have len_eq: ohu.length = ohv.length := by aesop have index_1: ∏ i : Fin ohv.length, RRSinglePushForward num den h ohv[i.val] b[i.val] = - ∏ i : Fin ohu.length, RRSinglePushForward num den h ohv[i.val] b[i.val] := by sorry + ∏ i : Fin ohu.length, RRSinglePushForward num den h ohv[i.val] b[i.val] := by + apply reindex + symm at len_eq + exact len_eq + exact hlen rw [index_1] rw [prod_over_prod] -- this needs proving sorry @@ -252,10 +265,21 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d simp at x_indices /- Now we need to apply the generalized reduction lemma, and then do some arithmetic. -/ + have valid_index5: a.length < l₁.length := by + rw [hl₁] + rw [@List.length_append] + aesop + linarith + have valid_index6: a.length < x.length := by + rw [←xlen1] + exact valid_index5 + have valid_index7: a.length < l₂.length := by + rw [xlen2] + exact valid_index6 rw [reduction_final_RAP n l₁ l₂ x (fun f => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] { calc - RAPPORSingleSample n query num den h (l₁[a.length]'(by sorry)) (x[a.length]'(by sorry)) / - RAPPORSingleSample n query num den h (l₂[a.length]'(by sorry)) (x[a.length]'(by sorry)) ≤ + RAPPORSingleSample n query num den h (l₁[a.length]'(valid_index5)) (x[a.length]'(valid_index6)) / + RAPPORSingleSample n query num den h (l₂[a.length]'(valid_index7)) (x[a.length]'(valid_index6)) ≤ ((1/2 + num / den) / (1/2 - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h _ ≤ ENNReal.ofReal (Real.exp (2 * Real.log ((↑↑↑den + 2 * ↑num) / (↑↑↑den - 2 * ↑num)))) := by sorry } From a9f3931787f8b2a9b6cb7372951d39ba9fc034b2 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 21:10:50 -0700 Subject: [PATCH 108/216] donefortoday --- .../Pure/Local/RAPPOR/Properties.lean | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index d0ba4d2c..9cc9dd05 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -181,15 +181,13 @@ lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): norm_cast simp +/- Good tip: use finCongr for re-indexing... -/ lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) (f : α -> β -> ENNReal): ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by - let e : Fin l.length ≃ Fin v.length := by - rw [h1] - apply Fintype.prod_equiv e + apply Fintype.prod_equiv (finCongr h1) intro x - simp - sorry + rfl /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): @@ -275,7 +273,7 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d exact valid_index5 have valid_index7: a.length < l₂.length := by rw [xlen2] - exact valid_index6 + exact valid_index6 rw [reduction_final_RAP n l₁ l₂ x (fun f => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] { calc RAPPORSingleSample n query num den h (l₁[a.length]'(valid_index5)) (x[a.length]'(valid_index6)) / From a1d153847fa042aff33380e7d6400e3f9066585b Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 29 Jul 2025 23:09:54 -0700 Subject: [PATCH 109/216] I lied --- .../Pure/Local/ENNRealLemmasSuite.lean | 4 ---- .../Pure/Local/RAPPOR/Properties.lean | 15 ++++++++++++++- .../RandomizedResponseMain.lean | 6 +++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index ea2a54be..a86644ae 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -37,10 +37,6 @@ lemma sub_le_add_ennreal (a b :ENNReal)(h1: b ≠ 0)(h3: b ≤ a)(h4: a ≠ ⊤) simp_all only [ne_eq, add_eq_zero, and_self, not_false_eq_true] exact h4 - - - - lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 9cc9dd05..606f35be 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -189,6 +189,13 @@ lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.le intro x rfl +lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) + (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): +∏ i : Fin (one_hot n query u).length, RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] + = RRSinglePushForward num den h (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) + * RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) + := by sorry + /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by @@ -230,6 +237,11 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den exact hlen rw [index_1] rw [prod_over_prod] -- this needs proving + simp_all only [ohv, ohu] + rw [single_DP_reduction n query num den h v u b oh_len hlen] + #check RRSamplePushForward_final_bound + /- now need a version of final_bound for RRPushForward -/ + /- use the "calc" tactic to prove this-/ sorry | false => simp at hlen @@ -266,7 +278,8 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d have valid_index5: a.length < l₁.length := by rw [hl₁] rw [@List.length_append] - aesop + simp_all only [List.append_assoc, List.singleton_append, List.length_append, List.length_cons, + List.length_singleton] linarith have valid_index6: a.length < x.length := by rw [←xlen1] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 09f68e55..6786dee1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -531,7 +531,11 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de simp apply pnat_zero_imp_false - +lemma RRSamplePushForward_final_bound (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : Bool) (b : Bool): + RRSinglePushForward num den h a b / RRSinglePushForward num den h a' b + ≤ (den + 2 * num) / (den - 2 * num) := by + rw [← RRSingleSample_is_RRSinglePushForward num den h] + apply final_bound lemma valid_index0 (l₁ : List T)(h1: l₁ = a++[n]++b) (i : Fin (l₁.length - 1)): (Fin.succAbove (a.length) i).val < l₁.length := by have hl : l₁.length - 1 + 1 = l₁.length := by From 3daa814da709d8ed8b5fcd7ea1d4eda08f0a0b78 Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 30 Jul 2025 09:51:17 -0700 Subject: [PATCH 110/216] Update dpproof --- .../Local/RandomizedResponse/DPProof.lean | 43 +++++++++++++++++-- .../RandomizedResponseMain.lean | 8 ++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 0ad43a2c..91294851 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -1,3 +1,4 @@ +import SampCert.Util.Log import Mathlib.Topology.Basic import Mathlib.Probability.ProbabilityMassFunction.Basic import Mathlib.Data.Real.Basic @@ -14,8 +15,10 @@ lemma numerator_pos (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num : exact add_pos_of_pos_of_nonneg den_real_pos two_num_nonneg lemma denominator_pos (num : ℕ) (den : PNat) (h : 2 * num < den) : (0 : ℝ) < ↑↑den - 2 * ↑num := by - rw [sub_pos] + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, sub_pos] norm_cast + exact Nat.cast_lt.mpr h + lemma step1 (num : Nat) (den : PNat) (h : 2 * num < den): ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by @@ -29,8 +32,9 @@ ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (R lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by rw [ENNReal.ofReal_div_of_pos] - · congr - norm_cast + · sorry + + · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h exact sub_pos.mpr foo @@ -38,3 +42,36 @@ lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by rw [← step1 num den h] exact step2 num den h + +lemma reduce (num : Nat) (den : PNat): +((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num): ℝ) = (1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den)) := by + simp + rw [inv_eq_one_div] + rw [div_add_div] + · rw [one_mul] + rw [div_sub_div] + rw [one_mul] + rw [div_div_div_eq] + nth_rewrite 2 [div_eq_mul_inv] + nth_rewrite 4 [mul_comm] + rw [← div_eq_mul_inv] + rw [mul_div_mul_left] + apply ne_of_gt + simp_all only [Nat.ofNat_pos, mul_pos_iff_of_pos_left, NNReal.coe_pos, Nat.cast_pos] + exact den.2 + aesop + apply ne_of_gt + simp_all only [NNReal.coe_pos, Nat.cast_pos] + exact den.2 + · aesop + · apply ne_of_gt + simp_all only [NNReal.coe_pos, Nat.cast_pos] + exact den.2 + + + +lemma final_coercion (num : Nat) (den : PNat) (h : 2 * num < den): +(↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) ≤ + ENNReal.ofReal (Real.exp (Real.log ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))))):= by + rw [final_step_combined num den h] + rw [reduce num den] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 30e85cc5..21b9ba0b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -756,7 +756,7 @@ open Finset open scoped BigOperators theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((den + 2 * num) / (den - 2 * num))) := by +DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by apply singleton_to_event_update intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with @@ -788,8 +788,10 @@ cases xlen1 : l₁.length == x.length with _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by /- apply final_step_combined exact h --/ - sorry - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop} + apply final_coercion + exact h + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop + } {apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} | false => simp at xlen1 From d3042f66e9d92bac4f2df71a69bae60efe7dba4e Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 30 Jul 2025 10:32:11 -0700 Subject: [PATCH 111/216] finish DPProof --- .../Local/RandomizedResponse/DPProof.lean | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean index 91294851..9dad3cc2 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean @@ -29,14 +29,32 @@ ENNReal.ofReal ((den + 2 * num) / (den - 2 * num)) = ENNReal.ofReal (Real.exp (R {exact numerator_pos num den} {exact denominator_pos num den h} +lemma ennreal_of_nat (n: Nat) : ↑n = ENNReal.ofReal (↑ n) := by simp_all only [ENNReal.ofReal_natCast] + +lemma ennreal_of_pnat (d : PNat) : ↑↑d = ENNReal.ofReal (↑↑d) := by simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, + ENNReal.ofReal_coe_nnreal] + + lemma step2 (num : Nat) (den : PNat) (h : 2 * num < den): (↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num) = ENNReal.ofReal ((↑↑den + 2 * ↑num) / (↑↑den - 2 * ↑num)) := by rw [ENNReal.ofReal_div_of_pos] - · sorry - - - · have foo : 2 * (num : ℝ) < (den : ℕ) := by exact_mod_cast h - exact sub_pos.mpr foo + rw [ennreal_of_nat] + rw [ennreal_of_pnat] + have h1 : 2 = ENNReal.ofReal (2) := by simp + rw [h1] + rw [← ENNReal.ofReal_mul] + rw [← ENNReal.ofReal_add] + rw [← ENNReal.ofReal_sub] + simp + simp + simp + simp + simp_all only [sub_pos] + norm_cast + rw [Mathlib.Tactic.Zify.natCast_lt] at h + simp_all only [Nat.cast_mul, Nat.cast_ofNat, NNReal.ofPNat, Nonneg.mk_natCast] + norm_cast + norm_cast at h lemma final_step_combined (num : Nat) (den : PNat) (h : 2 * num < den) : (den + (2: ENNReal) * num) / (den - (2 : ENNReal) * num) = ENNReal.ofReal (Real.exp (Real.log ((den + 2 * num) / (den - 2 * num)))) := by From 409971146847a358cc07e3621e8630b1a0928c5e Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 30 Jul 2025 11:04:26 -0700 Subject: [PATCH 112/216] reduction12 --- .../Pure/Local/RAPPOR/Properties.lean | 105 ++++++++++++++++-- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 606f35be..94d31aeb 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -100,6 +100,20 @@ lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) ( | inr h1 => aesop } +lemma one_hot_different_answer_ex_two_contrp {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j]'(by simp) = (one_hot n query u)[j]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: query v ≠ j ∧ query u ≠ j ↔ ¬ (query v = j ∨ query u = j) := by simp_all only [ne_eq, not_or] + rw [h1] + rw [←one_hot_different_answer_ex_two n query v u j h] + simp + +lemma one_hot_different_answer_ex_two_contrp' {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j.val]'(by simp) = (one_hot n query u)[j.val]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: (one_hot n query v)[j.val]'(by simp) = (one_hot n query v)[j]'(by simp) := by simp + have h2: (one_hot n query u)[j.val]'(by simp) = (one_hot n query u)[j]'(by simp) := by simp + rw [h1, h2] + rw [one_hot_different_answer_ex_two_contrp n query v u j h] + /- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob @@ -107,7 +121,7 @@ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num /- RRSamplePushForward gives a non-zero probability for an output of the same length. This is needed in the DP proof. -/ -lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): +lemma RRSamplePushForward_non_zero (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): RRSamplePushForward num den h l b ≠ 0 := by rw [RRSamplePushForward] rw [prod_of_ind_prob _ _ _ _ k] @@ -117,7 +131,7 @@ lemma RRSamplePushForward_non_zero {T : Type} (num : Nat) (den : PNat) (h: 2 * n apply RRSinglePushForward_non_zero /- RRSamplePushForward is always finite. This is needed in the DP proof. -/ -lemma RRSamplePushForward_finite {T : Type} (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): +lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): RRSamplePushForward num den h l b ≠ ⊤ := by cases hlen: l.length == b.length with | true => @@ -189,12 +203,86 @@ lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.le intro x rfl -lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) +lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) + (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): + RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / + RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] = + if query v = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by aesop)) + else if query u = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query u] (b[query u]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query u] (b[query u]'(by aesop)) + else 1 := by + cases hi : (finCongr (by aesop) i) == query v with + | true => simp at hi + have h1: i.val = (query v).val := by + rw [← hi] + simp + aesop + | false => simp at hi + cases hi2: (finCongr (by aesop) i) == query u with + | true => simp at hi2 + have h1: i.val = (query u).val := by + rw [← hi2] + simp + simp [h1, -one_hot] + simp_all only [not_false_eq_true, List.getElem_ofFn, Fin.eta, decide_True, + decide_False, ↓reduceIte] + split + next h_1 => + simp_all only [decide_True] + next h_1 => simp_all only [decide_False] + | false => simp at hi2 + simp_all only [List.getElem_ofFn, finCongr_apply, Fin.getElem_fin, Fin.coe_cast, + Fin.eta] + split + next h_1 => simp_all only [not_true_eq_false] + next h_1 => + split + next h_2 => simp_all only [not_true_eq_false] + next h_2 => + have h1: (one_hot n query v)[i.val]'(by omega) = (one_hot n query u)[i.val]'(by omega) := + by convert one_hot_different_answer_ex_two_contrp' n query v u (finCongr (by aesop) i) + aesop + -- simp_all only [ne_eq, not_or, not_and] + rw [h1] + rw [ENNReal.div_self] + apply RRSinglePushForward_non_zero + apply RRSinglePushForward_finite + + +lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) + (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): + (∏ i : Fin (one_hot n query u).length, + if query v = (finCongr (by aesop) i) then f (one_hot n query v)[query v] (b[query v]'(by aesop)) / f (one_hot n query u)[query v] (b[query v]'(by aesop)) + else if query u = (finCongr (by aesop) i) then f (one_hot n query v)[query u] (b[query u]'(by aesop)) / f (one_hot n query u)[query u] (b[query u]'(by aesop)) + else 1) = + f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) + * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) + * ∏ i : Fin (one_hot n query u).length, + if query v = (finCongr (by aesop) i) then 1 + else if query u = (finCongr (by aesop) i) then 1 + else 1 := by + simp[-one_hot] + rw? + sorry + +lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): ∏ i : Fin (one_hot n query u).length, RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] = RRSinglePushForward num den h (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) - * RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) - := by sorry + * RRSinglePushForward num den h (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) + := by + conv => + enter [1, 2, i] + rw [reduction_helper1 n query num den h v u b ohu_len onhv_len h_users i] + rw [reduction_helper2] + have reduction_helper3: + ∏ i : Fin (one_hot n query u).length, (if query v = (finCongr (by aesop) i) then 1 + else if query u = (finCongr (by aesop) i) then 1 else 1) = 1 := by simp + conv => + enter [1, 2] + simp [reduction_helper3] + simp_all only [mul_one] + exact ohu_len + exact onhv_len /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): @@ -215,12 +303,10 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den {exact arith_1 num den h} /- The statement of arith_1 might have to change...-/ { apply RRSamplePushForward_non_zero - exact T rw[←hlen] exact oh_len } { apply RRSamplePushForward_finite - exact T } | false => simp at h_eq @@ -238,10 +324,11 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [index_1] rw [prod_over_prod] -- this needs proving simp_all only [ohv, ohu] - rw [single_DP_reduction n query num den h v u b oh_len hlen] + rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] #check RRSamplePushForward_final_bound /- now need a version of final_bound for RRPushForward -/ /- use the "calc" tactic to prove this-/ + /- We should wait for Perryn to give an exact statement of the bound to match RR-/ sorry | false => simp at hlen @@ -298,13 +385,11 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d intro k bo hbo rw [RAPPORSingleSample] apply RRSamplePushForward_non_zero - exact T aesop } { intro k bo rw [RAPPORSingleSample] apply RRSamplePushForward_finite - exact T } {apply x_indices} | false => /- This part of the proof is completely disgusting, I am not proud of it -/ From 87577d3c76060bfe3d12a428af96187a501d2101 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 30 Jul 2025 11:21:21 -0700 Subject: [PATCH 113/216] readytoedit --- .../DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 94d31aeb..22ed3644 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -149,8 +149,7 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l simp lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): - (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by - sorry + (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by sorry /- lemma RAPPOR_cancel {T : Type} (n : Nat) (query : T -> Fin n) (num : Nat) (den : PNat) (h : 2 * num < den) (v u : T) (len_eq: (one_hot n query v).length = (one_hot n query u).length) (b : List Bool) (hlen: (one_hot n query u).length = b.length): ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) @@ -261,7 +260,6 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL else if query u = (finCongr (by aesop) i) then 1 else 1 := by simp[-one_hot] - rw? sorry lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) From 02230c1e76045c07b83100a647d470daa49d61c9 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 30 Jul 2025 20:08:48 -0700 Subject: [PATCH 114/216] donefor730 --- .../Pure/Local/RAPPOR/Properties.lean | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 22ed3644..6e2ad722 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -246,8 +246,7 @@ lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (de apply RRSinglePushForward_non_zero apply RRSinglePushForward_finite - -lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) +lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): (∏ i : Fin (one_hot n query u).length, if query v = (finCongr (by aesop) i) then f (one_hot n query v)[query v] (b[query v]'(by aesop)) / f (one_hot n query u)[query v] (b[query v]'(by aesop)) @@ -255,12 +254,41 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL else 1) = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) - * ∏ i : Fin (one_hot n query u).length, - if query v = (finCongr (by aesop) i) then 1 - else if query u = (finCongr (by aesop) i) then 1 - else 1 := by - simp[-one_hot] - sorry + := by + simp_all only [finCongr_apply, Fin.getElem_fin, Fin.coe_cast, List.getElem_ofFn, Fin.eta] + have h4 (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop + conv => + enter [1] + rw [@Finset.prod_ite] + simp [-one_hot] + rw [@Finset.prod_ite] + simp [-one_hot] + -- rw [Finset.prod_ite_ite_one] + -- rw [Finset.prod_set_coe] + simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] + have hblen : b.length = n := by aesop + have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop + have h6: (Finset.filter (fun x => query u = Fin.cast (by sorry) x) (Finset.filter (fun x => ¬query v = Fin.cast (by sorry) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by + rw [@Finset.card_eq_one] + use (finCongr (by aesop) (query u)) + aesop + have h8: ∏ x ∈ Finset.filter (fun x => query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)), + f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) + = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) := by + subst hblen + simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] + conv => + enter [1, 2] + simp + simp + have h9: ∏ x ∈ Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) Finset.univ : Finset (Fin (one_hot n query u).length)), + f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) + = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) := by + simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] + simp + rw [h8] + rw [h9] + rw [@mul_div] lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): @@ -271,15 +299,11 @@ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) ( conv => enter [1, 2, i] rw [reduction_helper1 n query num den h v u b ohu_len onhv_len h_users i] - rw [reduction_helper2] + rw [reduction_helper2 _ _ _ _ _ _ h_users] have reduction_helper3: ∏ i : Fin (one_hot n query u).length, (if query v = (finCongr (by aesop) i) then 1 else if query u = (finCongr (by aesop) i) then 1 else 1) = 1 := by simp - conv => - enter [1, 2] - simp [reduction_helper3] simp_all only [mul_one] - exact ohu_len exact onhv_len /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ From fbd503f4fd5aab48182376a7dc9d2e1af23b4032 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Thu, 31 Jul 2025 12:23:05 -0400 Subject: [PATCH 115/216] work on sorry --- .../Pure/Local/ENNRealLemmasSuite.lean | 2 ++ .../Pure/Local/RAPPOR/Properties.lean | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index a86644ae..2f49d8e4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -246,5 +246,7 @@ lemma sub_add_cancel_ennreal (a b :ENNReal)(h:b≤ a)(h1 : b ≠ ⊤): a -b +b = exact h exact h1 +lemma le_double (a b : ENNReal)(h: a ≤ b): a * a ≤ b * b := by sorry + end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 22ed3644..e98fde3d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -156,7 +156,7 @@ lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): -(1 : ENNReal) ≤ ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by +(1 : ENNReal) ≤ ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by rw [@sq] simp cases frac_zero : num/den.val == (0:ENNReal) with @@ -284,7 +284,7 @@ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) ( /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): - (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by + (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by -- probably want to restate the bound in an arithmetically equivalent way simp_all only [RAPPORSingleSample] set ohv := one_hot n query v @@ -323,11 +323,30 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [prod_over_prod] -- this needs proving simp_all only [ohv, ohu] rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] - #check RRSamplePushForward_final_bound + rw [@mul_div_assoc] + + rw [← @Fin.getElem_fin] + rw [← @Fin.getElem_fin] + rw [← @Fin.getElem_fin] + rw [← @Fin.getElem_fin] + rw [← @Fin.getElem_fin] + rw [← @Fin.getElem_fin] + have single : RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by sorry)) / + RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by sorry)) ≤ + (den + 2 * num) / (den - 2 * num) := by + apply RRSamplePushForward_final_bound + + rw [@sq] + have exp_eq : (den + 2 * num) / (den - 2 * num) = ((2:ENNReal)⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den) := by sorry + rw [exp_eq] at single + + + --#check RRSamplePushForward_final_bound + /- now need a version of final_bound for RRPushForward -/ /- use the "calc" tactic to prove this-/ /- We should wait for Perryn to give an exact statement of the bound to match RR-/ - sorry + | false => simp at hlen have h1: RRSamplePushForward num den h ohv b = 0 := RAPPORSingleSample_diff_lengths n query num den h v b hlen From 3fae0401fb0becc0a857e4a2e26bba3ff3e0145e Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 10:06:16 -0700 Subject: [PATCH 116/216] beforemerge --- .../Pure/Local/RAPPOR/Properties.lean | 36 ++++++++++++++++--- .../RandomizedResponseMain.lean | 4 +-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 6e2ad722..45659b06 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -308,7 +308,7 @@ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) ( /- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): - (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((1/2 + num / den) / (1/2 - num / den))^2 := by + (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by -- probably want to restate the bound in an arithmetically equivalent way simp_all only [RAPPORSingleSample] set ohv := one_hot n query v @@ -322,7 +322,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq rw [same_answer] rw [@ENNReal.div_self] - {exact arith_1 num den h} /- The statement of arith_1 might have to change...-/ + {sorry} /- The statement of arith_1 might have to change...-/ { apply RRSamplePushForward_non_zero rw[←hlen] @@ -362,9 +362,35 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den #check Real.log_rpow -- we'll need this later +lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): + 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by + rw [←Real.log_rpow] + simp + sorry + +lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): + Real.exp (Real.log (((2⁻¹ + num / den) / (2⁻¹ - num / den))^2)) = ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by + rw [Real.exp_log] + sorry + + +lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): + ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by + conv => + enter [2, 1, 1] + rw [log_rw] + rfl + exact h + conv => + enter [2, 1] + -- rw [Real.exp_log] + rw [exp_rw num den h] + rw [ENNReal.ofReal] + sorry + /- This extends the previous lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): - DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((den + 2 * num) / (den - 2 * num))) + DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((2⁻¹ + num/den) / (2⁻¹ - num/den))) := by apply singleton_to_event_update intros l₁ l₂ h_adj x @@ -400,8 +426,8 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d { calc RAPPORSingleSample n query num den h (l₁[a.length]'(valid_index5)) (x[a.length]'(valid_index6)) / RAPPORSingleSample n query num den h (l₂[a.length]'(valid_index7)) (x[a.length]'(valid_index6)) ≤ - ((1/2 + num / den) / (1/2 - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h - _ ≤ ENNReal.ofReal (Real.exp (2 * Real.log ((↑↑↑den + 2 * ↑num) / (↑↑↑den - 2 * ↑num)))) := by sorry + ((2⁻¹ + num / den) / (2⁻¹ - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h + _ = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by rw[←arith_2 num den h] } { intro k bo hbo diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index be739c5e..6f12641d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -687,7 +687,7 @@ open Finset open scoped BigOperators theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by +DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by apply singleton_to_event_update intros l₁ l₂ h_adj x cases xlen1 : l₁.length == x.length with @@ -721,7 +721,7 @@ cases xlen1 : l₁.length == x.length with exact h --/ apply final_coercion exact h - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den)))) := by aesop + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by aesop } {apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} From 986cd96bae3658cbf9a9ac0ff3f18f4003d5b489 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 31 Jul 2025 10:06:22 -0700 Subject: [PATCH 117/216] Prod_over_prod lemma --- .../Pure/Local/RAPPOR/Properties.lean | 157 ------------------ 1 file changed, 157 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 6e2ad722..d6c2835f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -100,20 +100,6 @@ lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) ( | inr h1 => aesop } -lemma one_hot_different_answer_ex_two_contrp {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): - (one_hot n query v)[j]'(by simp) = (one_hot n query u)[j]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by - have h1: query v ≠ j ∧ query u ≠ j ↔ ¬ (query v = j ∨ query u = j) := by simp_all only [ne_eq, not_or] - rw [h1] - rw [←one_hot_different_answer_ex_two n query v u j h] - simp - -lemma one_hot_different_answer_ex_two_contrp' {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): - (one_hot n query v)[j.val]'(by simp) = (one_hot n query u)[j.val]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by - have h1: (one_hot n query v)[j.val]'(by simp) = (one_hot n query v)[j]'(by simp) := by simp - have h2: (one_hot n query u)[j.val]'(by simp) = (one_hot n query u)[j]'(by simp) := by simp - rw [h1, h2] - rw [one_hot_different_answer_ex_two_contrp n query v u j h] - /- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob @@ -151,144 +137,10 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by sorry -/- lemma RAPPOR_cancel {T : Type} (n : Nat) (query : T -> Fin n) (num : Nat) (den : PNat) (h : 2 * num < den) (v u : T) (len_eq: (one_hot n query v).length = (one_hot n query u).length) (b : List Bool) (hlen: (one_hot n query u).length = b.length): - ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) - / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ -lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): -(1 : ENNReal) ≤ ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by - rw [@sq] - simp - cases frac_zero : num/den.val == (0:ENNReal) with - | true => - simp_all only [beq_iff_eq] - rw [@Decidable.le_iff_lt_or_eq] - right - simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, - Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] - rw [← ENNReal.coe_two] - norm_cast - simp - rw [ENNReal.div_self] - simp - simp - | false => - rw [@Decidable.le_iff_lt_or_eq] - left - apply ENNRealLemmas.quot_gt_one_rev - apply ENNRealLemmas.sub_le_add_ennreal - aesop - rw [@ENNReal.le_inv_iff_mul_le] - rw [@ENNReal.div_eq_inv_mul] - rw [mul_assoc] - rw [mul_comm] - rw [← @ENNReal.le_inv_iff_mul_le] - simp - rw [@Decidable.le_iff_lt_or_eq] - left - rw [@Nat.cast_comm] - norm_cast - simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, - Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] - rw [← ENNReal.coe_two] - norm_cast - simp -/- Good tip: use finCongr for re-indexing... -/ -lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) - (f : α -> β -> ENNReal): - ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by - apply Fintype.prod_equiv (finCongr h1) - intro x - rfl -lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) - (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): - RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / - RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] = - if query v = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by aesop)) - else if query u = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query u] (b[query u]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query u] (b[query u]'(by aesop)) - else 1 := by - cases hi : (finCongr (by aesop) i) == query v with - | true => simp at hi - have h1: i.val = (query v).val := by - rw [← hi] - simp - aesop - | false => simp at hi - cases hi2: (finCongr (by aesop) i) == query u with - | true => simp at hi2 - have h1: i.val = (query u).val := by - rw [← hi2] - simp - simp [h1, -one_hot] - simp_all only [not_false_eq_true, List.getElem_ofFn, Fin.eta, decide_True, - decide_False, ↓reduceIte] - split - next h_1 => - simp_all only [decide_True] - next h_1 => simp_all only [decide_False] - | false => simp at hi2 - simp_all only [List.getElem_ofFn, finCongr_apply, Fin.getElem_fin, Fin.coe_cast, - Fin.eta] - split - next h_1 => simp_all only [not_true_eq_false] - next h_1 => - split - next h_2 => simp_all only [not_true_eq_false] - next h_2 => - have h1: (one_hot n query v)[i.val]'(by omega) = (one_hot n query u)[i.val]'(by omega) := - by convert one_hot_different_answer_ex_two_contrp' n query v u (finCongr (by aesop) i) - aesop - -- simp_all only [ne_eq, not_or, not_and] - rw [h1] - rw [ENNReal.div_self] - apply RRSinglePushForward_non_zero - apply RRSinglePushForward_finite -lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) (h_users: query u ≠ query v) - (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): - (∏ i : Fin (one_hot n query u).length, - if query v = (finCongr (by aesop) i) then f (one_hot n query v)[query v] (b[query v]'(by aesop)) / f (one_hot n query u)[query v] (b[query v]'(by aesop)) - else if query u = (finCongr (by aesop) i) then f (one_hot n query v)[query u] (b[query u]'(by aesop)) / f (one_hot n query u)[query u] (b[query u]'(by aesop)) - else 1) = - f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) - * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) - := by - simp_all only [finCongr_apply, Fin.getElem_fin, Fin.coe_cast, List.getElem_ofFn, Fin.eta] - have h4 (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop - conv => - enter [1] - rw [@Finset.prod_ite] - simp [-one_hot] - rw [@Finset.prod_ite] - simp [-one_hot] - -- rw [Finset.prod_ite_ite_one] - -- rw [Finset.prod_set_coe] - simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] - have hblen : b.length = n := by aesop - have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop - have h6: (Finset.filter (fun x => query u = Fin.cast (by sorry) x) (Finset.filter (fun x => ¬query v = Fin.cast (by sorry) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by - rw [@Finset.card_eq_one] - use (finCongr (by aesop) (query u)) - aesop - have h8: ∏ x ∈ Finset.filter (fun x => query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) - = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) := by - subst hblen - simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] - conv => - enter [1, 2] - simp - simp - have h9: ∏ x ∈ Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) - = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) := by - simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] - simp - rw [h8] - rw [h9] - rw [@mul_div] lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): @@ -345,12 +197,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den exact hlen rw [index_1] rw [prod_over_prod] -- this needs proving - simp_all only [ohv, ohu] - rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] - #check RRSamplePushForward_final_bound - /- now need a version of final_bound for RRPushForward -/ - /- use the "calc" tactic to prove this-/ - /- We should wait for Perryn to give an exact statement of the bound to match RR-/ sorry | false => simp at hlen @@ -379,9 +225,6 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d | Update hl₁ hl₂ => rename_i a y c z simp - cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4 _ hl₂; apply xlen2)).length = n) == true with - | true => - simp at x_indices /- Now we need to apply the generalized reduction lemma, and then do some arithmetic. -/ have valid_index5: a.length < l₁.length := by From 38aa0d27fcb91e32076ac27aedf7e500971e0a2a Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 31 Jul 2025 10:08:55 -0700 Subject: [PATCH 118/216] prod_over_prod --- .../Pure/Local/ENNRealLemmasSuite.lean | 28 --- .../Pure/Local/RAPPOR/Properties.lean | 196 +++++++++++++++++- 2 files changed, 194 insertions(+), 30 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index a86644ae..6f356b31 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -11,32 +11,6 @@ lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring -lemma le_add_non_zero (a b :ENNReal)(h: b ≠ 0)(h2: a ≠ ⊤): a < a+b := by - rw [@lt_iff_le_and_ne] - apply And.intro - simp_all - simp - rw [Not] - intro c - have gg : a + 0 =a +b := by - simp - exact c - rw [ENNReal.add_right_inj] at gg - symm at gg - subst gg - have hh: (0 ≠ 0) → False := by simp - apply hh - exact_mod_cast h - exact h2 - -lemma sub_le_add_ennreal (a b :ENNReal)(h1: b ≠ 0)(h3: b ≤ a)(h4: a ≠ ⊤): a -b < a +b := by - apply ENNReal.sub_lt_of_lt_add - exact h3 - rw [add_assoc] - apply le_add_non_zero - simp_all only [ne_eq, add_eq_zero, and_self, not_false_eq_true] - exact h4 - lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): @@ -140,8 +114,6 @@ lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> exact h.left exact h.right - - lemma quot_gt_one_rev (a b : ENNReal): b < a -> 1 < a/b := by cases hb : b == 0 with | true => simp at hb diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index d6c2835f..1938c426 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -100,6 +100,20 @@ lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) ( | inr h1 => aesop } +lemma one_hot_different_answer_ex_two_contrp {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j]'(by simp) = (one_hot n query u)[j]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: query v ≠ j ∧ query u ≠ j ↔ ¬ (query v = j ∨ query u = j) := by simp_all only [ne_eq, not_or] + rw [h1] + rw [←one_hot_different_answer_ex_two n query v u j h] + simp + +lemma one_hot_different_answer_ex_two_contrp' {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j.val]'(by simp) = (one_hot n query u)[j.val]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: (one_hot n query v)[j.val]'(by simp) = (one_hot n query v)[j]'(by simp) := by simp + have h2: (one_hot n query u)[j.val]'(by simp) = (one_hot n query u)[j]'(by simp) := by simp + rw [h1, h2] + rw [one_hot_different_answer_ex_two_contrp n query v u j h] + /- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob @@ -134,13 +148,180 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l rw [hzero] simp -lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): - (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by sorry + lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal)(nonzero: ∀i, g i ≠ 0)(noninf: ∀i, g i ≠ ⊤): + (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by + induction n with + | zero => simp + | succ m ih => + conv => + enter[2] + simp [@Fin.prod_univ_add] + rw[← ih] + simp [@Fin.prod_univ_add] + conv => + enter[1] + rw[div_eq_mul_inv] + rw[ENNReal.mul_inv] + rw[mul_assoc] + conv => + enter[2] + rw[mul_comm] + rw[← mul_assoc] + rw[← mul_assoc] + conv => + enter [1,1] + rw[mul_comm] + rw[← ENNReal.div_eq_inv_mul] + rw[mul_assoc] + rw[← ENNReal.div_eq_inv_mul] + rfl + apply Or.inr + apply noninf + apply Or.inr + apply nonzero + intro i + apply nonzero + intro i + apply noninf +/- lemma RAPPOR_cancel {T : Type} (n : Nat) (query : T -> Fin n) (num : Nat) (den : PNat) (h : 2 * num < den) (v u : T) (len_eq: (one_hot n query v).length = (one_hot n query u).length) (b : List Bool) (hlen: (one_hot n query u).length = b.length): + ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) + / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ +lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): +(1 : ENNReal) ≤ ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by + rw [@sq] + simp + cases frac_zero : num/den.val == (0:ENNReal) with + | true => + simp_all only [beq_iff_eq] + rw [@Decidable.le_iff_lt_or_eq] + right + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp + rw [ENNReal.div_self] + simp + simp + | false => + rw [@Decidable.le_iff_lt_or_eq] + left + apply ENNRealLemmas.quot_gt_one_rev + apply ENNRealLemmas.sub_le_add_ennreal + aesop + rw [@ENNReal.le_inv_iff_mul_le] + rw [@ENNReal.div_eq_inv_mul] + rw [mul_assoc] + rw [mul_comm] + rw [← @ENNReal.le_inv_iff_mul_le] + simp + rw [@Decidable.le_iff_lt_or_eq] + left + rw [@Nat.cast_comm] + norm_cast + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp +/- Good tip: use finCongr for re-indexing... -/ +lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) + (f : α -> β -> ENNReal): + ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by + apply Fintype.prod_equiv (finCongr h1) + intro x + rfl +lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) + (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): + RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / + RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] = + if query v = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by aesop)) + else if query u = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query u] (b[query u]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query u] (b[query u]'(by aesop)) + else 1 := by + cases hi : (finCongr (by aesop) i) == query v with + | true => simp at hi + have h1: i.val = (query v).val := by + rw [← hi] + simp + aesop + | false => simp at hi + cases hi2: (finCongr (by aesop) i) == query u with + | true => simp at hi2 + have h1: i.val = (query u).val := by + rw [← hi2] + simp + simp [h1, -one_hot] + simp_all only [not_false_eq_true, List.getElem_ofFn, Fin.eta, decide_True, + decide_False, ↓reduceIte] + split + next h_1 => + simp_all only [decide_True] + next h_1 => simp_all only [decide_False] + | false => simp at hi2 + simp_all only [List.getElem_ofFn, finCongr_apply, Fin.getElem_fin, Fin.coe_cast, + Fin.eta] + split + next h_1 => simp_all only [not_true_eq_false] + next h_1 => + split + next h_2 => simp_all only [not_true_eq_false] + next h_2 => + have h1: (one_hot n query v)[i.val]'(by omega) = (one_hot n query u)[i.val]'(by omega) := + by convert one_hot_different_answer_ex_two_contrp' n query v u (finCongr (by aesop) i) + aesop + -- simp_all only [ne_eq, not_or, not_and] + rw [h1] + rw [ENNReal.div_self] + apply RRSinglePushForward_non_zero + apply RRSinglePushForward_finite +lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) (h_users: query u ≠ query v) + (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): + (∏ i : Fin (one_hot n query u).length, + if query v = (finCongr (by aesop) i) then f (one_hot n query v)[query v] (b[query v]'(by aesop)) / f (one_hot n query u)[query v] (b[query v]'(by aesop)) + else if query u = (finCongr (by aesop) i) then f (one_hot n query v)[query u] (b[query u]'(by aesop)) / f (one_hot n query u)[query u] (b[query u]'(by aesop)) + else 1) = + f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) + * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) + := by + simp_all only [finCongr_apply, Fin.getElem_fin, Fin.coe_cast, List.getElem_ofFn, Fin.eta] + have h4 (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop + conv => + enter [1] + rw [@Finset.prod_ite] + simp [-one_hot] + rw [@Finset.prod_ite] + simp [-one_hot] + -- rw [Finset.prod_ite_ite_one] + -- rw [Finset.prod_set_coe] + simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] + have hblen : b.length = n := by aesop + have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop + have h6: (Finset.filter (fun x => query u = Fin.cast (by sorry) x) (Finset.filter (fun x => ¬query v = Fin.cast (by sorry) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by + rw [@Finset.card_eq_one] + use (finCongr (by aesop) (query u)) + aesop + have h8: ∏ x ∈ Finset.filter (fun x => query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)), + f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) + = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) := by + subst hblen + simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] + conv => + enter [1, 2] + simp + simp + have h9: ∏ x ∈ Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) Finset.univ : Finset (Fin (one_hot n query u).length)), + f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) + = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) := by + simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] + simp + rw [h8] + rw [h9] + rw [@mul_div] lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): @@ -197,6 +378,14 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den exact hlen rw [index_1] rw [prod_over_prod] -- this needs proving + simp_all only [ohv, ohu] + rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] + #check RRSamplePushForward_final_bound + /- now need a version of final_bound for RRPushForward -/ + /- use the "calc" tactic to prove this-/ + /- We should wait for Perryn to give an exact statement of the bound to match RR-/ + sorry + sorry sorry | false => simp at hlen @@ -225,6 +414,9 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d | Update hl₁ hl₂ => rename_i a y c z simp + cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4 _ hl₂; apply xlen2)).length = n) == true with + | true => + simp at x_indices /- Now we need to apply the generalized reduction lemma, and then do some arithmetic. -/ have valid_index5: a.length < l₁.length := by From 4cbe85579bb1754b54e01331c74d3e7d7c9f4993 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 31 Jul 2025 10:09:16 -0700 Subject: [PATCH 119/216] prod_over_prod --- .../Pure/Local/ENNRealLemmasSuite.lean | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 6f356b31..a86644ae 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -11,6 +11,32 @@ lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring +lemma le_add_non_zero (a b :ENNReal)(h: b ≠ 0)(h2: a ≠ ⊤): a < a+b := by + rw [@lt_iff_le_and_ne] + apply And.intro + simp_all + simp + rw [Not] + intro c + have gg : a + 0 =a +b := by + simp + exact c + rw [ENNReal.add_right_inj] at gg + symm at gg + subst gg + have hh: (0 ≠ 0) → False := by simp + apply hh + exact_mod_cast h + exact h2 + +lemma sub_le_add_ennreal (a b :ENNReal)(h1: b ≠ 0)(h3: b ≤ a)(h4: a ≠ ⊤): a -b < a +b := by + apply ENNReal.sub_lt_of_lt_add + exact h3 + rw [add_assoc] + apply le_add_non_zero + simp_all only [ne_eq, add_eq_zero, and_self, not_false_eq_true] + exact h4 + lemma mult_ne_zero (a b : ENNReal) (h1 : a ≠ 0) (h2 : b ≠ 0): a * b ≠ 0 := by aesop lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): @@ -114,6 +140,8 @@ lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> exact h.left exact h.right + + lemma quot_gt_one_rev (a b : ENNReal): b < a -> 1 < a/b := by cases hb : b == 0 with | true => simp at hb From 2aa1f44620b866d8a8e04d7e28f82cc7a917d4fe Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 10:12:53 -0700 Subject: [PATCH 120/216] mergedwithAras --- .../Pure/Local/RAPPOR/Properties.lean | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index bb7342af..889e6d8d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -385,8 +385,10 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den /- use the "calc" tactic to prove this-/ /- We should wait for Perryn to give an exact statement of the bound to match RR-/ sorry - sorry - sorry + intro i + apply RRSinglePushForward_non_zero + intro i + apply RRSinglePushForward_finite | false => simp at hlen have h1: RRSamplePushForward num den h ohv b = 0 := RAPPORSingleSample_diff_lengths n query num den h v b hlen @@ -394,7 +396,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.zero_div] simp - #check Real.log_rpow -- we'll need this later lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): @@ -423,7 +424,7 @@ lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): rw [ENNReal.ofReal] sorry -/- This extends the previous lemma to a dataset of arbitrary size -/ +/- This extends the previous DP lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((2⁻¹ + num/den) / (2⁻¹ - num/den))) := by From afef1a792fe8ce879611d246ec38b83c2b6c05c1 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 10:16:51 -0700 Subject: [PATCH 121/216] arithmetic --- .../Pure/Local/RAPPOR/Properties.lean | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 889e6d8d..8f09ea08 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -402,11 +402,23 @@ lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by rw [←Real.log_rpow] simp + rw [@div_pos_iff] + apply Or.inl + apply And.intro + norm_num + rw [@one_div] + sorry + norm_num + simp_all only [one_div] sorry lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): Real.exp (Real.log (((2⁻¹ + num / den) / (2⁻¹ - num / den))^2)) = ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by rw [Real.exp_log] + rw [@sq_pos_iff] + rw [@div_ne_zero_iff] + apply And.intro + sorry sorry From ebf9ca7953ea062984a25eeff29e2980278be01e Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 12:39:11 -0700 Subject: [PATCH 122/216] morearith --- .../Pure/Local/RAPPOR/Properties.lean | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 8f09ea08..e09f304c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -421,6 +421,52 @@ lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): sorry sorry +lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : +(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = + ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by + have h1: ENNReal.ofReal 2⁻¹ = (2⁻¹ : ENNReal) := by + field_simp + rw [ENNReal.ofReal_div_of_pos] + simp + linarith + have h2: (0 : ℝ) ≤ num / den.val := by + rw [@div_nonneg_iff] + apply Or.inl + apply And.intro + aesop + aesop + rw [ENNReal.ofReal_div_of_pos] + congr + { + rw [ennreal_of_nat] + rw [ennreal_of_pnat] + rw [ENNReal.ofReal_add] + rw [ENNReal.ofReal_div_of_pos] + norm_cast + rw [h1] + aesop + aesop + aesop + simp [h2] + } + { rw [ENNReal.ofReal_sub] + rw [h1] + rw [ENNReal.ofReal_div_of_pos] + aesop + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] + exact den.2 + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] + convert h2 + } + { rw [@sub_pos] + rw [←one_div] + sorry + } + +lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : +(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) * (((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = +ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) * ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by +rw [arith_2_helper num den h] lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by @@ -433,8 +479,18 @@ lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): enter [2, 1] -- rw [Real.exp_log] rw [exp_rw num den h] - rw [ENNReal.ofReal] - sorry + rw [@sq, @sq] + simp + rw [ENNReal.ofReal_mul] + convert arith_2_mult_helper num den h + -- now prove the single version, can use format similar to step 2 + { + rw [@div_nonneg_iff] + apply Or.inl + apply And.intro + {sorry} -- this is also the same as a previous proof + {sorry} -- this is exactly the same as the proof in helper + } /- This extends the previous DP lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): From 0c3b335b2a2e6d85f185a6830f4f6761349ed4ca Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 31 Jul 2025 12:39:47 -0700 Subject: [PATCH 123/216] Sorries --- .../Pure/Local/RAPPOR/Properties.lean | 22 +++++++++++++++++-- .../RandomizedResponseMain.lean | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 8f09ea08..3f0f4231 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -407,7 +407,7 @@ lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): apply And.intro norm_num rw [@one_div] - sorry + positivity norm_num simp_all only [one_div] sorry @@ -418,8 +418,26 @@ lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): rw [@sq_pos_iff] rw [@div_ne_zero_iff] apply And.intro + positivity + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq] + apply Aesop.BuiltinRules.not_intro + intro a + rw[sub_eq_zero] at a + rw [inv_eq_one_div] at a + rw [div_eq_div_iff] at a + rw[one_mul] at a + symm at a + rw [mul_comm] at a sorry - sorry + simp + aesop? + + + + + + + lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean index 6f12641d..82b3a2b5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean @@ -362,6 +362,7 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de intro b norm_cast + left simp rw[Not] From d43f8d0303d4f593ff6151c68322fbc8a92b55ee Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 13:37:28 -0700 Subject: [PATCH 124/216] newlemma --- .../Pure/Local/RAPPOR/Properties.lean | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index e030ce44..ea0daf62 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -398,6 +398,9 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den #check Real.log_rpow -- we'll need this later +lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): + num / den < (2⁻¹ : ℝ) := by sorry + lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by rw [←Real.log_rpow] @@ -410,7 +413,7 @@ lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): positivity norm_num simp_all only [one_div] - sorry + convert num_den_simper num den h lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): Real.exp (Real.log (((2⁻¹ + num / den) / (2⁻¹ - num / den))^2)) = ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by @@ -430,13 +433,7 @@ lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): rw [mul_comm] at a sorry simp - aesop? - - - - - - + aesop lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : @@ -477,8 +474,7 @@ lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : convert h2 } { rw [@sub_pos] - rw [←one_div] - sorry + convert num_den_simper num den h } lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : From 0faedb1843842b103229fa558c0ec2aaaefa65a9 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 13:51:17 -0700 Subject: [PATCH 125/216] lesssorries --- .../Pure/Local/RAPPOR/Properties.lean | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index ea0daf62..7a222acf 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -399,7 +399,17 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den #check Real.log_rpow -- we'll need this later lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): - num / den < (2⁻¹ : ℝ) := by sorry + num / den < (2⁻¹ : ℝ) := by + rw [@div_lt_iff] + have h1 : 2 * (num : ℝ) < den.val := by exact_mod_cast h + have h2: 2 * (num : ℝ) < den := by aesop + have h3 : 2⁻¹ * (2 * (num : ℝ)) < 2⁻¹ * den := by + rw [@mul_lt_mul_left] + apply h2 + aesop + aesop + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] + exact den.2 lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by @@ -497,13 +507,16 @@ lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): simp rw [ENNReal.ofReal_mul] convert arith_2_mult_helper num den h - -- now prove the single version, can use format similar to step 2 { rw [@div_nonneg_iff] apply Or.inl apply And.intro - {sorry} -- this is also the same as a previous proof - {sorry} -- this is exactly the same as the proof in helper + {positivity} + { rw [@sub_nonneg] + rw [@Decidable.le_iff_lt_or_eq] + apply Or.inl + convert num_den_simper num den h + } } /- This extends the previous DP lemma to a dataset of arbitrary size -/ From 3961ce66f87786294cbc533efe90c700d12c61ae Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Thu, 31 Jul 2025 17:24:38 -0400 Subject: [PATCH 126/216] RapporSingleDP --- .../Pure/Local/ENNRealLemmasSuite.lean | 46 ++++++++++++++++++- .../Pure/Local/RAPPOR/Properties.lean | 27 ++++++----- .../Local/RandomizedResponse/BasicLemmas.lean | 15 ++++++ 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 2f49d8e4..a164095f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -246,7 +246,51 @@ lemma sub_add_cancel_ennreal (a b :ENNReal)(h:b≤ a)(h1 : b ≠ ⊤): a -b +b = exact h exact h1 -lemma le_double (a b : ENNReal)(h: a ≤ b): a * a ≤ b * b := by sorry + +lemma le_double (a b c : ENNReal)(h1 : a ≤ b)(h2 : c ≤ d)(htop1: a ≠ ⊤)(htop2 : c ≠ ⊤): a * c ≤ b * d := by + rw [@Decidable.le_iff_eq_or_lt] + rw [@Decidable.le_iff_eq_or_lt] at h1 + rw [@Decidable.le_iff_eq_or_lt] at h2 + cases h1 with + | inl h1l => + cases h2 with + | inl h2l => + left + rw [h1l] + rw [h2l] + | inr h2r => + cases bzero : b == 0 with + | true => + left + subst h1l + simp_all only [ne_eq, not_false_eq_true, beq_iff_eq, zero_mul] + | false => + right + subst h1l + simp_all only [beq_eq_false_iff_ne] + rw [propext (ENNReal.mul_lt_mul_left bzero htop1)] + exact h2r + | inr hr => + cases h2 with + | inl h2l => + rw [← h2l] + cases czero : c == 0 with + | true => + left + subst h2l + simp_all only [ne_eq, beq_iff_eq, mul_zero] + | false => + right + rw [@beq_eq_false_iff_ne] at czero + rw [propext (ENNReal.mul_lt_mul_right czero htop2)] + exact hr + | inr h2r => + right + apply ENNReal.mul_lt_mul + exact hr + exact h2r + +lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal)⁻¹ + num / den) / (2⁻¹ - num / den) = (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := by sorry end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 71869e0e..bf4b7f26 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -148,6 +148,8 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l rw [hzero] simp + + lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal): (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by sorry @@ -348,24 +350,21 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den simp_all only [ohv, ohu] rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] rw [@mul_div_assoc] + rw [@sq] + have exp_eq : ((2:ENNReal)⁻¹ + num / den) / (2⁻¹ - num / den) = (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := by + apply ENNRealLemmas.exp_change_form + exact h + simp at exp_eq + rw [exp_eq] + apply ENNRealLemmas.le_double + apply RRSamplePushForward_final_bound + apply RRSamplePushForward_final_bound + apply RRSinglePushForward_div_finite + apply RRSinglePushForward_div_finite - rw [← @Fin.getElem_fin] - rw [← @Fin.getElem_fin] - rw [← @Fin.getElem_fin] - rw [← @Fin.getElem_fin] - rw [← @Fin.getElem_fin] - rw [← @Fin.getElem_fin] - have single : RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by sorry)) / - RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by sorry)) ≤ - (den + 2 * num) / (den - 2 * num) := by - apply RRSamplePushForward_final_bound - rw [@sq] - have exp_eq : (den + 2 * num) / (den - 2 * num) = ((2:ENNReal)⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den) := by sorry - rw [exp_eq] at single - --#check RRSamplePushForward_final_bound /- now need a version of final_bound for RRPushForward -/ /- use the "calc" tactic to prove this-/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean index c417ced8..caa19d2b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean @@ -226,6 +226,21 @@ lemma RRSinglePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l rw [←RRSingleSample_is_RRSinglePushForward] apply RRSingleSample_finite +lemma RRSinglePushForward_div_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ l₂ : Bool) (b : Bool): + RRSinglePushForward num den h l₁ b / RRSinglePushForward num den h l₂ b ≠ ⊤ := by + simp + rw [Not] + intro h1 + rw [@ENNReal.div_eq_top] at h1 + cases h1 with + | inl hl => + apply And.right at hl + have hcontr : RRSinglePushForward num den h l₂ b ≠ 0 := by apply RRSinglePushForward_non_zero (fun x : Bool => x) + contradiction + | inr hr => + apply And.left at hr + have hcontr: RRSinglePushForward num den h l₁ b ≠ ⊤ := by apply RRSinglePushForward_finite + contradiction lemma RRSamplePushForward_diff_lengths (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List Bool) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): RRSamplePushForward num den h l₁ l₂ = 0 := by From 6c36f5ae4946409133d8208abd1ccb777ae7f894 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 14:25:58 -0700 Subject: [PATCH 127/216] readytomerge --- .../Pure/Local/RAPPOR/Properties.lean | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 7a222acf..1a86530d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -433,19 +433,10 @@ lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): apply And.intro positivity simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq] - apply Aesop.BuiltinRules.not_intro - intro a - rw[sub_eq_zero] at a - rw [inv_eq_one_div] at a - rw [div_eq_div_iff] at a - rw[one_mul] at a - symm at a - rw [mul_comm] at a - sorry - simp + rw [@sub_eq_zero] + have h1 : num / den < (2⁻¹ : ℝ) := num_den_simper num den h aesop - lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : (((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by From 3377944fc07a5e735e79cc59ca06cb51475b6c38 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 31 Jul 2025 14:39:23 -0700 Subject: [PATCH 128/216] sorries --- .../Pure/Local/RAPPOR/Properties.lean | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 7a222acf..66c8be32 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -301,13 +301,13 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] have hblen : b.length = n := by aesop have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop - have h6: (Finset.filter (fun x => query u = Fin.cast (by sorry) x) (Finset.filter (fun x => ¬query v = Fin.cast (by sorry) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by + have h6: (Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by rw [@Finset.card_eq_one] use (finCongr (by aesop) (query u)) aesop have h8: ∏ x ∈ Finset.filter (fun x => query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) - = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) := by + f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) + = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) := by subst hblen simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] conv => @@ -315,9 +315,11 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL simp simp have h9: ∏ x ∈ Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) - = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) := by - simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] + f (one_hot n query v)[(query u).val] (b[(query u).val]'(by + simp_all only [List.getElem_ofFn, Fin.eta, decide_True, decide_False, Fin.is_lt])) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by + simp_all only [one_hot, List.getElem_ofFn, Fin.eta, decide_True, decide_False, Fin.is_lt])) + = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) := by + simp_all only [List.getElem_ofFn, Fin.eta, decide_True, Finset.prod_const] simp rw [h8] rw [h9] @@ -326,8 +328,8 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): ∏ i : Fin (one_hot n query u).length, RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] - = RRSinglePushForward num den h (one_hot n query v)[(query v).val] (b[(query v).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query v).val] (b[(query v).val]'(by sorry)) - * RRSinglePushForward num den h (one_hot n query v)[(query u).val] (b[(query u).val]'(by sorry)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by sorry)) + = RRSinglePushForward num den h (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) + * RRSinglePushForward num den h (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) := by conv => enter [1, 2, i] @@ -397,7 +399,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den simp #check Real.log_rpow -- we'll need this later - lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): num / den < (2⁻¹ : ℝ) := by rw [@div_lt_iff] @@ -492,6 +493,9 @@ lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) * ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by rw [arith_2_helper num den h] + + + lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by conv => From 2545e62a6017145ee9925b13332d80da3610e52c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 31 Jul 2025 14:41:20 -0700 Subject: [PATCH 129/216] closetodone --- .../Pure/Local/RAPPOR/Properties.lean | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index e0c7df4b..e7269332 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -355,7 +355,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den have same_answer: ohv = ohu := one_hot_same_answer n query v u h_eq rw [same_answer] rw [@ENNReal.div_self] - {sorry} /- The statement of arith_1 might have to change...-/ + {apply arith_1 _ _ h} { apply RRSamplePushForward_non_zero rw[←hlen] @@ -392,15 +392,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den apply RRSamplePushForward_final_bound apply RRSinglePushForward_div_finite apply RRSinglePushForward_div_finite - - - - - - /- now need a version of final_bound for RRPushForward -/ - /- use the "calc" tactic to prove this-/ - /- We should wait for Perryn to give an exact statement of the bound to match RR-/ - sorry intro i apply RRSinglePushForward_non_zero intro i @@ -412,8 +403,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.zero_div] simp -#check Real.log_rpow -- we'll need this later - lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): num / den < (2⁻¹ : ℝ) := by rw [@div_lt_iff] From e45debdee18b5de027a320b25c914d9fbdba4087 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 1 Aug 2025 12:41:53 -0400 Subject: [PATCH 130/216] two sorrys left --- .../Pure/Local/ENNRealLemmasSuite.lean | 118 +++++++++++++++++- .../Pure/Local/RAPPOR/Properties.lean | 1 - 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index a164095f..521fcd12 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -53,7 +53,7 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry -#check ENNReal.mul_eq_top + lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by rw [@Ne.eq_def] @@ -290,7 +290,121 @@ lemma le_double (a b c : ENNReal)(h1 : a ≤ b)(h2 : c ≤ d)(htop1: a ≠ ⊤)( exact hr exact h2r -lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal)⁻¹ + num / den) / (2⁻¹ - num / den) = (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := by sorry +lemma pnat_zero_imp_false2 (den : PNat): (den : Nat) = 0 -> False := by aesop + + +lemma mult_div_comm_mult_div (a b c :ENNReal): a*(b/c) = b*(a/c):= by + rw [@ENNReal.div_eq_inv_mul] + rw [@ENNReal.div_eq_inv_mul] + rw [mul_comm] + rw[← mul_assoc] + conv => + enter [1,1] + rw[mul_comm] + +lemma div_mult_eq_mult_div (a b c :ENNReal) : a/b*c = c*(a/b) := by rw [@CanonicallyOrderedCommSemiring.mul_comm] + +lemma lt_sub_left (a b : Nat):a-b>0 ↔ b< a := by aesop + +lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal)⁻¹ + num / den) / (2⁻¹ - num / den) + = (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := by + simp + rw [ENNReal.div_eq_div_iff] + rw [mul_comm] + rw [← ennreal_mul_assoc] + conv => + enter [2] + rw [mul_comm] + rw [← ennreal_mul_assoc] + rw [ENNReal.mul_sub] + rw [ENNReal.mul_sub] + rw [ENNReal.mul_sub] + rw [ENNReal.mul_sub] + rw [← mul_assoc] + rw [@ENNReal.mul_comm_div] + rw [ENNReal.inv_mul_cancel] + rw [ENNReal.div_self] + rw [mul_one] + rw [one_mul] + conv => + enter [2,2,1] + rw[mul_comm] + rw[← mul_assoc] + rw [ENNReal.inv_mul_cancel] + rw [one_mul] + rw [mul_comm] + rw [mult_div_comm_mult_div] + rw [ENNReal.div_self] + rw [mul_one] + rw[div_mult_eq_mult_div] + + simp + apply pnat_zero_imp_false2 + + norm_cast + rw [Not] + intro B + contradiction + + simp + simp + + simp + apply pnat_zero_imp_false2 + + norm_cast + rw [Not] + intro B + contradiction + + simp + simp + + intro B + intro C + norm_cast + rw[Not] + intro D + contradiction + + intro b + intro C + norm_cast + rw [Not] + intro D + contradiction + + intro B + intro C + simp_all + rw [@ENNReal.div_eq_top] + rw[Not] + intro D + cases D with + | inl dl => + apply And.right at dl + have hh : ¬ den.val = 0 := by simp + norm_num at dl + contradiction + | inr dr => + apply And.left at dr + contradiction + + intro A + intro B + simp_all + + rw [← lt_sub_left] at h + rw [@ne_iff_lt_or_gt] + right + sorry + aesop + sorry + aesop + + + + end ENNRealLemmas diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 8685c9e2..1c2e4e62 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -400,7 +400,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den /- now need a version of final_bound for RRPushForward -/ /- use the "calc" tactic to prove this-/ /- We should wait for Perryn to give an exact statement of the bound to match RR-/ - sorry intro i apply RRSinglePushForward_non_zero intro i From fa000e2ee2e9c8937aa8b470bfe26f8be6cdaf91 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 1 Aug 2025 11:01:11 -0700 Subject: [PATCH 131/216] readytopull --- .../Pure/Local/RAPPOR/Properties.lean | 21 +++++++------------ .../RandomizedResponse/AccuracyProof.lean | 1 + .../DifferentialPrivacy/RenyiDivergence.lean | 1 - 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean index 4a19eb29..b3f34012 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean @@ -148,6 +148,8 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l rw [hzero] simp + /- The quotient of two products is the product of the quotients, under the condition that the denominators are non-zero and non-infinite -/ + /- This is needed in the DP proof -/ lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal)(nonzero: ∀i, g i ≠ 0)(noninf: ∀i, g i ≠ ⊤): (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by induction n with @@ -184,10 +186,7 @@ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l intro i apply noninf -/- lemma RAPPOR_cancel {T : Type} (n : Nat) (query : T -> Fin n) (num : Nat) (den : PNat) (h : 2 * num < den) (v u : T) (len_eq: (one_hot n query v).length = (one_hot n query u).length) (b : List Bool) (hlen: (one_hot n query u).length = b.length): - ∏ i : Fin ohu.length, RRSinglePushForward num den h ((one_hot n query v)[i.val]'(by sorry)) (b[↑i.val]'(by sorry)) - / RRSinglePushForward num den h ((one_hot n query u)[↑i.val](by sorry)) (b[↑i.val](by sorry)) = 1 := by sorry -/ - +/- Arithmetic step for the RAPPOR DP proof-/ lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): (1 : ENNReal) ≤ ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by rw [@sq] @@ -227,7 +226,7 @@ lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): norm_cast simp -/- Good tip: use finCongr for re-indexing... -/ +/- Good tip: use finCongr for re-indexing... --/ lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) (f : α -> β -> ENNReal): ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by @@ -235,6 +234,7 @@ lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.le intro x rfl +/- Uses the one-hot-encoding lemmas to rewrite a quotient of RRSinglePushForward applications into an if-then-else statement -/ lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / @@ -335,9 +335,6 @@ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) ( enter [1, 2, i] rw [reduction_helper1 n query num den h v u b ohu_len onhv_len h_users i] rw [reduction_helper2 _ _ _ _ _ _ h_users] - have reduction_helper3: - ∏ i : Fin (one_hot n query u).length, (if query v = (finCongr (by aesop) i) then 1 - else if query u = (finCongr (by aesop) i) then 1 else 1) = 1 := by simp simp_all only [mul_one] exact onhv_len @@ -379,7 +376,7 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den exact len_eq exact hlen rw [index_1] - rw [prod_over_prod] -- this needs proving + rw [prod_over_prod] simp_all only [ohv, ohu] rw [single_DP_reduction n query num den h v u b (by aesop) oh_len hlen] rw [@mul_div_assoc] @@ -491,9 +488,6 @@ lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) * ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by rw [arith_2_helper num den h] - - - lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by conv => @@ -503,7 +497,6 @@ lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): exact h conv => enter [2, 1] - -- rw [Real.exp_log] rw [exp_rw num den h] rw [@sq, @sq] simp @@ -555,7 +548,7 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d have valid_index7: a.length < l₂.length := by rw [xlen2] exact valid_index6 - rw [reduction_final_RAP n l₁ l₂ x (fun f => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] + rw [reduction_final_RAP n l₁ l₂ x (fun _ => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] { calc RAPPORSingleSample n query num den h (l₁[a.length]'(valid_index5)) (x[a.length]'(valid_index6)) / RAPPORSingleSample n query num den h (l₂[a.length]'(valid_index7)) (x[a.length]'(valid_index6)) ≤ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index ef97dfcd..4659708f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -33,6 +33,7 @@ def addMulRealToRV (Y : SLang Nat) (R : Real) (S: Real): SLang Real := do let n ← Y -- Sample a Nat from Y return S * ((n : Real) + R) -- Convert to Real and add R /- + variables {α : Type*} [AddMonoid α] instance : AddMonoid (List α) where diff --git a/SampCert/DifferentialPrivacy/RenyiDivergence.lean b/SampCert/DifferentialPrivacy/RenyiDivergence.lean index 9ff28400..da0f799d 100644 --- a/SampCert/DifferentialPrivacy/RenyiDivergence.lean +++ b/SampCert/DifferentialPrivacy/RenyiDivergence.lean @@ -26,7 +26,6 @@ Simplified consequence of absolute continuity between PMF's. -/ def AbsCts (p q : T -> ENNReal) : Prop := ∀ x : T, q x = 0 -> p x = 0 - /-- All PMF's are absolutely continuous with respect to themselves. -/ From d6cb153649b0343983e1c4bec9cddc4dddbfc200 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 1 Aug 2025 13:52:33 -0700 Subject: [PATCH 132/216] RAPPORdone --- .../Pure/Local/ENNRealLemmasSuite.lean | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 521fcd12..9d9e713c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -50,7 +50,7 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): sorry -/ -lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry +-- lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry @@ -126,9 +126,9 @@ lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ subst hl simp_all only [ne_eq, not_true_eq_false] -lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by +/- lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by intro h1 - sorry + sorry -/ lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by intro h1 @@ -208,12 +208,13 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hb apply hbT -lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry +-- lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by intro h - apply div_ineq_flip - exact quot_gt_one_rev a b h + apply ENNReal.div_lt_of_lt_mul' + rw [mul_one] + exact h lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): ∑' (x : List Bool), f x = (∑'(x : List Bool), if x = [] then 0 else f x) := by @@ -223,6 +224,7 @@ lemma tsum_func_zero_simp (f : List Bool -> ENNReal) (h : f [] = 0): intro i aesop +/- The unused variable "assm" is here on purpose -/ lemma tsum_ite_not (f : List Bool -> ENNReal): ∑' (x : List Bool), (if x = [] then 0 else f x) = ∑' (x : List Bool), if assm : x ≠ [] then f x else 0 := by simp_all [ite_not] @@ -292,7 +294,6 @@ lemma le_double (a b c : ENNReal)(h1 : a ≤ b)(h2 : c ≤ d)(htop1: a ≠ ⊤)( lemma pnat_zero_imp_false2 (den : PNat): (den : Nat) = 0 -> False := by aesop - lemma mult_div_comm_mult_div (a b c :ENNReal): a*(b/c) = b*(a/c):= by rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] @@ -306,8 +307,18 @@ lemma div_mult_eq_mult_div (a b c :ENNReal) : a/b*c = c*(a/b) := by rw [@Canonic lemma lt_sub_left (a b : Nat):a-b>0 ↔ b< a := by aesop +lemma lt_cancel (a b c : ENNReal) (h1: c ≠ 0) (h2 : c ≠ ⊤): c * a < c * b -> a < b := by + intro h + apply (ENNReal.mul_lt_mul_right h1 h2).mp + rw [mul_comm] + nth_rw 2 [mul_comm] + exact h + + lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal)⁻¹ + num / den) / (2⁻¹ - num / den) = (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := by + have h1: ENNReal.ofNNReal ((@Subtype.val ℕ (fun n => 0 < n) den) : NNReal) = (den : ENNReal) := by norm_cast + have h2: (2 : ENNReal) * num < ENNReal.ofNNReal ((@Subtype.val ℕ (fun n => 0 < n) den) : NNReal) := by norm_cast simp rw [ENNReal.div_eq_div_iff] rw [mul_comm] @@ -360,15 +371,15 @@ lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal) simp simp - intro B - intro C + intro _ + intro _ norm_cast rw[Not] intro D contradiction - intro b - intro C + intro _ + intro _ norm_cast rw [Not] intro D @@ -390,21 +401,38 @@ lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal) apply And.left at dr contradiction - intro A - intro B + intro _ + intro _ simp_all - rw [← lt_sub_left] at h rw [@ne_iff_lt_or_gt] right - sorry + simp_all only [gt_iff_lt, tsub_pos_iff_lt, ENNReal.coe_natCast, NNReal.ofPNat, Nonneg.mk_natCast] + norm_cast aesop - sorry + rw [← @zero_lt_iff] + rw [@tsub_pos_iff_lt] + rw [h1] + have h3: ↑(2 * num) / ↑(NNReal.ofPNat den) < (1 : ENNReal) := by + apply ENNReal.div_lt_of_lt_mul' + rw [mul_one] + aesop + have h4: ↑(2 * num) / ↑(NNReal.ofPNat den) < 2 * (2⁻¹ : ENNReal) := by + have ha: 2 * (2⁻¹ : ENNReal) = 1 := by + rw [ENNReal.mul_inv_cancel] + aesop + decide + rw [ha] + exact h3 + set s : ENNReal := (num/ ↑(NNReal.ofPNat den)) + have h5: 2 * s < 2 * (2⁻¹ : ENNReal) := by + rw [mul_div] + convert h4 + norm_cast + apply lt_cancel _ _ 2 + aesop + decide + exact h5 aesop - - - - - end ENNRealLemmas From abb333e6b9aa7b20dce015d8bf7081094c4ed5d4 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Fri, 1 Aug 2025 13:56:11 -0700 Subject: [PATCH 133/216] ShuffleModelx --- .../Pure/Local/ShuffleModel/Definitions.lean | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean new file mode 100644 index 00000000..201ec783 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -0,0 +1,18 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code + +namespace SLang + +def swap{T: Type}(l: Array T)(i j : Nat)(hi: i Date: Fri, 1 Aug 2025 16:59:41 -0700 Subject: [PATCH 134/216] majorcleanup --- .../Local/MultiBernoulli.lean | 0 .../Local/RandomizedResponseAlt.lean | 161 ----- .../Pure/Local/ENNRealLemmasSuite.lean | 35 +- .../Pure/Local/LawfulMonadSLang.lean | 2 +- .../Pure/Local/RAPPOR/Basic.lean | 5 + .../Pure/Local/RAPPOR/Definitions.lean | 3 +- .../Local/RAPPOR/Properties/Arithmetic.lean | 178 ++++++ .../Local/RAPPOR/Properties/BasicLemmas.lean | 147 +++++ .../DPProof.lean} | 348 +---------- .../Local/RAPPOR/Properties/PMFProof.lean | 29 + .../Properties}/Reduction.lean | 3 +- .../Pure/Local/RandomizedResponse/Basic.lean | 10 +- .../Local/RandomizedResponse/BasicLemmas.lean | 299 ---------- .../Local/RandomizedResponse/Definitions.lean | 16 +- .../Arithmetic.lean} | 3 +- .../BasicLemmas.lean} | 564 +++++++++--------- .../Properties/DPProof.lean | 101 ++++ .../PMFProof.lean} | 3 + .../Properties/Reduction.lean | 148 +++++ 19 files changed, 946 insertions(+), 1109 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean delete mode 100644 SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Basic.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Arithmetic.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean rename SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/{Properties.lean => Properties/DPProof.lean} (52%) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean rename SampCert/DifferentialPrivacy/Pure/Local/{RandomizedResponse => RAPPOR/Properties}/Reduction.lean (97%) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean rename SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/{DPProof.lean => Properties/Arithmetic.lean} (97%) rename SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/{RandomizedResponseMain.lean => Properties/BasicLemmas.lean} (57%) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean rename SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/{PMFProperties.lean => Properties/PMFProof.lean} (94%) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean deleted file mode 100644 index e69de29b..00000000 diff --git a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean b/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean deleted file mode 100644 index 9bce62d7..00000000 --- a/SampCert/DifferentialPrivacy/Local/RandomizedResponseAlt.lean +++ /dev/null @@ -1,161 +0,0 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert -import SampCert.DifferentialPrivacy.Pure.DP -import SampCert.Samplers.Bernoulli.Properties -import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang - - -lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num ≤ den): den - 2*num ≤ 2 * den := by - simp_all only [tsub_le_iff_right] - linarith - -def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do -/- RRSingleSample takes in a single user and produces a sample according to the distribution - induced by the user's actual response. - If the user's actual response to the query is "true", then RRSingleSample samples "true" - with probability 1/2 + num/den. If the user's actual response to the query is "false," then RRSingleSample - samples true with probability 1/2 - num/den. --/ - match query l with - | true => let r ← SLang.BernoulliSample (den + 2 * num) (2 * den) (by linarith) - return r - | false => let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) - return r - -def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ - l.mapM (fun x => RRSingleSample query num den h x) - -def RRSingleSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : SLang Bool := do - let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) - return Bool.xor (query l) r - -def RRSample2 {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample2 on an entire dataset. -/ - l.mapM (fun x => RRSingleSample2 query num den h x) - - lemma RRSingleSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : T) : - HasSum (RRSingleSample2 query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - rw [@tsum_bool] - rw[RRSingleSample2] - cases query l - { - simp_all only [bind, pure, Bool.false_bne, SLang.bind_apply, ENNReal.natCast_sub, - Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq] - rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] - rw[tsum_bool] - } - { - simp_all only [bind, pure, Bool.true_bne, SLang.bind_apply, ENNReal.natCast_sub, - Nat.cast_mul, Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, SLang.pure_apply, Bool.false_eq, Bool.not_eq_false', - mul_ite, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, Bool.true_eq, Bool.not_eq_true', Bool.false_eq_true] - rw[←SLang.BernoulliSample_normalizes (den - 2 * num) (2 * den) (arith_0 num den h)] - rw[tsum_bool] - rw [@AddCommMonoidWithOne.add_comm] - } - -lemma RRSample2_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : - HasSum (RRSample2 query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - induction l.length with - | zero => have h1: l.length = 0 := by - sorry - /- exact nil_case query num den h -/ - | succ n ha => exact ha -/- At this point, we should be set to prove that RRSample is normalized and that it is - differentially private. The definition is computable, as we need. -/ -def RRSample_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (l : List T) : PMF (List Bool) := - ⟨RRSample2 query num den h l, RRSample2_PMF_helper query num den h l⟩ --- namespace SLang - - -namespace SLang -lemma simplifier_1 (f : T -> SLang Bool): -(∑' (a : List Bool), if c = a then mapM f tl a else 0) = mapM f tl c := by -rw[tsum_eq_single c] -aesop -intro b h -simp_all only [ne_eq, mapM, ite_eq_right_iff] -intro a -subst a -simp_all only [not_true_eq_false] - - - - - - - -lemma mapM_dist_cons (f: T → SLang Bool) (b: Bool)(c: List Bool)(hd: T)(tl: List T): -mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by -rw[List.mapM_cons] -simp[-mapM] -rw [@tsum_bool] -cases b with -| true => -simp[-mapM] -conv => - enter [1, 2] - rw [simplifier_1] -| false => -simp [-mapM] -conv => - enter [1, 2] - rw [simplifier_1] - -lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num ≤ den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): -RRSample2 query num den h (hd::tl) (b::c) = RRSingleSample2 query num den h hd b * RRSample2 query num den h tl c := by -unfold RRSample2 -set f := fun x => RRSingleSample2 query num den h x -rw[mapM_dist_cons f b c hd tl] - - - - - -lemma prod_of_ind_prob(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): -RRSample2 query num den h l a = (∏'(i: Fin l.length), RRSingleSample2 query num den h (l.get i) (a.get (Fin.cast k i ))):= by -induction l generalizing a with -| nil => - simp - rw[List.length_nil] at k - symm at k - apply List.eq_nil_of_length_eq_zero at k - rw[k] - unfold RRSample2 - rw [List.mapM_nil] - simp [pure] - -| cons hd tl ih => - simp - simp at ih - cases a with - | nil => - simp at k - | cons b c => - rw[RRSample_rec query num den h] - rw[ih c] - rw [@tprod_fintype] - rw [@tprod_fintype] - - rw[Fin.prod_univ_succ] - simp - simp at k - exact k - -theorem prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den)(a: List Bool)(l: List T)(k: l.length = a.length): -RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample2 query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob - -namespace SLang - - -theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num ≤ den) : -PureDP (RRSample_PMF query num den h) ((num: NNReal) / den) := by --- let ε := ↑num / NNReal.ofPNat den -apply singleton_to_event -intros l₁ l₂ h_adj x -rw[prod_of_ind_prob_PMF query num den h x l₁] -rw[prod_of_ind_prob_PMF query num den h x l₂] -sorry diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 9d9e713c..4c8c4a96 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -2,13 +2,27 @@ import SampCert namespace ENNRealLemmas +/- Lemmas mostly having to do with mathematically trivial arithmetic in the + extended non-negative reals. +-/ + +lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop + lemma tsum_equal_comp {α β: Type} [AddCommMonoid β] [TopologicalSpace β] (f g : α -> β) (h: ∀i : α, f i = g i ): ∑' (i : α), f i = ∑' (i : α), g i := by simp_all +lemma simplifier_3 {β : Type} [DecidableEq β] (f : T -> SLang β) (c : List β) (a b : β): +(∑' (a_1 : List β), if b = a ∧ c = a_1 then mapM f tl a_1 else 0) = if b = a then mapM f tl c else 0 := by +rw[tsum_eq_single c] +aesop +aesop + lemma ennreal_mul_eq (a b c : ENNReal): a = b -> c * a = c * b := by intro h rw[h] +lemma ennreal_div_one (a: ENNReal) : a / 1 = a := by simp_all only [div_one] + lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring lemma le_add_non_zero (a b :ENNReal)(h: b ≠ 0)(h2: a ≠ ⊤): a < a+b := by @@ -43,18 +57,6 @@ lemma ineq_coercion (num : Nat) (den : PNat) (h : 2 * num < den): 2 * (@Nat.cast ENNReal NonAssocSemiring.toNatCast num) < @Nat.cast ENNReal CanonicallyOrderedCommSemiring.toNatCast ↑den := by norm_cast -/- lemma mult_inv_dist (a b : ENNReal): (a * b)⁻¹ = a⁻¹ * b⁻¹ := by - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - rw [@inv_eq_one_div] - sorry --/ - --- lemma mult_ne_zero_inv (a b : ENNReal) (h1 : a ≠ T) (h2 : b ≠ T): (a * b)⁻¹ ≠ 0 := by sorry - - - - lemma mult_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ ⊤): a * b ≠ ⊤ := by rw [@Ne.eq_def] intro h @@ -112,7 +114,6 @@ lemma Finset.prod_ne_top_fin (n : Nat) (f : Fin n -> ENNReal) (h : ∀ i, f i subst hn simp - lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ := by simp rw [Not] @@ -126,10 +127,6 @@ lemma div_ne_top (a b : ENNReal) (h1 : a ≠ ⊤) (h2 : b ≠ 0): a / b ≠ ⊤ subst hl simp_all only [ne_eq, not_true_eq_false] -/- lemma div_div_cancel (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a/c = b/c -> a = b := by - intro h1 - sorry -/ - lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> a / c < b / c := by intro h1 apply ENNReal.div_lt_of_lt_mul @@ -140,8 +137,6 @@ lemma div_div_cancel_rev (a b c : ENNReal) (h : c ≠ 0 ∧ c ≠ ⊤): a < b -> exact h.left exact h.right - - lemma quot_gt_one_rev (a b : ENNReal): b < a -> 1 < a/b := by cases hb : b == 0 with | true => simp at hb @@ -208,8 +203,6 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by apply hb apply hbT --- lemma div_ineq_flip (a b c : ENNReal): a / b > c -> b / a < c := by sorry - lemma quot_lt_one_rev (a b : ENNReal): b < a -> b/a < 1 := by intro h apply ENNReal.div_lt_of_lt_mul' diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean b/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean index c56db4d4..92c61333 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LawfulMonadSLang.lean @@ -2,7 +2,7 @@ import SampCert open SLang -/- In this file, we instantiate SLang as a LawfulMonad. This makes simp much stronger.-/ +/- Instantiation of SLang as a LawfulMonad. This makes simp much stronger.-/ instance SLang.LawfulMonad : LawfulMonad SLang where map_const := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Basic.lean new file mode 100644 index 00000000..c2d2d9fd --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Basic.lean @@ -0,0 +1,5 @@ +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Properties.Reduction diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean index 9f7c0909..623bd9a8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Definitions.lean @@ -2,7 +2,7 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof namespace RAPPOR @@ -20,7 +20,6 @@ def one_hot {T : Type} (n : Nat) (query : T -> Fin n) (v : T) : List Bool := Lis The rational privacy parameter lambda = num/den relates to the parameter f in the paper via the equation lambda = 1/2 (1 - f). -/ - def RAPPORSingleSample {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : SLang (List Bool) := do RRSamplePushForward num den h (one_hot n query v) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Arithmetic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Arithmetic.lean new file mode 100644 index 00000000..db013d48 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Arithmetic.lean @@ -0,0 +1,178 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Basic + +open SLang +open ENNRealLemmas +open RandomizedResponse + +namespace RAPPOR + +/- Mathematically trivial arithmetical steps for the Proof of DP, + together with the final bound for DP. +-/ + +lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): +(1 : ENNReal) ≤ ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by + rw [@sq] + simp + cases frac_zero : num/den.val == (0:ENNReal) with + | true => + simp_all only [beq_iff_eq] + rw [@Decidable.le_iff_lt_or_eq] + right + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp + rw [ENNReal.div_self] + simp + simp + | false => + rw [@Decidable.le_iff_lt_or_eq] + left + apply ENNRealLemmas.quot_gt_one_rev + apply ENNRealLemmas.sub_le_add_ennreal + aesop + rw [@ENNReal.le_inv_iff_mul_le] + rw [@ENNReal.div_eq_inv_mul] + rw [mul_assoc] + rw [mul_comm] + rw [← @ENNReal.le_inv_iff_mul_le] + simp + rw [@Decidable.le_iff_lt_or_eq] + left + rw [@Nat.cast_comm] + norm_cast + simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, + Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] + rw [← ENNReal.coe_two] + norm_cast + simp + +/- Good tip: use finCongr for re-indexing... --/ +lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) + (f : α -> β -> ENNReal): + ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by + apply Fintype.prod_equiv (finCongr h1) + intro x + rfl + + +lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): + num / den < (2⁻¹ : ℝ) := by + rw [@div_lt_iff] + have h1 : 2 * (num : ℝ) < den.val := by exact_mod_cast h + have h2: 2 * (num : ℝ) < den := by aesop + have h3 : 2⁻¹ * (2 * (num : ℝ)) < 2⁻¹ * den := by + rw [@mul_lt_mul_left] + apply h2 + aesop + aesop + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] + exact den.2 + +lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): + 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by + rw [←Real.log_rpow] + simp + rw [@div_pos_iff] + apply Or.inl + apply And.intro + norm_num + rw [@one_div] + positivity + norm_num + simp_all only [one_div] + convert num_den_simper num den h + +lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): + Real.exp (Real.log (((2⁻¹ + num / den) / (2⁻¹ - num / den))^2)) = ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by + rw [Real.exp_log] + rw [@sq_pos_iff] + rw [@div_ne_zero_iff] + apply And.intro + positivity + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq] + rw [@sub_eq_zero] + have h1 : num / den < (2⁻¹ : ℝ) := num_den_simper num den h + aesop + +lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : +(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = + ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by + have h1: ENNReal.ofReal 2⁻¹ = (2⁻¹ : ENNReal) := by + field_simp + rw [ENNReal.ofReal_div_of_pos] + simp + linarith + have h2: (0 : ℝ) ≤ num / den.val := by + rw [@div_nonneg_iff] + apply Or.inl + apply And.intro + aesop + aesop + rw [ENNReal.ofReal_div_of_pos] + congr + { + rw [ennreal_of_nat] + rw [ennreal_of_pnat] + rw [ENNReal.ofReal_add] + rw [ENNReal.ofReal_div_of_pos] + norm_cast + rw [h1] + aesop + aesop + aesop + simp [h2] + } + { rw [ENNReal.ofReal_sub] + rw [h1] + rw [ENNReal.ofReal_div_of_pos] + aesop + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] + exact den.2 + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] + convert h2 + } + { rw [@sub_pos] + convert num_den_simper num den h + } + +lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : +(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) * (((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = +ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) * ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by +rw [arith_2_helper num den h] + +lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): + ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by + conv => + enter [2, 1, 1] + rw [log_rw] + rfl + exact h + conv => + enter [2, 1] + rw [exp_rw num den h] + rw [@sq, @sq] + simp + rw [ENNReal.ofReal_mul] + convert arith_2_mult_helper num den h + { + rw [@div_nonneg_iff] + apply Or.inl + apply And.intro + {positivity} + { rw [@sub_nonneg] + rw [@Decidable.le_iff_lt_or_eq] + apply Or.inl + convert num_den_simper num den h + } + } + +lemma RRSamplePushForward_final_bound (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : Bool) (b : Bool): + RRSinglePushForward num den h a b / RRSinglePushForward num den h a' b + ≤ (den + 2 * num) / (den - 2 * num) := by + rw [← RRSingleSample_is_RRSinglePushForward num den h] + apply final_bound + +end RAPPOR diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean new file mode 100644 index 00000000..58b7f685 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean @@ -0,0 +1,147 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Basic + +namespace RAPPOR + +open RandomizedResponse +open SLang + +/- In the RAPPOR algorithm with n possible responses, the probability of an output of different length than n is zero.-/ +lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): + RAPPORSingleSample n query num den h l₁ l₂= 0 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen +/- The same as above, but extended to the entire dataset. -/ +lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): + RAPPORSample n query num den h l₁ x = 0 := by + induction l₁ generalizing x with + | nil => simp [RAPPORSample, -mapM] + aesop + | cons hd tl ih => + simp [RAPPORSample, -mapM] + simp [RAPPORSample, -mapM] at ih + intro b + apply Or.inr + intro y hy + subst hy + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +/- The next few lemmas are helper lemmas to simplify proofs involving one-hot encodings. +-/ +lemma one_hot_same_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query v = query u) : + one_hot n query v = one_hot n query u := by + simp + rw [h] + +lemma one_hot_same_answer_index {T : Type} (n : Nat) (query: T -> Fin n) (v : T) (j : Fin n) : + (one_hot n query v)[j]'(by simp) = true ↔ query v = j := by + simp [one_hot] + +lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query u ≠ query v): + (one_hot n query v)[query v]'(by simp) ≠ (one_hot n query u)[query v]'(by simp) := by + simp + rw [← @Ne.eq_def] + exact h + +lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j]'(by simp) ≠ (one_hot n query u)[j]'(by simp) ↔ query v = j ∨ query u = j := by + simp [one_hot] + apply Iff.intro + { intro ha + by_contra hb -- actually aesop can take it from here + rw [Mathlib.Tactic.PushNeg.not_or_eq] at hb + apply ha + apply Iff.intro + intro hc + apply And.left at hb + contradiction + intro hc + apply And.right at hb + contradiction + } + { intro ha + cases ha with + | inl h1 => aesop + | inr h1 => aesop + } + +lemma one_hot_different_answer_ex_two_contrp {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j]'(by simp) = (one_hot n query u)[j]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: query v ≠ j ∧ query u ≠ j ↔ ¬ (query v = j ∨ query u = j) := by simp_all only [ne_eq, not_or] + rw [h1] + rw [←one_hot_different_answer_ex_two n query v u j h] + simp + +lemma one_hot_different_answer_ex_two_contrp' {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): + (one_hot n query v)[j.val]'(by simp) = (one_hot n query u)[j.val]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by + have h1: (one_hot n query v)[j.val]'(by simp) = (one_hot n query v)[j]'(by simp) := by simp + have h2: (one_hot n query u)[j.val]'(by simp) = (one_hot n query u)[j]'(by simp) := by simp + rw [h1, h2] + rw [one_hot_different_answer_ex_two_contrp n query v u j h] + +/- RRSamplePushForward gives a non-zero probability for an output of the same length. + This is needed in the DP proof. +-/ +lemma RRSamplePushForward_non_zero (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): + RRSamplePushForward num den h l b ≠ 0 := by + rw [RRSamplePushForward] + rw [prod_of_ind_prob _ _ _ _ k] + rw [@tprod_fintype] + rw [@Finset.prod_ne_zero_iff] + intro a _ + apply RRSinglePushForward_non_zero + +/- RRSamplePushForward is always finite. This is needed in the DP proof. -/ +lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): + RRSamplePushForward num den h l b ≠ ⊤ := by + cases hlen: l.length == b.length with + | true => + simp at hlen + unfold RRSamplePushForward + rw [prod_of_ind_prob _ _ _ _ hlen] + rw [@tprod_fintype] + apply ENNRealLemmas.Finset.prod_ne_top_fin + intro i + apply RRSinglePushForward_finite + | false => + simp at hlen + have hzero: RRSamplePushForward num den h l b = 0 := RRSamplePushForward_diff_lengths num den h l b hlen + rw [hzero] + simp + +/- The quotient of two products is the product of the quotients, under the condition that the denominators are non-zero and non-infinite -/ + /- This is needed in the DP proof -/ + lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal)(nonzero: ∀i, g i ≠ 0)(noninf: ∀i, g i ≠ ⊤): + (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by + induction n with + | zero => simp + | succ m ih => + conv => + enter[2] + simp [@Fin.prod_univ_add] + rw[← ih] + simp [@Fin.prod_univ_add] + conv => + enter[1] + rw[div_eq_mul_inv] + rw[ENNReal.mul_inv] + rw[mul_assoc] + conv => + enter[2] + rw[mul_comm] + rw[← mul_assoc] + rw[← mul_assoc] + conv => + enter [1,1] + rw[mul_comm] + rw[← ENNReal.div_eq_inv_mul] + rw[mul_assoc] + rw[← ENNReal.div_eq_inv_mul] + rfl + apply Or.inr + apply noninf + apply Or.inr + apply nonzero + intro i + apply nonzero + intro i + apply noninf diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean similarity index 52% rename from SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean index b3f34012..25733873 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean @@ -1,239 +1,19 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.RandomizedResponseMain -import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Reduction - +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Basic +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Properties.BasicLemmas +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Properties.Arithmetic +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Properties.PMFProof namespace RAPPOR open RandomizedResponse open SLang -/- In this file, we show normalization for the One-Time Basic RAPPOR Algorithm. --/ - -/- Normalization of the single-user RAPPOR, which essentially relies on the normalization property - of randomized response. -/ -lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : - HasSum (RAPPORSingleSample n query num den h v) 1 := by - rw [RAPPORSingleSample] - apply RRSamplePushForward_PMF_helper - -/- Extension to the multi-user RAPPOR, which follows from our normalization lemma. -/ -lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : - HasSum (RAPPORSample n query num den h v) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold RAPPORSample - apply Norm_func_norm_on_list - intro a - rw [← Summable.hasSum_iff ENNReal.summable] - apply RAPPORSingleSample_PMF_helper query num den h a - -/- Promotion of RAPPOR to a PMF-/ -def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := - ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ - -/- In the RAPPOR algorithm with n possible responses, the probability of an output of different length than n is zero.-/ -lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : (one_hot n query l₁).length ≠ l₂.length): - RAPPORSingleSample n query num den h l₁ l₂= 0 := by - rw [RAPPORSingleSample] - apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen -/- The same as above, but extended to the entire dataset. -/ -lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): - RAPPORSample n query num den h l₁ x = 0 := by - induction l₁ generalizing x with - | nil => simp [RAPPORSample, -mapM] - aesop - | cons hd tl ih => - simp [RAPPORSample, -mapM] - simp [RAPPORSample, -mapM] at ih - intro b - apply Or.inr - intro y hy - subst hy - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - -/- The next few lemmas are helper lemmas to simplify proofs involving one-hot encodings. --/ -lemma List.ofFn_rw {T : Type} (n : Nat) (f : Fin n -> T) (i : Fin n): - (List.ofFn f)[i] = f i := by - simp [List.ofFn_eq_map] - -lemma one_hot_same_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query v = query u) : - one_hot n query v = one_hot n query u := by - simp - rw [h] - -lemma one_hot_same_answer_index {T : Type} (n : Nat) (query: T -> Fin n) (v : T) (j : Fin n) : - (one_hot n query v)[j]'(by simp) = true ↔ query v = j := by - simp [one_hot] - -lemma one_hot_different_answer {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (h : query u ≠ query v): - (one_hot n query v)[query v]'(by simp) ≠ (one_hot n query u)[query v]'(by simp) := by - simp - rw [← @Ne.eq_def] - exact h - -lemma one_hot_different_answer_ex_two {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): - (one_hot n query v)[j]'(by simp) ≠ (one_hot n query u)[j]'(by simp) ↔ query v = j ∨ query u = j := by - simp [one_hot] - apply Iff.intro - { intro ha - by_contra hb -- actually aesop can take it from here - rw [Mathlib.Tactic.PushNeg.not_or_eq] at hb - apply ha - apply Iff.intro - intro hc - apply And.left at hb - contradiction - intro hc - apply And.right at hb - contradiction - } - { intro ha - cases ha with - | inl h1 => aesop - | inr h1 => aesop - } - -lemma one_hot_different_answer_ex_two_contrp {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): - (one_hot n query v)[j]'(by simp) = (one_hot n query u)[j]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by - have h1: query v ≠ j ∧ query u ≠ j ↔ ¬ (query v = j ∨ query u = j) := by simp_all only [ne_eq, not_or] - rw [h1] - rw [←one_hot_different_answer_ex_two n query v u j h] - simp - -lemma one_hot_different_answer_ex_two_contrp' {T : Type} (n : Nat) (query: T -> Fin n) (v u : T) (j : Fin n) (h: query v ≠ query u): - (one_hot n query v)[j.val]'(by simp) = (one_hot n query u)[j.val]'(by simp) ↔ query v ≠ j ∧ query u ≠ j := by - have h1: (one_hot n query v)[j.val]'(by simp) = (one_hot n query v)[j]'(by simp) := by simp - have h2: (one_hot n query u)[j.val]'(by simp) = (one_hot n query u)[j]'(by simp) := by simp - rw [h1, h2] - rw [one_hot_different_answer_ex_two_contrp n query v u j h] +/- In this file, we show that the One-Time Basic RAPPOR algorithm + is ε-differentially private with ε = 2 ln (1/2 + λ)/(1/2 - λ). -/ /- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -/- RRSamplePushForward gives a non-zero probability for an output of the same length. - This is needed in the DP proof. --/ -lemma RRSamplePushForward_non_zero (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool) (k: l.length = b.length): - RRSamplePushForward num den h l b ≠ 0 := by - rw [RRSamplePushForward] - rw [prod_of_ind_prob _ _ _ _ k] - rw [@tprod_fintype] - rw [@Finset.prod_ne_zero_iff] - intro a _ - apply RRSinglePushForward_non_zero - -/- RRSamplePushForward is always finite. This is needed in the DP proof. -/ -lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): - RRSamplePushForward num den h l b ≠ ⊤ := by - cases hlen: l.length == b.length with - | true => - simp at hlen - unfold RRSamplePushForward - rw [prod_of_ind_prob _ _ _ _ hlen] - rw [@tprod_fintype] - apply ENNRealLemmas.Finset.prod_ne_top_fin - intro i - apply RRSinglePushForward_finite - | false => - simp at hlen - have hzero: RRSamplePushForward num den h l b = 0 := RRSamplePushForward_diff_lengths num den h l b hlen - rw [hzero] - simp - - /- The quotient of two products is the product of the quotients, under the condition that the denominators are non-zero and non-infinite -/ - /- This is needed in the DP proof -/ - lemma prod_over_prod (n : Nat) (f : Fin n -> ENNReal) (g : Fin n -> ENNReal)(nonzero: ∀i, g i ≠ 0)(noninf: ∀i, g i ≠ ⊤): - (∏ i : Fin n, f i) / (∏ i : Fin n, g i) = ∏ i : Fin n, (f i / g i) := by - induction n with - | zero => simp - | succ m ih => - conv => - enter[2] - simp [@Fin.prod_univ_add] - rw[← ih] - simp [@Fin.prod_univ_add] - conv => - enter[1] - rw[div_eq_mul_inv] - rw[ENNReal.mul_inv] - rw[mul_assoc] - conv => - enter[2] - rw[mul_comm] - rw[← mul_assoc] - rw[← mul_assoc] - conv => - enter [1,1] - rw[mul_comm] - rw[← ENNReal.div_eq_inv_mul] - rw[mul_assoc] - rw[← ENNReal.div_eq_inv_mul] - rfl - apply Or.inr - apply noninf - apply Or.inr - apply nonzero - intro i - apply nonzero - intro i - apply noninf - -/- Arithmetic step for the RAPPOR DP proof-/ -lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): -(1 : ENNReal) ≤ ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) ^ 2 := by - rw [@sq] - simp - cases frac_zero : num/den.val == (0:ENNReal) with - | true => - simp_all only [beq_iff_eq] - rw [@Decidable.le_iff_lt_or_eq] - right - simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, - Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] - rw [← ENNReal.coe_two] - norm_cast - simp - rw [ENNReal.div_self] - simp - simp - | false => - rw [@Decidable.le_iff_lt_or_eq] - left - apply ENNRealLemmas.quot_gt_one_rev - apply ENNRealLemmas.sub_le_add_ennreal - aesop - rw [@ENNReal.le_inv_iff_mul_le] - rw [@ENNReal.div_eq_inv_mul] - rw [mul_assoc] - rw [mul_comm] - rw [← @ENNReal.le_inv_iff_mul_le] - simp - rw [@Decidable.le_iff_lt_or_eq] - left - rw [@Nat.cast_comm] - norm_cast - simp_all only [beq_eq_false_iff_ne, ne_eq, ENNReal.div_eq_zero_iff, - Nat.cast_eq_zero, ENNReal.natCast_ne_top, or_false, Nat.cast_mul, Nat.cast_ofNat] - rw [← ENNReal.coe_two] - norm_cast - simp - -/- Good tip: use finCongr for re-indexing... --/ -lemma reindex (α β : Type) (l v : List α) (b : List β) (h1 : l.length = v.length) (h2 : l.length = b.length) - (f : α -> β -> ENNReal): - ∏ (i : Fin l.length), f l[i] b[i] = ∏ (i : Fin v.length), f l[i] b[i] := by - apply Fintype.prod_equiv (finCongr h1) - intro x - rfl - /- Uses the one-hot-encoding lemmas to rewrite a quotient of RRSinglePushForward applications into an if-then-else statement -/ lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): @@ -296,8 +76,6 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL simp [-one_hot] rw [@Finset.prod_ite] simp [-one_hot] - -- rw [Finset.prod_ite_ite_one] - -- rw [Finset.prod_set_coe] simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] have hblen : b.length = n := by aesop have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop @@ -402,118 +180,6 @@ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den rw [@ENNReal.zero_div] simp -#check Real.log_rpow -- we'll need this later -lemma num_den_simper (num : Nat) (den : PNat) (h : 2 * num < den): - num / den < (2⁻¹ : ℝ) := by - rw [@div_lt_iff] - have h1 : 2 * (num : ℝ) < den.val := by exact_mod_cast h - have h2: 2 * (num : ℝ) < den := by aesop - have h3 : 2⁻¹ * (2 * (num : ℝ)) < 2⁻¹ * den := by - rw [@mul_lt_mul_left] - apply h2 - aesop - aesop - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] - exact den.2 - -lemma log_rw (num : Nat) (den : PNat) (h: 2 * num < den): - 2 * Real.log ((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den))) = Real.log (((2⁻¹ + ↑num / ↑(NNReal.ofPNat den)) / (2⁻¹ - ↑num / ↑(NNReal.ofPNat den)))^2) := by - rw [←Real.log_rpow] - simp - rw [@div_pos_iff] - apply Or.inl - apply And.intro - norm_num - rw [@one_div] - positivity - norm_num - simp_all only [one_div] - convert num_den_simper num den h - -lemma exp_rw (num : Nat) (den : PNat) (h: 2 * num < den): - Real.exp (Real.log (((2⁻¹ + num / den) / (2⁻¹ - num / den))^2)) = ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by - rw [Real.exp_log] - rw [@sq_pos_iff] - rw [@div_ne_zero_iff] - apply And.intro - positivity - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, ne_eq] - rw [@sub_eq_zero] - have h1 : num / den < (2⁻¹ : ℝ) := num_den_simper num den h - aesop - -lemma arith_2_helper (num : Nat) (den : PNat) (h : 2 * num < den) : -(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = - ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by - have h1: ENNReal.ofReal 2⁻¹ = (2⁻¹ : ENNReal) := by - field_simp - rw [ENNReal.ofReal_div_of_pos] - simp - linarith - have h2: (0 : ℝ) ≤ num / den.val := by - rw [@div_nonneg_iff] - apply Or.inl - apply And.intro - aesop - aesop - rw [ENNReal.ofReal_div_of_pos] - congr - { - rw [ennreal_of_nat] - rw [ennreal_of_pnat] - rw [ENNReal.ofReal_add] - rw [ENNReal.ofReal_div_of_pos] - norm_cast - rw [h1] - aesop - aesop - aesop - simp [h2] - } - { rw [ENNReal.ofReal_sub] - rw [h1] - rw [ENNReal.ofReal_div_of_pos] - aesop - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, NNReal.coe_pos, Nat.cast_pos] - exact den.2 - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] - convert h2 - } - { rw [@sub_pos] - convert num_den_simper num den h - } - -lemma arith_2_mult_helper (num : Nat) (den : PNat) (h : 2 * num < den) : -(((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) * (((2⁻¹ : ENNReal) + ↑num / den) / (2⁻¹ - ↑num / ↑↑↑den.val)) = -ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) * ENNReal.ofReal ((2⁻¹ + ↑num / ↑↑ den.val) / (2⁻¹ - ↑num / ↑↑↑den)) := by -rw [arith_2_helper num den h] - -lemma arith_2 (num : Nat) (den : PNat) (h: 2 * num < den): - ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by - conv => - enter [2, 1, 1] - rw [log_rw] - rfl - exact h - conv => - enter [2, 1] - rw [exp_rw num den h] - rw [@sq, @sq] - simp - rw [ENNReal.ofReal_mul] - convert arith_2_mult_helper num den h - { - rw [@div_nonneg_iff] - apply Or.inl - apply And.intro - {positivity} - { rw [@sub_nonneg] - rw [@Decidable.le_iff_lt_or_eq] - apply Or.inl - convert num_den_simper num den h - } - } - /- This extends the previous DP lemma to a dataset of arbitrary size -/ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((2⁻¹ + num/den) / (2⁻¹ - num/den))) @@ -566,7 +232,7 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d apply RRSamplePushForward_finite } {apply x_indices} - | false => /- This part of the proof is completely disgusting, I am not proud of it -/ + | false => simp at x_indices cases x_indices with | intro i hi => diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean new file mode 100644 index 00000000..c67a4ec8 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean @@ -0,0 +1,29 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RAPPOR.Basic + +namespace RAPPOR + +open SLang +open RandomizedResponse +/- In this file, we show normalization for the One-Time Basic RAPPOR Algorithm. +-/ + +/- Normalization of the single-user RAPPOR, which essentially relies on the normalization property + of randomized response. -/ +lemma RAPPORSingleSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : T) : + HasSum (RAPPORSingleSample n query num den h v) 1 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_PMF_helper + +/- Extension to the multi-user RAPPOR, which follows from our normalization lemma. -/ +lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : + HasSum (RAPPORSample n query num den h v) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold RAPPORSample + apply Norm_func_norm_on_list + intro a + rw [← Summable.hasSum_iff ENNReal.summable] + apply RAPPORSingleSample_PMF_helper query num den h a + +/- Promotion of RAPPOR to a PMF-/ +def RAPPORSample_PMF [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) : PMF (List (List Bool)) := + ⟨RAPPORSample n query num den h v, RAPPORSample_PMF_helper query num den h v⟩ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Reduction.lean similarity index 97% rename from SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Reduction.lean index 89a44364..b8775b77 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Reduction.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/Reduction.lean @@ -1,7 +1,8 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.RandomizedResponseMain +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.DPProof open RandomizedResponse +/- Step 2 of the DP Proof over a dataset: cancellation of probabilities in the numerator and denominator. -/ lemma fin_prod_cast_RAP {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean index 9e6a21a3..a66c7014 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean @@ -1,4 +1,10 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Arithmetic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.AccuracyProof +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert.DifferentialPrivacy.Pure.DP +import SampCert.Samplers.Bernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean deleted file mode 100644 index caa19d2b..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/BasicLemmas.lean +++ /dev/null @@ -1,299 +0,0 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert -import SampCert.DifferentialPrivacy.Pure.DP -import SampCert.Samplers.Bernoulli.Properties -import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties -import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite - -namespace RandomizedResponse - -open SLang -open ENNRealLemmas - -lemma pnat_zero_imp_false (den : PNat): (den : Nat) = 0 -> False := by aesop - - - /- RRSinglePushForward is like RRSingleSample, but with "query" taken to be the identity map-/ -lemma RRSingleSample_is_RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool): - RRSingleSample (fun x => x) num den h l = RRSinglePushForward num den h l := by - simp [RRSingleSample, RRSinglePushForward] - - /- RRSamplePushForward is like RRSample, but with "query" taken to be the identity map -/ -lemma RRSample_is_RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool): - RRSample (fun x => x) num den h l = RRSamplePushForward num den h l := by - simp [RRSample, RRSamplePushForward, -mapM] - rfl - -lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): - RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample, RRSinglePushForward] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, - pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, reduceIte, mul_one, mul_zero, tsum_ite_eq] - simp - rw [ENNReal.sub_eq_of_add_eq] - simp - rw [@ENNReal.div_eq_top] - rw [Not] - intro A - rcases A with ⟨_,hb⟩ - simp at hb - rename_i h_1 - simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] - have h_le : (2:ENNReal) *num ≤ den.val := by - rw [@Nat.lt_iff_le_and_ne] at h - rcases h with ⟨hl,_⟩ - exact mod_cast hl - have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by - simp - rw [Not] - intro B - norm_cast - have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by - simp - rw [@ENNReal.div_add_div_same] - rw [add_comm] - conv => - enter [2,1,2] - rw [add_comm] - rw [← add_assoc] - rw [sub_add_cancel_ennreal] - have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by - rw[two_mul] - rw [ENNReal.div_self] - simp - simp - norm_cast - rw [@ENNReal.le_coe_iff] - simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] - apply h_le - simp_all only [WithTop.coe_natCast, Nat.cast_inj] - apply Eq.refl - exact two_num_fin - symm - exact hh - - /- This is arithmetically true, but proving arithmetic things is a mess -/ - -lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): - RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample, RRSinglePushForward] - simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): - RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by - rw[RRSingleSample, RRSinglePushForward] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, - Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - apply Eq.refl - -lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): - RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by - rw[RRSingleSample, RRSinglePushForward] - simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, - Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, - mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] - rw [ENNReal.sub_eq_of_add_eq] - simp - rw [@ENNReal.div_eq_top] - rw [Not] - intro A - rcases A with ⟨_,hb⟩ - simp at hb - rename_i h_1 - simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] - have h_le : (2:ENNReal) *num ≤ den.val := by - rw [@Nat.lt_iff_le_and_ne] at h - rcases h with ⟨hl,_⟩ - exact mod_cast hl - have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by - simp - rw [Not] - intro B - norm_cast - have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by - simp - rw [@ENNReal.div_add_div_same] - rw [add_comm] - conv => - enter [2,1,2] - rw [add_comm] - rw [← add_assoc] - rw [sub_add_cancel_ennreal] - have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by - rw[two_mul] - rw [ENNReal.div_self] - simp - simp - norm_cast - rw [@ENNReal.le_coe_iff] - simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] - apply h_le - simp_all only [WithTop.coe_natCast, Nat.cast_inj] - apply Eq.refl - exact two_num_fin - symm - exact hh - -lemma RRSinglePushForward_non_zero {T : Type} (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : T) (b : Bool): - RRSinglePushForward num den h (query l) b ≠ 0 := by - simp [RRSinglePushForward] - cases hb : b == query l with - | true => simp at hb - subst hb - simp - rw [@tsub_eq_zero_iff_le] - rw [@Mathlib.Tactic.PushNeg.not_le_eq] - apply quot_lt_one_rev - norm_cast - rw [PNat.mul_coe] - simp_all only [PNat.val_ofNat] - have hh : den.val - 2*num ≤ den.val:= by simp - have gg : den.val < 2 *den.val := by simp - rw [@Nat.le_iff_lt_or_eq] at hh - cases hh with - | inl hl => - apply LT.lt.trans hl gg - | inr hr => - rw [hr] - simp - | false => simp at hb - rw [← Bool.eq_not_of_ne hb] - intro - apply And.intro - trivial - apply And.intro - {norm_cast - rw [@Nat.sub_eq_zero_iff_le] - linarith - } - {exact ne_of_beq_false rfl} - -lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ 0 := by - rw [RRSingleSample] - apply RRSinglePushForward_non_zero - -lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): - RRSingleSample query num den h l b ≠ ⊤ := by - have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by - rw [@ne_iff_lt_or_gt] - apply Or.inr - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt, ENNReal.coe_pos, Nat.cast_pos] - apply den.2 - cases hb: b with - | true => cases hq: query l with - | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] - apply div_ne_top - exact Ne.symm (ne_of_beq_false rfl) - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.true.h2.h1 ?true.true.h2.h2 - aesop - exact hden - | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] - apply div_ne_top - aesop - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.false.h2.h1 ?true.false.h2.h2 - aesop - exact hden - | false => cases hq: query l with - | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] - apply div_ne_top - aesop - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.true.h2.h1 ?false.true.h2.h2 - aesop - exact hden - | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] - apply div_ne_top - rw [@ENNReal.add_ne_top] - apply And.intro - aesop - exact Ne.symm (ne_of_beq_false rfl) - refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.false.h2.h1 ?false.false.h2.h2 - aesop - exact hden - -/- Given what was already proved, the simplest way to prove the next lemma - is to note that RRSinglePushForward and RRSample with the identity query are the same -/ -lemma RRSinglePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) (b : Bool): - RRSinglePushForward num den h l b ≠ ⊤ := by - rw [←RRSingleSample_is_RRSinglePushForward] - apply RRSingleSample_finite - -lemma RRSinglePushForward_div_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ l₂ : Bool) (b : Bool): - RRSinglePushForward num den h l₁ b / RRSinglePushForward num den h l₂ b ≠ ⊤ := by - simp - rw [Not] - intro h1 - rw [@ENNReal.div_eq_top] at h1 - cases h1 with - | inl hl => - apply And.right at hl - have hcontr : RRSinglePushForward num den h l₂ b ≠ 0 := by apply RRSinglePushForward_non_zero (fun x : Bool => x) - contradiction - | inr hr => - apply And.left at hr - have hcontr: RRSinglePushForward num den h l₁ b ≠ ⊤ := by apply RRSinglePushForward_finite - contradiction - -lemma RRSamplePushForward_diff_lengths (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List Bool) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSamplePushForward num den h l₁ l₂ = 0 := by - induction l₁ generalizing l₂ with - | nil => simp [RRSamplePushForward, -mapM] - aesop - | cons hd tl ih => - simp [RRSamplePushForward, -mapM] - simp [RRSamplePushForward, -mapM] at ih - apply And.intro - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - -lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSample query num den h l₁ l₂= 0 := by - induction l₁ generalizing l₂ with - | nil => simp [RRSample, -mapM] - aesop - | cons hd tl ih => - simp [RRSample, -mapM] - simp [RRSample, -mapM] at ih - apply And.intro - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - apply Or.inr - intro b - intro a - subst a - simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] - -lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): - RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen - -/- lemma mwi1 (n : Nat) (f : Fin n -> Real): ∏ (i : Fin n), f i = ∏ (i : Fin (n + 1 - 1)), f i := by congr - -lemma mwi2 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by sorry)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by sorry)) := by congr - -lemma valid_index1 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin n): i.val < l.length := by - sorry - -lemma valid_index2 (n : Nat) (l : List Real) (h : l.length < n) (i : Fin (n + 1 - 1)): i.val < l.length := by - sorry - -lemma mwi3 (n : Nat) (f : Real -> Real) (l : List Real) (h : l.length < n): ∏ (i : Fin n), f (l[i]'(by apply valid_index1; apply h)) = ∏ (i : Fin (n + 1 - 1)), f (l[i]' (by apply valid_index2; apply h)) := by congr --/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index 747d126d..24e2995a 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -6,28 +6,28 @@ namespace RandomizedResponse open SLang +/- Arithmetic lemma for the next definition. -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith - + +/- RRSinglePushForward performs the Randomized Response algorithm, but + associates each user with their private response. +-/ def RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (l) r +/- Single-user Randomized Response with a gven query. -/ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do RRSinglePushForward num den h (query l) -def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (query l) r) -/- Y is a random variable which outputs the function measuring whether or not a given person - changes their answer. It is distributed according to the probability distribution - from which we sample r.-/ - +/- Extentio of Randomized Response to a dataset by use of monadic map. -/ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) +/- The next definition is used in RAPPOR. -/ def RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) : SLang (List Bool) := do - /- For use in RAPPOR -/ l.mapM (fun x => RRSinglePushForward num den h x) end RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Arithmetic.lean similarity index 97% rename from SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Arithmetic.lean index 9dad3cc2..983cc1f5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Arithmetic.lean @@ -6,6 +6,8 @@ import Mathlib.Data.Complex.Exponential import Mathlib.Analysis.SpecialFunctions.Exp import Mathlib.Analysis.SpecialFunctions.Log.Basic +/- We prove the arithmetic bounds necessary for the proof of DP for Randomized Response. -/ + lemma numerator_pos (num : ℕ) (den : ℕ+) : (0 : ℝ) < ↑↑den + 2 * num := by have den_pos : 0 < (den : ℕ) := den.property have den_real_pos : (0 : ℝ) < ↑(den : ℕ) := Nat.cast_pos.mpr den_pos @@ -87,7 +89,6 @@ lemma reduce (num : Nat) (den : PNat): exact den.2 - lemma final_coercion (num : Nat) (den : PNat) (h : 2 * num < den): (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) ≤ ENNReal.ofReal (Real.exp (Real.log ((1 / 2 + ↑num / ↑(NNReal.ofPNat den)) / (1 / 2 - ↑num / ↑(NNReal.ofPNat den))))):= by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/BasicLemmas.lean similarity index 57% rename from SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/BasicLemmas.lean index 82b3a2b5..05902c4b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/RandomizedResponseMain.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/BasicLemmas.lean @@ -1,76 +1,299 @@ -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert -import SampCert.DifferentialPrivacy.Pure.DP -import SampCert.Samplers.Bernoulli.Properties -import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.BasicLemmas -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.DPProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties -import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Basic + +namespace RandomizedResponse open SLang open ENNRealLemmas -open RandomizedResponse - -namespace SLang - -lemma simplifier_3 {β : Type} [DecidableEq β] (f : T -> SLang β) (c : List β) (a b : β): -(∑' (a_1 : List β), if b = a ∧ c = a_1 then mapM f tl a_1 else 0) = if b = a then mapM f tl c else 0 := by -rw[tsum_eq_single c] -aesop -aesop - -lemma mapM_dist_cons {β : Type} [DecidableEq β] (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): -mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by - rw[List.mapM_cons] - simp[-mapM] - conv => - enter [1, 1, a, 2] - simp[-mapM] - rw [simplifier_3] - rw [tsum_eq_single b] - aesop - aesop - -lemma RRSample_rec (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (hd: T)(tl : List T)(b: Bool)(c: List Bool): -RRSample query num den h (hd::tl) (b::c) = RRSingleSample query num den h hd b * RRSample query num den h tl c := by -unfold RRSample -set f := fun x => RRSingleSample query num den h x -rw[mapM_dist_cons f b c hd tl] - -lemma prod_of_ind_prob (β : Type) [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : - mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by - induction l generalizing a with - | nil => - simp[-mapM] - rw[List.length_nil] at k - symm at k - apply List.eq_nil_of_length_eq_zero at k - rw[k] - | cons hd tl ih => - cases a with - | nil => - simp at k - | cons b c => - rw [mapM_dist_cons] - rw [ih c] - rw [@tprod_fintype] - rw [@tprod_fintype] - simp - rw[Fin.prod_univ_succ] - simp at k - apply Eq.refl - aesop - -end SLang - -theorem RRSample_prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): -RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob - -lemma ennreal_div_one (a: ENNReal) : a / 1 = a := by aesop - +/- Basic facts about Randomized Response, e.g., its distribution and finiteness. + We then use these facts to prove the final bound for the proof of DP. +-/ + + /- RRSinglePushForward is like RRSingleSample, but with "query" taken to be the identity map-/ +lemma RRSingleSample_is_RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool): + RRSingleSample (fun x => x) num den h l = RRSinglePushForward num den h l := by + simp [RRSingleSample, RRSinglePushForward] + + /- RRSamplePushForward is like RRSample, but with "query" taken to be the identity map -/ +lemma RRSample_is_RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool): + RRSample (fun x => x) num den h l = RRSamplePushForward num den h l := by + simp [RRSample, RRSamplePushForward, -mapM] + rfl + +/- Probability of a person with private answer "true" giving randomized response "true."-/ +lemma RRSingleSample_true_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): + RRSingleSample query num den h l true = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample, RRSinglePushForward] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, + pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, reduceIte, mul_one, mul_zero, tsum_ite_eq] + simp + rw [ENNReal.sub_eq_of_add_eq] + simp + rw [@ENNReal.div_eq_top] + rw [Not] + intro A + rcases A with ⟨_,hb⟩ + simp at hb + rename_i h_1 + simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] + have h_le : (2:ENNReal) *num ≤ den.val := by + rw [@Nat.lt_iff_le_and_ne] at h + rcases h with ⟨hl,_⟩ + exact mod_cast hl + have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by + simp + rw [Not] + intro B + norm_cast + have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by + simp + rw [@ENNReal.div_add_div_same] + rw [add_comm] + conv => + enter [2,1,2] + rw [add_comm] + rw [← add_assoc] + rw [sub_add_cancel_ennreal] + have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by + rw[two_mul] + rw [ENNReal.div_self] + simp + simp + norm_cast + rw [@ENNReal.le_coe_iff] + simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] + apply h_le + simp_all only [WithTop.coe_natCast, Nat.cast_inj] + apply Eq.refl + exact two_num_fin + symm + exact hh + +/- Probability of a person with private answer "true" giving randomized response "false."-/ +lemma RRSingleSample_true_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = true): + RRSingleSample query num den h l false = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample, RRSinglePushForward] + simp_all only [bind, pure, Bool.true_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, Bool.not_eq_false', mul_ite, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +/- Probability of a person with private answer "false" giving randomized response "true."-/ +lemma RRSingleSample_false_true {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): + RRSingleSample query num den h l true = (den - 2 * num) / (2 * den) := by + rw[RRSingleSample, RRSinglePushForward] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.true_eq, Bool.not_eq_true', mul_ite, + Bool.false_eq_true, ↓reduceIte, mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + apply Eq.refl + +/- Probability of a person with private answer "false" giving randomized response "false."-/ +lemma RRSingleSample_false_false {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (hq : query l = false): + RRSingleSample query num den h l false = (den + 2 * num) / (2 * den) := by + rw[RRSingleSample, RRSinglePushForward] + simp_all only [bind, pure, Bool.false_bne, bind_apply, BernoulliSample_apply, ENNReal.natCast_sub, Nat.cast_mul, + Nat.cast_ofNat, PNat.mul_coe, PNat.val_ofNat, pure_apply, Bool.false_eq, mul_ite, Bool.false_eq_true, ↓reduceIte, + mul_one, mul_zero, tsum_ite_eq, NNReal.ofPNat, Nonneg.mk_natCast] + rw [ENNReal.sub_eq_of_add_eq] + simp + rw [@ENNReal.div_eq_top] + rw [Not] + intro A + rcases A with ⟨_,hb⟩ + simp at hb + rename_i h_1 + simp_all only [ENNReal.sub_eq_top_iff, ENNReal.natCast_ne_top, ne_eq, false_and] + have h_le : (2:ENNReal) *num ≤ den.val := by + rw [@Nat.lt_iff_le_and_ne] at h + rcases h with ⟨hl,_⟩ + exact mod_cast hl + have two_num_fin : (2:ENNReal)* num ≠ ⊤:= by + simp + rw [Not] + intro B + norm_cast + have hh : 1 = (den.val + (2:ENNReal) * num)/(2 *den) + (den-2*num)/(2*den):= by + simp + rw [@ENNReal.div_add_div_same] + rw [add_comm] + conv => + enter [2,1,2] + rw [add_comm] + rw [← add_assoc] + rw [sub_add_cancel_ennreal] + have den_den : 1 = ((den.val :ENNReal) + den.val)/(2*(den.val:ENNReal)) := by + rw[two_mul] + rw [ENNReal.div_self] + simp + simp + norm_cast + rw [@ENNReal.le_coe_iff] + simp_all only [ne_eq, Nat.cast_mul, Nat.cast_ofNat] + apply h_le + simp_all only [WithTop.coe_natCast, Nat.cast_inj] + apply Eq.refl + exact two_num_fin + symm + exact hh + +/- RRSinglePushForward always outputs non-zero probabilities. -/ +lemma RRSinglePushForward_non_zero {T : Type} (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : T) (b : Bool): + RRSinglePushForward num den h (query l) b ≠ 0 := by + simp [RRSinglePushForward] + cases hb : b == query l with + | true => simp at hb + subst hb + simp + rw [@tsub_eq_zero_iff_le] + rw [@Mathlib.Tactic.PushNeg.not_le_eq] + apply quot_lt_one_rev + norm_cast + rw [PNat.mul_coe] + simp_all only [PNat.val_ofNat] + have hh : den.val - 2*num ≤ den.val:= by simp + have gg : den.val < 2 *den.val := by simp + rw [@Nat.le_iff_lt_or_eq] at hh + cases hh with + | inl hl => + apply LT.lt.trans hl gg + | inr hr => + rw [hr] + simp + | false => simp at hb + rw [← Bool.eq_not_of_ne hb] + intro + apply And.intro + trivial + apply And.intro + {norm_cast + rw [@Nat.sub_eq_zero_iff_le] + linarith + } + {exact ne_of_beq_false rfl} + +/- RRSingleSample always outputs non-zero probabilities. -/ +lemma RRSingleSample_non_zero {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ 0 := by + rw [RRSingleSample] + apply RRSinglePushForward_non_zero + +/- RRSinglePushForward always outputs finite probabilities. -/ +lemma RRSingleSample_finite {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) (b : Bool): + RRSingleSample query num den h l b ≠ ⊤ := by + have hden: ↑(NNReal.ofPNat den) ≠ (0 : ENNReal) := by + rw [@ne_iff_lt_or_gt] + apply Or.inr + simp_all only [NNReal.ofPNat, Nonneg.mk_natCast, gt_iff_lt, ENNReal.coe_pos, Nat.cast_pos] + apply den.2 + cases hb: b with + | true => cases hq: query l with + | true => rw [RRSingleSample_true_true _ _ _ _ _ hq] + apply div_ne_top + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.true.h2.h1 ?true.true.h2.h2 + aesop + exact hden + | false => rw [RRSingleSample_false_true _ _ _ _ _ hq] + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?true.false.h2.h1 ?true.false.h2.h2 + aesop + exact hden + | false => cases hq: query l with + | true => rw [RRSingleSample_true_false _ _ _ _ _ hq] + apply div_ne_top + aesop + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.true.h2.h1 ?false.true.h2.h2 + aesop + exact hden + | false => rw [RRSingleSample_false_false _ _ _ _ _ hq] + apply div_ne_top + rw [@ENNReal.add_ne_top] + apply And.intro + aesop + exact Ne.symm (ne_of_beq_false rfl) + refine mult_ne_zero 2 ↑(NNReal.ofPNat den) ?false.false.h2.h1 ?false.false.h2.h2 + aesop + exact hden + +/- RRSinglePushForward always outputs finite probabilities. + Given what was already proved, the simplest way to prove the next lemma + is to note that RRSinglePushForward and RRSample with the identity query are the same -/ +lemma RRSinglePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) (b : Bool): + RRSinglePushForward num den h l b ≠ ⊤ := by + rw [←RRSingleSample_is_RRSinglePushForward] + apply RRSingleSample_finite + +/- The next lemma is helpful for the DP Proof. -/ +lemma RRSinglePushForward_div_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ l₂ : Bool) (b : Bool): + RRSinglePushForward num den h l₁ b / RRSinglePushForward num den h l₂ b ≠ ⊤ := by + simp + rw [Not] + intro h1 + rw [@ENNReal.div_eq_top] at h1 + cases h1 with + | inl hl => + apply And.right at hl + have hcontr : RRSinglePushForward num den h l₂ b ≠ 0 := by apply RRSinglePushForward_non_zero (fun x : Bool => x) + contradiction + | inr hr => + apply And.left at hr + have hcontr: RRSinglePushForward num den h l₁ b ≠ ⊤ := by apply RRSinglePushForward_finite + contradiction + +/- The corresponding lemmas showing that RRPushForward is non-zero and finite are in a different file, + since we need our prod_of_ind_prob lemma for them. +-/ + +/- RRSamplePushForward assigns a zero probability of transition to a list of different length. -/ +lemma RRSamplePushForward_diff_lengths (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List Bool) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSamplePushForward num den h l₁ l₂ = 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSamplePushForward, -mapM] + aesop + | cons hd tl ih => + simp [RRSamplePushForward, -mapM] + simp [RRSamplePushForward, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +/- RRSample assigns a zero probability of transition to a list of different length. -/ +lemma RRSample_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample query num den h l₁ l₂= 0 := by + induction l₁ generalizing l₂ with + | nil => simp [RRSample, -mapM] + aesop + | cons hd tl ih => + simp [RRSample, -mapM] + simp [RRSample, -mapM] at ih + apply And.intro + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + apply Or.inr + intro b + intro a + subst a + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +/- Applies the above to the PMF instantiation of RRSample. -/ +lemma RRSamplePMF_diff_lengths {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l₁ : List T) (l₂ : List Bool) (hlen : l₁.length ≠ l₂.length): + RRSample_PMF query num den h l₁ l₂ = 0 := RRSample_diff_lengths query num den h l₁ l₂ hlen + +/- Final arithmetic bound for the DP proof, + by considering eight possible cases. -/ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : T) (b : Bool): RRSingleSample query num den h a b / RRSingleSample query num den h a' b ≤ (den + 2 * num) / (den - 2 * num) := by @@ -531,206 +754,3 @@ lemma final_bound (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < de left simp apply pnat_zero_imp_false - -lemma RRSamplePushForward_final_bound (num : Nat) (den : PNat) (h : 2 * num < den) (a a' : Bool) (b : Bool): - RRSinglePushForward num den h a b / RRSinglePushForward num den h a' b - ≤ (den + 2 * num) / (den - 2 * num) := by - rw [← RRSingleSample_is_RRSinglePushForward num den h] - apply final_bound - -lemma valid_index0 (l₁ : List T)(h1: l₁ = a++[n]++b) (i : Fin (l₁.length - 1)): (Fin.succAbove (a.length) i).val < l₁.length := by - have hl : l₁.length - 1 + 1 = l₁.length := by - rw [Nat.sub_add_cancel] - rw [h1] - simp - linarith - simp [Fin.succAbove] - split - simp [Fin.castSucc] - {calc - i.val < l₁.length - 1 := i.2 - _ < l₁.length := by aesop} - { - calc - i.succ.val = i.val + 1 := by simp - _ < l₁.length - 1 + 1 := by linarith[i.2] - _ = l₁.length := by rw [hl] - } - -lemma valid_index1 (l₁ l₂ : List T)(h1: l₁ = a++[n]++b) (h2: l₂ = a++[m]++b) (i : Fin ((l₁.length - 1))): (Fin.succAbove (a.length) i).val < l₂.length := by - have hl: l₁.length = l₂.length := by aesop - rw[←hl] - apply valid_index0 - exact h1 - -lemma mod_helper (a b: ℕ)(h1: b ≥ 1)(h2: a - unfold Fin.succAbove - have h' : i.castSucc < ↑a.length := by - rw [@Fin.castSucc_lt_iff_succ_le] - rw [@Fin.le_iff_val_le_val] - simp - rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] - simp[Nat.succ_le_of_lt h] - - simp only[h'] - simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] - rw[List.getElem_append_left (a++[n]) b (by simp[h];linarith)] - rw[List.getElem_append_left a [n] h] - rw[List.getElem_append_left (a++[m]) b (by simp[h];linarith)] - rw[List.getElem_append_left] - - case neg h => - have iab: i.val - a.length < b.length := by - have ile : i < l₁.length - 1 := i.is_lt - simp[h1] at ile - rw[tsub_lt_iff_left] - exact ile - simp at h - exact h - unfold Fin.succAbove - have h' : ¬ i.castSucc < ↑a.length := by - simp at h - simp - rw [@Fin.le_castSucc_iff] - apply Nat.lt_succ_of_le - simp - rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] - exact h - simp only[h'] - simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] - rw[List.getElem_append_right (a++[n]) b (by simp;linarith)] - rw[List.getElem_append_right (a++[m]) b (by simp;linarith)] - simp - simp - linarith - simp - linarith - - -lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): - i.val < l₁.length := by - have hl1: l₁.length - 1 + 1 = l₁.length := by - rw [Nat.sub_add_cancel] - rw[h1] - simp - linarith - exact Nat.lt_of_lt_of_eq i.2 hl1 - -lemma valid_index3 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): - i.val < x.length := by - rw[←hx] - apply valid_index2 h1 i - - -lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / - (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by - rw[tprod_fintype] - rw[tprod_fintype] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(valid_index2 h1 b)) (x[b.val]'(valid_index3 h1 hx b))) a.length] - - have ind: a.length < x.length := by - rw[← hx] - rw[h1] - simp - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] - have helper: l₁.length - 1 = l₂.length - 1 := by aesop - have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by - apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper)) - simp[succHelp l₁ l₂ h1 h2] - intro i - congr - rw [← propext cast_eq_iff_heq] - rw [← propext cast_eq_iff_heq] - rw[hlp] - rw[ENNReal.mul_div_mul_right] - simp - - simp[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] - simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] - - rw[Finset.prod_ne_zero_iff] - intro i - simp[nonzero] - rw[← lt_top_iff_ne_top] - apply ENNReal.prod_lt_top - intro i - simp[noninf] - -lemma fin_prod_cast {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : - ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by - subst h - simp - -lemma conversion {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang β): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by - rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] - simp - -theorem reduction_final {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / - (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by - have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith - have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith - rw[conversion l₂ x h2 hl2 hy f] - rw[conversion l₁ x h1 hl1 hx f] - rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] - -open Finset -open scoped BigOperators - -theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by -apply singleton_to_event_update -intros l₁ l₂ h_adj x -cases xlen1 : l₁.length == x.length with -| true => - rw[RRSample_prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] - rw[RRSample_prod_of_ind_prob_PMF query num den h x l₂ - (by rw[←UpdateNeighbour_length h_adj] - simp at xlen1 - exact xlen1)] - cases h_adj with - | Update hl₁ hl₂ => - rename_i a n b m - have hlen: l₁.length = l₂.length := by aesop - have xlen2 : l₂.length = x.length := by aesop - simp - have xlen3 : l₁.length = x.length := by aesop - rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] - have i1: a.length < x.length := by - rw[←xlen3] - subst hl₁ hl₂ - simp_all only [List.append_assoc, List.singleton_append, List.length_append, - List.length_cons, beq_iff_eq] - rw[←xlen1] - rw [@Nat.lt_add_right_iff_pos] - simp - {calc - RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) - / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - /- apply final_step_combined - exact h --/ - apply final_coercion - exact h - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by aesop - } - {apply RRSingleSample_non_zero query num den h} - {apply RRSingleSample_finite query num den h} -| false => simp at xlen1 - rw [←Ne.eq_def] at xlen1 - have numerator_zero: RRSample_PMF query num den h l₁ x = 0 := by - rw [RRSamplePMF_diff_lengths] - exact xlen1 - rw [numerator_zero] - rw [@ENNReal.zero_div] - simp diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean new file mode 100644 index 00000000..6292f30f --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean @@ -0,0 +1,101 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Basic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.BasicLemmas +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Reduction + +open SLang +open ENNRealLemmas +open RandomizedResponse + +namespace SLang + +/- Helper lemma about mapM to prove "prod_of_ind_prob"-/ +lemma mapM_dist_cons {β : Type} [DecidableEq β] (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): +mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by + rw[List.mapM_cons] + simp[-mapM] + conv => + enter [1, 1, a, 2] + simp[-mapM] + rw [simplifier_3] + rw [tsum_eq_single b] + aesop + aesop + +/- First step of the DP Proof: using indepenendence to rewrite the total probability + as a product of probabilities. -/ +lemma prod_of_ind_prob (β : Type) [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : + mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by + induction l generalizing a with + | nil => + simp[-mapM] + rw[List.length_nil] at k + symm at k + apply List.eq_nil_of_length_eq_zero at k + rw[k] + | cons hd tl ih => + cases a with + | nil => + simp at k + | cons b c => + rw [mapM_dist_cons] + rw [ih c] + rw [@tprod_fintype] + rw [@tprod_fintype] + simp + rw[Fin.prod_univ_succ] + simp at k + apply Eq.refl + aesop + +end SLang + +/- Version of the above lemma for the PMF instantiation of RRSample. -/ +theorem RRSample_prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): +RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob + +/- Proof of DP for Randomized Response. -/ +theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : +DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by +apply singleton_to_event_update +intros l₁ l₂ h_adj x +cases xlen1 : l₁.length == x.length with +| true => + rw[RRSample_prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] + rw[RRSample_prod_of_ind_prob_PMF query num den h x l₂ + (by rw[←UpdateNeighbour_length h_adj] + simp at xlen1 + exact xlen1)] + cases h_adj with + | Update hl₁ hl₂ => + rename_i a n b m + have hlen: l₁.length = l₂.length := by aesop + have xlen2 : l₂.length = x.length := by aesop + simp + have xlen3 : l₁.length = x.length := by aesop + rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + have i1: a.length < x.length := by + rw[←xlen3] + subst hl₁ hl₂ + simp_all only [List.append_assoc, List.singleton_append, List.length_append, + List.length_cons, beq_iff_eq] + rw[←xlen1] + rw [@Nat.lt_add_right_iff_pos] + simp + {calc + RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) + / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by + apply final_coercion + exact h + _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by aesop + } + {apply RRSingleSample_non_zero query num den h} + {apply RRSingleSample_finite query num den h} +| false => simp at xlen1 + rw [←Ne.eq_def] at xlen1 + have numerator_zero: RRSample_PMF query num den h l₁ x = 0 := by + rw [RRSamplePMF_diff_lengths] + exact xlen1 + rw [numerator_zero] + rw [@ENNReal.zero_div] + simp diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean similarity index 94% rename from SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean rename to SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean index 23a1f2ac..b553d16c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/PMFProperties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean @@ -11,6 +11,7 @@ open RandomizedResponse #check RandomizedResponse.RRSingleSample #check SLang.BernoulliSample_normalizes +/- Instantiation of RRSinglePushForward as a PMF. -/ lemma RRSinglePushForward_PMF (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : HasSum (RRSinglePushForward num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -32,6 +33,7 @@ lemma RRSinglePushForward_PMF (num : Nat) (den : PNat) (h: 2 * num < den) (l : B rw [@AddCommMonoidWithOne.add_comm] } +/- The next lemmas lead towards the instantiation of RRSample as a PMF. -/ lemma RRSingleSample_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : HasSum (RRSingleSample query num den h l) 1 := by rw [RRSingleSample] @@ -55,5 +57,6 @@ lemma RRSamplePushForward_PMF_helper [LawfulMonad SLang] (num : Nat) (den : PNat rw [← Summable.hasSum_iff ENNReal.summable] apply RRSinglePushForward_PMF +/- Instantiation of RRSample as a PMF. -/ def RRSample_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRSample query num den h l, RRSample_PMF_helper query num den h l⟩ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean new file mode 100644 index 00000000..65694de2 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean @@ -0,0 +1,148 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Basic + +/- Step 2 of the DP Proof: cancellation of probalities in the numerator and denominator. -/ +lemma valid_index0 (l₁ : List T)(h1: l₁ = a++[n]++b) (i : Fin (l₁.length - 1)): (Fin.succAbove (a.length) i).val < l₁.length := by + have hl : l₁.length - 1 + 1 = l₁.length := by + rw [Nat.sub_add_cancel] + rw [h1] + simp + linarith + simp [Fin.succAbove] + split + simp [Fin.castSucc] + {calc + i.val < l₁.length - 1 := i.2 + _ < l₁.length := by aesop} + { + calc + i.succ.val = i.val + 1 := by simp + _ < l₁.length - 1 + 1 := by linarith[i.2] + _ = l₁.length := by rw [hl] + } + +lemma valid_index1 (l₁ l₂ : List T)(h1: l₁ = a++[n]++b) (h2: l₂ = a++[m]++b) (i : Fin ((l₁.length - 1))): (Fin.succAbove (a.length) i).val < l₂.length := by + have hl: l₁.length = l₂.length := by aesop + rw[←hl] + apply valid_index0 + exact h1 + +lemma mod_helper (a b: ℕ)(h1: b ≥ 1)(h2: a + unfold Fin.succAbove + have h' : i.castSucc < ↑a.length := by + rw [@Fin.castSucc_lt_iff_succ_le] + rw [@Fin.le_iff_val_le_val] + simp + rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] + simp[Nat.succ_le_of_lt h] + + simp only[h'] + simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] + rw[List.getElem_append_left (a++[n]) b (by simp[h];linarith)] + rw[List.getElem_append_left a [n] h] + rw[List.getElem_append_left (a++[m]) b (by simp[h];linarith)] + rw[List.getElem_append_left] + + case neg h => + have iab: i.val - a.length < b.length := by + have ile : i < l₁.length - 1 := i.is_lt + simp[h1] at ile + rw[tsub_lt_iff_left] + exact ile + simp at h + exact h + unfold Fin.succAbove + have h' : ¬ i.castSucc < ↑a.length := by + simp at h + simp + rw [@Fin.le_castSucc_iff] + apply Nat.lt_succ_of_le + simp + rw[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] + exact h + simp only[h'] + simp only [↓reduceIte, Fin.coe_castSucc,Fin.getElem_fin] + rw[List.getElem_append_right (a++[n]) b (by simp;linarith)] + rw[List.getElem_append_right (a++[m]) b (by simp;linarith)] + simp + simp + linarith + simp + linarith + + +lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): + i.val < l₁.length := by + have hl1: l₁.length - 1 + 1 = l₁.length := by + rw [Nat.sub_add_cancel] + rw[h1] + simp + linarith + exact Nat.lt_of_lt_of_eq i.2 hl1 + +lemma valid_index3 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): + i.val < x.length := by + rw[←hx] + apply valid_index2 h1 i + + +lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / + (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by + rw[tprod_fintype] + rw[tprod_fintype] + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f (l₁[b.val]'(valid_index2 h1 b)) (x[b.val]'(valid_index3 h1 hx b))) a.length] + + have ind: a.length < x.length := by + rw[← hx] + rw[h1] + simp + rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] + have helper: l₁.length - 1 = l₂.length - 1 := by aesop + have hlp: (∏ i : Fin (l₁.length - 1), f l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by + apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper)) + simp[succHelp l₁ l₂ h1 h2] + intro i + congr + rw [← propext cast_eq_iff_heq] + rw [← propext cast_eq_iff_heq] + rw[hlp] + rw[ENNReal.mul_div_mul_right] + simp + + simp[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] + simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] + + rw[Finset.prod_ne_zero_iff] + intro i + simp[nonzero] + rw[← lt_top_iff_ne_top] + apply ENNReal.prod_lt_top + intro i + simp[noninf] + +lemma fin_prod_cast {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : + ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by + subst h + simp + +lemma conversion {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang β): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by + rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] + simp + +theorem reduction_final {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by + have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith + have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith + rw[conversion l₂ x h2 hl2 hy f] + rw[conversion l₁ x h1 hl1 hx f] + rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] From 07e64e47af3c6a4d6493de5ae27a938aba3056da Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 4 Aug 2025 09:42:25 -0700 Subject: [PATCH 135/216] pullrequestversion --- .../Pure/Local/MultiBernoulli/Properties.lean | 2 +- .../Pure/Local/Normalization.lean | 8 +++- .../Pure/Local/ProbabilityProduct.lean | 45 +++++++++++++++++++ .../Pure/Local/RAPPOR/Properties/DPProof.lean | 7 ++- .../Local/RAPPOR/Properties/PMFProof.lean | 2 +- .../Pure/Local/RandomizedResponse/Basic.lean | 1 + .../Properties/DPProof.lean | 45 +------------------ .../Properties/PMFProof.lean | 4 +- 8 files changed, 62 insertions(+), 52 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean index 8d50b4bd..6a80f054 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/MultiBernoulli/Properties.lean @@ -52,7 +52,7 @@ lemma bernoulli_helper [LawfulMonad SLang] (hd : Bool) (hd_1 : SeedType) : berno lemma MultiBernoulliSample_normalizes [LawfulMonad SLang] (seeds : List SeedType) : ∑' (b: List Bool), MultiBernoulliSample seeds b = 1 := by unfold MultiBernoulliSample - apply Norm_func_norm_on_list + apply norm_func_norm_on_list intro a rw [bernoulli_mapper_sums_to_1] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean index adfc6ff9..6f7b3150 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/Normalization.lean @@ -6,6 +6,8 @@ import Mathlib.Data.Set.Basic import Mathlib.Data.Set.Basic import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite +namespace SLang + /- In this file, we show that if a function f : α -> SLang β normalizes, in the sense that ∑' (b : List β) f a b = 1 for any fixed a : α, @@ -14,6 +16,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite This is valuable for local algorithms, where a randomizer is first defined for a single user and then applied to a dataset of users. + The proof is by induction and follows by a straightforward + application of the Fubini-Tonelli theorem. -/ /- Helper lemma to simplify a if-then-else statement in a sum-/ @@ -106,7 +110,7 @@ lemma simplifier3_gen [LawfulMonad SLang] (α β : Type)(f : α → SLang β)(hd normalizes, then the function obtained applying monadic map to f and some list al also normalizes. -/ -lemma Norm_func_norm_on_list [LawfulMonad SLang] (α β : Type) [DecidableEq β] (f: α → SLang β) (al: List α): +lemma norm_func_norm_on_list [LawfulMonad SLang] (α β : Type) [DecidableEq β] (f: α → SLang β) (al: List α): (∀ a : α, ∑' (b : β), f a b = 1) → ∑' (b : List β), mapM f al b = 1 := by intro h induction al with @@ -132,3 +136,5 @@ lemma Norm_func_norm_on_list [LawfulMonad SLang] (α β : Type) [DecidableEq β] rfl rw[h hd] apply ih + +end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean new file mode 100644 index 00000000..c2496aa2 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean @@ -0,0 +1,45 @@ +import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite + +namespace SLang + +open ENNRealLemmas +/- Helper lemma about mapM to prove "prod_of_ind_prob"-/ +lemma mapM_dist_cons {β : Type} [LawfulMonad SLang] [DecidableEq β] (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): +mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by + rw[List.mapM_cons] + simp[-mapM] + conv => + enter [1, 1, a, 2] + simp[-mapM] + rw [simplifier_3] + rw [tsum_eq_single b] + aesop + aesop + +/- First step of the DP Proof: using indepenendence to rewrite the total probability + as a product of probabilities. -/ +lemma prod_of_ind_prob (β : Type) [LawfulMonad SLang] [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : + mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by + induction l generalizing a with + | nil => + simp[-mapM] + rw[List.length_nil] at k + symm at k + apply List.eq_nil_of_length_eq_zero at k + rw[k] + | cons hd tl ih => + cases a with + | nil => + simp at k + | cons b c => + rw [mapM_dist_cons] + rw [ih c] + rw [@tprod_fintype] + rw [@tprod_fintype] + simp + rw[Fin.prod_univ_succ] + simp at k + apply Eq.refl + aesop + +end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean index 25733873..038cf80f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean @@ -53,12 +53,12 @@ lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (de have h1: (one_hot n query v)[i.val]'(by omega) = (one_hot n query u)[i.val]'(by omega) := by convert one_hot_different_answer_ex_two_contrp' n query v u (finCongr (by aesop) i) aesop - -- simp_all only [ne_eq, not_or, not_and] rw [h1] rw [ENNReal.div_self] apply RRSinglePushForward_non_zero apply RRSinglePushForward_finite +/- Rewrites the if-then-else statement from above into a simpler form, with most terms cancelled. -/ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): (∏ i : Fin (one_hot n query u).length, @@ -103,6 +103,7 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL rw [h9] rw [@mul_div] +/- Cancellation of terms in the DP proof by using the above two lemmas.-/ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): ∏ i : Fin (one_hot n query u).length, RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] @@ -116,7 +117,7 @@ lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) ( simp_all only [mul_one] exact onhv_len -/- This shows that that RAPPOR algorithm applied to a single user is differentially private. -/ +/- This gives the RAPPOR bound when the algorithm is applied to a single user. -/ lemma RAPPORSingle_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool): (RAPPORSingleSample n query num den h v b) / (RAPPORSingleSample n query num den h u b) ≤ ((2⁻¹ + num / den) / (2⁻¹ - num / den))^2 := by -- probably want to restate the bound in an arithmetically equivalent way @@ -200,8 +201,6 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4 _ hl₂; apply xlen2)).length = n) == true with | true => simp at x_indices - /- Now we need to apply the generalized reduction lemma, - and then do some arithmetic. -/ have valid_index5: a.length < l₁.length := by rw [hl₁] rw [@List.length_append] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean index c67a4ec8..68e9e465 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/PMFProof.lean @@ -19,7 +19,7 @@ lemma RAPPORSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Fin n) HasSum (RAPPORSample n query num den h v) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold RAPPORSample - apply Norm_func_norm_on_list + apply norm_func_norm_on_list intro a rw [← Summable.hasSum_iff ENNReal.summable] apply RAPPORSingleSample_PMF_helper query num den h a diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean index a66c7014..a8af2541 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean @@ -8,3 +8,4 @@ import SampCert.Samplers.Bernoulli.Properties import SampCert.DifferentialPrivacy.Pure.Local.LawfulMonadSLang import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.ENNRealLemmasSuite +import SampCert.DifferentialPrivacy.Pure.Local.ProbabilityProduct diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean index 6292f30f..5c171fed 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean @@ -8,52 +8,11 @@ open RandomizedResponse namespace SLang -/- Helper lemma about mapM to prove "prod_of_ind_prob"-/ -lemma mapM_dist_cons {β : Type} [DecidableEq β] (f: T → SLang β) (b: β)(c: List β)(hd: T)(tl: List T): -mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by - rw[List.mapM_cons] - simp[-mapM] - conv => - enter [1, 1, a, 2] - simp[-mapM] - rw [simplifier_3] - rw [tsum_eq_single b] - aesop - aesop - -/- First step of the DP Proof: using indepenendence to rewrite the total probability - as a product of probabilities. -/ -lemma prod_of_ind_prob (β : Type) [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : - mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by - induction l generalizing a with - | nil => - simp[-mapM] - rw[List.length_nil] at k - symm at k - apply List.eq_nil_of_length_eq_zero at k - rw[k] - | cons hd tl ih => - cases a with - | nil => - simp at k - | cons b c => - rw [mapM_dist_cons] - rw [ih c] - rw [@tprod_fintype] - rw [@tprod_fintype] - simp - rw[Fin.prod_univ_succ] - simp at k - apply Eq.refl - aesop - -end SLang - -/- Version of the above lemma for the PMF instantiation of RRSample. -/ +/- Version of the prod_of_ind_prob lemma for the PMF instantiation of RRSample. -/ theorem RRSample_prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -/- Proof of DP for Randomized Response. -/ +/- Proof of DP for Randomized Response. We use the reduction lemma from a different file. -/ theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by apply singleton_to_event_update diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean index b553d16c..ac6f6238 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean @@ -43,7 +43,7 @@ lemma RRSample_PMF_helper [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num HasSum (RRSample query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold RRSample - apply Norm_func_norm_on_list + apply norm_func_norm_on_list intro a rw [← Summable.hasSum_iff ENNReal.summable] apply RRSingleSample_PMF_helper @@ -52,7 +52,7 @@ lemma RRSamplePushForward_PMF_helper [LawfulMonad SLang] (num : Nat) (den : PNat HasSum (RRSamplePushForward num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold RRSamplePushForward - apply Norm_func_norm_on_list + apply norm_func_norm_on_list intro a rw [← Summable.hasSum_iff ENNReal.summable] apply RRSinglePushForward_PMF From 9ce3fcc9e1fa1487264f9ec8a485c2e6463406ea Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Mon, 4 Aug 2025 09:49:03 -0700 Subject: [PATCH 136/216] removedshuffle --- .../Pure/Local/ShuffleModel/Definitions.lean | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 201ec783..f4d54be6 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -1,18 +1,3 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code - namespace SLang - -def swap{T: Type}(l: Array T)(i j : Nat)(hi: i Date: Mon, 4 Aug 2025 10:25:41 -0700 Subject: [PATCH 137/216] Shuffle Folder --- .../Pure/Local/ShuffleModel/Definitions.lean | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean new file mode 100644 index 00000000..af599a20 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -0,0 +1,23 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code + +namespace SLang + + + + + +def single_swap {T:Type}(l:Array T)(i: PNat)(h: i < l.size) := do + let j ← UniformSample i + return l.swap ⟨i.val, h⟩ ⟨j, sorry⟩ + + + + +def Shuffler {α: Type}(l:List α): SLang (List α) := + let a := l.toArray + sorry + + +def reverseIterSafe {α : Type} (a : Array α) : Array α := + Array.range a.size |>.reverse |>.map (λ i => a.get! i) From 1def98682eb09d8401ed674d0414d2c4e02ae6fb Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 5 Aug 2025 09:17:11 -0700 Subject: [PATCH 138/216] Shuffler update --- .../Pure/Local/ShuffleModel/Definitions.lean | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index af599a20..37a566de 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -4,20 +4,31 @@ import SampCert.Samplers.Uniform.Code namespace SLang +def swapAt {α : Type} (arr : Array α) (i j : Nat) : Array α := + if h1 : i < arr.size ∧ j < arr.size then + let tmp := arr[i] + arr.set! i arr[j] |>.set! j tmp + else + arr - -def single_swap {T:Type}(l:Array T)(i: PNat)(h: i < l.size) := do +def single_swap {T:Type}(l: Array T)(i: PNat) := do let j ← UniformSample i - return l.swap ⟨i.val, h⟩ ⟨j, sorry⟩ - + return l.swap ⟨i, sorry⟩ ⟨j, sorry⟩ +#check single_swap -def Shuffler {α: Type}(l:List α): SLang (List α) := - let a := l.toArray - sorry +def Shuffler {α: Type}(l:List α) := do + let mut a := l.toArray + for h: i in [1:a.size] do + let j ← UniformSample (Nat.toPNat' i) + a := a.swap ⟨i, sorry⟩ ⟨j, sorry⟩ + return a +def Shuffler{α: Type}(l:Array α) : SLang (List α) := do + let j ← UniformSample (Nat.toPNat' i) -def reverseIterSafe {α : Type} (a : Array α) : Array α := - Array.range a.size |>.reverse |>.map (λ i => a.get! i) + match l with + | [] => [] + | hd::tl => hd.index From c161290c1cf83c7b08b493a6aec356a6131f5374 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 5 Aug 2025 09:18:07 -0700 Subject: [PATCH 139/216] Shuffler --- .../Pure/Local/ShuffleModel/Definitions.lean | 7 ------- 1 file changed, 7 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 37a566de..96104032 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -25,10 +25,3 @@ def Shuffler {α: Type}(l:List α) := do let j ← UniformSample (Nat.toPNat' i) a := a.swap ⟨i, sorry⟩ ⟨j, sorry⟩ return a - -def Shuffler{α: Type}(l:Array α) : SLang (List α) := do - let j ← UniformSample (Nat.toPNat' i) - - match l with - | [] => [] - | hd::tl => hd.index From 491d196beabf17949b47c493c7cb98b849bceed6 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 5 Aug 2025 09:19:43 -0700 Subject: [PATCH 140/216] nochange --- .../Pure/Local/ENNRealLemmasSuite.lean | 6 +- .../Pure/Local/RAPPOR/Properties/DPProof.lean | 2 +- .../RandomizedResponse/AccuracyProof.lean | 14 ++--- .../RandomizedResponse/AccuracyProof2.lean | 60 +++++++++++++++++++ .../Pure/Local/ShuffleModel/Definitions.lean | 19 ++++++ 5 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 4c8c4a96..b1063bd4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -285,8 +285,6 @@ lemma le_double (a b c : ENNReal)(h1 : a ≤ b)(h2 : c ≤ d)(htop1: a ≠ ⊤)( exact hr exact h2r -lemma pnat_zero_imp_false2 (den : PNat): (den : Nat) = 0 -> False := by aesop - lemma mult_div_comm_mult_div (a b c :ENNReal): a*(b/c) = b*(a/c):= by rw [@ENNReal.div_eq_inv_mul] rw [@ENNReal.div_eq_inv_mul] @@ -343,7 +341,7 @@ lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal) rw[div_mult_eq_mult_div] simp - apply pnat_zero_imp_false2 + apply pnat_zero_imp_false norm_cast rw [Not] @@ -354,7 +352,7 @@ lemma exp_change_form (num : Nat) (den : PNat) (h: 2 * num < den) : ((2:ENNReal) simp simp - apply pnat_zero_imp_false2 + apply pnat_zero_imp_false norm_cast rw [Not] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean index 038cf80f..c82f554f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean @@ -69,7 +69,7 @@ lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SL * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) := by simp_all only [finCongr_apply, Fin.getElem_fin, Fin.coe_cast, List.getElem_ofFn, Fin.eta] - have h4 (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop + have _ (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop conv => enter [1] rw [@Finset.prod_ite] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean index 4659708f..9c65464a 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean @@ -19,8 +19,8 @@ noncomputable def coeff {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ : noncomputable def constants {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := (- (X.length) / 2) + (num * X.length) / den -def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : List (SLang Bool) := - X.map (fun x => RRSingleSample query num den h x) +/- def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : List (SLang Bool) := + X.map (fun x => RRSingleSample query num den h x) -/ def sumBernoulli (xs : List (SLang Bool)) : SLang Nat := @@ -60,10 +60,10 @@ def p {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 (true_count) / X.length -noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den):= - let coef := coeff X num den - let cons := constants X num den - let s := applying_RR_individually query X num den h +/- noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h : 2 * num < den):= + let coef := coeff l num den + let cons := constants l num den + let s := RRSample query num den h l let sum_of_ys := sumBernoulli s let p_estimate := addMulRealToRV sum_of_ys cons coef - p_estimate + p_estimate -/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean new file mode 100644 index 00000000..b432887a --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean @@ -0,0 +1,60 @@ +import SampCert +import Mathlib.Probability.ProbabilityMassFunction.Basic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.BasicLemmas + +open SLang +open PMF +open RandomizedResponse +open ENNRealLemmas + +-- UNDER CONSTRUCTION -- + +def coeff_1 (num : Nat) (den : PNat) : NNRat := den / (2 * num) +def coeff_2 (num : Nat) (den : PNat) : NNRat := den / (4 * num) + 1/2 + +/- Given a list of booleans, p_pushforward returns the proportion of "true" responses +-/ +def p_pushforward (l : List Bool) : NNRat := + let true_count := (l.filter (fun b => b)).length + (true_count) / l.length + +/- Given a list of users and a query, p_actual returns the proportion of "yes" responses + Our goal is to estimate p_actual. -/ +def p_actual {T : Type} (query: T -> Bool) (l : List T) : NNRat := + let bool_lst := l.map query + p_pushforward bool_lst + +/- Unbiased estimator-/ +def p_estimate {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : SLang NNRat := do + let randomized ← RRSample query num den h l + let avg_yes := p_pushforward randomized + /- now "return avg_yes" would gives a function that, for each NNRat q, + returns the probability that the proportion of "yes" responses + after doing randomized response on l is equal to q. -/ + return (coeff_1 num den) * avg_yes - (coeff_2 num den) + +lemma RRSample_diff_lengths_simp {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : + RRSample query num den h l a = (if l.length = a.length then RRSample query num den h l a else 0) := by + split + next h_1 => simp + next h_1 => + rw [RRSample_diff_lengths] + simp + exact h_1 + +lemma estimator_unbiased {T : Type} (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : + ∑' (q : NNRat), (p_estimate query num den h l q) * (q : NNReal) = (p_actual query l : NNReal) := by + simp [p_estimate, p_actual] + have h1 (q : NNRat): (∑' (a : List Bool), if q = (coeff_1 num den) * p_pushforward a - (coeff_2 num den) then RRSample query num den h l a else 0) * (q : NNReal) = + (∑' (a : List Bool), if q = (coeff_1 num den) * p_pushforward a - (coeff_2 num den) then RRSample query num den h l a * (q : NNReal) else 0) := by + sorry + conv => + enter [1, 1, q] + rw [h1] + rw [@ENNReal.tsum_comm] + simp_all [ite_not] + + + + sorry diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index f4d54be6..24d83f15 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -1,3 +1,22 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code namespace SLang + +def my_func {α: Type} (l:Array α) := do +let mut l := l +for h: i in [1:l.size] do + let j ← UniformSample (Nat.toPNat' i: PNat) + l := l.swap ⟨i, sorry⟩ ⟨j, sorry⟩ +return l + +#check my_func + +def Shuffler {α: Type}(l:List α) := do +let mut a := l.toArray +for h: i in [1:a.size] do + let j ← UniformSample (Nat.toPNat' i: PNat) + a := a.swap ⟨i, sorry⟩ ⟨j, sorry⟩ +return a + +#check my_func +#check Array.swap From 58e0a15a6183757b4a3512a61a320977321ffb62 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 5 Aug 2025 16:13:59 -0700 Subject: [PATCH 141/216] Shuffle Model Implementation --- .../Pure/Local/ShuffleModel/Definitions.lean | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 96104032..891e1315 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -1,27 +1,24 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code +import SampCert.Samplers.Uniform.Properties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions namespace SLang -def swapAt {α : Type} (arr : Array α) (i j : Nat) : Array α := - if h1 : i < arr.size ∧ j < arr.size then - let tmp := arr[i] - arr.set! i arr[j] |>.set! j tmp - else - arr - - -def single_swap {T:Type}(l: Array T)(i: PNat) := do - let j ← UniformSample i - return l.swap ⟨i, sorry⟩ ⟨j, sorry⟩ - -#check single_swap - +def UniformSample2 (n : PNat) : IO (Fin n) := do + let r ← IO.rand 0 (n.val - 1) + return ⟨r, by decide⟩ def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray + let mut b : Array α := Array.empty for h: i in [1:a.size] do - let j ← UniformSample (Nat.toPNat' i) - a := a.swap ⟨i, sorry⟩ ⟨j, sorry⟩ - return a + let j ← UniformSample (Nat.toPNat' i+1) + + b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by sorry⟩ + return b.toList + +def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + return Shuffler l From 2532049aa1fc04ad6f21a9816ec5c9e5d9900c2e Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 5 Aug 2025 16:50:13 -0700 Subject: [PATCH 142/216] Shuffle Updates --- .../Pure/Local/ShuffleModel/Definitions.lean | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 891e1315..14d8e20e 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -2,13 +2,14 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code import SampCert.Samplers.Uniform.Properties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import Mathlib.Probability.ProbabilityMassFunction.Basic -namespace SLang +namespace SLang -def UniformSample2 (n : PNat) : IO (Fin n) := do - let r ← IO.rand 0 (n.val - 1) - return ⟨r, by decide⟩ +open SLang +open RandomizedResponse def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray @@ -21,4 +22,15 @@ def Shuffler {α: Type}(l:List α) := do def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l - return Shuffler l + let b ← Shuffler l + return b + + +lemma ShuffleModel_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : + HasSum (ShuffleModel query num den h l) 1 := by sorry + +def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := + ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ + +theorem ShuffleDP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : +DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by sorry From 57eec6c1b953e69365759fc7f076ea7c4ae883e6 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Wed, 6 Aug 2025 12:33:14 -0400 Subject: [PATCH 143/216] changed uniform sampling --- .../Pure/Local/ShuffleModel/Definitions.lean | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 891e1315..376826df 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -2,19 +2,23 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code import SampCert.Samplers.Uniform.Properties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization namespace SLang -def UniformSample2 (n : PNat) : IO (Fin n) := do - let r ← IO.rand 0 (n.val - 1) - return ⟨r, by decide⟩ +def UniformSample' (n : PNat) : SLang (Fin n) := do + let r ← UniformSample n + return (r : Fin n) + def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray let mut b : Array α := Array.empty for h: i in [1:a.size] do - let j ← UniformSample (Nat.toPNat' i+1) + let j ← UniformSample' (Nat.toPNat' i+1) + have hj : j < a.size := by + have hi : j ≤ i := by b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by sorry⟩ return b.toList @@ -22,3 +26,21 @@ def Shuffler {α: Type}(l:List α) := do def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l return Shuffler l + +lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold Shuffler + rename_i inst + simp_all only [bind, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] + + sorry + + +lemma ShuffleModel_norms [LawfulMonad SLang] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : +HasSum (ShuffleModel query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold ShuffleModel + unfold RandomizedResponse.RRSample + rename_i inst + simp_all only [bind, pure, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] + sorry From ba7f815727569ce34ac28a4775e6af8a1bdf5caa Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Wed, 6 Aug 2025 16:26:23 -0700 Subject: [PATCH 144/216] Shuffler Sorry --- .../Pure/Local/ShuffleModel/Definitions.lean | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 14d8e20e..1ed7cf50 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -11,13 +11,32 @@ namespace SLang open SLang open RandomizedResponse + +def UniformSample' (n : PNat) : SLang (Fin n) := do + let r ← UniformSample n + return (r : Fin n) + def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray let mut b : Array α := Array.empty for h: i in [1:a.size] do - let j ← UniformSample (Nat.toPNat' i+1) - - b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by sorry⟩ + let j ← UniformSample' (Nat.toPNat' i+1) + + b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by + aesop + have h1: j ≤ i := by + rw [@Fin.le_iff_val_le_val] + norm_num + aesop + have h1: j.val < i.toPNat' + 1 := j.2 + aesop + rw[← Nat.lt_add_one_iff] + exact h1 + linarith[h.1] + + have h2: i < l.length := by exact Membership.get_elem_helper h rfl + exact Nat.lt_of_le_of_lt h1 (by aesop) + ⟩ return b.toList def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do @@ -25,12 +44,11 @@ def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l let b ← Shuffler l return b - lemma ShuffleModel_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : HasSum (ShuffleModel query num den h l) 1 := by sorry def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ -theorem ShuffleDP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : +theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by sorry From ba83a74d15c509f843ef607642289e5dbee89849 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 7 Aug 2025 08:58:44 -0700 Subject: [PATCH 145/216] Shuffle sorry --- .../Pure/Local/ShuffleModel/Definitions.lean | 3 --- 1 file changed, 3 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index fd5ca6d0..f1ea1e9b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -17,9 +17,6 @@ def Shuffler {α: Type}(l:List α) := do let mut b : Array α := Array.empty for h: i in [1:a.size] do let j ← UniformSample' (Nat.toPNat' i+1) - let j ← UniformSample' (Nat.toPNat' i+1) - have hj : j < a.size := by - have hi : j ≤ i := by b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by aesop From bfaeaa53d06910961a5d73c3c3db887071ecf2c5 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Thu, 7 Aug 2025 12:09:09 -0400 Subject: [PATCH 146/216] UniformSample' norms --- .../Pure/Local/PushForward.lean | 2 +- .../Pure/Local/ShuffleModel/Definitions.lean | 82 ++++++++++++++++--- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean index 63791a50..bfded7e3 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean @@ -3,7 +3,7 @@ import SampCert open SLang /- This proves that the push-forward of a probability measure is a probability measure. -We do not actually use this anywhere yet.-/ +We use this when defining UniformSample', i.e., UniformSample as an SLang (Fin n). -/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := fun s => ∑' (t : T), if f t = s then p t else 0 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 224bf0c5..58d2bcdf 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -3,6 +3,7 @@ import SampCert.Samplers.Uniform.Code import SampCert.Samplers.Uniform.Properties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.PushForward namespace SLang @@ -11,29 +12,92 @@ def UniformSample' (n : PNat) : SLang (Fin n) := do let r ← UniformSample n return (r : Fin n) +lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by + constructor + intro h + rw [h] + apply Nat.mod_lt + simp + intro h + exact Eq.symm (Nat.mod_eq_of_lt h) + +lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by + unfold UniformSample' + conv => + lhs + simp [pure, bind] + rw [tsum_eq_single x.val] + simp_all only [Fin.cast_val_eq_self, ↓reduceIte, Fin.is_lt, UniformSample_apply, one_div] + intro b' a + simp_all only [ne_eq, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [Fin.val_natCast] + rw [Not] at a + have h : b' < n → False := by + intro h1 + rw [← fin_helper] at h1 + apply a + exact h1 + rw [← Not] at h + rw [Nat.not_lt_eq] at h + simp_all only [imp_false, ge_iff_le, UniformSample_apply_out] + +lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n := by + rw [UniformSample'_eq_UnformSample] + exact UniformSample_apply n x.val (Fin.is_lt x) + + +lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by + rw [UniformSample'] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + set f : ℕ → Fin n := fun a => a + set p : SLang ℕ := UniformSample n + have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by + rfl + rw [←push_forward_prob_is_prob p f] + simp [h1] + have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop + conv => + enter [2, 1, z, 1, a] + rw [←h2] + simp [p] + + def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray let mut b : Array α := Array.empty for h: i in [1:a.size] do let j ← UniformSample' (Nat.toPNat' i+1) - have hj : j < a.size := by - have hi : j ≤ i := by - - b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by sorry⟩ + have hi : i < a.size := by + rename_i b_1 col r + simp_all only [Array.size_toArray, col, a] + exact Membership.get_elem_helper h rfl + b := a.swap ⟨i, by sorry⟩ ⟨j, by sorry⟩ return b.toList + +#check Shuffler + def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l return b +#check ShuffleModel + lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold Shuffler rename_i inst - simp_all only [bind, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] - + simp_all only [bind, PNat.add_coe, PNat.val_ofNat, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, + mul_ite, mul_one, mul_zero] + unfold probBind + simp [pure, bind] + set sorry @@ -41,16 +105,12 @@ lemma ShuffleModel_norms [LawfulMonad SLang] (query: T -> Bool) (num : Nat) (den HasSum (ShuffleModel query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold ShuffleModel + simp [pure,bind] unfold RandomizedResponse.RRSample rename_i inst simp_all only [bind, pure, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] sorry - let b ← Shuffler l - return b - -lemma ShuffleModel_PMF_helper {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : - HasSum (ShuffleModel query num den h l) 1 := by sorry def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ From da5fe132b215cf48179561f693f04e5c4525fa56 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 7 Aug 2025 11:41:08 -0700 Subject: [PATCH 147/216] --- .../Pure/Local/ShuffleModel/Definitions.lean | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 5f20bfa5..a48cd1cd 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -4,6 +4,7 @@ import SampCert.Samplers.Uniform.Properties import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.PushForward +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour namespace SLang @@ -108,18 +109,15 @@ lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffle mul_ite, mul_one, mul_zero] unfold probBind simp [pure, bind] - set sorry -lemma ShuffleModel_norms [LawfulMonad SLang] (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : +lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : HasSum (ShuffleModel query num den h l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold ShuffleModel simp [pure,bind] unfold RandomizedResponse.RRSample - rename_i inst - simp_all only [bind, pure, bind_apply, pure_apply, mul_ite, mul_one, mul_zero] sorry From 599c12f711e79755209b72c6b141d4865aeac070 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Mon, 11 Aug 2025 14:47:52 -0400 Subject: [PATCH 148/216] Binoimial Theorem stuff --- .../Pure/Local/ShuffleModel/Definitions.lean | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index a48cd1cd..ef9e604f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -9,6 +9,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour namespace SLang +/- Uniform Sample allows to draw a uniform sample n, but returns type Fin n. This allows +us to prove the index is valid in the Shuffler function -/ def UniformSample' (n : PNat) : SLang (Fin n) := do let r ← UniformSample n return (r : Fin n) @@ -22,6 +24,8 @@ lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by intro h exact Eq.symm (Nat.mod_eq_of_lt h) +/- Proves that an output drawn from the Uniform Sample has the same probability as +an output drawn for UniformSample' given the n values are the same. -/ lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by unfold UniformSample' conv => @@ -48,7 +52,6 @@ lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n rw [UniformSample'_eq_UnformSample] exact UniformSample_apply n x.val (Fin.is_lt x) - lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by rw [UniformSample'] simp @@ -67,7 +70,7 @@ lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by simp [p] - +/- The Shuffler follows the Fischer-Yates method for shuffling lists. -/ def Shuffler {α: Type}(l:List α) := do let mut a := l.toArray let mut b : Array α := Array.empty @@ -94,6 +97,24 @@ def Shuffler {α: Type}(l:List α) := do #check Shuffler +def BinomialSample (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) := do + let mut acc := 0 + for _ in [0:n] do + let b ← BernoulliSample num den h + if b=True then + acc := acc + 1 + return acc + +theorem BinomialSample_norms (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) : + HasSum (BinomialSample num den h n) 1 := by sorry + +theorem BinomialSample_kprob (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) (k : Nat) : + BinomialSample num den h n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by + sorry + + + +/- This is the Shuffle Model. -/ def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l @@ -101,6 +122,7 @@ def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l #check ShuffleModel + lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] unfold Shuffler From 78b67bc80d1605e8851619e911c91a186935f4e3 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 13 Aug 2025 09:26:01 -0700 Subject: [PATCH 149/216] Update SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean Co-authored-by: alexanderknop-google <155482221+alexanderknop-google@users.noreply.github.com> --- SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean index c2496aa2..c770cdb3 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean @@ -16,7 +16,7 @@ mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by aesop aesop -/- First step of the DP Proof: using indepenendence to rewrite the total probability +/- Proof that using `mapM f l` is the same as sampling from f (l.get i) for all i independently as a product of probabilities. -/ lemma prod_of_ind_prob (β : Type) [LawfulMonad SLang] [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by From f873803b3dd392e7d4a2d60b62a6f4ec6e5dbeb8 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Wed, 13 Aug 2025 14:49:55 -0400 Subject: [PATCH 150/216] binomial normalizes --- .../Pure/Local/ShuffleModel/Definitions.lean | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index ef9e604f..34a4c3ce 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -5,6 +5,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties namespace SLang @@ -94,22 +96,40 @@ def Shuffler {α: Type}(l:List α) := do ⟩ return b.toList +def BinomialSample (seed: MultiBernoulli.SeedType)(n: PNat) := do + let list := List.replicate n seed + let list ← MultiBernoulli.MultiBernoulliSample (list) + let k := List.count true list + return k -#check Shuffler -def BinomialSample (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) := do - let mut acc := 0 - for _ in [0:n] do - let b ← BernoulliSample num den h - if b=True then - acc := acc + 1 - return acc - -theorem BinomialSample_norms (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) : - HasSum (BinomialSample num den h n) 1 := by sorry - -theorem BinomialSample_kprob (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) (k : Nat) : - BinomialSample num den h n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by +theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : + HasSum (BinomialSample seed n) 1 := by + rw [BinomialSample] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) + (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => + (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample + (List.replicate (↑n) seed) a else 0)) := by + unfold push_forward + have h1 (x: List Bool): (fun a => List.count true a) x = List.count true x := by aesop + conv => + enter [1,s,1 ,t ] + rw [h1] + have h2 (x:ENNReal)(s: Nat)(t: List Bool): (if List.count true t = s then x else 0) = (if s =List.count true t then x else 0) := by aesop + conv => + enter [1,s,1,t] + rw [h2] + rw [← h] + rw [push_forward_prob_is_prob] + simp [MultiBernoulli.MultiBernoulliSample_normalizes] + + + +theorem BinomialSample_kprob (seed : MultiBernoulli.SeedType) (n : PNat) (k : Nat) : + BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by sorry From 7b5b4bb9ed5a54d2d0528686544deceab98af48c Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Wed, 13 Aug 2025 16:11:58 -0400 Subject: [PATCH 151/216] changed push forward --- .../Pure/Local/PushForward.lean | 4 +-- .../Pure/Local/ShuffleModel/Definitions.lean | 36 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean index bfded7e3..cfb1a413 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean @@ -6,13 +6,13 @@ open SLang We use this when defining UniformSample', i.e., UniformSample as an SLang (Fin n). -/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := - fun s => ∑' (t : T), if f t = s then p t else 0 + fun s => ∑' (t : T), if s = f t then p t else 0 lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : ∑' (s : S), (push_forward p f) s = 1 := by simp [push_forward] rw [@ENNReal.tsum_comm] - have h1: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by + have _: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by intro b rw [tsum_eq_single (f b)] simp diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 34a4c3ce..9527b59f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -61,14 +61,11 @@ lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by simp [Summable.hasSum_iff ENNReal.summable] set f : ℕ → Fin n := fun a => a set p : SLang ℕ := UniformSample n - have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by - rfl rw [←push_forward_prob_is_prob p f] + have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if b= f a then p a else 0) := by + unfold push_forward + rfl simp [h1] - have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop - conv => - enter [2, 1, z, 1, a] - rw [←h2] simp [p] @@ -114,14 +111,7 @@ theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed) a else 0)) := by unfold push_forward - have h1 (x: List Bool): (fun a => List.count true a) x = List.count true x := by aesop - conv => - enter [1,s,1 ,t ] - rw [h1] - have h2 (x:ENNReal)(s: Nat)(t: List Bool): (if List.count true t = s then x else 0) = (if s =List.count true t then x else 0) := by aesop - conv => - enter [1,s,1,t] - rw [h2] + rfl rw [← h] rw [push_forward_prob_is_prob] simp [MultiBernoulli.MultiBernoulliSample_normalizes] @@ -166,5 +156,21 @@ HasSum (ShuffleModel query num den h l) 1 := by def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ + +def ShuffleModel_is_privPostProcess (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : +ShuffleModel query num den h l = privPostProcess (RandomizedResponse.RRSample query num den h l) (Shuffler) (l) := by + unfold ShuffleModel + unfold RandomizedResponse.RRSample + simp [privPostProcess] + sorry + theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : -DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by sorry +DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by + unfold ShuffleModel_PMF + unfold ShuffleModel + simp [pure, bind] + unfold probBind + + sorry + +end SLang From 5af56447fe77376158901d985ae5910384c9e325 Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 13 Aug 2025 14:00:13 -0700 Subject: [PATCH 152/216] Update ENNRealLemmasSuite.lean --- .../Pure/Local/ENNRealLemmasSuite.lean | 61 ++----------------- 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index 4c8c4a96..03988ed9 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -26,22 +26,7 @@ lemma ennreal_div_one (a: ENNReal) : a / 1 = a := by simp_all only [div_one] lemma ennreal_mul_assoc (a b c : ENNReal): a * c + b * c = (a + b) * c := by ring lemma le_add_non_zero (a b :ENNReal)(h: b ≠ 0)(h2: a ≠ ⊤): a < a+b := by - rw [@lt_iff_le_and_ne] - apply And.intro - simp_all - simp - rw [Not] - intro c - have gg : a + 0 =a +b := by - simp - exact c - rw [ENNReal.add_right_inj] at gg - symm at gg - subst gg - have hh: (0 ≠ 0) → False := by simp - apply hh - exact_mod_cast h - exact h2 + exact ENNReal.lt_add_right h2 h lemma sub_le_add_ennreal (a b :ENNReal)(h1: b ≠ 0)(h3: b ≤ a)(h4: a ≠ ⊤): a -b < a +b := by apply ENNReal.sub_lt_of_lt_add @@ -243,47 +228,9 @@ lemma sub_add_cancel_ennreal (a b :ENNReal)(h:b≤ a)(h1 : b ≠ ⊤): a -b +b = lemma le_double (a b c : ENNReal)(h1 : a ≤ b)(h2 : c ≤ d)(htop1: a ≠ ⊤)(htop2 : c ≠ ⊤): a * c ≤ b * d := by - rw [@Decidable.le_iff_eq_or_lt] - rw [@Decidable.le_iff_eq_or_lt] at h1 - rw [@Decidable.le_iff_eq_or_lt] at h2 - cases h1 with - | inl h1l => - cases h2 with - | inl h2l => - left - rw [h1l] - rw [h2l] - | inr h2r => - cases bzero : b == 0 with - | true => - left - subst h1l - simp_all only [ne_eq, not_false_eq_true, beq_iff_eq, zero_mul] - | false => - right - subst h1l - simp_all only [beq_eq_false_iff_ne] - rw [propext (ENNReal.mul_lt_mul_left bzero htop1)] - exact h2r - | inr hr => - cases h2 with - | inl h2l => - rw [← h2l] - cases czero : c == 0 with - | true => - left - subst h2l - simp_all only [ne_eq, beq_iff_eq, mul_zero] - | false => - right - rw [@beq_eq_false_iff_ne] at czero - rw [propext (ENNReal.mul_lt_mul_right czero htop2)] - exact hr - | inr h2r => - right - apply ENNReal.mul_lt_mul - exact hr - exact h2r + apply mul_le_mul_of_nonneg + all_goals aesop + lemma pnat_zero_imp_false2 (den : PNat): (den : Nat) = 0 -> False := by aesop From 55778911869be1d68b6b16ebea0026a9d4bb6285 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Wed, 13 Aug 2025 17:50:17 -0400 Subject: [PATCH 153/216] added postprivprocess for DP Update --- .../Local/LocalDP/DPwithUpdateNeighbour.lean | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index 8c58ee8f..6084fbca 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -40,3 +40,44 @@ theorem singleton_to_event_update (m : Mechanism T U) (ε : ℝ) (h : DP_singlet · right simp exact Real.exp_pos ε + +theorem event_to_singleton_update (m : Mechanism T U) (ε : ℝ)(h: DP_withUpdateNeighbour m ε): + DP_singleton_withUpdateNeighbour m ε := by + simp [DP_singleton_withUpdateNeighbour] + intros l₁ l₂ h1 x + replace h1 := h l₁ l₂ h1 {x} + simp at h1 + rw [tsum_eq_single x] at h1 + · simp at h1 + rw [tsum_eq_single x] at h1 + · simp at h1 + trivial + · aesop + · aesop + +theorem event_eq_singleton_update (m : Mechanism T U) (ε : ℝ) : + DP_withUpdateNeighbour m ε ↔ DP_singleton_withUpdateNeighbour m ε := by + constructor + · apply event_to_singleton_update + · apply singleton_to_event_update + +lemma privPostProcess_DPwithUpdate_bound{nq : Mechanism T U} {ε : NNReal} (h : DP_withUpdateNeighbour nq ε) (f : U → V) : + DP_withUpdateNeighbour (privPostProcess nq f) ε := by + rw [event_eq_singleton_update] at * + simp [DP_singleton_withUpdateNeighbour] at * + intros l₁ l₂ neighbours x + replace h := h l₁ l₂ neighbours + simp [privPostProcess] + apply ENNReal.div_le_of_le_mul + rw [← ENNReal.tsum_mul_left] + apply tsum_le_tsum _ ENNReal.summable (by aesop) + intro i + split + · rename_i h + subst h + refine (ENNReal.div_le_iff_le_mul ?inl.hb0 ?inl.hbt).mp (h i) + · aesop + · right + simp + exact Real.exp_pos ε + · simp From 8166b737df823c885214958cd5d83971e09eb765 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Thu, 14 Aug 2025 12:16:29 -0700 Subject: [PATCH 154/216] nochange --- .../Local/MultiBernoulli.lean | 0 .../Pure/Local/LocalDP/LocalDP.lean | 15 ++++ .../Pure/Local/LocalDP/LocalToDataset.lean | 79 +++++++++++++++++++ .../Properties/DPProof.lean | 28 ++++++- .../Properties/Reduction.lean | 22 ++++-- 5 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalDP.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean new file mode 100644 index 00000000..e69de29b diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalDP.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalDP.lean new file mode 100644 index 00000000..4571ae14 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalDP.lean @@ -0,0 +1,15 @@ +import SampCert + +namespace SLang + +/- We define local differential privacy. The definition is the same as + for central DP, but the mechanisms take in a user, not a dataset, as input. + as input. -/ + +open Classical + +abbrev LocalMechanism (T U : Type) := T → PMF U + +def Local_DP (m : LocalMechanism T U) (ε : ℝ) : Prop := + ∀ u₁ u₂ : T, ∀ y : U, + m u₁ y / m u₂ y ≤ ENNReal.ofReal (Real.exp ε) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean new file mode 100644 index 00000000..c4fb134c --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -0,0 +1,79 @@ +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.LocalDP +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.ProbabilityProduct +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Reduction + +namespace SLang + +open Classical + +def local_to_dataset (m : LocalMechanism T U) (l : List T) : SLang (List U) := + (l.mapM (fun x => (m x).1)) + +lemma local_to_dataset_normalizes (m : LocalMechanism T U) (l : List T): +HasSum (local_to_dataset m l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + rw [local_to_dataset] + apply norm_func_norm_on_list + intro x + rw [← Summable.hasSum_iff ENNReal.summable] + exact (m x).2 + +def local_to_dataset_PMF (m : LocalMechanism T U) (l : List T) : PMF (List U) := + ⟨local_to_dataset m l, local_to_dataset_normalizes m l⟩ + +lemma local_to_dataset_diff_lengths (l₁ : List T) (x : List U) (hlen : l₁.length ≠ x.length): + local_to_dataset m l₁ x = 0 := by + induction l₁ generalizing x with + | nil => simp [local_to_dataset, -mapM] + aesop + | cons hd tl ih => + simp [local_to_dataset, -mapM] + simp [local_to_dataset, -mapM] at ih + intro b + apply Or.inr + intro y hy + subst hy + simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] + +lemma local_to_dataset_prob_of_ind_prob_PMF (m: LocalMechanism T U) (l : List T) (a: List U) (k : l.length = a.length) : + local_to_dataset_PMF m l a = (∏'(i: Fin l.length), m (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob + +theorem local_to_dataset_reduction {T β: Type} (a b : List T) (n m : T) (l₁ l₂: List T)(x: List β)(f: T → PMF β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) +(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) +(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) + := reduction_final l₁ l₂ a b n m x _ h1 h2 hx hy (by simp[nonzero]) noninf + +/- We need a stronger version of reduction, where we only assume it's nonzero for b of the same length. -/ + +lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) + (nonzero: ∀ (k : T) (bo : U), (m k) bo ≠ 0) + (noninf: ∀ (k : T) (bo : U), (m k) bo ≠ ⊤): + Local_DP m ε → DP_withUpdateNeighbour (local_to_dataset_PMF m) ε := by + intro hloc + apply singleton_to_event_update + intros l₁ l₂ h_adj x + cases xlen1 : l₁.length == x.length with + | true => simp at xlen1 + have xlen2: l₂.length = x.length := by + rw [←xlen1] + rw[←UpdateNeighbour_length h_adj] + rw[local_to_dataset_prob_of_ind_prob_PMF m l₁ x xlen1] + rw[local_to_dataset_prob_of_ind_prob_PMF m l₂ x xlen2] + cases h_adj with + | Update hl₁ hl₂ => + rename_i a y c z + simp + rw [local_to_dataset_reduction a c y z l₁ l₂ x m hl₁ hl₂ xlen1 xlen2 _ noninf] + simp[Local_DP] at hloc + apply hloc + intro i + apply nonzero + | false => simp at xlen1 + rw [←Ne.eq_def] at xlen1 + have numerator_zero: local_to_dataset_PMF m l₁ x = 0 := local_to_dataset_diff_lengths l₁ x xlen1 + rw [numerator_zero] + rw [@ENNReal.zero_div] + simp diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean index 5c171fed..f099dfd1 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean @@ -1,6 +1,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Basic import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.BasicLemmas import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Reduction +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.LocalToDataset open SLang open ENNRealLemmas @@ -31,7 +32,7 @@ cases xlen1 : l₁.length == x.length with have xlen2 : l₂.length = x.length := by aesop simp have xlen3 : l₁.length = x.length := by aesop - rw[reduction_final l₁ l₂ x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] + rw[reduction_final l₁ l₂ a b n m x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] have i1: a.length < x.length := by rw[←xlen3] subst hl₁ hl₂ @@ -48,7 +49,8 @@ cases xlen1 : l₁.length == x.length with exact h _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by aesop } - {apply RRSingleSample_non_zero query num den h} + {intro i + apply RRSingleSample_non_zero query num den h} {apply RRSingleSample_finite query num den h} | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 @@ -58,3 +60,25 @@ cases xlen1 : l₁.length == x.length with rw [numerator_zero] rw [@ENNReal.zero_div] simp + +/- A different perspective-/ +def RRSingle_Local (query : T → Bool) (num: Nat) (den : PNat) (h: 2 * num < den): LocalMechanism T Bool := + fun l => ⟨RRSingleSample query num den h l, RRSingleSample_PMF_helper query num den h l⟩ + +lemma RR_Local_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < den): Local_DP (RRSingle_Local query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by + rw [Local_DP] + intro u₁ u₂ y + simp [RRSingle_Local] + have h1: RRSingleSample query num den h u₁ y / RRSingleSample query num den h u₂ y ≤ ENNReal.ofReal (Real.exp (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num)))) := by + calc + RRSingleSample query num den h u₁ y / RRSingleSample query num den h u₂ y ≤ (↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num) := final_bound query num den h u₁ u₂ y + _ = ENNReal.ofReal (Real.exp (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num)))) := by rw [final_step_combined num den h] + apply h1 + +lemma RRSample_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < den): DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by + have h1: RRSample_PMF query num den h = local_to_dataset_PMF (RRSingle_Local query num den h) := rfl + rw [h1] + apply LocalDP_to_dataset + apply RRSingleSample_non_zero query num den h + apply RRSingleSample_finite query num den h + apply RR_Local_DP diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean index 65694de2..df7d4254 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/Reduction.lean @@ -95,8 +95,14 @@ lemma valid_index3 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b rw[←hx] apply valid_index2 h1 i +lemma valid_index8 {β: Type}{l₁ : List T} {x : List β} (h1: l₁ = a++[n]++b) (hx: l₁.length = x.length) (i : Fin ((l₁.length - 1) + 1)): + i.val < x.length := by + rw[←hx] + apply valid_index2 h1 i -lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / +lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) + (nonzero: ∀ (i : Fin (l₂.length - 1)), f (l₂[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) (x[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) ≠ 0) + (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / (∏' (i : Fin ((l₂.length-1)+1)), f (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by rw[tprod_fintype] rw[tprod_fintype] @@ -123,8 +129,8 @@ lemma reduction2 {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1 simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] rw[Finset.prod_ne_zero_iff] - intro i - simp[nonzero] + intro i _ + apply nonzero i rw[← lt_top_iff_ne_top] apply ENNReal.prod_lt_top intro i @@ -139,10 +145,12 @@ lemma conversion {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.le rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] simp -theorem reduction_final {β: Type}(l₁ l₂: List T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0)(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / - (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by - have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith - have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith +theorem reduction_final {β: Type}(l₁ l₂ a b: List T)(n m : T)(x: List β)(f: T → SLang β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) + (nonzero: ∀ (i : Fin (l₂.length - 1)), f (l₂[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) (x[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) ≠ 0) + (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / + (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by + have hl2: l₂.length ≥ 1 := by rw[h2];simp; omega + have hl1: l₁.length ≥ 1 := by rw[h1];simp; omega rw[conversion l₂ x h2 hl2 hy f] rw[conversion l₁ x h1 hl1 hx f] rw [reduction2 l₁ l₂ x f h1 h2 hx hy nonzero noninf] From 3622b22cbab666832799e4c111748bde2ced8d11 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Thu, 14 Aug 2025 13:33:25 -0700 Subject: [PATCH 155/216] New Model of Shuffler --- .../Local/RandomizedResponse/Definitions.lean | 14 +- .../Pure/Local/ShuffleModel/Definitions.lean | 215 ++++++++++-------- .../Pure/Local/ShuffleModel/junk.lean | 93 ++++++++ .../presentation-code/Examples.lean | 35 +++ 4 files changed, 256 insertions(+), 101 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean create mode 100644 SampCert/DifferentialPrivacy/presentation-code/Examples.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index 102ba9b2..98f003cc 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -6,28 +6,28 @@ namespace RandomizedResponse open SLang +/- Arithmetic lemma for the next definition. -/ lemma arith_0 (num : Nat) (den : PNat) (_ : 2 * num < den): den - 2*num ≤ 2 * den := by simp_all only [tsub_le_iff_right] linarith +/- RRSinglePushForward performs the Randomized Response algorithm, but + associates each user with their private response. +-/ def RRSinglePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : SLang Bool := do let r ← SLang.BernoulliSample (den - 2*num) (2 * den) (arith_0 num den h) return Bool.xor (l) r +/- Single-user Randomized Response with a gven query. -/ def RRSingleSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : T) : SLang Bool := do RRSinglePushForward num den h (query l) -def Y (query : T -> Bool): Bool -> (T -> Bool) := fun r => (fun l => Bool.xor (query l) r) -/- Y is a random variable which outputs the function measuring whether or not a given person - changes their answer. It is distributed according to the probability distribution - from which we sample r.-/ - +/- Extension of Randomized Response to a dataset by use of monadic map. -/ def RRSample {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : SLang (List Bool) := do -/- RRSample uses monadic map to apply RRSingleSample on an entire dataset. -/ l.mapM (fun x => RRSingleSample query num den h x) +/- The next definition is used in RAPPOR. -/ def RRSamplePushForward (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) : SLang (List Bool) := do - /- For use in RAPPOR -/ l.mapM (fun x => RRSinglePushForward num den h x) end RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index ef9e604f..878be721 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -5,115 +5,54 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour - +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code namespace SLang -/- Uniform Sample allows to draw a uniform sample n, but returns type Fin n. This allows -us to prove the index is valid in the Shuffler function -/ -def UniformSample' (n : PNat) : SLang (Fin n) := do - let r ← UniformSample n - return (r : Fin n) +def Shuffler {α: Type}(l:List α) := do +match l with +| [] => pure [] +| hd::tl => + let len := (hd :: tl).length + let i : Nat ← UniformSample (Nat.toPNat' len) + let rest : List α ← Shuffler tl + return rest.insertNth i hd +#check Shuffler -lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by - constructor - intro h - rw [h] - apply Nat.mod_lt - simp - intro h - exact Eq.symm (Nat.mod_eq_of_lt h) -/- Proves that an output drawn from the Uniform Sample has the same probability as -an output drawn for UniformSample' given the n values are the same. -/ -lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by - unfold UniformSample' - conv => - lhs - simp [pure, bind] - rw [tsum_eq_single x.val] - simp_all only [Fin.cast_val_eq_self, ↓reduceIte, Fin.is_lt, UniformSample_apply, one_div] - intro b' a - simp_all only [ne_eq, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [Fin.val_natCast] - rw [Not] at a - have h : b' < n → False := by - intro h1 - rw [← fin_helper] at h1 - apply a - exact h1 - rw [← Not] at h - rw [Nat.not_lt_eq] at h - simp_all only [imp_false, ge_iff_le, UniformSample_apply_out] - -lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n := by - rw [UniformSample'_eq_UnformSample] - exact UniformSample_apply n x.val (Fin.is_lt x) - -lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by - rw [UniformSample'] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - set f : ℕ → Fin n := fun a => a - set p : SLang ℕ := UniformSample n - have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by - rfl - rw [←push_forward_prob_is_prob p f] - simp [h1] - have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop - conv => - enter [2, 1, z, 1, a] - rw [←h2] - simp [p] - - -/- The Shuffler follows the Fischer-Yates method for shuffling lists. -/ -def Shuffler {α: Type}(l:List α) := do - let mut a := l.toArray - let mut b : Array α := Array.empty - for h: i in [1:a.size] do - let j ← UniformSample' (Nat.toPNat' i+1) - - b := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h rfl⟩ ⟨j, by - aesop - have h1: j ≤ i := by - rw [@Fin.le_iff_val_le_val] - norm_num - aesop - have h1: j.val < i.toPNat' + 1 := j.2 - aesop - rw[← Nat.lt_add_one_iff] - exact h1 - linarith[h.1] - have h2: i < l.length := by exact Membership.get_elem_helper h rfl - exact Nat.lt_of_le_of_lt h1 (by aesop) - ⟩ - return b.toList -#check Shuffler +def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do + let seeds := List.replicate n seed + let list ← MultiBernoulli.MultiBernoulliSample (seeds) + let k := List.count true list + return k + + + + -def BinomialSample (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) := do - let mut acc := 0 - for _ in [0:n] do - let b ← BernoulliSample num den h - if b=True then - acc := acc + 1 - return acc -theorem BinomialSample_norms (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) : - HasSum (BinomialSample num den h n) 1 := by sorry -theorem BinomialSample_kprob (num : Nat) (den : PNat) (h: num ≤ den) (n : PNat) (k : Nat) : - BinomialSample num den h n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by + + + + + +theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : + BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by + rw[BinomialSample] + simp + unfold MultiBernoulli.MultiBernoulliSample + simp [pure, bind] + sorry + + /- This is the Shuffle Model. -/ def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l @@ -133,6 +72,94 @@ lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffle simp [pure, bind] sorry +lemma one_step {α: Type}[BEq α](hd: α)(tl: List α)(l: List α)(h: List.isPerm (hd::tl) l): Shuffler (hd::tl) l = Shuffler tl (l.erase hd) / (tl.length+1) := by + unfold Shuffler + simp[probBind,pure,pure_apply] + have h: (List.toArray (hd::tl)).size = (List.toArray tl).size+1 := by + simp + rename_i inst h_1 + rw[tsum_eq_single (List.toArray l)] + rw[tsum_eq_single (List.toArray l)] + simp + unfold UniformSample' + simp + aesop + + + + + + + + +lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen: n = l₁.length)(hlen2: n = l₂.length)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by + induction l₁ generalizing l₂ n with + | nil => + simp [List.isPerm] at h + have h1: l₂ = [] := by sorry + subst h1 + sorry + | cons hd tl ih => + simp [List.isPerm] at h + have h1: Shuffler tl (l₂.erase hd) = 1 / (tl.length).factorial := by + rw [ih (l₂.erase hd)] + rfl + have h2: tl.length = n - 1 := by simp[hlen] + rw [h2] + have h3: (l₂.erase hd).length = l₂.length - 1 := by sorry + rw [h3] + sorry + exact h.right + rw[one_step] + rw[ih] + + + + /- induction n generalizing l₁ l₂ + case zero => + simp + symm at hlen + rw[List.length_eq_zero] at hlen + symm at hlen2 + rw[List.length_eq_zero] at hlen2 + rw[hlen, hlen2] + simp [Shuffler] + aesop + sorry + case succ x ih => + + cases h + case nil => + simp at hlen + case cons hd tl₁ tl₂ h => + simp at hlen + simp at hlen2 + sorry + -/ + + +/--/ + cases l₁ + case nil => + simp at hlen + case cons hd tl => + cases l₂ + case nil => + simp at hlen2 + case cons hd2 tl2 => + + + + + + + + + + + + + lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : HasSum (ShuffleModel query num den h l) 1 := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean new file mode 100644 index 00000000..5f53deef --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean @@ -0,0 +1,93 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code +import SampCert.Samplers.Uniform.Properties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.PushForward +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +namespace SLang + +/- Uniform Sample allows to draw a uniform sample n, but returns type Fin n. This allows +us to prove the index is valid in the Shuffler function -/ +def UniformSample' (n : PNat) : SLang (Fin n) := do + let r ← UniformSample n + return (r : Fin n) + +lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by + constructor + intro h + rw [h] + apply Nat.mod_lt + simp + intro h + exact Eq.symm (Nat.mod_eq_of_lt h) + +/- Proves that an output drawn from the Uniform Sample has the same probability as +an output drawn for UniformSample' given the n values are the same. -/ +lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by + unfold UniformSample' + conv => + lhs + simp [pure, bind] + rw [tsum_eq_single x.val] + simp_all only [Fin.cast_val_eq_self, ↓reduceIte, Fin.is_lt, UniformSample_apply, one_div] + intro b' a + simp_all only [ne_eq, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [Fin.val_natCast] + rw [Not] at a + have h : b' < n → False := by + intro h1 + rw [← fin_helper] at h1 + apply a + exact h1 + rw [← Not] at h + rw [Nat.not_lt_eq] at h + simp_all only [imp_false, ge_iff_le, UniformSample_apply_out] + +lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n := by + rw [UniformSample'_eq_UnformSample] + exact UniformSample_apply n x.val (Fin.is_lt x) + +lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by + rw [UniformSample'] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + set f : ℕ → Fin n := fun a => a + set p : SLang ℕ := UniformSample n + have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by + rfl + rw [←push_forward_prob_is_prob p f] + simp [h1] + have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop + conv => + enter [2, 1, z, 1, a] + rw [←h2] + simp [p] + + /- The Shuffler follows the Fischer-Yates method for shuffling lists. -/ +def Shuffler2 {α: Type}(l:List α) := do + let mut a := l.toArray + let mut b : Array α := Array.empty + for h: i in [1:a.size] do + let j ← UniformSample' (Nat.toPNat' i+1) + + a := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h (by simp;)⟩ ⟨j, by + aesop + have h1: j ≤ i := by + rw [@Fin.le_iff_val_le_val] + norm_num + aesop + have h1: j.val < i.toPNat' + 1 := j.2 + aesop + rw[← Nat.lt_add_one_iff] + exact h1 + linarith[h.1] + + have h2: i < l.length := by exact Membership.get_elem_helper h rfl + exact Nat.lt_of_le_of_lt h1 (by aesop) + ⟩ + return a.toList diff --git a/SampCert/DifferentialPrivacy/presentation-code/Examples.lean b/SampCert/DifferentialPrivacy/presentation-code/Examples.lean new file mode 100644 index 00000000..3c72a25b --- /dev/null +++ b/SampCert/DifferentialPrivacy/presentation-code/Examples.lean @@ -0,0 +1,35 @@ +import SampCert + +/- A tactic is a proof strategy that can be applied to simplify a proof goal. + In practice, proving a statement involves using a series to tactic to manipulate the + proof state and reducing the goal to previously established theorems or hypotheses. -/ + +/- The simplest tactic is `rfl`, which handles definitional equality -/ +lemma two_plus_two : 2 + 2 = 4 := rfl + +/- Once we have the above lemma, we can use it to prove other lemmas: -/ +lemma two_plus_two' : 4 = 2 + 2 := (Eq.symm two_plus_two) + +lemma real_add_pres_eq (a b c : ℝ): a = b → a + c = b + c := by + intro h + rw [h] + /- We could also do `rw[h]` instead of `rewrite`, which automatically uses `rfl` to close the goal -/ + +/- If our goal is `B`, and we know that `A → B` then the `apply` tactic says that it's enough to prove `A` -/ +lemma silly_arithmetic (c : ℝ): (2 *2 - 1) + c = 3 + c := by + apply real_add_pres_eq + ring + +/- Propositions are types in Lean! -/ + +def List.add_1_to_hd (l : List ℕ) (h : l ≠ []) := List.head l h + 1 + +/- First, notice that we are using a dependent type: List ℕ -/ + +/- What is the type of the function List.head_add_1? Is it List ℕ → List ℕ? -/ + +#check List.add_1_to_hd + +/- `l ≠ []` is a type, and an inhabitant of this type is a proof that `l` is not empty! -/ + +theorem Fermat : ∀ (a b c : ℕ) (n : ℕ), n > 2 → a^n + b^n = c^n → False := sorry From bc8c47363bdf501f1f628da301a861aaa7c9eef9 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Thu, 14 Aug 2025 16:39:42 -0400 Subject: [PATCH 156/216] cleaned defs --- .../Pure/Local/ShuffleModel/Definitions.lean | 172 ++---------------- .../Pure/Local/ShuffleModel/junk.lean | 130 +++++++++++++ 2 files changed, 145 insertions(+), 157 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 52fec342..4aea2e06 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -21,26 +21,27 @@ match l with return rest.insertNth i hd #check Shuffler - - - - def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do let seeds := List.replicate n seed let list ← MultiBernoulli.MultiBernoulliSample (seeds) let k := List.count true list return k - - - - - - - - - - +theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : + HasSum (BinomialSample seed n) 1 := by + rw [BinomialSample] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) + (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => + (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample + (List.replicate (↑n) seed) a else 0)) := by + unfold push_forward + rfl + rw [← h] + rw [push_forward_prob_is_prob] + simp [MultiBernoulli.MultiBernoulliSample_normalizes] theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by @@ -50,146 +51,3 @@ theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat simp [pure, bind] sorry - - - - - -/- This is the Shuffle Model. -/ -def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do - let l ← RandomizedResponse.RRSample query num den h l - let b ← Shuffler l - return b - -#check ShuffleModel - - -lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold Shuffler - rename_i inst - simp_all only [bind, PNat.add_coe, PNat.val_ofNat, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, - mul_ite, mul_one, mul_zero] - unfold probBind - simp [pure, bind] - sorry - -lemma one_step {α: Type}[BEq α](hd: α)(tl: List α)(l: List α)(h: List.isPerm (hd::tl) l): Shuffler (hd::tl) l = Shuffler tl (l.erase hd) / (tl.length+1) := by - unfold Shuffler - simp[probBind,pure,pure_apply] - have h: (List.toArray (hd::tl)).size = (List.toArray tl).size+1 := by - simp - rename_i inst h_1 - rw[tsum_eq_single (List.toArray l)] - rw[tsum_eq_single (List.toArray l)] - simp - unfold UniformSample' - simp - aesop - - - - - - - - -lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen: n = l₁.length)(hlen2: n = l₂.length)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n with - | nil => - simp [List.isPerm] at h - have h1: l₂ = [] := by sorry - subst h1 - sorry - | cons hd tl ih => - simp [List.isPerm] at h - have h1: Shuffler tl (l₂.erase hd) = 1 / (tl.length).factorial := by - rw [ih (l₂.erase hd)] - rfl - have h2: tl.length = n - 1 := by simp[hlen] - rw [h2] - have h3: (l₂.erase hd).length = l₂.length - 1 := by sorry - rw [h3] - sorry - exact h.right - rw[one_step] - rw[ih] - - - - /- induction n generalizing l₁ l₂ - case zero => - simp - symm at hlen - rw[List.length_eq_zero] at hlen - symm at hlen2 - rw[List.length_eq_zero] at hlen2 - rw[hlen, hlen2] - simp [Shuffler] - aesop - sorry - case succ x ih => - - cases h - case nil => - simp at hlen - case cons hd tl₁ tl₂ h => - simp at hlen - simp at hlen2 - sorry - -/ - - -/--/ - cases l₁ - case nil => - simp at hlen - case cons hd tl => - cases l₂ - case nil => - simp at hlen2 - case cons hd2 tl2 => - - - - - - - - - - - - - - -lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -HasSum (ShuffleModel query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold ShuffleModel - simp [pure,bind] - unfold RandomizedResponse.RRSample - sorry - - -def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ - - -def ShuffleModel_is_privPostProcess (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -ShuffleModel query num den h l = privPostProcess (RandomizedResponse.RRSample query num den h l) (Shuffler) (l) := by - unfold ShuffleModel - unfold RandomizedResponse.RRSample - simp [privPostProcess] - sorry - -theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : -DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by - unfold ShuffleModel_PMF - unfold ShuffleModel - simp [pure, bind] - unfold probBind - - sorry - -end SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean index 5f53deef..7438201b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean @@ -91,3 +91,133 @@ def Shuffler2 {α: Type}(l:List α) := do exact Nat.lt_of_le_of_lt h1 (by aesop) ⟩ return a.toList + + /- This is the Shuffle Model. -/ +def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + let b ← Shuffler l + return b + +lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold Shuffler + rename_i inst + simp_all only [bind, PNat.add_coe, PNat.val_ofNat, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, + mul_ite, mul_one, mul_zero] + unfold probBind + simp [pure, bind] + sorry + +lemma one_step {α: Type}[BEq α](hd: α)(tl: List α)(l: List α)(h: List.isPerm (hd::tl) l): Shuffler (hd::tl) l = Shuffler tl (l.erase hd) / (tl.length+1) := by + unfold Shuffler + simp[probBind,pure,pure_apply] + have h: (List.toArray (hd::tl)).size = (List.toArray tl).size+1 := by + simp + rename_i inst h_1 + rw[tsum_eq_single (List.toArray l)] + rw[tsum_eq_single (List.toArray l)] + simp + unfold UniformSample' + simp + aesop + + +lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen: n = l₁.length)(hlen2: n = l₂.length)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by + induction l₁ generalizing l₂ n with + | nil => + simp [List.isPerm] at h + have h1: l₂ = [] := by sorry + subst h1 + sorry + | cons hd tl ih => + simp [List.isPerm] at h + have h1: Shuffler tl (l₂.erase hd) = 1 / (tl.length).factorial := by + rw [ih (l₂.erase hd)] + rfl + have h2: tl.length = n - 1 := by simp[hlen] + rw [h2] + have h3: (l₂.erase hd).length = l₂.length - 1 := by sorry + rw [h3] + sorry + exact h.right + rw[one_step] + rw[ih] + + + + /- induction n generalizing l₁ l₂ + case zero => + simp + symm at hlen + rw[List.length_eq_zero] at hlen + symm at hlen2 + rw[List.length_eq_zero] at hlen2 + rw[hlen, hlen2] + simp [Shuffler] + aesop + sorry + case succ x ih => + + cases h + case nil => + simp at hlen + case cons hd tl₁ tl₂ h => + simp at hlen + simp at hlen2 + sorry + -/ + + +/--/ + cases l₁ + case nil => + simp at hlen + case cons hd tl => + cases l₂ + case nil => + simp at hlen2 + case cons hd2 tl2 => + + + + + + + + + + + + + + +lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : +HasSum (ShuffleModel query num den h l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + unfold ShuffleModel + simp [pure,bind] + unfold RandomizedResponse.RRSample + sorry + + +def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := + ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ + + +def ShuffleModel_is_privPostProcess (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : +ShuffleModel query num den h l = privPostProcess (RandomizedResponse.RRSample query num den h l) (Shuffler) (l) := by + unfold ShuffleModel + unfold RandomizedResponse.RRSample + simp [privPostProcess] + sorry + +theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : +DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by + unfold ShuffleModel_PMF + unfold ShuffleModel + simp [pure, bind] + unfold probBind + + sorry + +end SLang From e2359cb9b9c2ab4cad89d92bc8a2a58da62b5d7e Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 11:04:56 -0400 Subject: [PATCH 157/216] - --- .../Pure/Local/ShuffleModel/Definitions.lean | 46 ++++++++++++++++++- .../Pure/Local/ShuffleModel/junk.lean | 6 +-- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 4aea2e06..2c4e74a6 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -10,7 +10,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties namespace SLang - def Shuffler {α: Type}(l:List α) := do match l with | [] => pure [] @@ -19,7 +18,44 @@ match l with let i : Nat ← UniformSample (Nat.toPNat' len) let rest : List α ← Shuffler tl return rest.insertNth i hd -#check Shuffler + +#eval List.insertNth 0 1 [2,3] +#eval List.eraseIdx [1,2,3] 0 +lemma insertNth_helper {α : Type}(b a_1: List α)(a: Nat)(h: α): b = List.insertNth a h a_1 ↔ a_1 = List.eraseIdx b a := by sorry + + +lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by + unfold Shuffler + rw [h] + simp [pure] + + + +lemma Shuffler_norm [DecidableEq α]{α: Type}(l:List α): HasSum (Shuffler l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + induction l with + | nil => + unfold Shuffler + simp [pure] + unfold probPure + simp + | cons h t ih => + unfold Shuffler + simp [pure] + rw [← Summable.hasSum_iff ENNReal.summable] + rw [Summable.hasSum_iff ENNReal.summable] + simp + conv => + enter [1,1,b,1,a,2,1,a_1] + rw [insertNth_helper] + rename_i α_1 inst + conv => + enter [1, 1, b, 1, a, 2, 1, a_1] + rw [tsum_add] + have h (a_1:List α)(b:List α)(a : Nat): (if a_1 = b.eraseIdx a then Shuffler t a_1 else 0) = Shuffler t a_1 := by sorry + + + def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do let seeds := List.replicate n seed @@ -51,3 +87,9 @@ theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat simp [pure, bind] sorry + + /- This is the Shuffle Model. -/ +def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + let b ← Shuffler l + return b diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean index 7438201b..6880f93a 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean @@ -92,11 +92,7 @@ def Shuffler2 {α: Type}(l:List α) := do ⟩ return a.toList - /- This is the Shuffle Model. -/ -def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do - let l ← RandomizedResponse.RRSample query num den h l - let b ← Shuffler l - return b + lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] From a077c0febd2685e9f6f4aa0a2ed9d808fa6e833f Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Fri, 15 Aug 2025 08:11:42 -0700 Subject: [PATCH 158/216] tsum --- .../Pure/Local/PushForward.lean | 17 ++++++++- .../Pure/Local/ShuffleModel/Definitions.lean | 37 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean index cfb1a413..1603d44d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean @@ -6,7 +6,7 @@ open SLang We use this when defining UniformSample', i.e., UniformSample as an SLang (Fin n). -/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := - fun s => ∑' (t : T), if s = f t then p t else 0 + fun s => ∑' (t : T), if f t = s then p t else 0 lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : ∑' (s : S), (push_forward p f) s = 1 := by @@ -22,3 +22,18 @@ lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : subst a_1 simp_all only [not_true_eq_false] simp_all + +lemma push_forward_prob_is_prob_gen {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = n) : + ∑' (s : S), (push_forward p f) s = n := by + simp [push_forward] + rw [@ENNReal.tsum_comm] + have _: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by + intro b + rw [tsum_eq_single (f b)] + simp + intro b' a + simp_all only [ne_eq, ite_eq_right_iff] + intro a_1 + subst a_1 + simp_all only [not_true_eq_false] + simp_all diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 4aea2e06..bff2c31e 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -51,3 +51,40 @@ theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat simp [pure, bind] sorry + + /- This is the Shuffle Model. -/ +def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + let b ← Shuffler l + return b + +lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by + induction l₁ generalizing l₂ n + simp at hlen1 + rw[symm hlen1] + simp + unfold Shuffler + simp + symm at hlen1 + rw[hlen1] at hlen2 + rw[List.length_eq_zero] at hlen2 + exact hlen2 + + case cons hd tl ih => + unfold Shuffler + simp + simp at hlen1 + rw[symm hlen1] + conv => + enter[1,1,a,2] + rw[tsum_eq_single (List.eraseIdx l₂ a ) (by + intro b' h + simp + + aesop + have h2: b' = List.eraseIdx (List.insertNth a hd b') a := by rw[List.eraseIdx_insertNth] + contradiction + )] + rfl + + rw[tsum_eq_single] From 5f2582e968554b99967f3110010c54a6c85e0269 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 13:18:10 -0400 Subject: [PATCH 159/216] shuffler_norms --- .../Pure/Local/PushForward.lean | 2 +- .../Pure/Local/ShuffleModel/Definitions.lean | 37 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean index 1603d44d..4fe04bbd 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean @@ -6,7 +6,7 @@ open SLang We use this when defining UniformSample', i.e., UniformSample as an SLang (Fin n). -/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := - fun s => ∑' (t : T), if f t = s then p t else 0 + fun s => ∑' (t : T), if s = f t then p t else 0 lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : ∑' (s : S), (push_forward p f) s = 1 := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index 0a4e5a15..262afa3d 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -30,29 +30,45 @@ lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by simp [pure] - -lemma Shuffler_norm [DecidableEq α]{α: Type}(l:List α): HasSum (Shuffler l) 1 := by +lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with | nil => unfold Shuffler simp [pure] unfold probPure - simp + rw [ENNReal.tsum_eq_add_tsum_ite []] + aesop | cons h t ih => unfold Shuffler simp [pure] rw [← Summable.hasSum_iff ENNReal.summable] rw [Summable.hasSum_iff ENNReal.summable] simp + rw [ENNReal.tsum_comm] + let hpf (b: Nat)(i: List α): (fun (i: List α) => ∑'(a : List α), (if i = List.insertNth b h a then Shuffler t a else 0)) = + (push_forward (Shuffler t) (fun (a: List α) => List.insertNth b h a)) := by + unfold push_forward + rfl conv => - enter [1,1,b,1,a,2,1,a_1] - rw [insertNth_helper] - rename_i α_1 inst - conv => - enter [1, 1, b, 1, a, 2, 1, a_1] - rw [tsum_add] - have h (a_1:List α)(b:List α)(a : Nat): (if a_1 = b.eraseIdx a then Shuffler t a_1 else 0) = Shuffler t a_1 := by sorry + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + rw [hpf b t] + enter [2] + apply push_forward_prob_is_prob_gen + rw [ih] + simp + + + + + + + + + + + @@ -75,6 +91,7 @@ theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType (List.replicate (↑n) seed) a else 0)) := by unfold push_forward rfl + rw [← h] rw [push_forward_prob_is_prob] simp [MultiBernoulli.MultiBernoulliSample_normalizes] From 2dc16c12795407f485f5e6ebd054f93ec3de6a5a Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 13:25:59 -0400 Subject: [PATCH 160/216] Shuffler reorganized --- .../Pure/ShuffleModel/Definitions.lean | 33 ++++++++++++++ .../Properties.lean} | 45 ++----------------- .../Pure/{Local => }/ShuffleModel/junk.lean | 0 3 files changed, 36 insertions(+), 42 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean rename SampCert/DifferentialPrivacy/Pure/{Local/ShuffleModel/Definitions.lean => ShuffleModel/Properties.lean} (77%) rename SampCert/DifferentialPrivacy/Pure/{Local => }/ShuffleModel/junk.lean (100%) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean new file mode 100644 index 00000000..470833f8 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -0,0 +1,33 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code +import SampCert.Samplers.Uniform.Properties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.PushForward +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties + +namespace SLang + +def Shuffler {α: Type}(l:List α) := do +match l with +| [] => pure [] +| hd::tl => + let len := (hd :: tl).length + let i : Nat ← UniformSample (Nat.toPNat' len) + let rest : List α ← Shuffler tl + return rest.insertNth i hd + + + /- This is the Shuffle Model. -/ +def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + let b ← Shuffler l + return b + +def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do + let seeds := List.replicate n seed + let list ← MultiBernoulli.MultiBernoulliSample (seeds) + let k := List.count true list + return k diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean similarity index 77% rename from SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean rename to SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 262afa3d..a634acfc 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -1,35 +1,21 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions -namespace SLang - -def Shuffler {α: Type}(l:List α) := do -match l with -| [] => pure [] -| hd::tl => - let len := (hd :: tl).length - let i : Nat ← UniformSample (Nat.toPNat' len) - let rest : List α ← Shuffler tl - return rest.insertNth i hd - -#eval List.insertNth 0 1 [2,3] -#eval List.eraseIdx [1,2,3] 0 -lemma insertNth_helper {α : Type}(b a_1: List α)(a: Nat)(h: α): b = List.insertNth a h a_1 ↔ a_1 = List.eraseIdx b a := by sorry +namespace SLang lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by unfold Shuffler rw [h] simp [pure] - lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with @@ -59,26 +45,6 @@ lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shu rw [ih] simp - - - - - - - - - - - - - - -def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do - let seeds := List.replicate n seed - let list ← MultiBernoulli.MultiBernoulliSample (seeds) - let k := List.count true list - return k - theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : HasSum (BinomialSample seed n) 1 := by rw [BinomialSample] @@ -96,6 +62,7 @@ theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType rw [push_forward_prob_is_prob] simp [MultiBernoulli.MultiBernoulliSample_normalizes] + theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by rw[BinomialSample] @@ -105,12 +72,6 @@ theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat sorry - /- This is the Shuffle Model. -/ -def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do - let l ← RandomizedResponse.RRSample query num den h l - let b ← Shuffler l - return b - lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by induction l₁ generalizing l₂ n simp at hlen1 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean similarity index 100% rename from SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/junk.lean rename to SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean From ee139cb90fcd7fd16691b7752a0eb67959cefa35 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 14:44:54 -0400 Subject: [PATCH 161/216] RRShuffle_norms --- .../Pure/ShuffleModel/Properties.lean | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index a634acfc..009d608b 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -7,6 +7,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties namespace SLang @@ -16,7 +17,8 @@ lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by rw [h] simp [pure] -lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by + +lemma Shuffler_PMF {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with | nil => @@ -32,7 +34,7 @@ lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shu rw [Summable.hasSum_iff ENNReal.summable] simp rw [ENNReal.tsum_comm] - let hpf (b: Nat)(i: List α): (fun (i: List α) => ∑'(a : List α), (if i = List.insertNth b h a then Shuffler t a else 0)) = + let hpf (b: Nat)(_: List α): (fun (i: List α) => ∑'(a : List α), (if i = List.insertNth b h a then Shuffler t a else 0)) = (push_forward (Shuffler t) (fun (a: List α) => List.insertNth b h a)) := by unfold push_forward rfl @@ -45,6 +47,10 @@ lemma Shuffler_norm {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shu rw [ih] simp +lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : List α), Shuffler l b = 1 := by + rw [← Summable.hasSum_iff ENNReal.summable] + apply Shuffler_PMF + theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : HasSum (BinomialSample seed n) 1 := by rw [BinomialSample] @@ -62,6 +68,21 @@ theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType rw [push_forward_prob_is_prob] simp [MultiBernoulli.MultiBernoulliSample_normalizes] +theorem RRShuffle_norms [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by + unfold RRShuffle + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply Shuffler_norms + simp + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSample_PMF_helper + theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by @@ -102,3 +123,4 @@ lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen1: l₁.len rfl rw[tsum_eq_single] + sorry From 61ea0ed8b6d4769f90978afcd53b9c2d136afa23 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Fri, 15 Aug 2025 14:29:37 -0700 Subject: [PATCH 162/216] localrandomizers --- .../Pure/Local/LocalDP/LocalToDataset.lean | 104 ++++++++++++++++-- .../Local/RAPPOR/Properties/BasicLemmas.lean | 15 ++- .../Pure/Local/RAPPOR/Properties/DPProof.lean | 59 ++++++++++ .../Properties/DPProof.lean | 22 +++- 4 files changed, 189 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean index c4fb134c..db558746 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -8,9 +8,15 @@ namespace SLang open Classical +/- We define a general way to transform local randomizers into algorithms on datasets, + and prove that the dataset-level algorithm satisfies the same DP bound as the local randomizer. -/ + +/- Transforms a local randomizer into a dataset-level algorithm. -/ def local_to_dataset (m : LocalMechanism T U) (l : List T) : SLang (List U) := (l.mapM (fun x => (m x).1)) +/- Proof of normalization for local_to_dataset. This is necessary to instantiate + local_to_dataset as a PMF. -/ lemma local_to_dataset_normalizes (m : LocalMechanism T U) (l : List T): HasSum (local_to_dataset m l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -20,9 +26,11 @@ HasSum (local_to_dataset m l) 1 := by rw [← Summable.hasSum_iff ENNReal.summable] exact (m x).2 +/- Instantiation of local_to_dataset as a PMF. -/ def local_to_dataset_PMF (m : LocalMechanism T U) (l : List T) : PMF (List U) := ⟨local_to_dataset m l, local_to_dataset_normalizes m l⟩ +/- local_to_dataset outputs datasets of different length than the input dataset with probability zero. -/ lemma local_to_dataset_diff_lengths (l₁ : List T) (x : List U) (hlen : l₁.length ≠ x.length): local_to_dataset m l₁ x = 0 := by induction l₁ generalizing x with @@ -37,19 +45,29 @@ lemma local_to_dataset_diff_lengths (l₁ : List T) (x : List U) (hlen : l₁.le subst hy simp_all only [mapM, List.length_cons, ne_eq, add_left_inj, not_false_eq_true] +/- Prob_of_ind_prob lemma for local_to_dataset. -/ lemma local_to_dataset_prob_of_ind_prob_PMF (m: LocalMechanism T U) (l : List T) (a: List U) (k : l.length = a.length) : local_to_dataset_PMF m l a = (∏'(i: Fin l.length), m (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -theorem local_to_dataset_reduction {T β: Type} (a b : List T) (n m : T) (l₁ l₂: List T)(x: List β)(f: T → PMF β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) -(nonzero: ∀(k: T) (bo: β), f k bo ≠ 0) +/- theorem local_to_dataset_reduction {T β: Type} (a b : List T) (n m : T) (l₁ l₂: List T)(x: List β)(f: T → PMF β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) +(nonzero: ∀ (i : Fin (l₂.length - 1)), f (l₂[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) (x[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) ≠ 0) (noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) - := reduction_final l₁ l₂ a b n m x _ h1 h2 hx hy (by simp[nonzero]) noninf - -/- We need a stronger version of reduction, where we only assume it's nonzero for b of the same length. -/ + := reduction_final l₁ l₂ a b n m x _ h1 h2 hx hy nonzero noninf -/ +/- This lemma states that, under a technical assumption, the algorithm + that applies the same local randomizer to each row of data satisfies the same + DP bound as the local randomizer. + The hypotheses "nonzero" and "equiv" should be regarded as technical conditions. + Essentially, they require that there exists a precise characterization of which + outputs of the local randomizer occur with probability zero. + This is necessary in order to apply the reduction lemma. + This lemma is applied in the proof of DP for both Randomized Response and One-Time Basic RAPPOR. + -/ lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) - (nonzero: ∀ (k : T) (bo : U), (m k) bo ≠ 0) + (P : T → U → Bool) + (nonzero: ∀ (k : T), ∀ (bo : U), P k bo ↔ (m k) bo ≠ 0) + (equiv: ∀ l₁ l₂ : List T, ∀ b : List U, (blen1: l₁.length = b.length) → (blen2: l₂.length = b.length) → (∀ i : Fin l₁.length, (m l₁[i]) (b[i]) = 0 ↔ (m l₂[i]) b[i] = 0)) (noninf: ∀ (k : T) (bo : U), (m k) bo ≠ ⊤): Local_DP m ε → DP_withUpdateNeighbour (local_to_dataset_PMF m) ε := by intro hloc @@ -66,11 +84,81 @@ lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) | Update hl₁ hl₂ => rename_i a y c z simp - rw [local_to_dataset_reduction a c y z l₁ l₂ x m hl₁ hl₂ xlen1 xlen2 _ noninf] + have valid_index9 (i : Fin (l₂.length - 1)): i.val + 1 < x.length := by + rw[←xlen2] + have h1: i.val < l₂.length - 1 := i.2 + omega + have valid_index10 (i : Fin (l₂.length - 1)): i.val < x.length := by + rw[←xlen2] + omega + cases P_true: ((∀ i : Fin (l₂.length - 1), P (l₂[(@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i]'(by simp[Fin.succAbove]; aesop)) (x[(@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i]'(by simp [Fin.succAbove]; aesop))) == true) with + | true => + rw [reduction_final l₁ l₂ a c y z x _ hl₁ hl₂ xlen1 xlen2 _ noninf] simp[Local_DP] at hloc apply hloc intro i - apply nonzero + apply (nonzero _ _).mp + simp at P_true + apply P_true i + | false => + simp at P_true + have nonzero2: ∀ (k : T) (bo : U), P k bo = false ↔ (m k) bo = 0 := by + intro k bo + apply not_iff_not.mp + simp + apply nonzero k bo + /- The next several "have" statements are just to prove index validity. + The proofs can undoubtedly be simplfied with some effort. -/ + have h1: a.length + (c.length + 1) = l₂.length := by + rw [hl₂] + simp_all only [ne_eq, Fin.getElem_fin, List.append_assoc, List.singleton_append, + List.length_append, List.length_cons] + have valid_index11 (t : Fin (l₂.length - 1)): t.val + 1 < a.length + (c.length + 1) := by + rw [h1] + omega + have valid_index12 (t : Fin (l₂.length - 1)): t.val < a.length + (c.length + 1) := by + rw [h1] + omega + have h1: ∏' (i : Fin l₂.length), (m l₂[i.val]) x[i.val] = 0 := by + rw [tprod_fintype] + rw [@Finset.prod_eq_zero_iff] + cases P_true with + | intro z hz => + have valid_index13: ((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove z).val < l₂.length := by + simp[Fin.succAbove] + rename_i z_1 + simp_all only [ne_eq, Fin.getElem_fin, List.append_assoc, List.singleton_append, + List.length_append, List.length_cons] + split + next h => simp_all only [Fin.coe_castSucc] + next h => simp_all only [not_lt, Fin.val_succ] + use ⟨(@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove z, valid_index13⟩ + apply And.intro + simp + apply (nonzero2 _ _).mp + apply hz + have h2: ∏' (i : Fin l₁.length), (m l₁[i.val]) x[i.val] = 0 := by + rw [tprod_fintype] + rw [@Finset.prod_eq_zero_iff] + cases P_true with + | intro z hz => + have valid_index13: ((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove z).val < l₁.length := by + simp[Fin.succAbove] + rename_i z_1 + simp_all only [ne_eq, Fin.getElem_fin, List.append_assoc, List.singleton_append, + List.length_append, List.length_cons] + split + next h => simp_all only [Fin.coe_castSucc] + next h => simp_all only [not_lt, Fin.val_succ] + use ⟨(@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove z, valid_index13⟩ + apply And.intro + simp + apply (equiv l₁ l₂ x xlen1 xlen2 _).mpr + apply (nonzero2 _ _).mp + apply hz + rw [h2] + rw [@ENNReal.zero_div] + simp | false => simp at xlen1 rw [←Ne.eq_def] at xlen1 have numerator_zero: local_to_dataset_PMF m l₁ x = 0 := local_to_dataset_diff_lengths l₁ x xlen1 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean index 58b7f685..a3ad2842 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean @@ -10,7 +10,9 @@ lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) ( RAPPORSingleSample n query num den h l₁ l₂= 0 := by rw [RAPPORSingleSample] apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen -/- The same as above, but extended to the entire dataset. -/ + + +/- The same "diff_lenghts" theorem as above, but extended to the entire dataset. -/ lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): RAPPORSample n query num den h l₁ x = 0 := by induction l₁ generalizing x with @@ -90,6 +92,17 @@ lemma RRSamplePushForward_non_zero (num : Nat) (den : PNat) (h: 2 * num < den) ( intro a _ apply RRSinglePushForward_non_zero +lemma RAPPORSingleSample_non_zero [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) (hlen : n = l₂.length): + RAPPORSingleSample n query num den h l₁ l₂ ≠ 0 := by + rw [RAPPORSingleSample] + apply RRSamplePushForward_non_zero num den h (one_hot n query l₁) l₂ (by simp[hlen]) + +lemma RAPPORSingleSample_zero_imp_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : T) (l₂ : List Bool) + (hlen: RAPPORSingleSample n query num den h l₁ l₂ = 0): n ≠ l₂.length := by + by_contra hx + have h_contr: RAPPORSingleSample n query num den h l₁ l₂ ≠ 0 := RAPPORSingleSample_non_zero n query num den h l₁ l₂ (by simp[hx]) + contradiction + /- RRSamplePushForward is always finite. This is needed in the DP proof. -/ lemma RRSamplePushForward_finite (num : Nat) (den : PNat) (h: 2 * num < den) (l : List Bool) (b : List Bool): RRSamplePushForward num den h l b ≠ ⊤ := by diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean index c82f554f..d732f36c 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean @@ -267,4 +267,63 @@ lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (d rw [@ENNReal.zero_div] simp +/- A different perspective -/ + +def RAPPORSingle_Local (n : Nat) (query : T → Fin n) (num: Nat) (den : PNat) (h: 2 * num < den): LocalMechanism T (List Bool) := + fun l => ⟨RAPPORSingleSample n query num den h l, RAPPORSingleSample_PMF_helper query num den h l⟩ + +lemma RAPPOR_Local_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den): + Local_DP (RAPPORSingle_Local n query num den h) (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by + rw [Local_DP] + intro u₁ u₂ y + simp [RAPPORSingle_Local] + calc + RAPPORSingleSample n query num den h u₁ y / + RAPPORSingleSample n query num den h u₂ y ≤ + ((2⁻¹ + num / den) / (2⁻¹ - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h + _ = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by rw[←arith_2 num den h] + + +lemma RAPPORSample_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): + DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((2⁻¹ + num/den) / (2⁻¹ - num/den))) + := by + have h1: RAPPORSample_PMF n query num den h = local_to_dataset_PMF (RAPPORSingle_Local n query num den h) := rfl + rw [h1] + let P : T → List Bool → Bool := fun _ bo => (n == bo.length) + apply LocalDP_to_dataset _ _ P + {intro k bo + simp [P, -ne_eq] + apply Iff.intro + simp [RAPPORSingle_Local, -ne_eq] + intro hlen + apply RAPPORSingleSample_non_zero + simp [one_hot, hlen] + simp [RAPPORSingle_Local, -ne_eq] + intro hbo + by_contra bol + have hcontr: RAPPORSingleSample n query num den h k bo = 0 := by + apply RAPPORSingleSample_diff_lengths + simp [one_hot] + exact bol + contradiction + } + {intro l₁ l₂ b k bo i + apply Iff.intro + simp [RAPPORSingle_Local] + intro h0 + apply RAPPORSingleSample_diff_lengths + apply RAPPORSingleSample_zero_imp_diff_lengths at h0 + simp [one_hot, h0] + intro h0 + apply RAPPORSingleSample_diff_lengths + apply RAPPORSingleSample_zero_imp_diff_lengths at h0 + simp [←ne_eq] + apply h0 + } + { + intro k bo + exact PMF.apply_ne_top (RAPPORSingle_Local n query num den h k) bo + } + {apply RAPPOR_Local_DP n query num den h} + end RAPPOR diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean index f099dfd1..6e629e40 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean @@ -78,7 +78,25 @@ lemma RR_Local_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < d lemma RRSample_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < den): DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by have h1: RRSample_PMF query num den h = local_to_dataset_PMF (RRSingle_Local query num den h) := rfl rw [h1] - apply LocalDP_to_dataset - apply RRSingleSample_non_zero query num den h + let P : T → Bool → Bool := fun _ _ => true + have hP: ∀ k : T, ∀ bo : Bool, P k bo ↔ RRSingle_Local query num den h k bo ≠ 0 := by + intro k bo + simp[RRSingle_Local, -ne_eq] + apply RRSingleSample_non_zero + exact h + apply LocalDP_to_dataset _ _ P + { apply hP } + { simp [RRSingle_Local] + intro l₁ l₂ b blen1 blen2 i + apply Iff.intro + intro hx + have h_contr: RRSingleSample query num den h l₁[i.val] b[i.val] = 0 := by aesop + have h_contr2: RRSingleSample query num den h l₁[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₁[i.val] b[i.val] + contradiction + intro hx + have h_contr: RRSingleSample query num den h l₂[i.val] b[i.val] = 0 := by aesop + have h_contr2: RRSingleSample query num den h l₂[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₂[i.val] b[i.val] + contradiction + } apply RRSingleSample_finite query num den h apply RR_Local_DP From c3a241a403b60044464615cd1f429ad849d0f0c4 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 18:55:21 -0400 Subject: [PATCH 163/216] added defs --- .../Pure/ShuffleModel/Definitions.lean | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 470833f8..1e1eee44 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -19,13 +19,63 @@ match l with let rest : List α ← Shuffler tl return rest.insertNth i hd - /- This is the Shuffle Model. -/ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l return b +#check Shuffler +#check RandomizedResponse.RRSample +#check Mechanism + +def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := + ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) + +lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang (List U)) (h:UniformShuffler f → True) +:∀(b: List U),∑' (i : List U), f b i = 1 := by + intro b + have h1 : ∑' (i : List U), f b i = ∑' (i: List U), if List.isPerm b i then f b i else f b i := by aesop + rw [h1] + have h2 (x i : List U) : f x i = if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal) := by + sorry + conv => + enter [1,1,i] + rw [h2] + simp + sorry + + + + +def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f → True)(l: List T) := do + let x ← m l + let b ← f x + return b + +lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f → True)(l: List T): +HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by + unfold ShuffleAlgorithm + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply UniformShuffler_norms + exact h + simp + rw [← Summable.hasSum_iff ENNReal.summable] + exact (m l).2 + +def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f → True)(l: List T) : PMF (List U) := + ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ + +theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) +(hsa: UniformShuffler f → True): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry + def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do let seeds := List.replicate n seed let list ← MultiBernoulli.MultiBernoulliSample (seeds) From 86d33209957cd4c12d18b93d94341c11c0ee6aa4 Mon Sep 17 00:00:00 2001 From: Renee Tetlow Date: Fri, 15 Aug 2025 19:30:14 -0400 Subject: [PATCH 164/216] changes --- .../Pure/ShuffleModel/Definitions.lean | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 1e1eee44..3cd77bb9 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -7,6 +7,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Generic namespace SLang @@ -32,28 +33,31 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) -lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang (List U)) (h:UniformShuffler f → True) +lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) :∀(b: List U),∑' (i : List U), f b i = 1 := by intro b - have h1 : ∑' (i : List U), f b i = ∑' (i: List U), if List.isPerm b i then f b i else f b i := by aesop - rw [h1] - have h2 (x i : List U) : f x i = if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal) := by - sorry + have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by + unfold UniformShuffler at h + exact h conv => enter [1,1,i] rw [h2] - simp + + rw [← @ENNRealLemmas.tsum_ite_mult] + conv => + enter [1] + rw[ENNReal.tsum_mul_left] sorry -def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f → True)(l: List T) := do +def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← m l let b ← f x return b -lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f → True)(l: List T): +lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by unfold ShuffleAlgorithm simp_all only [bind, pure, bind_pure] @@ -70,7 +74,7 @@ HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by rw [← Summable.hasSum_iff ENNReal.summable] exact (m l).2 -def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f → True)(l: List T) : PMF (List U) := +def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) From 40a8d1d0e950f336add6e9aebe570d59b769abf7 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Fri, 15 Aug 2025 16:52:51 -0700 Subject: [PATCH 165/216] Update on shuffle_permutes proof --- .../Pure/Local/ShuffleModel/Definitions.lean | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean new file mode 100644 index 00000000..f6fa65d0 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -0,0 +1,199 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code +import SampCert.Samplers.Uniform.Properties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.PushForward +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties + +namespace SLang + +def Shuffler {α: Type}(l:List α) := do +match l with +| [] => pure [] +| hd::tl => + let len := (hd :: tl).length + let i : Nat ← UniformSample (Nat.toPNat' len) + let rest : List α ← Shuffler tl + return rest.insertNth i hd + +#eval List.insertNth 0 1 [2,3] +#eval List.eraseIdx [1,2,3] 0 +lemma insertNth_helper {α : Type}(b a_1: List α)(a: Nat)(h: α): b = List.insertNth a h a_1 ↔ a_1 = List.eraseIdx b a := by sorry + + +lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by + unfold Shuffler + rw [h] + simp [pure] + + + +lemma Shuffler_norm [DecidableEq α]{α: Type}(l:List α): HasSum (Shuffler l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + induction l with + | nil => + unfold Shuffler + simp [pure] + unfold probPure + simp + | cons h t ih => + unfold Shuffler + simp [pure] + rw [← Summable.hasSum_iff ENNReal.summable] + rw [Summable.hasSum_iff ENNReal.summable] + simp + conv => + enter [1,1,b,1,a,2,1,a_1] + rw [insertNth_helper] + rename_i α_1 inst + conv => + enter [1, 1, b, 1, a, 2, 1, a_1] + rw [tsum_add] + have h (a_1:List α)(b:List α)(a : Nat): (if a_1 = b.eraseIdx a then Shuffler t a_1 else 0) = Shuffler t a_1 := by sorry + + + + +def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do + let seeds := List.replicate n seed + let list ← MultiBernoulli.MultiBernoulliSample (seeds) + let k := List.count true list + return k + +theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : + HasSum (BinomialSample seed n) 1 := by + rw [BinomialSample] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) + (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => + (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample + (List.replicate (↑n) seed) a else 0)) := by + unfold push_forward + rfl + rw [← h] + rw [push_forward_prob_is_prob] + simp [MultiBernoulli.MultiBernoulliSample_normalizes] + +theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : + BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by + rw[BinomialSample] + simp + unfold MultiBernoulli.MultiBernoulliSample + simp [pure, bind] + + sorry + + /- This is the Shuffle Model. -/ +def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do + let l ← RandomizedResponse.RRSample query num den h l + let b ← Shuffler l + return b + +lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry + +lemma insertNth_eq_iff {α : Type} [DecidableEq α] + {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): + l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry +lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by + induction l₁ generalizing l₂ n + simp at hlen1 + rw[symm hlen1] + simp + unfold Shuffler + simp + symm at hlen1 + rw[hlen1] at hlen2 + rw[List.length_eq_zero] at hlen2 + exact hlen2 + + case cons hd tl ih => + unfold Shuffler + simp + simp at hlen1 + rw[symm hlen1] + unfold List.isPerm at h + rw[Bool.and_eq_true] at h + cases h + rename_i left right + have h := contains_idx l₂ hd left + cases h + rename_i j ht + conv => + enter[1,1,a,2] + rw[tsum_eq_single (l₂.eraseIdx j) (by + intro l h + simp + intro h1 + rw[insertNth_eq_iff] at h1 + cases h1 + rename_i left right + simp at h + sorry + + + + + + )] + rfl + simp + rw[tsum_eq_single j.val (by + intro a h + sorry + )] + have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry + rw[if_pos] + + rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by sorry) (by sorry)] + rw[UniformSample_apply] + rw[hlen1] + rw[inv_eq_one_div] + + simp + have nonzero: n > 0 := by rw[symm hlen1]; linarith + rw[if_pos nonzero] + rw[← ENNReal.mul_inv] + rw[inv_inj] + linarith[Nat.mul_factorial_pred] + rw[hlen1] + rw[symm hlen2] + have nonzero: n > 0 := by rw[symm hlen1]; linarith + rw[symm hlen2] at nonzero + simp[nonzero] + exact h2 + + +/--/ + unfold List.isPerm at h + rw[Bool.and_eq_true] at h + cases h + rename_i left right + have h := contains_idx l₂ hd left + cases h + rename_i j ht + + + conv => + enter[1,1,a,2] + rw[tsum_eq_single (List.eraseIdx l₂ j ) (by + intro b' h + simp + + + have h2: b' = List.eraseIdx (List.insertNth j hd b') j := by rw[List.eraseIdx_insertNth] + intro ht + simp[ht] at h + sorry + + + )] + rfl + conv => + enter[1,1,a] + rw[UniformSample_apply (tl.length + 1).toPNat'] + rfl From 9011927a33be955b0ff2caeffdbd040bb3b208fc Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 18 Aug 2025 09:19:42 -0700 Subject: [PATCH 166/216] post-processing is DP for randomized post-processing --- .../Pure/Postprocessing.lean | 174 ++++++++++++++++++ .../Pure/ShuffleModel/Definitions.lean | 2 + 2 files changed, 176 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean index 4e57529a..1410c3fc 100644 --- a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean @@ -5,6 +5,8 @@ Authors: Jean-Baptiste Tristan -/ import SampCert.DifferentialPrivacy.Pure.DP import SampCert.DifferentialPrivacy.Generic +import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Topology.Algebra.InfiniteSum.Basic import Mathlib.Data.Set.Defs import Mathlib.Data.Set.Prod @@ -42,6 +44,178 @@ lemma privPostProcess_DP_bound {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq exact Real.exp_pos ε · simp +def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : Mechanism T V := + fun l => (nq l).bind g + +lemma div_le_iff_mul_le {a b c : ENNReal} (hb : b ≠ 0) (h2 : ⊤ ≠ b) : + a ≤ c * b ↔ a / b ≤ c := by + constructor + · intro h + have bruh : a * b⁻¹ ≤ c * b * b⁻¹ := by + have := mul_le_mul_right' h (b⁻¹) + simpa [mul_assoc] using this + rw [← div_eq_mul_inv, mul_assoc, ENNReal.mul_inv_cancel, mul_one] at bruh + · exact bruh + · aesop + · aesop + · intro h + have hmul : (a / b) * b ≤ c * b := by + simpa [mul_assoc] using mul_le_mul_right' h b + rw [ENNReal.div_mul_cancel] at hmul + · aesop + · aesop + · aesop + +lemma frog {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U): + (∑' (x : U), if x = u then ((m l₁) x) else 0) = (m l₁) u := by + classical + have hpoint : + (fun x : U => if x = u then (m l₁) x else 0) = + (fun x : U => if x = u then (m l₁) u else 0) := by + funext x + by_cases hx : x = u + · simp [hx] + · simp [hx] + have hcollapse : (∑' x : U, if x = u then (m l₁) u else 0) = (m l₁) u := by + simp [tsum_ite_eq (β := U) (α := ENNReal) u ((m l₁) u)] + simp [hpoint, hcollapse] + + +lemma DP.pointwise_ratio_bound {T U : Type} + {m : Mechanism T U} {ε : ℝ} + (h : DP m ε) {l₁ l₂ : List T} (hN : Neighbour l₁ l₂) : + ∀ u : U, m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + intro u + have hS := h l₁ l₂ hN ({u} : Set U) + have hnum : + (∑' x : U, (if x ∈ ({u} : Set U) then m l₁ x else 0)) = m l₁ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₁ x else 0) + = (fun x : U => if x = u then m l₁ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (frog m l₁ u) + have hden : + (∑' x : U, (if x ∈ ({u} : Set U) then m l₂ x else 0)) = m l₂ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₂ x else 0) + = (fun x : U => if x = u then m l₂ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (frog m l₂ u) + have hratio : m l₁ u / m l₂ u ≤ ENNReal.ofReal (Real.exp ε) := by + rw [hnum, hden] at hS + exact hS + by_cases hz : m l₂ u = 0 + · have hfin : ENNReal.ofReal (Real.exp ε) ≠ ⊤ := by aesop + have hzero : m l₁ u = 0 := by + by_contra hpos + have htop : m l₁ u / m l₂ u = ⊤ := by + exact ENNReal.div_eq_top.mpr (Or.inl ⟨by simp [hpos], hz⟩) + have : (⊤ : ENNReal) ≤ ENNReal.ofReal (Real.exp ε) := by + simp at hratio + aesop + have : ENNReal.ofReal (Real.exp ε) = ⊤ := top_le_iff.mp this + exact hfin this + simp [hz, hzero, zero_mul] + · have h_not_infty : ⊤ ≠ m l₂ u := by + have hle : m l₂ u ≤ 1 := by simpa using (m l₂).coe_le_one u + have hlt : m l₂ u < ⊤ := lt_of_le_of_lt hle (by simp) + aesop + have : m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + rw [div_le_iff_mul_le hz h_not_infty] + exact hratio + simpa using this + + + +lemma bruh1 {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): +(∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by + calc + (∑' v : V, if v ∈ S then p u * g u v else 0) + = ∑' v : V, p u * (if v ∈ S then g u v else 0) := by + simp [hsplit] + _ = p u * ∑' v : V, (if v ∈ S then g u v else 0) := by + simpa using + (ENNReal.tsum_mul_left + (a := p u) + (f := fun v : V => if v ∈ S then g u v else 0)) + +lemma tsum_bind_indicator {U V : Type} + (p : PMF U) (g : U → PMF V) (S : Set V) : + (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by + classical + have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by + intro v; simp [PMF.bind_apply] + calc + (∑' v : V, if v ∈ S then (p.bind g) v else 0) + = ∑' v, if v ∈ S then (∑' u, p u * g u v) else 0 := by + simp [hbind] + _ = ∑' v, ∑' u, (if v ∈ S then p u * g u v else 0) := by + refine tsum_congr ?_ + intro v; by_cases hv : v ∈ S <;> simp [hv] + _ = ∑' u, ∑' v, (if v ∈ S then p u * g u v else 0) := by + simpa using + ENNReal.tsum_comm (f := fun v u => (if v ∈ S then p u * g u v else 0)) + _ = ∑' u, p u * (∑' v, if v ∈ S then g u v else 0) := by + refine tsum_congr ?_ + intro u + have hsplit :(fun v => if v ∈ S then p u * g u v else 0) = (fun v => p u * (if v ∈ S then g u v else 0)) := by + funext v; by_cases hv : v ∈ S <;> simp [hv, mul_comm, mul_left_comm, mul_assoc] + exact bruh1 p g S u hsplit + + + +lemma bruh {a b c : ENNReal} (h1: a ≤ c * b) (h2: 0 < b) : a / b ≤ c := by + by_cases hbtop : b = ⊤ + · aesop + · have hb0 : b ≠ 0 := ne_of_gt h2 + have : a * b⁻¹ ≤ (c * b) * b⁻¹ := (mul_le_mul_right' h1 b⁻¹) + rw [mul_assoc, ENNReal.mul_inv_cancel, mul_one] at this + aesop + · aesop + · aesop + + +lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq ε) (g : U → PMF V) : + DP (privPostProcessRand nq g) ε := by + intro l₁ l₂ hN S + let p₁ := nq l₁ + let p₂ := nq l₂ + let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) + have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) + = ∑' u : U, p₁ u * w u := by + simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S + have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) + = ∑' u : U, p₂ u * w u := by + simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S + have hpt := DP.pointwise_ratio_bound (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN + have hsum : + (∑' u : U, p₁ u * w u) + ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by + have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by + intro u + have := hpt u + have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u + have hw0 : 0 ≤ w u := by aesop + have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 + simpa [mul_left_comm, mul_comm, mul_assoc] using hmul + have := ENNReal.tsum_le_tsum hpt' + simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this + by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 + · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by + have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum + exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + simp [hNum, hDen, hNum0, hDen0] + · have hpos : (0 : ENNReal) < (∑' u : U, p₂ u * w u) := lt_of_le_of_ne' (by exact bot_le) hDen0 + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact bruh hsum hpos) + simpa [hNum, hDen] using this + + /-- ``privPostProcess`` satisfies pure DP, for any surjective postprocessing function. -/ diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 3cd77bb9..42e317bc 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -7,6 +7,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Postprocessing import SampCert.DifferentialPrivacy.Generic namespace SLang @@ -77,6 +78,7 @@ HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ + theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) (hsa: UniformShuffler f → True): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry From 7b996b7de4e81780ce93fc75d67d876dbbf74d3e Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Mon, 18 Aug 2025 09:42:32 -0700 Subject: [PATCH 167/216] Shuffle model permutes proof --- .../Pure/Local/ShuffleModel/Definitions.lean | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean index f6fa65d0..8c30be8f 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean @@ -99,6 +99,8 @@ lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: lemma insertNth_eq_iff {α : Type} [DecidableEq α] {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry + +lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by induction l₁ generalizing l₂ n simp at hlen1 @@ -125,31 +127,29 @@ lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: L rename_i j ht conv => enter[1,1,a,2] - rw[tsum_eq_single (l₂.eraseIdx j) (by + rw[tsum_eq_single (l₂.eraseIdx a) (by intro l h simp intro h1 rw[insertNth_eq_iff] at h1 cases h1 - rename_i left right + rename_i left2 right2 simp at h - sorry - - - - - + contradiction + exact h1 )] rfl simp rw[tsum_eq_single j.val (by intro a h - sorry + simp + intro h1 + )] have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry rw[if_pos] - rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by sorry) (by sorry)] + rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by rw[symm hlen2, List.length_eraseIdx];simp) (by rw[erase_eq_eraseIdx ht];exact right)] rw[UniformSample_apply] rw[hlen1] rw[inv_eq_one_div] @@ -159,7 +159,14 @@ lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: L rw[if_pos nonzero] rw[← ENNReal.mul_inv] rw[inv_inj] - linarith[Nat.mul_factorial_pred] + sorry + left + simp + linarith + + left + simp + /-rw[Nat.mul_factorial_pred nonzero]-/ rw[hlen1] rw[symm hlen2] have nonzero: n > 0 := by rw[symm hlen1]; linarith From 84701cae76222d3f8f56eb8187cc15e0ae639356 Mon Sep 17 00:00:00 2001 From: PCChess Date: Mon, 18 Aug 2025 14:22:11 -0700 Subject: [PATCH 168/216] file imports --- .../DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean | 3 +-- .../Pure/Local/LocalDP/DPwithGeneralNeighbour.lean | 3 +-- .../Pure/Local/LocalDP/DPwithUpdateNeighbour.lean | 1 - .../Pure/Local/LocalDP/UpdateNeighbour.lean | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean index 7fa969f4..deb2dcf4 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPNeighbour.lean @@ -1,4 +1,4 @@ -import SampCert +import SampCert.DifferentialPrivacy.Pure.DP import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithGeneralNeighbour import SampCert.DifferentialPrivacy.Neighbours @@ -17,4 +17,3 @@ theorem DP_withARUNeighbour_isDP (m : Mechanism T U) (ε : ℝ) : DP_withARUNeighbour m ε ↔ DP m ε := by simp [DP_withARUNeighbour, DP_withGeneralNeighbour, DP] end SLang - diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean index 5b5a6b51..d5b86c50 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean @@ -1,5 +1,4 @@ -import SampCert - +import SampCert.DifferentialPrivacy.Generic namespace SLang open SLang diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index 6084fbca..fd1de78b 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -1,4 +1,3 @@ -import SampCert import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithGeneralNeighbour import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.UpdateNeighbour diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean index dc7b92ae..bd3aa457 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/UpdateNeighbour.lean @@ -20,5 +20,5 @@ lemma UpdateNeighbour_length {T : Type} {l₁ l₂ : List T} (H : UpdateNeighbou rename_i _ _ _ _ Hl1 Hl2 rw[Hl1, Hl2] simp - + end SLang From adf8ea82685eaeaef51040ed66598be75cd0943b Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 09:35:20 -0700 Subject: [PATCH 169/216] readytomerge --- .../Pure/Local/LocalDP/LocalToDataset.lean | 6 ------ .../DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean index db558746..9adacbfb 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -49,12 +49,6 @@ lemma local_to_dataset_diff_lengths (l₁ : List T) (x : List U) (hlen : l₁.le lemma local_to_dataset_prob_of_ind_prob_PMF (m: LocalMechanism T U) (l : List T) (a: List U) (k : l.length = a.length) : local_to_dataset_PMF m l a = (∏'(i: Fin l.length), m (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob -/- theorem local_to_dataset_reduction {T β: Type} (a b : List T) (n m : T) (l₁ l₂: List T)(x: List β)(f: T → PMF β)(h1: l₁ = a++[n]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length)(hy: l₂.length = x.length) -(nonzero: ∀ (i : Fin (l₂.length - 1)), f (l₂[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) (x[↑((@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove i)]'(by apply valid_index8 h2; aesop)) ≠ 0) -(noninf: ∀(k: T) (bo: β), f k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / - (∏' (i : Fin (l₂.length)), f (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f (l₁[(a.length)]'(by rw[h1];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) - := reduction_final l₁ l₂ a b n m x _ h1 h2 hx hy nonzero noninf -/ - /- This lemma states that, under a technical assumption, the algorithm that applies the same local randomizer to each row of data satisfies the same DP bound as the local randomizer. diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean index c770cdb3..18017f18 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ProbabilityProduct.lean @@ -16,8 +16,7 @@ mapM f (hd :: tl) (b :: c) = f hd b * mapM f tl c := by aesop aesop -/- Proof that using `mapM f l` is the same as sampling from f (l.get i) for all i independently - as a product of probabilities. -/ +/- Proof that using `mapM f l` is the same as sampling from f (l.get i) for all i independently -/ lemma prod_of_ind_prob (β : Type) [LawfulMonad SLang] [DecidableEq β] (f : T -> SLang β) (a : List β) (l : List T) (k : l.length = a.length) : mapM f l a = (∏' (i : Fin l.length), f (l.get i) (a.get (Fin.cast k i))) := by induction l generalizing a with From de80b8eaa0ad9be07619d1c8eac9b38d86f41c37 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 10:02:18 -0700 Subject: [PATCH 170/216] main --- .../DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean index 9adacbfb..a99ba377 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -11,6 +11,8 @@ open Classical /- We define a general way to transform local randomizers into algorithms on datasets, and prove that the dataset-level algorithm satisfies the same DP bound as the local randomizer. -/ +/- Test -/ + /- Transforms a local randomizer into a dataset-level algorithm. -/ def local_to_dataset (m : LocalMechanism T U) (l : List T) : SLang (List U) := (l.mapM (fun x => (m x).1)) From 0aea4eb0cfea823c2a29d45606f2c73dd0162b6d Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 10:33:03 -0700 Subject: [PATCH 171/216] fixedimports --- SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean | 0 .../Pure/Local/LocalDP/DPwithGeneralNeighbour.lean | 4 ++++ .../Pure/Local/LocalDP/DPwithUpdateNeighbour.lean | 3 +++ .../Pure/Local/LocalDP/LocalToDataset.lean | 2 -- .../DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean | 6 ++---- 5 files changed, 9 insertions(+), 6 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean deleted file mode 100644 index e69de29b..00000000 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean index d5b86c50..dbac1ce8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithGeneralNeighbour.lean @@ -4,13 +4,17 @@ namespace SLang open SLang open Classical +/- DP with a general neighbour relation. + The definition of the neighbour relation is passed in as a parameter.-/ def DP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour: List T -> List T -> Prop) (ε : ℝ): Prop := ∀ l₁ l₂ : List T, VariableNeighbour l₁ l₂ → ∀ S : Set U, (∑' x : U, if x ∈ S then m l₁ x else 0) / (∑' x : U, if x ∈ S then m l₂ x else 0) ≤ ENNReal.ofReal (Real.exp ε) +/- Instantiation of PureDP with a general neighbour relation. -/ def PureDP_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour : List T -> List T -> Prop) (ε : NNReal) : Prop := DP_withGeneralNeighbour m VariableNeighbour ε +/- Instantiation of DP_singleton with a general neighbour relation. -/ def DP_singleton_withGeneralNeighbour (m : Mechanism T U) (VariableNeighbour: List T -> List T -> Prop) (ε : ℝ): Prop := ∀ l₁ l₂ : List T, VariableNeighbour l₁ l₂ → ∀ x : U, (m l₁ x) / (m l₂ x) ≤ ENNReal.ofReal (Real.exp ε) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index fd1de78b..3acd59c8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -6,11 +6,14 @@ open Classical namespace SLang +/- DP with the update neighbour relation. -/ def DP_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_withGeneralNeighbour m (UpdateNeighbour) ε +/- Instantiation of DP_singleton with the update neighbour relation-/ def DP_singleton_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_singleton_withGeneralNeighbour m (UpdateNeighbour) ε + end SLang theorem singleton_to_event_update (m : Mechanism T U) (ε : ℝ) (h : DP_singleton_withUpdateNeighbour m ε) : diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean index a99ba377..9adacbfb 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -11,8 +11,6 @@ open Classical /- We define a general way to transform local randomizers into algorithms on datasets, and prove that the dataset-level algorithm satisfies the same DP bound as the local randomizer. -/ -/- Test -/ - /- Transforms a local randomizer into a dataset-level algorithm. -/ def local_to_dataset (m : LocalMechanism T U) (l : List T) : SLang (List U) := (l.mapM (fun x => (m x).1)) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 42e317bc..959b9a1b 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -34,7 +34,7 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) -lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) +lemma UniformShuffler_norms {U: Type} [BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) :∀(b: List U),∑' (i : List U), f b i = 1 := by intro b have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by @@ -51,8 +51,6 @@ lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang sorry - - def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← m l let b ← f x @@ -80,7 +78,7 @@ def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) -(hsa: UniformShuffler f → True): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry +(hsa: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do let seeds := List.replicate n seed From 16736c59df9334e5aea2ef050ad9a234ebb7f9b0 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 19 Aug 2025 10:34:20 -0700 Subject: [PATCH 172/216] Reorganization --- .../Pure/ShuffleModel/Definitions.lean | 43 ------- .../Pure/ShuffleModel/Properties.lean | 108 ++++++++++++++++-- 2 files changed, 97 insertions(+), 54 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 42e317bc..574fec7d 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -1,7 +1,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.Samplers.Uniform.Code import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.DifferentialPrivacy.Pure.Local.PushForward import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour @@ -34,54 +33,12 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) -lemma UniformShuffler_norms {U: Type}[DecidableEq U][BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) -:∀(b: List U),∑' (i : List U), f b i = 1 := by - intro b - have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by - unfold UniformShuffler at h - exact h - conv => - enter [1,1,i] - rw [h2] - - rw [← @ENNRealLemmas.tsum_ite_mult] - conv => - enter [1] - rw[ENNReal.tsum_mul_left] - sorry - - - def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← m l let b ← f x return b -lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): -HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by - unfold ShuffleAlgorithm - simp_all only [bind, pure, bind_pure] - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - rw [ENNReal.tsum_comm] - conv => - enter [1,1,b] - rw [ENNReal.tsum_mul_left] - enter [2] - apply UniformShuffler_norms - exact h - simp - rw [← Summable.hasSum_iff ENNReal.summable] - exact (m l).2 - -def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := - ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ - - -theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) -(hsa: UniformShuffler f → True): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry - def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do let seeds := List.replicate n seed let list ← MultiBernoulli.MultiBernoulliSample (seeds) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 009d608b..0c9df442 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -7,7 +7,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.PMFProperties +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof namespace SLang @@ -17,7 +17,6 @@ lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by rw [h] simp [pure] - lemma Shuffler_PMF {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with @@ -51,6 +50,24 @@ lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : rw [← Summable.hasSum_iff ENNReal.summable] apply Shuffler_PMF +lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): +HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by + unfold ShuffleAlgorithm + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply UniformShuffler_norms + exact h + simp + rw [← Summable.hasSum_iff ENNReal.summable] + exact (m l).2 + + theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : HasSum (BinomialSample seed n) 1 := by rw [BinomialSample] @@ -83,6 +100,21 @@ theorem RRShuffle_norms [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : N rw [← Summable.hasSum_iff ENNReal.summable] apply RRSample_PMF_helper +lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) +:∀(b: List U),∑' (i : List U), f b i = 1 := by + intro b + have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by + unfold UniformShuffler at h + exact h + conv => + enter [1,1,i] + rw [h2] + + rw [← @ENNRealLemmas.tsum_ite_mult] + conv => + enter [1] + rw[ENNReal.tsum_mul_left] + sorry theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by @@ -92,8 +124,14 @@ theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat simp [pure, bind] sorry +lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry -lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by +lemma insertNth_eq_iff {α : Type} [DecidableEq α] + {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): + l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry + +lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry +lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by induction l₁ generalizing l₂ n simp at hlen1 rw[symm hlen1] @@ -110,17 +148,65 @@ lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen1: l₁.len simp simp at hlen1 rw[symm hlen1] + unfold List.isPerm at h + rw[Bool.and_eq_true] at h + cases h + rename_i left right + have h := contains_idx l₂ hd left + cases h + rename_i j ht conv => enter[1,1,a,2] - rw[tsum_eq_single (List.eraseIdx l₂ a ) (by - intro b' h - simp - - aesop - have h2: b' = List.eraseIdx (List.insertNth a hd b') a := by rw[List.eraseIdx_insertNth] - contradiction + rw[tsum_eq_single (l₂.eraseIdx a) (by + intro l h + simp + intro h1 + rw[insertNth_eq_iff] at h1 + cases h1 + rename_i left2 right2 + simp at h + contradiction + exact h1 )] rfl + simp + rw[tsum_eq_single j.val (by + intro a h + simp + intro h1 + + )] + have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry + rw[if_pos] + + rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by rw[symm hlen2, List.length_eraseIdx];simp) (by rw[erase_eq_eraseIdx ht];exact right)] + rw[UniformSample_apply] + rw[hlen1] + rw[inv_eq_one_div] - rw[tsum_eq_single] + simp + have nonzero: n > 0 := by rw[symm hlen1]; linarith + rw[if_pos nonzero] + rw[← ENNReal.mul_inv] + rw[inv_inj] sorry + left + simp + linarith + + left + simp + /-rw[Nat.mul_factorial_pred nonzero]-/ + rw[hlen1] + rw[symm hlen2] + have nonzero: n > 0 := by rw[symm hlen1]; linarith + rw[symm hlen2] at nonzero + simp[nonzero] + exact h2 + +def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := + ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ + + +theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) +(hsa: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry From de303066fb2f9a217d468139a361860afb1eade0 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 19 Aug 2025 11:00:11 -0700 Subject: [PATCH 173/216] Reorg 2 --- .../Local/MultiBernoulli.lean | 0 .../Pure/Local/BinomialSample/Binomial.lean | 32 ++++++++++ .../Pure/ShuffleModel/Definitions.lean | 6 -- .../Pure/ShuffleModel/Properties.lean | 59 ++++++------------- 4 files changed, 49 insertions(+), 48 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean create mode 100644 SampCert/DifferentialPrivacy/Pure/Local/BinomialSample/Binomial.lean diff --git a/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean b/SampCert/DifferentialPrivacy/Local/MultiBernoulli.lean deleted file mode 100644 index e69de29b..00000000 diff --git a/SampCert/DifferentialPrivacy/Pure/Local/BinomialSample/Binomial.lean b/SampCert/DifferentialPrivacy/Pure/Local/BinomialSample/Binomial.lean new file mode 100644 index 00000000..ac5cc1e7 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/Local/BinomialSample/Binomial.lean @@ -0,0 +1,32 @@ +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Local.PushForward + +namespace SLang + +def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do + let seeds := List.replicate n seed + let list ← MultiBernoulli.MultiBernoulliSample (seeds) + let k := List.count true list + return k + +theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : + HasSum (BinomialSample seed n) 1 := by + rw [BinomialSample] + simp + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) + (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => + (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample + (List.replicate (↑n) seed) a else 0)) := by + unfold push_forward + rfl + + rw [← h] + rw [push_forward_prob_is_prob] + simp [MultiBernoulli.MultiBernoulliSample_normalizes] + + def BinomialSample_PMF [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : PMF ℕ := + ⟨BinomialSample seed n, BinomialSample_norms seed n⟩ diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 574fec7d..3025f3a3 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -38,9 +38,3 @@ def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang let x ← m l let b ← f x return b - -def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do - let seeds := List.replicate n seed - let list ← MultiBernoulli.MultiBernoulliSample (seeds) - let k := List.count true list - return k diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 0c9df442..15793386 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -50,6 +50,22 @@ lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : rw [← Summable.hasSum_iff ENNReal.summable] apply Shuffler_PMF +lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) +:∀(b: List U),∑' (i : List U), f b i = 1 := by + intro b + have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by + unfold UniformShuffler at h + exact h + conv => + enter [1,1,i] + rw [h2] + + rw [← @ENNRealLemmas.tsum_ite_mult] + conv => + enter [1] + rw[ENNReal.tsum_mul_left] + sorry + lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by unfold ShuffleAlgorithm @@ -67,24 +83,6 @@ HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by rw [← Summable.hasSum_iff ENNReal.summable] exact (m l).2 - -theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : - HasSum (BinomialSample seed n) 1 := by - rw [BinomialSample] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) - (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => - (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample - (List.replicate (↑n) seed) a else 0)) := by - unfold push_forward - rfl - - rw [← h] - rw [push_forward_prob_is_prob] - simp [MultiBernoulli.MultiBernoulliSample_normalizes] - theorem RRShuffle_norms [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by unfold RRShuffle simp_all only [bind, pure, bind_pure] @@ -100,30 +98,6 @@ theorem RRShuffle_norms [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : N rw [← Summable.hasSum_iff ENNReal.summable] apply RRSample_PMF_helper -lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) -:∀(b: List U),∑' (i : List U), f b i = 1 := by - intro b - have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by - unfold UniformShuffler at h - exact h - conv => - enter [1,1,i] - rw [h2] - - rw [← @ENNRealLemmas.tsum_ite_mult] - conv => - enter [1] - rw[ENNReal.tsum_mul_left] - sorry - -theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : - BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by - rw[BinomialSample] - simp - unfold MultiBernoulli.MultiBernoulliSample - simp [pure, bind] - - sorry lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry lemma insertNth_eq_iff {α : Type} [DecidableEq α] @@ -174,6 +148,7 @@ lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: L intro a h simp intro h1 + sorry )] have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry From 2a3071a3c7eec1bdb8cace3f33c0acb0acc872bf Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 19 Aug 2025 13:29:19 -0700 Subject: [PATCH 174/216] Cleaning --- .../Pure/Local/ShuffleModel/Definitions.lean | 207 ------------------ .../Pure/ShuffleModel/Definitions.lean | 10 +- .../Pure/ShuffleModel/Properties.lean | 116 +++++----- 3 files changed, 66 insertions(+), 267 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean deleted file mode 100644 index 0d4c9961..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ /dev/null @@ -1,207 +0,0 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties - -namespace SLang - -def Shuffler {α: Type}(l:List α) := do -match l with -| [] => pure [] -| hd::tl => - let len := (hd :: tl).length - let i : Nat ← UniformSample (Nat.toPNat' len) - let rest : List α ← Shuffler tl - return rest.insertNth i hd - -#eval List.insertNth 0 1 [2,3] -#eval List.eraseIdx [1,2,3] 0 -lemma insertNth_helper {α : Type}(b a_1: List α)(a: Nat)(h: α): b = List.insertNth a h a_1 ↔ a_1 = List.eraseIdx b a := by sorry - - -lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by - unfold Shuffler - rw [h] - simp [pure] - - - -lemma Shuffler_norm [DecidableEq α]{α: Type}(l:List α): HasSum (Shuffler l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - induction l with - | nil => - unfold Shuffler - simp [pure] - unfold probPure - simp - | cons h t ih => - unfold Shuffler - simp [pure] - rw [← Summable.hasSum_iff ENNReal.summable] - rw [Summable.hasSum_iff ENNReal.summable] - simp - conv => - enter [1,1,b,1,a,2,1,a_1] - rw [insertNth_helper] - rename_i α_1 inst - conv => - enter [1, 1, b, 1, a, 2, 1, a_1] - rw [tsum_add] - have h (a_1:List α)(b:List α)(a : Nat): (if a_1 = b.eraseIdx a then Shuffler t a_1 else 0) = Shuffler t a_1 := by sorry - - - - -def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do - let seeds := List.replicate n seed - let list ← MultiBernoulli.MultiBernoulliSample (seeds) - let k := List.count true list - return k - -theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : - HasSum (BinomialSample seed n) 1 := by - rw [BinomialSample] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) - (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => - (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample - (List.replicate (↑n) seed) a else 0)) := by - unfold push_forward - rfl - rw [← h] - rw [push_forward_prob_is_prob] - simp [MultiBernoulli.MultiBernoulliSample_normalizes] - -theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : - BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by - rw[BinomialSample] - simp - unfold MultiBernoulli.MultiBernoulliSample - simp [pure, bind] - - sorry - - /- This is the Shuffle Model. -/ -def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do - let l ← RandomizedResponse.RRSample query num den h l - let b ← Shuffler l - return b - -lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry - -lemma insertNth_eq_iff {α : Type} [DecidableEq α] - {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): - l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry - -lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry -lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n - simp at hlen1 - rw[symm hlen1] - simp - unfold Shuffler - simp - symm at hlen1 - rw[hlen1] at hlen2 - rw[List.length_eq_zero] at hlen2 - exact hlen2 - - case cons hd tl ih => - unfold Shuffler - simp - simp at hlen1 - rw[symm hlen1] - unfold List.isPerm at h - rw[Bool.and_eq_true] at h - cases h - rename_i left right - have h := contains_idx l₂ hd left - cases h - rename_i j ht - conv => - enter[1,1,a,2] - rw[tsum_eq_single (l₂.eraseIdx a) (by - intro l h - simp - intro h1 - rw[insertNth_eq_iff] at h1 - cases h1 - rename_i left2 right2 - simp at h - contradiction - exact h1 - )] - rfl - simp - rw[tsum_eq_single j.val (by - intro a h - simp - intro h1 - - )] - have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry - rw[if_pos] - - rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by rw[symm hlen2, List.length_eraseIdx];simp) (by rw[erase_eq_eraseIdx ht];exact right)] - rw[UniformSample_apply] - rw[hlen1] - rw[inv_eq_one_div] - - simp - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[if_pos nonzero] - rw[← ENNReal.mul_inv] - rw[inv_inj] - sorry - left - simp - linarith - - left - simp - /-rw[Nat.mul_factorial_pred nonzero]-/ - rw[hlen1] - rw[symm hlen2] - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[symm hlen2] at nonzero - simp[nonzero] - exact h2 - - -/--/ - unfold List.isPerm at h - rw[Bool.and_eq_true] at h - cases h - rename_i left right - have h := contains_idx l₂ hd left - cases h - rename_i j ht - - - conv => - enter[1,1,a,2] - rw[tsum_eq_single (List.eraseIdx l₂ j ) (by - intro b' h - simp - - - have h2: b' = List.eraseIdx (List.insertNth j hd b') j := by rw[List.eraseIdx_insertNth] - intro ht - simp[ht] at h - sorry - - - )] - rfl - conv => - enter[1,1,a] - rw[UniformSample_apply (tl.length + 1).toPNat'] - rfl - diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 3025f3a3..7803e7f1 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -11,6 +11,7 @@ import SampCert.DifferentialPrivacy.Generic namespace SLang +-- Implementation of the Shuffler for the Shuffle Model. def Shuffler {α: Type}(l:List α) := do match l with | [] => pure [] @@ -20,7 +21,7 @@ match l with let rest : List α ← Shuffler tl return rest.insertNth i hd - /- This is the Shuffle Model. -/ + /- This is the implementation of the Shuffle algorithm using Randomized Response as the local randomizer. -/ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l @@ -30,11 +31,12 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L #check RandomizedResponse.RRSample #check Mechanism +/- Definition of a function that uniformly permutes a given list.-/ def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) - -def ShuffleAlgorithm [BEq U](m : List T → SLang (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do - let x ← m l +/- Generalized version of the shuffle algorithm that takes in any mechanism -/ +def ShuffleAlgorithm [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do + let x ← (m l).toSLang let b ← f x return b diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 15793386..68a3d6a7 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -12,12 +12,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMF namespace SLang -lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by - unfold Shuffler - rw [h] - simp [pure] - -lemma Shuffler_PMF {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by +/- Shuffler function normalizes-/ +lemma Shuffler_PMF_helper {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with | nil => @@ -48,55 +44,8 @@ lemma Shuffler_PMF {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuf lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : List α), Shuffler l b = 1 := by rw [← Summable.hasSum_iff ENNReal.summable] - apply Shuffler_PMF - -lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) -:∀(b: List U),∑' (i : List U), f b i = 1 := by - intro b - have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by - unfold UniformShuffler at h - exact h - conv => - enter [1,1,i] - rw [h2] + apply Shuffler_PMF_helper - rw [← @ENNRealLemmas.tsum_ite_mult] - conv => - enter [1] - rw[ENNReal.tsum_mul_left] - sorry - -lemma ShuffleAlgorithm_norms {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): -HasSum (ShuffleAlgorithm (fun x => (m x).1) f h l) 1 := by - unfold ShuffleAlgorithm - simp_all only [bind, pure, bind_pure] - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - rw [ENNReal.tsum_comm] - conv => - enter [1,1,b] - rw [ENNReal.tsum_mul_left] - enter [2] - apply UniformShuffler_norms - exact h - simp - rw [← Summable.hasSum_iff ENNReal.summable] - exact (m l).2 - -theorem RRShuffle_norms [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by - unfold RRShuffle - simp_all only [bind, pure, bind_pure] - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - rw [ENNReal.tsum_comm] - conv => - enter [1,1,b] - rw [ENNReal.tsum_mul_left] - enter [2] - apply Shuffler_norms - simp - rw [← Summable.hasSum_iff ENNReal.summable] - apply RRSample_PMF_helper lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry @@ -105,6 +54,8 @@ lemma insertNth_eq_iff {α : Type} [DecidableEq α] l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry + +/- Shuffler l outputs 1/n! for any permutation of a list l.-/ lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by induction l₁ generalizing l₂ n simp at hlen1 @@ -179,9 +130,62 @@ lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: L simp[nonzero] exact h2 -def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := - ⟨ShuffleAlgorithm (fun x => (m x).1) f h l, ShuffleAlgorithm_norms m f h l⟩ +/- RRShuffle normalizes. -/ +theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by + unfold RRShuffle + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply Shuffler_norms + simp + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSample_PMF_helper + + +/- Unsolved proof that any Uniform Shuffler normalizes. TODO-/ +lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) +:∀(b: List U),∑' (i : List U), f b i = 1 := by + intro b + have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by + unfold UniformShuffler at h + exact h + conv => + enter [1,1,i] + rw [h2] + + rw [← @ENNRealLemmas.tsum_ite_mult] + conv => + enter [1] + rw[ENNReal.tsum_mul_left] + sorry +/- Any shuffle algorithm normalizes. -/ +lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): +HasSum (ShuffleAlgorithm m f h l) 1 := by + unfold ShuffleAlgorithm + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply UniformShuffler_norms + exact h + simp + + + +/- Conversion of SLang output to PMF.-/ +def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := + ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h l⟩ +/- Shuffle Algorithm is ε-differentially private, given that local randomized ε-DP. -/ theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) (hsa: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry From 6763a2cd94115484c38c63a7160a73403f8b2f24 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Tue, 19 Aug 2025 13:54:46 -0700 Subject: [PATCH 175/216] Cleaning --- .../Pure/Local/ShuffleModel/Definitions.lean | 203 ------------------ 1 file changed, 203 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean deleted file mode 100644 index baa4164f..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/ShuffleModel/Definitions.lean +++ /dev/null @@ -1,203 +0,0 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties - -namespace SLang - -def Shuffler {α: Type}(l:List α) := do -match l with -| [] => pure [] -| hd::tl => - let len := (hd :: tl).length - let i : Nat ← UniformSample (Nat.toPNat' len) - let rest : List α ← Shuffler tl - return rest.insertNth i hd - -#eval List.insertNth 0 1 [2,3] -#eval List.eraseIdx [1,2,3] 0 -lemma insertNth_helper {α : Type}(b a_1: List α)(a: Nat)(h: α): b = List.insertNth a h a_1 ↔ a_1 = List.eraseIdx b a := by sorry - - -lemma Shuffler_empty {α: Type}(l:List α)(h: l = []): Shuffler l [] = 1 := by - unfold Shuffler - rw [h] - simp [pure] - - - -lemma Shuffler_norm [DecidableEq α]{α: Type}(l:List α): HasSum (Shuffler l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - induction l with - | nil => - unfold Shuffler - simp [pure] - unfold probPure - simp - | cons h t ih => - unfold Shuffler - simp [pure] - rw [← Summable.hasSum_iff ENNReal.summable] - rw [Summable.hasSum_iff ENNReal.summable] - simp - conv => - enter [1,1,b,1,a,2,1,a_1] - rw [insertNth_helper] - rename_i α_1 inst - conv => - enter [1, 1, b, 1, a, 2, 1, a_1] - sorry - - -def BinomialSample (seed: MultiBernoulli.SeedType)(n:PNat) := do - let seeds := List.replicate n seed - let list ← MultiBernoulli.MultiBernoulliSample (seeds) - let k := List.count true list - return k - -theorem BinomialSample_norms [LawfulMonad SLang] (seed : MultiBernoulli.SeedType) (n : PNat) : - HasSum (BinomialSample seed n) 1 := by - rw [BinomialSample] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - have h: (push_forward (MultiBernoulli.MultiBernoulliSample (List.replicate (↑n) seed)) - (fun (a : List Bool) => (List.count true a))) = (fun (b : Nat) => - (∑' (a : List Bool), if b = List.count true a then MultiBernoulli.MultiBernoulliSample - (List.replicate (↑n) seed) a else 0)) := by - unfold push_forward - rfl - rw [← h] - rw [push_forward_prob_is_prob] - simp [MultiBernoulli.MultiBernoulliSample_normalizes] - -theorem BinomialSample_kprob (seed: MultiBernoulli.SeedType) (n : PNat) (k : Nat) : - BinomialSample seed n k = ((n: Nat).choose k) * ((num / den) ^ k) * ((1 - (num / den)) ^ (n - k)) := by - rw[BinomialSample] - simp - unfold MultiBernoulli.MultiBernoulliSample - simp [pure, bind] - - sorry - - /- This is the Shuffle Model. -/ -def ShuffleModel(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do - let l ← RandomizedResponse.RRSample query num den h l - let b ← Shuffler l - return b - -lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry - -lemma insertNth_eq_iff {α : Type} [DecidableEq α] - {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): - l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry - -lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry -lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n - simp at hlen1 - rw[symm hlen1] - simp - unfold Shuffler - simp - symm at hlen1 - rw[hlen1] at hlen2 - rw[List.length_eq_zero] at hlen2 - exact hlen2 - - case cons hd tl ih => - unfold Shuffler - simp - simp at hlen1 - rw[symm hlen1] - unfold List.isPerm at h - rw[Bool.and_eq_true] at h - cases h - rename_i left right - have h := contains_idx l₂ hd left - cases h - rename_i j ht - conv => - enter[1,1,a,2] - rw[tsum_eq_single (l₂.eraseIdx a) (by - intro l h - simp - intro h1 - rw[insertNth_eq_iff] at h1 - cases h1 - rename_i left2 right2 - simp at h - contradiction - exact h1 - )] - rfl - simp - rw[tsum_eq_single j.val (by - intro a h - simp - intro h1 - - )] - have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry - rw[if_pos] - - rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by rw[symm hlen2, List.length_eraseIdx];simp) (by rw[erase_eq_eraseIdx ht];exact right)] - rw[UniformSample_apply] - rw[hlen1] - rw[inv_eq_one_div] - - simp - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[if_pos nonzero] - rw[← ENNReal.mul_inv] - rw[inv_inj] - sorry - left - simp - linarith - - left - simp - /-rw[Nat.mul_factorial_pred nonzero]-/ - rw[hlen1] - rw[symm hlen2] - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[symm hlen2] at nonzero - simp[nonzero] - exact h2 - - -/--/ - unfold List.isPerm at h - rw[Bool.and_eq_true] at h - cases h - rename_i left right - have h := contains_idx l₂ hd left - cases h - rename_i j ht - - - conv => - enter[1,1,a,2] - rw[tsum_eq_single (List.eraseIdx l₂ j ) (by - intro b' h - simp - - - have h2: b' = List.eraseIdx (List.insertNth j hd b') j := by rw[List.eraseIdx_insertNth] - intro ht - simp[ht] at h - sorry - - - )] - rfl - conv => - enter[1,1,a] - rw[UniformSample_apply (tl.length + 1).toPNat'] - rfl From e780ade7156ceb1dac7b3daa0c6d3ad2b2112f7c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 14:15:28 -0700 Subject: [PATCH 176/216] fixedunified --- .../Pure/Local/LocalDP/LocalToDataset.lean | 19 +++++------ .../Local/RAPPOR/Properties/BasicLemmas.lean | 2 +- .../Pure/Local/RAPPOR/Properties/DPProof.lean | 19 +---------- .../Properties/DPProof.lean | 33 ++++++++----------- 4 files changed, 24 insertions(+), 49 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean index 79f990f8..8d648aa5 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/LocalToDataset.lean @@ -59,11 +59,13 @@ lemma local_to_dataset_prob_of_ind_prob_PMF (m: LocalMechanism T U) (l : List T) This lemma is applied in the proof of DP for both Randomized Response and One-Time Basic RAPPOR. -/ lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) - (P : T → U → Bool) - (nonzero: ∀ (k : T), ∀ (bo : U), P k bo ↔ (m k) bo ≠ 0) - (equiv: ∀ l₁ l₂ : List T, ∀ b : List U, (blen1: l₁.length = b.length) → (blen2: l₂.length = b.length) → (∀ i : Fin l₁.length, (m l₁[i]) (b[i]) = 0 ↔ (m l₂[i]) b[i] = 0)) + (equiv: ∀ l₁ l₂ : List T, (h_upd: UpdateNeighbour l₁ l₂) → ∀ b : List U, (blen1: l₁.length = b.length) → + (∀ i : Fin l₁.length, (m l₁[i]) (b[i]) = 0 ↔ (m (l₂[i]'(by + have h: l₂.length = b.length := by rw[←blen1]; apply (Eq.symm (UpdateNeighbour_length h_upd)) + omega))) b[i] = 0)) (noninf: ∀ (k : T) (bo : U), (m k) bo ≠ ⊤): Local_DP m ε → DP_withUpdateNeighbour (local_to_dataset_PMF m) ε := by + let P: T → U → Bool := fun k bo => (m k) bo ≠ 0 intro hloc apply singleton_to_event_update intros l₁ l₂ h_adj x @@ -91,16 +93,14 @@ lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) simp[Local_DP] at hloc apply hloc intro i - apply (nonzero _ _).mp - simp at P_true + simp[P] at P_true apply P_true i | false => simp at P_true have nonzero2: ∀ (k : T) (bo : U), P k bo = false ↔ (m k) bo = 0 := by intro k bo apply not_iff_not.mp - simp - apply nonzero k bo + simp [P] /- The next several "have" statements are just to prove index validity. The proofs can undoubtedly be simplfied with some effort. -/ have h1: a.length + (c.length + 1) = l₂.length := by @@ -147,9 +147,8 @@ lemma LocalDP_to_dataset (m : LocalMechanism T U) (ε : ℝ) use ⟨(@Nat.cast (Fin (l₂.length - 1 + 1)) Fin.instNatCast a.length).succAbove z, valid_index13⟩ apply And.intro simp - apply (equiv l₁ l₂ x xlen1 xlen2 _).mpr - apply (nonzero2 _ _).mp - apply hz + apply (equiv l₁ l₂ (UpdateNeighbour.Update hl₁ hl₂) x xlen1 _).mpr + simp_all [P] rw [h2] rw [@ENNReal.zero_div] simp diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean index a3ad2842..d707c2cc 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/BasicLemmas.lean @@ -12,7 +12,7 @@ lemma RAPPORSingleSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) ( apply RRSamplePushForward_diff_lengths num den h (one_hot n query l₁) l₂ hlen -/- The same "diff_lenghts" theorem as above, but extended to the entire dataset. -/ +/- The same "diff_lengths" theorem as above, but extended to the entire dataset. -/ lemma RAPPORSample_diff_lengths [LawfulMonad SLang] {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (l₁ : List T) (x : List (List Bool)) (hlen : l₁.length ≠ x.length): RAPPORSample n query num den h l₁ x = 0 := by induction l₁ generalizing x with diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean index 28e520b2..7f3041cb 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RAPPOR/Properties/DPProof.lean @@ -201,24 +201,7 @@ theorem RAPPORSample_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (de := by have h1: RAPPORSample_PMF n query num den h = local_to_dataset_PMF (RAPPORSingle_Local n query num den h) := rfl rw [h1] - let P : T → List Bool → Bool := fun _ bo => (n == bo.length) - apply LocalDP_to_dataset _ _ P - {intro k bo - simp [P, -ne_eq] - apply Iff.intro - simp [RAPPORSingle_Local, -ne_eq] - intro hlen - apply RAPPORSingleSample_non_zero - simp [one_hot, hlen] - simp [RAPPORSingle_Local, -ne_eq] - intro hbo - by_contra bol - have hcontr: RAPPORSingleSample n query num den h k bo = 0 := by - apply RAPPORSingleSample_diff_lengths - simp [one_hot] - exact bol - contradiction - } + apply LocalDP_to_dataset _ _ {intro l₁ l₂ b k bo i apply Iff.intro simp [RAPPORSingle_Local] diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean index d89b1f82..6989e7ce 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/DPProof.lean @@ -466,25 +466,18 @@ lemma RR_Local_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < d theorem RRSample_DP (query : T → Bool) (num : Nat) (den : PNat) (h : 2 * num < den): DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by have h1: RRSample_PMF query num den h = local_to_dataset_PMF (RRSingle_Local query num den h) := rfl rw [h1] - let P : T → Bool → Bool := fun _ _ => true - have hP: ∀ k : T, ∀ bo : Bool, P k bo ↔ RRSingle_Local query num den h k bo ≠ 0 := by - intro k bo - simp[RRSingle_Local, -ne_eq] - apply RRSingleSample_non_zero - exact h - apply LocalDP_to_dataset _ _ P - { apply hP } - { simp [RRSingle_Local] - intro l₁ l₂ b blen1 blen2 i - apply Iff.intro - intro hx - have h_contr: RRSingleSample query num den h l₁[i.val] b[i.val] = 0 := by aesop - have h_contr2: RRSingleSample query num den h l₁[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₁[i.val] b[i.val] - contradiction - intro hx - have h_contr: RRSingleSample query num den h l₂[i.val] b[i.val] = 0 := by aesop - have h_contr2: RRSingleSample query num den h l₂[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₂[i.val] b[i.val] - contradiction - } + apply LocalDP_to_dataset _ _ + simp [RRSingle_Local] + intro l₁ l₂ h_upd b blen1 i + have hlen: l₂.length = b.length := by rw[←blen1]; apply Eq.symm (UpdateNeighbour_length h_upd) + apply Iff.intro + intro hx + have h_contr: RRSingleSample query num den h l₁[i.val] b[i.val] = 0 := by aesop + have h_contr2: RRSingleSample query num den h l₁[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₁[i.val] b[i.val] + contradiction + intro hx + have h_contr: RRSingleSample query num den h l₂[i.val] b[i.val] = 0 := by aesop + have h_contr2: RRSingleSample query num den h l₂[i.val] b[i.val] ≠ 0 := RRSingleSample_non_zero query num den h l₂[i.val] b[i.val] + contradiction apply RRSingleSample_finite query num den h apply RR_Local_DP From 6d9f6003e34a051a8f226aa43df7cf8b6d5fd191 Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 19 Aug 2025 14:56:11 -0700 Subject: [PATCH 177/216] Put Randomized Post Processing in New File --- .../Pure/RandomizedPostProcessing.lean | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean new file mode 100644 index 00000000..c036de38 --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -0,0 +1,154 @@ +import SampCert.DifferentialPrivacy.Pure.DP +import SampCert.DifferentialPrivacy.Generic +import Mathlib.Probability.ProbabilityMassFunction.Basic +import Mathlib.Topology.Algebra.InfiniteSum.Basic +import Mathlib.Data.Set.Defs +import Mathlib.Data.Set.Prod + +noncomputable section + +open Classical Set + +namespace SLang + + +def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : Mechanism T V := + fun l => (nq l).bind g + +lemma tsum_ite_eq_single {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U): + (∑' (x : U), if x = u then ((m l₁) x) else 0) = (m l₁) u := by + classical + have hpoint : + (fun x : U => if x = u then (m l₁) x else 0) = + (fun x : U => if x = u then (m l₁) u else 0) := by + funext x + by_cases hx : x = u + · simp [hx] + · simp [hx] + have hcollapse : (∑' x : U, if x = u then (m l₁) u else 0) = (m l₁) u := by + simp [tsum_ite_eq (β := U) (α := ENNReal) u ((m l₁) u)] + simp [hpoint, hcollapse] + + +lemma DP.pointwise_ratio_bound {T U : Type} + {m : Mechanism T U} {ε : ℝ} + (h : DP m ε) {l₁ l₂ : List T} (hN : Neighbour l₁ l₂) : + ∀ u : U, m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + intro u + have hS := h l₁ l₂ hN ({u} : Set U) + have hnum : + (∑' x : U, (if x ∈ ({u} : Set U) then m l₁ x else 0)) = m l₁ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₁ x else 0) + = (fun x : U => if x = u then m l₁ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (tsum_ite_eq_single m l₁ u) + have hden : + (∑' x : U, (if x ∈ ({u} : Set U) then m l₂ x else 0)) = m l₂ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₂ x else 0) + = (fun x : U => if x = u then m l₂ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (tsum_ite_eq_single m l₂ u) + have hratio : m l₁ u / m l₂ u ≤ ENNReal.ofReal (Real.exp ε) := by + rw [hnum, hden] at hS + exact hS + by_cases hz : m l₂ u = 0 + · have hfin : ENNReal.ofReal (Real.exp ε) ≠ ⊤ := by aesop + have hzero : m l₁ u = 0 := by + by_contra hpos + have htop : m l₁ u / m l₂ u = ⊤ := by + exact ENNReal.div_eq_top.mpr (Or.inl ⟨by simp [hpos], hz⟩) + have : (⊤ : ENNReal) ≤ ENNReal.ofReal (Real.exp ε) := by + simp at hratio + aesop + have : ENNReal.ofReal (Real.exp ε) = ⊤ := top_le_iff.mp this + exact hfin this + simp [hz, hzero, zero_mul] + · have h_not_infty : ⊤ ≠ m l₂ u := by + have hle : m l₂ u ≤ 1 := by simpa using (m l₂).coe_le_one u + have hlt : m l₂ u < ⊤ := lt_of_le_of_lt hle (by simp) + aesop + have : m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + rw [← ENNReal.div_le_iff_le_mul] + · exact hratio + · aesop + · aesop + simpa using this + + + +lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): +(∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by + calc + (∑' v : V, if v ∈ S then p u * g u v else 0) + = ∑' v : V, p u * (if v ∈ S then g u v else 0) := by + simp [hsplit] + _ = p u * ∑' v : V, (if v ∈ S then g u v else 0) := by + simpa using + (ENNReal.tsum_mul_left + (a := p u) + (f := fun v : V => if v ∈ S then g u v else 0)) + +lemma tsum_bind_indicator {U V : Type} + (p : PMF U) (g : U → PMF V) (S : Set V) : + (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by + classical + have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by + intro v; simp [PMF.bind_apply] + calc + (∑' v : V, if v ∈ S then (p.bind g) v else 0) + = ∑' v, if v ∈ S then (∑' u, p u * g u v) else 0 := by + simp [hbind] + _ = ∑' v, ∑' u, (if v ∈ S then p u * g u v else 0) := by + refine tsum_congr ?_ + intro v; by_cases hv : v ∈ S <;> simp [hv] + _ = ∑' u, ∑' v, (if v ∈ S then p u * g u v else 0) := by + simpa using + ENNReal.tsum_comm (f := fun v u => (if v ∈ S then p u * g u v else 0)) + _ = ∑' u, p u * (∑' v, if v ∈ S then g u v else 0) := by + refine tsum_congr ?_ + intro u + have hsplit :(fun v => if v ∈ S then p u * g u v else 0) = (fun v => p u * (if v ∈ S then g u v else 0)) := by + funext v; by_cases hv : v ∈ S <;> simp [hv, mul_comm, mul_left_comm, mul_assoc] + exact tsum_indicator_mul_left p g S u hsplit + +lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq ε) (g : U → PMF V) : + DP (privPostProcessRand nq g) ε := by + intro l₁ l₂ hN S + let p₁ := nq l₁ + let p₂ := nq l₂ + let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) + have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) + = ∑' u : U, p₁ u * w u := by + simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S + have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) + = ∑' u : U, p₂ u * w u := by + simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S + have hpt := DP.pointwise_ratio_bound (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN + have hsum : + (∑' u : U, p₁ u * w u) + ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by + have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by + intro u + have := hpt u + have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u + have hw0 : 0 ≤ w u := by aesop + have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 + simpa [mul_left_comm, mul_comm, mul_assoc] using hmul + have := ENNReal.tsum_le_tsum hpt' + simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this + by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 + · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by + have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum + exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + simp [hNum, hDen, hNum0, hDen0] + · nth_rewrite 1 [mul_comm] at hsum + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) + simpa [hNum, hDen] using this From ba048f27f86cbce0fc9f9b89443b7bfdfb90d2b5 Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 19 Aug 2025 15:58:21 -0700 Subject: [PATCH 178/216] randomized post processing with update neighbour --- .../Pure/Postprocessing.lean | 3 +- .../Pure/RandomizedPostProcessing.lean | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean index 1410c3fc..511a92ff 100644 --- a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean @@ -44,6 +44,7 @@ lemma privPostProcess_DP_bound {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq exact Real.exp_pos ε · simp +/- def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : Mechanism T V := fun l => (nq l).bind g @@ -214,7 +215,7 @@ lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} · have hpos : (0 : ENNReal) < (∑' u : U, p₂ u * w u) := lt_of_le_of_ne' (by exact bot_le) hDen0 have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact bruh hsum hpos) simpa [hNum, hDen] using this - +-/ /-- ``privPostProcess`` satisfies pure DP, for any surjective postprocessing function. diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index c036de38..d6ce8185 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -1,5 +1,6 @@ import SampCert.DifferentialPrivacy.Pure.DP import SampCert.DifferentialPrivacy.Generic +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour import Mathlib.Probability.ProbabilityMassFunction.Basic import Mathlib.Topology.Algebra.InfiniteSum.Basic import Mathlib.Data.Set.Defs @@ -152,3 +153,88 @@ lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} · nth_rewrite 1 [mul_comm] at hsum have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) simpa [hNum, hDen] using this + + +lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} + {m : Mechanism T U} {ε : ℝ} + (h : DP_withUpdateNeighbour m ε) {l₁ l₂ : List T} (hN : UpdateNeighbour l₁ l₂) : + ∀ u : U, m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + intro u + have hS := h l₁ l₂ hN ({u} : Set U) + have hnum : (∑' x : U, (if x ∈ ({u} : Set U) then m l₁ x else 0)) = m l₁ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₁ x else 0) + = (fun x : U => if x = u then m l₁ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (tsum_ite_eq_single m l₁ u) + have hden : (∑' x : U, (if x ∈ ({u} : Set U) then m l₂ x else 0)) = m l₂ u := by + classical + have : (fun x : U => if x ∈ ({u} : Set U) then m l₂ x else 0) + = (fun x : U => if x = u then m l₂ u else 0) := by + funext x + by_cases hx : x = u + · subst hx; simp + · simp [Set.mem_singleton_iff, hx] + simpa [this] using (tsum_ite_eq_single m l₂ u) + have hratio : m l₁ u / m l₂ u ≤ ENNReal.ofReal (Real.exp ε) := by + rw [hnum, hden] at hS + exact hS + by_cases hz : m l₂ u = 0 + · have hfin : ENNReal.ofReal (Real.exp ε) ≠ ⊤ := by aesop + have hzero : m l₁ u = 0 := by + by_contra hpos + have htop : m l₁ u / m l₂ u = ⊤ := by + exact ENNReal.div_eq_top.mpr (Or.inl ⟨by simp [hpos], hz⟩) + have : (⊤ : ENNReal) ≤ ENNReal.ofReal (Real.exp ε) := by + simp at hratio + aesop + have : ENNReal.ofReal (Real.exp ε) = ⊤ := top_le_iff.mp this + exact hfin this + simp [hz, hzero, zero_mul] + · have h_not_infty : ⊤ ≠ m l₂ u := by + have hle : m l₂ u ≤ 1 := by simpa using (m l₂).coe_le_one u + have hlt : m l₂ u < ⊤ := lt_of_le_of_lt hle (by simp) + aesop + have : m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by + rw [← ENNReal.div_le_iff_le_mul] + · exact hratio + · aesop + · aesop + simpa using this + +lemma randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : DP_withUpdateNeighbour nq ε) (g : U → PMF V) : + DP_withUpdateNeighbour (privPostProcessRand nq g) ε := by + intro l₁ l₂ hN S + let p₁ := nq l₁ + let p₂ := nq l₂ + let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) + have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) + = ∑' u : U, p₁ u * w u := by + simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S + have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) + = ∑' u : U, p₂ u * w u := by + simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S + have hpt := DP.pointwise_ratio_bound_for_Update_neighbour (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN + have hsum : + (∑' u : U, p₁ u * w u) + ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by + have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by + intro u + have := hpt u + have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u + have hw0 : 0 ≤ w u := by aesop + have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 + simpa [mul_left_comm, mul_comm, mul_assoc] using hmul + have := ENNReal.tsum_le_tsum hpt' + simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this + by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 + · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by + have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum + exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + simp [hNum, hDen, hNum0, hDen0] + · nth_rewrite 1 [mul_comm] at hsum + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) + simpa [hNum, hDen] using this From bf30bcd6e217687fe3b5a72b5b4d51814f25d735 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 15:59:34 -0700 Subject: [PATCH 179/216] DP --- .../Properties/PMFProof.lean | 3 +-- .../Pure/ShuffleModel/Definitions.lean | 6 +---- .../Pure/ShuffleModel/Properties.lean | 27 ++++++++++++++++--- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean index 3b9e611d..b29daae8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Properties/PMFProof.lean @@ -4,9 +4,8 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.Normalization import SampCert.Samplers.Bernoulli.Properties - +namespace RandomizedResponse open SLang -open RandomizedResponse /- Instantiation of RRSinglePushForward as a PMF. -/ lemma RRSinglePushForward_PMF (num : Nat) (den : PNat) (h: 2 * num < den) (l : Bool) : diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 7803e7f1..c5721ce8 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -21,16 +21,12 @@ match l with let rest : List α ← Shuffler tl return rest.insertNth i hd - /- This is the implementation of the Shuffle algorithm using Randomized Response as the local randomizer. -/ +/- This is the implementation of the Shuffle algorithm using Randomized Response as the local randomizer. -/ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l return b -#check Shuffler -#check RandomizedResponse.RRSample -#check Mechanism - /- Definition of a function that uniformly permutes a given list.-/ def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 68a3d6a7..98c3a2cd 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -11,6 +11,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMF namespace SLang +open RandomizedResponse /- Shuffler function normalizes-/ lemma Shuffler_PMF_helper {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by @@ -46,6 +47,8 @@ lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : rw [← Summable.hasSum_iff ENNReal.summable] apply Shuffler_PMF_helper +def Shuffler_PMF {α : Type} [DecidableEq α] [BEq α] (l : List α) : PMF (List α) := + ⟨Shuffler l, Shuffler_PMF_helper l⟩ lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry @@ -146,10 +149,12 @@ theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (nu rw [← Summable.hasSum_iff ENNReal.summable] apply RRSample_PMF_helper +def RRShuffle_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := + ⟨RRShuffle query num den h l, RRShuffle_PMF_helper query num den h l⟩ /- Unsolved proof that any Uniform Shuffler normalizes. TODO-/ -lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f) -:∀(b: List U),∑' (i : List U), f b i = 1 := by +lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f): +∀(b: List U),∑' (i : List U), f b i = 1 := by intro b have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by unfold UniformShuffler at h @@ -164,6 +169,11 @@ lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:Un rw[ENNReal.tsum_mul_left] sorry +lemma UniformShuffler_norms' {U : Type} [BEq U] (f : List U → SLang (List U)) (h : UniformShuffler f) (u : List U): + HasSum (f u) 1 := by + simp [Summable.hasSum_iff] + apply UniformShuffler_norms f h + /- Any shuffle algorithm normalizes. -/ lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): HasSum (ShuffleAlgorithm m f h l) 1 := by @@ -186,6 +196,15 @@ HasSum (ShuffleAlgorithm m f h l) 1 := by def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h l⟩ +lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f): privPostProcessRand m (fun u => ⟨f u, UniformShuffler_norms' f h_uniform u⟩) = ShuffleAlgorithm_PMF m f h_uniform := by + funext x + simp [privPostProcessRand, ShuffleAlgorithm_PMF, ShuffleAlgorithm] + congr + /- Shuffle Algorithm is ε-differentially private, given that local randomized ε-DP. -/ -theorem ShuffleAlgorithm_is_DP [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) -(hsa: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f hsa) ε := by sorry +theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) +(h_uniform: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform) ε := by +conv => + enter [1] + rw [←shuffling_is_postprocessing] +sorry From 1e62fc7d6ea3ffeaf263c534132eabd60d3b2b6e Mon Sep 17 00:00:00 2001 From: PCChess Date: Tue, 19 Aug 2025 15:59:42 -0700 Subject: [PATCH 180/216] Update RandomizedPostProcessing.lean --- SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index d6ce8185..3d267c6e 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -217,7 +217,7 @@ lemma randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechani have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) = ∑' u : U, p₂ u * w u := by simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S - have hpt := DP.pointwise_ratio_bound_for_Update_neighbour (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN + have hpt := DP.pointwise_ratio_bound_for_UpdateNeighbour (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN have hsum : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by From f79d3188b80513455cab7477c95f2054cf6955e0 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 16:20:16 -0700 Subject: [PATCH 181/216] provedDP --- .../DifferentialPrivacy/Pure/RandomizedPostProcessing.lean | 4 ++-- .../DifferentialPrivacy/Pure/ShuffleModel/Properties.lean | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index 3d267c6e..2faa9994 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -156,7 +156,7 @@ lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} - {m : Mechanism T U} {ε : ℝ} + {m : Mechanism T U} {ε : Real} (h : DP_withUpdateNeighbour m ε) {l₁ l₂ : List T} (hN : UpdateNeighbour l₁ l₂) : ∀ u : U, m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by intro u @@ -205,7 +205,7 @@ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} · aesop simpa using this -lemma randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : DP_withUpdateNeighbour nq ε) (g : U → PMF V) : +lemma randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechanism T U} {ε : Real} (h : DP_withUpdateNeighbour nq ε) (g : U → PMF V) : DP_withUpdateNeighbour (privPostProcessRand nq g) ε := by intro l₁ l₂ hN S let p₁ := nq l₁ diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 98c3a2cd..c63c603b 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -8,6 +8,7 @@ import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof +import SampCert.DifferentialPrivacy.Pure.RandomizedPostProcessing namespace SLang @@ -201,10 +202,11 @@ lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T ( simp [privPostProcessRand, ShuffleAlgorithm_PMF, ShuffleAlgorithm] congr -/- Shuffle Algorithm is ε-differentially private, given that local randomized ε-DP. -/ +/- Shuffle Algorithm is ε-differentially private, given that the local algorithm is ε-differentially private. -/ theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) (h_uniform: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform) ε := by conv => enter [1] rw [←shuffling_is_postprocessing] -sorry +let g : List U → PMF (List U) := (fun u => ⟨f u, UniformShuffler_norms' f h_uniform u⟩) +apply @randPostProcess_DP_bound_with_UpdateNeighbour T _ _ m ε hdp g From d3c764c3a27f2792e470a3b49272aacba127a9bd Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:51:04 -0700 Subject: [PATCH 182/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0c5ca2d..0c4a25c4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SampCert +# Verifying Differential Privacy in Lean A verified implementation using [Lean](https://github.com/leanprover/lean4) and [Mathlib](https://github.com/leanprover-community/mathlib4) of randomized algorithms including [the discrete Gaussian sampler for differential privacy](https://arxiv.org/abs/2004.00010), key results in [zero concentrated differential privacy](https://arxiv.org/abs/1605.02065), and [some verified (unbounded) private queries](https://arxiv.org/pdf/1909.01917). From 07802f1d5de82b8bfbe7001d38d2a07b941301e8 Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:07:42 -0700 Subject: [PATCH 183/216] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c4a25c4..58d6540d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Verifying Differential Privacy in Lean -A verified implementation using [Lean](https://github.com/leanprover/lean4) and [Mathlib](https://github.com/leanprover-community/mathlib4) of randomized algorithms including [the discrete Gaussian sampler for differential privacy](https://arxiv.org/abs/2004.00010), key results in [zero concentrated differential privacy](https://arxiv.org/abs/1605.02065), and [some verified (unbounded) private queries](https://arxiv.org/pdf/1909.01917). +The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for DP mechanisms, and the Gaussian and Laplace mechanisms. + +We build upon SampCert, creating support for the local model. We also implement the randomized response and one-time basic RAPPOR mechanisms, as well as implement a more robust post-processing property. + +A verified implementation using [Lean](https://github.com/leanprover/SampCert)) and [Mathlib](https://github.com/leanprover-community/mathlib4) of randomized algorithms including [the discrete Gaussian sampler for differential privacy](https://arxiv.org/abs/2004.00010), key results in [zero concentrated differential privacy](https://arxiv.org/abs/1605.02065), and [some verified (unbounded) private queries](https://arxiv.org/pdf/1909.01917). SampCert is deployed and used in the [AWS Clean Rooms Differential Privacy service](https://docs.aws.amazon.com/clean-rooms/latest/userguide/differential-privacy.html#dp-overview). SampCert proves deep properties about some of its randomized algorithm and makes heavy use of Mathlib. For example, we use theorems such as [the Poisson summation formula](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Fourier/PoissonSummation.html#Real.tsum_eq_tsum_fourierIntegral_of_rpow_decay). From 813bd6c3e31d81f819b1468e4be1a63a8b671337 Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:17:58 -0700 Subject: [PATCH 184/216] Update README.md --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 58d6540d..0a289266 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,11 @@ The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for DP mechanisms, and the Gaussian and Laplace mechanisms. -We build upon SampCert, creating support for the local model. We also implement the randomized response and one-time basic RAPPOR mechanisms, as well as implement a more robust post-processing property. +We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implement a more robust post-processing property for randomized mappings. -A verified implementation using [Lean](https://github.com/leanprover/SampCert)) and [Mathlib](https://github.com/leanprover-community/mathlib4) of randomized algorithms including [the discrete Gaussian sampler for differential privacy](https://arxiv.org/abs/2004.00010), key results in [zero concentrated differential privacy](https://arxiv.org/abs/1605.02065), and [some verified (unbounded) private queries](https://arxiv.org/pdf/1909.01917). -SampCert is deployed and used in the [AWS Clean Rooms Differential Privacy service](https://docs.aws.amazon.com/clean-rooms/latest/userguide/differential-privacy.html#dp-overview). SampCert proves deep properties about some of its randomized algorithm and makes heavy use of Mathlib. For example, we use theorems such as [the Poisson summation formula](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Fourier/PoissonSummation.html#Real.tsum_eq_tsum_fourierIntegral_of_rpow_decay). -The principal developer of SampCert is [Jean-Baptiste Tristan](https://jtristan.github.io/). It is also developed by [Markus de Medeiros](https://www.markusde.ca/). - -Other people have contributed important ideas or tools for deployment including (in no particular order): Leo de Moura, Anjali Joshi, Joseph Tassarotti, Stefan Zetzsche, Aws Albharghouti, Muhammad Naveed, Tristan Ravitch, Fabian Zaiser, Tomas Skrivan. - -To cite SampCert you can currently use the following reference: +We would like to thank SampCert for motivating and being the basis for our project. ``` @software{Tristan_SampCert_Verified_2024, author = {Tristan, Jean-Baptiste}, From b378ff59f31cf3cc2eb508c7759ac7628fb6e187 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 21:49:26 -0700 Subject: [PATCH 185/216] fortoday --- .../Pure/ShuffleModel/Definitions.lean | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index c5721ce8..4c8af018 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -36,3 +36,40 @@ def ShuffleAlgorithm [BEq U](m : Mechanism T (List U))(f : List U → SLang (Li let x ← (m l).toSLang let b ← f x return b + +noncomputable def num_perms (l : List U) [BEq U] [DecidableEq (List U)] : ENNReal := ((List.permutations l).toFinset).card + +def UniformShuffler_v2 {U : Type} [BEq U] [DecidableEq (List U)] (f : List U → SLang (List U)) : Prop := + ∀ l : List U, ∀ a : List U, (List.isPerm l a → f l a = (num_perms a)⁻¹) ∧ (¬ List.isPerm l a → f l a = 0) + +lemma UniformShuffler_v2_norms {U : Type} [BEq U] [DecidableEq U] [DecidableEq (List U)] (f : List U → SLang (List U)) (h : UniformShuffler_v2 f) (l : List U): HasSum (f l) 1 := by + simp [Summable.hasSum_iff] + have h1 (b : List U): f l b = (if List.isPerm l b then (num_perms l)⁻¹ else 0) := by + cases h_perm: (l.isPerm b == true) with + | true => + simp at h_perm + simp [UniformShuffler_v2] at h + induction l generalizing b with + | nil => + simp_all [List.isPerm, List.isEmpty] + aesop + | cons hd tl ih => + cases hb : (hd :: tl).isPerm b with + | true => simp + sorry + | false => simp [hb] + apply (h (hd :: tl) b).right hb + | false => + simp at h_perm + simp_all only [Bool.false_eq_true, ↓reduceIte, CharP.cast_eq_zero] + simp [UniformShuffler_v2] at h + apply (h l b).right h_perm + conv => + enter [1, 1, b] + rw [h1] + have h2: ∑' (b : List U), (if l.isPerm b then ((num_perms l)⁻¹) else 0) = (num_perms l) * (num_perms l)⁻¹ := by + sorry + rw [h2] + apply ENNReal.mul_inv_cancel + simp [num_perms, List.permutations] + simp [num_perms, List.permutations] From 0f046fbd6ea7afeaf813ef26cf75241a84aac35d Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:53:14 -0700 Subject: [PATCH 186/216] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0a289266..3de77406 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # Verifying Differential Privacy in Lean -The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for DP mechanisms, and the Gaussian and Laplace mechanisms. - -We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implement a more robust post-processing property for randomized mappings. - +The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and several differentially private algorithms, including the Gaussian and Laplace mechanisms. +We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implementing a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. We would like to thank SampCert for motivating and being the basis for our project. ``` From 6587cf48f14d0b780c7676fda6e58ae9e490c79d Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Tue, 19 Aug 2025 22:46:13 -0700 Subject: [PATCH 187/216] Update README.md --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/README.md b/README.md index 3de77406..7ddeaf46 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,73 @@ The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et a We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implementing a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. +Our implementations are in SampCert/DifferentialPrivacy/Pure. Our additions are in the Local folder, Shuffle Model folder, and RandomizedPostProcessing.lean file. + +### Local + +#### BinomialSample + +#### LocalDP + +The files in this folder provide a variety of lemmas to support general ideas about the local model of DP. A key lemma here is `DP_withUpdateNeighbour`, which defines differential privacy with an user provided definition of neighbour. This provides more functionality than the Add-Remove-Update definition of neighbour in SampCert. + +Another key idea is `LocalDP_to_dataset`. A key idea in the local model is that if an algorithm on an individual user is $\epsilon$-DP, then the algorithm on the entire dataset is $\epsilon$-DP. This lemma shows that. + +#### MultiBernoulli + +The files here provide some helper lemmas related to the Bernoulli distribution. Drawing from the Bernoulli distribution is needed for our implementation of randomized response. + +#### RAPPOR + +In this folder, we provide an implementation of basic one-time RAPPOR and give a proof of differential privacy. The file Definitions.lean provides our implementation of RAPPOR. The file `Properties/DPProof.lean` provides a proof that our implementation of RAPPOR is differentially private. + +#### RandomizedResponse + +In this folder, we provide an implementation of randomized response and give a proof of differential privacy. The file Definitions.lean provides our implementation of randomized response. The file `Properties/DPProof.lean` provides a proof that our implementation of randomized response is differentially private. + +#### ENNRealLemmaSuites.lean + +A significant challenge was proving arithemetic states over ENNReal. Mathlib currently provides less support for arithmetic over ENNReal, so oftentimes we have to prove statements ourselves. These statements are in this file. + +#### LawfulMonadSLang.lean + +Instantiates SLang as a LawfulMonad. + +#### Normalization.lean + +Lemmas relating to proving that applying a monadic map to a normalized function remains normalized. + +#### ProbabilityProduct.lean + +Shows that the probability of generating a sequence of outputs is equal to the product of the probabilities of generating each output independently. Used in randomized response and RAPPOR. + +#### PushForward.lean + + + +#### Reduction.lean + +Helper function used in proving a local algorithm is DP. Allows us to reduce the problem from considering the entire dataset to the local randomizer. + +### Shuffle model + +This folder contains the implementation and properties of the shuffle model. + +#### Definitions.lean + +This contains the definition of the shuffle model. The definition `Shuffler` is the randomized permutation. We implement the shuffle model with randomized response in `RRShuffle`, and provide a more general definition in `ShuffleAlgorithm`. + +#### Properties.lean + +This contains a variety of proofs that show that the shuffle model indeed outputs a PMF, and that the random permutation is uniform. The theorem `ShuffleAlgorithm_is_DP` also proves that the shuffle model is differentially private using the post-processing property. + +### RandomizedPostProcess.lean + +This file provides the Lean proof about the post-processing property of differential privacy: If there is an $\epsilon$-DP mechanism $M: X \rightarrow W$ and a (possibly randomized) mapping $F: W \rightarrow Z$, then $F \circ M$ is $\epsilon$-DP. The case where $F$ is deterministic is implemented in SampCert. We implement the case where $F$ is random. The result is in the lemma +``lemma randPostProcess_DP_bound_with_UpdateNeighbour``. + + + We would like to thank SampCert for motivating and being the basis for our project. ``` @software{Tristan_SampCert_Verified_2024, From c13bcc53938d0995b7714e7a0141817c0ea180fe Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 19 Aug 2025 23:11:18 -0700 Subject: [PATCH 188/216] donefortoday --- .../Pure/ShuffleModel/Definitions.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 4c8af018..8baec9c3 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -39,6 +39,17 @@ def ShuffleAlgorithm [BEq U](m : Mechanism T (List U))(f : List U → SLang (Li noncomputable def num_perms (l : List U) [BEq U] [DecidableEq (List U)] : ENNReal := ((List.permutations l).toFinset).card +lemma List.isPerm_iff_mem_permutations {U : Type} [DecidableEq U] [DecidableEq (List U)] (l : List U) (a : List U) : l.isPerm a ↔ a ∈ List.permutations l := by + apply Iff.intro + simp + rw [@List.perm_comm] + intro hp + apply (@List.isPerm_iff U _ l a).mp hp + simp + intro hp + rw [@List.perm_comm] at hp + apply (@List.isPerm_iff U _ l a).mpr hp + def UniformShuffler_v2 {U : Type} [BEq U] [DecidableEq (List U)] (f : List U → SLang (List U)) : Prop := ∀ l : List U, ∀ a : List U, (List.isPerm l a → f l a = (num_perms a)⁻¹) ∧ (¬ List.isPerm l a → f l a = 0) @@ -69,6 +80,8 @@ lemma UniformShuffler_v2_norms {U : Type} [BEq U] [DecidableEq U] [DecidableEq ( rw [h1] have h2: ∑' (b : List U), (if l.isPerm b then ((num_perms l)⁻¹) else 0) = (num_perms l) * (num_perms l)⁻¹ := by sorry + /- Idea: how about we rewrite l.isPerm in terms of membership in a list, + i.e., prove l.isPerm b ↔ b ∈ List.permutations l-/ rw [h2] apply ENNReal.mul_inv_cancel simp [num_perms, List.permutations] From 821780e8fba7ce13aa51f94c03972f11ebed0797 Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 20 Aug 2025 10:06:46 -0700 Subject: [PATCH 189/216] Update DPwithUpdateNeighbour.lean --- .../Pure/Local/LocalDP/DPwithUpdateNeighbour.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean index 3acd59c8..7ea9e57a 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/LocalDP/DPwithUpdateNeighbour.lean @@ -10,6 +10,7 @@ namespace SLang def DP_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_withGeneralNeighbour m (UpdateNeighbour) ε + /- Instantiation of DP_singleton with the update neighbour relation-/ def DP_singleton_withUpdateNeighbour (m : Mechanism T U) (ε : ℝ) : Prop := DP_singleton_withGeneralNeighbour m (UpdateNeighbour) ε From 948d228b72953bc669b7dd86b9e3019dd42b60bb Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:01:11 -0700 Subject: [PATCH 190/216] Update README.md --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index 7ddeaf46..460d465f 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,4 @@ This file provides the Lean proof about the post-processing property of differen -We would like to thank SampCert for motivating and being the basis for our project. -``` -@software{Tristan_SampCert_Verified_2024, -author = {Tristan, Jean-Baptiste}, -doi = {10.5281/zenodo.11204806}, -month = may, -title = {{SampCert : Verified Differential Privacy}}, -url = {https://github.com/leanprover/SampCert}, -version = {1.0.0}, -year = {2024} -} -``` +We would like to thank SampCert for motivating and being the basis for our project From 454067a39bf1c92754e17e71f80254c71bc3338e Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 20 Aug 2025 13:21:07 -0700 Subject: [PATCH 191/216] final --- .../Pure/ShuffleModel/Definitions.lean | 50 ------------------- .../Pure/ShuffleModel/Properties.lean | 44 +++++----------- 2 files changed, 12 insertions(+), 82 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 8baec9c3..c5721ce8 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -36,53 +36,3 @@ def ShuffleAlgorithm [BEq U](m : Mechanism T (List U))(f : List U → SLang (Li let x ← (m l).toSLang let b ← f x return b - -noncomputable def num_perms (l : List U) [BEq U] [DecidableEq (List U)] : ENNReal := ((List.permutations l).toFinset).card - -lemma List.isPerm_iff_mem_permutations {U : Type} [DecidableEq U] [DecidableEq (List U)] (l : List U) (a : List U) : l.isPerm a ↔ a ∈ List.permutations l := by - apply Iff.intro - simp - rw [@List.perm_comm] - intro hp - apply (@List.isPerm_iff U _ l a).mp hp - simp - intro hp - rw [@List.perm_comm] at hp - apply (@List.isPerm_iff U _ l a).mpr hp - -def UniformShuffler_v2 {U : Type} [BEq U] [DecidableEq (List U)] (f : List U → SLang (List U)) : Prop := - ∀ l : List U, ∀ a : List U, (List.isPerm l a → f l a = (num_perms a)⁻¹) ∧ (¬ List.isPerm l a → f l a = 0) - -lemma UniformShuffler_v2_norms {U : Type} [BEq U] [DecidableEq U] [DecidableEq (List U)] (f : List U → SLang (List U)) (h : UniformShuffler_v2 f) (l : List U): HasSum (f l) 1 := by - simp [Summable.hasSum_iff] - have h1 (b : List U): f l b = (if List.isPerm l b then (num_perms l)⁻¹ else 0) := by - cases h_perm: (l.isPerm b == true) with - | true => - simp at h_perm - simp [UniformShuffler_v2] at h - induction l generalizing b with - | nil => - simp_all [List.isPerm, List.isEmpty] - aesop - | cons hd tl ih => - cases hb : (hd :: tl).isPerm b with - | true => simp - sorry - | false => simp [hb] - apply (h (hd :: tl) b).right hb - | false => - simp at h_perm - simp_all only [Bool.false_eq_true, ↓reduceIte, CharP.cast_eq_zero] - simp [UniformShuffler_v2] at h - apply (h l b).right h_perm - conv => - enter [1, 1, b] - rw [h1] - have h2: ∑' (b : List U), (if l.isPerm b then ((num_perms l)⁻¹) else 0) = (num_perms l) * (num_perms l)⁻¹ := by - sorry - /- Idea: how about we rewrite l.isPerm in terms of membership in a list, - i.e., prove l.isPerm b ↔ b ∈ List.permutations l-/ - rw [h2] - apply ENNReal.mul_inv_cancel - simp [num_perms, List.permutations] - simp [num_perms, List.permutations] diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index c63c603b..24031032 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -153,30 +153,8 @@ theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (nu def RRShuffle_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRShuffle query num den h l, RRShuffle_PMF_helper query num den h l⟩ -/- Unsolved proof that any Uniform Shuffler normalizes. TODO-/ -lemma UniformShuffler_norms {U: Type}[BEq U](f: List U → SLang (List U)) (h:UniformShuffler f): -∀(b: List U),∑' (i : List U), f b i = 1 := by - intro b - have h2 :∀ x i: List U, f x i = (if List.isPerm x i then (1: ENNReal)/(x.length.factorial) else (0: ENNReal)) := by - unfold UniformShuffler at h - exact h - conv => - enter [1,1,i] - rw [h2] - - rw [← @ENNRealLemmas.tsum_ite_mult] - conv => - enter [1] - rw[ENNReal.tsum_mul_left] - sorry - -lemma UniformShuffler_norms' {U : Type} [BEq U] (f : List U → SLang (List U)) (h : UniformShuffler f) (u : List U): - HasSum (f u) 1 := by - simp [Summable.hasSum_iff] - apply UniformShuffler_norms f h - /- Any shuffle algorithm normalizes. -/ -lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T): +lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T): HasSum (ShuffleAlgorithm m f h l) 1 := by unfold ShuffleAlgorithm simp_all only [bind, pure, bind_pure] @@ -187,26 +165,28 @@ HasSum (ShuffleAlgorithm m f h l) 1 := by enter [1,1,b] rw [ENNReal.tsum_mul_left] enter [2] - apply UniformShuffler_norms - exact h + apply h1 simp - - /- Conversion of SLang output to PMF.-/ -def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f)(l: List T) : PMF (List U) := - ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h l⟩ +def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T) : PMF (List U) := + ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h h1 l⟩ -lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f): privPostProcessRand m (fun u => ⟨f u, UniformShuffler_norms' f h_uniform u⟩) = ShuffleAlgorithm_PMF m f h_uniform := by +lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): privPostProcessRand m (fun u => ⟨f u, by + simp [Summable.hasSum_iff ENNReal.summable] + exact h1 u⟩) = ShuffleAlgorithm_PMF m f h_uniform h1:= by funext x simp [privPostProcessRand, ShuffleAlgorithm_PMF, ShuffleAlgorithm] congr /- Shuffle Algorithm is ε-differentially private, given that the local algorithm is ε-differentially private. -/ theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) -(h_uniform: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform) ε := by +(h_uniform: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform h1) ε := by +have h2 (u : List U): HasSum (f u) 1 := by + simp [Summable.hasSum_iff ENNReal.summable] + exact h1 u conv => enter [1] rw [←shuffling_is_postprocessing] -let g : List U → PMF (List U) := (fun u => ⟨f u, UniformShuffler_norms' f h_uniform u⟩) +let g : List U → PMF (List U) := (fun u => ⟨f u, h2 u⟩) apply @randPostProcess_DP_bound_with_UpdateNeighbour T _ _ m ε hdp g From 300c6dde07feb201f518e0540ee6030455c997bc Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Wed, 20 Aug 2025 13:29:13 -0700 Subject: [PATCH 192/216] Cleaning --- .../Pure/ShuffleModel/Properties.lean | 83 ------- .../Pure/ShuffleModel/junk.lean | 219 ------------------ 2 files changed, 302 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 24031032..33b11c6f 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -51,89 +51,6 @@ lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : def Shuffler_PMF {α : Type} [DecidableEq α] [BEq α] (l : List α) : PMF (List α) := ⟨Shuffler l, Shuffler_PMF_helper l⟩ -lemma contains_idx {α: Type}[BEq α](l: List α)(a: α)(h: l.contains a): ∃i: Fin l.length, l[i] = a := by sorry - -lemma insertNth_eq_iff {α : Type} [DecidableEq α] - {l : List α} (a : Nat){x : α} {l' : List α}(h: l = l'.insertNth a x): - l = l'.insertNth a x ↔ l' = l.eraseIdx a ∧ l[a]'(sorry) = x := sorry - -lemma erase_eq_eraseIdx [BEq α]{l : List α} {i : Fin l.length} {x : α} (h : l[i] = x) : l.eraseIdx i = l.erase x := by sorry - -/- Shuffler l outputs 1/n! for any permutation of a list l.-/ -lemma Shuffle_permutes {α: Type} [DecidableEq α][BEq α] (n: Nat)(l₁ l₂: List α)(hlen1: l₁.length = n)(hlen2: l₂.length = n)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n - simp at hlen1 - rw[symm hlen1] - simp - unfold Shuffler - simp - symm at hlen1 - rw[hlen1] at hlen2 - rw[List.length_eq_zero] at hlen2 - exact hlen2 - - case cons hd tl ih => - unfold Shuffler - simp - simp at hlen1 - rw[symm hlen1] - unfold List.isPerm at h - rw[Bool.and_eq_true] at h - cases h - rename_i left right - have h := contains_idx l₂ hd left - cases h - rename_i j ht - conv => - enter[1,1,a,2] - rw[tsum_eq_single (l₂.eraseIdx a) (by - intro l h - simp - intro h1 - rw[insertNth_eq_iff] at h1 - cases h1 - rename_i left2 right2 - simp at h - contradiction - exact h1 - )] - rfl - simp - rw[tsum_eq_single j.val (by - intro a h - simp - intro h1 - sorry - - )] - have h2: l₂ = List.insertNth j hd (l₂.eraseIdx j) := by sorry - rw[if_pos] - - rw[ih (n-1) (l₂.eraseIdx ↑j) (by simp[symm hlen1]) (by rw[symm hlen2, List.length_eraseIdx];simp) (by rw[erase_eq_eraseIdx ht];exact right)] - rw[UniformSample_apply] - rw[hlen1] - rw[inv_eq_one_div] - - simp - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[if_pos nonzero] - rw[← ENNReal.mul_inv] - rw[inv_inj] - sorry - left - simp - linarith - - left - simp - /-rw[Nat.mul_factorial_pred nonzero]-/ - rw[hlen1] - rw[symm hlen2] - have nonzero: n > 0 := by rw[symm hlen1]; linarith - rw[symm hlen2] at nonzero - simp[nonzero] - exact h2 - /- RRShuffle normalizes. -/ theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by unfold RRShuffle diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean deleted file mode 100644 index 6880f93a..00000000 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean +++ /dev/null @@ -1,219 +0,0 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -namespace SLang - -/- Uniform Sample allows to draw a uniform sample n, but returns type Fin n. This allows -us to prove the index is valid in the Shuffler function -/ -def UniformSample' (n : PNat) : SLang (Fin n) := do - let r ← UniformSample n - return (r : Fin n) - -lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by - constructor - intro h - rw [h] - apply Nat.mod_lt - simp - intro h - exact Eq.symm (Nat.mod_eq_of_lt h) - -/- Proves that an output drawn from the Uniform Sample has the same probability as -an output drawn for UniformSample' given the n values are the same. -/ -lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by - unfold UniformSample' - conv => - lhs - simp [pure, bind] - rw [tsum_eq_single x.val] - simp_all only [Fin.cast_val_eq_self, ↓reduceIte, Fin.is_lt, UniformSample_apply, one_div] - intro b' a - simp_all only [ne_eq, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [Fin.val_natCast] - rw [Not] at a - have h : b' < n → False := by - intro h1 - rw [← fin_helper] at h1 - apply a - exact h1 - rw [← Not] at h - rw [Nat.not_lt_eq] at h - simp_all only [imp_false, ge_iff_le, UniformSample_apply_out] - -lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n := by - rw [UniformSample'_eq_UnformSample] - exact UniformSample_apply n x.val (Fin.is_lt x) - -lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by - rw [UniformSample'] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - set f : ℕ → Fin n := fun a => a - set p : SLang ℕ := UniformSample n - have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by - rfl - rw [←push_forward_prob_is_prob p f] - simp [h1] - have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop - conv => - enter [2, 1, z, 1, a] - rw [←h2] - simp [p] - - /- The Shuffler follows the Fischer-Yates method for shuffling lists. -/ -def Shuffler2 {α: Type}(l:List α) := do - let mut a := l.toArray - let mut b : Array α := Array.empty - for h: i in [1:a.size] do - let j ← UniformSample' (Nat.toPNat' i+1) - - a := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h (by simp;)⟩ ⟨j, by - aesop - have h1: j ≤ i := by - rw [@Fin.le_iff_val_le_val] - norm_num - aesop - have h1: j.val < i.toPNat' + 1 := j.2 - aesop - rw[← Nat.lt_add_one_iff] - exact h1 - linarith[h.1] - - have h2: i < l.length := by exact Membership.get_elem_helper h rfl - exact Nat.lt_of_le_of_lt h1 (by aesop) - ⟩ - return a.toList - - - -lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold Shuffler - rename_i inst - simp_all only [bind, PNat.add_coe, PNat.val_ofNat, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, - mul_ite, mul_one, mul_zero] - unfold probBind - simp [pure, bind] - sorry - -lemma one_step {α: Type}[BEq α](hd: α)(tl: List α)(l: List α)(h: List.isPerm (hd::tl) l): Shuffler (hd::tl) l = Shuffler tl (l.erase hd) / (tl.length+1) := by - unfold Shuffler - simp[probBind,pure,pure_apply] - have h: (List.toArray (hd::tl)).size = (List.toArray tl).size+1 := by - simp - rename_i inst h_1 - rw[tsum_eq_single (List.toArray l)] - rw[tsum_eq_single (List.toArray l)] - simp - unfold UniformSample' - simp - aesop - - -lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen: n = l₁.length)(hlen2: n = l₂.length)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n with - | nil => - simp [List.isPerm] at h - have h1: l₂ = [] := by sorry - subst h1 - sorry - | cons hd tl ih => - simp [List.isPerm] at h - have h1: Shuffler tl (l₂.erase hd) = 1 / (tl.length).factorial := by - rw [ih (l₂.erase hd)] - rfl - have h2: tl.length = n - 1 := by simp[hlen] - rw [h2] - have h3: (l₂.erase hd).length = l₂.length - 1 := by sorry - rw [h3] - sorry - exact h.right - rw[one_step] - rw[ih] - - - - /- induction n generalizing l₁ l₂ - case zero => - simp - symm at hlen - rw[List.length_eq_zero] at hlen - symm at hlen2 - rw[List.length_eq_zero] at hlen2 - rw[hlen, hlen2] - simp [Shuffler] - aesop - sorry - case succ x ih => - - cases h - case nil => - simp at hlen - case cons hd tl₁ tl₂ h => - simp at hlen - simp at hlen2 - sorry - -/ - - -/--/ - cases l₁ - case nil => - simp at hlen - case cons hd tl => - cases l₂ - case nil => - simp at hlen2 - case cons hd2 tl2 => - - - - - - - - - - - - - - -lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -HasSum (ShuffleModel query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold ShuffleModel - simp [pure,bind] - unfold RandomizedResponse.RRSample - sorry - - -def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ - - -def ShuffleModel_is_privPostProcess (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -ShuffleModel query num den h l = privPostProcess (RandomizedResponse.RRSample query num den h l) (Shuffler) (l) := by - unfold ShuffleModel - unfold RandomizedResponse.RRSample - simp [privPostProcess] - sorry - -theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : -DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by - unfold ShuffleModel_PMF - unfold ShuffleModel - simp [pure, bind] - unfold probBind - - sorry - -end SLang From 7bed9e55b8837a2db84f7aa08cee0023d67d1c79 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:33:08 -0700 Subject: [PATCH 193/216] Update README.md --- README.md | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 460d465f..0ba16d3a 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,17 @@ Our implementations are in SampCert/DifferentialPrivacy/Pure. Our additions are #### BinomialSample +We implement anod show normalization for a sampler for the Binomial distribution. + #### LocalDP -The files in this folder provide a variety of lemmas to support general ideas about the local model of DP. A key lemma here is `DP_withUpdateNeighbour`, which defines differential privacy with an user provided definition of neighbour. This provides more functionality than the Add-Remove-Update definition of neighbour in SampCert. +The files in this folder provide a variety of definitions to support the local model of differential privacy. We create a definition of differential privacy that is parametrized by a neighbour relation, thus generalizing the SampCert definition. For the local model, we use the 'DP_withUpdateNeighbour' definition of neighboring datasets, which considers two lists two be neighbouring if they differ in the update of a single entry. -Another key idea is `LocalDP_to_dataset`. A key idea in the local model is that if an algorithm on an individual user is $\epsilon$-DP, then the algorithm on the entire dataset is $\epsilon$-DP. This lemma shows that. +We also show that if an $\epsilon$-local randomizer is extended to a dataset via monadic map, the resulting algorithm is $\epsilon$-differentially private. This is proven in `LocalDP_toDataset.` #### MultiBernoulli -The files here provide some helper lemmas related to the Bernoulli distribution. Drawing from the Bernoulli distribution is needed for our implementation of randomized response. +The files here implement and show normalization for the "multi-Bernoulli" distribution, which we define to be the distribution associated with flipping each entry of a list independently and with the same probability. #### RAPPOR @@ -28,29 +30,15 @@ In this folder, we provide an implementation of basic one-time RAPPOR and give a In this folder, we provide an implementation of randomized response and give a proof of differential privacy. The file Definitions.lean provides our implementation of randomized response. The file `Properties/DPProof.lean` provides a proof that our implementation of randomized response is differentially private. -#### ENNRealLemmaSuites.lean - -A significant challenge was proving arithemetic states over ENNReal. Mathlib currently provides less support for arithmetic over ENNReal, so oftentimes we have to prove statements ourselves. These statements are in this file. - -#### LawfulMonadSLang.lean - -Instantiates SLang as a LawfulMonad. - -#### Normalization.lean - -Lemmas relating to proving that applying a monadic map to a normalized function remains normalized. - #### ProbabilityProduct.lean -Shows that the probability of generating a sequence of outputs is equal to the product of the probabilities of generating each output independently. Used in randomized response and RAPPOR. - +Shows that the probability of generating a list of outputs is equal to the product of the probabilities of generating each output independently. This is used to prove our 'LocalDP_toDataset' lemma. #### PushForward.lean - - +We prove that the push-forward of a probability measure is a probability measure. A similar statement already exists in SampCert, but our rephrasing was slightly more convenient for our purposes. #### Reduction.lean -Helper function used in proving a local algorithm is DP. Allows us to reduce the problem from considering the entire dataset to the local randomizer. +Helper function used in proving a local algorithm is DP. Allows us to reduce the problem to just considering the local randomizer. ### Shuffle model @@ -58,17 +46,27 @@ This folder contains the implementation and properties of the shuffle model. #### Definitions.lean -This contains the definition of the shuffle model. The definition `Shuffler` is the randomized permutation. We implement the shuffle model with randomized response in `RRShuffle`, and provide a more general definition in `ShuffleAlgorithm`. +This contains the definition of the shuffle model. The definition `Shuffler` is the randomized permutation. We implement the shuffle algorithm with randomized response in `RRShuffle`, and provide a more general definition in `ShuffleAlgorithm`. #### Properties.lean -This contains a variety of proofs that show that the shuffle model indeed outputs a PMF, and that the random permutation is uniform. The theorem `ShuffleAlgorithm_is_DP` also proves that the shuffle model is differentially private using the post-processing property. +This instantiates the shuffle algorithm as a PMF, and show that the algorithm for a random permutation that we define is uniform. In the theorem `ShuffleAlgorithm_is_DP`, we show that shuffling the output of a differentially-private algorithm does not worsen the differential privacy bound. + +#### ENNRealLemmaSuite.lean + +A significant challenge in our work was proving arithemetic statements over extended non-negative reals. Mathlib currently provides less support for arithmetic in the ENNReal type, so oftentimes we have to prove statements ourselves. These statements are in this file. + +#### LawfulMonadSLang.lean + +We instantiate SLang as a LawfulMonad. + +#### Normalization.lean + +We prove a lemma showing that the mass of a distribution is preserved under monadic map. This is used for both of the local algorithms that we implemented. ### RandomizedPostProcess.lean This file provides the Lean proof about the post-processing property of differential privacy: If there is an $\epsilon$-DP mechanism $M: X \rightarrow W$ and a (possibly randomized) mapping $F: W \rightarrow Z$, then $F \circ M$ is $\epsilon$-DP. The case where $F$ is deterministic is implemented in SampCert. We implement the case where $F$ is random. The result is in the lemma ``lemma randPostProcess_DP_bound_with_UpdateNeighbour``. - - -We would like to thank SampCert for motivating and being the basis for our project +We would like to thank SampCert for motivating and being the basis for our project. We would also like to thank Google for sponsoring and mentoring this project, and the Institute of Pure and Applied Mathematics (IPAM) for supporting and hosting our work. From 127d61985c58f33120a2086fba21c1b7500687c0 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:33:41 -0700 Subject: [PATCH 194/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ba16d3a..06c3eb0c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Verifying Differential Privacy in Lean -The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and several differentially private algorithms, including the Gaussian and Laplace mechanisms. +The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and implemented several differentially private algorithms, including the Gaussian and Laplace mechanisms. We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implementing a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. From 4162c4aa68ef3e23e1347795405739be07cb9cfc Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:33:55 -0700 Subject: [PATCH 195/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06c3eb0c..d2ef73ea 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Our implementations are in SampCert/DifferentialPrivacy/Pure. Our additions are #### BinomialSample -We implement anod show normalization for a sampler for the Binomial distribution. +We implement and show normalization for a sampler for the Binomial distribution. #### LocalDP From f0256f2ecc6f05f3a140a874dcba67c47f4fa11a Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:34:16 -0700 Subject: [PATCH 196/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d2ef73ea..6abfdbe7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ We implement and show normalization for a sampler for the Binomial distribution. #### LocalDP -The files in this folder provide a variety of definitions to support the local model of differential privacy. We create a definition of differential privacy that is parametrized by a neighbour relation, thus generalizing the SampCert definition. For the local model, we use the 'DP_withUpdateNeighbour' definition of neighboring datasets, which considers two lists two be neighbouring if they differ in the update of a single entry. +The files in this folder provide a variety of definitions to support the local model of differential privacy. We create a definition of differential privacy that is parametrized by a neighbour relation, thus generalizing the SampCert definition. For the local model, we use the 'DP_withUpdateNeighbour' definition of neighboring datasets, which considers two lists to be neighbouring if they differ in the update of a single entry. We also show that if an $\epsilon$-local randomizer is extended to a dataset via monadic map, the resulting algorithm is $\epsilon$-differentially private. This is proven in `LocalDP_toDataset.` From fa49bf117a40ce806a796bacdafa40c522e9e62d Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:36:34 -0700 Subject: [PATCH 197/216] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6abfdbe7..7005f90f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et a We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implementing a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. -Our implementations are in SampCert/DifferentialPrivacy/Pure. Our additions are in the Local folder, Shuffle Model folder, and RandomizedPostProcessing.lean file. +Our implementations are in `SampCert/DifferentialPrivacy/Pure`. Our additions are in the Local folder, Shuffle Model folder, and a few separate files in the Pure folder. ### Local @@ -24,21 +24,21 @@ The files here implement and show normalization for the "multi-Bernoulli" distri #### RAPPOR -In this folder, we provide an implementation of basic one-time RAPPOR and give a proof of differential privacy. The file Definitions.lean provides our implementation of RAPPOR. The file `Properties/DPProof.lean` provides a proof that our implementation of RAPPOR is differentially private. +In this folder, we provide an implementation of basic one-time RAPPOR and give a proof of differential privacy. The file `Definitions.lean` provides our implementation of RAPPOR. The file `Properties/DPProof.lean` provides a proof that our implementation of RAPPOR is differentially private. #### RandomizedResponse -In this folder, we provide an implementation of randomized response and give a proof of differential privacy. The file Definitions.lean provides our implementation of randomized response. The file `Properties/DPProof.lean` provides a proof that our implementation of randomized response is differentially private. +In this folder, we provide an implementation of randomized response and give a proof of differential privacy. The file `Definitions.lean` provides our implementation of randomized response. The file `Properties/DPProof.lean` provides a proof that our implementation of randomized response is differentially private. #### ProbabilityProduct.lean -Shows that the probability of generating a list of outputs is equal to the product of the probabilities of generating each output independently. This is used to prove our 'LocalDP_toDataset' lemma. +We show that the probability of generating a list of outputs is equal to the product of the probabilities of generating each output independently. This is used to prove our 'LocalDP_toDataset' lemma. #### PushForward.lean We prove that the push-forward of a probability measure is a probability measure. A similar statement already exists in SampCert, but our rephrasing was slightly more convenient for our purposes. #### Reduction.lean -Helper function used in proving a local algorithm is DP. Allows us to reduce the problem to just considering the local randomizer. +We prove a helper lemma that is used in proving a local algorithm is DP. Allows us to reduce the problem to just considering the local randomizer. ### Shuffle model From 97a864e97b49187522b03bf04c17e6432a4ee190 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:37:22 -0700 Subject: [PATCH 198/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7005f90f..7bdf3ae0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ We implement and show normalization for a sampler for the Binomial distribution. #### LocalDP -The files in this folder provide a variety of definitions to support the local model of differential privacy. We create a definition of differential privacy that is parametrized by a neighbour relation, thus generalizing the SampCert definition. For the local model, we use the 'DP_withUpdateNeighbour' definition of neighboring datasets, which considers two lists to be neighbouring if they differ in the update of a single entry. +The files in this folder provide a variety of definitions to support the local model of differential privacy. We create a definition of differential privacy that is parametrized by a neighbour relation, thus generalizing the SampCert definition. For the local model, we use the `DP_withUpdateNeighbour` definition of neighboring datasets, which considers two lists to be neighbouring if they differ in the update of a single entry. We also show that if an $\epsilon$-local randomizer is extended to a dataset via monadic map, the resulting algorithm is $\epsilon$-differentially private. This is proven in `LocalDP_toDataset.` From f4fa100fda1f54442146d6e55852a44a90f45d0e Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:38:03 -0700 Subject: [PATCH 199/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7bdf3ae0..9102173c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ We show that the probability of generating a list of outputs is equal to the pro We prove that the push-forward of a probability measure is a probability measure. A similar statement already exists in SampCert, but our rephrasing was slightly more convenient for our purposes. #### Reduction.lean -We prove a helper lemma that is used in proving a local algorithm is DP. Allows us to reduce the problem to just considering the local randomizer. +We prove a helper lemma that is used in proving a local algorithm is DP. It allows us to reduce the problem to just considering the local randomizer. ### Shuffle model From 06acc04d733cbb3e6abc23eee94172a1232db61c Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:38:31 -0700 Subject: [PATCH 200/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9102173c..433fe326 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ In this folder, we provide an implementation of randomized response and give a p #### ProbabilityProduct.lean -We show that the probability of generating a list of outputs is equal to the product of the probabilities of generating each output independently. This is used to prove our 'LocalDP_toDataset' lemma. +We show that the probability of generating a list of outputs is equal to the product of the probabilities of generating each output independently. This is used to prove our `LocalDP_toDataset` lemma. #### PushForward.lean We prove that the push-forward of a probability measure is a probability measure. A similar statement already exists in SampCert, but our rephrasing was slightly more convenient for our purposes. From 084487fefe2be75164164fb0a074d388dd34c12c Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 20 Aug 2025 13:39:16 -0700 Subject: [PATCH 201/216] deleteold --- .../Local/Playground/ENNRealCoercions.lean | 76 --- .../Pure/Local/Playground/Junk.lean | 619 ------------------ .../PatternMatchingSumsExamples.lean | 22 - .../Pure/Local/Playground/UsefulLemmas.lean | 10 - .../Pure/Local/Playground/test.lean | 25 - .../RandomizedResponse/AccuracyProof.lean | 69 -- .../RandomizedResponse/AccuracyProof2.lean | 60 -- .../Pure/ShuffleModel/junk.lean | 219 ------- 8 files changed, 1100 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean delete mode 100644 SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean deleted file mode 100644 index 5ad7628f..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/ENNRealCoercions.lean +++ /dev/null @@ -1,76 +0,0 @@ -import SampCert - -lemma real_to_ennreal_eq (a b : Real): a = b -> ENNReal.ofReal a = ENNReal.ofReal b := by - intro h - exact congrArg ENNReal.ofReal h - -#check ENNReal.ofReal_lt_ofReal_iff_of_nonneg -#check ENNReal.ofReal_le_ofReal_iff -#check ENNReal.mul_ne_top -#check ENNReal.add_ne_top - - -lemma div_not_top (a b : ENNReal) (h0: a ≠ ⊤) (h1: b ≠ 0): a/b ≠ ⊤ := by - rw [@ENNReal.div_eq_inv_mul] - apply ENNReal.mul_ne_top - {apply ENNReal.inv_ne_top.mpr h1} - {exact h0} - -lemma arith_0 (a : ENNReal) (h0 : a ≠ ⊤) (h1: a < 1) : 1 - a + a = 1 := by - rw [← ENNReal.toReal_eq_one_iff] - have h2: (1 - a + a).toReal = 1 - a.toReal + a.toReal := by - rw [@tsub_add_eq_max] - rw[max_eq_left_of_lt h1] - rw [ENNReal.one_toReal] - ring - rw[h2] - ring - -lemma arith_1 (num : Nat) (den : PNat) (h : 2 * num < den): - 1 - ((2 : ENNReal) * num + den) / (2 * den) + (2 * num + den) / (2 * den) = 1 := - by - apply arith_0 - apply div_not_top - { - simp - apply ENNReal.mul_ne_top - simp - simp - } - { - simp - exact Nat.not_eq_zero_of_lt h - } - { refine ENNReal.div_lt_of_lt_mul' ?h1.h - aesop - have h1 : ENNReal.ofReal (2 * num) < den := by - refine (ENNReal.ofReal_lt_iff_lt_toReal ?ha ?hb).mpr ?_ - simp - simp - simp - sorry - } - - -lemma arith_3b (num : Nat) (den : PNat) (h : 2 * num < den) : - (2 : ENNReal) * num < den := by - -lemma arith_3c (num : Nat) (den : PNat) (h : 2 * num < den): - (den - (2 : ENNReal) * num) / (2 * den) < 1 := by - simp_all only [NNReal.ofPNat, Nonneg.mk_natCast] - apply arith_3b at h - norm_num at h - sorry - -lemma arith_3 (num: Nat) (den: PNat) (h : 2 * num < den): -1 - ((den : ENNReal) - 2 * num) / (2 * den) + (den - 2 * num) / (2 * den) = 1 := - by - apply arith_0 - apply div_not_top - {simp} - {simp - exact Nat.not_eq_zero_of_lt h - } - { apply arith_3c - exact h - } diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean deleted file mode 100644 index 37b4ac03..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/Junk.lean +++ /dev/null @@ -1,619 +0,0 @@ -import SampCert - -/- -open MultiBernoulli - -/- These is stuff that might be helpful for understanding - how to deal with some specific Lean issues, mainly - to do with manipulating tsums and List.mapM, but isn't - actually used for anything at this point. -/ - -/- WORKING WITH SUBTYPES: -/ -example (b : {b : List Bool // b ≠ []}): b.val.head b.property ∨ ¬ b.val.head b.property := by - cases b.val.head b.property with - | true => left; rfl - | false => right; simp - -def silly: {b : List Bool // b ≠ []} := ⟨[true], by decide⟩ - -/- SIMPLIFICATION LEMMAS FOR IF-THEN-ELSE STATEMENTS: -/ -noncomputable def explicit_prob (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := - match b with - | [] => 0 - | x :: xs => bernoulli_mapper hd x * (mapM bernoulli_mapper tl) xs - -lemma ite_simplifier1 (b : List Bool) (hd : SeedType): -(if assm : b ≠ [] then explicit_prob hd [] b else 0) = -if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * mapM bernoulli_mapper [] b.tail else 0 := by - cases b with - | nil => simp - | cons x xs => simp_all [explicit_prob, -mapM] - -lemma ite_simplifier2 (b : List Bool) (hd : SeedType): -(if h : b = [] then 0 else if b.tail = [] then bernoulli_mapper hd (b.head h) else 0) = -(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) := by - cases b with - | nil => simp - | cons x xs => simp - -lemma ite_simplifier3 (b : List Bool) (hd : SeedType): -(if assm: b ≠ [] then if b = [b.head assm] then bernoulli_mapper hd (b.head assm) else 0 else 0) = -(if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) := by - simp - cases b with - | nil => simp - | cons x xs => simp - cases xs with - | nil => simp - cases x with - | true => simp - | false => simp - | cons => simp - -lemma list_bool_length_1 (b : List Bool): - b.length = 1 ↔ b = [true] ∨ b = [false] := by - apply Iff.intro - · intro a - have h : ∃s : Bool, b = [s] := by - apply List.length_eq_one.mp - exact a - cases h with - | intro s hs => - subst hs - simp_all only [List.length_singleton, List.cons.injEq, and_true, Bool.eq_true_or_eq_false_self] - · intro a - cases a with - | inl h => - subst h - simp_all only [List.length_singleton] - | inr h_1 => - subst h_1 - simp_all only [List.length_singleton] - -lemma sum_simplifier1 (hd : SeedType): - (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) - = if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by - split - next h => - subst h - simp_all only [List.length_singleton, ↓reduceDIte, List.head_cons] - next h => - split - next h_1 => - subst h_1 - simp_all only [List.cons.injEq, Bool.false_eq_true, and_true, not_false_eq_true, List.length_singleton, - ↓reduceDIte, List.head_cons] - next h_1 => - split - next h_2 => apply False.elim - have p: ¬ (b = [false] ∨ b = [true]) := by simp_all only [or_self, not_false_eq_true] - apply p - rw [Or.comm] - apply (list_bool_length_1 b).mp - exact h_2 - next h_2 => simp_all only - - lemma sum_simplifier2 (hd : SeedType): - ∑' (b : List Bool), (if b = [true] then bernoulli_mapper hd true else if b = [false] then bernoulli_mapper hd false else 0) - = ∑' (b : List Bool), if assm: b.length = 1 then bernoulli_mapper hd (b.head (by aesop)) else 0 := by - conv => - enter [1, 1, b] - rw [sum_simplifier1 hd] - -/- EXAMPLE OF WHAT ISN'T WORKING: -/ -lemma explicit_prob_sums_to_1 (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob hd tl b = 1 := by - induction tl with - | nil => rw [ENNReal.tsum_eq_add_tsum_ite []] - rw[explicit_prob] - simp - convert tsum_zero (explicit_prob hd []) - apply symm - conv => - enter [1, 1, b] - rw [ite_simplifier1 b hd] - convert show (1 + 0 : ENNReal) = 1 from add_zero 1 - simp [-mapM] - conv => - enter [1, 1, b] - rw [ite_simplifier2 b hd] - rw [ite_simplifier3 b hd] - rw[sum_simplifier2 hd] - rw [←bernoulli_mapper_sums_to_1 hd] - rw [tsum_bool] - rw [@AddCommMonoidWithOne.add_comm] - sorry - /- FOR ETHAN: At this point I would love to say that - in the else case, the x ≠ [] and so we can just - pattern-match on it...but I don't know how to get Lean - to understand that. -/ - | cons tl_hd tl_tl ih => sorry -/- If we could get the above proof to work, we would be done...-/ - -/- IGNORE -- after getting stuck with the previous def, I tried to make it - explicitly recursive, and got stuck...but hopefully we don't need it. -/ - noncomputable def explicit_prob2 (hd : SeedType) (tl : List SeedType) (b : List Bool) : ENNReal := - match b with - | [] => 0 - | x :: xs => bernoulli_mapper hd x * - match tl with - | [] => if xs = [] then 1 else 0 - | tl_hd :: tl_tl => explicit_prob2 tl_hd tl_tl xs - -lemma explicit_prob_nonempty (hd : SeedType) (tl : List SeedType) (b : List Bool) (h : b ≠ []): - explicit_prob hd tl b = bernoulli_mapper hd (b.head h) * (mapM bernoulli_mapper tl b.tail) := by - rw[explicit_prob] - cases b with - | nil => contradiction - | cons => - rename_i head tail - simp_all only [List.head_cons, List.tail_cons] - -lemma explicit_prob_sum_except_empty (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), explicit_prob hd tl b = - ∑' (b : List Bool), if assm: b ≠ [] then bernoulli_mapper hd (b.head assm) * (mapM bernoulli_mapper tl) b.tail else 0 := by - unfold explicit_prob - simp_all only [ne_eq, dite_eq_ite, ite_not] - sorry - -lemma multi_bernoulli_explicit [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool): - mapM bernoulli_mapper (hd :: tl) b = explicit_prob hd tl b := by - unfold explicit_prob - rw[List.mapM_cons] - simp_all only [bind, pure, SLang.bind_apply, SLang.pure_apply, mul_ite, mul_one, mul_zero] - split - next b => simp_all only [↓reduceIte, tsum_zero, mul_zero] - simp - next b x xs => - simp_all only [List.cons.injEq] - rw[tsum_bool] - cases x with - | false => simp[-mapM] - rw[tsum_eq_single xs] - simp_all - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - | true => simp[-mapM] - rw[tsum_eq_single xs] - simp_all - intro b' a - simp_all only [ne_eq, mapM, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - -lemma multi_bernoulli_explicit_sum [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType): - ∑' (b : List Bool), mapM bernoulli_mapper (hd :: tl) b = ∑' (b : List Bool), explicit_prob hd tl b := by - simp_all [multi_bernoulli_explicit, -mapM] - -lemma explicit_prob_eq_prob2 [LawfulMonad SLang] (hd : SeedType) (tl : List SeedType) (b : List Bool) : - explicit_prob hd tl b = explicit_prob2 hd tl b := by - induction b generalizing hd with - | nil => unfold explicit_prob explicit_prob2 - simp_all only [mapM, bernoulli_mapper, pure, SLang.pure_apply, zero_mul] - | cons n ns ih => simp [explicit_prob, -mapM] - unfold explicit_prob2 - split - next tl => - simp_all only [mul_ite, mul_one, mul_zero] - split - next h => - subst h - rw [List.mapM_nil] - simp [pure] - next h => - simp_all only [mul_eq_zero] - apply Or.inr - rw [List.mapM_nil] - simp [pure] - exact h - next tl tl_hd tl_tl => - simp[explicit_prob, -mapM] at ih - split at ih - next b => simp [explicit_prob2, -mapM] - next b x xs => apply ennreal_mul_eq - unfold explicit_prob2 - rw [List.mapM_cons] - simp [-mapM] - rw[tsum_bool] - cases tl_tl with - | nil => simp only [mul_ite, mul_one, mul_zero] - rename_i inst - split - next h => - subst h - simp_all only [↓reduceIte, _root_.tsum_zero, mul_zero] - cases x with - | true => - simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, - _root_.tsum_zero, mul_zero, true_and, zero_add] - simp [tsum_eq_single, -mapM] - simp_all [tsum_ite_eq] - rw [tsum_eq_single []] - simp - intro b hb - simp - exact id (Ne.symm hb) - | false => - simp_all only [Bool.true_eq_false, false_and, ↓reduceIte, - _root_.tsum_zero, mul_zero, true_and, zero_add] - simp [tsum_eq_single, -mapM] - simp_all [tsum_ite_eq] - rw [tsum_eq_single []] - simp - intro b hb - simp - exact id (Ne.symm hb) - next - h => - simp_all only [add_eq_zero, mul_eq_zero, ENNReal.tsum_eq_zero, - ite_eq_right_iff, and_imp, forall_apply_eq_imp_iff] - apply And.intro - · apply Or.inr - intro hx - rw [List.mapM_nil] - simp [pure] - exact h - · apply Or.inr - intro hx - rw [List.mapM_nil] - simp [pure] - exact h - | cons => - sorry - -open Set - -lemma all_lists_eq_all_tails_bool : (univ : Set (List Bool)) = { l | ∃ x xs, l = (x :: xs).tail } := by - ext l -- extensionality: sets equal iff same elements - constructor - · intro _ - -- show l is in the tail set: pick any Bool x and let xs := l - use true, l - rfl - · rintro ⟨x, xs, h⟩ - -- l = tail of some cons => l is a list => l ∈ univ - subst h - let ll := true::l - have h: ll.tail =l := by rfl - rw [← h] - exact mem_univ _ - -lemma all_list_eq_all_true_tails (b: Bool):(univ : Set (List Bool)) = { l | ∃ xs, l = (b :: xs).tail } := by - ext l - constructor - intro _ - simp_all only [mem_univ, List.tail_cons, exists_eq', setOf_true] - intro _ - simp_all only [List.tail_cons, exists_eq', setOf_true, mem_univ] - -/- /- Version of the prod_of_ind_prob lemma for the PMF instantiation of RRSample. -/ -theorem RRSample_prod_of_ind_prob_PMF(query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den)(a: List Bool)(l: List T)(k: l.length = a.length): -RRSample_PMF query num den h l a = (∏'(i: Fin l.length), RRSingleSample query num den h (l.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob - -/- Proof of DP for Randomized Response. We use the reduction lemma from a different file. -/ -theorem RRSample_is_DP (query: T → Bool)(num: Nat)(den:PNat)(h: 2*num < den) : -DP_withUpdateNeighbour (RRSample_PMF query num den h) (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den))) := by -apply singleton_to_event_update -intros l₁ l₂ h_adj x -cases xlen1 : l₁.length == x.length with -| true => - rw[RRSample_prod_of_ind_prob_PMF query num den h x l₁ (by aesop)] - rw[RRSample_prod_of_ind_prob_PMF query num den h x l₂ - (by rw[←UpdateNeighbour_length h_adj] - simp at xlen1 - exact xlen1)] - cases h_adj with - | Update hl₁ hl₂ => - rename_i a n b m - have hlen: l₁.length = l₂.length := by aesop - have xlen2 : l₂.length = x.length := by aesop - simp - have xlen3 : l₁.length = x.length := by aesop - rw[reduction_final l₁ l₂ a b n m x (RRSingleSample query num den h ) hl₁ hl₂ xlen3 xlen2] - have i1: a.length < x.length := by - rw[←xlen3] - subst hl₁ hl₂ - simp_all only [List.append_assoc, List.singleton_append, List.length_append, - List.length_cons, beq_iff_eq] - rw[←xlen1] - rw [@Nat.lt_add_right_iff_pos] - simp - {calc - RRSingleSample query num den h (l₁[a.length]'(by aesop)) (x[a.length]'(by aesop)) - / RRSingleSample query num den h (l₂[a.length]'(by aesop)) (x[a.length]'(by aesop)) ≤ (den + 2 * num) / (den - 2 * num) := by apply final_bound - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((1/2 + num/den) / (1/2 - num/den)))) := by - apply final_coercion - exact h - _ ≤ ENNReal.ofReal (Real.exp (Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by aesop - } - {intro i - apply RRSingleSample_non_zero query num den h} - {apply RRSingleSample_finite query num den h} -| false => simp at xlen1 - rw [←Ne.eq_def] at xlen1 - have numerator_zero: RRSample_PMF query num den h l₁ x = 0 := by - rw [RRSamplePMF_diff_lengths] - exact xlen1 - rw [numerator_zero] - rw [@ENNReal.zero_div] - simp - -/- A different perspective-/ -/ - -/- /-/- This allows us to use prob_ind_prob in the RAPPOR DP proof -/ -lemma RAPPOR_prob_of_ind_prob_PMF {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v : List T) (a: List (List Bool)) (k : v.length = a.length) : - RAPPORSample_PMF n query num den h v a = (∏'(i: Fin v.length), RAPPORSingleSample n query num den h (v.get i) (a.get (Fin.cast k i ))):= by apply prod_of_ind_prob - -/- Uses the one-hot-encoding lemmas to rewrite a quotient of RRSinglePushForward applications into an if-then-else statement -/ -lemma reduction_helper1 {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) - (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length) (h_users: query u ≠ query v) (i : Fin (one_hot n query u).length): - RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / - RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] = - if query v = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query v] (b[query v]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query v] (b[query v]'(by aesop)) - else if query u = (finCongr (by aesop) i) then RRSinglePushForward num den h (one_hot n query v)[query u] (b[query u]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[query u] (b[query u]'(by aesop)) - else 1 := by - cases hi : (finCongr (by aesop) i) == query v with - | true => simp at hi - have h1: i.val = (query v).val := by - rw [← hi] - simp - aesop - | false => simp at hi - cases hi2: (finCongr (by aesop) i) == query u with - | true => simp at hi2 - have h1: i.val = (query u).val := by - rw [← hi2] - simp - simp [h1, -one_hot] - simp_all only [not_false_eq_true, List.getElem_ofFn, Fin.eta, decide_True, - decide_False, ↓reduceIte] - split - next h_1 => - simp_all only [decide_True] - next h_1 => simp_all only [decide_False] - | false => simp at hi2 - simp_all only [List.getElem_ofFn, finCongr_apply, Fin.getElem_fin, Fin.coe_cast, - Fin.eta] - split - next h_1 => simp_all only [not_true_eq_false] - next h_1 => - split - next h_2 => simp_all only [not_true_eq_false] - next h_2 => - have h1: (one_hot n query v)[i.val]'(by omega) = (one_hot n query u)[i.val]'(by omega) := - by convert one_hot_different_answer_ex_two_contrp' n query v u (finCongr (by aesop) i) - aesop - rw [h1] - rw [ENNReal.div_self] - apply RRSinglePushForward_non_zero - apply RRSinglePushForward_finite - -/- Rewrites the if-then-else statement from above into a simpler form, with most terms cancelled. -/ -lemma reduction_helper2 {T : Type} (n : Nat) (query: T -> Fin n) (f : Bool -> SLang Bool) (v u : T) (b : List Bool) (h_users: query u ≠ query v) - (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): - (∏ i : Fin (one_hot n query u).length, - if query v = (finCongr (by aesop) i) then f (one_hot n query v)[query v] (b[query v]'(by aesop)) / f (one_hot n query u)[query v] (b[query v]'(by aesop)) - else if query u = (finCongr (by aesop) i) then f (one_hot n query v)[query u] (b[query u]'(by aesop)) / f (one_hot n query u)[query u] (b[query u]'(by aesop)) - else 1) = - f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) - * f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) - := by - simp_all only [finCongr_apply, Fin.getElem_fin, Fin.coe_cast, List.getElem_ofFn, Fin.eta] - have _ (g : Fin b.length -> ENNReal) : ∏ i : Fin b.length, g i = ∏ (i ∈ Finset.univ), g i := by aesop - conv => - enter [1] - rw [@Finset.prod_ite] - simp [-one_hot] - rw [@Finset.prod_ite] - simp [-one_hot] - simp_all only [finCongr_apply, implies_true, List.getElem_ofFn, Fin.eta, decide_True] - have hblen : b.length = n := by aesop - have h5 (k : T): Finset.filter (fun x => query k = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)) = {finCongr (by aesop) (query k)} := by aesop - have h6: (Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)))).card = 1 := by - rw [@Finset.card_eq_one] - use (finCongr (by aesop) (query u)) - aesop - have h8: ∏ x ∈ Finset.filter (fun x => query v = Fin.cast (by aesop) x) (Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) - = f (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / f (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) := by - subst hblen - simp_all only [List.getElem_ofFn, Fin.eta, Finset.prod_const] - conv => - enter [1, 2] - simp - simp - have h9: ∏ x ∈ Finset.filter (fun x => query u = Fin.cast (by aesop) x) (Finset.filter (fun x => ¬query v = Fin.cast (by aesop) x) Finset.univ : Finset (Fin (one_hot n query u).length)), - f (one_hot n query v)[(query u).val] (b[(query u).val]'(by - simp_all only [List.getElem_ofFn, Fin.eta, decide_True, decide_False, Fin.is_lt])) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by - simp_all only [one_hot, List.getElem_ofFn, Fin.eta, decide_True, decide_False, Fin.is_lt])) - = f (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / f (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) := by - simp_all only [List.getElem_ofFn, Fin.eta, decide_True, Finset.prod_const] - simp - rw [h8] - rw [h9] - rw [@mul_div] - -/- Cancellation of terms in the DP proof by using the above two lemmas.-/ -lemma single_DP_reduction {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (v u : T) (b : List Bool) (h_users: query u ≠ query v) - (ohu_len : (one_hot n query u).length = b.length) (onhv_len : (one_hot n query v).length = b.length): -∏ i : Fin (one_hot n query u).length, RRSinglePushForward num den h (one_hot n query v)[i.val] b[i.val] / RRSinglePushForward num den h (one_hot n query u)[i.val] b[i.val] - = RRSinglePushForward num den h (one_hot n query v)[(query v).val] (b[(query v).val]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[(query v).val] (b[(query v).val]'(by aesop)) - * RRSinglePushForward num den h (one_hot n query v)[(query u).val] (b[(query u).val]'(by aesop)) / RRSinglePushForward num den h (one_hot n query u)[(query u).val] (b[(query u).val]'(by aesop)) - := by - conv => - enter [1, 2, i] - rw [reduction_helper1 n query num den h v u b ohu_len onhv_len h_users i] - rw [reduction_helper2 _ _ _ _ _ _ h_users] - simp_all only [mul_one] - exact onhv_len --/ - -/- -/- This extends the previous DP lemma to a dataset of arbitrary size -/ -lemma RAPPORSample_is_DP {T : Type} (n : Nat) (query: T -> Fin n) (num : Nat) (den : PNat) (h: 2 * num < den) (b : List Bool): - DP_withUpdateNeighbour (RAPPORSample_PMF n query num den h) (2 * Real.log ((2⁻¹ + num/den) / (2⁻¹ - num/den))) - := by - apply singleton_to_event_update - intros l₁ l₂ h_adj x - cases xlen1 : l₁.length == x.length with - | true => simp at xlen1 - have xlen2: l₂.length = x.length := by - rw [←xlen1] - rw[←UpdateNeighbour_length h_adj] - rw[RAPPOR_prob_of_ind_prob_PMF n query num den h l₁ x xlen1] - rw[RAPPOR_prob_of_ind_prob_PMF n query num den h l₂ x xlen2] - cases h_adj with - | Update hl₁ hl₂ => - rename_i a y c z - simp - cases x_indices: (∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4 _ hl₂; apply xlen2)).length = n) == true with - | true => - simp at x_indices - have valid_index5: a.length < l₁.length := by - rw [hl₁] - rw [@List.length_append] - simp_all only [List.append_assoc, List.singleton_append, List.length_append, List.length_cons, - List.length_singleton] - linarith - have valid_index6: a.length < x.length := by - rw [←xlen1] - exact valid_index5 - have valid_index7: a.length < l₂.length := by - rw [xlen2] - exact valid_index6 - rw [reduction_final_RAP n l₁ l₂ x (fun _ => RAPPORSingleSample n query num den h ) hl₁ hl₂ xlen1 _ xlen2] - { calc - RAPPORSingleSample n query num den h (l₁[a.length]'(valid_index5)) (x[a.length]'(valid_index6)) / - RAPPORSingleSample n query num den h (l₂[a.length]'(valid_index7)) (x[a.length]'(valid_index6)) ≤ - ((2⁻¹ + num / den) / (2⁻¹ - num / den)) ^ 2 := by apply RAPPORSingle_DP n query num den h - _ = ENNReal.ofReal (Real.exp (2 * Real.log ((2⁻¹ + num / den) / (2⁻¹ - num / den)))) := by rw[←arith_2 num den h] - } - { - intro k bo hbo - rw [RAPPORSingleSample] - apply RRSamplePushForward_non_zero - aesop - } - { intro k bo - rw [RAPPORSingleSample] - apply RRSamplePushForward_finite - } - {apply x_indices} - | false => - simp at x_indices - cases x_indices with - | intro i hi => - have numerator_zero: (∏' (i : Fin l₁.length), RAPPORSingleSample n query num den h l₁[i.val] x[i.val]) = 0 := by - rw [@tprod_fintype] - rw[Finset.prod_eq_zero_iff] - norm_num - have hl1len:l₁.length > 0 := by - rw[hl₁] - rw [@List.length_append] - aesop - use (Fin.ofNat' i.val (hl1len)) - apply RAPPORSingleSample_diff_lengths n query num den h - simp - have h_coe: i.val % l₁.length = i.val := by - rw [Nat.mod_eq] - have hival: i.val < l₁.length := by - rw [xlen1] - apply valid_index4 _ hl₂ - exact xlen2 - aesop - conv => - enter[1, 2, 1, 2] - rw[h_coe] - aesop - rw [numerator_zero] - rw [@ENNReal.zero_div] - simp - | false => simp at xlen1 - rw [←Ne.eq_def] at xlen1 - have numerator_zero: RAPPORSample_PMF n query num den h l₁ x = 0 := RAPPORSample_diff_lengths n query num den h l₁ x xlen1 - rw [numerator_zero] - rw [@ENNReal.zero_div] - simp - -/- A different perspective -/ --/ -/ - -/- -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.DPProof - -open RandomizedResponse - -/- Step 2 of the DP Proof over a dataset: cancellation of probabilities in the numerator and denominator. -/ - -lemma fin_prod_cast_RAP {n m : ℕ} (h : n = m)(f : Fin n → ENNReal) : - ∏' i : Fin n, f i = ∏' i : Fin m, f (Fin.cast h.symm i) := by - subst h - simp - -lemma conversion_RAP {β: Type}(l: List T) (x: List β)(h1: l = a++[n]++b)(hl : l.length ≥ 1)(hx: l.length = x.length)(f: T → SLang β): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) := by - rw [fin_prod_cast (by rw [← Nat.sub_add_cancel hl])] - simp - -lemma fin_conv_helper (l₂ : List T) (hl₂: l₂ = a++[m]++b): l₂.length - 1 + 1 = l₂.length := by --this is useful later - rw [Nat.sub_add_cancel] - rw [@Nat.succ_le_iff] - rw [hl₂] - rw [@List.length_append] - aesop -lemma valid_index4 (l₂ : List T) (hl₂ : l₂ = a++[m]++b)(x : List (List Bool)) (i : Fin (l₂.length - 1 + 1)) (xlen2 : l₂.length = x.length): i.val < x.length := by - conv => - enter [2] - rw [←xlen2] - rw [←fin_conv_helper _ hl₂] - exact i.2 - -lemma reduction2_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool))(h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4; apply h2; aesop)).length = n) (hy: l₂.length = x.length) (nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0) (noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin ((l₁.length-1)+1)), f n (l₁[i.val]'(valid_index2 h1 i)) (x[i.val]'(valid_index3 h1 hx i))) / - (∏' (i : Fin ((l₂.length-1)+1)), f n (l₂[i.val]'(valid_index2 h2 i)) (x[i.val]'(valid_index3 h2 hy i))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx]; rw[h1]; simp)) := by - rw[tprod_fintype] - rw[tprod_fintype] - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₁.length-1)+1)) => f n (l₁[b.val]'(valid_index2 h1 b)) (x[b.val]'(valid_index3 h1 hx b))) a.length] - have ind: a.length < x.length := by - rw[← hx] - rw[h1] - simp - rw[Fin.prod_univ_succAbove (fun (b: Fin ((l₂.length-1)+1)) => f n (l₂[b.val]'(valid_index2 h2 b)) (x[b.val]'(valid_index3 h2 hy b))) a.length] - have helper: l₁.length - 1 = l₂.length - 1 := by aesop - have hlp: (∏ i : Fin (l₁.length - 1), f n l₁[(Fin.succAbove a.length i).val] x[↑(Fin.succAbove a.length i).val]) = ∏ i : Fin (l₂.length - 1), f n l₂[(Fin.succAbove a.length i).val] x[(Fin.succAbove a.length i).val] := by - apply Fintype.prod_equiv (Equiv.cast (congr_arg Fin helper)) - simp[succHelp l₁ l₂ h1 h2] - intro i - congr - rw [← propext cast_eq_iff_heq] - rw [← propext cast_eq_iff_heq] - rw[hlp] - rw[ENNReal.mul_div_mul_right] - simp - - simp[mod_helper (a.length) (l₁.length) (by rw[h1];simp;linarith) (by rw[h1]; simp)] - simp[mod_helper (a.length) (l₂.length) (by rw[h2];simp;linarith) (by rw[h2]; simp)] - - rw[Finset.prod_ne_zero_iff] - intro i - aesop - rw[← lt_top_iff_ne_top] - apply ENNReal.prod_lt_top - intro i - simp[noninf] - -theorem reduction_final_RAP (n : Nat) (l₁ l₂: List T)(x: List (List Bool)) (f: Nat -> T → SLang (List Bool)) (h1: l₁ = a++[l]++b)(h2: l₂ = a++[m]++b)(hx: l₁.length = x.length) (hx2 : ∀ i : Fin (l₂.length - 1 + 1), (x[i]'(by apply valid_index4; apply h2; aesop)).length = n) (hy: l₂.length = x.length)(nonzero: ∀(k: T) (bo: (List Bool)), bo.length = n -> f n k bo ≠ 0)(noninf: ∀(k: T) (bo: (List Bool)), f n k bo ≠ ⊤):(∏' (i : Fin (l₁.length)), f n (l₁[i.val]'(by simp)) (x[i.val]'(by rw[← hx]; simp))) / - (∏' (i : Fin (l₂.length)), f n (l₂[i.val]'(by simp)) (x[i.val]'(by rw[← hy];simp))) = f n (l₁[(a.length)]'(by rw[h1]; simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) / f n (l₂[a.length]'(by rw[h2];simp)) (x[a.length]'(by rw[← hx];rw[h1];simp)) := by - have hl2: l₂.length ≥ 1 := by rw[h2];simp; linarith - have hl1: l₁.length ≥ 1 := by rw[h1];simp; linarith - rw[conversion_RAP l₂ x h2 hl2 hy (f n)] - rw[conversion_RAP l₁ x h1 hl1 hx (f n)] - rw [reduction2_RAP n l₁ l₂ x f h1 h2 hx hx2 hy nonzero noninf] - -open Finset -open scoped BigOperators - --/ --/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean deleted file mode 100644 index be77471c..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/PatternMatchingSumsExamples.lean +++ /dev/null @@ -1,22 +0,0 @@ -import SampCert - -noncomputable def f : List Bool -> ENNReal := fun b => - match b with - | [] => 1 - | x :: _ => if x then 0 else 0 - -lemma f_sums_to_1: ∑' (b : List Bool), (if assm: b ≠ [] then f b else 1) = 1 := by - rw [ENNReal.tsum_eq_add_tsum_ite []] - convert show (1 + 0 : ENNReal) = 1 from add_zero 1 - rw [ENNReal.tsum_eq_zero] - intro b - by_cases h : b = [] - · simp [h] - · unfold f; aesop - -lemma silly: (∑' (b : List Bool), if b = [true] then 1 else 0) = 1 := by - rw [@tsum_ite_eq] - - -lemma silly2 (f : Bool -> ENNReal): (∑' (b : List Bool), if b = [true] then f true else if b = [false] then f false else 0) -= f true + f false := by sorry diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean deleted file mode 100644 index 77ac44b7..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/UsefulLemmas.lean +++ /dev/null @@ -1,10 +0,0 @@ -import SampCert - -/- USEFUL LEMMAS: -/ -#check SLang.BernoulliSample_apply -#check List.mapM_cons -#check List.mapM_nil -#check tsum_eq_tsum_diff_singleton -#check tsum_ite_eq -#check tsum_eq_single -#check ENNReal.div_self diff --git a/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean b/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean deleted file mode 100644 index 749783da..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/Playground/test.lean +++ /dev/null @@ -1,25 +0,0 @@ - -import Mathlib.Data.ENNReal.Basic - - - -lemma valid_index2 {l₁ : List T} (h1: l₁ = a++[n]++b) (i : Fin ((l₁.length - 1) + 1)): - i.val < l₁.length := by - have hl1: l₁.length - 1 + 1 = l₁.length := by - rw [Nat.sub_add_cancel] - rw[h1] - simp - linarith - exact Nat.lt_of_lt_of_eq i.2 hl1 - -lemma conversion2 (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hx: l.length = x.length)(f: T → Bool → ENNReal): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(by sorry)) (x[i.val]'(by rw[← hx];sorry))) := by - rw[Nat.sub_add_cancel] - rw[h1] - simp - linarith - -lemma conversion (l: List T) (x: List Bool)(h1: l = a++[n]++b)(hx: l.length = x.length)(f: T → Bool → ENNReal): (∏' (i : Fin (l.length)), f (l[i.val]'(by simp)) (x[i.val]'(by rw[← hx];simp))) = (∏' (i : Fin ((l.length-1)+1)), f (l[i.val]'(valid_index2 h1 i)) (x[i.val]'(by rw[← hx];sorry))) := by - rw[Nat.sub_add_cancel] - rw[h1] - simp - linarith diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean deleted file mode 100644 index 9c65464a..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof.lean +++ /dev/null @@ -1,69 +0,0 @@ -import SampCert -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions - -open SLang -open PMF -open RandomizedResponse - --- UNDER CONSTRUCTION -- -def toSingletonLists {α : Type u} (l : List α) : List (List α) := - l.map (fun x => [x]) - - -def sum_list (Y : List ℕ) := Y.foldl (· + ·) 0 - -noncomputable def coeff {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := - (1.0 / (X.length : ℝ)) * ((den : ℝ) / (2.0 * (num : ℝ))) - -noncomputable def constants {T : Type} (X : List T) (num : Nat) (den : PNat) : ℝ := - (- (X.length) / 2) + (num * X.length) / den - -/- def applying_RR_individually {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : List (SLang Bool) := - X.map (fun x => RRSingleSample query num den h x) -/ - - -def sumBernoulli (xs : List (SLang Bool)) : SLang Nat := - xs.foldlM (fun sum x => do - let b ← x - return sum + if b then 1 else 0 - ) 0 - -def addMulRealToRV (Y : SLang Nat) (R : Real) (S: Real): SLang Real := do - let n ← Y -- Sample a Nat from Y - return S * ((n : Real) + R) -- Convert to Real and add R -/- - -variables {α : Type*} [AddMonoid α] - -instance : AddMonoid (List α) where - nsmul := fun n l => List.join (List.replicate n l) - add := (· ++ ·) - zero := [] - add_assoc := List.append_assoc - zero_add := List.nil_append - add_zero := List.append_nil - - - -noncomputable def pmf.add (X Y : PMF α) : PMF α := - X.bind (λ a => Y.map (λ b => a + b)) - -noncomputable def pmf.sum_list : List (PMF α) → PMF α -| [] => PMF.pure 0 -| (x::xs) => pmf.add x (pmf.sum_list xs) --/ - -def p {T : Type} (query: T -> Bool) (X : List T) (num : Nat) (den : PNat) (h : 2 * num < den) : ℚ := - let bool_lst := X.map query - let true_count := (bool_lst.filter (fun b => b)).length - (true_count) / X.length - - -/- noncomputable def unbiased_estimator {T : Type} (query: T -> Bool) (l : List T) (num : Nat) (den : PNat) (h : 2 * num < den):= - let coef := coeff l num den - let cons := constants l num den - let s := RRSample query num den h l - let sum_of_ys := sumBernoulli s - let p_estimate := addMulRealToRV sum_of_ys cons coef - p_estimate -/ diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean deleted file mode 100644 index b432887a..00000000 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/AccuracyProof2.lean +++ /dev/null @@ -1,60 +0,0 @@ -import SampCert -import Mathlib.Probability.ProbabilityMassFunction.Basic -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.BasicLemmas - -open SLang -open PMF -open RandomizedResponse -open ENNRealLemmas - --- UNDER CONSTRUCTION -- - -def coeff_1 (num : Nat) (den : PNat) : NNRat := den / (2 * num) -def coeff_2 (num : Nat) (den : PNat) : NNRat := den / (4 * num) + 1/2 - -/- Given a list of booleans, p_pushforward returns the proportion of "true" responses --/ -def p_pushforward (l : List Bool) : NNRat := - let true_count := (l.filter (fun b => b)).length - (true_count) / l.length - -/- Given a list of users and a query, p_actual returns the proportion of "yes" responses - Our goal is to estimate p_actual. -/ -def p_actual {T : Type} (query: T -> Bool) (l : List T) : NNRat := - let bool_lst := l.map query - p_pushforward bool_lst - -/- Unbiased estimator-/ -def p_estimate {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : SLang NNRat := do - let randomized ← RRSample query num den h l - let avg_yes := p_pushforward randomized - /- now "return avg_yes" would gives a function that, for each NNRat q, - returns the probability that the proportion of "yes" responses - after doing randomized response on l is equal to q. -/ - return (coeff_1 num den) * avg_yes - (coeff_2 num den) - -lemma RRSample_diff_lengths_simp {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : - RRSample query num den h l a = (if l.length = a.length then RRSample query num den h l a else 0) := by - split - next h_1 => simp - next h_1 => - rw [RRSample_diff_lengths] - simp - exact h_1 - -lemma estimator_unbiased {T : Type} (query : T -> Bool) (num : Nat) (den : PNat) (h : 2 * num < den) (l : List T) : - ∑' (q : NNRat), (p_estimate query num den h l q) * (q : NNReal) = (p_actual query l : NNReal) := by - simp [p_estimate, p_actual] - have h1 (q : NNRat): (∑' (a : List Bool), if q = (coeff_1 num den) * p_pushforward a - (coeff_2 num den) then RRSample query num den h l a else 0) * (q : NNReal) = - (∑' (a : List Bool), if q = (coeff_1 num den) * p_pushforward a - (coeff_2 num den) then RRSample query num den h l a * (q : NNReal) else 0) := by - sorry - conv => - enter [1, 1, q] - rw [h1] - rw [@ENNReal.tsum_comm] - simp_all [ite_not] - - - - sorry diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean deleted file mode 100644 index 6880f93a..00000000 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/junk.lean +++ /dev/null @@ -1,219 +0,0 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -namespace SLang - -/- Uniform Sample allows to draw a uniform sample n, but returns type Fin n. This allows -us to prove the index is valid in the Shuffler function -/ -def UniformSample' (n : PNat) : SLang (Fin n) := do - let r ← UniformSample n - return (r : Fin n) - -lemma fin_helper (x : Nat)(n : PNat) : x = x % n ↔ x < n := by - constructor - intro h - rw [h] - apply Nat.mod_lt - simp - intro h - exact Eq.symm (Nat.mod_eq_of_lt h) - -/- Proves that an output drawn from the Uniform Sample has the same probability as -an output drawn for UniformSample' given the n values are the same. -/ -lemma UniformSample'_eq_UnformSample (n : PNat)(x : Fin n) : UniformSample' n x = UniformSample n x := by - unfold UniformSample' - conv => - lhs - simp [pure, bind] - rw [tsum_eq_single x.val] - simp_all only [Fin.cast_val_eq_self, ↓reduceIte, Fin.is_lt, UniformSample_apply, one_div] - intro b' a - simp_all only [ne_eq, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [Fin.val_natCast] - rw [Not] at a - have h : b' < n → False := by - intro h1 - rw [← fin_helper] at h1 - apply a - exact h1 - rw [← Not] at h - rw [Nat.not_lt_eq] at h - simp_all only [imp_false, ge_iff_le, UniformSample_apply_out] - -lemma UniformSample'_uniform (n : PNat) (x: Fin n) : UniformSample' n x = 1 / n := by - rw [UniformSample'_eq_UnformSample] - exact UniformSample_apply n x.val (Fin.is_lt x) - -lemma UniformSample'_norms (n : PNat) : HasSum (UniformSample' n) 1 := by - rw [UniformSample'] - simp - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - set f : ℕ → Fin n := fun a => a - set p : SLang ℕ := UniformSample n - have h1: push_forward p f = (fun (b : Fin n) => ∑' (a : ℕ), if f a = b then p a else 0) := by - rfl - rw [←push_forward_prob_is_prob p f] - simp [h1] - have h2 (b : Fin n.val) (a : Nat): (if b = f a then p a else 0) = if f a = b then p a else 0 := by aesop - conv => - enter [2, 1, z, 1, a] - rw [←h2] - simp [p] - - /- The Shuffler follows the Fischer-Yates method for shuffling lists. -/ -def Shuffler2 {α: Type}(l:List α) := do - let mut a := l.toArray - let mut b : Array α := Array.empty - for h: i in [1:a.size] do - let j ← UniformSample' (Nat.toPNat' i+1) - - a := a.swap ⟨i, by aesop; exact Membership.get_elem_helper h (by simp;)⟩ ⟨j, by - aesop - have h1: j ≤ i := by - rw [@Fin.le_iff_val_le_val] - norm_num - aesop - have h1: j.val < i.toPNat' + 1 := j.2 - aesop - rw[← Nat.lt_add_one_iff] - exact h1 - linarith[h.1] - - have h2: i < l.length := by exact Membership.get_elem_helper h rfl - exact Nat.lt_of_le_of_lt h1 (by aesop) - ⟩ - return a.toList - - - -lemma Shuffle_norms [LawfulMonad SLang] {α : Type}(l: List α): HasSum (Shuffler l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold Shuffler - rename_i inst - simp_all only [bind, PNat.add_coe, PNat.val_ofNat, pure, pure_bind, Array.toList_eq, bind_apply, pure_apply, - mul_ite, mul_one, mul_zero] - unfold probBind - simp [pure, bind] - sorry - -lemma one_step {α: Type}[BEq α](hd: α)(tl: List α)(l: List α)(h: List.isPerm (hd::tl) l): Shuffler (hd::tl) l = Shuffler tl (l.erase hd) / (tl.length+1) := by - unfold Shuffler - simp[probBind,pure,pure_apply] - have h: (List.toArray (hd::tl)).size = (List.toArray tl).size+1 := by - simp - rename_i inst h_1 - rw[tsum_eq_single (List.toArray l)] - rw[tsum_eq_single (List.toArray l)] - simp - unfold UniformSample' - simp - aesop - - -lemma Shuffle_permutes {α: Type} [BEq α] (l₁ l₂: List α)(hlen: n = l₁.length)(hlen2: n = l₂.length)(h: List.isPerm l₁ l₂): Shuffler l₁ l₂ = 1/Nat.factorial n := by - induction l₁ generalizing l₂ n with - | nil => - simp [List.isPerm] at h - have h1: l₂ = [] := by sorry - subst h1 - sorry - | cons hd tl ih => - simp [List.isPerm] at h - have h1: Shuffler tl (l₂.erase hd) = 1 / (tl.length).factorial := by - rw [ih (l₂.erase hd)] - rfl - have h2: tl.length = n - 1 := by simp[hlen] - rw [h2] - have h3: (l₂.erase hd).length = l₂.length - 1 := by sorry - rw [h3] - sorry - exact h.right - rw[one_step] - rw[ih] - - - - /- induction n generalizing l₁ l₂ - case zero => - simp - symm at hlen - rw[List.length_eq_zero] at hlen - symm at hlen2 - rw[List.length_eq_zero] at hlen2 - rw[hlen, hlen2] - simp [Shuffler] - aesop - sorry - case succ x ih => - - cases h - case nil => - simp at hlen - case cons hd tl₁ tl₂ h => - simp at hlen - simp at hlen2 - sorry - -/ - - -/--/ - cases l₁ - case nil => - simp at hlen - case cons hd tl => - cases l₂ - case nil => - simp at hlen2 - case cons hd2 tl2 => - - - - - - - - - - - - - - -lemma ShuffleModel_PMF_helper (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -HasSum (ShuffleModel query num den h l) 1 := by - rw [Summable.hasSum_iff ENNReal.summable] - unfold ShuffleModel - simp [pure,bind] - unfold RandomizedResponse.RRSample - sorry - - -def ShuffleModel_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨ShuffleModel query num den h l ,ShuffleModel_PMF_helper query num den h l⟩ - - -def ShuffleModel_is_privPostProcess (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) : -ShuffleModel query num den h l = privPostProcess (RandomizedResponse.RRSample query num den h l) (Shuffler) (l) := by - unfold ShuffleModel - unfold RandomizedResponse.RRSample - simp [privPostProcess] - sorry - -theorem Shuffle_is_DP (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) : -DP_withUpdateNeighbour (ShuffleModel_PMF query num den h) (Real.log ((2⁻¹ + ↑num / ↑↑↑den) / (2⁻¹ - ↑num / ↑↑↑den))) := by - unfold ShuffleModel_PMF - unfold ShuffleModel - simp [pure, bind] - unfold probBind - - sorry - -end SLang From 45c48e00e4f2503e54116ea563793f534fa928ee Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:39:36 -0700 Subject: [PATCH 202/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 433fe326..4e367f86 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This contains the definition of the shuffle model. The definition `Shuffler` is #### Properties.lean -This instantiates the shuffle algorithm as a PMF, and show that the algorithm for a random permutation that we define is uniform. In the theorem `ShuffleAlgorithm_is_DP`, we show that shuffling the output of a differentially-private algorithm does not worsen the differential privacy bound. +This instantiates the shuffle algorithm as a PMF, and show that our algorithm for a random permutation that is indeed uniformly random. In the theorem `ShuffleAlgorithm_is_DP`, we show that shuffling the output of a differentially-private algorithm does not worsen the differential privacy bound. #### ENNRealLemmaSuite.lean From c6dde034ac5091e637ff8348485c78893a0297c1 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:40:01 -0700 Subject: [PATCH 203/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e367f86..ea41bb4e 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,6 @@ We prove a lemma showing that the mass of a distribution is preserved under mona ### RandomizedPostProcess.lean This file provides the Lean proof about the post-processing property of differential privacy: If there is an $\epsilon$-DP mechanism $M: X \rightarrow W$ and a (possibly randomized) mapping $F: W \rightarrow Z$, then $F \circ M$ is $\epsilon$-DP. The case where $F$ is deterministic is implemented in SampCert. We implement the case where $F$ is random. The result is in the lemma -``lemma randPostProcess_DP_bound_with_UpdateNeighbour``. +``randPostProcess_DP_bound_with_UpdateNeighbour``. We would like to thank SampCert for motivating and being the basis for our project. We would also like to thank Google for sponsoring and mentoring this project, and the Institute of Pure and Applied Mathematics (IPAM) for supporting and hosting our work. From 12d102de57d733a70af9202d6e5d1f8a18f0be0f Mon Sep 17 00:00:00 2001 From: perrynchang <162051316+perrynchang@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:41:56 -0700 Subject: [PATCH 204/216] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea41bb4e..d8d89c28 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and implemented several differentially private algorithms, including the Gaussian and Laplace mechanisms. -We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as implementing a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. +We build upon SampCert, creating support for the local model using [Lean](https://lean-lang.org/) and the extensive [Mathlib](https://github.com/leanprover-community/mathlib4) library. We also implement the [randomized response](https://www.tandfonline.com/doi/abs/10.1080/01621459.1965.10480775) and [one-time basic RAPPOR](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/42852.pdf) mechanisms, as well as a more robust post-processing property for randomized mappings. We additionally move towards an implementation of the shuffle model. Our implementations are in `SampCert/DifferentialPrivacy/Pure`. Our additions are in the Local folder, Shuffle Model folder, and a few separate files in the Pure folder. From 423864fc70b1d892e592f5e3d2564c0a42a9e3ae Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 20 Aug 2025 13:51:22 -0700 Subject: [PATCH 205/216] addedbasic --- .../Pure/RandomizedPostProcessing.lean | 2 -- .../Pure/ShuffleModel/Basic.lean | 12 ++++++++++ .../Pure/ShuffleModel/Definitions.lean | 22 +++++++----------- .../Pure/ShuffleModel/Properties.lean | 23 ++++++++----------- 4 files changed, 29 insertions(+), 30 deletions(-) create mode 100644 SampCert/DifferentialPrivacy/Pure/ShuffleModel/Basic.lean diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index 2faa9994..a4f32eea 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -83,8 +83,6 @@ lemma DP.pointwise_ratio_bound {T U : Type} · aesop simpa using this - - lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): (∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by calc diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Basic.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Basic.lean new file mode 100644 index 00000000..f00cedbc --- /dev/null +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Basic.lean @@ -0,0 +1,12 @@ +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions +import SampCert.Samplers.Uniform.Code +import SampCert.Samplers.Uniform.Properties +import SampCert.DifferentialPrivacy.Pure.Local.Normalization +import SampCert.DifferentialPrivacy.Pure.Local.PushForward +import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code +import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.Postprocessing +import SampCert.DifferentialPrivacy.Generic +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof +import SampCert.DifferentialPrivacy.Pure.RandomizedPostProcessing diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index c5721ce8..34fef386 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -1,17 +1,9 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties -import SampCert.DifferentialPrivacy.Pure.Postprocessing -import SampCert.DifferentialPrivacy.Generic +import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Basic namespace SLang --- Implementation of the Shuffler for the Shuffle Model. +/- Implementation of the Shuffler for the Shuffle Model. + Outputs a random permutation of the input list. -/ def Shuffler {α: Type}(l:List α) := do match l with | [] => pure [] @@ -27,12 +19,14 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L let b ← Shuffler l return b +def num_perms {α: Type} (l: List α) [DecidableEq α]: ℕ := (l.permutations.toFinset).card + /- Definition of a function that uniformly permutes a given list.-/ -def UniformShuffler {U: Type}[BEq U](f: List U → SLang (List U)) : Prop := - ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(l₁.length.factorial) else (0: ENNReal) +def UniformShuffler {U: Type}[BEq U] [DecidableEq U] (f: List U → SLang (List U)) : Prop := + ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(num_perms l₁) else (0: ENNReal) /- Generalized version of the shuffle algorithm that takes in any mechanism -/ -def ShuffleAlgorithm [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do +def ShuffleAlgorithm [BEq U][DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← (m l).toSLang let b ← f x return b diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 33b11c6f..a32020d4 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -1,14 +1,5 @@ -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions -import SampCert.Samplers.Uniform.Code -import SampCert.Samplers.Uniform.Properties -import SampCert.DifferentialPrivacy.Pure.Local.Normalization -import SampCert.DifferentialPrivacy.Pure.Local.PushForward -import SampCert.DifferentialPrivacy.Pure.Local.LocalDP.DPwithUpdateNeighbour -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Code -import SampCert.DifferentialPrivacy.Pure.Local.MultiBernoulli.Properties +import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Basic import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof -import SampCert.DifferentialPrivacy.Pure.RandomizedPostProcessing namespace SLang @@ -44,10 +35,12 @@ lemma Shuffler_PMF_helper {α: Type} [DecidableEq α][BEq α] (l:List α): HasSu rw [ih] simp +/- A restatement of the above lemma. -/ lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : List α), Shuffler l b = 1 := by rw [← Summable.hasSum_iff ENNReal.summable] apply Shuffler_PMF_helper +/-Instantiation of Shuffler as a PMF. -/ def Shuffler_PMF {α : Type} [DecidableEq α] [BEq α] (l : List α) : PMF (List α) := ⟨Shuffler l, Shuffler_PMF_helper l⟩ @@ -67,11 +60,12 @@ theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (nu rw [← Summable.hasSum_iff ENNReal.summable] apply RRSample_PMF_helper +/- Instantiation of RRShuffle as a PMF. -/ def RRShuffle_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := ⟨RRShuffle query num den h l, RRShuffle_PMF_helper query num den h l⟩ /- Any shuffle algorithm normalizes. -/ -lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T): +lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] [DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T): HasSum (ShuffleAlgorithm m f h l) 1 := by unfold ShuffleAlgorithm simp_all only [bind, pure, bind_pure] @@ -86,10 +80,11 @@ HasSum (ShuffleAlgorithm m f h l) 1 := by simp /- Conversion of SLang output to PMF.-/ -def ShuffleAlgorithm_PMF {U: Type}[BEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T) : PMF (List U) := +def ShuffleAlgorithm_PMF {U: Type}[BEq U] [DecidableEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T) : PMF (List U) := ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h h1 l⟩ -lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): privPostProcessRand m (fun u => ⟨f u, by +/- Shows that shuffling is a postprocessing function. -/ +lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] [DecidableEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): privPostProcessRand m (fun u => ⟨f u, by simp [Summable.hasSum_iff ENNReal.summable] exact h1 u⟩) = ShuffleAlgorithm_PMF m f h_uniform h1:= by funext x @@ -97,7 +92,7 @@ lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] (m : Mechanism T ( congr /- Shuffle Algorithm is ε-differentially private, given that the local algorithm is ε-differentially private. -/ -theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U](m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) +theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U] [DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) (h_uniform: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform h1) ε := by have h2 (u : List U): HasSum (f u) 1 := by simp [Summable.hasSum_iff ENNReal.summable] From 6e8ac8d7d9eeca8ced7a1d83bd9ff838932bf856 Mon Sep 17 00:00:00 2001 From: Arasyilmaz1 Date: Wed, 20 Aug 2025 13:51:51 -0700 Subject: [PATCH 206/216] cleaning --- .../presentation-code/Examples.lean | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 SampCert/DifferentialPrivacy/presentation-code/Examples.lean diff --git a/SampCert/DifferentialPrivacy/presentation-code/Examples.lean b/SampCert/DifferentialPrivacy/presentation-code/Examples.lean deleted file mode 100644 index 3c72a25b..00000000 --- a/SampCert/DifferentialPrivacy/presentation-code/Examples.lean +++ /dev/null @@ -1,35 +0,0 @@ -import SampCert - -/- A tactic is a proof strategy that can be applied to simplify a proof goal. - In practice, proving a statement involves using a series to tactic to manipulate the - proof state and reducing the goal to previously established theorems or hypotheses. -/ - -/- The simplest tactic is `rfl`, which handles definitional equality -/ -lemma two_plus_two : 2 + 2 = 4 := rfl - -/- Once we have the above lemma, we can use it to prove other lemmas: -/ -lemma two_plus_two' : 4 = 2 + 2 := (Eq.symm two_plus_two) - -lemma real_add_pres_eq (a b c : ℝ): a = b → a + c = b + c := by - intro h - rw [h] - /- We could also do `rw[h]` instead of `rewrite`, which automatically uses `rfl` to close the goal -/ - -/- If our goal is `B`, and we know that `A → B` then the `apply` tactic says that it's enough to prove `A` -/ -lemma silly_arithmetic (c : ℝ): (2 *2 - 1) + c = 3 + c := by - apply real_add_pres_eq - ring - -/- Propositions are types in Lean! -/ - -def List.add_1_to_hd (l : List ℕ) (h : l ≠ []) := List.head l h + 1 - -/- First, notice that we are using a dependent type: List ℕ -/ - -/- What is the type of the function List.head_add_1? Is it List ℕ → List ℕ? -/ - -#check List.add_1_to_hd - -/- `l ≠ []` is a type, and an inhabitant of this type is a proof that `l` is not empty! -/ - -theorem Fermat : ∀ (a b c : ℕ) (n : ℕ), n > 2 → a^n + b^n = c^n → False := sorry From b008c093a544f49d94d9bacb8a9010d7df3ab3f0 Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 20 Aug 2025 14:06:38 -0700 Subject: [PATCH 207/216] Update RandomizedPostProcessing.lean --- .../Pure/RandomizedPostProcessing.lean | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index a4f32eea..d77d54de 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -11,11 +11,13 @@ noncomputable section open Classical Set namespace SLang +/-This file establishes the post-processing property for randomized post-processing functions-/ - +/-Composes a mechanism with a randomized post-processing function-/ def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : Mechanism T V := fun l => (nq l).bind g +/-The following 3 lemmas are helper functions for the main theorems at the end-/ lemma tsum_ite_eq_single {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U): (∑' (x : U), if x = u then ((m l₁) x) else 0) = (m l₁) u := by classical @@ -31,6 +33,43 @@ lemma tsum_ite_eq_single {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U) simp [hpoint, hcollapse] + +lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): +(∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by + calc + (∑' v : V, if v ∈ S then p u * g u v else 0) + = ∑' v : V, p u * (if v ∈ S then g u v else 0) := by + simp [hsplit] + _ = p u * ∑' v : V, (if v ∈ S then g u v else 0) := by + simpa using + (ENNReal.tsum_mul_left + (a := p u) + (f := fun v : V => if v ∈ S then g u v else 0)) + +lemma tsum_bind_indicator {U V : Type} + (p : PMF U) (g : U → PMF V) (S : Set V) : + (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by + classical + have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by + intro v; simp [PMF.bind_apply] + calc + (∑' v : V, if v ∈ S then (p.bind g) v else 0) + = ∑' v, if v ∈ S then (∑' u, p u * g u v) else 0 := by + simp [hbind] + _ = ∑' v, ∑' u, (if v ∈ S then p u * g u v else 0) := by + refine tsum_congr ?_ + intro v; by_cases hv : v ∈ S <;> simp [hv] + _ = ∑' u, ∑' v, (if v ∈ S then p u * g u v else 0) := by + simpa using + ENNReal.tsum_comm (f := fun v u => (if v ∈ S then p u * g u v else 0)) + _ = ∑' u, p u * (∑' v, if v ∈ S then g u v else 0) := by + refine tsum_congr ?_ + intro u + have hsplit :(fun v => if v ∈ S then p u * g u v else 0) = (fun v => p u * (if v ∈ S then g u v else 0)) := by + funext v; by_cases hv : v ∈ S <;> simp [hv, mul_comm, mul_left_comm, mul_assoc] + exact tsum_indicator_mul_left p g S u hsplit + +/-Provides a pointwise guarantee of a DP bound-/ lemma DP.pointwise_ratio_bound {T U : Type} {m : Mechanism T U} {ε : ℝ} (h : DP m ε) {l₁ l₂ : List T} (hN : Neighbour l₁ l₂) : @@ -83,6 +122,8 @@ lemma DP.pointwise_ratio_bound {T U : Type} · aesop simpa using this + + lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): (∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by calc @@ -203,7 +244,43 @@ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} · aesop simpa using this -lemma randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechanism T U} {ε : Real} (h : DP_withUpdateNeighbour nq ε) (g : U → PMF V) : +/-Proves that combing DP algorithm with post-processor does not worsen the DP bound-/ +theorem randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq ε) (g : U → PMF V) : + DP (privPostProcessRand nq g) ε := by + intro l₁ l₂ hN S + let p₁ := nq l₁ + let p₂ := nq l₂ + let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) + have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) + = ∑' u : U, p₁ u * w u := by + simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S + have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) + = ∑' u : U, p₂ u * w u := by + simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S + have hpt := DP.pointwise_ratio_bound (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN + have hsum : + (∑' u : U, p₁ u * w u) + ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by + have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by + intro u + have := hpt u + have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u + have hw0 : 0 ≤ w u := by aesop + have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 + simpa [mul_left_comm, mul_comm, mul_assoc] using hmul + have := ENNReal.tsum_le_tsum hpt' + simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this + by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 + · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by + have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum + exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + simp [hNum, hDen, hNum0, hDen0] + · nth_rewrite 1 [mul_comm] at hsum + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) + simpa [hNum, hDen] using this + +/-Same thing as randPostProcess_DP_bound, but uses our DP_withUpdateNeighbour-/ +theorem randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mechanism T U} {ε : Real} (h : DP_withUpdateNeighbour nq ε) (g : U → PMF V) : DP_withUpdateNeighbour (privPostProcessRand nq g) ε := by intro l₁ l₂ hN S let p₁ := nq l₁ From d854bfe4746bf962e682b0a4111cda9d693d6a12 Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 20 Aug 2025 14:10:04 -0700 Subject: [PATCH 208/216] Update RandomizedPostProcessing.lean --- .../Pure/RandomizedPostProcessing.lean | 73 +------------------ 1 file changed, 1 insertion(+), 72 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index d77d54de..85af63dc 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -122,78 +122,7 @@ lemma DP.pointwise_ratio_bound {T U : Type} · aesop simpa using this - - -lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): -(∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by - calc - (∑' v : V, if v ∈ S then p u * g u v else 0) - = ∑' v : V, p u * (if v ∈ S then g u v else 0) := by - simp [hsplit] - _ = p u * ∑' v : V, (if v ∈ S then g u v else 0) := by - simpa using - (ENNReal.tsum_mul_left - (a := p u) - (f := fun v : V => if v ∈ S then g u v else 0)) - -lemma tsum_bind_indicator {U V : Type} - (p : PMF U) (g : U → PMF V) (S : Set V) : - (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by - classical - have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by - intro v; simp [PMF.bind_apply] - calc - (∑' v : V, if v ∈ S then (p.bind g) v else 0) - = ∑' v, if v ∈ S then (∑' u, p u * g u v) else 0 := by - simp [hbind] - _ = ∑' v, ∑' u, (if v ∈ S then p u * g u v else 0) := by - refine tsum_congr ?_ - intro v; by_cases hv : v ∈ S <;> simp [hv] - _ = ∑' u, ∑' v, (if v ∈ S then p u * g u v else 0) := by - simpa using - ENNReal.tsum_comm (f := fun v u => (if v ∈ S then p u * g u v else 0)) - _ = ∑' u, p u * (∑' v, if v ∈ S then g u v else 0) := by - refine tsum_congr ?_ - intro u - have hsplit :(fun v => if v ∈ S then p u * g u v else 0) = (fun v => p u * (if v ∈ S then g u v else 0)) := by - funext v; by_cases hv : v ∈ S <;> simp [hv, mul_comm, mul_left_comm, mul_assoc] - exact tsum_indicator_mul_left p g S u hsplit - -lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq ε) (g : U → PMF V) : - DP (privPostProcessRand nq g) ε := by - intro l₁ l₂ hN S - let p₁ := nq l₁ - let p₂ := nq l₂ - let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) - have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) - = ∑' u : U, p₁ u * w u := by - simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S - have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) - = ∑' u : U, p₂ u * w u := by - simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S - have hpt := DP.pointwise_ratio_bound (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN - have hsum : - (∑' u : U, p₁ u * w u) - ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by - have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by - intro u - have := hpt u - have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u - have hw0 : 0 ≤ w u := by aesop - have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 - simpa [mul_left_comm, mul_comm, mul_assoc] using hmul - have := ENNReal.tsum_le_tsum hpt' - simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this - by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 - · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by - have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum - exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) - simp [hNum, hDen, hNum0, hDen0] - · nth_rewrite 1 [mul_comm] at hsum - have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) - simpa [hNum, hDen] using this - - +/-Provides a pointwise guarantee of a DP bound but for our DP_withUpdateNeighbour-/ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} {m : Mechanism T U} {ε : Real} (h : DP_withUpdateNeighbour m ε) {l₁ l₂ : List T} (hN : UpdateNeighbour l₁ l₂) : From 66496be7bb699615312bc8fd4036c92cd38c0d8c Mon Sep 17 00:00:00 2001 From: PCChess Date: Wed, 20 Aug 2025 14:11:12 -0700 Subject: [PATCH 209/216] Update Postprocessing.lean --- .../Pure/Postprocessing.lean | 172 ------------------ 1 file changed, 172 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean index 511a92ff..a5e732ae 100644 --- a/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/Postprocessing.lean @@ -44,178 +44,6 @@ lemma privPostProcess_DP_bound {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq exact Real.exp_pos ε · simp -/- -def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : Mechanism T V := - fun l => (nq l).bind g - -lemma div_le_iff_mul_le {a b c : ENNReal} (hb : b ≠ 0) (h2 : ⊤ ≠ b) : - a ≤ c * b ↔ a / b ≤ c := by - constructor - · intro h - have bruh : a * b⁻¹ ≤ c * b * b⁻¹ := by - have := mul_le_mul_right' h (b⁻¹) - simpa [mul_assoc] using this - rw [← div_eq_mul_inv, mul_assoc, ENNReal.mul_inv_cancel, mul_one] at bruh - · exact bruh - · aesop - · aesop - · intro h - have hmul : (a / b) * b ≤ c * b := by - simpa [mul_assoc] using mul_le_mul_right' h b - rw [ENNReal.div_mul_cancel] at hmul - · aesop - · aesop - · aesop - -lemma frog {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U): - (∑' (x : U), if x = u then ((m l₁) x) else 0) = (m l₁) u := by - classical - have hpoint : - (fun x : U => if x = u then (m l₁) x else 0) = - (fun x : U => if x = u then (m l₁) u else 0) := by - funext x - by_cases hx : x = u - · simp [hx] - · simp [hx] - have hcollapse : (∑' x : U, if x = u then (m l₁) u else 0) = (m l₁) u := by - simp [tsum_ite_eq (β := U) (α := ENNReal) u ((m l₁) u)] - simp [hpoint, hcollapse] - - -lemma DP.pointwise_ratio_bound {T U : Type} - {m : Mechanism T U} {ε : ℝ} - (h : DP m ε) {l₁ l₂ : List T} (hN : Neighbour l₁ l₂) : - ∀ u : U, m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by - intro u - have hS := h l₁ l₂ hN ({u} : Set U) - have hnum : - (∑' x : U, (if x ∈ ({u} : Set U) then m l₁ x else 0)) = m l₁ u := by - classical - have : (fun x : U => if x ∈ ({u} : Set U) then m l₁ x else 0) - = (fun x : U => if x = u then m l₁ u else 0) := by - funext x - by_cases hx : x = u - · subst hx; simp - · simp [Set.mem_singleton_iff, hx] - simpa [this] using (frog m l₁ u) - have hden : - (∑' x : U, (if x ∈ ({u} : Set U) then m l₂ x else 0)) = m l₂ u := by - classical - have : (fun x : U => if x ∈ ({u} : Set U) then m l₂ x else 0) - = (fun x : U => if x = u then m l₂ u else 0) := by - funext x - by_cases hx : x = u - · subst hx; simp - · simp [Set.mem_singleton_iff, hx] - simpa [this] using (frog m l₂ u) - have hratio : m l₁ u / m l₂ u ≤ ENNReal.ofReal (Real.exp ε) := by - rw [hnum, hden] at hS - exact hS - by_cases hz : m l₂ u = 0 - · have hfin : ENNReal.ofReal (Real.exp ε) ≠ ⊤ := by aesop - have hzero : m l₁ u = 0 := by - by_contra hpos - have htop : m l₁ u / m l₂ u = ⊤ := by - exact ENNReal.div_eq_top.mpr (Or.inl ⟨by simp [hpos], hz⟩) - have : (⊤ : ENNReal) ≤ ENNReal.ofReal (Real.exp ε) := by - simp at hratio - aesop - have : ENNReal.ofReal (Real.exp ε) = ⊤ := top_le_iff.mp this - exact hfin this - simp [hz, hzero, zero_mul] - · have h_not_infty : ⊤ ≠ m l₂ u := by - have hle : m l₂ u ≤ 1 := by simpa using (m l₂).coe_le_one u - have hlt : m l₂ u < ⊤ := lt_of_le_of_lt hle (by simp) - aesop - have : m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by - rw [div_le_iff_mul_le hz h_not_infty] - exact hratio - simpa using this - - - -lemma bruh1 {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): -(∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by - calc - (∑' v : V, if v ∈ S then p u * g u v else 0) - = ∑' v : V, p u * (if v ∈ S then g u v else 0) := by - simp [hsplit] - _ = p u * ∑' v : V, (if v ∈ S then g u v else 0) := by - simpa using - (ENNReal.tsum_mul_left - (a := p u) - (f := fun v : V => if v ∈ S then g u v else 0)) - -lemma tsum_bind_indicator {U V : Type} - (p : PMF U) (g : U → PMF V) (S : Set V) : - (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by - classical - have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by - intro v; simp [PMF.bind_apply] - calc - (∑' v : V, if v ∈ S then (p.bind g) v else 0) - = ∑' v, if v ∈ S then (∑' u, p u * g u v) else 0 := by - simp [hbind] - _ = ∑' v, ∑' u, (if v ∈ S then p u * g u v else 0) := by - refine tsum_congr ?_ - intro v; by_cases hv : v ∈ S <;> simp [hv] - _ = ∑' u, ∑' v, (if v ∈ S then p u * g u v else 0) := by - simpa using - ENNReal.tsum_comm (f := fun v u => (if v ∈ S then p u * g u v else 0)) - _ = ∑' u, p u * (∑' v, if v ∈ S then g u v else 0) := by - refine tsum_congr ?_ - intro u - have hsplit :(fun v => if v ∈ S then p u * g u v else 0) = (fun v => p u * (if v ∈ S then g u v else 0)) := by - funext v; by_cases hv : v ∈ S <;> simp [hv, mul_comm, mul_left_comm, mul_assoc] - exact bruh1 p g S u hsplit - - - -lemma bruh {a b c : ENNReal} (h1: a ≤ c * b) (h2: 0 < b) : a / b ≤ c := by - by_cases hbtop : b = ⊤ - · aesop - · have hb0 : b ≠ 0 := ne_of_gt h2 - have : a * b⁻¹ ≤ (c * b) * b⁻¹ := (mul_le_mul_right' h1 b⁻¹) - rw [mul_assoc, ENNReal.mul_inv_cancel, mul_one] at this - aesop - · aesop - · aesop - - -lemma randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNReal} (h : PureDP nq ε) (g : U → PMF V) : - DP (privPostProcessRand nq g) ε := by - intro l₁ l₂ hN S - let p₁ := nq l₁ - let p₂ := nq l₂ - let w : U → ENNReal := fun u => (∑' v : V, if v ∈ S then g u v else 0) - have hNum : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₁) v else 0) - = ∑' u : U, p₁ u * w u := by - simpa [privPostProcessRand, p₁] using tsum_bind_indicator (nq l₁) g S - have hDen : (∑' v : V, if v ∈ S then (privPostProcessRand nq g l₂) v else 0) - = ∑' u : U, p₂ u * w u := by - simpa [privPostProcessRand, p₂] using tsum_bind_indicator (nq l₂) g S - have hpt := DP.pointwise_ratio_bound (T:=T) (U:=U) (m:=nq) (ε:=ε) h hN - have hsum : - (∑' u : U, p₁ u * w u) - ≤ ENNReal.ofReal (Real.exp ε) * (∑' u : U, p₂ u * w u) := by - have hpt' : ∀ u, p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := by - intro u - have := hpt u - have hpt' : p₁ u ≤ ENNReal.ofReal (Real.exp ε) * p₂ u := by simpa [p₁, p₂] using hpt u - have hw0 : 0 ≤ w u := by aesop - have hmul : p₁ u * w u ≤ (ENNReal.ofReal (Real.exp ε) * p₂ u) * w u := mul_le_mul_of_nonneg_right hpt' hw0 - simpa [mul_left_comm, mul_comm, mul_assoc] using hmul - have := ENNReal.tsum_le_tsum hpt' - simpa [ENNReal.tsum_mul_left, mul_left_comm, mul_assoc] using this - by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 - · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by - have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum - exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) - simp [hNum, hDen, hNum0, hDen0] - · have hpos : (0 : ENNReal) < (∑' u : U, p₂ u * w u) := lt_of_le_of_ne' (by exact bot_le) hDen0 - have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact bruh hsum hpos) - simpa [hNum, hDen] using this --/ /-- ``privPostProcess`` satisfies pure DP, for any surjective postprocessing function. From c9de83d0badf307211298a990ea5bc9700b0d646 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Wed, 20 Aug 2025 14:22:05 -0700 Subject: [PATCH 210/216] urandom --- ffi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi.cpp b/ffi.cpp index ac08dfdc..8b3775b3 100644 --- a/ffi.cpp +++ b/ffi.cpp @@ -47,7 +47,7 @@ extern "C" lean_object * prob_While(lean_object * condition, lean_object * body, extern "C" lean_object * my_run(lean_object * a) { if (urandom == -1) { - urandom = open("/dev/urandom", O_RDONLY); + urandom = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (urandom == -1) { lean_internal_panic("prob_UniformByte: /dev/urandom cannot be opened"); } From 6c7fe5d2eca8246d36a6a2b93d13b65ada8ebc7f Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Sat, 23 Aug 2025 15:15:19 -0700 Subject: [PATCH 211/216] Update README with personal project information Added personal project details and focus areas after RIPS 2025. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d8d89c28..9ab6918e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +This contains some personal work done by me after the RIPS 2025 project on Lean and Differential Privacy. I will mostly focus on cleaning up the shuffle model proofs and adding more support for the shuffle model. The rest is the README from the original LeanDP repository: + # Verifying Differential Privacy in Lean The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and implemented several differentially private algorithms, including the Gaussian and Laplace mechanisms. From 5ebb1b2cd6807fb009eee3c5af1cbee0b9ecff92 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:42:16 -0700 Subject: [PATCH 212/216] Remove AccuracyProof import from Basic.lean Removed reference to AccuracyProof.lean file, which no longer exists. This broken import was creating errors. --- .../DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean | 1 - 1 file changed, 1 deletion(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean index a8af2541..b77d5966 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean @@ -1,7 +1,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Arithmetic import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.AccuracyProof import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties From 03949417310c596f590cf96fd476c97189406ae4 Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 26 Aug 2025 17:02:12 -0700 Subject: [PATCH 213/216] final --- .vscode/c_cpp_properties.json | 20 ++ .vscode/settings.json | 3 + .../Pure/Local/PushForward.lean | 20 +- .../Pure/Local/RandomizedResponse/Basic.lean | 1 - .../Local/RandomizedResponse/Definitions.lean | 1 - .../Pure/RandomizedPostProcessing.lean | 10 +- .../Pure/ShuffleModel/Definitions.lean | 17 +- .../Pure/ShuffleModel/Properties.lean | 200 +++++++++--- Tests/SampCert.dfy | 288 ++++++++++++++++++ ffi.cpp | 3 +- 10 files changed, 493 insertions(+), 70 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 Tests/SampCert.dfy diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..13f68a1d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "compilerPath": "C:\\msys64\\ucrt64\\bin\\gcc.exe", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "windows-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..70e34ecb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "C_Cpp.errorSquiggles": "disabled" +} \ No newline at end of file diff --git a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean index 4fe04bbd..90d738fb 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/PushForward.lean @@ -3,26 +3,11 @@ import SampCert open SLang /- This proves that the push-forward of a probability measure is a probability measure. -We use this when defining UniformSample', i.e., UniformSample as an SLang (Fin n). -/ +-/ noncomputable def push_forward {T S: Type} [DecidableEq S] (p : SLang T) (f : T -> S) : SLang S := fun s => ∑' (t : T), if s = f t then p t else 0 -lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : - ∑' (s : S), (push_forward p f) s = 1 := by - simp [push_forward] - rw [@ENNReal.tsum_comm] - have _: ∀b : T, ∑' (a : S), (if f b = a then p b else 0 : ENNReal) = p b := by - intro b - rw [tsum_eq_single (f b)] - simp - intro b' a - simp_all only [ne_eq, ite_eq_right_iff] - intro a_1 - subst a_1 - simp_all only [not_true_eq_false] - simp_all - lemma push_forward_prob_is_prob_gen {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = n) : ∑' (s : S), (push_forward p f) s = n := by simp [push_forward] @@ -37,3 +22,6 @@ lemma push_forward_prob_is_prob_gen {T S : Type} [DecidableEq S] (p : SLang T) ( subst a_1 simp_all only [not_true_eq_false] simp_all + +lemma push_forward_prob_is_prob {T S : Type} [DecidableEq S] (p : SLang T) (f : T -> S) (h : ∑' (t : T), p t = 1) : + ∑' (s : S), (push_forward p f) s = 1 := push_forward_prob_is_prob_gen p f h diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean index a8af2541..b77d5966 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Basic.lean @@ -1,7 +1,6 @@ import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Definitions import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.Arithmetic import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.PMFProof -import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.AccuracyProof import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert.DifferentialPrivacy.Pure.DP import SampCert.Samplers.Bernoulli.Properties diff --git a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean index ae8c674d..103557a8 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/RandomizedResponse/Definitions.lean @@ -1,6 +1,5 @@ import Mathlib.Probability.ProbabilityMassFunction.Basic import SampCert -/-import SampCert.DifferentialPrivacy.Local.MultiBernoulli -/ namespace RandomizedResponse diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index 85af63dc..8100305f 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -20,7 +20,6 @@ def privPostProcessRand {T U V : Type} (nq : Mechanism T U) (g : U → PMF V) : /-The following 3 lemmas are helper functions for the main theorems at the end-/ lemma tsum_ite_eq_single {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U): (∑' (x : U), if x = u then ((m l₁) x) else 0) = (m l₁) u := by - classical have hpoint : (fun x : U => if x = u then (m l₁) x else 0) = (fun x : U => if x = u then (m l₁) u else 0) := by @@ -32,8 +31,6 @@ lemma tsum_ite_eq_single {T U : Type} (m : Mechanism T U) (l₁ : List T)(u : U) simp [tsum_ite_eq (β := U) (α := ENNReal) u ((m l₁) u)] simp [hpoint, hcollapse] - - lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) (u : U) (hsplit : (fun v => if v ∈ S then p u * g u v else 0) = fun v => p u * if v ∈ S then g u v else 0): (∑' v : V, if v ∈ S then p u * g u v else 0) = p u * ∑' v : V, if v ∈ S then (g u) v else 0:= by calc @@ -49,7 +46,6 @@ lemma tsum_indicator_mul_left {U V : Type} (p : PMF U) (g : U → PMF V) (S : Se lemma tsum_bind_indicator {U V : Type} (p : PMF U) (g : U → PMF V) (S : Set V) : (∑' v : V, if v ∈ S then (p.bind g) v else 0) = (∑' u : U, p u * (∑' v : V, if v ∈ S then g u v else 0)) := by - classical have hbind : ∀ v, (p.bind g) v = ∑' u, p u * g u v := by intro v; simp [PMF.bind_apply] calc @@ -130,7 +126,6 @@ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} intro u have hS := h l₁ l₂ hN ({u} : Set U) have hnum : (∑' x : U, (if x ∈ ({u} : Set U) then m l₁ x else 0)) = m l₁ u := by - classical have : (fun x : U => if x ∈ ({u} : Set U) then m l₁ x else 0) = (fun x : U => if x = u then m l₁ u else 0) := by funext x @@ -139,7 +134,6 @@ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} · simp [Set.mem_singleton_iff, hx] simpa [this] using (tsum_ite_eq_single m l₁ u) have hden : (∑' x : U, (if x ∈ ({u} : Set U) then m l₂ x else 0)) = m l₂ u := by - classical have : (fun x : U => if x ∈ ({u} : Set U) then m l₂ x else 0) = (fun x : U => if x = u then m l₂ u else 0) := by funext x @@ -237,8 +231,8 @@ theorem randPostProcess_DP_bound_with_UpdateNeighbour {T U V : Type} {nq : Mecha by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum - exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + exact le_antisymm (le_trans this (by aesop)) (bot_le) simp [hNum, hDen, hNum0, hDen0] · nth_rewrite 1 [mul_comm] at hsum - have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := ENNReal.div_le_of_le_mul' hsum simpa [hNum, hDen] using this diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 34fef386..0ab1e4f5 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -1,13 +1,14 @@ import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Basic namespace SLang +open Classical /- Implementation of the Shuffler for the Shuffle Model. Outputs a random permutation of the input list. -/ -def Shuffler {α: Type}(l:List α) := do +def Shuffler {α: Type}(l:List α): SLang (List α) := do match l with -| [] => pure [] -| hd::tl => +| [] => return [] +| hd :: tl => let len := (hd :: tl).length let i : Nat ← UniformSample (Nat.toPNat' len) let rest : List α ← Shuffler tl @@ -19,14 +20,16 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L let b ← Shuffler l return b -def num_perms {α: Type} (l: List α) [DecidableEq α]: ℕ := (l.permutations.toFinset).card +/- Computes the number of permutations of a list, accounting + for the number of unique elements in the list. -/ +noncomputable def num_perms {α: Type} (l: List α): ℕ := (l.permutations.toFinset).card /- Definition of a function that uniformly permutes a given list.-/ -def UniformShuffler {U: Type}[BEq U] [DecidableEq U] (f: List U → SLang (List U)) : Prop := - ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (1: ENNReal)/(num_perms l₁) else (0: ENNReal) +def UniformShuffler {U: Type} (f: List U → SLang (List U)) : Prop := + ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (num_perms l₁ : ENNReal)⁻¹ else (0: ENNReal) /- Generalized version of the shuffle algorithm that takes in any mechanism -/ -def ShuffleAlgorithm [BEq U][DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do +def ShuffleAlgorithm (m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← (m l).toSLang let b ← f x return b diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index a32020d4..366a9e28 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -1,12 +1,104 @@ import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Basic import SampCert.DifferentialPrivacy.Pure.ShuffleModel.Definitions - +import SampCert.DifferentialPrivacy.Pure.Local.RandomizedResponse.Properties.DPProof +import Mathlib.Data.Set.Card namespace SLang open RandomizedResponse +open Classical + +lemma List.isEmpty_iff (l : List α): l.isEmpty ↔ l = [] := by + apply Iff.intro + intro hl + simp [List.isEmpty] at hl + cases l with + | nil => rfl + | cons hd tl => simp at hl + intro hl + simp [List.isEmpty, hl] + +lemma Shuffler_on_perm {U : Type} (l₁ l₂ : List U) (hp: l₁.isPerm l₂): Shuffler l₁ l₂ = (num_perms l₁ : ENNReal)⁻¹ := by + induction l₁ generalizing l₂ with + | nil => + simp_all [List.isPerm] + rw [List.isEmpty_iff] at hp + subst hp + simp [Shuffler, num_perms] + | cons hd tl ih => + simp [Shuffler] + + #check List.eraseIdx + sorry + +lemma Shuffler_not_perm {U : Type} (l₁ l₂ : List U) (hp: l₁.isPerm l₂ = false): Shuffler l₁ l₂ = 0 := by + induction l₁ generalizing l₂ with + | nil => + simp_all [List.isPerm] + have hp' : ¬l₂ = [] := by aesop + simp [Shuffler, hp'] + | cons hd tl ih => + simp [Shuffler] + intro i + cases hi: (i < (hd :: tl).length) == true with + | false => + simp at hi + apply Or.inl + refine UniformSample_apply_out (tl.length + 1).toPNat' i ?cons.true.h.support + aesop + | true => + simp at hi + apply Or.inr + intro l₂' hl₂' + apply ih l₂' + by_contra hperm + simp at hperm + simp [List.isPerm] at hp + have: hd ∈ l₂ := by + simp [hl₂'] + refine (List.mem_insertNth ?_).mpr ?_ + have: l₂'.length = tl.length := by + apply List.Perm.length_eq + rw [List.isPerm_iff] at hperm + rw [@List.perm_comm] + exact hperm + rw [this] + linarith + apply Or.inl + rfl + have hp': tl.isPerm (l₂.erase hd) = false := hp this + have h1: l₂.eraseIdx i = l₂' := by simp_all [List.eraseIdx_insertNth] + have h2: tl.isPerm (l₂.eraseIdx i) → tl.isPerm (l₂.erase hd) := by + rw [List.isPerm_iff, List.isPerm_iff] + intro h + constructor + apply h + have: l₂.erase hd = l₂.eraseIdx (l₂.indexOf hd) := by + sorry + rw [this] + sorry + have hp': tl.isPerm (l₂.eraseIdx i) = false := by + by_contra x + simp at x + have: ¬ (tl.isPerm (l₂.erase hd) = false) := by simp [h2 x] + contradiction + have nothp: ¬ (tl.isPerm l₂' = true) := by rw[h1] at hp'; simp [hp'] + contradiction + +lemma Shuffler_is_uniform {U : Type}: @UniformShuffler U (Shuffler) := by + simp [UniformShuffler] + intro l₁ l₂ + cases hp: l₁.isPerm l₂ with + | true => + simp + apply Shuffler_on_perm + exact hp + | false => + simp + apply Shuffler_not_perm + exact hp /- Shuffler function normalizes-/ -lemma Shuffler_PMF_helper {α: Type} [DecidableEq α][BEq α] (l:List α): HasSum (Shuffler l) 1 := by +lemma Shuffler_PMF_helper {α: Type} (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] induction l with | nil => @@ -36,36 +128,60 @@ lemma Shuffler_PMF_helper {α: Type} [DecidableEq α][BEq α] (l:List α): HasSu simp /- A restatement of the above lemma. -/ -lemma Shuffler_norms {α: Type} [DecidableEq α][BEq α] (l:List α): ∑' (b : List α), Shuffler l b = 1 := by +lemma Shuffler_norms {α: Type} (l:List α): ∑' (b : List α), Shuffler l b = 1 := by rw [← Summable.hasSum_iff ENNReal.summable] apply Shuffler_PMF_helper /-Instantiation of Shuffler as a PMF. -/ -def Shuffler_PMF {α : Type} [DecidableEq α] [BEq α] (l : List α) : PMF (List α) := +def Shuffler_PMF {α : Type} (l : List α) : PMF (List α) := ⟨Shuffler l, Shuffler_PMF_helper l⟩ -/- RRShuffle normalizes. -/ -theorem RRShuffle_PMF_helper [LawfulMonad SLang]{T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by - unfold RRShuffle - simp_all only [bind, pure, bind_pure] - unfold probBind - simp [Summable.hasSum_iff ENNReal.summable] - rw [ENNReal.tsum_comm] - conv => - enter [1,1,b] - rw [ENNReal.tsum_mul_left] - enter [2] - apply Shuffler_norms +lemma List.isPerm_iff_mem_permutations {U : Type} [DecidableEq U] [DecidableEq (List U)] (l : List U) (a : List U) : l.isPerm a ↔ (a ∈ l.permutations) := by + apply Iff.intro simp - rw [← Summable.hasSum_iff ENNReal.summable] - apply RRSample_PMF_helper + rw [@List.perm_comm] + intro hp + apply (@List.isPerm_iff U _ l a).mp hp + simp + intro hp + rw [@List.perm_comm] at hp + apply (@List.isPerm_iff U _ l a).mpr hp -/- Instantiation of RRShuffle as a PMF. -/ -def RRShuffle_PMF [LawfulMonad SLang] {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := - ⟨RRShuffle query num den h l, RRShuffle_PMF_helper query num den h l⟩ +lemma UniformShuffler_norms' {U: Type} (f: List U → SLang (List U))(h: UniformShuffler f)(l: List U): + ∑' (b : List U), f l b = 1 := by + have h2: ∑' (b : List U), (if List.isPerm l b then ((num_perms l : ENNReal)⁻¹) else 0) = (num_perms l) * (num_perms l : ENNReal)⁻¹ := by + set b := (num_perms l : ENNReal)⁻¹ + simp [@tsum_split_ite] + conv => + enter [1, 1, b_1] + rw [←one_mul b] + rw [@ENNReal.tsum_mul_right] + congr + simp [num_perms] + have h0: {x // l.isPerm x} = {x // x ∈ l.permutations.toFinset} := by + congr + apply funext + intro x + rw [eq_iff_iff] + have h0a: x ∈ l.permutations.toFinset ↔ x ∈ l.permutations := by aesop + rw [h0a] + exact List.isPerm_iff_mem_permutations l x + rw [h0] + rw [tsum_fintype] + clear h0 + simp_all only [Finset.univ_eq_attach, Finset.sum_const, Finset.card_attach, nsmul_eq_mul, mul_one, Nat.cast_inj] + simp_all [UniformShuffler] + apply ENNReal.mul_inv_cancel + all_goals simp[num_perms, List.permutations] + +/- Alternate version -/ +lemma UniformShuffler_norms {U: Type} (f: List U → SLang (List U))(h: UniformShuffler f)(l: List U): + HasSum (f l) 1 := by + rw [Summable.hasSum_iff ENNReal.summable] + exact UniformShuffler_norms' f h l /- Any shuffle algorithm normalizes. -/ -lemma ShuffleAlgorithm_PMF_helper {U: Type} [BEq U] [DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f)(h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T): +lemma ShuffleAlgorithm_PMF_helper {U: Type} (m : Mechanism T (List U))(f : List U → SLang (List U))(h: UniformShuffler f) (l: List T): HasSum (ShuffleAlgorithm m f h l) 1 := by unfold ShuffleAlgorithm simp_all only [bind, pure, bind_pure] @@ -76,29 +192,41 @@ HasSum (ShuffleAlgorithm m f h l) 1 := by enter [1,1,b] rw [ENNReal.tsum_mul_left] enter [2] - apply h1 + apply UniformShuffler_norms' + exact h simp /- Conversion of SLang output to PMF.-/ -def ShuffleAlgorithm_PMF {U: Type}[BEq U] [DecidableEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1) (l: List T) : PMF (List U) := - ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h h1 l⟩ +def ShuffleAlgorithm_PMF {U: Type} [DecidableEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (l: List T) : PMF (List U) := + ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h l⟩ + +/- RRShuffle normalizes. -/ +theorem RRShuffle_PMF_helper {T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by + have heqv: RRShuffle query num den h = ShuffleAlgorithm (RRSample_PMF query num den h) (Shuffler) (Shuffler_is_uniform) := by rfl + rw [heqv] + apply ShuffleAlgorithm_PMF_helper + +/- Instantiation of RRShuffle as a PMF. -/ +def RRShuffle_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := + ⟨RRShuffle query num den h l, RRShuffle_PMF_helper query num den h l⟩ /- Shows that shuffling is a postprocessing function. -/ -lemma shuffling_is_postprocessing [LawfulMonad SLang] [BEq U] [DecidableEq U] (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): privPostProcessRand m (fun u => ⟨f u, by - simp [Summable.hasSum_iff ENNReal.summable] - exact h1 u⟩) = ShuffleAlgorithm_PMF m f h_uniform h1:= by - funext x +lemma shuffling_is_postprocessing (m : Mechanism T (List U)) (f : List U → SLang (List U)) (h_uniform : UniformShuffler f): privPostProcessRand m (fun u => ⟨f u, UniformShuffler_norms f h_uniform u⟩) = ShuffleAlgorithm_PMF m f h_uniform := by + funext simp [privPostProcessRand, ShuffleAlgorithm_PMF, ShuffleAlgorithm] - congr + rfl /- Shuffle Algorithm is ε-differentially private, given that the local algorithm is ε-differentially private. -/ -theorem ShuffleAlgorithm_is_DP [LawfulMonad SLang] [BEq U] [DecidableEq U] (m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) -(h_uniform: UniformShuffler f) (h1: ∀u : List U, ∑' (v : List U), f u v = 1): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform h1) ε := by -have h2 (u : List U): HasSum (f u) 1 := by - simp [Summable.hasSum_iff ENNReal.summable] - exact h1 u +theorem ShuffleAlgorithm_is_DP (m : Mechanism T (List U))(f : List U → SLang (List U))(ε : ℝ)(hdp: DP_withUpdateNeighbour m ε) +(h_uniform: UniformShuffler f): DP_withUpdateNeighbour (ShuffleAlgorithm_PMF m f h_uniform) ε := by conv => enter [1] rw [←shuffling_is_postprocessing] -let g : List U → PMF (List U) := (fun u => ⟨f u, h2 u⟩) +let g : List U → PMF (List U) := (fun u => ⟨f u, UniformShuffler_norms f h_uniform u⟩) apply @randPostProcess_DP_bound_with_UpdateNeighbour T _ _ m ε hdp g + +theorem RRShuffle_is_DP (ε : ℝ) (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den): DP_withUpdateNeighbour (RRShuffle_PMF query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by + have heqv: RRShuffle_PMF query num den h = ShuffleAlgorithm_PMF (RRSample_PMF query num den h) (Shuffler) (Shuffler_is_uniform) := by rfl + rw [heqv] + apply ShuffleAlgorithm_is_DP + exact RRSample_DP query num den h diff --git a/Tests/SampCert.dfy b/Tests/SampCert.dfy new file mode 100644 index 00000000..84dbf63e --- /dev/null +++ b/Tests/SampCert.dfy @@ -0,0 +1,288 @@ + +module {:extern} Random { + + class {:extern} Random { + + static method {:extern "UniformPowerOfTwoSample"} ExternUniformPowerOfTwoSample(n: nat) returns (u: nat) + + } + +} + +module {:extern} SampCert { + + import Random + + type pos = x: nat | x > 0 witness 1 + + class SLang { + + + method UniformPowerOfTwoSample(n: nat) returns (u: nat) + + requires n >= 1 + + { + + u := Random.Random.ExternUniformPowerOfTwoSample(n); + + } + method {:verify false} UniformSample (n: pos) + returns (o: nat) + modifies this + decreases * + { + var x := UniformPowerOfTwoSample(2 * n); + while ! (x < n) + decreases * + { + x := UniformPowerOfTwoSample(2 * n); + } + var r := x; + o := r; + } + + method {:verify false} BernoulliSample (num: nat, den: pos) + returns (o: bool) + requires num <= den + modifies this + decreases * + { + var d := UniformSample(den); + o := d < num; + } + + method {:verify false} BernoulliExpNegSampleUnitLoop (num: nat, den: pos, state: (bool,pos)) + returns (o: (bool,pos)) + requires num <= den + modifies this + decreases * + { + var A := BernoulliSample(num, state.1 * den); + o := (A,state.1 + 1); + } + + method {:verify false} BernoulliExpNegSampleUnitAux (num: nat, den: pos) + returns (o: nat) + requires num <= den + modifies this + decreases * + { + var state := (true,1); + while state.0 + decreases * + { + state := BernoulliExpNegSampleUnitLoop(num, den, state); + } + var r := state; + o := r.1; + } + + method {:verify false} BernoulliExpNegSampleUnit (num: nat, den: pos) + returns (o: bool) + requires num <= den + modifies this + decreases * + { + var K := BernoulliExpNegSampleUnitAux(num, den); + if K % 2 == 0 { + o := true; + } else { + o := false; + } + } + + method {:verify false} BernoulliExpNegSampleGenLoop (iter: nat) + returns (o: bool) + modifies this + decreases * + { + if iter == 0 { + o := true; + } else { + var B := BernoulliExpNegSampleUnit(1, 1); + if ! (B == true) { + o := B; + } else { + var R := BernoulliExpNegSampleGenLoop(iter - 1); + o := R; + } + } + } + + method {:verify false} BernoulliExpNegSample (num: nat, den: pos) + returns (o: bool) + modifies this + decreases * + { + if num <= den { + var X := BernoulliExpNegSampleUnit(num, den); + o := X; + } else { + var gamf := num / den; + var B := BernoulliExpNegSampleGenLoop(gamf); + if B == true { + var X := BernoulliExpNegSampleUnit(num % den, den); + o := X; + } else { + o := false; + } + } + } + + method {:verify false} DiscreteLaplaceSampleLoopIn1Aux (t: pos) + returns (o: (nat,bool)) + modifies this + decreases * + { + var U := UniformSample(t); + var D := BernoulliExpNegSample(U, t); + o := (U,D); + } + + method {:verify false} DiscreteLaplaceSampleLoopIn1 (t: pos) + returns (o: nat) + modifies this + decreases * + { + var x := DiscreteLaplaceSampleLoopIn1Aux(t); + while ! (x.1) + decreases * + { + x := DiscreteLaplaceSampleLoopIn1Aux(t); + } + var r1 := x; + o := r1.0; + } + + method {:verify false} DiscreteLaplaceSampleLoopIn2Aux (num: nat, den: pos, K: (bool,nat)) + returns (o: (bool,nat)) + modifies this + decreases * + { + var A := BernoulliExpNegSample(num, den); + o := (A,K.1 + 1); + } + + method {:verify false} DiscreteLaplaceSampleLoopIn2 (num: nat, den: pos) + returns (o: nat) + modifies this + decreases * + { + var K := (true,0); + while K.0 + decreases * + { + K := DiscreteLaplaceSampleLoopIn2Aux(num, den, K); + } + var r2 := K; + o := r2.1; + } + + method {:verify false} DiscreteLaplaceSampleLoop (num: pos, den: pos) + returns (o: (bool,nat)) + modifies this + decreases * + { + var v := DiscreteLaplaceSampleLoopIn2(den, num); + var V := v - 1; + var B := BernoulliSample(1, 2); + o := (B,V); + } + + method {:verify false} DiscreteLaplaceSampleLoop' (num: pos, den: pos) + returns (o: (bool,nat)) + modifies this + decreases * + { + var U := DiscreteLaplaceSampleLoopIn1(num); + var v := DiscreteLaplaceSampleLoopIn2(1, 1); + var V := v - 1; + var X := U + num * V; + var Y := X / den; + var B := BernoulliSample(1, 2); + o := (B,Y); + } + + method {:verify false} DiscreteLaplaceSample (num: pos, den: pos) + returns (o: int) + modifies this + decreases * + { + var x := DiscreteLaplaceSampleLoop(num, den); + while ! (! (x.0 == true && x.1 == 0)) + decreases * + { + x := DiscreteLaplaceSampleLoop(num, den); + } + var r := x; + var Z := if r.0 == true then - (r.1) else r.1; + o := Z; + } + + method {:verify false} DiscreteLaplaceSampleOptimized (num: pos, den: pos) + returns (o: int) + modifies this + decreases * + { + var x := DiscreteLaplaceSampleLoop'(num, den); + while ! (! (x.0 == true && x.1 == 0)) + decreases * + { + x := DiscreteLaplaceSampleLoop'(num, den); + } + var r := x; + var Z := if r.0 == true then - (r.1) else r.1; + o := Z; + } + + method {:verify false} DiscreteLaplaceSampleMixed (num: pos, den: pos, mix: nat) + returns (o: int) + modifies this + decreases * + { + if num <= den * mix { + var v := DiscreteLaplaceSample(num, den); + o := v; + } else { + var v := DiscreteLaplaceSampleOptimized(num, den); + o := v; + } + } + + method {:verify false} DiscreteGaussianSampleLoop (num: pos, den: pos, t: pos, mix: nat) + returns (o: (int,bool)) + modifies this + decreases * + { + var Y := DiscreteLaplaceSampleMixed(t, 1, mix); + var y := (if Y < 0 then -(Y) else (Y)); + var n := ((if y * t * den - num < 0 then -(y * t * den - num) else (y * t * den - num))) * ((if y * t * den - num < 0 then -(y * t * den - num) else (y * t * den - num))); + var d := 2 * num * (t) * (t) * den; + var C := BernoulliExpNegSample(n, d); + o := (Y,C); + } + + method {:verify false} DiscreteGaussianSample (num: pos, den: pos, mix: nat) + returns (o: int) + modifies this + decreases * + { + var ti := num / den; + var t := ti + 1; + var num := (num) * (num); + var den := (den) * (den); + var x := DiscreteGaussianSampleLoop(num, den, t, mix); + while ! (x.1) + decreases * + { + x := DiscreteGaussianSampleLoop(num, den, t, mix); + } + var r := x; + o := r.0; + } + + +} + +} diff --git a/ffi.cpp b/ffi.cpp index 8b3775b3..41258d37 100644 --- a/ffi.cpp +++ b/ffi.cpp @@ -47,7 +47,8 @@ extern "C" lean_object * prob_While(lean_object * condition, lean_object * body, extern "C" lean_object * my_run(lean_object * a) { if (urandom == -1) { - urandom = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + // add O_CLOEXEC for Linux + urandom = open("/dev/urandom", O_RDONLY); if (urandom == -1) { lean_internal_panic("prob_UniformByte: /dev/urandom cannot be opened"); } From 456ee49671d24fbf4f2a76d2f194138f597e72cf Mon Sep 17 00:00:00 2001 From: rshlyakh Date: Tue, 26 Aug 2025 17:16:59 -0700 Subject: [PATCH 214/216] readytomerge --- .../Pure/Local/ENNRealLemmasSuite.lean | 2 +- .../Pure/RandomizedPostProcessing.lean | 9 +- .../Pure/ShuffleModel/Definitions.lean | 15 ++- .../Pure/ShuffleModel/Properties.lean | 120 +++--------------- 4 files changed, 32 insertions(+), 114 deletions(-) diff --git a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean index c06590eb..40350945 100644 --- a/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean +++ b/SampCert/DifferentialPrivacy/Pure/Local/ENNRealLemmasSuite.lean @@ -166,7 +166,7 @@ lemma quot_gt_one (a b : ENNReal): 1 < a/b -> b < a := by have nh: ¬ (1 : ENNReal) < 0/0 := by simp contradiction | false => simp at ha - have ha1: a > 0 := by exact pos_iff_ne_zero.mpr ha + have ha1: a > 0 := pos_iff_ne_zero.mpr ha rw[←hb] at ha1 exact ha1 | false => simp at hb diff --git a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean index 8100305f..3a203f16 100644 --- a/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean +++ b/SampCert/DifferentialPrivacy/Pure/RandomizedPostProcessing.lean @@ -162,9 +162,8 @@ lemma DP.pointwise_ratio_bound_for_UpdateNeighbour {T U : Type} aesop have : m l₁ u ≤ ENNReal.ofReal (Real.exp ε) * m l₂ u := by rw [← ENNReal.div_le_iff_le_mul] - · exact hratio - · aesop - · aesop + exact hratio + all_goals aesop simpa using this /-Proves that combing DP algorithm with post-processor does not worsen the DP bound-/ @@ -196,10 +195,10 @@ theorem randPostProcess_DP_bound {T U V : Type} {nq : Mechanism T U} {ε : NNRea by_cases hDen0 : (∑' u : U, p₂ u * w u) = 0 · have hNum0 : (∑' u : U, p₁ u * w u) = 0 := by have : (∑' u : U, p₁ u * w u) ≤ ENNReal.ofReal (Real.exp ε) * 0 := by simpa [hDen0] using hsum - exact le_antisymm (le_trans this (by aesop)) (by exact bot_le) + exact le_antisymm (le_trans this (by aesop)) bot_le simp [hNum, hDen, hNum0, hDen0] · nth_rewrite 1 [mul_comm] at hsum - have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := by (exact ENNReal.div_le_of_le_mul' hsum) + have : (∑' u : U, p₁ u * w u) / (∑' u : U, p₂ u * w u) ≤ ENNReal.ofReal (Real.exp ε) := ENNReal.div_le_of_le_mul' hsum simpa [hNum, hDen] using this /-Same thing as randPostProcess_DP_bound, but uses our DP_withUpdateNeighbour-/ diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean index 0ab1e4f5..c2d69128 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Definitions.lean @@ -4,7 +4,9 @@ namespace SLang open Classical /- Implementation of the Shuffler for the Shuffle Model. - Outputs a random permutation of the input list. -/ + Outputs a random permutation of the input list. + This is a bit unsatisfactory, + since we have not actually shown that the output distribution is uniform. -/ def Shuffler {α: Type}(l:List α): SLang (List α) := do match l with | [] => return [] @@ -14,7 +16,7 @@ match l with let rest : List α ← Shuffler tl return rest.insertNth i hd -/- This is the implementation of the Shuffle algorithm using Randomized Response as the local randomizer. -/ +/- This is the implementation of the Shuffle algorithm using randomized response as the local randomizer. -/ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: List T) := do let l ← RandomizedResponse.RRSample query num den h l let b ← Shuffler l @@ -22,14 +24,15 @@ def RRShuffle(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l: L /- Computes the number of permutations of a list, accounting for the number of unique elements in the list. -/ -noncomputable def num_perms {α: Type} (l: List α): ℕ := (l.permutations.toFinset).card -/- Definition of a function that uniformly permutes a given list.-/ +def num_perms {α: Type} [DecidableEq α] (l: List α): ℕ := (l.permutations.toFinset).card + +/- Definition of a function that produces random permutations. -/ def UniformShuffler {U: Type} (f: List U → SLang (List U)) : Prop := ∀ l₁ l₂: List U, f l₁ l₂ = if List.isPerm l₁ l₂ then (num_perms l₁ : ENNReal)⁻¹ else (0: ENNReal) -/- Generalized version of the shuffle algorithm that takes in any mechanism -/ -def ShuffleAlgorithm (m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do +/- Generalized version of the shuffle algorithm that takes in any mechanism. -/ +def ShuffleAlgorithm (m : Mechanism T (List U))(f : List U → SLang (List U))(_: UniformShuffler f)(l: List T) := do let x ← (m l).toSLang let b ← f x return b diff --git a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean index 366a9e28..35a90444 100644 --- a/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean +++ b/SampCert/DifferentialPrivacy/Pure/ShuffleModel/Properties.lean @@ -7,96 +7,6 @@ namespace SLang open RandomizedResponse open Classical -lemma List.isEmpty_iff (l : List α): l.isEmpty ↔ l = [] := by - apply Iff.intro - intro hl - simp [List.isEmpty] at hl - cases l with - | nil => rfl - | cons hd tl => simp at hl - intro hl - simp [List.isEmpty, hl] - -lemma Shuffler_on_perm {U : Type} (l₁ l₂ : List U) (hp: l₁.isPerm l₂): Shuffler l₁ l₂ = (num_perms l₁ : ENNReal)⁻¹ := by - induction l₁ generalizing l₂ with - | nil => - simp_all [List.isPerm] - rw [List.isEmpty_iff] at hp - subst hp - simp [Shuffler, num_perms] - | cons hd tl ih => - simp [Shuffler] - - #check List.eraseIdx - sorry - -lemma Shuffler_not_perm {U : Type} (l₁ l₂ : List U) (hp: l₁.isPerm l₂ = false): Shuffler l₁ l₂ = 0 := by - induction l₁ generalizing l₂ with - | nil => - simp_all [List.isPerm] - have hp' : ¬l₂ = [] := by aesop - simp [Shuffler, hp'] - | cons hd tl ih => - simp [Shuffler] - intro i - cases hi: (i < (hd :: tl).length) == true with - | false => - simp at hi - apply Or.inl - refine UniformSample_apply_out (tl.length + 1).toPNat' i ?cons.true.h.support - aesop - | true => - simp at hi - apply Or.inr - intro l₂' hl₂' - apply ih l₂' - by_contra hperm - simp at hperm - simp [List.isPerm] at hp - have: hd ∈ l₂ := by - simp [hl₂'] - refine (List.mem_insertNth ?_).mpr ?_ - have: l₂'.length = tl.length := by - apply List.Perm.length_eq - rw [List.isPerm_iff] at hperm - rw [@List.perm_comm] - exact hperm - rw [this] - linarith - apply Or.inl - rfl - have hp': tl.isPerm (l₂.erase hd) = false := hp this - have h1: l₂.eraseIdx i = l₂' := by simp_all [List.eraseIdx_insertNth] - have h2: tl.isPerm (l₂.eraseIdx i) → tl.isPerm (l₂.erase hd) := by - rw [List.isPerm_iff, List.isPerm_iff] - intro h - constructor - apply h - have: l₂.erase hd = l₂.eraseIdx (l₂.indexOf hd) := by - sorry - rw [this] - sorry - have hp': tl.isPerm (l₂.eraseIdx i) = false := by - by_contra x - simp at x - have: ¬ (tl.isPerm (l₂.erase hd) = false) := by simp [h2 x] - contradiction - have nothp: ¬ (tl.isPerm l₂' = true) := by rw[h1] at hp'; simp [hp'] - contradiction - -lemma Shuffler_is_uniform {U : Type}: @UniformShuffler U (Shuffler) := by - simp [UniformShuffler] - intro l₁ l₂ - cases hp: l₁.isPerm l₂ with - | true => - simp - apply Shuffler_on_perm - exact hp - | false => - simp - apply Shuffler_not_perm - exact hp - /- Shuffler function normalizes-/ lemma Shuffler_PMF_helper {α: Type} (l:List α): HasSum (Shuffler l) 1 := by rw [Summable.hasSum_iff ENNReal.summable] @@ -136,6 +46,7 @@ lemma Shuffler_norms {α: Type} (l:List α): ∑' (b : List α), Shuffler l b = def Shuffler_PMF {α : Type} (l : List α) : PMF (List α) := ⟨Shuffler l, Shuffler_PMF_helper l⟩ +/- Helper lemma about List.isPerm-/ lemma List.isPerm_iff_mem_permutations {U : Type} [DecidableEq U] [DecidableEq (List U)] (l : List U) (a : List U) : l.isPerm a ↔ (a ∈ l.permutations) := by apply Iff.intro simp @@ -168,7 +79,6 @@ lemma UniformShuffler_norms' {U: Type} (f: List U → SLang (List U))(h: Uniform exact List.isPerm_iff_mem_permutations l x rw [h0] rw [tsum_fintype] - clear h0 simp_all only [Finset.univ_eq_attach, Finset.sum_const, Finset.card_attach, nsmul_eq_mul, mul_one, Nat.cast_inj] simp_all [UniformShuffler] apply ENNReal.mul_inv_cancel @@ -200,11 +110,23 @@ HasSum (ShuffleAlgorithm m f h l) 1 := by def ShuffleAlgorithm_PMF {U: Type} [DecidableEq U] (m : Mechanism T (List U ))(f : List U → SLang (List U))(h: UniformShuffler f) (l: List T) : PMF (List U) := ⟨ShuffleAlgorithm m f h l, ShuffleAlgorithm_PMF_helper m f h l⟩ -/- RRShuffle normalizes. -/ -theorem RRShuffle_PMF_helper {T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by - have heqv: RRShuffle query num den h = ShuffleAlgorithm (RRSample_PMF query num den h) (Shuffler) (Shuffler_is_uniform) := by rfl - rw [heqv] - apply ShuffleAlgorithm_PMF_helper +/- RRShuffle normalizes. + This is a bit unsatisfactory: if we had that ''RandomShuffle'' is in fact + a UniformShuffler, then the prove would be trivial simply by instantiating RRShuffle as a UniformShuffler-/ +lemma RRShuffle_PMF_helper {T : Type}(query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den)(l : List T): HasSum (RRShuffle query num den h l) 1 := by + unfold RRShuffle + simp_all only [bind, pure, bind_pure] + unfold probBind + simp [Summable.hasSum_iff ENNReal.summable] + rw [ENNReal.tsum_comm] + conv => + enter [1,1,b] + rw [ENNReal.tsum_mul_left] + enter [2] + apply Shuffler_norms + simp + rw [← Summable.hasSum_iff ENNReal.summable] + apply RRSample_PMF_helper /- Instantiation of RRShuffle as a PMF. -/ def RRShuffle_PMF {T : Type} (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den) (l : List T) : PMF (List Bool) := @@ -224,9 +146,3 @@ conv => rw [←shuffling_is_postprocessing] let g : List U → PMF (List U) := (fun u => ⟨f u, UniformShuffler_norms f h_uniform u⟩) apply @randPostProcess_DP_bound_with_UpdateNeighbour T _ _ m ε hdp g - -theorem RRShuffle_is_DP (ε : ℝ) (query: T -> Bool) (num : Nat) (den : PNat) (h: 2 * num < den): DP_withUpdateNeighbour (RRShuffle_PMF query num den h) (Real.log ((↑(NNReal.ofPNat den) + 2 * ↑num) / (↑(NNReal.ofPNat den) - 2 * ↑num))) := by - have heqv: RRShuffle_PMF query num den h = ShuffleAlgorithm_PMF (RRSample_PMF query num den h) (Shuffler) (Shuffler_is_uniform) := by rfl - rw [heqv] - apply ShuffleAlgorithm_is_DP - exact RRSample_DP query num den h From cdf62cea2affca32e09602727644a9d44484a701 Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:18:34 -0700 Subject: [PATCH 215/216] Add O_CLOEXEC flag when opening /dev/urandom --- ffi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi.cpp b/ffi.cpp index 41258d37..c17da02b 100644 --- a/ffi.cpp +++ b/ffi.cpp @@ -48,7 +48,7 @@ extern "C" lean_object * prob_While(lean_object * condition, lean_object * body, extern "C" lean_object * my_run(lean_object * a) { if (urandom == -1) { // add O_CLOEXEC for Linux - urandom = open("/dev/urandom", O_RDONLY); + urandom = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (urandom == -1) { lean_internal_panic("prob_UniformByte: /dev/urandom cannot be opened"); } From 8e9a7babd4db177aff57cb21279a1979e497784b Mon Sep 17 00:00:00 2001 From: rshlyakh <157648681+rshlyakh@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:45:42 -0700 Subject: [PATCH 216/216] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 9ab6918e..d8d89c28 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -This contains some personal work done by me after the RIPS 2025 project on Lean and Differential Privacy. I will mostly focus on cleaning up the shuffle model proofs and adding more support for the shuffle model. The rest is the README from the original LeanDP repository: - # Verifying Differential Privacy in Lean The [SampCert](https://github.com/leanprover/SampCert) project (de Medeiros et al. 2024) was created to implement and formalize differential privacy notions in Lean. It provided support for various notions of differential privacy, a framework for implementing differentially private mechanisms via verified sampling algorithms, and implemented several differentially private algorithms, including the Gaussian and Laplace mechanisms.