Theory Differential_Privacy_Divergence

(*
 Title: Differential_Privacy_Divergence.thy
 Author: Tetsuya Sato
 Author: Yasuhiko Minamide
*)

theory Differential_Privacy_Divergence
  imports "Comparable_Probability_Measures"
begin

section ‹Divergence for Differential Privacy›

text‹ First, we introduce the divergence for differential privacy. ›

definition DP_divergence :: "'a measure  'a measure  real  ereal " where
  "DP_divergence M N ε = Sup {ereal(measure M A - (exp ε) * measure N A) | A::'a set. A  (sets M)}"

lemma DP_divergence_SUP:
  shows "DP_divergence M N ε = ( (A::'a set)  (sets M). ereal(measure M A - (exp ε) * measure N A))"
  by (auto simp: setcompr_eq_image DP_divergence_def)

subsection ‹Basic Properties›

subsubsection ‹Non-negativity›

lemma DP_divergence_nonnegativity:
  shows "0  DP_divergence M N ε"
proof(unfold DP_divergence_SUP, rule Sup_upper2[of 0])
  have "{}  sets M" "(λA :: 'a set. ereal (measure M A - exp ε * measure N A)) {} = 0"
    by auto
  thus "0  (λA. ereal (measure M A - exp ε * measure N A)) ` sets M"
    by force
qed(auto)

subsubsection ‹Graded predicate›

lemma DP_divergence_forall:
  shows "( A  (sets M). (measure M A - (exp ε) * measure N A  (δ :: real)))
  DP_divergence M N ε  (δ :: real)"
  unfolding DP_divergence_SUP by (auto simp: SUP_le_iff)

lemma DP_divergence_leE:
  assumes "DP_divergence M N ε  (δ :: real)"
  shows "measure M A  (exp ε) * measure N A + (δ :: real)"
proof(cases  "A  (sets M)")
  case True
  then show ?thesis using DP_divergence_forall assms(1) by force
next
  case False (* If "A ∈ (sets M)" then "measure M A = 0" *)
  then have "measure M A = 0 " "0   measure N A " "0  (exp ε)"
    using measure_notin_sets by auto
  moreover have "0  ereal δ"
    using assms DP_divergence_nonnegativity[of M N ε] dual_order.trans by blast
  ultimately show ?thesis by auto
qed

lemma DP_divergence_leI:
  assumes " A. A  (sets M)  measure M A  (exp ε) * measure N A + (δ :: real)"
  shows "DP_divergence M N ε  (δ :: real)"
  using assms unfolding DP_divergence_def by(intro cSup_least, fastforce+)



subsubsection (0,0)›-DP means the equality of distributions ›

lemma prob_measure_eqI_le:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and le:" A  sets M. emeasure M A  emeasure N A"
  shows "M = N"
proof(rule measure_eqI)
  interpret pM: prob_space M
    using actual_prob_space M by auto
  interpret pN: prob_space N
    using actual_prob_space N by auto
  show *: "sets M = sets N"
    using M N space_prob_algebra_sets by blast
  show " A. A  sets M  emeasure M A = emeasure N A"
  proof (rule ccontr)
    fix A assume A: "A  sets M" and neq:"emeasure M A  emeasure N A"
    then have A2: "A  sets N" and A3: "A  sets L"
      using M N space_prob_algebra_sets by blast+
    from neq consider
      "emeasure M A < emeasure N A" | "emeasure M A > emeasure N A"
      by fastforce
    then show False
    proof(cases)
      case 1
      have Ms: "emeasure M (space M - A) = emeasure M (space M) - emeasure M A"
        using A sets.sets_into_space by(subst emeasure_Diff,auto)
      have Ns: "emeasure N (space M - A) = emeasure N (space N) - emeasure N A"
        using A2 sets.sets_into_space sets_eq_imp_space_eq[OF *] by(subst emeasure_Diff,auto)
      have "emeasure M (space M - A) > emeasure N (space M - A)"
        unfolding Ms Ns pM.emeasure_space_1 pN.emeasure_space_1
        by (meson "1" ennreal_mono_minus_cancel ennreal_one_less_top leI less_le_not_le pM.measure_le_1 pN.measure_le_1)
      moreover have "emeasure M (space M - A) emeasure N (space M - A)"
        using le A by auto
      ultimately show ?thesis
        by auto
    next
      case 2
      from le A have "emeasure M A  emeasure N A"
        by auto
      with 2 show ?thesis
        by auto
    qed
  qed
qed

lemma DP_divergence_zero:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and DP0:"DP_divergence M N (0::real)  (0::real)"
  shows "M = N"
proof(intro prob_measure_eqI_le[of M L N] M N ballI)
  interpret comparable_probability_measures L M N
    by(unfold_locales,auto simp: M N)
  interpret pM: prob_space M
    by auto
  interpret pN: prob_space N
    by auto
  fix A assume " A  sets M"
  hence "measure M A - measure N A  0"
    using DP0[simplified zero_ereal_def] unfolding DP_divergence_forall[symmetric] by auto
  hence "measure M A  measure N A"
    by auto
  thus "emeasure M A  emeasure N A"
    by (simp add: pM.emeasure_eq_measure pN.emeasure_eq_measure)
