Theory Lazy_Greedy_Oracle

theory Lazy_Greedy_Oracle
  imports "Greedy_Submodular_Construct"
begin

text ‹
  This theory provides auxiliary lazy-selection primitives based on cached
  upper bounds for marginal gains. It is used by the stateful lazy greedy
  construction below, while the final approximation theorem is stated in the
  proof layer.
›

section ‹Lazy selection via cached upper bounds›

context Submodular_Func
begin

definition ub_valid :: "'a set  'a set  ('a  real)  bool" where
  "ub_valid S A ub  (xA. gain S x  ub x)"

definition untight :: "'a set  'a set  ('a  real)  'a set" where
  "untight S A ub = {xA. ub x > gain S x}"

definition tighten :: "'a set  ('a  real)  'a  ('a  real)" where
  "tighten S ub x = ub(x := gain S x)"

definition pick_ub_some :: "'a set  ('a  real)  'a" where
  "pick_ub_some A ub = (SOME x. x  A  is_arg_max ub (λy. y  A) x)"

lemma pick_ub_some_mem:
  assumes finA: "finite A" and neA: "A  {}"
  shows "pick_ub_some A ub  A"
proof -
  have exB: "xA. is_arg_max ub (λy. y  A) x"
    using finite_is_arg_max_in[OF finA neA] by blast
  have ex: "x. x  A  is_arg_max ub (λy. y  A) x"
    using exB by auto
  show ?thesis
    unfolding pick_ub_some_def
    using someI_ex[OF ex] by blast
qed

lemma pick_ub_some_max:
  assumes finA: "finite A" and neA: "A  {}" and yA: "y  A"
  shows "ub y  ub (pick_ub_some A ub)"
proof -
  have exB: "xA. is_arg_max ub (λz. z  A) x"
    using finite_is_arg_max_in[OF finA neA] by blast
  have ex: "x. x  A  is_arg_max ub (λz. z  A) x"
    using exB by auto
  have chosen: "is_arg_max ub (λz. z  A) (pick_ub_some A ub)"
    unfolding pick_ub_some_def
    using someI_ex[OF ex] by blast
  show ?thesis
    using is_arg_maxD_le[OF chosen yA] .
qed

fun lazy_argmax_gain_fuel ::
  "nat  'a set  'a set  ('a  real)  'a"
where
  "lazy_argmax_gain_fuel 0 S A ub = pick_ub_some A ub"
| "lazy_argmax_gain_fuel (Suc n) S A ub =
    (let x = pick_ub_some A ub in
     if ub x = gain S x then x
     else lazy_argmax_gain_fuel n S A (tighten S ub x))"

definition lazy_argmax_gain :: "'a set  'a set  ('a  real)  'a" where
  "lazy_argmax_gain S A ub = lazy_argmax_gain_fuel (card A) S A ub"

lemma ub_valid_tighten:
  assumes ubv: "ub_valid S A ub"
  shows "ub_valid S A (tighten S ub x)"
  using ubv unfolding ub_valid_def tighten_def by auto

lemma untight_tighten:
  assumes xA: "x  A" and gt: "ub x > gain S x"
  shows "untight S A (tighten S ub x) = untight S A ub - {x}"
  using xA gt
  unfolding untight_def tighten_def
  by auto


lemma finite_untight:
  assumes finA: "finite A"
  shows "finite (untight S A ub)"
  using finA unfolding untight_def by simp

lemma lazy_argmax_gain_fuel_max:
  assumes finA: "finite A" and neA: "A  {}"
  shows "ub_valid S A ub  card (untight S A ub)  n 
         yA. gain S y  gain S (lazy_argmax_gain_fuel n S A ub)"
proof (induction n arbitrary: ub)
  case 0
  from 0 have ubv: "ub_valid S A ub" by simp
  from 0 have bound: "card (untight S A ub)  0" by simp

  have finU: "finite (untight S A ub)" using finite_untight[OF finA] .
  have U0: "untight S A ub = {}"
    using bound finU by auto

  have ub_le_gain: "yA. ub y  gain S y"
  proof (intro ballI)
    fix y assume yA: "y  A"
    have "¬ (ub y > gain S y)"
      using U0 yA unfolding untight_def by auto
    thus "ub y  gain S y" by simp
  qed

  have ub_eq_gain: "yA. ub y = gain S y"
  proof (intro ballI)
    fix y assume yA: "y  A"
    have "gain S y  ub y"
      using ubv yA unfolding ub_valid_def by auto
    moreover have "ub y  gain S y"
      using ub_le_gain yA by auto
    ultimately show "ub y = gain S y" by simp
  qed

  let ?x = "pick_ub_some A ub"
  have xA: "?x  A" using pick_ub_some_mem[OF finA neA] .
  have ubx: "ub ?x = gain S ?x"
    using ub_eq_gain xA by auto

  show ?case
  proof (intro ballI)
    fix y assume yA: "y  A"
    have "gain S y = ub y" using ub_eq_gain yA by auto
    also have "  ub ?x"
      using pick_ub_some_max[OF finA neA yA] .
    also have " = gain S ?x"
      using ubx by simp
    finally show "gain S y  gain S (lazy_argmax_gain_fuel 0 S A ub)"
      by (simp add: Let_def)
  qed
