Theory Generic_Push_Relabel

section ‹Generic Push Relabel Algorithm›
theory Generic_Push_Relabel
imports 
  Flow_Networks.Fofu_Abs_Base
  Flow_Networks.Ford_Fulkerson
begin

  
  
subsection ‹Labeling›
 
text ‹The central idea of the push-relabel algorithm is to add natural number
  labels l : node ⇒ nat› to each node, and maintain the invariant that for 
  all edges (u,v)› in the residual graph, we have l u ≤ l v + 1›.›

type_synonym labeling = "node  nat"
  
locale Labeling = NPreflow +
  fixes l :: labeling
  assumes valid: "(u,v)  cf.E  l(u)  l(v) + 1"
  assumes lab_src[simp]: "l s = card V"
  assumes lab_sink[simp]: "l t = 0"
begin
text ‹Generalizing validity to paths›
lemma gen_valid: "l(u)  l(x) + length p" if "cf.isPath u p x"
  using that by (induction p arbitrary: u; fastforce dest: valid)

text ‹
  In a valid labeling, there cannot be an augmenting path~\cormen{26.17}.

  The proof works by contradiction, using the validity constraint 
  to show that any augmenting path would be too long for a simple path.
›
theorem no_augmenting_path: "¬isAugmentingPath p"
proof
  assume "isAugmentingPath p"  
  hence SP: "cf.isSimplePath s p t" unfolding isAugmentingPath_def .
  hence "cf.isPath s p t" unfolding cf.isSimplePath_def by auto
  from gen_valid[OF this] have "length p  card V" by auto
  with cf.simplePath_length_less_V[OF _ SP] show False by auto 
qed

text ‹
  The idea of push relabel algorithms is to maintain a valid labeling,
  and, ultimately, arrive at a valid flow, i.e., no nodes have excess flow. 
  We then immediately get that the flow is maximal:
›  
corollary no_excess_imp_maxflow:    
  assumes "uV-{s,t}. excess f u = 0"
  shows "isMaxFlow f"  
proof -    
  from assms interpret NFlow 
    apply unfold_locales 
    using no_deficient_nodes unfolding excess_def by auto
  from noAugPath_iff_maxFlow no_augmenting_path show "isMaxFlow f" by auto
qed
  
end ― ‹Labeling›
    
subsection ‹Basic Operations›    
text ‹
  The operations of the push relabel algorithm are local operations on 
  single nodes and edges.  
›

subsubsection ‹Augmentation of Edges›
context Network
begin
  
text ‹We define a function to augment a single edge in the residual graph.›
  
definition augment_edge :: "'capacity flow  _" 
  where "augment_edge f  λ(u,v) Δ. 
    if (u,v)E then f( (u,v) := f (u,v) + Δ )
    else if (v,u)E then f( (v,u) := f (v,u) - Δ )
    else f"
  
lemma augment_edge_zero[simp]: "augment_edge f e 0 = f" 
  unfolding augment_edge_def by (auto split: prod.split) 
    
lemma augment_edge_same[simp]: "eE  augment_edge f e Δ e = f e + Δ"
  unfolding augment_edge_def by (auto split!: prod.splits)
    
lemma augment_edge_other[simp]:"eE; e'e   augment_edge f e Δ e' = f e'"    
  unfolding augment_edge_def by (auto split!: prod.splits)

lemma augment_edge_rev_same[simp]: 
  "(v,u)E  augment_edge f (u,v) Δ (v,u) = f (v,u) - Δ"    
  using no_parallel_edge
  unfolding augment_edge_def by (auto split!: prod.splits)

lemma augment_edge_rev_other[simp]: 
  "(u,v)E; e'(v,u)  augment_edge f (u,v) Δ e' = f e'"    
  unfolding augment_edge_def by (auto split!: prod.splits)

lemma augment_edge_cf[simp]: "(u,v)EE¯  
    cf_of (augment_edge f (u,v) Δ) 
  = (cf_of f)( (u,v) := cf_of f (u,v) - Δ, (v,u) := cf_of f (v,u) + Δ)"    
  apply (intro ext; cases "(u,v)E")
  subgoal for e' 
    apply (cases "e'=(u,v)")  
    subgoal by (simp split!: if_splits add: no_self_loop residualGraph_def)
    apply (cases "e'=(v,u)")  
    subgoal by (simp split!: if_splits add: no_parallel_edge residualGraph_def)
    subgoal by (simp 
                  split!: if_splits prod.splits 
                  add: residualGraph_def augment_edge_def)
    done
  subgoal for e'
    apply (cases "e'=(u,v)")  
    subgoal by (simp split!: if_splits add: no_self_loop residualGraph_def)
    apply (cases "e'=(v,u)")  
    subgoal by (simp split!: if_splits add: no_self_loop residualGraph_def)
    subgoal by (simp 
                  split!: if_splits prod.splits 
                  add: residualGraph_def augment_edge_def)
    done
  done
    
lemma augment_edge_cf': "(u,v)cfE_of f  
    cf_of (augment_edge f (u,v) Δ) 
  = (cf_of f)( (u,v) := cf_of f (u,v) - Δ, (v,u) := cf_of f (v,u) + Δ)"    
proof -
  assume "(u,v)cfE_of f"
  hence "(u,v)EE¯" using cfE_of_ss_invE ..
  thus ?thesis by simp
qed      
  
text ‹The effect of augmenting an edge on the residual graph›  
definition (in -) augment_edge_cf :: "_ flow  _" where 
  "augment_edge_cf cf 
     λ(u,v) Δ. (cf)( (u,v) := cf (u,v) - Δ, (v,u) := cf (v,u) + Δ)"
  
lemma cf_of_augment_edge:
  assumes A: "(u,v)cfE_of f" 
  shows "cf_of (augment_edge f (u,v) Δ) = augment_edge_cf (cf_of f) (u,v) Δ"  