qed

subsubsection ‹Conversion from pointwise DP \cite{Prasad803anote} ›

(* (cf. [Kasiviswanathan and Smith JPC 2014]) to DP *)

lemma DP_divergence_pointwise:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and C: "C  sets M"
    and "Asets M. A  C  measure M A  exp ε * measure N A"
    and "1 - δ  measure M C"
  shows "DP_divergence M N (ε::real)  δ"
proof(rule DP_divergence_leI)
  interpret comparable_probability_measures L M N
    by(unfold_locales, auto simp: assms)
  fix A assume A:"A  sets M"
  define A1 and A2 where "A1 = A  C" and "A2 = A - C"
  hence A1s[measurable]:"A1  sets M" and A2s[measurable]:"A2  sets M" and A1C:"A1  C" and A12: "A1  A2 = A"
    unfolding A1_def A2_def using C A by auto
  hence "measure M A1  (exp ε) * measure N A1"
    using assms(4) by blast
  also have "...  (exp ε) * measure N A"
    using A1s A2s unfolding A12[symmetric]
    by(subst measure_Union[of N A1 A2]) (auto simp add: finite_measure.emeasure_finite A1_def A2_def)
  finally have 1: "measure M A1  exp ε * measure N A".

  have A2Cc:"A2  space M - C"
    by (metis A A2_def Diff_mono sets.sets_into_space subset_refl)
  have "measure M A2  measure M (space M - C)"
    by(intro finite_measure.finite_measure_mono A2Cc Sigma_Algebra.sets.compl_sets C,auto)
  also have "... = measure M (space M) - measure M C"
    using assms(3) finM finite_measure.finite_measure_compl by blast
  also have "... 1 - (1 - δ)"
    using M actual_prob_space prob_space.axioms assms(5) prob_space.prob_space by force
  also have "... = δ"
    by auto
  finally have 2: "measure M A2  δ".

  have "measure M A = measure M A1 + measure M A2"
    using A1s A2s unfolding A12[symmetric]
    by(subst measure_Union[of M A1 A2]) (auto simp add: finite_measure.emeasure_finite A1_def A2_def)
  also have "...  exp ε * measure N A + δ"
    using 1 2 by auto
  finally show "measure M A  exp ε * measure N A + δ" .
qed

subsubsection ‹(Reverse-) Monotonicity for ε›

lemma DP_divergence_monotonicity:
  assumes "ε1  ε2"
  shows "DP_divergence M N ε2  DP_divergence M N ε1"
