Theory Simplex_for_Reals

theory Simplex_for_Reals
imports Farkas Simplex_Incremental
(* Author: R. Thiemann *)

section ‹Unsatisfiability over the Reals›

text ‹By using Farkas' Lemma we prove that a finite set of 
  linear rational inequalities is satisfiable over the rational numbers
  if and only if it is satisfiable over the real numbers.
  Hence, the simplex algorithm either gives a rational solution or
  shows unsatisfiability over the real numbers.›

theory Simplex_for_Reals
  imports 
    Farkas
    Simplex.Simplex_Incremental
begin


instantiation real :: lrv
begin
definition scaleRat_real :: "rat ⇒ real ⇒ real" where
  [simp]: "x *R y = real_of_rat x * y"
instance by standard (auto simp add: field_simps of_rat_mult of_rat_add)
end

abbreviation real_satisfies_constraints :: "real valuation ⇒ constraint set ⇒ bool" (infixl "⊨rcs" 100) where
  "v ⊨rcs cs ≡ ∀ c ∈ cs. v ⊨c c"

definition of_rat_val :: "rat valuation ⇒ real valuation" where
  "of_rat_val v x = of_rat (v x)" 

lemma of_rat_val_eval: "p ⦃of_rat_val v⦄ = of_rat (p ⦃v⦄)" 
  unfolding of_rat_val_def linear_poly_sum of_rat_sum 
  by (rule sum.cong, auto simp: of_rat_mult)

lemma of_rat_val_constraint: "of_rat_val v ⊨c c ⟷ v ⊨c c" 
  by (cases c, auto simp: of_rat_val_eval of_rat_less of_rat_less_eq)

lemma of_rat_val_constraints: "of_rat_val v ⊨rcs cs ⟷ v ⊨cs cs" 
  using of_rat_val_constraint by auto

lemma sat_scale_rat_real: assumes "(v :: real valuation) ⊨c c"
  shows "v ⊨c (r *R c)"
proof -
  have "r < 0 ∨ r = 0 ∨ r > 0" by auto
  then show ?thesis using assms by (cases c, simp_all add: right_diff_distrib 
        valuate_minus valuate_scaleRat scaleRat_leq1 scaleRat_leq2 valuate_zero
        of_rat_less of_rat_mult)
qed

fun of_rat_lec :: "rat le_constraint ⇒ real le_constraint" where
  "of_rat_lec (Le_Constraint r p c) = Le_Constraint r p (of_rat c)" 

lemma lec_of_constraint_real: 
  assumes "is_le c"
  shows "(v ⊨le of_rat_lec (lec_of_constraint c)) ⟷ (v ⊨c c)"
  using assms by (cases c, auto)

lemma of_rat_lec_add: "of_rat_lec (c + d) = of_rat_lec c + of_rat_lec d" 
  by (cases c; cases d, auto simp: of_rat_add)

lemma of_rat_lec_zero: "of_rat_lec 0 = 0" 
  unfolding zero_le_constraint_def by simp

lemma of_rat_lec_sum: "of_rat_lec (sum_list c) = sum_list (map of_rat_lec c)" 
  by (induct c, auto simp: of_rat_lec_zero of_rat_lec_add)

text ‹This is the main lemma: a finite set of linear constraints is 
  satisfiable over Q if and only if it is satisfiable over R.›
lemma rat_real_conversion: assumes "finite cs" 
  shows "(∃ v :: rat valuation. v ⊨cs cs) ⟷ (∃ v :: real valuation. v ⊨rcs cs)" 
proof
  show "∃v. v ⊨cs cs ⟹ ∃v. v ⊨rcs cs" using of_rat_val_constraint by auto
  assume "∃v. v ⊨rcs cs" 
  then obtain v where *: "v ⊨rcs cs" by auto
  show "∃v. v ⊨cs cs" 
  proof (rule ccontr)
    assume "∄v. v ⊨cs cs" 
    from farkas_coefficients[OF assms] this
    obtain C where "farkas_coefficients cs C" by auto
    from this[unfolded farkas_coefficients_def]
    obtain d rel where
      isleq: "(∀(r,c) ∈ set C. c ∈ cs ∧ is_le (r *R c) ∧ r ≠ 0)" and
      leq: "(∑ (r,c) ← C. lec_of_constraint (r *R c)) = Le_Constraint rel 0 d" and
      choice: "rel = Lt_Rel ∧ d ≤ 0 ∨ rel = Leq_Rel ∧ d < 0" by blast
    {
      fix r c
      assume c: "(r,c) ∈ set C" 
      from c * isleq have "v ⊨c c" by auto
      hence v: "v ⊨c (r *R c)" by (rule sat_scale_rat_real)
      from c isleq have "is_le (r *R c)" by auto
      from lec_of_constraint_real[OF this] v 
      have "v ⊨le of_rat_lec (lec_of_constraint (r *R c))" by blast
    } note v = this
    have "Le_Constraint rel 0 (of_rat d) = of_rat_lec (∑ (r,c) ← C. lec_of_constraint (r *R c))" 
      unfolding leq by simp
    also have "… = (∑ (r,c) ← C. of_rat_lec (lec_of_constraint (r *R c)))" (is "_ = ?sum")
      unfolding of_rat_lec_sum map_map o_def by (rule arg_cong[of _ _ sum_list], auto)
    finally have leq: "Le_Constraint rel 0 (of_rat d) = ?sum" by simp
    have "v ⊨le Le_Constraint rel 0 (of_rat d)" unfolding leq
      by (rule satisfies_sumlist_le_constraints, insert v, auto)
    with choice show False by (auto simp: linear_poly_sum)
  qed
