Theory Ribbons_Graphical_Soundness

section ‹Soundness for graphical diagrams›

theory Ribbons_Graphical_Soundness imports
  Ribbons_Graphical
  More_Finite_Map
begin

text ‹We prove that the proof rules for graphical ribbon proofs are sound
  with respect to the rules of separation logic.

  We impose an additional assumption to achieve soundness: that the
  Frame rule has no side-condition. This assumption is reasonable because there
  are several separation logics that lack such a side-condition, such as
  ``variables-as-resource''.

  We first describe how to extract proofchains from a diagram. This process is
  similar to the process of extracting commands from a diagram, which was
  described in @{theory Ribbon_Proofs.Ribbons_Graphical}. When we extract a proofchain, we
  don't just include the commands, but the assertions in between them. Our
  main lemma for proving soundness says that each of these proofchains
  corresponds to a valid separation logic proof.
›

subsection ‹Proofstate chains›

text ‹When extracting a proofchain from a diagram, we need to keep track
  of which nodes we have processed and which ones we haven't. A
  proofstate, defined below, maps a node to ``Top'' if it hasn't been
  processed and ``Bot'' if it has.›

datatype topbot = Top | Bot

type_synonym proofstate = "node f topbot"

text ‹A proofstate chain contains all the nodes and edges of a graphical
  diagram, interspersed with proofstates that track which nodes have been
  processed at each point.›

type_synonym ps_chain = "(proofstate, node + edge) chain"

text ‹The @{term "next_ps σ"} function processes one node or one edge in a
  diagram, given the current proofstate @{term σ}. It processes a node
  @{term v} by replacing the mapping from @{term v} to @{term Top} with a
  mapping from @{term v} to @{term Bot}. It processes an edge @{term e}
  (whose source and target nodes are @{term vs} and @{term ws} respectively)
  by removing all the mappings from @{term vs} to @{term Bot}, and adding
  mappings from @{term ws} to @{term Top}.›

fun next_ps :: "proofstate  node + edge  proofstate"
where
  "next_ps σ (Inl v) = σ  {|v|} ++f [{|v|} |=> Bot]"
| "next_ps σ (Inr e) = σ  fst3 e ++f [thd3 e |=> Top]"

text ‹The function @{term "mk_ps_chain Π π"} generates from @{term π}, which
  is a list of nodes and edges, a proofstate chain, by interspersing the
  elements of @{term π} with the appropriate proofstates. The first argument
  @{term Π} is the part of the chain that has already been converted.›

definition
  mk_ps_chain :: "[ps_chain, (node + edge) list]  ps_chain"
where
  "mk_ps_chain  foldl (λΠ x. cSnoc Π x (next_ps (post Π) x))"

lemma mk_ps_chain_preserves_length:
  fixes π Π
  shows "chainlen (mk_ps_chain Π π) = chainlen Π + length π"
proof (induct π arbitrary: Π)
  case Nil
  show ?case by (unfold mk_ps_chain_def, auto)
next
  case (Cons x π)
  show ?case
  apply (unfold mk_ps_chain_def list.size foldl.simps)
  apply (fold mk_ps_chain_def)
  apply (auto simp add: Cons len_snoc)
  done
qed

text ‹Distributing @{term mk_ps_chain} over @{term Cons}.›
lemma mk_ps_chain_cons:
  "mk_ps_chain Π (x # π) = mk_ps_chain (cSnoc Π x (next_ps (post Π) x)) π"
by (auto simp add: mk_ps_chain_def)

text ‹Distributing @{term mk_ps_chain} over @{term snoc}.›
lemma mk_ps_chain_snoc:
  "mk_ps_chain Π (π @ [x])
    = cSnoc (mk_ps_chain Π π) x (next_ps (post (mk_ps_chain Π π)) x)"
by (unfold mk_ps_chain_def, auto)

text ‹Distributing @{term mk_ps_chain} over @{term cCons}.›
lemma mk_ps_chain_ccons:
  fixes π Π
  shows "mk_ps_chain ( σ   x  Π) π =  σ   x  mk_ps_chain Π π "
by (induct π arbitrary: Π, auto simp add: mk_ps_chain_cons mk_ps_chain_def)

lemma pre_mk_ps_chain:
  fixes Π π
  shows "pre (mk_ps_chain Π π) = pre Π"
apply (induct π arbitrary: Π)
apply (auto simp add: mk_ps_chain_def mk_ps_chain_cons pre_snoc)
done

text ‹A chain which is obtained from the list @{term π}, has @{term π}
  as its list of commands. The following lemma states this in a slightly
  more general form, that allows for part of the chain to have already
  been processed.›

lemma comlist_mk_ps_chain:
  "comlist (mk_ps_chain Π π) = comlist Π @ π"
proof (induct π arbitrary: Π)
  case Nil
  thus ?case by (auto simp add: mk_ps_chain_def)
next
  case (Cons x π')
  show ?case
  apply (unfold mk_ps_chain_def foldl.simps, fold mk_ps_chain_def)
  apply (auto simp add: Cons comlist_snoc)
  done
qed

text ‹In order to perform induction over our diagrams, we shall wish
  to obtain ``smaller'' diagrams, by removing nodes or edges. However, the
  syntax and well-formedness constraints for diagrams are such that although
  we can always remove an edge from a diagram, we cannot (in general) remove
  a node -- the resultant diagram would not be a well-formed if an edge
  connected to that node.

  Hence, we consider ``partially-processed diagrams'' @{term "(G,S)"}, which
  comprise a diagram @{term G} and a set @{term S} of nodes. @{term S} denotes
  the subset of @{term G}'s initial nodes that have already been processed,
  and can be thought of as having been removed from @{term G}.

  We now give an updated version of the @{term "lins G"} function. This was
  originally defined in @{theory Ribbon_Proofs.Ribbons_Graphical}. We provide an extra
  parameter, @{term S}, which denotes the subset of @{term G}'s initial nodes
  that shouldn't be included in the linear extensions.›

definition lins2 :: "[node fset, diagram]  lin set"
where
  "lins2 S G  {π :: lin .
    (distinct π)
   (set π = (fset G^V - fset S) <+> set G^E)
   (i j v e. i < length π  j < length π
     π!i = Inl v  π!j = Inr e  v |∈| fst3 e  i<j)
   (j k w e. j < length π  k < length π
     π!j = Inr e  π!k = Inl w  w |∈| thd3 e  j<k) }"

lemma lins2D:
  assumes "π  lins2 S G"
  shows "distinct π"
    and "set π = (fset G^V - fset S) <+> set G^E"
    and "i j v e.  i < length π ; j < length π ;
      π!i = Inl v ; π!j = Inr e ; v |∈| fst3 e   i<j"
    and "i k w e.  j < length π ; k < length π ;
      π!j = Inr e ; π!k = Inl w ; w |∈| thd3 e   j<k"