proof(unfold DP_divergence_SUP, rule SUP_mono)
  fix A assume *: "A  sets M"
  have "ereal (measure M A - exp ε2 * measure N A)  ereal (measure M A - exp ε1 * measure N A)"
    by (auto simp: assms mult_mono')
  with * show "msets M. ereal (measure M A - exp ε2 * measure N A)  ereal (measure M m - exp ε1 * measure N m)"
    by blast
qed

corollary DP_divergence_monotonicity':
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and "ε1  ε2" and "δ1  δ2"
  shows "DP_divergence M N ε1  δ1  DP_divergence M N ε2  δ2"
  by (meson DP_divergence_monotonicity M N assms gfp.leq_trans)

subsubsection ‹ Reflexivity ›

lemma DP_divergence_reflexivity:
  shows "DP_divergence M M 0 = 0"
proof(unfold DP_divergence_SUP)
  have " A sets M. ereal (measure M A - exp 0 * measure M A) = (0 :: ereal)"
    by auto
  thus "(A sets M. ereal (measure M A - exp 0 * measure M A)) = (0 :: ereal)"
    by (metis (no_types, lifting) SUP_ereal_eq_0_iff_nonneg empty_iff order_refl sets.top)
qed

corollary DP_divergence_reflexivity':
  assumes "0  ε "
  shows "DP_divergence M M ε = 0"
proof-
  have "DP_divergence M M ε  DP_divergence M M 0"
    by(rule DP_divergence_monotonicity, auto simp: assms)
  also have "... = 0"
    by(auto simp: DP_divergence_reflexivity)
  finally have 1: "DP_divergence M M ε  0".
  have 2: "0  DP_divergence M M ε"
    by(auto simp: DP_divergence_nonnegativity[of M M ε])
  from 1 2 show ?thesis by auto
qed

subsubsection ‹ Transitivity ›

lemma DP_divergence_transitivity:
  assumes  DP1: "DP_divergence M1 M2 ε1  0"
    and DP2: "DP_divergence M2 M3 ε2  0"
  shows "DP_divergence M1 M3 (ε1+ε2)  0"
  unfolding zero_ereal_def
proof(subst DP_divergence_forall[symmetric],intro ballI)
  fix A assume AM1: " A  sets M1"
  have "measure M1 A  exp ε1 * measure M2 A"
    using DP_divergence_leE[OF DP1[simplified zero_ereal_def]] by auto
  also have " ...  exp ε1 * exp ε2 * measure M3 A"
    using DP_divergence_leE[OF DP2[simplified zero_ereal_def]] by auto
  finally have "measure M1 A  exp (ε1 + ε2) * measure M3 A"
    unfolding exp_add[of ε1 ε2, symmetric] by auto
  thus "measure M1 A - exp (ε1 + ε2) * measure M3 A  0" by auto
qed

subsubsection ‹ Composability ›

proposition DP_divergence_composability:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and f: "f  L M prob_algebra K"
    and g: "g  L M prob_algebra K"
    and div1: "DP_divergence M N ε1  (δ1 :: real)"
    and div2: "x  (space L). DP_divergence (f x) (g x) ε2  (δ2 :: real)"
    and "0  ε1" and "0  ε2"
  shows "DP_divergence (M  f) (N  g) (ε1 + ε2)  δ1 + δ2"
proof(subst DP_divergence_forall[THEN sym])
  note [measurable] = f g
  interpret comparable_probability_measures L M N
    by(unfold_locales, auto simp: assms)

  show "Asets (M  f). measure (M  f) A - exp (ε1 + ε2) * measure (N  g) A  δ1 + δ2"
  proof
    fix "A" assume "A  sets (M  f)"
    hence AinSetsK: "A  sets K"
      using M f sets_bind' by blast

    have f2[measurable]: "f  sum_measure M N M prob_algebra K"
      using setMN_L by measurable
    have g2[measurable]: "g  sum_measure M N M prob_algebra K"
      using setMN_L by measurable
    have ess_boundedf[simp]: "ess_bounded (sum_measure M N) (λx. measure (f x) A)"
      by (intro probability_kernel_evaluation_ess_bounded[where M = K] AinSetsK f2)
    have ess_boundedg[simp]: "ess_bounded (sum_measure M N) (λx. measure (g x) A)"
      by (intro probability_kernel_evaluation_ess_bounded[where M = K] AinSetsK g2)
    have integrablef[simp]: "integrable (sum_measure M N) (λx. measure (f x) A)"
      by (auto simp:AinSetsK)
    have integrableg[simp]: "integrable (sum_measure M N) (λx. measure (g x) A)"
      by (auto simp: AinSetsK)
    have meas1[measurable_cong]: "measurable L (prob_algebra K) = measurable (sum_measure M N) (prob_algebra K)"
      by(rule measurable_cong_sets, simp_all)
    have f2[measurable]: "f  sum_measure M N M prob_algebra K"
      by auto
    have g2[measurable]: "g  sum_measure M N M prob_algebra K"
      by auto

    have "x space (sum_measure M N). measure (f x) A - (exp ε2) * measure (g x) A  δ2"
    proof
      fix x assume A: "x  space (sum_measure M N)"
      hence A2: "A  sets (f x)"
        by (metis AinSetsK f2 measurable_prob_algebraD subprob_measurableD(2))
      from A have xxx: "DP_divergence (f x) (g x) ε2  δ2"
        using div2 spaceMN_L by auto
      show "measure (f x) A - exp ε2* measure (g x) A  δ2"
        using xxx[simplified DP_divergence_forall[THEN sym,rule_format]] A2 by blast
    qed
    hence ineq7: "x space (sum_measure M N). measure (f x) A - δ2  exp ε2 * measure (g x) A "
      by auto

    have "x space (sum_measure M N). measure (f x) A- δ2  min 1 (exp ε2 * measure (g x) A) "
    proof
      fix x assume Ass0: "x  space (sum_measure M N)"
      hence "0  DP_divergence (f x) (g x) ε2"
        using DP_divergence_nonnegativity by auto
      also have "DP_divergence (f x) (g x) ε2  δ2"
        using div2 Ass0 spaceMN_L by auto
      finally have posdelta2: "0  δ2"by auto
      hence minA: "measure (f x) A - δ2  1"
        using evaluations_probabilistic_process(3)[OF f AinSetsK] assms Ass0 spaceMN_L by auto
      have minB: "measure (f x) A - δ2  exp ε2 * measure (g x) A"
        using Ass0 ineq7 by auto
      from minA minB show "measure (f x) A - δ2  min 1 (exp ε2 * measure (g x) A)"
        by auto
    qed
    hence inequalityB: "x space (sum_measure M N). max 0 (measure (f x) A - δ2)  min 1 (exp ε2 * measure (g x) A)"
      by auto

    have inequalityC: "x space (sum_measure M N). dM x * max 0 (measure (f x) A - δ2)  dM x * min 1 (exp ε2 * measure (g x) A)"
    proof
      fix x assume A0: "x space (sum_measure M N)"
      with inequalityB have P1: "(max 0 (measure (f x) A - δ2))  min 1 ((exp ε2) * measure (g x) A) "
        by auto
      thus "dM x * max 0 (measure (f x) A - δ2)  dM x * min 1 (exp ε2 * measure (g x) A)"
        by(intro mult_left_mono, auto)
    qed

    have "(measure (M  f) A) - exp (ε1 + ε2) * (measure (N  g) A) = ( x. (dM x) * (measure (f x) A) (sum_measure M N)) - (exp (ε1 + ε2)) * ( x. (dN x) * (measure (g x) A) (sum_measure M N))"
      using AinSetsK dM_bind_integral[OF f2] dN_bind_integral[OF g2] by auto
    also have "... = ( x. (dM x) * (measure (f x) A) (sum_measure M N)) - ( x. (exp (ε1 + ε2)) * (dN x) * (measure (g x) A) (sum_measure M N)) "
      by (metis (mono_tags, lifting) Bochner_Integration.integral_cong integral_mult_right_zero mult.assoc)
    also have "... = ( x. (dM x) * (measure (f x) A) - (exp (ε1 + ε2)) * (dN x) * (measure (g x) A) (sum_measure M N)) "
      by(rule Bochner_Integration.integral_diff[THEN sym],auto)
    also have "... = ( x. (dM x) * (measure (f x) A) - (exp ε1) * (exp ε2) * (dN x) * (measure (g x) A) (sum_measure M N)) "
      by (auto simp: exp_add)
    also have "...  ( x. (dM x) * (max 0 (measure (f x) A - δ2) + δ2) - (exp ε1) * (dN x) * min 1 ((exp ε2) * (measure (g x) A)) (sum_measure M N)) "
    proof(rule integral_mono)
      fix x assume A0: "x  space (sum_measure M N)"
      have "dM x * measure (f x) A - exp ε1 * exp ε2 * dN x * measure (g x) A = dM x * ((measure (f x) A - δ2) + δ2)- exp ε1 * dN x *(exp ε2 * measure (g x) A)"
        by auto
      also have "...  dM x * ((max 0 (measure (f x) A - δ2)) + δ2)- exp ε1 * dN x *(exp ε2 * measure (g x) A)"
        by(auto simp: mult_left_mono)
      also have "...  dM x * ((max 0 (measure (f x) A - δ2)) + δ2)- exp ε1 * dN x * (min 1 (exp ε2 * measure (g x) A))"
        by(auto simp: mult_left_mono)
      finally show "dM x * measure (f x) A - exp ε1 * exp ε2 * dN x * measure (g x) A  dM x * (max 0 (measure (f x) A - δ2) + δ2) - exp ε1 * dN x * min 1 (exp ε2 * measure (g x) A)".
    qed(auto)
    also have "... = ( x. (dM x) * (max 0 (measure (f x) A - δ2)) - (exp ε1) * (dN x) * min 1 ((exp ε2)* (measure (g x) A)) + (dM x) *δ2 (sum_measure M N)) "
      by (auto simp: diff_add_eq ring_class.ring_distribs(1))
    also have "...  ( x. (dM x) * (min 1 ((exp ε2)* (measure (g x) A))) - (exp ε1) * (dN x) * min 1 ((exp ε2)* (measure (g x) A)) + dM x *δ2 (sum_measure M N)) "
    proof(rule integral_mono)
      show "integrable (sum_measure M N) (λx. dM x * (min 1 ((exp ε2) * measure (g x) A)) - exp ε1 * dN x * min 1 (exp ε2 * measure (g x) A) + dM x * δ2)" by auto
      fix x assume "x  space (sum_measure M N)"
      with inequalityC have "dM x * max 0 (measure (f x) A - δ2)  dM x * (min 1 ((exp ε2) * measure (g x) A))"
        by blast
      thus "dM x * max 0 (measure (f x) A - δ2) - exp ε1 * dN x * min 1 (exp ε2 * measure (g x) A) + dM x * δ2  dM x * min 1 (exp ε2 * measure (g x) A) - exp ε1 * dN x * min 1 (exp ε2 * measure (g x) A) + dM x * δ2"
        by auto
    qed(auto)
    also have "... =( x. ((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2) * (measure (g x) A)) + dM x *δ2 (sum_measure M N)) "
      by (auto simp: vector_space_over_itself.scale_left_diff_distrib)
    also have "... =( x. ((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2) * (measure (g x) A))(sum_measure M N)) +( x. dM x *δ2 (sum_measure M N)) "
      by(rule Bochner_Integration.integral_add,auto)
    finally have *: "(measure (M  f) A) - exp (ε1 + ε2) * (measure (N  g) A)( x. ((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2)* (measure (g x) A))(sum_measure M N)) +( x. dM x *δ2 (sum_measure M N))".

    have "( x. dM x *δ2 (sum_measure M N))=( x. δ2 (density (sum_measure M N) dM))"
      by(rule integral_real_density[THEN sym],auto)
    also have "... =( x. δ2 (density (sum_measure M N)(ennreal o dM)))"
      by (meson comp_def[THEN sym])
    also have "... =( x. δ2 M)"
      using dM_density by auto
    also have "... = δ2 * measure M (space M)"
      by auto
    also have "...  δ2"
      using prob_space.prob_space prob_spaceM by fastforce
    finally have **: "( x. dM x *δ2 (sum_measure M N))  δ2".

    let ?B = "{x space (sum_measure M N). 0  ((dM x) - (exp ε1) * (dN x))}"
    have mble10: "?B  sets (sum_measure M N)"
      by measurable

    have "( x. ((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2)* (measure (g x) A))(sum_measure M N))  ( x ?B. (((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2)* (measure (g x) A)))(sum_measure M N))"
    proof(rule integral_drop_negative_part2)
      show "(λx. (dM x - exp ε1 * dN x) * min 1 (exp ε2 * measure (g x) A))  borel_measurable (sum_measure M N)"
        using integrablef integrableg by measurable
      show "integrable (sum_measure M N) (λx. (dM x - exp ε1 * dN x) * min 1 (exp ε2 * measure (g x) A))"
        by auto
      show "{x  space (sum_measure M N). 0  dM x - exp ε1 * dN x}  sets (sum_measure M N)"
        by measurable
      show "{x  space (sum_measure M N). 0 < (dM x - exp ε1 * dN x) * min 1 (exp ε2 * measure (g x) A)}  {x  space (sum_measure M N). 0  dM x - exp ε1 * dN x}"
      proof(rule subsetI, safe)
        fix x assume "x  space (sum_measure M N)" and "0 < (dM x - exp ε1 * dN x) * min 1 (exp ε2 * measure (g x) A)"
        hence "0 < (dM x - exp ε1 * dN x)  0 < min 1 (exp ε2 * measure (g x) A)"
          by (metis exp_gt_zero lambda_one min.absorb4 min.order_iff mult_pos_pos vector_space_over_itself.scale_zero_right verit_comp_simplify1(3) zero_less_measure_iff zero_less_mult_pos2)
        thus "0  dM x - exp ε1 * dN x"
          by auto
      qed
      show "{x  space (sum_measure M N). 0  dM x - exp ε1 * dN x}  {x  space (sum_measure M N). 0  (dM x - exp ε1 * dN x) * min 1 (exp ε2 * measure (g x) A)}"
        by auto
    qed
    also have "...  ( x ?B. ((dM x) - (exp ε1) * (dN x)) (sum_measure M N))"
      using mble10 by(intro set_integral_mono,unfold set_integrable_def,auto simp: mult_left_le)
    also have "... = ( x ?B. (dM x) (sum_measure M N)) - ( x ?B. ((exp ε1) * (dN x)) (sum_measure M N))"
      using mble10 by(intro set_integral_diff, auto simp:set_integrable_def)
    also have "... = ( x ?B. (dM x) (sum_measure M N)) - (exp ε1) * ( x ?B. (dN x) (sum_measure M N))"
      by auto
    also have "... = measure M ?B - (exp ε1) * (measure N ?B)"
    proof-
      have "( x ?B. dM x (sum_measure M N)) = ( x. (dM x * indicator ?B x)  (sum_measure M N))"
        by (auto simp: mult.commute set_lebesgue_integral_def)
      also have "... = ( x. indicator ?B x (density (sum_measure M N) dM))"
        by(rule integral_real_density[THEN sym],measurable)
      also have "... = ( x. indicator ?B x (density (sum_measure M N) (ennreal o dM)))"
        by (metis comp_apply)
      also have "... = ( x. indicator ?B x *R 1 (density (sum_measure M N) (ennreal o dM)))"
        by auto
      also have "... = ( x ?B. 1 (density (sum_measure M N) (ennreal o dM)))"
        by(auto simp:set_lebesgue_integral_def)
      also have "... = 1 * measure (density (sum_measure M N) (ennreal o dM)) ?B"
			proof(rule set_integral_indicator)
			  show "emeasure (density (sum_measure M N) (ennreal  dM)) ?B < "
			    using finite_measure.emeasure_finite[of M ?B,OF finM] diff_gt_0_iff_gt_ennreal by force
			qed(measurable)
			also have "... = 1 * measure M ?B"
			  using dM_density by auto
			finally have eq11: "( x ?B. dM x (sum_measure M N)) = measure M ?B"
			  by auto

			have "( x ?B. dN x (sum_measure M N)) = ( x. (dN x * indicator ?B x)  (sum_measure M N))"
			  by (auto simp: mult.commute set_lebesgue_integral_def)
			also have "... = ( x. indicator ?B x (density (sum_measure M N) dN))"
			  by(rule integral_real_density[THEN sym],measurable)
			also have "... = ( x. indicator ?B x (density (sum_measure M N) (ennreal o dN)))"
			  by (metis comp_apply)
			also have "... = ( x ?B. 1 (density (sum_measure M N) (ennreal o dN)))"
			  by(auto simp:set_lebesgue_integral_def)
			also have "... = 1 * measure (density (sum_measure M N) (ennreal o dN)) ?B"
			proof(rule set_integral_indicator,measurable)
			  show "emeasure (density (sum_measure M N) (ennreal  dN)) ?B < "
			    using finite_measure.emeasure_finite[of N ?B,OF finN] diff_gt_0_iff_gt_ennreal by force
			qed
			also have "... = 1 * measure N ?B"
			  using dN_density by auto
			finally have eq21: "( x ?B. dN x (sum_measure M N)) = measure N ?B"
			  by auto

			show ?thesis
			  using eq11 eq21 by auto
		qed
		also have "...  δ1"
		  using div1 DP_divergence_forall mble10 setMN_M by blast
		finally have ***: "( x. ((dM x) - (exp ε1) * (dN x)) * min 1 ((exp ε2)* (measure (g x) A))(sum_measure M N))  δ1".

		show "measure (M  f) A - exp (ε1 + ε2) * measure (N  g) A  δ1 + δ2"
		  using * ** *** by auto
	qed
qed

subsubsection ‹ Post-processing inequality  ›

corollary DP_divergence_postprocessing:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and f: "f  L M (prob_algebra K)"
    and div1: "DP_divergence M N ε1  (δ1 :: real)"
    and "0 ε1"
  shows "DP_divergence (bind M f) (bind N f) ε1  δ1"
proof-
  have div2: "x  (space L). DP_divergence (f x) (f x) 0  ereal 0"
  proof
    fix x assume A0: "x  space L"
    have "DP_divergence (f x) (f x) 0 = 0"
      by(rule DP_divergence_reflexivity)
    thus "DP_divergence (f x) (f x) 0  ereal 0"
      by auto
  qed
  have "DP_divergence (M  f) (N  f) (ε1 + 0)  ereal (δ1 + 0) "
    by(rule DP_divergence_composability[of M L N f K f ε1 δ1 0 0], auto simp: assms div2)
  thus "DP_divergence (M  f) (N  f) ε1  ereal δ1"by auto
qed

subsubsection ‹ Law for the strength of the Giry monad ›

lemma DP_divergence_strength:
  assumes "M  space (prob_algebra L)"
    and "N  space (prob_algebra L)"
    and "DP_divergence M N ε1  (δ1 :: real)"
    and "0 ε1"
    and x:"x  space K"
  shows "DP_divergence ((return K x) M M) ((return K x) M N) ε1  δ1"
proof-
  interpret comparable_probability_measures L M N
    by(unfold_locales, auto simp: assms)
  define f where "f = (λ y. (return (K M L) (x, y)))"
  have f: "f  L M (prob_algebra (K M L))"
    using assms f_def by force
  have 1:"DP_divergence (bind M f) (bind N f) ε1  δ1"
    using DP_divergence_postprocessing assms f by blast
  moreover have 2:"(return K x) M M = (M  f)" 
    by(subst Giry_strength_bind_return[OF M x])(metis f_def return_cong sets_pair_measure_cong sets_return spaceM)
  moreover have 3:"(return K x) M N = (N  f) "
    by(subst Giry_strength_bind_return[OF N x])(metis f_def return_cong sets_pair_measure_cong sets_return spaceN)
  ultimately show "DP_divergence ((return K x) M M) ((return K x) M N) ε1  δ1"
    by auto
qed

subsubsection ‹ Additivity: law for the double-strength of the Giry monad›

lemma DP_divergence_additivity:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and M2: "M2  space (prob_algebra K)"
    and N2: "N2  space (prob_algebra K)"
    and div1: "DP_divergence M N ε1  (δ1 :: real)"
    and div2': "DP_divergence M2 N2 ε2  (δ2 :: real)"
    and "0  ε1" and "0  ε2"
  shows "DP_divergence (M M M2) (N M N2) (ε1 + ε2)  δ1 + δ2"
proof-
  interpret comparable_probability_measures L M N
    by(unfold_locales, auto simp: assms)
  interpret c2: comparable_probability_measures K M2 N2
    by(unfold_locales, auto simp: assms)
  have 1: "M M M2 = M  (λx. M2  (λy. return (L M K) (x, y)))"
    by(subst return_cong[of "M M M2",symmetric])(auto simp: pair_prob_space_def pair_sigma_finite_def assms finite_measure_def prob_space_imp_sigma_finite sets_pair_measure intro!: pair_prob_space.pair_measure_eq_bind )
  have 2: "N M N2 = N  (λx. N2  (λy. return (L M K) (x, y)))"
    by(subst return_cong[of "N M N2",symmetric])(auto simp: pair_prob_space_def pair_sigma_finite_def assms finite_measure_def prob_space_imp_sigma_finite sets_pair_measure intro!: pair_prob_space.pair_measure_eq_bind )
  have " DP_divergence (M M M2) (N M N2) (ε1 + ε2) = DP_divergence (M  (λx. M2  (λy. return (L M K) (x, y)))) (N  (λx. N2  (λy. return (L M K) (x, y)))) (ε1 + ε2)"
    using 1 2 by auto
  also have "...  δ1 + δ2"
  proof(rule DP_divergence_composability[where L = L and K = " (L M K)"])
    show "(λx. M2  (λy. return (L M K) (x, y)))  L M prob_algebra (L M K)"
      and "(λx. N2  (λy. return (L M K) (x, y)))  L M prob_algebra (L M K)"
      by(rule measurable_bind_prob_space2[where N = K],auto simp: assms)+
  qed(auto intro!: DP_divergence_postprocessing[where K = "L M K"] assms measurable_bind_prob_space2[where N = K])
  finally show ?thesis.
qed

subsection ‹Hypotehsis testing interpretation›

subsubsection ‹Privacy region›

definition DP_region_one_side :: "real  real  (real × real) set" where
  "DP_region_one_side ε δ = {(x::real,y::real). (x - exp(ε) * y  δ)  0  x  x  1  0 y  y  1} "

lemma DP_divergence_region:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
  shows "( A  (sets M). ((measure M A, measure N A)  DP_region_one_side ε δ))  DP_divergence M N ε  (δ :: real)"
proof-
  interpret comparable_probability_measures L M N
    by(unfold_locales,auto simp: assms)
  show ?thesis 
    by(subst DP_divergence_forall[symmetric],unfold  DP_region_one_side_def,auto intro!: prob_space.prob_le_1)
qed

subsubsection ‹2-generatedness of @{term DP_divergence} \cite{DBLP:journals/corr/abs-1905-09982} ›

lemma DP_divergence_2_generated_deterministic:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
    and "0  ε"
  shows "DP_divergence M N ε = ( f  L M (count_space (UNIV :: bool set)). DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)"
proof-
  interpret comparable_probability_measures L M N
    by(unfold_locales,auto simp: assms)
  show ?thesis
  proof(intro SUP_eqI[THEN sym])
    fix f :: "_bool" assume A0: "Measurable.pred L f"
    hence eq1: "distr M (count_space UNIV) f = M  return (count_space UNIV)  f"
      by (metis bind_return_distr measurable_cong_sets prob_space.not_empty prob_spaceM spaceM)
    have eq2: "distr N (count_space UNIV) f = N  return (count_space UNIV)  f"
      by (metis A0 bind_return_distr measurable_cong_sets prob_space.not_empty prob_spaceN spaceN)
    have "DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε = DP_divergence (M  return (count_space UNIV)  f) (N  return (count_space UNIV)  f) ε"
      using eq1 eq2 by auto
    also have "...  DP_divergence M N ε"
      by(rule ereal_le_real,auto intro!: DP_divergence_postprocessing measurable_comp[where N = "count_space UNIV"] A0 assms measurable_return_prob_space)
    finally show "DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε  DP_divergence M N ε".
  next
    fix y :: ereal assume A0: "(f. Measurable.pred L f  DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε  y)"
    show "DP_divergence M N ε  y"
      unfolding DP_divergence_def
    proof(subst Sup_le_iff,safe)
      fix i assume A1: "i  sets M"
      hence [measurable]: "Measurable.pred L (λ x.(x  i))"
        by auto
      have [simp]:"(λx. x  i) -` {True} = i"
        by auto
      from A1 A0 have "DP_divergence (distr M (count_space UNIV) (λ x.(x  i))) (distr N (count_space UNIV) (λ x.(x  i))) ε  y"
        by auto
      hence ineq1: "measure (distr M (count_space UNIV) (λ x.(x  i))) {True} - exp ε * measure (distr N (count_space UNIV) (λ x.(x  i))) {True}  y"
        using DP_divergence_def[of "(distr M (count_space UNIV) (λ x.(x  i)))" "(distr N (count_space UNIV) (λ x.(x  i)))"ε ] by (auto simp: Sup_le_iff)
      have eq1: "measure (distr M (count_space UNIV) (λ x.(x  i))) {True} = measure M i "
        using measure_distr[of "(λ x.(x  i))"M "(count_space UNIV)" "{True}"] spaceM(1) A1 by auto
      have eq2: "measure (distr N (count_space UNIV) (λ x.(x  i))) {True} = measure N i "
        using measure_distr[of "(λ x.(x  i))"N "(count_space UNIV)" "{True}"] spaceN(1) A1 by auto
      from ineq1 show "ereal (measure M i - exp ε * measure N i)  y"
        unfolding eq1 eq2 by auto
    qed
  qed
qed

lemma DP_divergence_2_generated:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)"
  assumes "0  ε"
  shows "DP_divergence M N ε = ( f  L M (prob_algebra (count_space (UNIV :: bool set))). DP_divergence (M  f) (N  f) ε)"
