Theory FiniteGraph
theory FiniteGraph
imports Main 
begin
section ‹Specification of a finite directed graph›
text‹A graph ‹G=(V,E)› consits of a set of vertices ‹V›, also called nodes, 
       and a set of edges ‹E›. The edges are tuples of vertices. Both, 
       the set of vertices and edges is finite.›
section ‹Graph›
subsection‹Definitions›
  text ‹A graph is represented by a record.›
  record 'v graph =
    nodes :: "'v set"
    edges :: "('v ×'v) set"
  text ‹In a well-formed graph, edges only go from nodes to nodes.›
  locale wf_graph = 
    fixes G :: "'v graph"
    
    assumes E_wf: "fst ` (edges G) ⊆ (nodes G)"
                     "snd ` (edges G) ⊆ (nodes G)"
    and finiteE: "finite (edges G)" 
    and finiteV: "finite (nodes G)"
  begin
    abbreviation "V ≡ (nodes G)"
    abbreviation "E ≡ (edges G)"
    lemma E_wfD: assumes "(v,v') ∈ E"
      shows "v ∈ V" "v' ∈ V"
      apply -
       apply (rule subsetD[OF E_wf(1)])
       using assms apply force
      apply (rule subsetD[OF E_wf(2)])
      using assms apply force
      done
    lemma E_wfD2: "∀e ∈ E. fst e ∈ V ∧ snd e ∈ V"
    by (auto simp add: E_wfD)
  end