using assms
apply (unfold lins2_def Collect_iff)
apply (elim conjE, assumption)+
apply blast+
done

lemma lins2I:
  assumes "distinct π"
    and "set π = (fset G^V - fset S) <+> set G^E"
    and "i j v e.  i < length π ; j < length π ;
      π!i = Inl v ; π!j = Inr e ; v |∈| fst3 e   i<j"
    and "j k w e.  j < length π ; k < length π ;
      π!j = Inr e ; π!k = Inl w ; w |∈| thd3 e   j<k"
  shows "π  lins2 S G"
using assms
apply (unfold lins2_def Collect_iff, intro conjI)
apply assumption+
apply blast+
done

text ‹When @{term S} is empty, the two definitions coincide.›
lemma lins_is_lins2_with_empty_S:
  "lins G = lins2 {||} G"
by (unfold lins_def lins2_def, auto)

text ‹The first proofstate for a diagram @{term G} is obtained by
  mapping each of its initial nodes to @{term Top}.›
definition
  initial_ps :: "diagram  proofstate"
where
  "initial_ps G  [ initials G |=> Top ]"

text ‹The first proofstate for the partially-processed diagram @{term G} is
  obtained by mapping each of its initial nodes to @{term Top}, except those
  in @{term S}, which are mapped to @{term Bot}.›
definition
  initial_ps2 :: "[node fset, diagram]  proofstate"
where
  "initial_ps2 S G  [ initials G - S |=> Top ] ++f [ S |=> Bot ]"

text ‹When @{term S} is empty, the above two definitions coincide.›
lemma initial_ps_is_initial_ps2_with_empty_S:
  "initial_ps = initial_ps2 {||}"
apply (unfold fun_eq_iff, intro allI)
apply (unfold initial_ps_def initial_ps2_def)
apply simp
done

text ‹The following function extracts the set of proofstate chains from
   a diagram.›
definition
  ps_chains :: "diagram  ps_chain set"
where
  "ps_chains G  mk_ps_chain (cNil (initial_ps G)) ` lins G"

text ‹The following function extracts the set of proofstate chains from
   a partially-processed diagram. Nodes in @{term S} are excluded from
   the resulting chains.›
definition
  ps_chains2 :: "[node fset, diagram]  ps_chain set"
where
  "ps_chains2 S G  mk_ps_chain (cNil (initial_ps2 S G)) ` lins2 S G"

text ‹When @{term S} is empty, the above two definitions coincide.›
lemma ps_chains_is_ps_chains2_with_empty_S:
  "ps_chains = ps_chains2 {||}"
apply (unfold fun_eq_iff, intro allI)
apply (unfold ps_chains_def ps_chains2_def)
apply (fold initial_ps_is_initial_ps2_with_empty_S)
apply (fold lins_is_lins2_with_empty_S)
apply auto
done

text ‹We now wish to describe proofstates chain that are well-formed. First,
  let us say that @{term "f ++fdisjoint g"} is defined, when @{term f} and
  @{term g} have disjoint domains, as @{term "f ++f g"}. Then, a well-formed
  proofstate chain consists of triples of the form @{term "(σ ++fdisjoint
  [{| v |} |=> Top], Inl v, σ ++fdisjoint [{| v |} |=> Bot])"}, where @{term v}
  is a node, or of the form @{term "(σ ++fdisjoint [{| vs |} |=> Bot], Inr e,
  σ ++fdisjoint [{| ws |} |=> Top])"}, where @{term e} is an edge with source
  and target nodes @{term vs} and @{term ws} respectively.

  The definition below describes a well-formed triple; we then lift this
  to complete chains shortly.›

definition
  wf_ps_triple :: "proofstate × (node + edge) × proofstate  bool"
where
  "wf_ps_triple T = (case snd3 T of
    Inl v  (σ. v |∉| fmdom σ
       fst3 T = [ {|v|} |=> Top ] ++f σ
       thd3 T = [ {|v|} |=> Bot ] ++f σ)
  | Inr e  (σ. (fst3 e |∪| thd3 e) |∩| fmdom σ = {||}
       fst3 T = [ fst3 e |=> Bot ] ++f σ
       thd3 T = [ thd3 e |=> Top ] ++f σ))"

lemma wf_ps_triple_nodeI:
  assumes "σ. v |∉| fmdom σ  
    σ1 = [ {|v|} |=> Top ] ++f σ 
    σ2 = [ {|v|} |=> Bot ] ++f σ"
  shows "wf_ps_triple (σ1, Inl v, σ2)"
using assms unfolding wf_ps_triple_def
by (auto simp add: fst3_simp snd3_simp thd3_simp)

lemma wf_ps_triple_edgeI:
  assumes "σ. (fst3 e |∪| thd3 e) |∩| fmdom σ = {||}
       σ1 = [ fst3 e |=> Bot ] ++f σ
       σ2 = [ thd3 e |=> Top ] ++f σ"
  shows "wf_ps_triple (σ1, Inr e, σ2)"
using assms unfolding wf_ps_triple_def
by (auto simp add: fst3_simp snd3_simp thd3_simp)

definition
  wf_ps_chain :: "ps_chain  bool"
where
  "wf_ps_chain  chain_all wf_ps_triple"

lemma next_initial_ps2_vertex:
  "initial_ps2 ({|v|} |∪| S) G
  = initial_ps2 S G  {|v|} ++f [ {|v|} |=> Bot ]"
apply (unfold initial_ps2_def)
apply transfer
apply (auto simp add: make_map_def map_diff_def map_add_def restrict_map_def)
done

lemma next_initial_ps2_edge:
  assumes "G = Graph V Λ E" and "G' = Graph V' Λ E'" and
    "V' = V - fst3 e" and "E' = removeAll e E" and "e  set E" and
    "fst3 e |⊆| S" and "S |⊆| initials G" and "wf_dia G"
  shows "initial_ps2 (S - fst3 e) G' =
  initial_ps2 S G  fst3 e ++f [ thd3 e |=> Top ]"