proof-
  interpret comparable_probability_measures L M N
    by(unfold_locales,auto simp: assms)
  show ?thesis
  proof(subst DP_divergence_2_generated_deterministic[OF assms],intro order_antisym)
    show "(fL M prob_algebra (count_space (UNIV :: bool set)). DP_divergence (M  f) (N  f) ε)  (fL M count_space (UNIV :: bool set). DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)"
    proof(subst SUP_le_iff)
      show "iL M prob_algebra (count_space (UNIV :: bool set)). DP_divergence (M  i) (N  i) ε  (fL M count_space (UNIV :: bool set). DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)"
      proof
        fix i :: "_  bool measure" assume A0: "i  L M prob_algebra (count_space UNIV)"
        have "DP_divergence (M  i) (N  i) ε  DP_divergence M N ε"
          using DP_divergence_postprocessing A0 assms ereal_le_real by blast
        also have "... = (f :: 'a  boolL M count_space UNIV. DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)"
          using DP_divergence_2_generated_deterministic assms by blast
        finally show "DP_divergence (M  i) (N  i) ε  (f :: 'a  boolL M count_space UNIV. DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)".
      qed
    qed
    show "(f :: 'a  boolL M count_space UNIV. DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε)  (f :: 'a  bool measureL M prob_algebra (count_space UNIV). DP_divergence (M  f) (N  f) ε)"
    proof(rule SUP_mono)
      fix f :: "_bool" assume A0: "f  L M count_space UNIV"
      show "m :: 'a  bool measureL M prob_algebra (count_space UNIV). DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε  DP_divergence (M  m) (N  m) ε"
      proof(unfold Bex_def)
        have "(((return (count_space UNIV)) o f)  L M prob_algebra (count_space UNIV))  (DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε = DP_divergence (M ((return (count_space (UNIV :: bool set))) o f)) (N ((return (count_space (UNIV :: bool set))) o f)) ε)"
        proof(safe)
          show "return (count_space UNIV)  f  L M prob_algebra (count_space UNIV)"using A0 by auto
          from A0 have eq1: "(distr M (count_space UNIV) f) = M ((return (count_space (UNIV :: bool set))) o f)"
            by (metis bind_return_distr measurable_return1 prob_space.not_empty return_sets_cong spaceM prob_spaceM)
          from A0 have eq2: "(distr N (count_space UNIV) f) = N ((return (count_space (UNIV :: bool set))) o f)"
            by (metis bind_return_distr measurable_cong_sets prob_space.not_empty spaceMN_M spaceMN_N spaceN prob_spaceN )
          from eq1 eq2 show "DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε = DP_divergence (M  return (count_space UNIV)  f) (N  return (count_space UNIV)  f) ε"
            by auto
        qed
        thus "x :: 'a  bool measure. x  L M prob_algebra (count_space UNIV)  DP_divergence (distr M (count_space UNIV) f) (distr N (count_space UNIV) f) ε  DP_divergence (M  x) (N  x) ε"
          by auto
      qed
    qed
  qed