subsection ‹Basic operations on Graphs›
  text ‹The empty graph.›
  definition empty :: "'v graph" where 
    "empty ≡ ⦇ nodes = {}, edges = {} ⦈"
  text ‹Adds a node to a graph.›
  definition add_node :: "'v ⇒ 'v graph ⇒ 'v graph" where 
    "add_node v G ≡ ⦇ nodes = ({v} ∪ (nodes G)), edges=edges G ⦈"
  text ‹Deletes a node from a graph. Also deletes all adjacent edges.›
  definition delete_node where "delete_node v G ≡ ⦇ 
      nodes = (nodes G) - {v},   
      edges = {(e1, e2). (e1, e2) ∈ edges G ∧ e1 ≠ v ∧ e2 ≠ v}
    ⦈"
  text ‹Adds an edge to a graph.›
  definition add_edge where 
  "add_edge v v' G = ⦇nodes = nodes G ∪ {v,v'}, edges = {(v, v')} ∪ edges G ⦈"
  text ‹Deletes an edge from a graph.›
  definition delete_edge where "delete_edge v v' G ≡ ⦇
      nodes = nodes G, 
      edges = {(e1,e2). (e1, e2) ∈ edges G ∧ (e1,e2) ≠ (v,v')}
    ⦈"
  
  definition delete_edges::"'v graph ⇒ ('v × 'v) set ⇒ 'v graph" where 
    "delete_edges G es ≡ ⦇
      nodes = nodes G, 
      edges = {(e1,e2). (e1, e2) ∈ edges G ∧ (e1,e2) ∉ es}
    ⦈"
  fun delete_edges_list::"'v graph ⇒ ('v × 'v) list ⇒ 'v graph" where 
    "delete_edges_list G [] = G"|
    "delete_edges_list G ((v,v')#es) = delete_edges_list (delete_edge v v' G) es"
  definition fully_connected :: "'v graph ⇒ 'v graph" where
    "fully_connected G ≡ ⦇nodes = nodes G, edges = nodes G × nodes G ⦈"
text ‹Extended graph operations›
  text ‹Reflexive transitive successors of a node. Or: All reachable nodes for ‹v› including ‹v›.›
  definition succ_rtran :: "'v graph ⇒ 'v ⇒ 'v set" where
    "succ_rtran G v = {e2. (v,e2) ∈ (edges G)⇧*}"
  text ‹Transitive successors of a node. Or: All reachable nodes for ‹v›.›
  definition succ_tran :: "'v graph ⇒ 'v ⇒ 'v set" where
    "succ_tran G v = {e2. (v,e2) ∈ (edges G)⇧+}"
  
  lemma succ_tran_finite: "wf_graph G ⟹ finite (succ_tran G v)"
  proof -
    assume "wf_graph G"
    from wf_graph.finiteE[OF this] have "finite ((edges G)⇧+)" using finite_trancl[symmetric, of "edges G"] by metis
    from this have "finite {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by simp
    from this have finite: "finite (snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+})" by (metis finite_imageI)
    have "{(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} ⊆ {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
    have 1: "snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} ⊆ snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
    have 2: "snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+ ∧ e1 = v} = {e2. (v,e2) ∈ (edges G)⇧+}" by force
    from 1 2 have "{e2. (v,e2) ∈ (edges G)⇧+} ⊆ snd ` {(e1,e2). (e1, e2) ∈ (edges G)⇧+}" by blast
    from this finite have "finite {e2. (v, e2) ∈ (edges G)⇧+}" by (metis finite_subset)
    thus "finite (succ_tran G v)" using succ_tran_def by metis
  qed
  
  text‹If there is no edge leaving from ‹v›, then ‹v› has no successors›
  lemma succ_tran_empty: "⟦ wf_graph G; v ∉ (fst ` edges G) ⟧ ⟹ succ_tran G v = {}"
    unfolding succ_tran_def using image_iff tranclD by fastforce
  text‹@{const succ_tran} is subset of nodes›
  lemma succ_tran_subseteq_nodes: "⟦ wf_graph G ⟧ ⟹ succ_tran G v ⊆ nodes G"
    unfolding succ_tran_def using tranclD2 wf_graph.E_wfD(2) by fastforce
  text ‹The number of reachable nodes from ‹v››
  definition num_reachable :: "'v graph ⇒ 'v ⇒ nat" where
    "num_reachable G v = card (succ_tran G v)"
  definition num_reachable_norefl :: "'v graph ⇒ 'v ⇒ nat" where
    "num_reachable_norefl G v = card (succ_tran G v - {v})"
  text‹@{const card} returns @{term 0} for infinite sets.
        Here, for a well-formed graph, if @{const num_reachable} is zero, there are actually no nodes reachable.›
  lemma num_reachable_zero: "⟦wf_graph G; num_reachable G v = 0⟧ ⟹ succ_tran G v = {}"
  unfolding num_reachable_def
  apply(subgoal_tac "finite (succ_tran G v)")
   apply(simp)
  apply(blast intro: succ_tran_finite)
  done
  lemma num_succtran_zero: "⟦succ_tran G v = {}⟧ ⟹ num_reachable G v = 0"
    unfolding num_reachable_def by simp
  lemma num_reachable_zero_iff: "⟦wf_graph G⟧ ⟹ (num_reachable G v = 0) ⟷ (succ_tran G v = {})"
  by(metis num_succtran_zero num_reachable_zero)
section‹Undirected Graph›
subsection‹undirected graph simulation›
  text ‹Create undirected graph from directed graph by adding backward links›
  definition backflows :: "('v × 'v) set ⇒ ('v × 'v) set" where
    "backflows E ≡ {(r,s). (s,r) ∈ E}"
  definition undirected :: "'v graph ⇒ 'v graph"
    where "undirected G = ⦇ nodes = nodes G, edges = (edges G) ∪ {(b,a). (a,b) ∈ edges G} ⦈"
section ‹Graph Lemmas›
  lemma graph_eq_intro: "(nodes (G::'a graph) = nodes G') ⟹ (edges G = edges G') ⟹ G = G'" by simp
  
  lemma wf_graph_finite_filterE: "wf_graph G ⟹ finite {(e1, e2). (e1, e2) ∈ edges G ∧ P e1 e2}"
  by(simp add: wf_graph.finiteE split_def)
  lemma wf_graph_finite_filterV: "wf_graph G ⟹ finite {n. n ∈ nodes G ∧ P n}"
  by(simp add: wf_graph.finiteV)
  
  lemma empty_wf[simp]: "wf_graph empty"
    unfolding empty_def by unfold_locales auto
  lemma nodes_empty[simp]: "nodes empty = {}" unfolding empty_def by simp
  lemma edges_empty[simp]: "edges empty = {}" unfolding empty_def by simp
  
  lemma add_node_wf[simp]: "wf_graph g ⟹ wf_graph (add_node v g)"
    unfolding add_node_def wf_graph_def by (auto)
  lemma delete_node_wf[simp]: "wf_graph G ⟹ wf_graph (delete_node v G)"
    by(auto simp add: delete_node_def wf_graph_def wf_graph_finite_filterE)
  
  lemma add_edge_wf[simp]: "wf_graph G ⟹ wf_graph (add_edge v v' G)"
    by(auto simp add: add_edge_def add_node_def wf_graph_def)
  
  lemma delete_edge_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edge v v' G)"
    by(auto simp add: delete_edge_def add_node_def wf_graph_def split_def)
 
  
  lemma delete_edges_list_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edges_list G E)"
    by(induction E arbitrary: G, simp, force)
  lemma delete_edges_wf[simp]: "wf_graph G ⟹ wf_graph (delete_edges G E)"
    by(auto simp add: delete_edges_def add_node_def wf_graph_def split_def)
  lemma delete_edges_list_set: "delete_edges_list G E = delete_edges G (set E)"
    proof(induction E arbitrary: G)
    case Nil thus ?case by (simp add: delete_edges_def)
    next
    case (Cons e E) thus ?case by(cases e)(simp add: delete_edge_def delete_edges_def)
    qed
  lemma delete_edges_list_union: "delete_edges_list G (ff @ keeps) = delete_edges G (set ff ∪ set keeps)"
   by(simp add: delete_edges_list_set)
  lemma add_edge_delete_edges_list: 
    "(add_edge (fst a) (snd a) (delete_edges_list G (a # ff))) = (add_edge (fst a) (snd a) (delete_edges G (set ff)))"
   by(auto simp add: delete_edges_list_set delete_edges_def add_edge_def add_node_def)
  lemma delete_edges_empty[simp]: "delete_edges G {} = G"
   by(simp add: delete_edges_def)
  lemma delete_edges_simp2: "delete_edges G E = ⦇ nodes = nodes G, edges = edges G - E⦈"
   by(auto simp add: delete_edges_def)
  lemma delete_edges_set_nodes: "nodes (delete_edges G E) = nodes G"
   by(simp add: delete_edges_simp2)
  lemma delete_edges_edges_mono: "E' ⊆ E ⟹ edges (delete_edges G E) ⊆ edges (delete_edges G E')"
    by(simp add: delete_edges_def, fast)
  lemma delete_edges_edges_empty: "(delete_edges G (edges G)) = G⦇edges := {}⦈"
    by(simp add: delete_edges_simp2)
 
  lemma add_delete_edge: "wf_graph (G::'a graph) ⟹ (a,b) ∈ edges G ⟹ add_edge a b (delete_edge a b G) = G"
   apply(simp add: delete_edge_def add_edge_def wf_graph_def)
   apply(intro graph_eq_intro)
    by auto
  lemma add_delete_edges: "wf_graph (G::'v graph) ⟹ (a,b) ∈ edges G ⟹ (a,b) ∉ fs ⟹
    add_edge a b (delete_edges G (insert (a, b) fs)) = (delete_edges G fs)"
    by(auto simp add: delete_edges_simp2 add_edge_def wf_graph_def)
 
  lemma fully_connected_simp: "fully_connected ⦇nodes = N, edges = ignore ⦈≡ ⦇nodes = N, edges = N × N ⦈"
    by(simp add: fully_connected_def)
  lemma fully_connected_wf: "wf_graph G ⟹ wf_graph (fully_connected G)"
    by(simp add: fully_connected_def wf_graph_def)
 
 lemma succ_tran_mono: 
  "wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ succ_tran ⦇nodes=N, edges=E'⦈ v ⊆ succ_tran ⦇nodes=N, edges=E⦈ v"
   apply(drule wf_graph.finiteE)
   apply(frule_tac A="E'" in rev_finite_subset, simp)
   apply(simp add: num_reachable_def)
   apply(simp add: succ_tran_def)
   apply(metis (lifting, full_types) Collect_mono trancl_mono)
  done
  
  lemma num_reachable_mono:
  "wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ num_reachable ⦇nodes=N, edges=E'⦈ v ≤ num_reachable ⦇nodes=N, edges=E⦈ v"
   apply(simp add: num_reachable_def)
   apply(frule_tac E'="E'" and v="v" in succ_tran_mono, simp)
   apply(frule_tac v="v" in succ_tran_finite)
   apply(simp add: card_mono)
  done
  
  lemma num_reachable_norefl_mono:
  "wf_graph ⦇nodes=N, edges=E⦈ ⟹ E' ⊆ E ⟹ num_reachable_norefl ⦇nodes=N, edges=E'⦈ v ≤ num_reachable_norefl ⦇nodes=N, edges=E⦈ v"
   apply(simp add: num_reachable_norefl_def)
   apply(frule_tac E'="E'" and v="v" in succ_tran_mono, simp)
   apply(frule_tac v="v" in succ_tran_finite)
   using card_mono by (metis Diff_mono finite_Diff subset_refl)
  
  lemma backflows_wf: 
    "wf_graph ⦇nodes=N, edges=E⦈ ⟹ wf_graph ⦇nodes=N, edges=backflows E⦈"
    using [[simproc add: finite_Collect]] by(auto simp add: wf_graph_def backflows_def)
  lemma undirected_backflows: 
    "undirected G = ⦇ nodes = nodes G, edges = (edges G) ∪ backflows (edges G) ⦈"
    by(simp add: backflows_def undirected_def)
  lemma backflows_id: 
    "backflows (backflows E) = E"
    by(simp add: backflows_def)
  lemma backflows_finite: "finite E ⟹ finite (backflows E)"
    using [[simproc add: finite_Collect]] by(simp add: backflows_def) 
  lemma backflows_minus_backflows: "backflows (X - backflows Y) = (backflows X) - Y"
    by(auto simp add: backflows_def)
  lemma backflows_subseteq: "X ⊆ Y ⟷ backflows X ⊆ backflows Y"
    by(auto simp add: backflows_def)
  lemma backflows_un: "backflows (A ∪ B) = (backflows A) ∪ (backflows B)"
    by(auto simp add: backflows_def)
  lemma backflows_inter: "backflows (A ∩ B) = (backflows A) ∩ (backflows B)"
    by(auto simp add: backflows_def)
  lemma backflows_alt_fstsnd: "backflows E = (λe. (snd e, fst e)) ` E"
    by(auto simp add: backflows_def, force)
lemmas graph_ops=add_node_def delete_node_def add_edge_def delete_edge_def delete_edges_simp2
  
  lemma wf_graph_remove_edges: "wf_graph ⦇ nodes = V, edges = E ⦈ ⟹ wf_graph ⦇ nodes = V, edges=E - X⦈"
    by (metis delete_edges_simp2 delete_edges_wf select_convs(1) select_convs(2))
  lemma wf_graph_remove_edges_union: 
    "wf_graph ⦇ nodes = V, edges = E ∪ E' ⦈ ⟹ wf_graph ⦇ nodes = V, edges=E⦈"
    by(auto simp add: wf_graph_def)
  lemma wf_graph_union_edges: "⟦ wf_graph ⦇ nodes = V, edges = E ⦈; wf_graph ⦇ nodes = V, edges=E'⦈ ⟧ ⟹
     wf_graph ⦇ nodes = V, edges=E ∪ E'⦈"
    by(auto simp add: wf_graph_def)
  lemma wf_graph_add_subset_edges: "⟦ wf_graph ⦇ nodes = V, edges = E ⦈; E' ⊆ E ⟧ ⟹
     wf_graph ⦇ nodes = V, edges= E ∪ E'⦈"
    by(auto simp add: wf_graph_def) (metis rev_finite_subset)
  text ‹Successors of a node.›
  definition succ :: "'v graph ⇒ 'v ⇒ 'v set"
    where "succ G v ≡ {v'. (v,v')∈edges G}"
  lemma succ_finite[simp, intro]: "finite (edges G) ⟹ finite (succ G v)"
    unfolding succ_def
    by (rule finite_subset[where B="snd`edges G"]) force+
  lemma succ_empty: "succ empty v = {}" unfolding empty_def succ_def by auto
  lemma (in wf_graph) succ_subset: "succ G v ⊆ V"
    unfolding succ_def using E_wf
    by (force)
end