proof (insert assms, unfold initial_ps2_def, transfer)
  fix G V Λ E G' V' E' e S
  assume G_def: "G = Graph V Λ E" and G'_def: "G' = Graph V' Λ E'" and
    V'_def: "V' = V - fst3 e" and E'_def: "E' = removeAll e E" and
    e_in_E: "e  set E" and fst_e_in_S: "fst3 e |⊆| S" and
    S_initials: "S |⊆| initials G" and wf_G: "wf_dia G"
  have "thd3 e |∩| initials G = {||}"
    by (auto simp add: initials_def G_def e_in_E)
  show "make_map (initials G' - (S - fst3 e)) Top ++ make_map (S - fst3 e) Bot
    = map_diff (make_map (initials G - S) Top ++ make_map S Bot) (fst3 e)
        ++ make_map (thd3 e) Top"
  apply (unfold make_map_def map_diff_def)
  apply (unfold map_add_def restrict_map_def)
  apply (unfold minus_fset)
  apply (unfold fun_eq_iff initials_def)
  apply (unfold G_def G'_def V'_def E'_def)
  apply (unfold edges.simps vertices.simps)
  apply (simp add: less_eq_fset.rep_eq e_in_E)
  apply safe
  apply (insert thd3 e |∩| initials G = {||})[1]
  apply (insert S_initials, fold fset_cong)[2]
  apply (unfold less_eq_fset.rep_eq initials_def filter_fset)
  apply (auto simp add: G_def e_in_E)[1]
  apply (auto simp add: G_def e_in_E)[1]
  apply (auto simp add: G_def e_in_E)[1]
  apply (insert wf_G)[1]
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(3))
  apply (unfold acyclicity_def)
  apply (metis fst_e_in_S inter_fset le_iff_inf subsetD)
  apply (insert wf_G)[1]
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(4))
  apply (drule linearityD2)
  apply (fold fset_cong, unfold inter_fset fset_simps)
  apply (insert e_in_E, blast)[1]
  apply (insert wf_G)[1]
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(3))
  apply (metis (lifting) e_in_E G_def empty_iff fset_simps(1)
    finter_iff linearityD(2) wf_G wf_dia_inv(4))
  apply (insert wf_G)[1]
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(4))
  apply (drule linearityD2)
  apply (fold fset_cong, unfold inter_fset fset_simps)
  apply (insert e_in_E, blast)[1]
  apply (insert wf_G)[1]
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(3))
  apply (metis (lifting) e_in_E G_def empty_iff fset_simps(1)
    finter_iff linearityD(2) wf_G wf_dia_inv(4))
  apply (insert wf_G)
  apply (unfold G_def vertices.simps edges.simps)
  apply (drule wf_dia_inv(5))
  apply (unfold less_eq_fset.rep_eq union_fset)
  apply auto[1]
  apply (drule wf_dia_inv(5))
  apply (unfold less_eq_fset.rep_eq union_fset)
  apply auto[1]
  apply (drule wf_dia_inv(5))
  apply (unfold less_eq_fset.rep_eq union_fset)
  apply (auto simp add: e_in_E)[1]
  apply (drule wf_dia_inv(5))
  apply (unfold less_eq_fset.rep_eq union_fset)
  apply (auto simp add: e_in_E)[1]
  done
qed

lemma next_lins2_vertex:
  assumes "Inl v # π  lins2 S G"
  assumes "v |∉| S"
  shows "π  lins2 ({|v|} |∪| S) G"
proof -
  note lins2D = lins2D[OF assms(1)]
  show ?thesis
  proof (intro lins2I)
    show "distinct π" using lins2D(1) by auto
  next
    have "set π = set (Inl v # π) - {Inl v}" using lins2D(1) by auto
    also have "... = (fset G^V - fset ({|v|} |∪| S)) <+> set G^E"
      using lins2D(2) by auto
    finally show "set π = (fset G^V - fset ({|v|} |∪| S)) <+> set G^E"
      by auto
  next
    fix i j v e
    assume "i < length π" "j < length π" "π ! i = Inl v"
      "π ! j = Inr e" "v |∈| fst3 e"
    thus "i < j" using lins2D(3)[of "i+1" "j+1"] by auto
  next
    fix j k w e
    assume "j < length π" "k < length π" "π ! j = Inr e"
      "π ! k = Inl w" "w |∈| thd3 e"
    thus "j < k" using lins2D(4)[of "j+1" "k+1"] by auto
  qed
qed

lemma next_lins2_edge:
  assumes "Inr e # π  lins2 S (Graph V Λ E)"
      and "vs |⊆| S"
      and "e = (vs,c,ws)"
  shows "π  lins2 (S - vs) (Graph (V - vs) Λ (removeAll e E))"
proof -
  note lins2D = lins2D[OF assms(1)]
  show ?thesis
  proof (intro lins2I, unfold vertices.simps edges.simps)
    show "distinct π"
    using lins2D(1) by auto
  next
    show "set π = (fset (V - vs) - fset (S - vs))
      <+> set (removeAll e E)"
    apply (insert lins2D(1) lins2D(2) assms(2))
    apply (unfold assms(3) vertices.simps edges.simps less_eq_fset.rep_eq, simp)
    apply (unfold diff_diff_eq)
    proof -
      have "a aa b.
       insert (Inr (vs, c, ws)) (set π) = (fset V - fset S) <+> set E 
       fset vs  fset S 
       Inr (vs, c, ws)  set π 
       distinct π  (a, aa, b)  set E  Inr (a, aa, b)  set π  b = ws"
     by (metis (lifting) InrI List.set_simps(2)
      prod.inject set_ConsD sum.simps(2))

     moreover have "a aa b.
       insert (Inr (vs, c, ws)) (set π) = (fset V - fset S) <+> set E 
       fset vs  fset S 
       Inr (vs, c, ws)  set π 
       distinct π  (a, aa, b)  set E  Inr (a, aa, b)  set π  aa = c"
     by (metis (lifting) InrI List.set_simps(2)
      prod.inject set_ConsD sum.simps(2))

     moreover have "x. insert (Inr (vs, c, ws)) (set π) = (fset V - fset S) <+> set E 
         fset vs  fset S 
         Inr (vs, c, ws)  set π 
         distinct π  x  set π  x  (fset V - fset S) <+> set E - {(vs, c, ws)}"
      apply (unfold insert_is_Un[of _ "set π"])
      apply (fold assms(3))
      apply clarify
      apply (subgoal_tac "set π = ((fset V - fset S) <+> set E) - {Inr e}")
      by auto
    ultimately show "Inr (vs, c, ws)  set π  distinct π 
      insert (Inr (vs, c, ws)) (set π) = (fset V - fset S) <+> set E 
      fset vs  fset S  set π = (fset V - fset S) <+> set E - {(vs, c, ws)}"
    by blast
    qed
  next
    fix i j v e
    assume "i < length π" "j < length π" "π ! i = Inl v"
      "π ! j = Inr e" "v |∈| fst3 e"
    thus "i < j" using lins2D(3)[of "i+1" "j+1"] by auto
  next
    fix j k w e
    assume "j < length π" "k < length π" "π ! j = Inr e"
      "π ! k = Inl w" "w |∈| thd3 e"
    thus "j < k" using lins2D(4)[of "j+1" "k+1"] by auto
  qed
