Theory Approx_MIS_Hoare
section "Independent Set"
theory Approx_MIS_Hoare
imports
"HOL-Hoare.Hoare_Logic"
"HOL-Library.Disjoint_Sets"
begin
text ‹The algorithm is classical, the proofs are inspired by the ones
by Berghammer and M\"uller-Olm \<^cite>‹"BerghammerM03"›.
In particular the approximation ratio is improved from ‹Δ+1› to ‹Δ›.›
subsection "Graph"
text ‹A set set is simply a set of edges, where an edge is a 2-element set.›
definition independent_vertices :: "'a set set ⇒ 'a set ⇒ bool" where
"independent_vertices E S ⟷ S ⊆ ⋃E ∧ (∀v1 v2. v1 ∈ S ∧ v2 ∈ S ⟶ {v1, v2} ∉ E)"
locale Graph_E =
fixes E :: "'a set set"
assumes finite_E: "finite E"
assumes edges2: "e ∈ E ⟹ card e = 2"
begin
fun vertices :: "'a set set ⇒ 'a set" where
"vertices G = ⋃G"
abbreviation V :: "'a set" where
"V ≡ vertices E"
definition approximation_miv :: "nat ⇒ 'a set ⇒ bool" where
"approximation_miv n S ⟷ independent_vertices E S ∧ (∀S'. independent_vertices E S' ⟶ card S' ≤ card S * n)"
fun neighbors :: "'a ⇒ 'a set" where
"neighbors v = {u. {u,v} ∈ E}"
fun degree_vertex :: "'a ⇒ nat" where
"degree_vertex v = card (neighbors v)"
abbreviation Δ :: nat where
"Δ ≡ Max{degree_vertex u|u. u ∈ V}"
lemma finite_edges: "e ∈ E ⟹ finite e"
using card_ge_0_finite and edges2 by force
lemma finite_V: "finite V"
using finite_edges and finite_E by auto
lemma finite_neighbors: "finite (neighbors u)"
using finite_V and rev_finite_subset [of V "neighbors u"] by auto
lemma independent_vertices_finite: "independent_vertices E S ⟹ finite S"
by (metis rev_finite_subset independent_vertices_def vertices.simps finite_V)
lemma edge_ex_vertices: "e ∈ E ⟹ ∃u v. u ≠ v ∧ e = {u, v}"
proof -
assume "e ∈ E"
then have "card e = Suc (Suc 0)" using edges2 by auto
then show "∃u v. u ≠ v ∧ e = {u, v}"
by (metis card_eq_SucD insertI1)
qed
lemma Δ_pos [simp]: "E = {} ∨ 0 < Δ"
proof cases
assume "E = {}"
then show "E = {} ∨ 0 < Δ" by auto
next
assume 1: "E ≠ {}"
then have "V ≠ {}" using edges2 by fastforce
moreover have "finite {degree_vertex u |u. u ∈ V}"
by (metis finite_V finite_imageI Setcompr_eq_image)
ultimately have 2: "Δ ∈ {degree_vertex u |u. u ∈ V}" using Max_in by auto
have "Δ ≠ 0"
proof
assume "Δ = 0"
with 2 obtain u where 3: "u ∈ V" and 4: "degree_vertex u = 0" by auto
from 3 obtain e where 5: "e ∈ E" and "u ∈ e" by auto
moreover with 4 have "∀v. {u, v} ≠ e" using finite_neighbors insert_absorb by fastforce
ultimately show False using edge_ex_vertices by auto
qed
then show "E = {} ∨ 0 < Δ" by auto
qed
lemma Δ_max_degree: "u ∈ V ⟹ degree_vertex u ≤ Δ"
proof -
assume H: "u ∈ V"
have "finite {degree_vertex u |u. u ∈ V}"
by (metis finite_V finite_imageI Setcompr_eq_image)
with H show "degree_vertex u ≤ Δ" using Max_ge by auto
qed
subsection ‹Wei's algorithm: ‹(Δ+1)›-approximation›
text ‹The 'functional' part of the invariant, used to prove that the algorithm produces an independent set of vertices.›
definition inv_iv :: "'a set ⇒ 'a set ⇒ bool" where
"inv_iv S X ⟷ independent_vertices E S
∧ X ⊆ V
∧ (∀v1 ∈ (V - X). ∀v2 ∈ S. {v1, v2} ∉ E)
∧ S ⊆ X"
text ‹Strenghten the invariant with an approximation ratio ‹r›:›
definition inv_approx :: "'a set ⇒ 'a set ⇒ nat ⇒ bool" where
"inv_approx S X r ⟷ inv_iv S X ∧ card X ≤ card S * r"
text ‹Preservation of the functional invariant:›
lemma inv_preserv:
fixes S :: "'a set"
and X :: "'a set"
and x :: "'a"
assumes inv: "inv_iv S X"
and x_def: "x ∈ V - X"
shows "inv_iv (insert x S) (X ∪ neighbors x ∪ {x})"
proof -
have inv1: "independent_vertices E S"
and inv2: "X ⊆ V"
and inv3: "S ⊆ X"
and inv4: "∀v1 v2. v1 ∈ (V - X) ∧ v2 ∈ S ⟶ {v1, v2} ∉ E"
using inv unfolding inv_iv_def by auto
have finite_S: "finite S" using inv1 and independent_vertices_finite by auto
have S1: "∀y ∈ S. {x, y} ∉ E" using inv4 and x_def by blast
have S2: "∀x ∈ S. ∀y ∈ S. {x, y} ∉ E" using inv1 unfolding independent_vertices_def by metis
have S3: "v1 ∈ insert x S ⟹ v2 ∈ insert x S ⟹ {v1, v2} ∉ E" for v1 v2
proof -
assume "v1 ∈ insert x S" and "v2 ∈ insert x S"
then consider
(a) "v1 = x" and "v2 = x"
| (b) "v1 = x" and "v2 ∈ S"
| (c) "v1 ∈ S" and "v2 = x"
| (d) "v1 ∈ S" and "v2 ∈ S"
by auto
then show "{v1, v2} ∉ E"
proof cases
case a then show ?thesis using edges2 by force
next
case b then show ?thesis using S1 by auto
next
case c then show ?thesis using S1 by (metis doubleton_eq_iff)
next
case d then show ?thesis using S2 by auto
qed
qed
have "independent_vertices E (insert x S)"
using S3 and inv1 and x_def unfolding independent_vertices_def by auto
moreover have "X ∪ neighbors x ∪ {x} ⊆ V"
proof
fix xa
assume "xa ∈ X ∪ neighbors x ∪ {x}"
then consider (a) "xa ∈ X" | (b) "xa ∈ neighbors x" | (c) "xa = x" by auto
then show "xa ∈ V"
proof cases
case a
then show ?thesis using inv2 by blast
next
case b
then show ?thesis by auto
next
case c
then show ?thesis using x_def by blast
qed
qed
moreover have "insert x S ⊆ X ∪ neighbors x ∪ {x}" using inv3 by auto
moreover have "v1 ∈ V - (X ∪ neighbors x ∪ {x}) ⟹ v2 ∈ insert x S ⟹ {v1, v2} ∉ E" for v1 v2
proof -
assume H: "v1 ∈ V - (X ∪ neighbors x ∪ {x})" and "v2 ∈ insert x S"
then consider (a) "v2 = x" | (b) "v2 ∈ S" by auto
then show "{v1, v2} ∉ E"
proof cases
case a
with H have "v1 ∉ neighbors v2" by blast
then show ?thesis by auto
next
case b
from H have "v1 ∈ V - X" by blast
with b and inv4 show ?thesis by blast
qed
qed
ultimately show "inv_iv (insert x S) (X ∪ neighbors x ∪ {x})" unfolding inv_iv_def by blast
qed
lemma inv_approx_preserv:
assumes inv: "inv_approx S X (Δ + 1)"
and x_def: "x ∈ V - X"
shows "inv_approx (insert x S) (X ∪ neighbors x ∪ {x}) (Δ + 1)"
proof -
have finite_S: "finite S" using inv and independent_vertices_finite
unfolding inv_approx_def inv_iv_def by auto
have Sx: "x ∉ S" using inv and x_def unfolding inv_approx_def inv_iv_def by blast
from inv have "inv_iv S X" unfolding inv_approx_def by auto
with x_def have "inv_iv (insert x S) (X ∪ neighbors x ∪ {x})"
proof (intro inv_preserv, auto) qed
moreover have "card (X ∪ neighbors x ∪ {x}) ≤ card (insert x S) * (Δ + 1)"
proof -
have "degree_vertex x ≤ Δ" using Δ_max_degree and x_def by auto
then have "card (neighbors x ∪ {x}) ≤ Δ + 1" using card_Un_le [of "neighbors x" "{x}"] by auto
then have "card (X ∪ neighbors x ∪ {x}) ≤ card X + Δ + 1" using card_Un_le [of X "neighbors x ∪ {x}"] by auto
also have "... ≤ card S * (Δ + 1) + Δ + 1" using inv unfolding inv_approx_def by auto
also have "... = card (insert x S) * (Δ + 1)" using finite_S and Sx by auto
finally show ?thesis .
qed
ultimately show "inv_approx (insert x S) (X ∪ neighbors x ∪ {x}) (Δ + 1)"
unfolding inv_approx_def by auto
qed
lemma inv_approx: "independent_vertices E S ⟹ card V ≤ card S * r ⟹ approximation_miv r S"
proof -
assume 1: "independent_vertices E S" and 2: "card V ≤ card S * r"
have "independent_vertices E S' ⟹ card S' ≤ card S * r" for S'
proof -
assume "independent_vertices E S'"
then have "S' ⊆ V" unfolding independent_vertices_def by auto
then have "card S' ≤ card V" using finite_V and card_mono by auto
also have "... ≤ card S * r" using 2 by auto
finally show "card S' ≤ card S * r" .
qed
with 1 show "approximation_miv r S" unfolding approximation_miv_def by auto
qed
theorem wei_approx_Δ_plus_1:
"VARS (S :: 'a set) (X :: 'a set) (x :: 'a)
{ True }
S := {};
X := {};
WHILE X ≠ V
INV { inv_approx S X (Δ + 1) }
DO x := (SOME x. x ∈ V - X);
S := insert x S;
X := X ∪ neighbors x ∪ {x}
OD
{ approximation_miv (Δ + 1) S }"
proof (vcg, goal_cases)
case (1 S X x)
then show ?case unfolding inv_approx_def inv_iv_def independent_vertices_def by auto
next
case (2 S X x)
let ?x = "(SOME x. x ∈ V - X)"
have "V - X ≠ {}" using 2 unfolding inv_approx_def inv_iv_def by blast
then have "?x ∈ V - X" using some_in_eq by metis
with 2 show ?case using inv_approx_preserv by auto
next
case (3 S X x)
then show ?case using inv_approx unfolding inv_approx_def inv_iv_def by auto
qed
subsection ‹Wei's algorithm: ‹Δ›-approximation›
text ‹The previous approximation uses very little information about the optimal solution (it has at most as many vertices as the set itself). With some extra effort we can improve the ratio to ‹Δ› instead of ‹Δ+1›. In order to do that we must show that among the vertices removed in each iteration, at most ‹Δ› could belong to an optimal solution. This requires carrying around a set ‹P› (via a ghost variable) which records the vertices deleted in each iteration.›
definition inv_partition :: "'a set ⇒ 'a set ⇒ 'a set set ⇒ bool" where
"inv_partition S X P ⟷ inv_iv S X
∧ ⋃P = X
∧ (∀p ∈ P. ∃s ∈ V. p = {s} ∪ neighbors s)
∧ card P = card S
∧ finite P"
lemma inv_partition_preserv:
assumes inv: "inv_partition S X P"
and x_def: "x ∈ V - X"
shows "inv_partition (insert x S) (X ∪ neighbors x ∪ {x}) (insert ({x} ∪ neighbors x) P)"
proof -
have finite_S: "finite S" using inv and independent_vertices_finite
unfolding inv_partition_def inv_iv_def by auto
have Sx: "x ∉ S" using inv and x_def unfolding inv_partition_def inv_iv_def by blast
from inv have "inv_iv S X" unfolding inv_partition_def by auto
with x_def have "inv_iv (insert x S) (X ∪ neighbors x ∪ {x})"
proof (intro inv_preserv, auto) qed
moreover have "⋃(insert ({x} ∪ neighbors x) P) = X ∪ neighbors x ∪ {x}"
using inv unfolding inv_partition_def by auto
moreover have "(∀p∈insert ({x} ∪ neighbors x) P. ∃s ∈ V. p = {s} ∪ neighbors s)"
using inv and x_def unfolding inv_partition_def by auto
moreover have "card (insert ({x} ∪ neighbors x) P) = card (insert x S)"
proof -
from x_def and inv have "x ∉ ⋃P" unfolding inv_partition_def by auto
then have "{x} ∪ neighbors x ∉ P" by auto
then have "card (insert ({x} ∪ neighbors x) P) = card P + 1" using inv unfolding inv_partition_def by auto
moreover have "card (insert x S) = card S + 1" using Sx and finite_S by auto
ultimately show ?thesis using inv unfolding inv_partition_def by auto
qed
moreover have "finite (insert ({x} ∪ neighbors x) P)"
using inv unfolding inv_partition_def by auto
ultimately show "inv_partition (insert x S) (X ∪ neighbors x ∪ {x}) (insert ({x} ∪ neighbors x) P)"
unfolding inv_partition_def by auto
qed
lemma card_Union_le_sum_card:
fixes U :: "'a set set"
assumes "∀u ∈ U. finite u"
shows "card (⋃U) ≤ sum card U"
proof (cases "finite U")
case False
then show "card (⋃U) ≤ sum card U"
using card_eq_0_iff finite_UnionD by auto
next
case True
then show "card (⋃U) ≤ sum card U"
proof (induct U rule: finite_induct)
case empty
then show ?case by auto
next
case (insert x F)
then have "card(⋃(insert x F)) ≤ card(x) + card (⋃F)" using card_Un_le by auto
also have "... ≤ card(x) + sum card F" using insert.hyps by auto
also have "... = sum card (insert x F)" using sum.insert_if and insert.hyps by auto
finally show ?case .
qed
qed
lemma sum_card:
fixes U :: "'a set set"
and n :: nat
assumes "∀S ∈ U. card S ≤ n"
shows "sum card U ≤ card U * n"
proof cases
assume "infinite U ∨ U = {}"
then have "sum card U = 0" using sum.infinite by auto
then show "sum card U ≤ card U * n" by auto
next
assume "¬(infinite U ∨ U = {})"
with assms have "finite U" and "U ≠ {}"and "∀S ∈ U. card S ≤ n" by auto
then show "sum card U ≤ card U * n"
proof (induct U rule: finite_ne_induct)
case (singleton x)
then show ?case by auto
next
case (insert x F)
assume "∀S∈insert x F. card S ≤ n"
then have 1:"card x ≤ n" and 2:"sum card F ≤ card F * n" using insert.hyps by auto
then have "sum card (insert x F) = card x + sum card F" using sum.insert_if and insert.hyps by auto
also have "... ≤ n + card F * n" using 1 and 2 by auto
also have "... = card (insert x F) * n" using card_insert_if and insert.hyps by auto
finally show ?case .
qed
qed
lemma x_or_neighbors:
fixes P :: "'a set set"
and S :: "'a set"
assumes inv: "∀p∈P. ∃s ∈ V. p = {s} ∪ neighbors s"
and ivS: "independent_vertices E S"
shows "∀p ∈ P. card (S ∩ p) ≤ Δ"
proof
fix p
assume "p ∈ P"
then obtain s where 1: "s ∈ V ∧ p = {s} ∪ neighbors s" using inv by blast
then show "card (S ∩ p) ≤ Δ"
proof cases
assume "s ∈ S"
then have "S ∩ neighbors s = {}" using ivS unfolding independent_vertices_def by auto
then have "S ∩ p ⊆ {s}" using 1 by auto
then have 2: "card (S ∩ p) ≤ 1" using subset_singletonD by fastforce
consider (a) "E = {}" | (b) "0 < Δ" using Δ_pos by auto
then show "card (S ∩ p) ≤ Δ"
proof cases
case a
then have "S = {}" using ivS unfolding independent_vertices_def by auto
then show ?thesis by auto
next
case b
then show ?thesis using 2 by auto
qed
next
assume "s ∉ S"
with 1 have "S ∩ p ⊆ neighbors s" by auto
then have "card (S ∩ p) ≤ degree_vertex s" using card_mono and finite_neighbors by auto
then show "card (S ∩ p) ≤ Δ" using 1 and Δ_max_degree [of s] by auto
qed
qed
lemma inv_partition_approx: "inv_partition S V P ⟹ approximation_miv Δ S"
proof -
assume H1: "inv_partition S V P"
then have "independent_vertices E S" unfolding inv_partition_def inv_iv_def by auto
moreover have "independent_vertices E S' ⟹ card S' ≤ card S * Δ" for S'
proof -
let ?I = "{S' ∩ p | p. p ∈ P}"
assume H2: "independent_vertices E S'"
then have "S' ⊆ V" unfolding independent_vertices_def using vertices.simps by blast
with H1 have "S' = S' ∩ ⋃P" unfolding inv_partition_def by auto
then have "S' = (⋃p ∈ P. S' ∩ p)" using Int_Union by auto
then have "S' = ⋃?I" by blast
moreover have "finite S'" using H2 and independent_vertices_finite by auto
then have "p ∈ P ⟹ finite (S' ∩ p)" for p by auto
ultimately have "card S' ≤ sum card ?I" using card_Union_le_sum_card [of ?I] by auto
also have "... ≤ card ?I * Δ"
using x_or_neighbors [of P S']
and sum_card [of ?I Δ]
and H1 and H2 unfolding inv_partition_def by auto
also have "... ≤ card P * Δ"
proof -
have "finite P" using H1 unfolding inv_partition_def by auto
then have "card ?I ≤ card P"
using Setcompr_eq_image [of "λp. S' ∩ p" P]
and card_image_le unfolding inv_partition_def by auto
then show ?thesis by auto
qed
also have "... = card S * Δ" using H1 unfolding inv_partition_def by auto
ultimately show "card S' ≤ card S * Δ" by auto
qed
ultimately show "approximation_miv Δ S" unfolding approximation_miv_def by auto
qed
theorem wei_approx_Δ:
"VARS (S :: 'a set) (X :: 'a set) (x :: 'a)
{ True }
S := {};
X := {};
WHILE X ≠ V
INV { ∃P. inv_partition S X P }
DO x := (SOME x. x ∈ V - X);
S := insert x S;
X := X ∪ neighbors x ∪ {x}
OD
{ approximation_miv Δ S }"
proof (vcg, goal_cases)
case (1 S X x)
have "inv_partition {} {} {}" unfolding inv_partition_def inv_iv_def independent_vertices_def by auto
then show ?case by auto
next
case (2 S X x)
let ?x = "(SOME x. x ∈ V - X)"
from 2 obtain P where I: "inv_partition S X P" by auto
then have "V - X ≠ {}" using 2 unfolding inv_partition_def by auto
then have "?x ∈ V - X" using some_in_eq by metis
with I have "inv_partition (insert ?x S) (X ∪ neighbors ?x ∪ {?x}) (insert ({?x} ∪ neighbors ?x) P)"
using inv_partition_preserv by blast
then show ?case by auto
next
case (3 S X x)
then show ?case using inv_partition_approx unfolding inv_approx_def by auto
qed
subsection "Wei's algorithm with dynamically computed approximation ratio"
text ‹In this subsection, we augment the algorithm with a variable used to compute the effective approximation ratio of the solution. In addition, the vertex of smallest degree is picked. With this heuristic, the algorithm achieves an approximation ratio of ‹(Δ+2)/3›, but this is not proved here.›
definition vertex_heuristic :: "'a set ⇒ 'a ⇒ bool" where
"vertex_heuristic X v = (∀u ∈ V - X. card (neighbors v - X) ≤ card (neighbors u - X))"
lemma ex_min_finite_set:
fixes S :: "'a set"
and f :: "'a ⇒ nat"
shows "finite S ⟹ S ≠ {} ⟹ ∃x. x ∈ S ∧ (∀y ∈ S. f x ≤ f y)"
(is "?P1 ⟹ ?P2 ⟹ ∃x. ?minf S x")
proof (induct S rule: finite_ne_induct)
case (singleton x)
have "?minf {x} x" by auto
then show ?case by auto
next
case (insert x F)
from insert(4) obtain y where Py: "?minf F y" by auto
show "∃z. ?minf (insert x F) z"
proof cases
assume "f x < f y"
then have "?minf (insert x F) x" using Py by auto
then show ?case by auto
next
assume "¬f x < f y"
then have "?minf (insert x F) y" using Py by auto
then show ?case by auto
qed
qed
lemma inv_approx_preserv2:
fixes S :: "'a set"
and X :: "'a set"
and s :: nat
and x :: "'a"
assumes inv: "inv_approx S X s"
and x_def: "x ∈ V - X"
shows "inv_approx (insert x S) (X ∪ neighbors x ∪ {x}) (max (card (neighbors x ∪ {x} - X)) s)"
proof -
have finite_S: "finite S" using inv and independent_vertices_finite unfolding inv_approx_def inv_iv_def by auto
have Sx: "x ∉ S" using inv and x_def unfolding inv_approx_def inv_iv_def by blast
from inv have "inv_iv S X" unfolding inv_approx_def by auto
with x_def have "inv_iv (insert x S) (X ∪ neighbors x ∪ {x})"
proof (intro inv_preserv, auto) qed
moreover have "card (X ∪ neighbors x ∪ {x}) ≤ card (insert x S) * max (card (neighbors x ∪ {x} - X)) s"
proof -
let ?N = "neighbors x ∪ {x} - X"
have "card (X ∪ ?N) ≤ card X + card ?N" using card_Un_le [of X ?N] by auto
also have "... ≤ card S * s + card ?N" using inv unfolding inv_approx_def by auto
also have "... ≤ card S * max (card ?N) s + card ?N" by auto
also have "... ≤ card S * max (card ?N) s + max (card ?N) s" by auto
also have "... = card (insert x S) * max (card ?N) s" using Sx and finite_S by auto
finally show ?thesis by auto
qed
ultimately show "inv_approx (insert x S) (X ∪ neighbors x ∪ {x}) (max (card (neighbors x ∪ {x} - X)) s)"
unfolding inv_approx_def by auto
qed
theorem wei_approx_min_degree_heuristic:
"VARS (S :: 'a set) (X :: 'a set) (x :: 'a) (r :: nat)
{ True }
S := {};
X := {};
r := 0;
WHILE X ≠ V
INV { inv_approx S X r }
DO x := (SOME x. x ∈ V - X ∧ vertex_heuristic X x);
S := insert x S;
r := max (card (neighbors x ∪ {x} - X)) r;
X := X ∪ neighbors x ∪ {x}
OD
{ approximation_miv r S }"
proof (vcg, goal_cases)
case (1 S X x r)
then show ?case unfolding inv_approx_def inv_iv_def independent_vertices_def by auto
next
case (2 S X x r)
let ?x = "(SOME x. x ∈ V - X ∧ vertex_heuristic X x)"
have "V - X ≠ {}" using 2 unfolding inv_approx_def inv_iv_def by blast
moreover have "finite (V - X)" using 2 and finite_V by auto
ultimately have "∃x. x ∈ V - X ∧ vertex_heuristic X x"
using ex_min_finite_set [where ?f = "λx. card (neighbors x - X)"]
unfolding vertex_heuristic_def by auto
then have x_def: "?x ∈ V - X ∧ vertex_heuristic X ?x"
using someI_ex [where ?P = "λx. x ∈ V - X ∧ vertex_heuristic X x"] by auto
with 2 show ?case using inv_approx_preserv2 by auto
next
case (3 S X x r)
then show ?case using inv_approx unfolding inv_approx_def inv_iv_def by auto
qed
end
end