qed

subsection ‹real version of @{term DP_divergence}

subsubsection ‹finiteness›

definition DP_divergence_real :: "'a measure  'a measure  real  real " where
  "DP_divergence_real M N ε = Sup {(measure M A - (exp ε) * measure N A) | A::'a set. A  (sets M)}"

lemma DP_divergence_real_SUP:
  shows "DP_divergence_real M N ε = ( (A::'a set)  (sets M). (measure M A - (exp ε) * measure N A))"
  by (auto simp: setcompr_eq_image DP_divergence_real_def)

lemma DP_divergence_real_leI:
  assumes " A. A  (sets M)  measure M A  (exp ε) * measure N A + (δ :: real)"
  shows "DP_divergence_real M N ε  (δ :: real)"
  using assms unfolding DP_divergence_real_def by(intro cSup_least, fastforce+)

subsubsection ‹real version of @{term DP_divergence}

text ‹ If @{term M} and  @{term N} are finite then @{term DP_divergence} and the following version @{term DP_divergence_real} are the same.
 However, if  @{term M} and @{term N} are not finite, @{term "DP_divergence_real M N ε"} is not well defined.
 Hence, the latter needs extra assumptions for the finiteness of measures @{term M} and @{term N}.
 For example the following lemmas need a kind of finiteness of@{term M} and @{term N}.›