qed


text ‹We wish to prove that every proofstate chain that can be obtained from
  a linear extension of @{term G} is well-formed and has as its final
  proofstate that state in which every terminal node in @{term G} is mapped
  to @{term Bot}.

  We first prove this for partially-processed diagrams, for
  then the result for ordinary diagrams follows as an easy corollary.

  We use induction on the size of the partially-processed diagram. The size of
  a partially-processed diagram @{term "(G,S)"} is defined as the number of
  nodes in @{term G}, plus the number of edges, minus the number of nodes in
  @{term S}.›

lemma wf_chains2:
  fixes k
  assumes "S |⊆| initials G"
      and "wf_dia G"
      and "Π  ps_chains2 S G"
      and "fcard G^V + length G^E = k + fcard S"
  shows "wf_ps_chain Π  (post Π = [ terminals G |=> Bot ])"
using assms
proof (induct k arbitrary: S G Π)
  case 0
  obtain V Λ E where G_def: "G = Graph V Λ E" by (metis diagram.exhaust)
  have "S |⊆| V"
    using "0.prems"(1) initials_in_vertices[of "G"]
    by (auto simp add: G_def)
  have "fcard V  fcard S"
    using "0.prems"(4)
    by (unfold G_def, auto)
  from fcard_seteq[OF S |⊆| V this] have "S = V" by auto
  hence "E = []" using "0.prems"(4) by (unfold G_def, auto)
  have "initials G = V"
    by (unfold G_def E=[], rule no_edges_imp_all_nodes_initial)
  have "terminals G = V"
    by (unfold G_def E=[], rule no_edges_imp_all_nodes_terminal)
  have "{} <+> {} = {}" by auto
  have "lins2 S G = { [] }"
  apply (unfold G_def S=V E=[])
  apply (unfold lins2_def, auto simp add: {} <+> {} = {})
  done
  hence Π_def: "Π =  initial_ps2 S G "
    using "0.prems"(3)
    by (auto simp add: ps_chains2_def mk_ps_chain_def)
  show ?case
  apply (intro conjI)
  apply (unfold Π_def wf_ps_chain_def, auto)
  apply (unfold post.simps initial_ps2_def initials G = V terminals G = V)
  apply (unfold S=V)
  apply (subgoal_tac "V - V = {||}", simp_all)
  done