next
  case (Suc n ub)
  from Suc.prems have ubv: "ub_valid S A ub" by simp
  from Suc.prems have bound: "card (untight S A ub)  Suc n" by simp

  let ?x = "pick_ub_some A ub"
  have xA: "?x  A" using pick_ub_some_mem[OF finA neA] .

  show ?case
  proof (cases "ub ?x = gain S ?x")
    case True
    show ?thesis
    proof (intro ballI)
      fix y assume yA: "y  A"
      have "gain S y  ub y"
        using ubv yA unfolding ub_valid_def by auto
      also have "  ub ?x"
        using pick_ub_some_max[OF finA neA yA] .
      finally show "gain S y  gain S (lazy_argmax_gain_fuel (Suc n) S A ub)"
        using True by (simp add: Let_def)
    qed
  next
    case False
    have le_gx: "gain S ?x  ub ?x"
      using ubv xA unfolding ub_valid_def by auto
    have gt: "ub ?x > gain S ?x"
      using le_gx False by (meson eq_iff not_le order_le_less)

    have Ueq: "untight S A (tighten S ub ?x) = untight S A ub - {?x}"
      using xA gt by (simp add: untight_tighten)

    have xU: "?x  untight S A ub"
      using xA gt unfolding untight_def by auto

    have finU: "finite (untight S A ub)"
      using finite_untight[OF finA] .

    have bound': "card (untight S A (tighten S ub ?x))  n"
    proof -
      have "card (untight S A (tighten S ub ?x)) = card (untight S A ub - {?x})"
        by (simp add: Ueq)
      also have " = card (untight S A ub) - 1"
        using finU xU by (simp add: card_Diff_singleton)
      finally show ?thesis
        using bound by arith
    qed

    have IH: "yA. gain S y  gain S (lazy_argmax_gain_fuel n S A (tighten S ub ?x))"
      using Suc.IH[OF ub_valid_tighten[OF ubv] bound'] .

    show ?thesis
      using False IH by (simp add: Let_def)
  qed
qed

lemma lazy_argmax_gain_fuel_mem:
  assumes finA: "finite A" and neA: "A  {}"
  shows "lazy_argmax_gain_fuel n S A ub  A"
proof (induction n arbitrary: ub)
  case 0
  show ?case
    using pick_ub_some_mem[OF finA neA] by simp
next
  case (Suc n)
  let ?x = "pick_ub_some A ub"
  have xA: "?x  A"
    using pick_ub_some_mem[OF finA neA] .

  show ?case
  proof (cases "ub ?x = gain S ?x")
    case True
    then show ?thesis
      using xA by (simp add: Let_def)
  next
    case False
    then show ?thesis
      using Suc.IH[of "tighten S ub ?x"] finA neA
      by (simp add: Let_def)
  qed
qed

lemma lazy_argmax_gain_mem:
  assumes finA: "finite A" and neA: "A  {}"
  shows "lazy_argmax_gain S A ub  A"
  unfolding lazy_argmax_gain_def
  by (rule lazy_argmax_gain_fuel_mem[OF finA neA])

lemma lazy_argmax_gain_max:
  assumes finA: "finite A" and neA: "A  {}"
      and ubv: "ub_valid S A ub"
  shows "yA. gain S y  gain S (lazy_argmax_gain S A ub)"
proof -
  have finU: "finite (untight S A ub)"
    using finite_untight[OF finA] .
  have "card (untight S A ub)  card A"
    using card_mono[OF finA] unfolding untight_def by auto
  hence "yA. gain S y  gain S (lazy_argmax_gain_fuel (card A) S A ub)"
    using lazy_argmax_gain_fuel_max[OF finA neA ubv] by blast
  thus ?thesis
    unfolding lazy_argmax_gain_def .
qed

end

end