Theory MTX_Flows

(*  Title:       Affine systems of ODEs
    Author:      Jonathan Julián Huerta y Munive, 2020
    Maintainer:  Jonathan Julián Huerta y Munive <jonjulian23@gmail.com>
*)

section ‹ Affine systems of ODEs ›

text ‹Affine systems of ordinary differential equations (ODEs) are those whose vector 
fields are linear operators. Broadly speaking, if there are functions $A$ and $B$ such that the 
system of ODEs $X'\, t = f\, (X\, t)$ turns into $X'\, t = (A\, t)\cdot (X\, t)+(B\, t)$, then it
is affine. The end goal of this section is to prove that every affine system of ODEs has a unique 
solution, and to obtain a characterization of said solution. ›

theory MTX_Flows
  imports 
    SQ_MTX 
    Hybrid_Systems_VCs.HS_ODEs

begin


subsection ‹ Existence and uniqueness for affine systems ›

definition matrix_continuous_on :: "real set  (real  ('a::real_normed_algebra_1)^'n^'m)  bool" 
  where "matrix_continuous_on T A = (t  T. ε > 0.  δ > 0. τT. ¦τ - t¦ < δ  A τ - A top  ε)"

lemma continuous_on_matrix_vector_multl:
  assumes "matrix_continuous_on T A"
  shows "continuous_on T (λt. A t *v s)"
proof(rule continuous_onI, simp add: dist_norm)
  fix e t::real assume "0 < e" and "t  T"
  let  = "e/((if s = 0 then 1 else s))"
  have " > 0"
    using 0 < e by simp 
  then obtain δ where dHyp: "δ > 0  (τT. ¦τ - t¦ < δ  A τ - A top  )"
    using assms t  T unfolding dist_norm matrix_continuous_on_def by fastforce
  {fix τ assume "τ  T" and "¦τ - t¦ < δ"
    have obs: " * (s) = (if s = 0 then 0 else e)"
      by auto
    have "A τ *v s - A t *v s = (A τ - A t) *v s"
      by (simp add: matrix_vector_mult_diff_rdistrib)      
    also have "...  (A τ - A top) * (s)"
      using norm_matrix_le_mult_op_norm by blast
    also have "...   * (s)"
      using dHyp τ  T ¦τ - t¦ < δ mult_right_mono norm_ge_zero by blast 
    finally have "A τ *v s - A t *v s  e"
      by (subst (asm) obs) (metis (mono_tags) 0 < e less_eq_real_def order_trans)}
  thus "d>0. τT. ¦τ - t¦ < d  A τ *v s - A t *v s  e"
    using dHyp by blast
qed

lemma lipschitz_cond_affine:
  fixes A :: "real  'a::real_normed_algebra_1^'n^'m" and T::"real set"
  defines "L  Sup {A top |t. t  T}"
  assumes "t  T" and "bdd_above {A top |t. t  T}"
  shows "A t *v x - A t *v y  L * (x - y)"
proof-
  have obs: "A top  Sup {A top |t. t  T}"
    apply(rule cSup_upper)
    using continuous_on_subset assms by (auto simp: dist_norm)
  have "A t *v x - A t *v y = A t *v (x - y)"
    by (simp add: matrix_vector_mult_diff_distrib)
  also have "...  (A top) * (x - y)"
    using norm_matrix_le_mult_op_norm by blast
  also have "...  Sup {A top |t. t  T} * (x - y)"
    using obs mult_right_mono norm_ge_zero by blast 
  finally show "A t *v x - A t *v y  L * (x - y)"
    unfolding assms .
qed

lemma local_lipschitz_affine:
  fixes A :: "real  'a::real_normed_algebra_1^'n^'m"
  assumes "open T" and "open S" 
    and Ahyp: "τ ε. ε > 0  τ  T  cball τ ε  T  bdd_above {A top |t. t  cball τ ε}"
  shows "local_lipschitz T S (λt s. A t *v s + B t)"