next
  case (Suc k)
  obtain V Λ E where G_def: "G = Graph V Λ E" by (metis diagram.exhaust)
  from Suc.prems(3) obtain π where
    Π_def: "Π = mk_ps_chain  initial_ps2 S G  π" and
    π_in: "π  lins2 S G"
    by (auto simp add: ps_chains2_def)
  note lins2 = lins2D[OF π_in]
  have "S |⊆| V"
    using Suc.prems(1) initials_in_vertices[of "G"]
    by (auto simp add: G_def)
  show ?case
  proof (cases π)
    case Nil
    from π_in have "V = S" "E = []"
    apply (-, unfold π = [] lins2_def, simp_all)
    apply (unfold empty_eq_Plus_conv)
    apply (unfold G_def vertices.simps edges.simps, auto)
    by (metis S |⊆| V less_eq_fset.rep_eq subset_antisym)

    with Suc.prems(4) have False by (simp add: G_def)
    thus ?thesis by auto
  next
    case (Cons x π')
    note π_def = this
    show ?thesis
    proof (cases x)
      case (Inl v)
      note x_def = this

      have "v |∉| S  v |∈| V"
      apply (subgoal_tac "v  fset V - fset S")
      apply (simp)
      apply (subgoal_tac "Inl v  (fset V - fset S) <+> set E")
      apply (metis Inl_inject Inr_not_Inl PlusE)
      apply (metis lins2(1) lins2(2) Cons G_def Inl distinct.simps(2)
        distinct_length_2_or_more edges.simps vertices.simps)
      done
      hence v_notin_S: "v |∉| S" and v_in_V: "v |∈| V" by auto

      have v_initial_not_S: "v |∈| initials G - S"
      apply (simp only: G_def initials_def vertices.simps edges.simps)
      apply (simp only: fminus_iff)
      apply (simp only: conj_commute, intro conjI, rule v_notin_S)
      apply (subgoal_tac
        "v  fset (ffilter (λv. eset E. v |∉| thd3 e) V)")
      apply simp
      apply (simp only: filter_fset, simp, simp only: conj_commute)
      apply (intro conjI ballI notI)
      apply (insert v_in_V, simp)
      proof -
        fix e :: edge
        assume "v  fset (thd3 e)"
        then have "v |∈| (thd3 e)" by auto
        assume "e  set E"
        hence "Inr e  set π" using lins2(2) by (auto simp add: G_def)
        then obtain j where
          "j < length π" "0 < length π" "π!j = Inr e" "π!0 = Inl v"
        by (metis π_def x_def in_set_conv_nth length_pos_if_in_set nth_Cons_0)
        with lins2(4)[OF this v |∈| (thd3 e)] show False by auto
      qed

      define S' where "S' = {|v|} |∪| S"

      define Π' where "Π' = mk_ps_chain  initial_ps2 S' G  π'"
      hence pre_Π': "pre Π' = initial_ps2 S' G"
      by (metis pre.simps(1) pre_mk_ps_chain)

      define σ where "σ = [ initials G - ({|v|} |∪| S) |=> Top ] ++f [ S |=> Bot ]"

      have "wf_ps_chain Π'  (post Π' = [terminals G |=> Bot])"
      proof (intro Suc.hyps[of "S'"])
        show "S' |⊆| initials G"
        apply (unfold S'_def, auto)
        apply (metis fminus_iff v_initial_not_S)
        by (metis Suc.prems(1) fset_rev_mp)
     next
        show "wf_dia G" by (rule Suc.prems(2))
      next
        show "Π'  ps_chains2 S' G"
        apply (unfold ps_chains2_def Π'_def)
        apply (intro imageI)
        apply (unfold S'_def)
        apply (intro next_lins2_vertex)
        apply (fold x_def, fold π_def)
        apply (rule π_in)
        by (metis v_notin_S)
      next
        show "fcard G^V + length G^E = k + fcard S'"
         apply (unfold S'_def)
         by (auto simp add: Suc.prems(4) fcard_finsert_disjoint[OF v_notin_S])
      qed
      hence
        wf_Π': "wf_ps_chain Π'" and
        post_Π': "post Π' = [terminals G |=> Bot]"
      by auto

      show ?thesis
      proof (intro conjI)
        have 1: "fmdom [ {|v|} |=> Bot ]
        |∩| fmdom ([ initials G - ({|v|} |∪| S) |=> Top ] ++f
     [ S |=> Bot ]) = {||}"
        by (metis (no_types) fdom_make_fmap fmdom_add
          bot_least funion_iff finter_finsert_left le_iff_inf
          fminus_iff finsert_fsubset sup_ge1 v_initial_not_S)
        show "wf_ps_chain Π"
        using [[unfold_abs_def = false]]
        apply (simp only: Π_def π_def x_def mk_ps_chain_cons)
        apply simp
        apply (unfold mk_ps_chain_ccons)
        apply (fold next_initial_ps2_vertex S'_def)
        apply (fold Π'_def)
        apply (unfold wf_ps_chain_def chain_all.simps conj_commute)
        apply (intro conjI)
        apply (fold wf_ps_chain_def, rule wf_Π')
        apply (intro wf_ps_triple_nodeI exI[of _ "σ"] conjI)
        apply (unfold σ_def fmdom_add fdom_make_fmap)
        apply (metis finsertI1 fminus_iff funion_iff v_notin_S)
        apply (unfold pre_Π' initial_ps2_def S'_def)
        apply (unfold fmap_add_commute[OF 1])
        apply (unfold fmadd_assoc)
        apply (fold fmadd_assoc[of _ "[ S |=> Bot ]"])
        apply (unfold make_fmap_union sup.commute[of "{|v|}"])
        apply (unfold fminus_funion)
        using v_initial_not_S apply auto
        by (metis (opaque_lifting, no_types) finsert_absorb finsert_fminus_single finter_fminus
            inf_commute inf_idem v_initial_not_S)
      next
        show "post Π = [ terminals G |=> Bot ]"
        apply (unfold Π_def π_def x_def mk_ps_chain_cons, simp)
        apply (unfold mk_ps_chain_ccons post.simps)
        apply (fold next_initial_ps2_vertex S'_def)
        apply (fold Π'_def, rule post_Π')
        done
      qed
    next
      case (Inr e)
      note x_def = this
      define vs where "vs = fst3 e"
      define ws where "ws = thd3 e"

      obtain c where e_def: "e = (vs, c, ws)"
      by (metis vs_def ws_def fst3_simp thd3_simp prod_cases3)

      have "linearity E" and "acyclicity E" and
        e_in_V: "e. e  set E  fst3 e |∪| thd3 e |⊆| V"
      by (insert Suc.prems(2) wf_dia_inv, unfold G_def, blast)+
      note lin = linearityD[OF this(1)]

      have acy: "e. e  set E  fst3 e |∩| thd3 e = {||}"
      apply (fold fset_cong, insert acyclicity E)
      apply (unfold acyclicity_def acyclic_def, auto)
      done

      note lins = lins2D[OF π_in]

      have e_in_E: "e  set E"
      apply (subgoal_tac "set π = (fset G^V - fset S) <+> set G^E")
      apply (unfold π_def x_def G_def edges.simps, auto)[1]
      apply (simp add: lins(2))
      done

      have vs_in_S: "vs |⊆| S"
      apply (insert e_in_V[OF e_in_E])
      apply (unfold less_eq_fset.rep_eq)
      apply (intro subsetI)
      apply (unfold vs_def)
      apply (rule ccontr)
      apply (subgoal_tac "x  fset V")
      prefer 2
      apply (auto)
      proof -
        fix v
        assume a: "v  fset (fst3 e)"
        assume "v  fset S" and "v  fset V"
        hence "Inl v  set π"
        by (metis (lifting) DiffI G_def InlI lins(2) vertices.simps)
        then obtain i where
          "i < length π" "0 < length π" "π!i = Inl v"  "π!0 = Inr e"
        by (metis Cons Inr in_set_conv_nth length_pos_if_in_set nth_Cons_0)
        from lins(3)[OF this] show "False" by (auto simp add: a)
      qed

      have "ws |∩| (initials G) = {||}"
      apply (insert e_in_V[OF e_in_E])
      apply (unfold initials_def less_eq_fset.rep_eq, fold fset_cong)
      apply (unfold ws_def G_def, auto simp add: e_in_E)
      done

      define S' where "S' = S - vs"
      define V' where "V' = V - vs"
      define E' where "E' = removeAll e E"
      define G' where "G' = Graph V' Λ E'"

      define Π' where "Π' = mk_ps_chain  initial_ps2 S' G'  π'"
      hence pre_Π': "pre Π' = initial_ps2 S' G'"
      by (metis pre.simps(1) pre_mk_ps_chain)

      define σ where "σ = [ initials G - S |=> Top ] ++f [ S - vs |=> Bot ]"

      have next_initial_ps2: "initial_ps2 S' G'
        = initial_ps2 S G  vs ++f [ws |=> Top]"
      using next_initial_ps2_edge[OF G_def _ _ _ e_in_E _ Suc.prems(1)
        Suc.prems(2)] G'_def E'_def vs_def ws_def V'_def vs_in_S S'_def
      by auto

      have "wf_ps_chain Π'  post Π' = [ terminals G' |=> Bot ]"
      proof (intro Suc.hyps[of "S'"])
        show "S' |⊆| initials G'"
        apply (insert Suc.prems(1))
        apply (unfold G'_def G_def initials_def)
        apply (unfold less_eq_fset.rep_eq S'_def E'_def V'_def)
        apply auto
        done
      next
        from Suc.prems(2) have "wf_dia (Graph V Λ E)"
          by (unfold G_def)
        note wf_G = wf_dia_inv[OF this]
        show "wf_dia G'"
        apply (unfold G'_def V'_def E'_def)
        apply (insert wf_G e_in_E vs_in_S Suc.prems(1))
        apply (unfold vs_def)
        apply (intro wf_dia)
        apply (unfold linearity_def initials_def G_def)
        apply (fold fset_cong, unfold less_eq_fset.rep_eq)
        apply (simp, simp)
        apply (unfold acyclicity_def, rule acyclic_subset)
        apply (auto simp add: distinct_removeAll)
        apply (metis (lifting) IntI empty_iff)
        done
      next
        show "Π'  ps_chains2 S' G'"
        apply (unfold Π_def Π'_def ps_chains2_def)
        apply (intro imageI)
        apply (unfold S'_def G'_def V'_def E'_def)
        apply (intro next_lins2_edge)
        apply (metis π_def G_def x_def π_in)
        by (simp only: vs_in_S e_def)+
      next
        have "vs |⊆| V" by (metis (lifting) S |⊆| V order_trans vs_in_S)
        have "distinct E" using linearity E linearity_def by auto
        show "fcard G'^V + length G'^E = k + fcard S'"
        apply (insert Suc.prems(4))
        apply (unfold G_def G'_def vertices.simps edges.simps)
        apply (unfold V'_def E'_def S'_def)
        apply (unfold fcard_funion_fsubset[OF vs |⊆| V])
        apply (unfold fcard_funion_fsubset[OF vs |⊆| S])
        apply (fold distinct_remove1_removeAll[OF distinct E])
        apply (unfold length_remove1)
        apply (simp add: e_in_E)
        apply (drule arg_cong[of _ _ "λx. x - fcard vs - 1"])
        apply (subst (asm) add_diff_assoc2[symmetric])
        apply (simp add: fcard_mono[OF vs |⊆| V])
        apply (subst add_diff_assoc, insert length_pos_if_in_set[OF e_in_E], arith, auto)
        apply (subst add_diff_assoc, auto simp add: fcard_mono[OF vs |⊆| S])
        done
      qed
      hence
        wf_Π': "wf_ps_chain Π'" and
        post_Π': "post Π' = [ terminals G' |=> Bot ]"
      by auto

      have terms_same: "terminals G = terminals G'"
      apply (unfold G'_def G_def terminals_def edges.simps vertices.simps)
      apply (unfold E'_def V'_def)
      apply (fold fset_cong, auto simp add: e_in_E vs_def)
      done

      have 1: "fmdom [ fst3 e |=> Bot ] |∩|
        fmdom([ ffilter (λv. eset E. v |∉| thd3 e) V - S |=> Top ]
        ++f [ S - fst3 e |=> Bot ]) = {||}"
      apply (unfold fmdom_add fdom_make_fmap)
      apply (fold fset_cong)
      apply auto
      apply (metis in_mono less_eq_fset.rep_eq vs_def vs_in_S)
      done

      show ?thesis
      proof (intro conjI)
        show "wf_ps_chain Π"
        using [[unfold_abs_def = false]]
        apply (unfold Π_def π_def x_def mk_ps_chain_cons)
        apply simp
        apply (unfold mk_ps_chain_ccons)
        apply (fold vs_def ws_def)
        apply (fold next_initial_ps2)
        apply (fold Π'_def)
        apply (unfold wf_ps_chain_def chain_all.simps conj_commute)
        apply (intro conjI)
        apply (fold wf_ps_chain_def)
        apply (rule wf_Π')
        apply (intro wf_ps_triple_edgeI exI[of _ "σ"])
        apply (unfold e_def fst3_simp thd3_simp σ_def, intro conjI)
        apply (insert Suc.prems(1))
        apply (unfold pre_Π' initial_ps2_def initials_def)
        apply (insert vs_in_S acy[OF e_in_E])
        apply (fold fset_cong)
        apply (unfold less_eq_fset.rep_eq)[1]
        apply (unfold G_def G'_def vs_def ws_def V'_def E'_def S'_def)
        apply (unfold vertices.simps edges.simps)
        apply (unfold fmap_add_commute[OF 1])
        apply (fold fmadd_assoc)
        apply (unfold make_fmap_union)
        apply (auto simp add: fdom_make_fmap e_in_E)[1]
        apply simp
        apply (unfold fmadd_assoc)
        apply (unfold make_fmap_union)
        apply (metis (lifting) funion_absorb2 vs_def vs_in_S)
        apply (intro arg_cong2[of _ _ "[ S - fst3 e |=> Bot ]"
            "[ S - fst3 e |=> Bot ]" "(++f)"])
        apply (intro arg_cong2[of _ _ "Top" "Top" "make_fmap"])
        defer 1
        apply (simp, simp)
        apply (fold fset_cong)
        apply (unfold less_eq_fset.rep_eq, simp)
        apply (elim conjE)
        apply (intro set_eqI iffI, simp_all)
        apply (elim conjE, intro disjI conjI ballI, simp)
        apply (case_tac "ea=e", simp_all)
        apply (elim disjE conjE, intro conjI ballI impI, simp_all)
        apply (insert e_in_E lin(2))[1]
        apply (subst (asm) (2) fset_cong[symmetric])
        apply (elim conjE)
        apply (subst (asm) inter_fset)
        apply (subst (asm) fset_simps)
        apply (insert disjoint_iff_not_equal)[1]
        apply blast
        apply (metis G_def Suc(3) e_in_E subsetD less_eq_fset.rep_eq wf_dia_inv')
        prefer 2
        apply (metis (lifting) IntI Suc(2) ws |∩| initials G = {||}
            empty_iff fset_simps(1) in_mono inter_fset less_eq_fset.rep_eq ws_def)
        apply auto
        done
      next
        show "post Π = [terminals G |=> Bot]"
        apply (unfold Π_def π_def x_def mk_ps_chain_cons)
        apply simp
        apply (unfold mk_ps_chain_ccons post.simps)
        apply (fold vs_def ws_def)
        apply (fold next_initial_ps2)
        apply (fold Π'_def)
        apply (unfold terms_same)
        apply (rule post_Π')
        done
      qed
    qed
  qed
qed

corollary wf_chains:
  assumes "wf_dia G"
  assumes "Π  ps_chains G"
  shows "wf_ps_chain Π  post Π = [ terminals G |=> Bot ]"
apply (intro wf_chains2[of "{||}"], insert assms(2))
by (auto simp add: assms(1) ps_chains_is_ps_chains2_with_empty_S fcard_fempty)


subsection ‹Interface chains›

type_synonym int_chain = "(interface, assertion_gadget + command_gadget) chain"

text ‹An interface chain is similar to a proofstate chain. However, where a
  proofstate chain talks about nodes and edges, an interface chain talks about
  the assertion-gadgets and command-gadgets that label those nodes and edges
  in a diagram. And where a proofstate chain talks about proofstates, an
  interface chain talks about the interfaces obtained from those proofstates.

  The following functions convert a proofstate chain into an
  interface chain.›

definition
  ps_to_int :: "[diagram, proofstate]  interface"
where
  "ps_to_int G σ 
    v |∈| fmdom σ. case_topbot top_ass bot_ass (lookup σ v) (G v)"

definition
  ps_chain_to_int_chain :: "[diagram, ps_chain]  int_chain"
where
  "ps_chain_to_int_chain G Π 
    chainmap (ps_to_int G) ((case_sum (Inl  G) (Inr  snd3))) Π"

lemma ps_chain_to_int_chain_simp:
  "ps_chain_to_int_chain (Graph V Λ E) Π =
    chainmap (ps_to_int (Graph V Λ E)) ((case_sum (Inl  Λ) (Inr  snd3))) Π"
by (simp add: ps_chain_to_int_chain_def)

subsection ‹Soundness proof›

text ‹We assume that @{term wr_com} always returns @{term "{}"}. This is
  equivalent to changing our axiomatization of separation logic such that the
  frame rule has no side-condition. One way to obtain a separation logic
  lacking a side-condition on its frame rule is to use variables-as-
  resource.

  We proceed by induction on the proof rules for graphical diagrams. We
  show that: (1) if a diagram @{term G} is provable w.r.t. interfaces
  @{term P} and @{term Q}, then @{term P} and @{term Q} are the top and bottom
  interfaces of @{term G}, and that the Hoare triple @{term "(asn P,
  c, asn Q)"} is provable for each command @{term c} that can be extracted
  from @{term G}; (2) if a command-gadget @{term C} is provable w.r.t.
  interfaces @{term P} and @{term Q}, then the Hoare triple @{term "(asn P,
  c, asn Q)"} is provable for each command @{term c} that can be extracted
  from @{term C}; and (3) if an assertion-gadget @{term A} is provable, and if
  the top and bottom interfaces of @{term A} are @{term P} and @{term Q}
  respectively, then the Hoare triple @{term "(asn P, c, asn Q)"} is provable
  for each command @{term c} that can be extracted from @{term A}.›



lemma soundness_graphical_helper:
  assumes no_var_interference: "c. wr_com c = {}"
  shows
    "(prov_dia G P Q 
      (P = top_dia G  Q = bot_dia G 
      (c. coms_dia G c  prov_triple (asn P, c, asn Q))))
    (prov_com C P Q 
      (c. coms_com C c  prov_triple (asn P, c, asn Q)))
    (prov_ass A 
      (c. coms_ass A c  prov_triple (asn (top_ass A), c, asn (bot_ass A))))"
proof (induct rule: prov_dia_prov_com_prov_ass.induct)
  case (Skip p)
  thus ?case
  apply (intro allI impI, elim conjE coms_skip_inv)
  apply (auto simp add: prov_triple.skip)
  done
next
  case (Exists G P Q x)
  thus ?case
  apply (intro allI impI, elim conjE coms_exists_inv)
  apply (auto simp add: prov_triple.exists)
  done
next
  case (Basic P c Q)
  thus ?case
  by (intro allI impI, elim conjE coms_basic_inv, auto)
next
  case (Choice G P Q H)
  thus ?case
  apply (intro allI impI, elim conjE coms_choice_inv)
  apply (auto simp add: prov_triple.choose)
  done
next
  case (Loop G P)
  thus ?case
  apply (intro allI impI, elim conjE coms_loop_inv)
  apply (auto simp add: prov_triple.loop)
  done
next
  case (Main G)
  thus ?case
  apply (intro conjI)
  apply (simp, simp)
  apply (intro allI impI)
  apply (elim coms_main_inv, simp)
  proof -
    fix c V Λ E
    fix π::"lin"
    fix cs::"command list"
    assume wf_G: "wf_dia (Graph V Λ E)"
    assume "v. v  fset V  c. coms_ass (Λ v) c 
      prov_triple (asn (top_ass (Λ v)), c, asn (bot_ass (Λ v)))"
    hence prov_vertex: "v c P Q F.  coms_ass (Λ v) c; v  fset V;
      P = (top_ass (Λ v)  F) ; Q = (bot_ass (Λ v)  F) 
       prov_triple (asn P, c, asn Q)"
    by (auto simp add: prov_triple.frame no_var_interference)
    assume "e. e  set E  c. coms_com (snd3 e) c  prov_triple
      (asn (v|∈|fst3 e. bot_ass (Λ v)),c,asn (v|∈|thd3 e. top_ass (Λ v)))"
    hence prov_edge: "e c P Q F.  e  set E ; coms_com (snd3 e) c ;
      P = ((v|∈|fst3 e. bot_ass (Λ v))  F) ;
      Q = ((v|∈|thd3 e. top_ass (Λ v))  F) 
       prov_triple (asn P, c, asn Q)"
    by (auto simp add: prov_triple.frame no_var_interference)
    assume len_cs: "length cs = length π"
    assume "i<length π.
      case_sum (coms_ass  Λ) (coms_com  snd3) (π ! i) (cs ! i)"
    hence π_cs: "i. i < length π 
      case_sum (coms_ass  Λ) (coms_com  snd3) (π ! i) (cs ! i)" by auto
    assume G_def: "G = Graph V Λ E"
    assume c_def: "c = foldr (;;) cs Skip"
    assume π_lin: "π  lins (Graph V Λ E)"

    note lins = linsD[OF π_lin]

    define Π where "Π = mk_ps_chain  initial_ps G  π"

    have "Π  ps_chains G" by (simp add: π_lin Π_def ps_chains_def G_def)
    hence 1: "post Π = [ terminals G |=> Bot ]"
      and 2: "chain_all wf_ps_triple Π"
    by (insert wf_chains G_def wf_G, auto simp add: wf_ps_chain_def)

    show "prov_triple (asn (v|∈|initials (Graph V Λ E). top_ass (Λ v)),
      foldr (;;) cs Skip, asn (v|∈|terminals (Graph V Λ E). bot_ass (Λ v)))"
    using [[unfold_abs_def = false]]
    apply (intro seq_fold[of _ "ps_chain_to_int_chain G Π"])
    apply (unfold len_cs)
    apply (unfold ps_chain_to_int_chain_def chainmap_preserves_length Π_def)
    apply (unfold mk_ps_chain_preserves_length, simp)
    apply (unfold pre_chainmap post_chainmap)
    apply (unfold pre_mk_ps_chain pre.simps)
    apply (fold Π_def, unfold 1)
    apply (unfold initial_ps_def)
    apply (unfold ps_to_int_def)
    apply (unfold fdom_make_fmap)
    apply (unfold G_def labelling.simps, fold G_def)
    apply (subgoal_tac "v  fset (initials G). top_ass (Λ v) =
      case_topbot top_ass bot_ass (lookup [ initials G |=> Top ] v) (Λ v)")
    apply (unfold iter_hcomp_cong, simp)
    apply (metis lookup_make_fmap topbot.simps(3))
    apply (subgoal_tac "v  fset (terminals G). bot_ass (Λ v) =
      case_topbot top_ass bot_ass (lookup [ terminals G |=> Bot ] v) (Λ v)")
    apply (unfold iter_hcomp_cong, simp)
    apply (metis lookup_make_fmap topbot.simps(4), simp)
    apply (unfold G_def, fold ps_chain_to_int_chain_simp G_def)
    proof -
      fix i
      assume "i < length π"
      hence "i < chainlen Π"
      by (metis Π_def add_0_left chainlen.simps(1)
        mk_ps_chain_preserves_length)
      hence wf_Πi: "wf_ps_triple (nthtriple Π i)"
        by (insert 2, simp add: chain_all_nthtriple)
      show "prov_triple (asn (fst3 (nthtriple (ps_chain_to_int_chain G Π) i)),
                 cs ! i, asn (thd3 (nthtriple (ps_chain_to_int_chain G Π) i)))"
      apply (unfold ps_chain_to_int_chain_def)
      apply (unfold nthtriple_chainmap[OF i < chainlen Π])
      apply (unfold fst3_simp thd3_simp)
      proof (cases "π!i")
        case (Inl v)

        have "snd3 (nthtriple Π i) = Inl v"
        apply (unfold snds_of_triples_form_comlist[OF i < chainlen Π])
        apply (auto simp add: Π_def comlist_mk_ps_chain Inl)
        done

        with wf_Πi wf_ps_triple_def obtain σ where
          v_notin_σ: "v |∉| fmdom σ" and
          fst_Πi: "fst3 (nthtriple Π i) = [ {|v|} |=> Top ] ++f σ" and
          thd_Πi: "thd3 (nthtriple Π i) = [ {|v|} |=> Bot ] ++f σ" by auto

        show "prov_triple (asn (ps_to_int G (fst3 (nthtriple Π i))),
                   cs ! i, asn (ps_to_int G (thd3 (nthtriple Π i))))"
        apply (intro prov_vertex[where v=v])
        apply (metis (no_types) Inl i < length π π_cs o_def sum.simps(5))
        apply (metis (lifting) Inl lins(2) Inl_not_Inr PlusE i < length π
          nth_mem sum.simps(1) vertices.simps)
        apply (unfold fst_Πi thd_Πi)
        apply (unfold ps_to_int_def)
        apply (unfold fmdom_add fdom_make_fmap)
        apply (unfold finsert_is_funion[symmetric])
        apply (insert v_notin_σ)
        apply (unfold iter_hcomp_insert)
        apply (unfold lookup_union2 lookup_make_fmap1)
        apply (unfold G_def labelling.simps)
        apply (subgoal_tac "va  fset (fmdom σ). case_topbot top_ass bot_ass
          (lookup ([ {|v|} |=> Top ] ++f σ) va) (Λ va) =
          case_topbot top_ass bot_ass (lookup ([{|v|} |=> Bot] ++f σ) va)(Λ va)")
        apply (unfold iter_hcomp_cong, simp)
        apply (metis lookup_union1, simp)
        done
      next
        case (Inr e)
        have "snd3 (nthtriple Π i) = Inr e"
        apply (unfold snds_of_triples_form_comlist[OF i < chainlen Π])
        apply (auto simp add: Π_def comlist_mk_ps_chain Inr)
        done

        with wf_Πi wf_ps_triple_def obtain σ where
          fst_e_disjoint_σ: "fst3 e |∩| fmdom σ = {||}" and
          thd_e_disjoint_σ: "thd3 e |∩| fmdom σ = {||}" and
          fst_Πi: "fst3 (nthtriple Π i) = [ fst3 e |=> Bot ] ++f σ" and
          thd_Πi: "thd3 (nthtriple Π i) = [ thd3 e |=> Top ] ++f σ"
        by (auto simp add: inf_sup_distrib2)


        let ?F = "v|∈|fmdom σ.
          case_topbot top_ass bot_ass (lookup ([ fst3 e |=> Bot ] ++f σ) v) (Λ v)"

        show "prov_triple (asn (ps_to_int G (fst3 (nthtriple Π i))),
                   cs ! i, asn (ps_to_int G (thd3 (nthtriple Π i))))"
        proof (intro prov_edge[where e=e])
          show "e  set E"
            apply (subgoal_tac "Inr e  set π")
             apply (metis Inr_not_Inl PlusE edges.simps lins(2) sum.simps(2))
            by (metis Inr i < length π nth_mem)
        next
          show "coms_com (snd3 e) (cs ! i)"
            by (metis (no_types) Inr i < length π π_cs o_def sum.simps(6))
        next
          show "ps_to_int G (fst3 (nthtriple Π i)) = ((v|∈|fst3 e. bot_ass (Λ v))  ?F)"
            unfolding fst_Πi ps_to_int_def G_def labelling.simps fmdom_add fdom_make_fmap
            apply (insert fst_e_disjoint_σ)
            apply (unfold iter_hcomp_union)
            apply (subgoal_tac "v  fset (fst3 e). case_topbot top_ass bot_ass
          (lookup ([ fst3 e |=> Bot ] ++f σ) v) (Λ v) = bot_ass (Λ v)")
             apply (unfold iter_hcomp_cong)
             apply (simp)
            apply (intro ballI)
            apply (subgoal_tac "v |∉| fmdom σ")
             apply (unfold lookup_union2)
             apply (metis lookup_make_fmap topbot.simps(4))
            by (metis fempty_iff finterI)
        next
          show "ps_to_int G (thd3 (nthtriple Π i)) = ((v|∈|thd3 e. top_ass (Λ v))  ?F)"
            unfolding thd_Πi ps_to_int_def G_def labelling.simps fmdom_add fdom_make_fmap
            apply (insert thd_e_disjoint_σ)
            apply (unfold iter_hcomp_union)
            apply (subgoal_tac "v  fset (thd3 e). case_topbot top_ass bot_ass
          (lookup ([ thd3 e |=> Top ] ++f σ) v) (Λ v) = top_ass (Λ v)")
             apply (unfold iter_hcomp_cong)
             apply (subgoal_tac "v  fset (fmdom σ). case_topbot top_ass bot_ass
          (lookup ([ thd3 e |=> Top ] ++f σ) v) (Λ v) =
          case_topbot top_ass bot_ass (lookup ([fst3 e |=> Bot] ++f σ) v) (Λ v)")
              apply (unfold iter_hcomp_cong)
              apply simp
            subgoal
              by (simp add: lookup_union1)
            subgoal
              by (simp add: fdisjoint_iff lookup_union2 lookup_make_fmap)
            done
        qed
      qed
    qed
  qed
qed

text ‹The soundness theorem states that any diagram provable using the
  proof rules for ribbons can be recreated as a valid proof in separation
  logic.›

corollary soundness_graphical:
  assumes "c. wr_com c = {}"
  assumes "prov_dia G P Q"
  shows "c. coms_dia G c  prov_triple (asn P, c, asn Q)"
using soundness_graphical_helper[OF assms(1)] and assms(2) by auto

end