Theory Limits_Higher_Order_Derivatives

section ‹Limits and Higher Order Derivatives›

theory Limits_Higher_Order_Derivatives
  imports "HOL-Analysis.Analysis" 
begin


subsection ‹\(\varepsilon\)--\(\delta\) Characterizations of Limits and Continuity›

lemma tendsto_at_top_epsilon_def:
  "(f  L) at_top = ( ε > 0. N. x  N. ¦(f (x::real)::real) - L¦ < ε)"
    by (simp add: Zfun_def tendsto_Zfun_iff eventually_at_top_linorder)

lemma tendsto_at_bot_epsilon_def:
  "(f  L) at_bot = ( ε > 0. N. x  N. ¦(f (x::real)::real) - L¦ < ε)"
    by (simp add: Zfun_def tendsto_Zfun_iff eventually_at_bot_linorder)

lemma tendsto_inf_at_top_epsilon_def:
  "(g  ) at_top = ( ε > 0. N. x  N. (g (x::real)::real) > ε)"
  by (subst tendsto_PInfty', subst Filter.eventually_at_top_linorder, simp)
  
lemma tendsto_inf_at_bot_epsilon_def:
  "(g  ) at_bot = ( ε > 0. N. x  N. (g (x::real)::real) > ε)"
  by (subst tendsto_PInfty', subst Filter.eventually_at_bot_linorder, simp)

lemma tendsto_minus_inf_at_top_epsilon_def:
  "(g  -) at_top = ( ε < 0. N. x  N. (g (x::real)::real) < ε)"
  by(subst tendsto_MInfty', subst Filter.eventually_at_top_linorder, simp)

lemma tendsto_minus_inf_at_bot_epsilon_def:
  "(g  -) at_bot = ( ε < 0. N. x  N. (g (x::real)::real) < ε)"
  by (subst tendsto_MInfty', subst Filter.eventually_at_bot_linorder, simp)

lemma tendsto_at_x_epsilon_def:
  fixes f :: "real  real" and L :: real and x :: real
  shows "(f  L) (at x) = (ε > 0. δ > 0. y. (y  x  ¦y - x¦ < δ)  ¦f y - L¦ < ε)"
  unfolding tendsto_def
proof (subst eventually_at, safe)
  text ‹ --- First Direction ---
  We show that the filter definition implies the \(\varepsilon\)--\(\delta\) formulation.›
  fix ε :: real
  assume lim_neigh: "S. open S  L  S  (d>0. xaUNIV. xa  x  dist xa x < d  f xa  S)"
  assume ε_pos: "0 < ε"
  show "δ>0. y. y  x  ¦y - x¦ < δ  ¦f y - L¦ < ε"
  proof -
    text ‹Choose \(S\) as the open ball around \(L\) with radius \(\varepsilon\).›

    have "open (ball L ε)" 
      by simp 
    text ‹Confirm that \(L\) lies in the ball.›
    moreover have "L  ball L ε"
      unfolding ball_def by (simp add: ε_pos)
    text ‹By applying lim\_neigh to the ball, we obtain a suitable \(\delta\).›
    ultimately obtain δ where d_pos: "δ > 0" 
      and δ_prop: "y. y  x  dist y x < δ  f y  ball L ε"
      by (meson UNIV_I lim_neigh)
    text ‹Since \(f(y)\in \mathrm{ball}(L,\varepsilon)\) means \(\lvert f(y)-L\rvert<\varepsilon\), 
          we deduce the \(\varepsilon\)–\(\delta\) condition.›
    hence "y. y  x  ¦y - x¦ < δ  ¦f y - L¦ < ε"
      by (auto simp: ball_def dist_norm)
    thus ?thesis
      using d_pos by blast
  qed
next
  text ‹--- Second Direction ---
     We show that the \(\varepsilon\)--\(\delta\) formulation implies the filter definition.›
  fix S :: "real set"
  assume eps_delta: "ε>0. δ>0. y. (y  x  ¦y - x¦ < δ)  ¦f y - L¦ < ε"
  and S_open: "open S"
  and L_in_S: "L  S"
  text ‹ Since \(S\) is open and contains \(L\), there exists an \(\varepsilon\)-ball
  around \(L\) contained in \(S\).›

  from S_open L_in_S obtain ε where ε_pos: "ε > 0" and ball_sub: "ball L ε  S"
    by (meson openE)
  text ‹Applying the \(\varepsilon\)--\(\delta\) assumption for this particular \(\varepsilon\)
  yields a \(\delta > 0\) such that for all \(y\), if \(y \neq x\) and
  \(\lvert y - x\rvert < \delta\) then \(\lvert f(y) - L\rvert < \varepsilon\).›

  from eps_delta obtain δ where δ_pos: "δ > 0" 
    and δ_prop: "y. (y  x  ¦y - x¦ < δ)  ¦f y - L¦ < ε"
    using ε_pos by blast
  text ‹ Notice that \(\lvert f(y) - L\rvert < \varepsilon\) is equivalent to
  \(f(y) \in \mathrm{ball}\,L\,\varepsilon\).›

  have "y. (y  x  dist y x < δ)  f y  ball L ε"
    using δ_prop dist_real_def by fastforce
  text ‹Since \(\mathrm{ball}(L,\varepsilon) \subseteq S\),
  for all \(y\) with \(y \neq x\) and \(\mathrm{dist}\,y\,x < \delta\), we have \(f\,y \in S\).›
  hence "y. (y  x  dist y x < δ)  f y  S"
    using ball_sub by blast
  text ‹This gives exactly the existence of some \(d\) (namely \(\delta\)) satisfying the filter condition.›

  thus "d>0. yUNIV. (y  x  dist y x < d)  f y  S"
    using δ_pos by blast
qed

lemma continuous_at_eps_delta:
  fixes g :: "real  real" and y :: real
  shows "continuous (at y) g = (ε > 0. δ > 0. x. ¦x - y¦ < δ  ¦g x - g y¦ < ε)"
proof -
  have "continuous (at y) g = (ε > 0. δ > 0. x. (x  y  ¦x - y¦ < δ)  ¦g x - g y¦ < ε)"
    by (simp add: isCont_def tendsto_at_x_epsilon_def)
  also have "... = (ε > 0. δ > 0. x. ¦x - y¦ < δ  ¦g x - g y¦ < ε)"
    by (metis abs_eq_0 diff_self)
  finally show ?thesis.
qed

lemma tendsto_divide_approaches_const:
  fixes f g :: "real  real"
  assumes f_lim:"((λx. f (x::real))  c) at_top"
      and g_lim: "((λx. g (x::real))  ) at_top"
    shows "((λx. f (x::real) / g x)  0) at_top"
proof(subst tendsto_at_top_epsilon_def, clarify)
  fix ε :: real
  assume ε_pos: "0 < ε"

  obtain M where M_def: "M = abs c + 1" and M_gt_0: "M > 0"
    by simp

  obtain N1 where N1_def: "xN1. abs (f x - c) < 1"
    using f_lim tendsto_at_top_epsilon_def zero_less_one by blast 

  have f_bound: "xN1. abs (f x) < M"
    using M_def N1_def by fastforce

  have M_over_ε_gt_0: "M / ε > 0"
    by (simp add: M_gt_0 ε_pos)

  then obtain N2 where N2_def: "xN2. g x > M / ε"
    using g_lim tendsto_inf_at_top_epsilon_def by blast

  obtain N where "N = max N1 N2" and N_ge_N1: "N  N1" and N_ge_N2: "N  N2"
    by auto 

  show "N::real. xN. ¦f x / g x - 0¦ < ε"
  proof(intro exI [where x=N], clarify)
    fix x :: real
    assume x_ge_N: " N  x"

    have f_bound_x: "¦f x¦ < M"
      using N_ge_N1 f_bound x_ge_N by auto

    have g_bound_x: "g x > M / ε"
      using N2_def N_ge_N2 x_ge_N by auto 

    have "¦f x / g x¦ = ¦f x¦ / ¦g x¦"
      using abs_divide by blast
    also have "... < M /  ¦g x¦"
      using M_over_ε_gt_0 divide_strict_right_mono f_bound_x g_bound_x by force
    also have  "... < ε"
      by (metis  M_over_ε_gt_0 ε_pos abs_real_def g_bound_x mult.commute order_less_irrefl order_less_trans pos_divide_less_eq)
    finally show "¦f x / g x - 0¦ < ε"
      by linarith
  qed      
qed

lemma tendsto_divide_approaches_const_at_bot:
  fixes f g :: "real  real"
  assumes f_lim: "((λx. f (x::real))  c) at_bot"
      and g_lim: "((λx. g (x::real))  ) at_bot"
    shows "((λx. f (x::real) / g x)  0) at_bot"
proof(subst tendsto_at_bot_epsilon_def, clarify)
  fix ε :: real
  assume ε_pos: "0 < ε"

  obtain M where M_def: "M = abs c + 1" and M_gt_0: "M > 0"
    by simp

  obtain N1 where N1_def: "xN1. abs (f x - c) < 1"
    using f_lim tendsto_at_bot_epsilon_def zero_less_one by blast 

  have f_bound: "xN1. abs (f x) < M"
    using M_def N1_def by fastforce

  have M_over_ε_gt_0: "M / ε > 0"
    by (simp add: M_gt_0 ε_pos)

  then obtain N2 where N2_def: "xN2. g x > M / ε"
    using g_lim tendsto_inf_at_bot_epsilon_def by blast

  obtain N where "N = min N1 N2" and N_le_N1: "N  N1" and N_le_N2: "N  N2"
    by auto 
    
  show "N::real. xN. ¦f x / g x - 0¦ < ε"
  proof(intro exI [where x=N], clarify)
    fix x :: real
    assume x_le_N: "x  N"

    have f_bound_x: "¦f x¦ < M"
      using N_le_N1 f_bound x_le_N by auto

    have g_bound_x: "g x > M / ε"
      using N2_def N_le_N2 x_le_N by auto 

    have "¦f x / g x¦ = ¦f x¦ / ¦g x¦"
      using abs_divide by blast
    also have "... < M /  ¦g x¦"
      using M_over_ε_gt_0 divide_strict_right_mono f_bound_x g_bound_x by force
    also have  "... < ε"
      by (metis  M_over_ε_gt_0 ε_pos abs_real_def g_bound_x mult.commute order_less_irrefl order_less_trans pos_divide_less_eq)
    finally show "¦f x / g x - 0¦ < ε"
      by linarith
  qed      
qed

lemma equal_limits_diff_zero_at_top:
  assumes f_lim: "(f  (L1::real)) at_top"
  assumes g_lim: "(g  (L2::real)) at_top"
  shows "((f - g)  (L1 - L2)) at_top"
proof -
  have "((λx. f x - g x)  L1 - L2) at_top"
    by (rule tendsto_diff, rule f_lim, rule g_lim)
  then show ?thesis 
    by (simp add: fun_diff_def)
qed

lemma equal_limits_diff_zero_at_bot:
  assumes f_lim: "(f  (L1::real)) at_bot"
  assumes g_lim: "(g  (L2::real)) at_bot"
  shows "((f - g)  (L1 - L2)) at_bot"
proof -
  have "((λx. f x - g x)  L1 - L2) at_bot"
    by (rule tendsto_diff, rule f_lim, rule g_lim)
  then show ?thesis 
    by (simp add: fun_diff_def)
qed


subsection ‹Nth Order Derivatives and $C^k(U)$ Smoothness›
fun Nth_derivative :: "nat  (real  real)  (real  real)" where
  "Nth_derivative 0 f = f " |
  "Nth_derivative (Suc n) f  = deriv (Nth_derivative n f)"

lemma first_derivative_alt_def:
  "Nth_derivative 1 f = deriv f"
  by simp

lemma second_derivative_alt_def:
  "Nth_derivative 2 f  = deriv (deriv f)"
  by (simp add: numeral_2_eq_2)

lemma limit_def_nth_deriv:
  fixes f :: "real  real" and a :: real and n :: nat
  assumes n_pos: "n > 0"
      and D_last: "DERIV (Nth_derivative (n - 1) f) a :> Nth_derivative n f a"
  shows
    "((λx. (Nth_derivative (n - 1) f x - Nth_derivative (n - 1) f a) / (x - a))
        Nth_derivative n f a) (at a)"
  using D_last has_field_derivativeD by blast

definition C_k_on :: "nat  (real  real)  real set  bool" where
  "C_k_on k f U  
     (if k = 0 then (open U  continuous_on U f)
      else (open U  (n < k. (Nth_derivative n f) differentiable_on U 
                          continuous_on U (Nth_derivative (Suc n) f))))"

lemma C0_on_def:
  "C_k_on 0 f U  (open U  continuous_on U f)"
  by (simp add: C_k_on_def)

lemma C1_cont_diff:
  assumes "C_k_on 1 f U"
  shows "f differentiable_on U  continuous_on U (deriv f)  
         ( y   U. (f has_real_derivative (deriv f) y) (at y))"
  using C_k_on_def DERIV_deriv_iff_real_differentiable assms at_within_open differentiable_on_def by fastforce

lemma C2_cont_diff:
  fixes f :: "real  real" and U :: "real set"
  assumes "C_k_on 2 f U"
  shows "f differentiable_on U  continuous_on U (deriv f)  
         (y  U. (f has_real_derivative (deriv f) y) (at y)) 
         deriv f differentiable_on U  continuous_on U (deriv (deriv f)) 
         (y  U. (deriv f has_real_derivative (deriv (deriv f)) y) (at y))"
  by (smt (verit, best) C1_cont_diff C_k_on_def Nth_derivative.simps(1,2) One_nat_def assms less_2_cases_iff less_numeral_extra(1) nat_1_add_1 order.asym pos_add_strict)

lemma C2_on_open_U_def2:
  fixes f :: "real  real"
  assumes openU : "open U"
      and diff_f : "f differentiable_on U"
      and diff_df: "deriv f differentiable_on U"
      and cont_d2f: "continuous_on U (deriv (deriv f))"
  shows "C_k_on 2 f U"
  by (simp add: C_k_on_def cont_d2f diff_df diff_f differentiable_imp_continuous_on less_2_cases_iff openU)

lemma C_k_on_subset:
  assumes "C_k_on k f U"
  assumes open_subset: "open S  S  U"  
  shows "C_k_on k f S"
  using assms
  by (smt (verit) C_k_on_def continuous_on_subset differentiable_on_eq_differentiable_at dual_order.strict_implies_order subset_eq)


definition smooth_on :: "(real  real)  real set  bool" where
  "smooth_on f U  k. C_k_on k f U"

end