proof(unfold local_lipschitz_def lipschitz_on_def, clarsimp)
  fix s t assume "s  S" and "t  T"
  then obtain e1 e2 where "cball t e1  T" and "cball s e2  S" and "min e1 e2 > 0"
    using open_cballE[OF _ open T] open_cballE[OF _ open S] by force
  hence obs: "cball t (min e1 e2)  T"
    by auto
  let ?L = "Sup {A τop |τ. τ  cball t (min e1 e2)}"
  have "A top  {A τop |τ. τ  cball t (min e1 e2)}"
    using min e1 e2 > 0 by auto
  moreover have bdd: "bdd_above {A τop |τ. τ  cball t (min e1 e2)}"
    by (rule Ahyp, simp only: min e1 e2 > 0, simp_all add: t  T obs)
  moreover have "Sup {A τop |τ. τ  cball t (min e1 e2)}  0"
    apply(rule order.trans[OF op_norm_ge_0[of "A t"]])
    by (rule cSup_upper[OF calculation])
  moreover have "xcball s (min e1 e2)  S. ycball s (min e1 e2)  S. 
    τcball t (min e1 e2)  T. dist (A τ *v x) (A τ *v y)  ?L * dist x y"
    apply(clarify, simp only: dist_norm, rule lipschitz_cond_affine)
    using min e1 e2 > 0 bdd by auto
  ultimately show "e>0. L. tcball t e  T. 0  L  
    (xcball s e  S. ycball s e  S. dist (A t *v x) (A t *v y)  L * dist x y)"
    using min e1 e2 > 0 by blast
qed

lemma picard_lindeloef_affine:
  fixes A :: "real  'a::{banach,real_normed_algebra_1,heine_borel}^'n^'n"
  assumes Ahyp: "matrix_continuous_on T A"
      and "τ ε. τ  T  ε > 0  bdd_above {A top |t. dist τ t  ε}"
      and Bhyp: "continuous_on T B" and "open S" 
      and "t0  T" and Thyp: "open T" "is_interval T" 
    shows "picard_lindeloef (λ t s. A t *v s + B t) T S t0"
  apply (unfold_locales, simp_all add: assms, clarsimp)
   apply (rule continuous_on_add[OF continuous_on_matrix_vector_multl[OF Ahyp] Bhyp])
  by (rule local_lipschitz_affine) (simp_all add: assms)

lemma picard_lindeloef_autonomous_affine: 
  fixes A :: "'a::{banach,real_normed_field,heine_borel}^'n^'n"
  shows "picard_lindeloef (λ t s. A *v s + B) UNIV UNIV t0"
  using picard_lindeloef_affine[of _ "λt. A" "λt. B"] 
  unfolding matrix_continuous_on_def by (simp only: diff_self op_norm0, auto)

lemma picard_lindeloef_autonomous_linear:
  fixes A :: "'a::{banach,real_normed_field,heine_borel}^'n^'n"
  shows "picard_lindeloef (λ t. (*v) A) UNIV UNIV t0"
  using picard_lindeloef_autonomous_affine[of A 0] by force

lemmas unique_sol_autonomous_affine = picard_lindeloef.ivp_unique_solution[OF 
    picard_lindeloef_autonomous_affine UNIV_I _ subset_UNIV]

lemmas unique_sol_autonomous_linear = picard_lindeloef.ivp_unique_solution[OF 
    picard_lindeloef_autonomous_linear UNIV_I _ subset_UNIV]


subsection ‹ Flow for affine systems ›


subsubsection ‹ Derivative rules for square matrices ›

declare has_derivative_component [simp del]

lemma has_derivative_exp_scaleRl[derivative_intros]:
  fixes f::"real  real" (* by Fabian Immler and Johannes Hölzl *)
  assumes "D f  f' at t within T"
  shows "D (λt. exp (f t *R A))  (λh. f' h *R (exp (f t *R A) * A)) at t within T"
proof -
  have "bounded_linear f'" 
    using assms by auto
  then obtain m where obs: "f' = (λh. h * m)"
    using real_bounded_linear by blast
  thus ?thesis
    using vector_diff_chain_within[OF _ exp_scaleR_has_vector_derivative_right] 
      assms obs by (auto simp: has_vector_derivative_def comp_def)
qed

lemma vderiv_on_exp_scaleRlI[poly_derivatives]:
  assumes "D f = f' on T" and "g' = (λx. f' x *R exp (f x *R A) * A)"
  shows "D (λx. exp (f x *R A)) = g' on T"
  using assms unfolding has_vderiv_on_def has_vector_derivative_def apply clarsimp
  by (rule has_derivative_exp_scaleRl, auto simp: fun_eq_iff)