proof -  
  show "cf_of (augment_edge f (u, v) Δ) = augment_edge_cf (cf_of f) (u, v) Δ"
    by (simp add: augment_edge_cf_def A augment_edge_cf')
qed      
  
  
  
lemma cfE_augment_ss:
  assumes EDGE: "(u,v)cfE_of f"  
  shows "cfE_of (augment_edge f (u,v) Δ)  insert (v,u) (cfE_of f)"
  using EDGE  
  apply (clarsimp simp: augment_edge_cf')
  unfolding Graph.E_def  
  apply (auto split: if_splits)  
  done
  
  
end ― ‹Network›  
  
context NPreflow begin  

text ‹Augmenting an edge (u,v)› with a flow Δ› that does not exceed the 
  available edge capacity, nor the available excess flow on the source node,
  preserves the preflow property.
›  
lemma augment_edge_preflow_preserve: "0Δ; Δ  cf (u,v); Δ  excess f u 
   Preflow c s t (augment_edge f (u,v) Δ)"  
  apply unfold_locales
  subgoal
    unfolding residualGraph_def augment_edge_def  
    using capacity_const
    by (fastforce split!: if_splits)
  subgoal
  proof (intro ballI; clarsimp)
    assume "0Δ" "Δ  cf (u,v)" "Δ  excess f u"
    fix v'
    assume V': "v'V" "v's" "v't"  
      
    show "sum (augment_edge f (u, v) Δ) (outgoing v')
             sum (augment_edge f (u, v) Δ) (incoming v')"  
    proof (cases)
      assume "Δ = 0"
      with no_deficient_nodes show ?thesis using V' by auto 
    next
      assume "Δ  0" with 0Δ have "0<Δ" by auto
      with Δ  cf (u,v) have "(u,v)cf.E" unfolding Graph.E_def by auto
          
      show ?thesis
      proof (cases)    
        assume [simp]: "(u,v)E"
        hence AE: "augment_edge f (u,v) Δ = f ( (u,v) := f (u,v) + Δ )"  
          unfolding augment_edge_def by auto
        have 1: "eoutgoing v'. augment_edge f (u,v) Δ e = f e" if "v'u"
          using that unfolding outgoing_def AE by auto
        have 2: "eincoming v'. augment_edge f (u,v) Δ e = f e" if "v'v"
          using that unfolding incoming_def AE by auto

        from (u,v)E no_self_loop have "uv" by blast
            
        {
          assume "v'  u" "v'  v"
          with 1 2 V' no_deficient_nodes have ?thesis by auto
        } moreover {
          assume [simp]: "v'=v" 
          have "sum (augment_edge f (u, v) Δ) (outgoing v') 
              = sum f (outgoing v)"  
            using 1 uv V' by auto
          also have "  sum f (incoming v)" 
            using V' no_deficient_nodes by auto
          also have "  sum (augment_edge f (u, v) Δ) (incoming v)"
            apply (rule sum_mono)
            using 0Δ
            by (auto simp: incoming_def augment_edge_def split!: if_split)
          finally have ?thesis by simp
        } moreover {
          assume [simp]: "v'=u"
          have A1: "sum (augment_edge f (u,v) Δ) (incoming v') 
                  = sum f (incoming u)"  
            using 2 uv by auto
          have "(u,v)  outgoing u" using (u,v)E 
            by (auto simp: outgoing_def)
          note AUX = sum.remove[OF _ this, simplified]
          have A2: "sum (augment_edge f (u,v) Δ) (outgoing u) 
                  = sum f (outgoing u) + Δ"
            using AUX[of "augment_edge f (u,v) Δ"] AUX[of "f"] by auto
          from A1 A2 Δ  excess f u no_deficient_nodes V' have ?thesis 
            unfolding excess_def by auto
        } ultimately show ?thesis by blast
      next
        assume [simp]: (u,v)E 
        hence [simp]: "(v,u)E" using cfE_ss_invE (u,v)cf.E by auto
        from (u,v)E (v,u)E have "uv" by blast
        
        have AE: "augment_edge f (u,v) Δ = f ( (v,u) := f (v,u) - Δ )"  
          unfolding augment_edge_def by simp
        have 1: "eoutgoing v'. augment_edge f (u,v) Δ e = f e" if "v'v"
          using that unfolding outgoing_def AE by auto
        have 2: "eincoming v'. augment_edge f (u,v) Δ e = f e" if "v'u"
          using that unfolding incoming_def AE by auto

        {
          assume "v'  u" "v'  v"
          with 1 2 V' no_deficient_nodes have ?thesis by auto
        } moreover {
          assume [simp]: "v'=u" 
          have A1: "sum (augment_edge f (u, v) Δ) (outgoing v') 
                  = sum f (outgoing u)"  
            using 1 uv V' by auto
              
          have "(v,u)  incoming u" 
            using (v,u)E by (auto simp: incoming_def)
          note AUX = sum.remove[OF _ this, simplified]    
          have A2: "sum (augment_edge f (u,v) Δ) (incoming u) 
                  = sum f (incoming u) - Δ"
            using AUX[of "augment_edge f (u,v) Δ"] AUX[of "f"] by auto
              
          from A1 A2 Δ  excess f u no_deficient_nodes V' have ?thesis 
            unfolding excess_def by auto
        } moreover {
          assume [simp]: "v'=v"
          have "sum (augment_edge f (u,v) Δ) (outgoing v') 
               sum f (outgoing v')"  
            apply (rule sum_mono)
            using 0<Δ  
            by (auto simp: augment_edge_def)  
          also have "  sum f (incoming v)" 
            using no_deficient_nodes V' by auto
          also have "  sum (augment_edge f (u,v) Δ) (incoming v')"    
            using 2 uv by auto
          finally have ?thesis by simp
        } ultimately show ?thesis by blast
      qed      
    qed              
  qed          
  done            
end ― ‹Network with Preflow› 

subsubsection ‹Push Operation›  
context Network 
begin  
text ‹The push operation pushes as much flow as possible flow from an active 
  node over an admissible edge.

  A node is called \emph{active} if it has positive excess, and an edge (u,v)›
  of the residual graph is called admissible, if @{term l u = l v + 1}.
› 
definition push_precond :: "'capacity flow  labeling  edge  bool" 
  where "push_precond f l 
     λ(u,v). excess f u > 0  (u,v)cfE_of f  l u = l v + 1"
  
text ‹The maximum possible flow is determined by the available excess flow at 
  the source node and the available capacity of the edge.›  
definition push_effect :: "'capacity flow  edge  'capacity flow" 
  where "push_effect f 
     λ(u,v). augment_edge f (u,v) (min (excess f u) (cf_of f (u,v)))"

lemma push_precondI[intro?]: 
  "excess f u > 0; (u,v)cfE_of f; l u = l v + 1  push_precond f l (u,v)"
  unfolding push_precond_def by auto

subsubsection ‹Relabel Operation›    
text ‹
  An active node (not the sink) without any outgoing admissible edges 
  can be relabeled. 
›    
definition relabel_precond :: "'capacity flow  labeling  node  bool" 
  where "relabel_precond f l u 
     ut  excess f u > 0  (v. (u,v)cfE_of f  l u  l v + 1)"

text ‹The new label is computed from the neighbour's labels, to be the minimum
  value that will create an outgoing admissible edge.›    
definition relabel_effect :: "'capacity flow  labeling  node  labeling"
  where "relabel_effect f l u 
     l( u := Min { l v | v. (u,v)cfE_of f } + 1 )"
  
subsubsection ‹Initialization›
(* TODO: The algorithm can be initialized with other labelings ... 
  reflect this in abstract complexity/correctness theorem.*)  
text ‹
  The initial preflow exhausts all outgoing edges of the source node.
›  
definition "pp_init_f  λ(u,v). if (u=s) then c (u,v) else 0"

text ‹
  The initial labeling labels the source with |V|›, and all other nodes
  with 0›.
›  
definition "pp_init_l  (λx. 0)(s := card V)"

end ― ‹Network›

subsection ‹Abstract Correctness›
text ‹We formalize the abstract correctness argument of the algorithm. 
  It consists of two parts:
     Execution of push and relabel operations maintain a valid labeling
     If no push or relabel operations can be executed, the preflow is actually 
      a flow.

  This section corresponds to the proof of \cormen{26.18}.
›  
  
subsubsection ‹Maintenance of Invariants›  
  
context Network 
begin  
  
lemma pp_init_invar: "Labeling c s t pp_init_f pp_init_l"
  apply (unfold_locales;
      ((auto simp: pp_init_f_def pp_init_l_def cap_non_negative; fail) 
        | (intro ballI)?))  
proof -  
  fix v
  assume "vV - {s,t}"
  hence "eoutgoing v. pp_init_f e = 0"
    by (auto simp: outgoing_def pp_init_f_def)
  hence [simp]: "sum pp_init_f (outgoing v) = 0" by auto
  have "0  pp_init_f e" for e
    by (auto simp: pp_init_f_def cap_non_negative split: prod.split)
  from sum_bounded_below[of "incoming v" 0 pp_init_f, OF this]
  have "0  sum pp_init_f (incoming v)" by auto
  thus "sum pp_init_f (outgoing v)  sum pp_init_f (incoming v)"
    by auto
      
next
  fix u v
  assume "(u, v)  Graph.E (residualGraph c pp_init_f)"  
  thus "pp_init_l u  pp_init_l v + 1"
    unfolding pp_init_l_def Graph.E_def pp_init_f_def residualGraph_def
    by (auto split: if_splits)
      
qed    

lemma pp_init_f_preflow: "NPreflow c s t pp_init_f" 
proof -  
  from pp_init_invar interpret Labeling c s t pp_init_f pp_init_l .
  show ?thesis by unfold_locales    
qed  
  
end ― ‹Network›  

context Labeling
begin
  
text ‹Push operations preserve a valid labeling~\cormen{26.16}.›
theorem push_pres_Labeling:
  assumes "push_precond f l e"
  shows "Labeling c s t (push_effect f e) l"
  unfolding push_effect_def  
proof (cases e; clarsimp)    
  fix u v
  assume [simp]: "e=(u,v)"
  let ?f' = "(augment_edge f (u, v) (min (excess f u) (cf (u, v))))"
    
  from assms have   
    ACTIVE: "excess f u > 0"
    and EDGE: "(u,v)cf.E"  
    and ADM: "l u = l v + 1"
    unfolding push_precond_def by auto
      
  interpret cf': Preflow c s t ?f'
   apply (rule augment_edge_preflow_preserve)
   using ACTIVE resE_nonNegative  
   by auto
  show "Labeling c s t ?f' l"
    apply unfold_locales using valid
    using cfE_augment_ss[OF EDGE] ADM
    apply (fastforce)
    by auto  
qed      
  
lemma finite_min_cf_outgoing[simp, intro!]: "finite {l v |v. (u, v)  cf.E}"    
proof -
  have "{l v |v. (u, v)  cf.E} = l`snd`cf.outgoing u"
    by (auto simp: cf.outgoing_def)
  moreover have "finite (l`snd`cf.outgoing u)" by auto
  ultimately show ?thesis by auto
qed  
  
text ‹Relabel operations preserve a valid labeling~\cormen{26.16}. 
  Moreover, they increase the label of the relabeled node~\cormen{26.15}.
›  
theorem 
  assumes PRE: "relabel_precond f l u"
  shows relabel_increase_u: "relabel_effect f l u u > l u" (is ?G1)
    and relabel_pres_Labeling: "Labeling c s t f (relabel_effect f l u)" (is ?G2)
proof -
  from PRE have  
        NOT_SINK: "ut"
    and ACTIVE: "excess f u > 0"
    and NO_ADM: "v. (u,v)cf.E  l u  l v + 1"
  unfolding relabel_precond_def by auto
  
  from ACTIVE have [simp]: "su" using excess_s_non_pos by auto
      
  from active_has_cf_outgoing[OF ACTIVE] have [simp]: "v. (u, v)  cf.E" 
    by (auto simp: cf.outgoing_def)
  
  from NO_ADM valid have "l u < l v + 1" if "(u,v)cf.E" for v
    by (simp add: nat_less_le that)
  hence LU_INCR: "l u  Min { l v | v. (u,v)cf.E }" 
    by (auto simp: less_Suc_eq_le)
  with valid have "u'. (u',u)cf.E  l u'  Min { l v | v. (u,v)cf.E } + 1"    
    by (smt ab_semigroup_add_class.add.commute add_le_cancel_left le_trans)
  moreover have "v. (u,v)cf.E  Min { l v | v. (u,v)cf.E } + 1  l v + 1"
    using Min_le by auto
  ultimately show ?G1 ?G2
    unfolding relabel_effect_def
    apply (clarsimp_all simp: PRE)
    subgoal using LU_INCR by (simp add: less_Suc_eq_le)
    apply (unfold_locales)
    subgoal for u' v' using valid by auto
    subgoal by auto    
    subgoal using NOT_SINK by auto
    done
qed      
 
lemma relabel_preserve_other: "uv  relabel_effect f l u v = l v" 
  unfolding relabel_effect_def by auto
  
subsubsection ‹Maxflow on Termination›    
text ‹
  If no push or relabel operations can be performed any more,
  we have arrived at a maximal flow.
›  
theorem push_relabel_term_imp_maxflow:
  assumes no_push: "(u,v)cf.E. ¬push_precond f l (u,v)"
  assumes no_relabel: "u. ¬relabel_precond f l u"
  shows "isMaxFlow f"  
proof -
  from assms have "uV-{t}. excess f u  0"
    unfolding push_precond_def relabel_precond_def
    by force 
  with excess_non_negative have "uV-{s,t}. excess f u = 0" by force
  with no_excess_imp_maxflow show ?thesis . 
qed      
  
end ― ‹Labeling›  
  
subsection ‹Convenience Lemmas›
text ‹We define a locale to reflect the effect of a push operation›  
locale push_effect_locale = Labeling +
  fixes u v
  assumes PRE: "push_precond f l (u,v)"
begin    
  abbreviation "f'  push_effect f (u,v)"
  sublocale l': Labeling c s t f' l  
    using push_pres_Labeling[OF PRE] .
  
  lemma uv_cf_edge[simp, intro!]: "(u,v)cf.E" 
    using PRE unfolding push_precond_def by auto
  lemma excess_u_pos: "excess f u > 0" 
    using PRE unfolding push_precond_def by auto   
  lemma l_u_eq[simp]: "l u = l v + 1" 
    using PRE unfolding push_precond_def by auto   

  lemma uv_edge_cases:
    obtains (par) "(u,v)E" "(v,u)E" 
          | (rev) "(v,u)E" "(u,v)E"
    using uv_cf_edge cfE_ss_invE no_parallel_edge by blast  

  lemma uv_nodes[simp, intro!]: "uV" "vV" 
    using E_ss_VxV cfE_ss_invE no_parallel_edge by auto
      
  lemma uv_not_eq[simp]: "uv" "vu"
    using E_ss_VxV cfE_ss_invE[THEN subsetD, OF uv_cf_edge] no_parallel_edge 
    by auto

  definition "Δ = min (excess f u) (cf_of f (u,v))"    
    
  lemma Δ_positive: "Δ > 0"  
    unfolding Δ_def 
    using excess_u_pos uv_cf_edge[unfolded cf.E_def] resE_positive 
    by auto
      
  lemma f'_alt: "f' = augment_edge f (u,v) Δ" 
    unfolding push_effect_def Δ_def by auto
      
  lemma cf'_alt: "l'.cf = augment_edge_cf cf (u,v) Δ"    
    unfolding push_effect_def Δ_def augment_edge_cf_def
    by (auto simp: augment_edge_cf')
      
  lemma excess'_u[simp]: "excess f' u = excess f u - Δ"
    unfolding excess_def[where f=f']
  proof -
    show "sum f' (incoming u) - sum f' (outgoing u) = excess f u - Δ"
    proof (cases rule: uv_edge_cases)  
      case [simp]: par 
      hence UV_ONI:"(u,v)outgoing u - incoming u"
        by (auto simp: incoming_def outgoing_def no_self_loop)
      have 1: "sum f' (incoming u) = sum f (incoming u)"    
        apply (rule sum.cong[OF refl])
        using UV_ONI unfolding f'_alt
        apply (subst augment_edge_other)
        by auto  
          
      have "sum f' (outgoing u) 
        = sum f (outgoing u) + (xoutgoing u. if x = (u, v) then Δ else 0)"
        unfolding f'_alt augment_edge_def sum.distrib[symmetric] 
          by (rule sum.cong) auto
      also have " = sum f (outgoing u) + Δ" 
        using UV_ONI by (auto simp: sum.delta)
      finally show ?thesis using 1 unfolding excess_def by simp 
    next  
      case [simp]: rev 
      have UV_INO:"(v,u)incoming u - outgoing u"
        by (auto simp: incoming_def outgoing_def no_self_loop)
      have 1: "sum f' (outgoing u) = sum f (outgoing u)"    
        apply (rule sum.cong[OF refl])
        using UV_INO unfolding f'_alt
        apply (subst augment_edge_rev_other)  
        by (auto)
      have "sum f' (incoming u) 
        = sum f (incoming u) + (xincoming u. if x = (v, u) then - Δ else 0)"
        unfolding f'_alt augment_edge_def sum.distrib[symmetric] 
          by (rule sum.cong) auto
      also have " = sum f (incoming u) - Δ"  
        using UV_INO by (auto simp: sum.delta)
      finally show ?thesis using 1 unfolding excess_def by auto
    qed      
  qed
    
  lemma excess'_v[simp]: "excess f' v = excess f v + Δ"
    unfolding excess_def[where f=f']
  proof -    
    show "sum f' (incoming v) - sum f' (outgoing v) = excess f v + Δ"
    proof (cases rule: uv_edge_cases)
      case [simp]: par 
      have UV_INO: "(u,v)incoming v - outgoing v"
        unfolding incoming_def outgoing_def by (auto simp: no_self_loop)
      have 1: "sum f' (outgoing v) = sum f (outgoing v)"    
        using UV_INO unfolding f'_alt
        by (auto simp: augment_edge_def intro: sum.cong)
          
      have "sum f' (incoming v) 
        = sum f (incoming v) + (xincoming v. if x=(u,v) then Δ else 0)"    
        unfolding f'_alt augment_edge_def sum.distrib[symmetric] 
        apply (rule sum.cong)
        using UV_INO unfolding f'_alt by auto
      also have " = sum f (incoming v) + Δ" 
        using UV_INO by (auto simp: sum.delta)
      finally show ?thesis using 1 by (auto simp: excess_def)
    next
      case [simp]: rev
      have UV_INO:"(v,u)outgoing v - incoming v"
        by (auto simp: incoming_def outgoing_def no_self_loop)

      have 1: "sum f' (incoming v) = sum f (incoming v)"
        using UV_INO unfolding f'_alt
        by (auto simp: augment_edge_def intro: sum.cong)
          
      have "sum f' (outgoing v) 
        = sum f (outgoing v) + (xoutgoing v. if x=(v,u) then - Δ else 0)"    
        unfolding f'_alt augment_edge_def sum.distrib[symmetric] 
        apply (rule sum.cong)
        using UV_INO unfolding f'_alt by auto
      also have " = sum f (outgoing v) - Δ" 
        using UV_INO by (auto simp: sum.delta)
      finally show ?thesis using 1 by (auto simp: excess_def)
    qed
  qed  
    
  lemma excess'_other[simp]:
    assumes "x  u" "x  v"  
    shows "excess f' x = excess f x"
  proof -
    have NE: "(u,v)incoming x" "(u,v)outgoing x"
          "(v,u)incoming x" "(v,u)outgoing x"
      using assms unfolding incoming_def outgoing_def by auto
    have 
      "sum f' (outgoing x) = sum f (outgoing x)"
      "sum f' (incoming x) = sum f (incoming x)"
      by (auto 
            simp: augment_edge_def f'_alt NE 
            split!: if_split 
            intro: sum.cong)  
    thus ?thesis    
      unfolding excess_def by auto
  qed      

  lemma excess'_if: 
    "excess f' x = (
           if x=u then excess f u - Δ 
      else if x=v then excess f v + Δ 
         else excess f x)"  
    by simp
    
end ― ‹Push Effect Locale› 
  
  
  
subsection ‹Complexity›  
text ‹
  Next, we analyze the complexity of the generic push relabel algorithm.
  We will show that it has a complexity of O(V2E)› basic operations.
  Here, we often trade precise estimation of constant factors for simplicity
  of the proof.
›  
 
subsubsection ‹Auxiliary Lemmas›  
context Network 
begin  
  
lemma cardE_nz_aux[simp, intro!]:
  "card E  0" "card E  Suc 0" "card E > 0"
proof -
  show "card E  0" by (simp add: E_not_empty)
  thus "card E  Suc 0" by linarith
  thus "card E > 0" by auto
qed
  
text ‹The number of nodes can be estimated by the number of edges.
  This estimation is done in various places to get smoother bounds.
›  
lemma card_V_est_E: "card V  2 * card E" 
proof -
  have "card V  card (fst`E) + card (snd`E)"
    by (auto simp: card_Un_le V_alt)
  also note card_image_le[OF finite_E]
  also note card_image_le[OF finite_E]
  finally show "card V  2 * card E" by auto
qed  
  

end  
  
subsubsection ‹Height Bound›  
text ‹A crucial idea of estimating the complexity is the insight that 
  no label will exceed 2|V|-1› during the algorithm.

  We define a locale that states this invariant, and show that the algorithm
  maintains it. The corresponds to the proof of \cormen{26.20}.
›  
locale Height_Bounded_Labeling = Labeling +
  assumes height_bound: "uV. l u  2*card V - 1"
begin    
  lemma height_bound': "uV  l u  2*card V - 1"
    using height_bound by auto
end  
  
lemma (in Network) pp_init_height_bound: 
  "Height_Bounded_Labeling c s t pp_init_f pp_init_l"
proof -
  interpret Labeling c s t pp_init_f pp_init_l by (rule pp_init_invar)
  show ?thesis by unfold_locales (auto simp: pp_init_l_def)  
qed    

context Height_Bounded_Labeling
begin

text ‹As push does not change the labeling, it trivially preserves the 
  height bound.›  
lemma push_pres_height_bound:
  assumes "push_precond f l e"
  shows "Height_Bounded_Labeling c s t (push_effect f e) l"  
proof -    
  from push_pres_Labeling[OF assms] 
  interpret l': Labeling c s t "push_effect f e" l .
  show ?thesis using height_bound by unfold_locales    
qed      

text ‹
  In a valid labeling,
  any active node has a (simple) path to the source node in 
  the residual graph~\cormen{26.19}.
›  
lemma (in Labeling) excess_imp_source_path: 
  assumes "excess f u > 0"
  obtains p where "cf.isSimplePath u p s"
proof -
  obtain U where U_def: "U = {v|p v. cf.isSimplePath u p v}" by blast
  have fct1: "U  V"
  proof 
    fix v
    assume "v  U"
    then have "(u, v)  cf.E*" 
      using U_def cf.isSimplePath_def cf.isPath_rtc by auto
    then obtain u' where "u = v  ((u, u')  cf.E*  (u', v)  cf.E)" 
      by (meson rtranclE)
    thus "v  V"
    proof
      assume "u = v"
      thus ?thesis using excess_nodes_only[OF assms] by blast
    next
      assume "(u, u')  cf.E*  (u', v)  cf.E"
      then have "v  cf.V" unfolding cf.V_def by blast
      thus ?thesis by simp
    qed
  qed 
    
  have "s  U"
  proof(rule ccontr)
    assume "s  U"
    obtain U' where U'_def: "U' = V - U" by blast
    
    have "(uU. excess f u) 
        = (uU. (vU'. f (v, u))) - (uU. (vU'. f (u, v)))"
    proof -
      have "(uU. excess f u) 
          = (uU. (vincoming u. f v)) - (uU.(voutgoing u. f v))"
        (is "_ = ?R1 - ?R2") unfolding excess_def by (simp add: sum_subtractf)
      also have "?R1 = (uU. (vV. f (v, u)))" 
        using sum_incoming_alt_flow fct1 by (meson subsetCE sum.cong)
      also have " = (uU. (vU. f (v, u))) + (uU. (vU'. f (v, u)))"
      proof -
        have "(vV. f (v, u)) = (vU. f (v, u)) + (vU'. f (v, u))" for u
          using U'_def fct1 finite_V 
          by (metis ab_semigroup_add_class.add.commute sum.subset_diff)
        thus ?thesis by (simp add: sum.distrib)
      qed
      also have "?R2 = (uU. (vV. f (u, v)))" 
        using sum_outgoing_alt_flow fct1 by (meson subsetCE sum.cong)
      also have " = (uU. (vU. f (u, v))) + (uU. (vU'. f (u, v)))"
      proof -
        have "(vV. f (u, v)) = (vU. f (u, v)) + (vU'. f (u, v))" for u
          using U'_def fct1 finite_V 
          by (metis ab_semigroup_add_class.add.commute sum.subset_diff)
        thus ?thesis by (simp add: sum.distrib)
      qed
      also have "(uU. (vU. f (u, v))) = (uU. (vU. f (v, u)))"
      proof -
        {
          fix A :: "nat set"
          assume "finite A"
          then have "(uA. (vA. f (u, v))) = (uA. (vA. f (v, u)))"
          proof (induction "card A" arbitrary: A)
            case 0
            then show ?case by auto
          next
            case (Suc x)
            then obtain A' a 
              where o1:"A = insert a A'" and o2:"x = card A'" and o3:"finite A'"
              by (metis card_insert_disjoint card_le_Suc_iff le_refl nat.inject)
            then have lm:"(eA. g e) = (eA'. g e) + g a" 
              for g :: "nat  'a"  
              using Suc.hyps(2)
              by (metis card_insert_if n_not_Suc_n 
                        semiring_normalization_rules(24) sum.insert)

            have "(uA. (vA. f (u, v))) 
              = (uA'. (vA. f (u, v))) + (vA. f (a, v))"
              (is "_ = ?R1 + ?R2") using lm by auto     
            also have "?R1 = (uA'. (vA'. f (u, v))) + (uA'. f(u, a))" 
              (is "_ = ?R1_1 + ?R1_2") using lm sum.distrib by force
            also note add.assoc
            also have "?R1_2 + ?R2 = (uA'. f(a, u)) + (vA. f(v, a))"
              (is "_ = ?R1_2' + ?R2'") using lm by auto    
            also have "?R1_1 = (uA'. (vA'. f (v, u)))" 
              (is "_ = ?R1_1'") using Suc.hyps(1)[of A'] o2 o3 by auto
            also note add.assoc[symmetric]
            also have "?R1_1' + ?R1_2' = (uA'. (vA. f (v, u)))"
              by (metis (no_types, lifting) lm sum.cong sum.distrib)
            finally show ?case using lm[symmetric] by auto
          qed
        } note this[of U]
        thus ?thesis using fct1 finite_V finite_subset by auto
      qed
      finally show ?thesis by arith
    qed
    moreover have "(uU. excess f u) > 0"
    proof -
      have "u  U" using U_def by simp
      moreover have "u  U  excess f u  0" for u 
        using fct1 excess_non_negative' s  U by auto
      ultimately show ?thesis using assms fct1 finite_V
        by (metis Diff_cancel Diff_eq_empty_iff 
                  Diff_infinite_finite finite_Diff sum_pos2)
    qed
    ultimately have 
      fct2: "(uU. (vU'. f (v, u))) - (uU. (vU'. f (u, v))) > 0" 
      by simp
    
    have fct3: "(uU. (vU'. f (v, u))) > 0"
    proof -
      have "(uU. (vU'. f (v, u)))  0" 
        using capacity_const by (simp add: sum_nonneg)
      moreover have "(uU. (vU'. f (u, v)))  0" 
        using capacity_const by (simp add: sum_nonneg)
      ultimately show ?thesis using fct2 by simp
    qed
      
    have "u' v'. (u'  U  v'  U'  f (v', u') > 0)"
    proof(rule ccontr)
      assume "¬ (u' v'. u'  U  v'  U'  f (v', u') > 0)"
      then have "(u' v'. (u'  U  v'  U'  f (v', u') = 0))"
        using capacity_const by (metis le_neq_trans)
      thus False using fct3 by simp
    qed
    then obtain u' v' where "u'  U" and "v'  U'" and "f (v', u') > 0" 
      by blast
    
    obtain p1 where "cf.isSimplePath u p1 u'" using U_def u'  U by auto
    moreover have "(u', v')  cf.E"
    proof -
      have "(v', u')  E" 
        using capacity_const f (v', u') > 0 
        by (metis not_less zero_flow_simp)
      then have "cf (u', v') > 0" unfolding cf_def 
        using no_parallel_edge f (v', u') > 0 by (auto split: if_split)
      thus ?thesis unfolding cf.E_def by simp
    qed
    ultimately have "cf.isPath u (p1 @ [(u', v')]) v'" 
      using Graph.isPath_append_edge Graph.isSimplePath_def by blast
    then obtain p2 where "cf.isSimplePath u p2 v'" 
      using cf.isSPath_pathLE by blast
    then have "v'  U" using U_def by auto
    thus False using v'  U' and U'_def by simp
  qed
  then obtain p' where "cf.isSimplePath u p' s" using U_def by auto
  thus ?thesis ..
qed
  
text ‹Relabel operations preserve the height bound~\cormen{26.20}.›
lemma relabel_pres_height_bound:
  assumes "relabel_precond f l u"
  shows "Height_Bounded_Labeling c s t f (relabel_effect f l u)"
proof -
  let ?l' = "relabel_effect f l u"
  
  from relabel_pres_Labeling[OF assms]  
  interpret l': Labeling c s t f ?l' .
  
  from assms have "excess f u > 0" unfolding relabel_precond_def by auto
  with l'.excess_imp_source_path obtain p where p_obt: "cf.isSimplePath u p s" .
  
  have "u  V" using excess_nodes_only excess f u > 0 .
  then have "length p < card V" 
    using cf.simplePath_length_less_V[of u p] p_obt by auto
  moreover have "?l' u  ?l' s + length p" 
    using p_obt l'.gen_valid[of u p s] p_obt 
    unfolding cf.isSimplePath_def by auto
  moreover have "?l' s = card V" 
    using l'.Labeling_axioms Labeling_def Labeling_axioms_def by auto
  ultimately have "?l' u  2*card V - 1" by auto
  thus "Height_Bounded_Labeling c s t f ?l'" 
    apply unfold_locales
    using height_bound relabel_preserve_other
    by metis  
qed      
  
text ‹Thus, the total number of relabel operations is 
  bounded by O(V2)›~\cormen{26.21}. 

  We express this bound by defining a measure function, and show that 
  it is decreased by relabel operations.
›  
definition (in Network) "sum_heights_measure l  vV. 2*card V - l v"
  
corollary relabel_measure:
  assumes "relabel_precond f l u"
  shows "sum_heights_measure (relabel_effect f l u) < sum_heights_measure l"
proof -
  let ?l' = "relabel_effect f l u"
  from relabel_pres_height_bound[OF assms] 
  interpret l': Height_Bounded_Labeling c s t f ?l' .
    
  from assms have "uV" 
    by (simp add: excess_nodes_only relabel_precond_def)
      
  hence V_split: "V = insert u V" by auto
      
  show ?thesis  
    using relabel_increase_u[OF assms] relabel_preserve_other[of u]
    using l'.height_bound 
    unfolding sum_heights_measure_def
    apply (rewrite at "_. _" V_split)+
    apply (subst sum.insert_remove[OF finite_V])+
    using uV  
    by auto   
qed        
end ― ‹Height Bounded Labeling›  
  
lemma (in Network) sum_height_measure_is_OV2: 
  "sum_heights_measure l  2*(card V)2"
  unfolding  sum_heights_measure_def
proof -
  have "2 * card V - l v  2 * card V" for v by auto
  then have "(vV. 2 * card V - l v)  (vV. 2 * card V)" 
    by (meson sum_mono)
  also have "(vV. 2 * card V) = card V * (2 * card V)" 
    using finite_V by auto
  finally show "(vV. 2 * card V - l v)  2 * (card V)^2" 
    by (simp add: power2_eq_square)
qed
  
  
subsubsection ‹Formulation of the Abstract Algorithm›
text ‹We give a simple relational characterization 
  of the abstract algorithm as a labeled transition system,
  where the labels indicate the type of operation (push or relabel) that
  have been executed.
›  
context Network
begin
  
datatype pr_operation = is_PUSH: PUSH | is_RELABEL: RELABEL
inductive_set pr_algo_lts 
  :: "(('capacity flow×labeling) × pr_operation × ('capacity flow×labeling)) set" 
where
  push: "push_precond f l e 
     ((f,l),PUSH,(push_effect f e,l))pr_algo_lts"
| relabel: "relabel_precond f l u
     ((f,l),RELABEL,(f,relabel_effect f l u))pr_algo_lts"

  
end ― ‹Network› 
  
text ‹We show invariant maintenance and correctness on termination›  
  
lemma (in Height_Bounded_Labeling) pr_algo_maintains_hb_labeling:
  assumes "((f,l),a,(f',l'))  pr_algo_lts"
  shows "Height_Bounded_Labeling c s t f' l'"  
  using assms
  by cases (simp_all add: push_pres_height_bound relabel_pres_height_bound)  
  
lemma (in Height_Bounded_Labeling) pr_algo_term_maxflow:
  assumes "(f,l)Domain pr_algo_lts"
  shows "isMaxFlow f"
proof -
  from assms have "e. push_precond f l e" and "u. relabel_precond f l u"
    by (auto simp: Domain_iff dest: pr_algo_lts.intros)
  with push_relabel_term_imp_maxflow show ?thesis by blast
qed      
  
  
subsubsection ‹Saturating and Non-Saturating Push Operations›  
context Network
begin
  
text ‹
  For complexity estimation, it is distinguished whether a push operation
  saturates the edge or not.
›  
definition sat_push_precond :: "'capacity flow  labeling  edge  bool"
  where "sat_push_precond f l 
   λ(u,v). excess f u > 0 
           excess f u  cf_of f (u,v) 
           (u,v)cfE_of f 
           l u = l v + 1"

definition nonsat_push_precond :: "'capacity flow  labeling  edge  bool"
  where "nonsat_push_precond f l 
   λ(u,v). excess f u > 0 
           excess f u < cf_of f (u,v) 
           (u,v)cfE_of f 
           l u = l v + 1"

lemma push_precond_eq_sat_or_nonsat: 
  "push_precond f l e  sat_push_precond f l e  nonsat_push_precond f l e"  
  unfolding push_precond_def sat_push_precond_def nonsat_push_precond_def
  by auto  
  
lemma sat_nonsat_push_disj: 
  "sat_push_precond f l e  ¬nonsat_push_precond f l e"
  "nonsat_push_precond f l e  ¬sat_push_precond f l e"
  unfolding sat_push_precond_def nonsat_push_precond_def
  by auto  
  
lemma sat_push_alt: "sat_push_precond f l e 
   push_effect f e = augment_edge f e (cf_of f e)"
  unfolding push_effect_def push_precond_eq_sat_or_nonsat sat_push_precond_def 
  by (auto simp: min_absorb2)
    
lemma nonsat_push_alt: "nonsat_push_precond f l (u,v) 
   push_effect f (u,v) = augment_edge f (u,v) (excess f u)"    
  unfolding push_effect_def push_precond_eq_sat_or_nonsat nonsat_push_precond_def 
  by (auto simp: min_absorb1)

end ― ‹Network› 
  
context push_effect_locale
begin
  lemma nonsat_push_Δ: "nonsat_push_precond f l (u,v)  Δ = excess f u"      
    unfolding Δ_def nonsat_push_precond_def by auto
  lemma sat_push_Δ: "sat_push_precond f l (u,v)  Δ = cf (u,v)"      
    unfolding Δ_def sat_push_precond_def by auto
    
end    
  
  
subsubsection ‹Refined Labeled Transition System›
context Network
begin
  
text ‹For simpler reasoning, we make explicit the different push operations,
  and integrate the invariant into the LTS›
  
datatype pr_operation' = 
  is_RELABEL': RELABEL' 
| is_NONSAT_PUSH': NONSAT_PUSH'
| is_SAT_PUSH': SAT_PUSH' edge   
  
inductive_set pr_algo_lts' where
  nonsat_push': "Height_Bounded_Labeling c s t f l; nonsat_push_precond f l e 
     ((f,l),NONSAT_PUSH',(push_effect f e,l))pr_algo_lts'"
| sat_push': "Height_Bounded_Labeling c s t f l; sat_push_precond f l e 
     ((f,l),SAT_PUSH' e,(push_effect f e,l))pr_algo_lts'"
| relabel': "Height_Bounded_Labeling c s t f l; relabel_precond f l u 
     ((f,l),RELABEL',(f,relabel_effect f l u))pr_algo_lts'"

fun project_operation where
  "project_operation RELABEL' = RELABEL"
| "project_operation NONSAT_PUSH' = PUSH"  
| "project_operation (SAT_PUSH' _) = PUSH"  

lemma is_RELABEL_project_conv[simp]: 
  "is_RELABEL  project_operation = is_RELABEL'"
  apply (clarsimp intro!: ext) subgoal for x by (cases x) auto done

lemma is_PUSH_project_conv[simp]: 
  "is_PUSH  project_operation = (λx. is_SAT_PUSH' x  is_NONSAT_PUSH' x)"
  apply (clarsimp intro!: ext) subgoal for x by (cases x) auto done
    
end ― ‹Network› 
  
context Height_Bounded_Labeling
begin  
lemma (in Height_Bounded_Labeling) xfer_run:
  assumes "((f,l),p,(f',l'))  trcl pr_algo_lts"
  obtains p' where "((f,l),p',(f',l'))  trcl pr_algo_lts'" 
               and "p = map project_operation p'"
proof -    
  have "p'. 
      Height_Bounded_Labeling c s t f' l'
     ((f,l),p',(f',l'))  trcl pr_algo_lts' 
     p = map project_operation p'"
    using assms
  proof (induction p arbitrary: f' l' rule: rev_induct)
    case Nil thus ?case using Height_Bounded_Labeling_axioms by simp  
  next
    case (snoc a p)
    from snoc.prems obtain fh lh 
      where PP: "((f, l), p, fh, lh)  trcl pr_algo_lts" 
        and LAST: "((fh,lh),a,(f',l'))pr_algo_lts" 
      by (auto dest!: trcl_rev_uncons)  

    from snoc.IH[OF PP] obtain p' 
      where HBL: "Height_Bounded_Labeling c s t fh lh" 
        and PP': "((f, l), p', fh, lh)  trcl pr_algo_lts'" 
        and [simp]: "p = map project_operation p'"
      by blast
        
    from LAST obtain a' 
      where LAST': "((fh,lh),a',(f',l'))pr_algo_lts'"
        and [simp]: "a = project_operation a'" 
      apply cases
      by (auto 
          simp: push_precond_eq_sat_or_nonsat
          dest: relabel'[OF HBL] nonsat_push'[OF HBL] sat_push'[OF HBL])  
    
    note HBL' = 
      Height_Bounded_Labeling.pr_algo_maintains_hb_labeling[OF HBL LAST]
      
    from HBL' trcl_rev_cons[OF PP' LAST'] show ?case by auto
  qed
  with assms that show ?thesis by blast
qed      
  
lemma xfer_relabel_bound:
  assumes BOUND: "p'. ((f,l),p',(f',l'))  trcl pr_algo_lts' 
           length (filter is_RELABEL' p')  B"
  assumes RUN: "((f,l),p,(f',l'))  trcl pr_algo_lts"
  shows "length (filter is_RELABEL p)  B"  
proof -
  from xfer_run[OF RUN] obtain p' 
    where RUN': "((f,l),p',(f',l'))  trcl pr_algo_lts'" 
      and [simp]: "p = map project_operation p'" .
  
  have "length (filter is_RELABEL p) = length (filter is_RELABEL' p')" 
    by simp
  also from BOUND[rule_format,OF RUN'] 
  have "length (filter is_RELABEL' p')  B" .
  finally show ?thesis .
qed
  
lemma xfer_push_bounds:
  assumes BOUND_SAT: "p'. ((f,l),p',(f',l'))  trcl pr_algo_lts' 
           length (filter is_SAT_PUSH' p')  B1"
  assumes BOUND_NONSAT: "p'. ((f,l),p',(f',l'))  trcl pr_algo_lts' 
           length (filter is_NONSAT_PUSH' p')  B2"
  assumes RUN: "((f,l),p,(f',l'))  trcl pr_algo_lts"
  shows "length (filter is_PUSH p)  B1 + B2"  
proof -
  
  from xfer_run[OF RUN] obtain p' 
    where RUN': "((f,l),p',(f',l'))  trcl pr_algo_lts'" 
      and [simp]: "p = map project_operation p'" .
  
  have [simp]: "length [xp' . is_SAT_PUSH' x  is_NONSAT_PUSH' x]
    = length (filter is_SAT_PUSH' p') + length (filter is_NONSAT_PUSH' p')"    
    by (induction p') auto
      
  have "length (filter is_PUSH p) 
    = length (filter is_SAT_PUSH' p') + length (filter is_NONSAT_PUSH' p')" 
    by simp
  also note BOUND_SAT[rule_format,OF RUN']    
  also note BOUND_NONSAT[rule_format,OF RUN']    
  finally show ?thesis by simp
qed
  
  
  
end ― ‹Height Bounded Labeling› 

  
subsubsection ‹Bounding the Relabel Operations›  
  
lemma (in Network) relabel_action_bound':
  assumes A: "(fxl,p,fxl')  trcl pr_algo_lts'"
  shows "length (filter (is_RELABEL') p)  2 * (card V)2"
proof -   
  from A have "length (filter (is_RELABEL') p)  sum_heights_measure (snd fxl)"
    apply (induction rule: trcl.induct)
    apply (auto elim!: pr_algo_lts'.cases)  
    apply (drule (1) Height_Bounded_Labeling.relabel_measure)
    apply auto
    done
  also note sum_height_measure_is_OV2
  finally show "length (filter (is_RELABEL') p)  2 * (card V)2" .
qed  
  
lemma (in Height_Bounded_Labeling) relabel_action_bound:
  assumes A: "((f,l),p,(f',l'))  trcl pr_algo_lts"
  shows "length (filter (is_RELABEL) p)  2 * (card V)2"
  using xfer_relabel_bound relabel_action_bound' A by meson
  
subsubsection ‹Bounding the Saturating Push Operations›
  
context Network
begin
text ‹
  The basic idea is to estimate the saturating push operations per edge:
  After a saturating push, the edge disappears from the residual graph.
  It can only re-appear due to a push over the reverse edge, which requires
  relabeling of the nodes. 

  The estimation in \cormen{26.22} uses the same idea. However, it invests 
  some extra work in getting a more precise constant factor 
  by counting the pushes for an edge and its reverse edge together.
›  

lemma labels_path_increasing:
  assumes "((f,l),p,(f',l'))  trcl pr_algo_lts'"
  shows "l u  l' u"
  using assms 
proof (induction p arbitrary: f l)
  case Nil thus ?case by simp
next
  case (Cons a p)
  then obtain fh lh 
    where FIRST: "((f,l),a,(fh,lh))  pr_algo_lts'"
      and PP: "((fh,lh),p,(f',l')): trcl pr_algo_lts'"
    by (auto simp: trcl_conv)  
      
  from FIRST interpret Height_Bounded_Labeling c s t f l
    by cases auto
    
  from FIRST Cons.IH[OF PP] show ?case
    apply (auto elim!: pr_algo_lts'.cases)
    using relabel_increase_u relabel_preserve_other
    by (metis le_trans nat_le_linear not_less)  
qed
  
lemma edge_reappears_at_increased_labeling:
  assumes "((f,l),p,(f',l'))  trcl pr_algo_lts'"
  assumes "l u  l v + 1"
  assumes "(u,v)  cfE_of f"  
  assumes E': "(u,v)  cfE_of f'"
  shows "l v < l' v"  
  using assms(1-3)
proof (induction p arbitrary: f l)
  case Nil thus ?case using E' by auto
next
  case (Cons a p)
  then obtain fh lh 
    where FIRST: "((f,l),a,(fh,lh))  pr_algo_lts'"
      and PP: "((fh,lh),p,(f',l')): trcl pr_algo_lts'"
    by (auto simp: trcl_conv)  
      
  from FIRST interpret Height_Bounded_Labeling c s t f l
    by cases auto
  
  consider 
    (push) u' v' 
      where "push_precond f l (u',v')" "fh = push_effect f (u',v')" "lh=l"
  | (relabel) u' 
      where "relabel_precond f l u'" "fh=f" "lh=relabel_effect f l u'"
    using FIRST       
    by (auto elim!: pr_algo_lts'.cases simp: push_precond_eq_sat_or_nonsat)  
  then show ?case proof cases
    case push
    note [simp] = push(2,3)  
    text ‹The push operation cannot go on edge (u,v)› or (v,u)›  
    from push(1) have "(u',v')(u,v)" "(u',v')(v,u)" "(u',v')cf.E"
      using l u  l v + 1 (u,v)cf.E
      by (auto simp: push_precond_def)
    hence NE': "(u,v)cfE_of fh" using (u,v)cf.E
      using cfE_augment_ss[of u' v' f]  
      by (auto simp: push_effect_def)  
    from Cons.IH[OF PP _ NE'] l u  l v + 1 show ?thesis by simp
  next
    case relabel
    note [simp] = relabel(2)  
      
    show ?thesis 
    proof (cases "u'=v")
      case False 
      from False relabel(3) relabel_preserve_other have [simp]: "lh v = l v" 
        by auto
      from False relabel(3) 
           relabel_preserve_other relabel_increase_u[OF relabel(1)]
      have "lh u  l u" by (cases "u'=u") auto
      with l u  l v + 1 have LHG: "lh u  lh v + 1" by auto
          
      from Cons.IH[OF PP LHG] (u,v)cf.E show ?thesis by simp
    next
      case True
      note [simp] = relabel(3)  
      from True relabel_increase_u[OF relabel(1)] 
      have "l v < lh v" by simp
      also note labels_path_increasing[OF PP, of v]
      finally show ?thesis by simp       
    qed
  qed
qed
  
lemma sat_push_edge_action_bound':
  assumes "((f,l),p,(f',l'))  trcl pr_algo_lts'" 
  shows "length (filter ((=) (SAT_PUSH' e)) p)  2*card V"
proof -
  obtain u v where [simp]: "e=(u,v)" by (cases e)
  
  have "length (filter ((=) (SAT_PUSH' (u,v))) p)  2*card V - l v"
    if "((f,l),p,(f',l'))  trcl pr_algo_lts'" for p
    using that
  proof (induction p arbitrary: f l rule: length_induct)
    case (1 p) thus ?case 
    proof (cases p)
      case Nil thus ?thesis by auto
    next        
      case [simp]: (Cons a p') 
      from "1.prems" obtain fh lh 
        where FIRST: "((f,l),a,(fh,lh))  pr_algo_lts'"
          and PP: "((fh,lh),p',(f',l'))  trcl pr_algo_lts'"
        by (auto dest!: trcl_uncons)  
        
      from FIRST interpret Height_Bounded_Labeling c s t f l
        by cases auto
          
      show ?thesis
      proof (cases "a = SAT_PUSH' (u,v)")  
        case [simp]: False
        from "1.IH" PP have 
          "length (filter ((=) (SAT_PUSH' (u, v))) p') 
           2 * card V - lh v"
          by auto
        with FIRST show ?thesis
          apply (cases; clarsimp)
        proof -
          fix ua :: nat
          assume a1: "length (filter ((=) (SAT_PUSH' (u, v))) p') 
                     2 * card V - relabel_effect f l ua v"
          assume a2: "relabel_precond f l ua"
          have "2 * card V - relabel_effect f l ua v  2 * card V - l v 
           length (filter ((=) (SAT_PUSH' (u, v))) p')  2 * card V - l v"
            using a1 order_trans by blast
          then show "length (filter ((=) (SAT_PUSH' (u, v))) p') 
                     2 * card V - l v"
            using a2 a1 by (metis (no_types) Labeling.relabel_increase_u 
                Labeling_axioms diff_le_mono2 nat_less_le 
                relabel_preserve_other)
        qed  
      next          
        case [simp]: True
  
        from FIRST have 
          [simp]: "fh = push_effect f (u,v)" "lh = l" 
          and PRE: "sat_push_precond f l (u,v)"
          by (auto elim !: pr_algo_lts'.cases)  
          
        from PRE have "(u,v)cf.E" "l u = l v + 1" 
          unfolding sat_push_precond_def by auto
        hence "uV" "vV" "uv" using cfE_ss_invE E_ss_VxV by auto
            
        have UVNEH: "(u,v)cfE_of fh" 
          using uv
          apply (simp 
                  add: sat_push_alt[OF PRE] augment_edge_cf'[OF (u,v)cf.E])
          unfolding Graph.E_def by simp
            
            
        show ?thesis 
        proof (cases "SAT_PUSH' (u,v)  set p'")  
          case False 
          hence [simp]: "filter ((=) (SAT_PUSH' (u,v))) p' = []" 
            by (induction p') auto
          show ?thesis 
            using bspec[OF height_bound uV]  
            using bspec[OF height_bound vV]
            using card_V_ge2 
            by simp
        next
          case True
          then obtain p1 p2 
            where [simp]: "p'=p1@SAT_PUSH' (u,v)#p2" 
              and NP1: "SAT_PUSH' (u,v)  set p1"
            using in_set_conv_decomp_first[of _ p'] by auto 
              
          from NP1 have [simp]: "filter ((=) (SAT_PUSH' (u,v))) p1 = []" 
            by (induction p1) auto
              
          from PP obtain f2 l2 f3 l3 
            where P1: "((fh,lh),p1,(f2,l2))  trcl pr_algo_lts'"
              and S: "((f2,l2),SAT_PUSH' (u,v),(f3,l3))  pr_algo_lts'"
              and P2: "((f3,l3),p2,(f',l'))trcl pr_algo_lts'"
            by (auto simp: trcl_conv)  
          from S have "(u,v)cfE_of f2" and [simp]: "l3=l2" 
            by (auto elim!: pr_algo_lts'.cases simp: sat_push_precond_def)
          with edge_reappears_at_increased_labeling[OF P1 _ UVNEH] 
            l u = l v + 1
          have AUX1: "l v < l2 v" by auto
  
          from S interpret l2: Height_Bounded_Labeling c s t f2 l2
            by (auto elim!: pr_algo_lts'.cases)
              
          from spec[OF "1.IH", of "SAT_PUSH' (u,v)#p2"] S P2 have 
            "Suc (length (filter ((=) (SAT_PUSH' (u, v))) p2)) 
             2 * card V - l2 v" 
            by (auto simp: trcl_conv)
          also have " + 1  2*card V - l v"
            using AUX1
            using bspec[OF l2.height_bound uV]  
            using bspec[OF l2.height_bound vV]
            by auto  
          finally show ?thesis
            by simp
        qed
      qed  
    qed
  qed
  thus ?thesis using assms by fastforce  
qed          

lemma sat_push_action_bound':
  assumes A: "((f,l),p,(f',l'))  trcl pr_algo_lts'"
  shows "length (filter is_SAT_PUSH' p)  4 * card V * card E"
proof -
  from A have IN_E: "eEE¯" if "SAT_PUSH' e  set p" for e
    using that cfE_of_ss_invE
    apply (induction p arbitrary: f l)
    apply (auto 
        simp: trcl_conv sat_push_precond_def 
        elim!: pr_algo_lts'.cases
      ; blast)+
    done  
    
  have AUX: "length (filter (λa. eS. a = SAT_PUSH' e) p) 
    = (eS. length (filter ((=) (SAT_PUSH' e)) p))" if "finite S" for S
    using that
    apply induction
    apply simp 
    apply clarsimp
    apply (subst length_filter_disj_or_conv; clarsimp)
    apply (fo_rule arg_cong)
    subgoal premises by (induction p) auto
    done    

  have "is_SAT_PUSH' a = (eEE¯. a = SAT_PUSH' e)" if "aset p" for a
    using IN_E that by (cases a) auto
  hence "length (filter is_SAT_PUSH' p) 
    = length (filter (λa. eEE¯. a = SAT_PUSH' e) p)" 
    by (auto cong: filter_cong)
  also have " = (eEE¯. length (filter ((=) (SAT_PUSH' e)) p))" 
    by (auto simp: AUX)
  also have "  (iE  E¯. 2 * card V)" 
    using sum_mono[OF sat_push_edge_action_bound'[OF A], where K="EE¯"] .
  also have "  4 * card V * card E" using card_Un_le[of E "E¯"] by simp
  finally show "length (filter is_SAT_PUSH' p)  4 * card V * card E" .
qed    
  
end ― ‹Network›  
  
subsubsection ‹Bounding the Non-Saturating Push Operations›    

text ‹
  For estimating the number of non-saturating push operations, we
  define a potential function that is the sum of the labels of
  all active nodes, and examine the effect of the operations
  on this potential:
     A non-saturating push deactivates the source node and may activate 
      the target node. As the source node's label is higher, the potential
      decreases.
     A saturating push may activate a node, thus increasing the potential 
      by O(V)›.
     A relabel operation may increase the potential by O(V)›.

  As there are at most O(V2)› relabel and O(VE)› saturating push operations,
  the above bounds suffice to yield an O(V2E)› bound for the non-saturating 
  push operations.

  This argumentation corresponds to \cormen{26.23}.
›  

text ‹Sum of heights of all active nodes›
definition (in Network) "nonsat_potential f l  sum l {vV. excess f v > 0}"
  
context Height_Bounded_Labeling
begin
  
text ‹The potential does not exceed O(V2)›. ›  
lemma nonsat_potential_bound:
  shows "nonsat_potential f l  2 * (card V)^2"
proof -
  have "nonsat_potential f l = (v{v  V. 0 < excess f v}. l v)" 
    unfolding nonsat_potential_def by auto
  also have "  (vV. l v)"
  proof -
    have f1:"{v  V. 0 < excess f v}  V" by auto
    thus ?thesis using sum.subset_diff[OF f1 finite_V, of l] by auto
  qed
  also have "   (vV. 2 * card V - 1)" 
    using height_bound by (meson sum_mono)
  also have " = card V * (2 * card V - 1)" by auto
  also have "card V * (2 * card V - 1)  2 * card V * card V" by auto
  finally show ?thesis by (simp add: power2_eq_square)
qed
  
  
  
text ‹A non-saturating push decreases the potential.›  
lemma nonsat_push_decr_nonsat_potential:
  assumes "nonsat_push_precond f l e"
  shows "nonsat_potential (push_effect f e) l < nonsat_potential f l"  
proof (cases e)
  case [simp]: (Pair u v)
    
  show ?thesis 
  proof simp  
    interpret push_effect_locale c s t f l u v 
      apply unfold_locales using assms 
      by (simp add: push_precond_eq_sat_or_nonsat)
      
    note [simp] = nonsat_push_Δ[OF assms[simplified]]
  
    define S where "S={xV. xu  xv  0<excess f x}"
    have S_alt: "S = {xV. xu  xv  0<excess f' x}"  
      unfolding S_def by auto
  
    have NES: "sS" "uS" "vS" 
      and [simp, intro!]: "finite S" 
      unfolding S_def using excess_s_non_pos
      by auto 
      
    have 1: "{vV. 0 < excess f' v} = (if s=v then S else insert v S)"
      unfolding S_alt
      using excess_u_pos excess_non_negative' l'.excess_s_non_pos
      by (auto intro!: add_nonneg_pos)
        
    have 2: "{vV. 0 < excess f v} 
      = insert u S  (if excess f v>0 then {v} else {})"    
      unfolding S_def using excess_u_pos by auto  
  
    show "nonsat_potential f' l < nonsat_potential f l"
      unfolding nonsat_potential_def 1 2
      by (cases "s=v"; cases "0<excess f v"; auto simp: NES)
  qed      
qed
  
  
text ‹A saturating push increases the potential by O(V)›.›
lemma sat_push_nonsat_potential:
  assumes PRE: "sat_push_precond f l e"
  shows "nonsat_potential (push_effect f e) l 
       nonsat_potential f l + 2 * card V"
proof - 
  obtain u v where [simp]: "e = (u, v)" by (cases e) auto   
  
  interpret push_effect_locale c s t f l u v
    using PRE
    by unfold_locales (simp add: push_precond_eq_sat_or_nonsat)

  have [simp, intro!]: "finite {vV. excess f v > 0}"
    by auto
      
  text ‹Only target node may get activated›
  have "{vV. excess f' v > 0}  insert v {vV. excess f v > 0}"    
    using Δ_positive
    by (auto simp: excess'_if)
  text ‹Thus, potential increases by at most l v›    
  with sum_mono2[OF _ this, of l]
  have "nonsat_potential f' l  nonsat_potential f l + l v"
    unfolding nonsat_potential_def 
    by (auto simp: sum.insert_if split: if_splits)
  text ‹Which is bounded by O(V)›    
  also note height_bound'[of v]
  finally show ?thesis by simp
qed

  
text ‹A relabeling increases the potential by at most O(V)›
lemma relabel_nonsat_potential:
  assumes PRE: "relabel_precond f l u"
  shows "nonsat_potential f (relabel_effect f l u) 
        nonsat_potential f l + 2 * card V"
proof -  
  have [simp, intro!]: "finite {vV. excess f v > 0}"
    by auto
  
  let ?l' = "relabel_effect f l u"
      
  interpret l': Height_Bounded_Labeling c s t f ?l'
    using relabel_pres_height_bound[OF assms] .
      
  from PRE have U_ACTIVE: "u  {vV. excess f v > 0}" and [simp]: "uV"
    unfolding relabel_precond_def using excess_nodes_only
    by auto
      
  have "nonsat_potential f ?l' 
      = sum ?l' ({v  V. 0 < excess f v} - {u}) + ?l' u"    
    unfolding nonsat_potential_def
    using U_ACTIVE by (auto intro: sum_arb)
  also have "sum ?l' ({v  V. 0 < excess f v} - {u}) 
      = sum l ({v  V. 0 < excess f v} - {u})"
    using relabel_preserve_other by auto
  also have "?l' u  l u + 2*card V"    
    using l'.height_bound'[OF uV] by auto
  finally have "nonsat_potential f ?l' 
               sum l ({v  V. 0 < excess f v} - {u}) + l u + 2 * card V"
    by auto
  also have "sum l ({v  V. 0 < excess f v} - {u}) + l u 
           = nonsat_potential f l"    
    unfolding nonsat_potential_def
    using U_ACTIVE by (auto intro: sum_arb[symmetric])
  finally show ?thesis .   
qed  

end ― ‹Height Bounded Labeling›  
  
context Network 
begin
  
lemma nonsat_push_action_bound':
  assumes A: "((f,l),p,(f',l'))  trcl pr_algo_lts'"
  shows "length (filter is_NONSAT_PUSH' p)  18 * (card V)2 * card E"
proof -
  have B1: "length (filter is_NONSAT_PUSH' p) 
       nonsat_potential f l 
      + 2 * card V * (length (filter is_SAT_PUSH' p))
      + 2 * card V * (length (filter is_RELABEL' p))"
    using A
  proof (induction p arbitrary: f l)    
    case Nil thus ?case by auto
  next
    case [simp]: (Cons a p)
    then obtain fh lh 
      where FIRST: "((f,l),a,(fh,lh))pr_algo_lts'" 
          and PP: "((fh,lh),p,(f',l'))  trcl pr_algo_lts'"
      by (auto simp: trcl_conv)  
    note IH = Cons.IH[OF PP]
  
    from FIRST interpret Height_Bounded_Labeling c s t f l 
      by cases auto
      
    show ?case using FIRST IH
      apply (cases a)
      apply (auto 
          elim!: pr_algo_lts'.cases 
          dest!: relabel_nonsat_potential nonsat_push_decr_nonsat_potential 
          dest!: sat_push_nonsat_potential
      )
      done
  qed
  
  
  
  (* TODO: Technical case distinction, as we do not assume invariant on f,l! *)
  show ?thesis proof (cases p)
    case Nil thus ?thesis by simp
  next
    case (Cons a' p')
    then interpret Height_Bounded_Labeling c s t f l using A
      by (auto simp: trcl_conv elim!: pr_algo_lts'.cases)
    note B1  
    also note nonsat_potential_bound  
    also note sat_push_action_bound'[OF A]
    also note relabel_action_bound'[OF A]
    finally have "length (filter is_NONSAT_PUSH' p)
       2 * (card V)2 + 8 * (card V)2 * card E + 4 * (card V)^3"  
      by (simp add: power2_eq_square power3_eq_cube)
    also have "(card V)^3  2 * (card V)2 * card E"  
      by (simp add: card_V_est_E power2_eq_square power3_eq_cube)
    finally have "length (filter is_NONSAT_PUSH' p) 
       2 * (card V)2 + 16 * (card V)2 * card E"
      by linarith
    also have "2 * (card V)2  2*(card V)2 * card E" by auto
    finally show "length (filter is_NONSAT_PUSH' p)  18 * (card V)2 * card E"
      by linarith
  qed
qed    
        
end ― ‹Network›  
        
subsubsection ‹Assembling the Final Theorem›
  
text ‹We combine the bounds for saturating and non-saturating push 
  operations.›
lemma (in Height_Bounded_Labeling) push_action_bound:
  assumes A: "((f,l),p,(f',l'))  trcl pr_algo_lts"
  shows "length (filter (is_PUSH) p)  22 * (card V)2 * card E"
  apply (rule order_trans[OF xfer_push_bounds[OF _ _ A]]; (intro allI impI)?)  
    apply (erule sat_push_action_bound'; fail)
    apply (erule nonsat_push_action_bound'; fail)
    apply (auto simp: power2_eq_square)
  done

text ‹We estimate the cost of a push by O(1)›, and of 
  a relabel operation by O(V)›
    
fun (in Network) cost_estimate :: "pr_operation  nat" where
  "cost_estimate RELABEL = card V"
| "cost_estimate PUSH = 1"  
  
text ‹We show the complexity bound of O(V2E)› when starting from any valid
  labeling \cormen{26.24}.›  
theorem (in Height_Bounded_Labeling) pr_algo_cost_bound:  
  assumes A: "((f,l),p,(f',l'))  trcl pr_algo_lts"
  shows "(ap. cost_estimate a)  26 * (card V)^2 * card E"
proof -
  have "(ap. cost_estimate a) 
    = card V * length (filter is_RELABEL p) + length (filter is_PUSH p)"
  proof (induction p)
    case Nil
    then show ?case by simp
  next
    case (Cons a p)
    then show ?case by (cases a) auto
  qed  
  also have "card V * length (filter is_RELABEL p)  2 * (card V)^3"  
    using relabel_action_bound[OF A] 
    by (auto simp: power2_eq_square power3_eq_cube) 
  also note push_action_bound[OF A]
  finally have "sum_list (map cost_estimate p) 
               2 * card V ^ 3 + 22 * (card V)2 * card E"
    by simp
  also have "(card V)^3  2 * (card V)2 * card E"  
    by (simp add: card_V_est_E power2_eq_square power3_eq_cube)
  finally show ?thesis by linarith     
qed      
      
subsection ‹Main Theorem: Correctness and Complexity›  
text ‹Finally, we state the main theorem of this section:
  If the algorithm executes some steps from the beginning, then
     If no further steps are possible from the reached state, we have 
      computed a maximum flow~\cormen{26.18}.
     The cost of these steps is bounded by O(V2E)›~\cormen{26.24}. 
      Note that this also implies termination.
›  
theorem (in Network) generic_preflow_push_OV2E_and_correct:
  assumes A: "((pp_init_f, pp_init_l), p, (f, l))  trcl pr_algo_lts" 
  shows "(xp. cost_estimate x)  26 * (card V)^2 * card E" (is ?G1)
    and "(f,l)Domain pr_algo_lts  isMaxFlow f" (is ?G2)
proof -
  show ?G1 
    using pp_init_height_bound Height_Bounded_Labeling.pr_algo_cost_bound A 
    by blast
  
  show ?G2 
    proof -
    from A interpret Height_Bounded_Labeling c s t f l
      apply (induction p arbitrary: f l rule: rev_induct)
      apply (auto 
        simp: pp_init_height_bound trcl_conv 
        intro: Height_Bounded_Labeling.pr_algo_maintains_hb_labeling)  
      done      
    from pr_algo_term_maxflow show ?G2 by simp
  qed      
qed    
  
subsection ‹Convenience Tools for Implementation›  
  
context Network
begin
text ‹In order to show termination of the algorithm, 
  we only need a well-founded relation over push and relabel steps›
  
inductive_set pr_algo_rel where
  push: "Height_Bounded_Labeling c s t f l; push_precond f l e 
     ((push_effect f e,l),(f,l))pr_algo_rel"
| relabel: "Height_Bounded_Labeling c s t f l; relabel_precond f l u
     ((f,relabel_effect f l u),(f,l))pr_algo_rel"

lemma pr_algo_rel_alt: "pr_algo_rel = 
    { ((push_effect f e,l),(f,l)) | f e l. 
        Height_Bounded_Labeling c s t f l  push_precond f l e }
   { ((f, relabel_effect f l u), (f,l)) | f u l. 
        Height_Bounded_Labeling c s t f l  relabel_precond f l u }"  
  by (auto elim!: pr_algo_rel.cases intro: pr_algo_rel.intros)
  
  
definition "pr_algo_len_bound  2 * (card V)2 + 22 * (card V)2 * card E"
  
lemma (in Height_Bounded_Labeling) pr_algo_lts_length_bound:  
  assumes A: "((f, l), p, (f', l'))  trcl pr_algo_lts"
  shows "length p  pr_algo_len_bound"
proof -
  have "length p = length (filter is_PUSH p) + length (filter is_RELABEL p)"
  proof (induction p)
    case Nil then show ?case by simp
  next
    case (Cons a p) then show ?case by (cases a) auto
  qed  
  also note push_action_bound[OF A]
  also note relabel_action_bound[OF A]
  finally show ?thesis unfolding pr_algo_len_bound_def by simp 
qed    
    
lemma (in Height_Bounded_Labeling) path_set_finite:  
  "finite {p. f' l'. ((f,l),p,(f',l'))  trcl pr_algo_lts}"
proof -  
  have FIN_OPS: "finite (UNIV::pr_operation set)"
    apply (rule finite_subset[where B="{PUSH,RELABEL}"])
    using pr_operation.exhaust by auto 
    
  have "{p. f' l'. ((f,l),p,(f',l'))  trcl pr_algo_lts} 
     {p. length p  pr_algo_len_bound}"  
    by (auto simp: pr_algo_lts_length_bound)
  also note finite_lists_length_le[OF FIN_OPS, simplified]
  finally (finite_subset) show ?thesis .
qed  
  
definition "pr_algo_measure 
   λ(f,l). Max {length p |p. aa ba. ((f, l), p, aa, ba)  trcl pr_algo_lts}"  
  
lemma pr_algo_measure: 
  assumes "(fl',fl)  pr_algo_rel"  
  shows "pr_algo_measure fl' < pr_algo_measure fl"
  using assms  
proof (cases fl'; cases fl; simp)
  fix f l f' l'
  assume A: "((f',l'),(f,l))  pr_algo_rel"  
  then obtain a where LTS_STEP: "((f,l),a,(f',l'))pr_algo_lts"
    by cases (auto intro: pr_algo_lts.intros)  
      
  from A interpret Height_Bounded_Labeling c s t f l by cases auto    
  from pr_algo_maintains_hb_labeling[OF LTS_STEP] 
  interpret f': Height_Bounded_Labeling c s t f' l' .

  let ?S1 = "{length p |p. fx lx. ((f, l), p, fx, lx)  trcl pr_algo_lts}"    
  let ?S2 = "{length p |p. fx lx. ((f', l'), p, fx, lx)  trcl pr_algo_lts}"    

  have "finite ?S1" using finite_image_set path_set_finite by blast 
  moreover have "?S1  {}" by (auto intro: exI[where x="[]"])   
  ultimately obtain p fx lx where 
    "length p = Max ?S1" 
    "((f, l), p, fx, lx)  trcl pr_algo_lts"
    apply -
    apply (drule (1) Max_in)
    by auto 

  have "finite ?S2" using finite_image_set f'.path_set_finite by blast 
  have "?S2  {}" by (auto intro: exI[where x="[]"])
  {
    assume MG: "Max ?S2  Max ?S1"
  
    from Max_in[OF finite ?S2 ?S2{}] obtain p fx lx where  
      "length p = Max ?S2" 
      "((f', l'), p, fx, lx)  trcl pr_algo_lts"
      by auto
    with MG LTS_STEP have
      LEN: "length (a#p) > Max ?S1"
      and P: "((f,l),a#p,(fx,lx))  trcl pr_algo_lts"
      by (auto simp: trcl_conv)  
    from P have "length (a#p)  ?S1" by blast
    from Max_ge[OF finite ?S1 this] LEN have False by simp   
  } thus "pr_algo_measure (f', l') < pr_algo_measure (f, l)"
    unfolding pr_algo_measure_def by (rule ccontr) auto
qed  

lemma wf_pr_algo_rel[simp, intro!]: "wf pr_algo_rel"  
  apply (rule wf_subset)
  apply (rule wf_measure[where f=pr_algo_measure])
  by (auto simp: pr_algo_measure)  
  
      
end ― ‹Network›
  
subsection ‹Gap Heuristics›  
context Network
begin
text ‹If we find a label value k› that is assigned to no node,
  we may relabel all nodes v› with k < l v < card V› to card V + 1›.
›
definition "gap_precond l k  vV. l v  k"
definition "gap_effect l k 
   λv. if k<l v  l v < card V then card V + 1 else l v"
  
text ‹The gap heuristics preserves a valid labeling.›  
lemma (in Labeling) gap_pres_Labeling:
  assumes PRE: "gap_precond l k"
  defines "l'  gap_effect l k"
  shows "Labeling c s t f l'"
proof    
  from lab_src show "l' s = card V" unfolding l'_def gap_effect_def by auto
  from lab_sink show "l' t = 0" unfolding l'_def gap_effect_def by auto
  
  have l'_incr: "l' v  l v" for v unfolding l'_def gap_effect_def by auto
      
  fix u v
  assume A: "(u,v)  cf.E"  
  hence "uV" "vV" using cfE_ss_invE E_ss_VxV by auto  
  thus "l' u  l' v + 1"  
    unfolding l'_def gap_effect_def
    using valid[OF A] PRE 
    unfolding gap_precond_def 
    by auto
qed  

text ‹The gap heuristics also preserves the height bounds.›  
lemma (in Height_Bounded_Labeling) gap_pres_hb_labeling:
  assumes PRE: "gap_precond l k"
  defines "l'  gap_effect l k"
  shows "Height_Bounded_Labeling c s t f l'"  
proof -  
  from gap_pres_Labeling[OF PRE] interpret Labeling c s t f l'
    unfolding l'_def .
  
  show ?thesis    
    apply unfold_locales
    unfolding l'_def gap_effect_def using height_bound by auto
qed  
  
text ‹We combine the regular relabel operation with the gap heuristics:
  If relabeling results in a gap, the gap heuristics is applied immediately.
›  
definition "gap_relabel_effect f l u  let l' = relabel_effect f l u in
  if (gap_precond l' (l u)) then gap_effect l' (l u) else l'
"  

text ‹The combined gap-relabel operation preserves a valid labeling.›  
lemma (in Labeling) gap_relabel_pres_Labeling:
  assumes PRE: "relabel_precond f l u"
  defines "l'  gap_relabel_effect f l u"
  shows "Labeling c s t f l'"
  unfolding l'_def gap_relabel_effect_def
  using relabel_pres_Labeling[OF PRE] Labeling.gap_pres_Labeling
  by (fastforce simp: Let_def)
  
text ‹The combined gap-relabel operation preserves the height-bound.›  
lemma (in Height_Bounded_Labeling) gap_relabel_pres_hb_labeling:
  assumes PRE: "relabel_precond f l u"
  defines "l'  gap_relabel_effect f l u"
  shows "Height_Bounded_Labeling c s t f l'"  
  unfolding l'_def gap_relabel_effect_def
  using relabel_pres_height_bound[OF PRE] Height_Bounded_Labeling.gap_pres_hb_labeling
  by (fastforce simp: Let_def)

subsubsection ‹Termination with Gap Heuristics›    
text ‹
  Intuitively, the algorithm with the gap heuristics terminates because 
  relabeling according to the gap heuristics preserves the invariant and 
  increases some labels towards their upper bound. 

  Formally, the simplest way is to combine a heights measure function with
  the already established measure for the standard algorithm:
›    
lemma (in Height_Bounded_Labeling) gap_measure:
  assumes "gap_precond l k"
  shows "sum_heights_measure (gap_effect l k)  sum_heights_measure l"
  unfolding gap_effect_def sum_heights_measure_def
  by (auto intro!: sum_mono)  
  
lemma (in Height_Bounded_Labeling) gap_relabel_measure:
  assumes PRE: "relabel_precond f l u"
  shows "sum_heights_measure (gap_relabel_effect f l u) < sum_heights_measure l"
  unfolding gap_relabel_effect_def
  using relabel_measure[OF PRE] relabel_pres_height_bound[OF PRE] Height_Bounded_Labeling.gap_measure
  by (fastforce simp: Let_def)
    
text ‹Analogously to @{const pr_algo_rel}, we provide a well-founded relation 
  that over-approximates the steps of a push-relabel algorithm with gap 
  heuristics.
›    
inductive_set gap_algo_rel where
  push: "Height_Bounded_Labeling c s t f l; push_precond f l e 
     ((push_effect f e,l),(f,l))gap_algo_rel"
| relabel: "Height_Bounded_Labeling c s t f l; relabel_precond f l u
     ((f,gap_relabel_effect f l u),(f,l))gap_algo_rel"
  
lemma wf_gap_algo_rel[simp, intro!]: "wf gap_algo_rel"  
proof -
  have "gap_algo_rel  inv_image (less_than <*lex*> less_than) (λ(f,l). (sum_heights_measure l, pr_algo_measure (f,l)))"
    using pr_algo_measure  
    using Height_Bounded_Labeling.gap_relabel_measure  
    by (fastforce elim!: gap_algo_rel.cases intro: pr_algo_rel.intros )
  thus ?thesis
    by (rule_tac wf_subset; auto)
qed  
  
end ― ‹Network›
  
  
end