Theory MDP_reward_Util

theory MDP_reward_Util
  imports Blinfun_Util
begin
section ‹Auxiliary Lemmas›

subsection ‹Summability›

lemma summable_powser_const:
  fixes c :: real 
  assumes "¦c¦ < 1"
  shows "summable (λn. c^n * x)"
  using assms
  by (auto simp: mult.commute)

subsection ‹Infinite sums›

lemma suminf_split_head': 
  "summable (f :: nat  'x :: real_normed_vector)  suminf f = f 0 + (n. f (Suc n))"
  by (auto simp: suminf_split_head)

lemma sum_disc_lim: 
  assumes "¦c :: real¦ < 1" 
  shows "(x. c^x * B) = B /(1-c)"
  by (simp add: assms suminf_geometric summable_geometric suminf_mult2[symmetric])

subsection ‹Bounded Functions›

lemma suminf_apply_bfun:
  fixes f :: "nat  'c b real"  
  assumes "summable f"
  shows "(i. f i) x = (i. f i x)"
  by (auto intro!: bounded_linear.suminf assms bounded_linear_intro[where K = 1] abs_le_norm_bfun)

lemma sum_apply_bfun:
  fixes f :: "nat  'c b real"  
  shows "(i<n. f i) x = (i<n. apply_bfun (f i) x)"
  by (induction n) auto  

subsection ‹Push-Forward of a Bounded Function›

lemma integrable_bfun_prob_space [simp]: 
  "integrable (measure_pmf P) (λt. apply_bfun f (F t) :: real)"
proof -
  obtain b where "t. ¦f (F t)¦  b"
    by (metis norm_le_norm_bfun real_norm_def)
  hence "(+ x. ennreal ¦f (F x)¦ P)  b"
    using nn_integral_mono ennreal_leI
    by (auto intro: measure_pmf.nn_integral_le_const)
  then show ?thesis
    using ennreal_less_top le_less_trans 
    by (fastforce simp: integrable_iff_bounded)
qed

lift_definition push_exp :: "('b  'c pmf)  ('c b real)  ('b b real)" is
  "λc f s. measure_pmf.expectation (c s) f"
  using bfun_integral_bound' .

declare push_exp.rep_eq[simp]

lemma norm_push_exp_le_norm: "norm (push_exp d x)  norm x"
proof -
  have "s. (s'. norm (x s') d s)  norm x"
    using measure_pmf.prob_space_axioms norm_le_norm_bfun[of x]
    by (auto intro!: prob_space.integral_le_const)
  hence aux: "s. norm (s'. x s' d s)  norm x"
    using integral_norm_bound order_trans by blast
  have "norm (push_exp d x) = (s. norm (s'. x s' d s))"
    unfolding norm_bfun_def'
    by auto
  also have "  norm x"
    using aux by (fastforce intro!: cSUP_least)
  finally show ?thesis .
qed

lemma push_exp_bounded_linear [simp]: "bounded_linear (push_exp d)"
  using norm_push_exp_le_norm
  by (auto intro!: bounded_linear_intro[where K = 1])

lemma onorm_push_exp [simp]: "onorm (push_exp d) = 1"
proof (intro antisym)
  show "onorm (push_exp d)  1"
    using norm_push_exp_le_norm 
    by (auto intro!: onorm_bound)
