Theory Directed_Graph_Specs
section ‹Abstract Datatype for Weighted Directed Graphs›
theory Directed_Graph_Specs
imports Directed_Graph
begin
locale adt_wgraph =
  fixes α :: "'g ⇒ ('v) wgraph"
    and invar :: "'g ⇒ bool"
    and succ :: "'g ⇒ 'v ⇒ (nat×'v) list"
    and empty_graph :: 'g
    and add_edge :: "'v×'v ⇒ nat ⇒ 'g ⇒ 'g"
  assumes succ_correct: "invar g ⟹ set (succ g u) = {(d,v). α g (u,v) = enat d}"
  assumes empty_graph_correct:
    "invar empty_graph"
    "α empty_graph = (λ_. ∞)"
  assumes add_edge_correct:
    "invar g ⟹ α g e = ∞ ⟹ invar (add_edge e d g)"
    "invar g ⟹ α g e = ∞ ⟹ α (add_edge e d g) = (α g)(e:=enat d)"
begin
  
lemmas wgraph_specs = succ_correct empty_graph_correct add_edge_correct
  
end
locale adt_finite_wgraph = adt_wgraph where α=α for α :: "'g ⇒ ('v) wgraph" +
  assumes finite: "invar g ⟹ finite (WGraph.edges (α g))"
subsection ‹Constructing Weighted Graphs from Lists›  
lemma edges_empty[simp]: "WGraph.edges (λ_. ∞) = {}" 
  by (auto simp: WGraph.edges_def)
  
lemma edges_insert[simp]: 
  "WGraph.edges (g(e:=enat d)) = Set.insert e (WGraph.edges g)" 
  by (auto simp: WGraph.edges_def)
text ‹A list represents a graph if there are no multi-edges or duplicate edges›
definition "valid_graph_rep l ≡ 
  (∀u d d' v. (u,v,d)∈set l ∧ (u,v,d')∈set l ⟶ d=d')
∧ distinct l
  "
text ‹Alternative characterization: all node pairs must be distinct›
lemma valid_graph_rep_code[code]: 
  "valid_graph_rep l ⟷ distinct (map (λ(u,v,_). (u,v)) l)"  
  by (auto simp: valid_graph_rep_def distinct_map inj_on_def)
  
lemma valid_graph_rep_simps[simp]:
  "valid_graph_rep []"
  "valid_graph_rep ((u,v,d) # l) ⟷ valid_graph_rep l ∧ (∀d'. (u,v,d')∉set l)"
  by (auto simp: valid_graph_rep_def)
  
text ‹For a valid graph representation, there is exactly one graph that 
  corresponds to it›  
lemma valid_graph_rep_ex1: 
  "valid_graph_rep l ⟹ ∃! w. ∀u v d. w (u,v) = enat d ⟷ (u,v,d)∈set l"  
  unfolding valid_graph_rep_code
  apply safe
  subgoal 
    apply (rule exI[where x="λ(u,v).
        if ∃d. (u,v,d)∈set l then enat (SOME d. (u,v,d)∈set l) else ∞"])
    by (auto intro: someI simp: distinct_map inj_on_def split: prod.splits; 
           blast)
  subgoal for w w'
    apply (simp add: fun_eq_iff)
    by (metis (mono_tags, opaque_lifting) not_enat_eq)
  done  
text ‹We define this graph using determinate choice›  
definition "wgraph_of_list l ≡ THE w. ∀u v d. w (u,v) = enat d ⟷ (u,v,d)∈set l"  
locale wgraph_from_list_algo = adt_wgraph 
begin
definition "from_list l ≡ fold (λ(u,v,d). add_edge (u,v) d) l empty_graph"
definition "edges_undef l w ≡ ∀u v d. (u,v,d)∈set l ⟶ w (u,v) = ∞"  
  
lemma edges_undef_simps[simp]:
  "edges_undef [] w"  
  "edges_undef l (λ_. ∞)"
  "edges_undef ((u,v,d)#l) w ⟷ edges_undef l w ∧ w (u,v) = ∞"
  "edges_undef l (w((u,v) := enat d)) ⟷ edges_undef l w ∧ (∀d'. (u,v,d')∉set l)"  
  by (auto simp: edges_undef_def)
lemma from_list_correct_aux:
  assumes "valid_graph_rep l"
  assumes "edges_undef l (α g)"
  assumes "invar g"
  defines "g' ≡ fold (λ(u,v,d). add_edge (u,v) d) l g"
  shows "invar g'"
    and "(∀u v d. α g' (u,v) = enat d ⟷ α g (u,v) = enat d ∨ (u,v,d)∈set l)"
  using assms(1-3) unfolding g'_def
  apply (induction l arbitrary: g)
  by (auto simp: wgraph_specs split: if_splits)
lemma from_list_correct':
  assumes "valid_graph_rep l"
  shows "invar (from_list l)" 
    and "(u,v,d)∈set l ⟷ α (from_list l) (u,v) = enat d"
  unfolding from_list_def 
  using from_list_correct_aux[OF assms, where g=empty_graph]
  by (auto simp: wgraph_specs)
  
lemma from_list_correct:
  assumes "valid_graph_rep l"
  shows "invar (from_list l)" "α (from_list l) = wgraph_of_list l"
proof -
  from theI'[OF valid_graph_rep_ex1[OF assms], folded wgraph_of_list_def]
  have "(wgraph_of_list l (u, v) = enat d) = ((u, v, d) ∈ set l)" for u v d 
    by blast
  then show "α (from_list l) = wgraph_of_list l" 
    using from_list_correct_aux[OF assms, where g=empty_graph]
    apply (clarsimp simp: fun_eq_iff wgraph_specs from_list_def)
    apply (metis (no_types) enat.exhaust)
    done
  
  show "invar (from_list l)"
    by (simp add: assms from_list_correct')
    
qed    
    
end
end