Theory CategoryOfTransitions

(*  Title:       CategoryOfTransitions
    Author:      Eugene W. Stark <stark@cs.stonybrook.edu>, 2024
    Maintainer:  Eugene W. Stark <stark@cs.stonybrook.edu>
*)

section "Categories of Transitions"

theory CategoryOfTransitions
imports Main Category3.EpiMonoIso CategoryWithBoundedPushouts
        ResiduatedTransitionSystem.ResiduatedTransitionSystem
begin

  text‹
    A category of transitions is a category with bounded pushouts in which every
    arrow is an epimorphism and the only isomorphisms are identities.
  ›

  locale category_of_transitions =
    category_with_bounded_pushouts +
  assumes arr_implies_epi: "arr t  epi t"
  and iso_implies_ide: "iso t  ide t"
  begin

    (* TODO: Move with rest of commutative_square facts (currently CategoryWithPullbacks). *)
    lemma commutative_square_sym:
    shows "commutative_square f g h k  commutative_square g f k h"
      by auto

    text ‹
      In this setting, pushouts are uniquely determined.
    ›

    sublocale elementary_category_with_bounded_pushouts C inj0 inj1
      using extends_to_elementary_category_with_bounded_pushouts by blast

    lemma pushouts_unique:
    assumes "pushout_square f g h k"
    shows "f = 𝗂1[h, k]" and "g = 𝗂0[h, k]"
    proof -
      obtain w where w: "w  𝗂1[h, k] = f  w  𝗂0[h, k] = g"
        using assms pushout_universal pushout_square_def by meson
      obtain w' where w': "w'  f = 𝗂1[h, k]  w'  g = 𝗂0[h, k]"
        using assms pushout_universal pushout_square_def pushout_commutes 
          bounded_spanI
        by meson
      have 1: "ide w"
      proof -
        have "commutative_square f g h k"
          using assms pushout_square_def by blast
        hence "ide (w  w')"
          using assms w w' comp_cod_arr ide_char' comp_assoc
          apply (elim commutative_squareE)
          by (metis arr_implies_epi epi_cancel seqE)
        thus ?thesis
          by (metis ideD(1) ide_is_iso inv_comp_right(2) iso_iff_section_and_epi
              sectionI seqE arr_implies_epi iso_implies_ide)
      qed
      show "f = 𝗂1[h, k]" and "g = 𝗂0[h, k]"
        using 1 w w' by (metis comp_ide_arr ext null_is_zero(2))+
    qed

    lemma inj_sym:
    shows "𝗂0[k, h] = 𝗂1[h, k]"
    proof -
      have "¬ bounded_span h k  ?thesis"
        using inj0_ext [of k h] inj1_ext [of h k] bounded_span_sym by metis
      moreover have "bounded_span h k  ?thesis"
      proof -
        assume 1: "bounded_span h k"
        have "(l. l  𝗂1[h, k] = 𝗂0[k, h])  (l'. l'  𝗂0[k, h] = 𝗂1[h, k])"
          by (meson 1 bounded_span_sym commutative_square_sym
              pushout_commutes pushout_universal)
        moreover have "epi 𝗂0[k, h]  epi 𝗂1[h, k]"
          by (meson 1 arr_implies_epi bounded_span_sym
              commutative_squareE pushout_commutes)
        ultimately show ?thesis
          by (metis arr_implies_epi iso_iff_section_and_epi comp_cod_arr
              comp_ide_arr epi_cancel epi_implies_arr ide_cod
              iso_implies_ide comp_assoc sectionI seqE)
      qed
      ultimately show ?thesis by auto
    qed

    lemma inj_arr_self:
    assumes "arr t"
    shows "𝗂0[t, t] = cod t" and "𝗂1[t, t] = cod t"
    proof -
      have 1: "pushout_square (cod t) (cod t) t t"
        using assms comp_arr_dom pushout_universal
        apply (intro pushout_squareI)
            apply auto[4]
        apply auto 
         apply (metis commutative_squareE inj_sym)
        by (metis ide_cod commutative_squareE comp_arr_ide)
      show "𝗂0[t, t] = cod t" and "𝗂1[t, t] = cod t"
        using 1 pushouts_unique by auto
    qed

    lemma inj_arr_dom:
    assumes "arr t"
    shows "𝗂0[t, dom t] = t" and "𝗂1[t, dom t] = cod t"
    proof -
      have 1: "pushout_square (cod t) t t (dom t)"
        using assms comp_arr_dom comp_cod_arr pushout_universal
        apply (intro pushout_squareI)
            apply auto[4]
        apply auto
         apply (metis cod_dom commutative_squareE)
        by (metis arr_implies_epi commutative_squareE epi_cancel)
      show "𝗂0[t, dom t] = t" and "𝗂1[t, dom t] = cod t"
        using 1 pushouts_unique by auto
    qed

    lemma eq_iff_ide_inj:
    assumes "span t u"
    shows "t = u  ide 𝗂0[t, u]  ide 𝗂0[u, t]"
    proof
      show "t = u  ide 𝗂0[t, u]  ide 𝗂0[u, t]"
        using assms by (simp add: inj_arr_self(1))
      show "ide 𝗂0[t, u]  ide 𝗂0[u, t]  t = u"
        by (metis (no_types, lifting) comp_cod_arr commutative_squareE
            ide_char ide_def inj0_ext inj_sym pushout_commutes)
    qed

    lemma inj_comp:
    assumes "bounded_span t (v  u)"
    shows "𝗂0[t, v  u] = 𝗂0[𝗂0[t, u], v]"
    and "𝗂0[v  u, t] = 𝗂0[v, 𝗂0[t, u]]  𝗂0[u, t]"
    proof -
      obtain w x where wx: "seq w t  w  t = x  v  u"
        using assms by blast
      have 1: "seq w t  w  t = (x  v)  u"
        using wx comp_assoc by auto
      have 2: "pushout_square 𝗂0[u, t] 𝗂0[t, u] t u"
        by (metis (full_types) 1 bounded_span_def cod_comp
            commutative_squareI dom_comp has_bounded_pushouts inj_sym seqE)
      have 3: "pushout_square 𝗂0[v, 𝗂0[t, u]] 𝗂0[𝗂0[t, u], v] 𝗂0[t, u] v"
      proof -
        have 4: "commutative_square w (x  v) t u"
          using wx comp_assoc
          by (metis (mono_tags, lifting) cod_comp commutative_square_def
              dom_comp seqE)
        obtain l where l: "l  𝗂0[u, t] = w  l  𝗂0[t, u] = x  v"
          using 2 4 pushout_square_def [of "𝗂0[u, t]" "𝗂0[t, u]" t u] by blast
        have "bounded_span 𝗂0[t, u] v"
          by (metis (full_types) 1 l bounded_spanI cod_comp
              commutative_squareI dom_comp seqE)
        thus ?thesis
          using assms has_bounded_pushouts inj_sym by auto
      qed
      show "𝗂0[t, v  u] = 𝗂0[𝗂0[t, u], v]"
        using assms 2 3 composition_of_pushouts pushouts_unique
        by (metis (full_types))
      show "𝗂0[v  u, t] = 𝗂0[v, 𝗂0[t, u]]  𝗂0[u, t]"
        using assms 2 3 composition_of_pushouts pushouts_unique inj_sym
        by metis
    qed

    lemma inj_prefix:
    assumes "arr (u  t)"
    shows "𝗂0[u  t, t] = u" and "𝗂0[t, u  t] = cod u"
    proof -
      have 1: "bounded_span (u  t) t"
        by (metis assms ideD(1) ide_cod inj1_ext inj_arr_self(1)
            inj_comp(2) inj_sym not_arr_null seqE)
      show "𝗂0[u  t, t] = u"
        using assms 1
        by (metis bounded_span_sym seqE comp_arr_dom inj_arr_dom(1)
            inj_arr_self(1) inj_comp(2))
      show "𝗂0[t, u  t] = cod u"
        using 1
        by (metis assms bounded_span_sym seqE inj_arr_dom(2) inj_arr_self(1)
            inj_comp(1) inj_sym)
    qed

  end

  lemma category_of_transitions_preserved_by_iso:
  assumes "isomorphic_categories A B"
  and "category_of_transitions A"
  shows "category_of_transitions B"
  proof -
    interpret A: category A
      using assms(1) isomorphic_categories.axioms(1) by blast
    interpret B: category B
      using assms(1) isomorphic_categories.axioms(2) by blast
    obtain F G where FG: "inverse_functors A B G F"
      using assms isomorphic_categories.iso by blast
    interpret FG: inverse_functors A B G F
      using FG by blast
    interpret A: category_of_transitions A
      using assms(2) by blast
    show ?thesis
    proof
      show "t. B.iso t  B.ide t"
        by (metis FG.F.preserves_iso FG.G.preserves_ide FG.inv FG.iso assms(2)
            category.iso_is_arr category_of_transitions.iso_implies_ide comp_eq_dest_lhs
            identity_functor.map_simp identity_functor_def inverse_functors_def)
      show "t. B.arr t  B.epi t"
      proof -
        fix t
        assume t: "B.arr t"
        hence "A.arr (G t)"
          by blast
        hence "A.epi (G t)"
          using A.arr_implies_epi by blast
        hence "B.epi (F (G t))"
        proof (intro B.epiI)
          show "A.epi (G t)  B.arr (F (G t))"
            using A.arr (G t) by blast
          show "g g'. A.epi (G t); B.seq g (F (G t)); B.seq g' (F (G t));
                        B g (F (G t)) = B g' (F (G t))  g = g'"
            by (metis A.arr_implies_epi A.category_axioms B.category_axioms B.is_faithful
                FG.F.preserves_arr FG.F.preserves_comp FG.inv category.cod_comp
                category.epi_cancel category.seqE o_apply)
        qed
        thus "B.epi t"
          by (metis B.map_simp FG.inv o_apply t)
      qed
      show "h k. B.bounded_span h k  f g. B.pushout_square f g h k"
      proof -
        fix h k
        assume hk: "B.bounded_span h k"
        have 1: "A.bounded_span (G h) (G k)"
        proof -
          obtain f g where fg: "B.commutative_square f g h k"
            using hk by blast
          have "A.commutative_square (G f) (G g) (G h) (G k)"
            by (metis (mono_tags, lifting) A.commutative_squareI B.commutative_squareE
                B.seqI FG.F.preserves_cod FG.F.preserves_comp FG.F.preserves_dom
                FG.F.preserves_reflects_arr fg)
          thus ?thesis by blast
        qed
        obtain Gf Gg where fg: "A.pushout_square Gf Gg (G h) (G k)"
          using 1 A.has_bounded_pushouts by blast
        have "B.pushout_square (F Gf) (F Gg) h k"
        proof
          show "B.span h k"
            using hk by auto
          show "B.cospan (F Gf) (F Gg)"
            using A.commutative_square_def A.pushout_square_def FG.G.preserves_arr
                  FG.G.preserves_cod fg
            by presburger
          show "B.dom (F Gf) = B.cod h"
            by (metis A.commutative_square_def A.pushout_square_def B.identity_functor_axioms
                FG.G.preserves_cod FG.G.preserves_dom FG.inv B.span h k fg
                identity_functor.map_def o_apply)
          show "B (F Gf) h = B (F Gg) k"
            by (metis (no_types, lifting) A.category_axioms A.pushout_square_def
                B.map_simp FG.G.preserves_comp FG.inv B.span h k category.commutative_squareE
                category.seqI fg o_apply)
          show "f' g'. B.commutative_square f' g' h k  ∃!l. B l (F Gf) = f'  B l (F Gg) = g'"
          proof -
            fix f' g'
            assume 1: "B.commutative_square f' g' h k"
            have 2: "A.commutative_square (G f') (G g') (G h) (G k)"
              by (metis (no_types, lifting) ext "1" A.commutative_squareI A.seqE
                  B.commutative_square_def B.comp_arr_dom B.seqI
                  FG.F.as_nat_trans.preserves_comp_2 FG.F.preserves_cod FG.F.preserves_seq
                  B.span h k)
            hence "∃!Gl. A Gl Gf = G f'  A Gl Gg = G g'"
              using A.pushout_square_def fg by presburger
            thus "∃!l. B l (F Gf) = f'  B l (F Gg) = g'"
              by (metis (full_types) "1" "2" A.category_axioms B.category_axioms
                  B.identity_functor_axioms FG.G.preserves_comp FG.inv
                  B.cospan (F Gf) (F Gg) t. B.arr t  B.epi t
                  category.commutative_square_def category.epi_cancel
                  identity_functor.map_simp o_apply)
          qed
        qed
        thus "f g. B.pushout_square f g h k" by blast
      qed
    qed
  qed