lemma DP_divergence_is_real:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)" (*we need them*)
  shows "DP_divergence M N ε = DP_divergence_real M N ε"
  unfolding DP_divergence_def DP_divergence_real_def
proof(subst ereal_Sup)
  interpret pM: prob_space M
    using M actual_prob_space by auto
  interpret pN: prob_space N
    using N actual_prob_space by auto
  {
    fix A assume "A  (sets M)"
    have "pM.prob A - exp ε * pN.prob A  pM.prob A"
      by auto
    also have "...  1"
      by auto
    finally have 1: "pM.prob A - exp ε * pN.prob A  1".

    have " - exp ε  - exp ε * pN.prob A"
      by auto
    also have "...  pM.prob A - exp ε * pN.prob A"
      by auto
    finally have 2: "- exp ε  pM.prob A - exp ε * pN.prob A".
    note 1 2
  }note * = this

  hence " (ereal ` {pM.prob A - exp ε * pN.prob A |A. A  pM.events})  ereal 1"
    unfolding SUP_le_iff by auto
  moreover from * have "ereal (- exp ε)   (ereal ` {pM.prob A - exp ε * pN.prob A |A. A  pM.events})"
    by(intro SUP_upper2, auto)
  ultimately show " ¦ (ereal ` {pM.prob A - exp ε * pN.prob A |A. A  pM.events})¦  "
    by auto
  have " {ereal (pM.prob A - exp ε * pN.prob A) |A. A  pM.events} = ereal ` {pM.prob A - exp ε * pN.prob A |A. A  pM.events}"
    by blast
  thus "  {ereal (pM.prob A - exp ε * pN.prob A) |A. A  pM.events} =  (ereal ` {pM.prob A - exp ε * pN.prob A |A. A  pM.events})"
    by auto
qed

corollary DP_divergence_real_forall:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)" (*we need them*)
  shows "( A  (sets M). (measure M A - (exp ε) * measure N A  (δ :: real)))  DP_divergence_real M N ε  (δ :: real)"
  unfolding DP_divergence_forall DP_divergence_is_real[OF M N] by auto

corollary DP_divergence_real_inequality1:
  assumes M: "M  space (prob_algebra L)"
    and N: "N  space (prob_algebra L)" (*we need them*)
    and "A  (sets M)" and "DP_divergence_real M N ε  (δ :: real)"
  shows "measure M A  (exp ε) * measure N A + (δ :: real)"
  using DP_divergence_real_forall[OF M N] assms(3) assms(4) by force

end