lemma has_derivative_mtx_ith[derivative_intros]:
  fixes t::real and T :: "real set"
  defines "t0  netlimit (at t within T)"
  assumes "D A  (λh. h *R A' t) at t within T"
  shows "D (λt. A t $$ i)  (λh. h *R A' t $$ i) at t within T"
  using assms unfolding has_derivative_def apply safe
   apply(force simp: bounded_linear_def bounded_linear_axioms_def)
  apply(rule_tac F="λτ. (A τ - A t0 - (τ - t0) *R A' t) /R (τ - t0)" in tendsto_zero_norm_bound)
  by (clarsimp, rule mult_left_mono, metis (no_types, lifting) norm_column_le_norm 
      sq_mtx_minus_ith sq_mtx_scaleR_ith) simp_all

lemmas has_derivative_mtx_vec_mult[derivative_intros] = 
  bounded_bilinear.FDERIV[OF bounded_bilinear_sq_mtx_vec_mult]

lemma vderiv_on_mtx_vec_multI[poly_derivatives]:
  assumes "D u = u' on T" and "D A = A' on T"
      and "g = (λt. A t *V u' t + A' t *V u t )"
    shows "D (λt. A t *V u t) = g on T"
  using assms unfolding has_vderiv_on_def has_vector_derivative_def apply clarify
  apply(erule_tac x=x in ballE, simp_all)+
  apply(rule derivative_eq_intros)
  by (auto simp: fun_eq_iff mtx_vec_scaleR_commute pth_6 scaleR_mtx_vec_assoc)

lemmas has_vderiv_on_ivl_integral = ivl_integral_has_vderiv_on[OF vderiv_on_continuous_on]

declare has_vderiv_on_ivl_integral [poly_derivatives]

lemma has_derivative_mtx_vec_multl[derivative_intros]:
  assumes " i j. D (λt. (A t) $$ i $ j)  (λτ. τ *R (A' t) $$ i $ j) (at t within T)"
  shows "D (λt. A t *V x)  (λτ. τ *R (A' t) *V x) at t within T"
  unfolding sq_mtx_vec_mult_sum_cols
  apply(rule_tac f'1="λi τ. τ *R  (x $ i *R 𝖼𝗈𝗅 i (A' t))" in derivative_eq_intros(10))
   apply(simp_all add: scaleR_right.sum)
  apply(rule_tac g'1="λτ. τ *R 𝖼𝗈𝗅 i (A' t)" in derivative_eq_intros(4), simp_all add: mult.commute)
  using assms unfolding sq_mtx_col_def column_def 
  by (transfer, simp add: has_derivative_component)

declare has_derivative_component [simp]

lemma continuous_on_mtx_vec_multr: "continuous_on S ((*V) A)"
  by transfer (simp add: matrix_vector_mult_linear_continuous_on)

text ‹Isabelle automatically generates derivative rules from this subsubsection ›

thm derivative_eq_intros(140-)


subsubsection ‹ Existence and uniqueness with square matrices ›

text ‹Finally, we can use the @{term exp} operation to characterize the general solutions for affine 
systems of ODEs. We show that they satisfy the @{term "local_flow"} locale.›

lemma continuous_on_sq_mtx_vec_multl:
  fixes A :: "real  ('n::finite) sq_mtx"
  assumes "continuous_on T A"
  shows "continuous_on T (λt. A t *V s)"
proof-
  have "matrix_continuous_on T (λt. to_vec (A t))"
    using assms by (force simp: continuous_on_iff dist_norm norm_sq_mtx_def matrix_continuous_on_def)
  hence "continuous_on T (λt. to_vec (A t) *v s)"
    by (rule continuous_on_matrix_vector_multl)
  thus ?thesis
    by transfer
qed

lemmas continuous_on_affine = continuous_on_add[OF continuous_on_sq_mtx_vec_multl]

lemma local_lipschitz_sq_mtx_affine:
  fixes A :: "real  ('n::finite) sq_mtx"
  assumes "continuous_on T A" "open T" "open S"
  shows "local_lipschitz T S (λt s. A t *V s + B t)"