section "Extensional RTS's with Composites as Categories"

  text ‹
    An extensional RTS with composites can be regarded as a category in an obvious way.
  ›

  locale extensional_rts_with_composites_as_category =
    A: extensional_rts_with_composites
  begin

    text ‹
      Because we've defined RTS composition to take its arguments in diagram order,
      the ordering has to be reversed to match the way it is done for categories.
    ›

    abbreviation comp
    where "comp  λu t. t  u"

    interpretation Category.partial_magma comp
      using A.comp_null by unfold_locales metis
    interpretation Category.partial_composition comp ..

    lemma null_char:
    shows "null = A.null"
      by (metis A.comp_nullCC(1) null_is_zero(2))

    lemma ide_char:
    shows "ide a  A.ide a"
    proof
      assume a: "A.ide a"
      show "ide a"
      proof (unfold ide_def, intro conjI allI impI)
        show "a  a  null"
          using a null_char by auto
        show "t. t  a  null  t  a = t"
          using a
          by (metis A.comp_def A.comp_is_composite_of(2) A.composable_def
              A.composite_of_def A.cong_char null_char)
        show "t. a  t  null  a  t = t"
          using a
          by (metis A.arr_comp A.ide_iff_trg_self A.comp_src_arr null_char
              A.comp_def A.arr_compEEC)
      qed
      next
      assume a: "ide a"
      show "A.ide a"
        using a
        by (metis A.arr_comp A.comp_def A.comp_eqI A.cong_reflexive
            ide_def null_char)
    qed

    lemma src_in_domains:
    assumes "A.arr t"
    shows "A.src t  domains t"
      unfolding domains_def
      using assms ide_char null_char by force

    lemma trg_in_codomains:
    assumes "A.arr t"
    shows "A.trg t  codomains t"
      unfolding codomains_def
      using assms ide_char null_char by force

    lemma arr_char:
    shows "arr = A.arr"
    proof
      fix t
      show "arr t  A.arr t"
      proof
        show "A.arr t  arr t"
          using src_in_domains trg_in_codomains arr_def by auto
        assume t: "arr t"
        have 1: "domains t  {}  codomains t  {}"
          using t arr_def by simp
        show "A.arr t"
        proof (cases "domains t  {}")
          case True
          obtain a where a: "a  domains t"
            using True by auto
          show "A.arr t"
            using a t A.composable_iff_seq [of a t] A.comp_def domains_def
                  null_char
            by auto
          next
          case False
          obtain a where a: "a  codomains t"
            using False 1 by auto
          show "A.arr t"
            using a t A.composable_iff_seq [of t a] A.comp_def codomains_def
                  null_char
            by auto
        qed
      qed
    qed

    lemma seq_char:
    shows "seq u t  A.seq t u"
      using arr_char by auto

    sublocale category comp
    proof
      show "g f. f  g  null  seq g f"
        using A.composable_iff_comp_not_null null_char seq_char by auto
      show "f. (domains f  {}) = (codomains f  {})"
        using arr_char arr_def src_in_domains trg_in_codomains empty_iff by metis
      fix f g h
      show 1: "f  (g  h) = (f  g)  h"
        using A.comp_assocEC by blast
      show "seq h g; seq (g  h) f  seq g f"
        using A.comp_assocEC seq_char by auto
      show "seq h (f  g); seq g f  seq h g"
        by (metis 1 A.arr_compEEC arr_char)
      show "seq g f; seq h g  seq (g  h) f"
        using seq_char by fastforce
    qed

    proposition is_category:
    shows "category comp"
      ..

    lemma cod_char:
    shows "cod = A.trg"
      unfolding A.trg_def
      by (metis arr_char cod_def cod_eqI' codomains_char null_char
          A.con_implies_arr(2) trg_in_codomains A.conI A.trg_def)

    lemma dom_char:
    shows "dom = A.src"
      unfolding A.src_def
      by (metis A.src_def arr_char arr_def dom_eqI' dom_def null_char
          src_in_domains)

    lemma arr_implies_epi:
    assumes "arr t"
    shows "epi t"
      using assms
      by (metis arr_char epiI A.comp_cancel_left)

    lemma iso_implies_ide:
    assumes "iso t"
    shows "ide t"
      by (metis A.ide_backward_stable A.src_ide arr_char arr_inv assms
          comp_inv_arr' dom_char dom_inv ide_char' iso_is_arr A.ide_src
          A.prfx_comp seqI)

  end

  locale composite_completion_as_category =
    R: rts +
    extensional_rts_with_composites_as_category composite_completion.resid resid

  section "Characterization"

  text ‹
    The categories arising from extensional RTS's with composites are categories
    of transitions.
  ›

  context extensional_rts_with_composites_as_category
  begin

    lemma has_bounded_pushouts:
    assumes "bounded_span h k"
    shows "pushout_square (k \\ h) (h \\ k) h k"
    proof
      have con: "h  k"
        using assms
        by (metis A.bounded_imp_conE A.cong_reflexive arr_char
            bounded_span_def commutative_squareE seqI)
      show "cospan (k \\ h) (h \\ k)"
        by (simp add: con A.apex_sym A.con_sym arr_char cod_char)
      show "span h k"
        using assms by blast
      show "dom (k \\ h) = cod h"
        using A.join_expansion(2) con seq_char by blast
      show "h  k \\ h = k  h \\ k"
        using A.diamond_commutes by blast
      show "f g h k. commutative_square f g h k
                          ∃!l. k \\ h  l = f  h \\ k  l = g"
      proof -
        fix f g h k
        assume 1: "commutative_square f g h k"
        hence 2: "h  f = k  g"
          using dom_char cod_char by auto
        hence 3: "h  k"
          by (metis "1" A.bounded_imp_conE A.prfx_reflexive arr_char
              commutative_squareE seqI)
        let ?l = "(h  f) \\ (h  k)"
        have 4: "(k \\ h)  ?l = f"
        proof -
          have "A.joinable h k"
            using 3 by simp
          thus ?thesis
            by (metis 1 A.comp_assocEC A.comp_resid_prfx A.induced_arrow(2)
                A.join_expansion(1) A.seq_implies_arr_comp commutative_squareE
                seqI seq_char)
        qed
        moreover have "(h \\ k)  ?l = g"
          by (metis "1" A.comp_resid_prfx A.induced_arrow(2)
              A.seq_implies_arr_comp calculation commutative_squareE
              seqI seq_char)
        ultimately have "l. (k \\ h)  ?l = f  (h \\ k)  ?l = g"
          by simp
        moreover have "l'. (k \\ h)  l' = f  (h \\ k)  l' = g  l' = ?l"
          by (metis 1 4 A.comp_cancel_left arr_char commutative_square_def)
        ultimately show "∃!l. (k \\ h)  l = f  (h \\ k)  l = g" by auto
      qed
    qed

    sublocale category_of_transitions comp
      using has_bounded_pushouts arr_implies_epi iso_implies_ide
      by unfold_locales auto

    proposition is_category_of_transitions:
    shows "category_of_transitions comp"
      ..

  end

  text ‹
    Every category of transitions is derived from an underlying extensional RTS,
    obtained by using pushouts to define residuation.
  ›

  locale underlying_rts =
    C: category_of_transitions
  begin

    abbreviation resid
    where "resid  λh k. 𝗂0[h, k]"

    interpretation ResiduatedTransitionSystem.partial_magma resid
      using C.inj0_ext C.inj1_ext C.commutative_squareE C.not_arr_null C.pushout_commutes
      by unfold_locales metis

    lemma null_char:
    shows "null = C.null"
      using null_eqI C.inj0_ext C.not_arr_null C.pushout_commutes
      by (metis C.commutative_squareE)

    interpretation residuation resid
    proof
      show 0: "t u. 𝗂0[t, u]  null  𝗂0[u, t]  null"
        by (metis C.inj0_ext C.inj_sym C.not_arr_null C.pushout_commutes
            C.commutative_squareE null_char)
      show "t u. 𝗂0[t, u]  null  𝗂0[𝗂0[t, u], 𝗂0[t, u]]  null"
        by (metis C.commutative_squareE C.eq_iff_ide_inj C.ideD(1) C.inj0_ext
            C.not_arr_null C.pushout_commutes null_char)
      have *: "t u v. 𝗂0[t, u]  null; 𝗂0[t, v]  null; 𝗂0[𝗂0[v, t], 𝗂0[u, t]]  null
                           w. 𝗂0[𝗂0[v, t], 𝗂0[u, t]] = w  𝗂0[𝗂0[v, u], 𝗂0[t, u]]"
      proof -
        fix t u v
        assume tu: "𝗂0[t, u]  null" and tv: "𝗂0[t, v]  null"
        and vt_ut: "𝗂0[𝗂0[v, t], 𝗂0[u, t]]  null"
        have 1: "(𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u])  u = (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v])  v"
        proof -
          have "(𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u])  u = 𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u]  u"
            using C.comp_assoc by simp
          also have "... =  𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[u, t]  t"
            using tu C.pushout_commutes
            by (metis C.commutative_squareE C.inj0_ext C.inj_sym null_char)
          also have "... = (𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[u, t])  t"
            using C.comp_assoc by simp
          also have "... = (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[v, t])  t"
            using vt_ut C.pushout_commutes
            by (metis (no_types, lifting) C.commutative_squareE C.inj0_ext C.inj_sym null_char)
          also have "... = 𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[v, t]  t"
            using C.comp_assoc by simp
          also have "... = 𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v]  v"
            using tv C.pushout_commutes
            by (metis C.commutative_squareE C.inj0_ext C.inj_sym null_char)
          also have "... = (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v])  v"
            using C.comp_assoc by simp
          finally show ?thesis by blast
        qed
        have 2: "C.commutative_square (𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u])
                  (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v]) u v"
        proof -
          have "C.span u v"
            by (metis (mono_tags, opaque_lifting) C.commutative_square_def
                C.inj0_ext C.pushout_commutes null_char tu tv)
          moreover have "C.cospan (𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u])
                                  (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v])"
          proof (intro conjI)
            show 3: "C.seq 𝗂0[𝗂0[v, t], 𝗂0[u, t]] 𝗂0[t, u]"
            proof
              show "«𝗂0[t, u] : C.cod u  C.cod 𝗂0[u, t]»"
                using C.pushout_commutes [of t u]
                by (metis C.commutative_squareE C.in_homI C.inj0_ext C.inj_sym null_char tu)
              show "«𝗂0[𝗂0[v, t], 𝗂0[u, t]] : C.cod 𝗂0[u, t]  C.cod 𝗂0[𝗂0[v, t], 𝗂0[u, t]]»"
                using C.pushout_commutes [of "𝗂0[v, t]" "𝗂0[u, t]"]
                      C.arr_iff_in_hom C.commutative_square_def C.inj0_ext null_char vt_ut
                by force
            qed
            show 4: "C.seq 𝗂0[𝗂0[u, t], 𝗂0[v, t]] 𝗂0[t, v]"
              by (metis 1 3 C.dom_inj(1) C.inj0_ext C.seqE C.seqI calculation
                  C.dom_comp null_char tu)
            show "C.cod (𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u]) =
                  C.cod (𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v])"
              by (metis 3 4 C.cod_inj C.inj0_ext C.inj_sym C.cod_comp)
          qed
          moreover have "C.dom (𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u]) = C.cod u"
            by (metis C.dom_comp C.dom_inj(1) C.inj0_ext calculation(2)
                null_char tu)
          ultimately show ?thesis
            using 1 by blast
        qed
        hence "C.bounded_span u v"
          by blast
        obtain l where l: "l  𝗂0[v, u] = 𝗂0[𝗂0[v, t], 𝗂0[u, t]]  𝗂0[t, u] 
                           l  𝗂0[u, v] = 𝗂0[𝗂0[u, t], 𝗂0[v, t]]  𝗂0[t, v]"
          using 2 C.pushout_universal by (metis C.inj_sym)
        have 3: "C.commutative_square 𝗂0[𝗂0[v, t], 𝗂0[u, t]] l 𝗂0[t, u] 𝗂0[v, u]"
          using tu tv vt_ut l 2
          by (metis (mono_tags, lifting) C.cod_comp C.commutative_square_def
              C.dom_comp C.seqE)
        obtain w where "𝗂0[𝗂0[v, t], 𝗂0[u, t]] = w  𝗂0[𝗂0[v, u], 𝗂0[t, u]] 
                        l = w  𝗂0[𝗂0[t, u], 𝗂0[v, u]]"
          using 3 C.pushout_universal by (metis C.inj_sym)
        thus "w. 𝗂0[𝗂0[v, t], 𝗂0[u, t]] = w  𝗂0[𝗂0[v, u], 𝗂0[t, u]]" by auto
      qed
      show "t u v. 𝗂0[𝗂0[v, t], 𝗂0[u, t]]  null
                        𝗂0[𝗂0[v, t], 𝗂0[u, t]] = 𝗂0[𝗂0[v, u], 𝗂0[t, u]]"
      proof -
        fix t u v
        assume vt_ut: "𝗂0[𝗂0[v, t], 𝗂0[u, t]]  null"
        have tu: "𝗂0[t, u]  null"
          using vt_ut
          by (metis u t. 𝗂0[t, u]  null  𝗂0[u, t]  null null_is_zero(2))
        have tv: "𝗂0[t, v]  null"
          using vt_ut
          by (metis u t. 𝗂0[t, u]  null  𝗂0[u, t]  null null_is_zero(1))
        obtain w where w: "𝗂0[𝗂0[v, t], 𝗂0[u, t]] = w  𝗂0[𝗂0[v, u], 𝗂0[t, u]]"
          using tu tv vt_ut * by blast
        have vu_tu: "𝗂0[𝗂0[v, u], 𝗂0[t, u]]  null"
          using w null_char vt_ut by fastforce
        obtain w' where w': "𝗂0[𝗂0[v, u], 𝗂0[t, u]] = w'  𝗂0[𝗂0[v, t], 𝗂0[u, t]]"
          using 0 vu_tu * null_is_zero(2) by metis
        have "C.ide (w  w')"
          using w w' vt_ut
          by (metis C.comp_cod_arr C.ext C.ide_cod C.inj_prefix(1-2) C.seqE
              null_char)
        hence "C.ide w"
          using w w'
          by (metis C.comp_arr_ide C.ide_compE C.inj_arr_dom(1) C.inj_prefix(2)
              C.seqE)
        thus "𝗂0[𝗂0[v, t], 𝗂0[u, t]] = 𝗂0[𝗂0[v, u], 𝗂0[t, u]]"
          using w C.comp_ide_arr
          by (metis C.ext null_char vt_ut)
      qed
    qed

    lemma con_char:
    shows "con t u  C.bounded_span t u"
      by (metis C.commutative_squareE C.inj0_ext C.not_arr_null
          C.pushout_commutes con_def null_char)

    lemma arr_char:
    shows "arr t  C.arr t"
      by (metis C.arr_cod C.inj_arr_self(1) C.not_arr_null C.pushout_commutes
          arr_def C.commutative_squareE con_char con_def null_char)

    lemma ide_char:
    shows "ide a  C.ide a"
      by (metis C.ide_char C.ide_char' C.ide_def C.inj_arr_self(1) arr_char ide_def
          null_char con_def ide_implies_arr)

    interpretation rts λh k. 𝗂0[h, k]
    proof
      show "a t. ide a; con t a  𝗂0[t, a] = t"
        using C.inj_arr_dom con_char ide_char by fastforce
      thus "a t. ide a; con a t  ide 𝗂0[a, t]"
        by (metis con_sym cube ide_def con_def)
      show "t. arr t  ide (trg t)"
        by (metis arr_char C.eq_iff_ide_inj ide_char resid_arr_self)
      thus "t u. con t u  a. ide a  con a t  con a u"
        using con_char ide_char
        by (metis C.ide_dom C.inj_arr_dom(1) C.pushout_commutes
            C.commutative_squareE con_implies_arr(1) con_sym arr_resid_iff_con)
      show "t u v. ide 𝗂0[t, u]; con u v  con 𝗂0[t, u] 𝗂0[v, u]"
        by (metis (no_types, lifting) C.dom_inj(1) C.ideD(2) C.inj_arr_dom(1)
            arr_char con_char ide_char arr_resid_iff_con con_sym ide_implies_arr)
    qed

    interpretation extensional_rts resid
      by unfold_locales
         (metis C.ideD(2) C.inj_sym C.pushout_commutes prfx_implies_con
                C.commutative_squareE C.comp_cod_arr con_char ide_char)

    lemma src_char:
    assumes "arr t"
    shows "src t = C.dom t"
      by (metis C.ide_dom C.inj_arr_dom(1) arr_char arr_resid_iff_con assms
          con_imp_eq_src ide_char src_ide)

    lemma trg_char:
    assumes "arr t"
    shows "trg t = C.cod t"
      by (metis assms C.inj_arr_self(1) resid_arr_self arr_char)

    lemma seq_char:
    shows "seq t u  C.seq u t"
      using seq_def sources_charWE targets_charWE arr_char src_char trg_char
      by auto

    lemma comp_char:
    shows "comp t u = u  t"
      by (metis C.cod_comp C.ext C.inj_arr_self(1) C.inj_prefix(1-2)
          arr_char composable_imp_seq cong_reflexive comp_def null_char
          prfx_decomp seq_char)

    sublocale extensional_rts_with_composites resid
      by unfold_locales
         (metis C.not_arr_null composable_iff_comp_not_null comp_char null_char
           seq_char)

    proposition is_extensional_rts_with_composites:
    shows "extensional_rts_with_composites resid"
      ..

  end

  context extensional_rts_with_composites_as_category
  begin

    interpretation R: underlying_rts comp ..

    lemma resid_char:
    shows "R.resid = resid"
    proof -
      have 1: "t u. R.con t u = (t  u)"
      proof -
        fix t u
        have "R.con t u  bounded_span t u"
          using R.con_char by blast
        also have "...  (v w. seq v t  comp v t = comp w u)"
          unfolding bounded_span_def
          by (metis (lifting) HOL.ext cod_comp commutative_square_def dom_comp seqE seqI)
        also have "...  (v w. A.seq t v  A.comp t v = A.comp u w)"
          using seq_char by simp
        also have "...  A.con t u"
          by (metis A.arr_def A.arr_resid_iff_con A.con_comp_iffEC(2) A.con_implies_arr(2)
              A.diamond_commutes A.has_joins A.join_expansion(2) A.seq_implies_arr_comp)
        finally show "R.con t u = (t  u)" by blast
      qed
      have "t u. R.resid t u = t \\ u"
        using 1
        by (metis A.con_def R.con_char has_bounded_pushouts inj0_ext null_char pushouts_unique(2))
      thus ?thesis by blast
    qed

  end

  locale pushout_preserving_functor =
    "functor" +
  assumes preserves_pushouts: "A.pushout_square f g h k
                                   B.pushout_square (F f) (F g) (F h) (F k)"

  lemma pushout_preserving_functor_identity:
  assumes "category A"
  shows "pushout_preserving_functor A A (identity_functor.map A)"
  proof -
    interpret A: category A
      using assms by blast
    interpret A: identity_functor A ..
    show ?thesis
    proof
      show "f g h k. A.pushout_square f g h k 
                         A.pushout_square (A.map f) (A.map g) (A.map h) (A.map k)"
        by (simp add: A.commutative_square_def A.pushout_square_def)
    qed
  qed

  lemma pushout_preserving_functor_comp:
  assumes "pushout_preserving_functor A B F"
  and "pushout_preserving_functor B C G"
  shows "pushout_preserving_functor A C (G  F)"
  proof -
    interpret F: pushout_preserving_functor A B F
      using assms(1) by blast
    interpret G: pushout_preserving_functor B C G
      using assms(2) by blast
    interpret GoF: composite_functor A B C F G ..
    show ?thesis
      using F.preserves_pushouts G.preserves_pushouts
      by unfold_locales auto
  qed

  lemma pushout_preserving_functor_const:
  assumes "category A" and "category B"
  and "partial_composition.ide B b"
  shows "pushout_preserving_functor A B (constant_functor.map A B b)"
  proof -
    interpret A: category A
      using assms(1) by blast
    interpret B: category B
      using assms(2) by blast
    interpret F: constant_functor A B b
      using assms(3) by unfold_locales blast
    show ?thesis
    proof
      fix f g h k
      assume 1: "A.pushout_square f g h k"
      show "B.pushout_square (F.map f) (F.map g) (F.map h) (F.map k)"
      proof
        show 2: "B.cospan (F.map f) (F.map g)"
          using 1 A.pushout_square_def by auto
        show 3: "B.span (F.map h) (F.map k)"
          using 1 A.commutative_square_def A.pushout_square_def F.value_is_ide by force
        show "B.dom (F.map f) = B.cod (F.map h)"
          using 2 3 B.ide_char F.map_simp F.preserves_reflects_arr F.value_is_ide
          by metis
        show "B (F.map f) (F.map h) = B (F.map g) (F.map k)"
          using 2 3 by force
        fix f' g'
        assume 4: "B.commutative_square f' g' (F.map h) (F.map k)"
        show "∃!l. B l (F.map f) = f'  B l (F.map g) = g'"
          by (metis (lifting) ext 2 4 B.commutative_square_def B.comp_arr_dom B.ide_char
              B.seqE F.map_simp F.preserves_reflects_arr F.value_is_ide)
      qed
    qed
  qed

  lemma invertible_functor_is_pushout_preserving:
  assumes "invertible_functor A B F"
  shows "pushout_preserving_functor A B F"
  proof -
    interpret F: invertible_functor A B F
      using assms by blast
    obtain G where G: "inverse_functors A B G F"
      using F.has_unique_inverse by blast
    interpret FG: inverse_functors A B G F
      using G by blast
    show ?thesis
    proof
      fix f g h k
      assume 1: "F.A.pushout_square f g h k"
      show "F.B.pushout_square (F f) (F g) (F h) (F k)"
      proof
        show "F.B.cospan (F f) (F g)"
          using 1 F.A.commutative_square_def F.A.pushout_square_def by force
        show "F.B.span (F h) (F k)"
          using 1 F.A.pushout_square_def by auto
        show "F.B.dom (F f) = F.B.cod (F h)"
          using 1 F.A.pushout_square_def by auto
        show "B (F f) (F h) = B (F g) (F k)"
          by (metis 1 F.A.commutative_square_def F.A.pushout_square_def F.A.seqI
              F.G.preserves_comp)
        fix f' g'
        assume 2: "F.B.commutative_square f' g' (F h) (F k)"
        show "∃!l'. B l' (F f) = f'  B l' (F g) = g'"
        proof -
          have 3: "F.A.commutative_square (G f') (G g') h k"
          proof
            show "F.A.cospan (G f') (G g')"
              by (metis 2 F.B.commutative_square_def FG.F.preserves_arr FG.F.preserves_cod)
            show "F.A.span h k"
              using 1 F.A.pushout_square_def by blast
            show "F.A.dom (G f') = F.A.cod h"
              by (metis 2 F.A.map_simp F.B.commutative_square_def FG.F.preserves_cod
                  FG.F.preserves_dom FG.inv' F.A.span h k o_apply)
            show "A (G f') h = A (G g') k"
              by (metis 2 F.A.map_simp F.B.commutative_squareE FG.F.preserves_comp G
                  F.A.span h k F.B.seqI comp_eq_dest_lhs inverse_functors.inv')
          qed
          obtain l where l: "A l f = G f'  A l g = G g'"
            using 1 3 by (meson F.A.pushout_square_def)
          have "B (F l) (F f) = f'  B (F l) (F g) = g'"
            by (metis 2 F.B.commutative_square_def F.B.identity_functor_axioms
                F.G.preserves_comp FG.F.preserves_arr FG.inv identity_functor.map_simp
                l o_apply)
          moreover have "l'. B l' (F f) = f'; B l' (F g) = g'  F l = l'"
          proof -
            fix l'
            assume 4: "B l' (F f) = f'" and 5: "B l' (F g) = g'"
            have "A (G l') f = G f'  A (G l') g = G g'"
              by (metis 3 4 5 F.A.commutative_square_def F.A.map_simp
                  F.G.preserves_reflects_arr FG.F.as_nat_trans.preserves_comp_2
                  FG.F.preserves_reflects_arr FG.inv' F.B.cospan (F f) (F g) o_apply)
            hence "G l' = l"
              using 1 3 l F.A.pushout_square_def [of f g h k] by blast
            thus "F l = l'"
              by (metis 3 F.A.commutative_squareE F.A.null_is_zero(1)
                  F.A.partial_composition_axioms F.B.map_def FG.F.extensionality FG.inv
                  l o_apply partial_composition.not_arr_null)
          qed
          ultimately show "∃!l'. B l' (F f) = f'  B l' (F g) = g'" by blast
        qed
      qed
    qed
  qed

  locale simulation_as_functor =
    simulation +
    A: extensional_rts_with_composites A +
    B: extensional_rts_with_composites B
  begin

    sublocale A.as_category: extensional_rts_with_composites_as_category A ..
    sublocale B.as_category: extensional_rts_with_composites_as_category B ..

    sublocale "functor" A.as_category.comp B.as_category.comp F
    proof
      show "f. ¬ A.as_category.arr f  F f = B.as_category.null"
        by (simp add: A.as_category.arr_char B.as_category.null_char extensionality)
      show "f. A.as_category.arr f  B.as_category.arr (F f)"
        by (simp add: A.as_category.arr_char B.as_category.arr_char)
      show "f. A.as_category.arr f  B.as_category.dom (F f) = F (A.as_category.dom f)"
        by (simp add: A.as_category.arr_char A.as_category.dom_char
            B.as_category.dom_char B.src_eqI)
      show "f. A.as_category.arr f  B.as_category.cod (F f) = F (A.as_category.cod f)"
        by (simp add: A.as_category.arr_char A.as_category.cod_char
            B.as_category.cod_char)
      show "g f. A.as_category.seq g f 
                     F (A.as_category.comp g f) = B.as_category.comp (F g) (F f)"
        by (metis A.as_category.seq_char A.comp_is_composite_of(1)
            A.rts_with_chosen_composites_axioms A.seq_implies_arr_comp
            B.extensional_rts_axioms rts_with_chosen_composites.composable_iff_arr_compCC
            simulation_axioms simulation_to_extensional_rts.intro
            simulation_to_extensional_rts.preserves_comp)
    qed

    lemma is_functor:
    shows "functor A.as_category.comp B.as_category.comp F"
      ..

    sublocale pushout_preserving_functor A.as_category.comp B.as_category.comp F
    proof
      fix f g h k
      assume 1: "A.as_category.pushout_square f g h k"
      show "B.as_category.pushout_square (F f) (F g) (F h) (F k)"
      proof
        show "B.as_category.cospan (F f) (F g)"
          by (metis 1 A.as_category.commutative_square_def A.as_category.pushout_square_def
              preserves_arr preserves_cod)
        show "B.as_category.span (F h) (F k)"
          using 1 A.as_category.pushout_square_def by auto
        show "B.as_category.dom (F f) = B.as_category.cod (F h)"
          by (metis 1 A.as_category.commutative_squareE A.as_category.pushout_square_def
              preserves_cod preserves_dom)
        show "B.as_category.comp (F f) (F h) = B.as_category.comp (F g) (F k)"
          by (metis 1 A.as_category.arr_char A.as_category.cod_char
              A.as_category.commutative_square_def A.as_category.dom_char
              A.as_category.pushout_square_def as_nat_trans.preserves_comp_2
              extensional_rts_with_composites.arr_compEC simulation_as_functor_axioms
              simulation_as_functor_def)
        show "f' g'. B.as_category.commutative_square f' g' (F h) (F k) 
                         ∃!l. B.as_category.comp l (F f) = f'  B.as_category.comp l (F g) = g'"
        proof -
          fix f' g'
          assume 2: "B.as_category.commutative_square f' g' (F h) (F k)"
          moreover have "B.as_category.pushout_square (F k \\B F h) (F h \\B F k) (F h) (F k)"
            using 2 B.as_category.has_bounded_pushouts by blast
          ultimately have "∃!l. B.as_category.comp l (F k \\B F h) = f' 
                                B.as_category.comp l (F h \\B F k) = g'"
            unfolding B.as_category.pushout_square_def by presburger
          moreover have "f = k \\A h  g = h \\A k"
            by (metis 1 A.as_category.bounded_spanI A.as_category.has_bounded_pushouts
                A.as_category.pushout_square_def A.as_category.pushouts_unique(1-2))
          ultimately show "∃!l. B.as_category.comp l (F f) = f'  B.as_category.comp l (F g) = g'"
            unfolding B.as_category.pushout_square_def
            using A.arr_resid_iff_con A.as_category.arr_char B.as_category.cospan (F f) (F g)
            by auto
        qed
      qed
    qed

    lemma is_pushout_preserving_functor:
    shows "pushout_preserving_functor A.as_category.comp B.as_category.comp F"
      ..

  end

  lemma simulation_is_pushout_preserving_functor:
  assumes "extensional_rts_with_composites A"
  and "extensional_rts_with_composites B"
  and "simulation A B F"
  shows "pushout_preserving_functor
           (extensional_rts_with_composites_as_category.comp A)
           (extensional_rts_with_composites_as_category.comp B)
           F"
  proof -
    interpret A: extensional_rts_with_composites A
      using assms(1) by blast
    interpret B: extensional_rts_with_composites B
      using assms(2) by blast
    interpret F: simulation A B F
      using assms(3) by blast
    interpret F: simulation_as_functor A B F ..
    show ?thesis
      using F.pushout_preserving_functor_axioms by blast
  qed

  lemma pushout_preserving_functor_is_simulation:
  assumes "category_of_transitions A"
  and "category_of_transitions B"
  and "pushout_preserving_functor A B F"
  shows "simulation
           (category_with_bounded_pushouts.inj0 A)
           (category_with_bounded_pushouts.inj0 B)
           F"
  proof -
    interpret A: category_of_transitions A
      using assms(1) by blast
    interpret B: category_of_transitions B
      using assms(2) by blast
    interpret F: pushout_preserving_functor A B F
      using assms(3) by blast
    interpret A': underlying_rts A ..
    interpret B': underlying_rts B ..
    show ?thesis
    proof
      show "t. ¬ A'.arr t  F t = B'.null"
        by (simp add: A'.arr_char B'.null_char F.extensionality)
      show "t u. A'.con t u  B'.con (F t) (F u)"
      proof -
        fix t u
        assume con: "A'.con t u"
        obtain v w where vw: "A'.seq t v  A'.comp t v = A'.comp u w"
          using con
          by (meson A'.diamond_commutes A'.has_joins A'.join_expansion(2))
        hence "F (A v t) = F (A w u)"
          by (simp add: A'.comp_char)
        hence "B (F v) (F t) = B (F w) (F u)"
          by (metis A'.comp_char A'.seq_char F.preserves_comp vw)
        thus "B'.con (F t) (F u)"
          by (metis A'.seq_char B'.bounded_imp_conE B'.comp_char B'.prfx_reflexive
              B'.seq_char B'.seq_implies_arr_comp F.preserves_seq vw)
      qed
      show "t u. A'.con t u  F (A.inj0 t u) = B.inj0 (F t) (F u)"
        by (meson A'.con_char A.has_bounded_pushouts B.category_of_transitions_axioms
            F.preserves_pushouts category_of_transitions.pushouts_unique(2))
    qed
  qed

end