qed

text ‹The main result of simplex, now using unsatisfiability over the reals.›

fun i_satisfies_cs_real (infixl "⊨rics" 100) where
  "(I,v) ⊨rics cs ⟷ v ⊨rcs Simplex.restrict_to I cs"

lemma simplex_index_real:
  "simplex_index cs = Unsat I ⟹ set I ⊆ fst ` set cs ∧ ¬ (∃ v. (set I, v) ⊨rics set cs) ∧ 
     (distinct_indices cs ⟶ (∀ J ⊂ set I. (∃ v. (J, v) ⊨ics set cs)))" ― ‹minimal unsat core over the reals›
  "simplex_index cs = Sat v ⟹ ⟨v⟩ ⊨cs (snd ` set cs)" ― ‹satisfying assingment›
  using simplex_index(1)[of cs I] simplex_index(2)[of cs v] 
    rat_real_conversion[of "Simplex.restrict_to (set I) (set cs)"]
  by auto


lemma simplex_real:
  "simplex cs = Unsat I ⟹ ¬ (∃ v. v ⊨rcs set cs)" ― ‹unsat of original constraints over the reals›
  "simplex cs = Unsat I ⟹ set I ⊆ {0..<length cs} ∧ ¬ (∃ v. v ⊨rcs {cs ! i | i. i ∈ set I})
    ∧ (∀J⊂set I. ∃v. v ⊨cs {cs ! i |i. i ∈ J})" ― ‹minimal unsat core over reals›
  "simplex cs = Sat v ⟹ ⟨v⟩ ⊨cs set cs"  ― ‹satisfying assignment over the rationals›
proof (intro simplex(1)[unfolded rat_real_conversion[OF finite_set]])
  assume unsat: "simplex cs = Inl I" 
  have "finite {cs ! i |i. i ∈ set I}" by auto
  from simplex(2)[OF unsat, unfolded rat_real_conversion[OF this]]
  show "set I ⊆ {0..<length cs} ∧ ¬ (∃ v. v ⊨rcs {cs ! i | i. i ∈ set I})
    ∧ (∀J⊂set I. ∃v. v ⊨cs {cs ! i |i. i ∈ J})" by auto
qed (insert simplex(3), auto)

text ‹Define notion of minimal unsat core over the reals:
  the subset has to be unsat over the reals, and every proper subset has
  to be satisfiable over the rational numbers.›

definition minimal_unsat_core_real :: "'i set ⇒ 'i i_constraint list ⇒ bool" where
  "minimal_unsat_core_real I ics  = ((I ⊆ fst ` set ics) ∧ (¬ (∃ v. (I,v) ⊨rics set ics))
     ∧ (distinct_indices ics ⟶ (∀ J. J ⊂ I ⟶ (∃ v. (J,v) ⊨ics set ics))))"

text ‹Because of equi-satisfiability the two notions of minimal unsat cores coincide.›
lemma minimal_unsat_core_real_conv: "minimal_unsat_core_real I ics = minimal_unsat_core I ics" 
proof 
  show "minimal_unsat_core_real I ics ⟹ minimal_unsat_core I ics" 
    unfolding minimal_unsat_core_real_def minimal_unsat_core_def
    using of_rat_val_constraint by simp metis
next
  assume "minimal_unsat_core I ics"     
  thus "minimal_unsat_core_real I ics" 
    unfolding minimal_unsat_core_real_def minimal_unsat_core_def
    using rat_real_conversion[of "Simplex.restrict_to I (set ics)"]
    by auto
qed

text ‹Easy consequence: The incremental simplex algorithm is also sound wrt. 
  minimal-unsat-cores over the reals.›
lemmas incremental_simplex_real = 
  init_simplex
  assert_simplex_ok
  assert_simplex_unsat[folded minimal_unsat_core_real_conv]
  assert_all_simplex_ok
  assert_all_simplex_unsat[folded minimal_unsat_core_real_conv]
  check_simplex_ok
  check_simplex_unsat[folded minimal_unsat_core_real_conv]
  solution_simplex
  backtrack_simplex
  checked_invariant_simplex

end