proof-
  have obs: "τ ε. 0 < ε   τ  T  cball τ ε  T  bdd_above {A t |t. t  cball τ ε}"
    by (rule bdd_above_norm_cont_comp, rule continuous_on_subset[OF assms(1)], simp_all)
  hence "τ ε. 0 < ε  τ  T  cball τ ε  T  bdd_above {to_vec (A t)op |t. t  cball τ ε}"
    by (simp add: norm_sq_mtx_def)
  hence "local_lipschitz T S (λt s. to_vec (A t) *v s + B t)"
    using local_lipschitz_affine[OF assms(2,3), of "λt. to_vec (A t)"] by force
  thus ?thesis
    by transfer 
qed

lemma picard_lindeloef_sq_mtx_affine:
  assumes "continuous_on T A" and "continuous_on T B" 
    and "t0  T" "is_interval T" "open T" and "open S"
  shows "picard_lindeloef (λt s. A t *V s + B t) T S t0"
  apply(unfold_locales, simp_all add: assms, clarsimp)
  using continuous_on_affine assms apply blast
  by (rule local_lipschitz_sq_mtx_affine, simp_all add: assms)

lemmas sq_mtx_unique_sol_autonomous_affine = picard_lindeloef.ivp_unique_solution[OF 
    picard_lindeloef_sq_mtx_affine[OF
      continuous_on_const 
      continuous_on_const 
      UNIV_I is_interval_univ 
      open_UNIV open_UNIV] 
    UNIV_I _ subset_UNIV]

lemma has_vderiv_on_sq_mtx_linear:
  "D (λt. exp ((t - t0) *R A) *V s) = (λt. A *V (exp ((t - t0) *R A) *V s)) on {t0--t}"
  by (rule poly_derivatives)+ (auto simp: exp_times_scaleR_commute sq_mtx_times_vec_assoc)

lemma has_vderiv_on_sq_mtx_affine:
  fixes t0::real and A :: "('a::finite) sq_mtx"
  defines "lSol c t  exp ((c * (t - t0)) *R A)"
  shows "D (λt. lSol 1 t *V s + lSol 1 t *V (∫⇩t0t (lSol (-1) τ *V B) τ)) = 
  (λt. A *V (lSol 1 t *V s + lSol 1 t *V (∫⇩t0t (lSol (-1) τ *V B) τ)) + B) on {t0--t}"
  unfolding assms apply(simp only: mult.left_neutral mult_minus1)
  apply(rule poly_derivatives, (force)?, (force)?, (force)?, (force)?)+
  by (simp add: mtx_vec_mult_add_rdistl sq_mtx_times_vec_assoc[symmetric] 
      exp_minus_inverse exp_times_scaleR_commute mult_exp_exp  scale_left_distrib[symmetric])

lemma autonomous_linear_sol_is_exp:
  assumes "D X = (λt. A *V X t) on {t0--t}" and "X t0 = s" 
  shows "X t = exp ((t - t0) *R A) *V s"
  apply(rule sq_mtx_unique_sol_autonomous_affine[of "λs. {t0--t}" _ t X A 0])
  using assms apply(simp_all add: ivp_sols_def)
  using has_vderiv_on_sq_mtx_linear by force+

lemma autonomous_affine_sol_is_exp_plus_int:
  assumes "D X = (λt. A *V X t + B) on {t0--t}" and "X t0 = s" 
  shows "X t = exp ((t - t0) *R A) *V s + exp ((t - t0) *R A) *V (∫⇩t0t(exp (- (τ - t0) *R A) *V B)τ)"
  apply(rule sq_mtx_unique_sol_autonomous_affine[of "λs. {t0--t}" _ t X A B])
  using assms apply(simp_all add: ivp_sols_def)
  using has_vderiv_on_sq_mtx_affine by force+

lemma local_flow_sq_mtx_linear: "local_flow ((*V) A) UNIV UNIV (λt s. exp (t *R A) *V s)"
  unfolding local_flow_def local_flow_axioms_def apply safe
  using picard_lindeloef_sq_mtx_affine[of _ "λt. A" "λt. 0"] apply force
  using has_vderiv_on_sq_mtx_linear[of 0] by auto

lemma local_flow_sq_mtx_affine: "local_flow (λs. A *V s + B) UNIV UNIV 
  (λt s. exp (t *R A) *V s + exp (t *R A) *V (∫⇩0t(exp (- τ *R A) *V B)τ))"
  unfolding local_flow_def local_flow_axioms_def apply safe
  using picard_lindeloef_sq_mtx_affine[of _ "λt. A" "λt. B"] apply force
  using has_vderiv_on_sq_mtx_affine[of 0 A] by auto


end