next
  show "1  onorm (push_exp d)"
    using onorm[of _ 1, OF push_exp_bounded_linear] 
    by (auto simp: norm_bfun_def')
qed

lemma push_exp_return[simp]: "push_exp return_pmf = id"
  by (auto simp: eq_id_iff[symmetric])

subsection ‹Boundedness›

lemma bounded_abs[intro]: 
  "bounded (X' :: real set)  bounded (abs ` X')"
  by (auto simp: bounded_iff)

lemma bounded_abs_range[intro]: 
  "bounded (range f :: real set)  bounded (range (λx. abs (f x)))"
  by (auto simp: bounded_iff)

subsection ‹Probability Theory›

lemma integral_measure_pmf_bind: 
  assumes "(x. ¦(f :: 'b  real) x¦  B)"
  shows "(x. f x ((measure_pmf M)  (λx. measure_pmf (N x)))) = (x. y. f y N x M)"
  using assms
  by (subst integral_bind[of _ "count_space UNIV" B]) (auto simp: measure_pmf_in_subprob_space)

lemma lemma_4_3_1': 
  assumes "set_pmf p  W" 
    and "bounded ((w :: 'c  real) ` W)" 
    and "W  {}"
    and "measure_pmf.expectation p w = (p  {p. set_pmf p  W}. measure_pmf.expectation p w)" 
  shows "x  W. measure_pmf.expectation p w = w x"
proof -
  have abs_w_le_sup: "y  W  ¦w y¦  (x  W. ¦w x¦)" for y
    using assms bounded_abs[OF assms(2)]
    by (auto intro!: cSUP_upper bounded_imp_bdd_above simp: image_image)
  have "False" if "x  set_pmf p" "w x < (w ` W)" for x
  proof -
    have ex_gr: "x'. x'  W  w x < w x'"
      using cSUP_least[of W w "w x"] that assms
      by fastforce
    let ?s = "λs. (if x = s then SOME x'. x'  W  w x < w x' else s)"
    have "measure_pmf.expectation p w < measure_pmf.expectation p (λxa. w (?s xa))"
    proof (intro measure_pmf.integral_less_AE[where A = "{x}"])
      show "integrable (measure_pmf p) w"
        using assms abs_w_le_sup 
        by (fastforce simp: AE_measure_pmf_iff 
            intro!: measure_pmf.integrable_const_bound)
      show "integrable (measure_pmf p) (λxa. w (?s xa))"
        using assms(1) ex_gr someI[where P = "λx'. (x'  W)  (w x < w x')"]
        by (fastforce simp: AE_measure_pmf_iff 
            intro!: abs_w_le_sup measure_pmf.integrable_const_bound)
      show "emeasure (measure_pmf p) {x}  0"
        by (simp add: emeasure_pmf_single_eq_zero_iff x  p)
      show "{x}  measure_pmf.events p"
        by auto
      show "AE xa{x} in p. w xa  w (?s xa)" "AE xa in p. w xa  w (?s xa)"
        using someI[where P = "λx'. (x'  W)  (w x < w x')"] ex_gr
        by (fastforce intro!: AE_pmfI)+
    qed
    hence "measure_pmf.expectation p w < ((λp. measure_pmf.expectation p w) ` {p. set_pmf p  W})"
    proof (subst less_cSUP_iff, goal_cases)
      case 1
      then show ?case 
        using assms(1) 
        by blast
    next
      case 2
      then show ?case 
        using abs_w_le_sup
        by (fastforce 
            simp: AE_measure_pmf_iff 
            intro: cSUP_upper2 bdd_aboveI[where M = "(xW. ¦w x¦)"]
            intro!: measure_pmf.integral_le_const measure_pmf.integrable_const_bound)
    next
      case 3
      then show ?case 
        using ex_gr someI[where P = "λx'. (x'  W)  (w x < w x')"] assms(1) 
        by (auto intro!: exI[of _ "map_pmf ?s p"])
    qed
    thus False
      using assms by auto
  qed
  hence 1: "x  set_pmf p  w x =  (w ` W)" for x
    using assms
    by (fastforce intro: antisym simp: bounded_imp_bdd_above cSUP_upper)
  hence "w (SOME x. x  set_pmf p) =   (w ` W)"
    by (simp add: set_pmf_not_empty some_in_eq)
  thus ?thesis
    using 1 assms(1) set_pmf_not_empty some_in_eq 
    by (fastforce intro!: bexI[of _ "SOME x. x  set_pmf p"]
        simp: AE_measure_pmf_iff Bochner_Integration.integral_cong_AE[where ?g = "λ_.  (w ` W)"])
qed

lemma lemma_4_3_1: 
  assumes "set_pmf p  W" "integrable (measure_pmf p) w" "bounded ((w :: 'c  real) ` W)"
  shows "measure_pmf.expectation p w  (w ` W)"
  using assms bounded_has_Sup(1) prob_space_measure_pmf
  by (fastforce simp: AE_measure_pmf_iff intro!: prob_space.integral_le_const)

lemma bounded_integrable: 
  assumes "bounded (range v)" "v  borel_measurable (measure_pmf p)" 
  shows "integrable (measure_pmf p) (v :: 'c  real)"
  using assms
  by (auto simp: bounded_iff AE_measure_pmf_iff intro!: measure_pmf.integrable_const_bound)

subsection ‹Argmax›
lemma finite_is_arg_max: "finite X  X  {}  x. is_arg_max (f :: 'c  real) (λx. x  X) x"
  unfolding is_arg_max_def
proof (induction rule: finite_induct)
  case (insert x F)
  then show ?case
  proof (cases "y  F. f y  f x")
    case True
    then show ?thesis
      by (auto intro!: exI[of _ x])
  next
    case False
    then show ?thesis
      using insert by force
  qed
qed simp

lemma finite_arg_max_le:
  assumes "finite (X :: 'c set)" "X  {}"
  shows "s  X  (f :: 'c  real) s  f (arg_max_on (f :: 'c  real) X)"
  unfolding arg_max_def arg_max_on_def
  by (metis assms(1) assms(2) finite_is_arg_max is_arg_max_linorder someI_ex)

lemma arg_max_on_in:
  assumes "finite (X :: 'c set)" "X  {}"
  shows "(arg_max_on (f :: 'c  real)  X)  X"
  unfolding arg_max_on_def arg_max_def
  by (metis assms(1) assms(2) finite_is_arg_max is_arg_max_def someI)

lemma finite_arg_max_eq_Max:
  assumes "finite (X :: 'c set)" "X  {}"
  shows "(f :: 'c  real) (arg_max_on f X) = Max (f ` X)"
  using assms
  by (auto intro!: Max_eqI[symmetric] finite_arg_max_le arg_max_on_in)


lemma arg_max_SUP: "is_arg_max (f :: 'b  real) (λx. x  X) m  f m = ((f ` X))"
  unfolding is_arg_max_def
  by (auto intro!: antisym cSUP_upper bdd_aboveI[of _ "f m"] cSUP_least)

definition "has_max X  x  X. x'  X. x'  x"
definition "has_arg_max f X  x. is_arg_max f (λx. x  X) x"

lemma "has_max ((f :: 'b  real) ` X)  has_arg_max f X"
  unfolding has_max_def has_arg_max_def is_arg_max_def
  using not_less by (auto dest!: leD simp: not_less)

lemma has_arg_max_is_arg_max: "has_arg_max f X  is_arg_max f (λx. x  X) (arg_max f (λx. x  X))"
  unfolding has_arg_max_def arg_max_def
  by (auto intro: someI)

lemma has_arg_max_arg_max: "has_arg_max f X  (arg_max f (λx. x  X))  X"
  unfolding has_arg_max_def arg_max_def is_arg_max_def by (auto intro: someI2_ex)
  

lemma app_arg_max_ge: "has_arg_max (f :: 'b  real) X  x  X  f x  f (arg_max_on f X)"
  unfolding has_arg_max_def arg_max_on_def arg_max_def is_arg_max_def
  using someI[where ?P = "λx. x  X  (y. y  X  f x < f y)"] le_less_linear
  by auto

lemma app_arg_max_eq_SUP: "has_arg_max (f :: 'b  real) X  f (arg_max_on f X) = (f ` X)"
  by (simp add: arg_max_SUP arg_max_on_def has_arg_max_is_arg_max)

lemma SUP_is_arg_max: 
  assumes "x  X" "bdd_above (f ` X)" "(f :: 'c  real) x = (f ` X)" 
  shows "is_arg_max f (λx. x  X) x"
  unfolding is_arg_max_def
  using not_less assms cSUP_upper[of _ X f] 
  by auto

lemma is_arg_max_linorderI[intro]: fixes f :: "'c  'b :: linorder"
  assumes "P x" "y. (P y  f x  f y)"
  shows "is_arg_max f P x"
  using assms by (auto simp: is_arg_max_linorder)

lemma is_arg_max_linorderD[dest]: fixes f :: "'c  'b :: linorder"
  assumes "is_arg_max f P x"
  shows "P x" "(P y  f x  f y)"
  using assms by (auto simp: is_arg_max_linorder)

lemma is_arg_max_cong: 
  assumes "x. P x  f x = g x"
  shows "is_arg_max f P x  is_arg_max g P x"
  unfolding is_arg_max_def using assms by auto

lemma is_arg_max_cong':
  assumes "x. P x  f x = g x"
  shows "is_arg_max f P = is_arg_max g P"
  using assms by (auto cong: is_arg_max_cong)

lemma is_arg_max_congI: 
  assumes "is_arg_max f P x" "x. P x  f x = g x" 
  shows "is_arg_max g P x" 
  using is_arg_max_cong assms by force

subsection ‹Contraction Mappings›

definition "is_contraction C  l. 0  l  l < 1  (v u. dist (C v) (C u)  l * dist v u)"

lemma banach':
  fixes C :: "'b :: complete_space  'b"
  assumes "is_contraction C"
  shows "∃!v. C v = v" "v. (λn. (C ^^ n) v)  (THE v. C v = v)"
proof -
  obtain v where C: "C v = v" "v'. C v' = v'  v' = v" 
    by (metis assms is_contraction_def banach_fix_type)
  obtain l where cont: "dist (C v) (C u)  l * dist v u" "0  l" "l < 1" for v u
    using assms is_contraction_def by blast
  have *: "n v0. dist ((C ^^ n) v0) v  l ^ n * dist v0 v"
  proof -
    fix n v0
    show "dist ((C ^^ n) v0) v  l ^ n * dist v0 v"
    proof (induction n)
      case (Suc n)
      thus "dist ((C ^^ Suc n) v0) v  l ^ Suc n * dist v0 v"
        using 0  l
        by (subst C(1)[symmetric]) (auto simp: algebra_simps intro!: order_trans[OF cont(1)] mult_left_mono)
    qed simp
  qed
  have "(λn. l ^ n)  0"
    by (simp add: LIMSEQ_realpow_zero 0  l l < 1)
  hence "v0. (λn. l ^ n * dist v0 v)  0"
    by (simp add: tendsto_mult_left_zero)
  hence "(λn. dist ((C ^^ n) v0) v)  0" for v0
      using * order_trans abs_ge_self
      by (subst Limits.tendsto_0_le[of "(λn. l ^ n * dist v0 v)" _ _ 1]) 
        (fastforce intro!: eventuallyI)+
  hence "v0. (λn. (C ^^ n) v0)  v"
    using tendsto_dist_iff by blast
  thus "v0. (λn. (C ^^ n) v0)  (THE v. C v = v)"
    by (metis (mono_tags, lifting) C theI')
next
  show "∃!v. C v = v"
    using assms banach_fix_type unfolding is_contraction_def by blast
qed

lemma contraction_dist:
  fixes C :: "'b :: complete_space  'b"
  assumes "v u. dist (C v) (C u)  c  * dist v u"
  assumes "0  c" "c < 1"
  shows "(1 - c) * dist v (THE v. C v = v)  dist v (C v)"
proof -
  have "is_contraction C"
    unfolding is_contraction_def using assms by auto
  then obtain v_fix where v_fix: "v_fix = (THE v. C v = v)"
    using the1_equality by blast
  hence "(λn. (C ^^ n) v)  v_fix"
    using banach'[OF is_contraction C] by simp
  have dist_contr_le_pow: "n. dist ((C ^^ n) v) ((C ^^ Suc n) v)  c ^ n * dist v (C v)"
  proof -
    fix n 
    show "dist ((C ^^ n) v) ((C ^^ Suc n) v)  c ^ n * dist v (C v)"
      using assms
      by (induction n) (auto simp: algebra_simps intro!: order.trans[OF assms(1)] mult_left_mono)
  qed
  have summable_C: "summable (λi. dist ((C ^^ i) v) ((C ^^ Suc i) v))"
        using dist_contr_le_pow assms summable_powser_const
        by (intro summable_comparison_test[of "(λi. dist ((C ^^ i) v) ((C ^^ Suc i) v))" "λi. c^i * dist v (C v)"]) 
          auto
  have "e > 0. dist v v_fix  (i. dist ((C ^^ i) v) ((C ^^ (Suc i)) v)) + e"
  proof safe
    fix e ::real assume "0 < e"
    have "F n in sequentially. dist ((C^^n) v) v_fix < e"
      using (λn. (C ^^ n) v)  v_fix 0 < e tendsto_iff by force
    then obtain N where "dist ((C^^N) v) v_fix < e"
      by fastforce
    hence *: "dist v v_fix  dist v ((C^^N) v) + e"
      by (metis add_le_cancel_left dist_commute dist_triangle_le less_eq_real_def)
    have "dist v ((C^^N) v)  (iN. dist ((C ^^ i) v) ((C ^^ (Suc i)) v))"
    proof (induction N arbitrary: v)
      case 0
      then show ?case by simp
    next
      case (Suc N)      
      have "dist v ((C ^^ Suc N) v)  dist v (C v) + dist (C v) ((C^^(Suc N)) v)"
        by metric
      also have " =  dist v (C v) + dist (C v) ((C^^N) (C v))"
        by (metis funpow_simps_right(2) o_def)
      also have "  dist v (C v) + (iN. dist ((C ^^ i) (C v)) ((C ^^ Suc i) (C v)))"
        using Suc.IH add_le_cancel_left by blast
      also have "  dist v (C v) + (iN. dist ((C ^^Suc i) v) ((C ^^ (Suc (Suc i))) v))"
        by (simp only: funpow_simps_right(2) o_def)
      also have "  (iSuc N. dist ((C ^^ i) v) ((C ^^ (Suc i)) v))"
        by (subst sum.atMost_Suc_shift) simp
      finally show "dist v ((C ^^ Suc N) v)  (iSuc N. dist ((C ^^ i) v) ((C ^^ Suc i) v))" .
    qed
    moreover have 
      "(iN. dist ((C ^^ i) v) ((C ^^ Suc i) v))  (i. dist ((C ^^ i) v) ((C ^^ (Suc i)) v))"
      using summable_C 
      by (auto intro: sum_le_suminf)
    ultimately have "dist v ((C^^N) v)  (i. dist ((C ^^ i) v) ((C ^^ (Suc i)) v))"
      by linarith
    thus "dist v v_fix  (i. dist ((C ^^ i) v) ((C ^^ Suc i) v)) + e"
      using * by fastforce
  qed
  hence le_suminf: "dist v v_fix  (i. dist ((C ^^ i) v) ((C ^^ Suc i) v))"
    using field_le_epsilon by blast
  have "dist v v_fix  (i. c ^ i * dist v (C v))"
    using dist_contr_le_pow summable_C assms summable_powser_const
    by (auto intro!: order_trans[OF le_suminf] suminf_le)
  hence "dist v v_fix  dist v (C v) / (1 - c)"
    using sum_disc_lim
    by (metis sum_disc_lim abs_of_nonneg assms(2) assms(3))
  hence "(1 - c) * dist v v_fix  dist v (C v)"
    using assms(3) mult.commute pos_le_divide_eq 
    by (metis diff_gt_0_iff_gt)
  thus ?thesis
    using v_fix by blast
qed

subsection ‹Limits›
lemma tendsto_bfun_sandwich:
  assumes 
    "(f :: nat  'b b real)  x" "(g :: nat  'b b real)  x" 
    "eventually (λn. f n  h n) sequentially" "eventually (λn. h n  g n) sequentially"
  shows "(h :: nat  'b b real)  x"
proof -
  have 1: "(λn.  dist (f n) (g n) + dist (g n) x)  0"
    using tendsto_dist[OF assms(1) assms(2)] tendsto_dist_iff assms
    by (auto intro!: tendsto_add_zero)
  have "eventually (λn. dist (h n) (g n)  dist (f n) (g n)) sequentially"    
    using assms(3) assms(4)
  proof eventually_elim
    case (elim n)
    hence "dist (h n a) (g n a)  dist (f n a) (g n a)" for a
    proof - 
      have "f n a  h n a" "h n a  g n a"
        using elim unfolding less_eq_bfun_def by auto
      thus ?thesis
        using dist_real_def by fastforce
    qed
    thus ?case
      unfolding dist_bfun.rep_eq
      by (auto intro!: cSUP_mono bounded_imp_bdd_above simp: dist_real_def bounded_minus_comp bounded_abs_range)
  qed
  moreover have "eventually (λn. dist (h n) x  dist (h n) (g n) + dist (g n) x) sequentially"
    by (simp add: dist_triangle)
  ultimately have 2: "eventually (λn. dist (h n) x  dist (f n) (g n) + dist (g n) x) sequentially"
    using eventually_elim2 by fastforce
  have "(λn. dist (h n) x)  0"
  proof (subst tendsto_iff, safe)
    fix e ::real
    assume "e > 0"
    hence 3: "F xa in sequentially. dist (f xa) (g xa) + dist (g xa) x < e"
      using 1 
      by (auto simp: tendsto_iff)
    show "F xa in sequentially. dist (dist (h xa) x) 0 < e"
      by (rule eventually_mp[OF _ 3]) (fastforce intro: 2 eventually_mono)
  qed
  thus ?thesis
    using tendsto_dist_iff
    by auto
qed

subsection ‹Supremum›

lemma SUP_add_le: 
  assumes "X  {}" "bounded (B ` X)" "bounded (A' ` X)" 
  shows "(c  X. (B :: 'a  real) c + A' c)  (b  X. B b) + (a  X. A' a)"
  using assms 
  by (auto simp: add_mono bounded_has_Sup(1) intro!: cSUP_least)+

lemma le_SUP_diff':
  assumes ne: "X  {}"
    and bdd: "bounded (B ` X)" "bounded (A' ` X)" 
    and sup_le: "(a  X. (A' :: 'a  real) a)  (b  X. B b)"
  shows  "(b  X. B b) - (a  X. (A' :: 'a  real) a)  (c  X. B c - A' c)"
proof -
  have "bounded ((λx. (B x - A' x)) ` X)"
    using bdd bounded_minus_comp by blast
  have "(b  X. B b) - (a  X. A' a) - e  (c  X. B c - A' c)" if e: "e > 0" for e
  proof -
    obtain z where z: "(b  X. B b) - e B z" "z  X"
       using e ne
       by (subst less_cSupE[where ?y = " (B ` X) - e", where ?X = "B`X"]) fastforce+
    hence " ((a  X. A' a)  B z + e)"
      using sup_le
      by force
    hence "A' z  B z + e"
      using z  X bdd bounded_has_Sup(1) by fastforce
    thus "(b  X. B b) - (a  X. A' a) -e  (c  X. B c - A' c)"
      using bounded ((λx. B x - A' x) ` X) z bounded_has_Sup(1)[OF bdd(2)]
      by (subst cSUP_upper2[where x = z]) (fastforce intro!: bounded_imp_bdd_above)+
  qed
  thus ?thesis
    by (subst field_le_epsilon) fastforce+
qed

lemma le_SUP_diff:
  fixes A' :: "'a  real"
  assumes "X  {}" "bounded (B ` X)" "bounded (A' ` X)" "(a  X. A' a)  (b  X. B b)"
  shows  "0  (c  X. B c - A' c)"
  using assms
  by (auto intro!: order.trans[OF _ le_SUP_diff'])

lemma bounded_SUP_mul[simp]: 
  "X  {}  0  l  bounded (f ` X)  (x  X. (l :: real) * f x) = (l * (x  X. f x))" 
proof -
  assume "X  {} "" bounded (f ` X)" "0  l"
  have "(x  X. ereal l * ereal (f x)) = (l * (x  X. ereal (f x)))"
    by (simp add: Sup_ereal_mult_left' 0  l X  {})
  obtain b where "a X. ¦f a¦  b"
    using bounded (f ` X) bounded_real by auto
  have "a X. ¦ereal (f a)¦  b"
    by (simp add: aX. ¦f a¦  b)
  hence sup_leb: "(aX. ¦ereal (f a)¦) b"
    by (simp add: SUP_least)
  have "(aX. ereal (f a))  (aX. ¦ereal (f a)¦)"
    by (auto intro: Complete_Lattices.SUP_mono')
  moreover have "-(aX. ereal (f a))  (aX. ¦ereal (f a)¦)"
    using X  {}
    by (auto intro!: Inf_less_eq cSUP_upper2 simp add: ereal_INF_uminus_eq[symmetric])
  ultimately have "¦(aX. ereal (f a))¦  (aX. ¦ereal (f a)¦)"
    by (auto intro: ereal_abs_leI)
  hence "¦aX. ereal (f a)¦  b"
    using sup_leb by auto
  hence "¦aX. ereal (f a)¦  "
    by auto
  hence "(x  X. ereal (f x)) = ereal (x  X. (f x))"
    using ereal_SUP by metis
  hence "(x  X. ereal (l * f x)) = ereal (l * (x  X. f x))"
    using (xX. ereal l * ereal (f x)) = ereal l * (xX. ereal (f x)) by auto
  hence "ereal (x  X. l * f x) = ereal (l * (x  X. f x))"
    by (simp add: ereal_SUP)
  thus ?thesis
    by auto
qed

lemma abs_cSUP_le[intro]: 
  "X  {}  bounded (F ` X)  ¦x  X. (F x) :: real¦  (x  X. ¦F x¦)"
  by (auto intro!: cSup_abs_le cSUP_upper2 bounded_imp_bdd_above simp: image_image[symmetric])

section ‹Least argmax›
definition "least_arg_max f P = (LEAST x. is_arg_max f P x)"

lemma least_arg_max_prop: "x::'a::wellorder. P x  finite {x. P x}  P (least_arg_max (f :: _  real) P)"
  unfolding least_arg_max_def
  apply (rule LeastI2_ex)
  using finite_is_arg_max[of "{x. P x}", where f = f]
  by auto

lemma is_arg_max_apply_eq: "is_arg_max (f :: _  _ :: linorder)  P x  is_arg_max f P y  f x = f y"
  by (auto simp: is_arg_max_def not_less dual_order.eq_iff)
  
lemma least_arg_max_apply:
  assumes "is_arg_max (f :: _  _ :: linorder) P (x::_::wellorder)" 
  shows "f (least_arg_max f P) = f x"
proof -
  have "is_arg_max f P (least_arg_max f P)"
    by (metis LeastI_ex assms least_arg_max_def)
  thus ?thesis
    using is_arg_max_apply_eq assms by metis
qed

lemma apply_arg_max_eq_max: "finite {x . P x}  is_arg_max (f :: _  _ :: linorder) P x  f x = Max (f ` {x. P x})"
  by (auto simp: is_arg_max_def intro!: Max_eqI[symmetric])

lemma apply_arg_max_eq_max': "finite X is_arg_max (f :: _  _ :: linorder) (λx. x  X) x  (MAX x  X. f x) = f x"
  by (auto simp: is_arg_max_linorder intro!: Max_eqI)

lemma least_arg_max_is_arg_max: "P  {}  finite P  is_arg_max f (λx::_::wellorder. x  P) (least_arg_max (f :: _  real) (λx. x  P))"
  unfolding least_arg_max_def
  apply (rule LeastI_ex)
  using finite_is_arg_max 
  by auto

lemma is_arg_max_const: "is_arg_max (f :: _  _ :: linorder) (λy. y = c) x  x = c"
  unfolding is_arg_max_def
  by auto

lemma least_arg_max_cong': 
  assumes "x. is_arg_max f P x = is_arg_max g P x"
  shows "least_arg_max f P = least_arg_max g P"
  unfolding least_arg_max_def using assms by metis

end