Theory FOL

theory FOL
imports IFOL
(*  Title:      FOL/FOL.thy
    Author:     Lawrence C Paulson and Markus Wenzel
*)

section ‹Classical first-order logic›

theory FOL
imports IFOL
keywords "print_claset" "print_induct_rules" :: diag
begin

ML_file ‹~~/src/Provers/classical.ML›
ML_file ‹~~/src/Provers/blast.ML›
ML_file ‹~~/src/Provers/clasimp.ML›


subsection ‹The classical axiom›

axiomatization where
  classical: ‹(¬ P ⟹ P) ⟹ P›


subsection ‹Lemmas and proof tools›

lemma ccontr: ‹(¬ P ⟹ False) ⟹ P›
  by (erule FalseE [THEN classical])


subsubsection ‹Classical introduction rules for ‹∨› and ‹∃››

lemma disjCI: ‹(¬ Q ⟹ P) ⟹ P ∨ Q›
  apply (rule classical)
  apply (assumption | erule meta_mp | rule disjI1 notI)+
  apply (erule notE disjI2)+
  done

text ‹Introduction rule involving only ‹∃››
lemma ex_classical:
  assumes r: ‹¬ (∃x. P(x)) ⟹ P(a)›
  shows ‹∃x. P(x)›
  apply (rule classical)
  apply (rule exI, erule r)
  done

text ‹Version of above, simplifying ‹¬∃› to ‹∀¬›.›
lemma exCI:
  assumes r: ‹∀x. ¬ P(x) ⟹ P(a)›
  shows ‹∃x. P(x)›
  apply (rule ex_classical)
  apply (rule notI [THEN allI, THEN r])
  apply (erule notE)
  apply (erule exI)
  done

lemma excluded_middle: ‹¬ P ∨ P›
  apply (rule disjCI)
  apply assumption
  done

lemma case_split [case_names True False]:
  assumes r1: ‹P ⟹ Q›
    and r2: ‹¬ P ⟹ Q›
  shows ‹Q›
  apply (rule excluded_middle [THEN disjE])
  apply (erule r2)
  apply (erule r1)
  done

ML ‹
  fun case_tac ctxt a fixes =
    Rule_Insts.res_inst_tac ctxt [((("P", 0), Position.none), a)] fixes @{thm case_split};
›

method_setup case_tac = ‹
  Args.goal_spec -- Scan.lift (Args.embedded_inner_syntax -- Parse.for_fixes) >>
    (fn (quant, (s, fixes)) => fn ctxt => SIMPLE_METHOD'' quant (case_tac ctxt s fixes))
› "case_tac emulation (dynamic instantiation!)"


subsection ‹Special elimination rules›

text ‹Classical implies (‹⟶›) elimination.›
lemma impCE:
  assumes major: ‹P ⟶ Q›
    and r1: ‹¬ P ⟹ R›
    and r2: ‹Q ⟹ R›
  shows ‹R›
  apply (rule excluded_middle [THEN disjE])
   apply (erule r1)
  apply (rule r2)
  apply (erule major [THEN mp])
  done

text ‹
  This version of ‹⟶› elimination works on ‹Q› before ‹P›. It works best for those cases in which P holds ``almost everywhere''.
  Can't install as default: would break old proofs.
›
lemma impCE':
  assumes major: ‹P ⟶ Q›
    and r1: ‹Q ⟹ R›
    and r2: ‹¬ P ⟹ R›
  shows ‹R›
  apply (rule excluded_middle [THEN disjE])
   apply (erule r2)
  apply (rule r1)
  apply (erule major [THEN mp])
  done

text ‹Double negation law.›
lemma notnotD: ‹¬ ¬ P ⟹ P›
  apply (rule classical)
  apply (erule notE)
  apply assumption
  done

lemma contrapos2:  ‹⟦Q; ¬ P ⟹ ¬ Q⟧ ⟹ P›
  apply (rule classical)
  apply (drule (1) meta_mp)
  apply (erule (1) notE)
  done


subsubsection ‹Tactics for implication and contradiction›

text ‹
  Classical ‹⟷› elimination. Proof substitutes ‹P = Q› in
  ‹¬ P ⟹ ¬ Q› and ‹P ⟹ Q›.
›
lemma iffCE:
  assumes major: ‹P ⟷ Q›
    and r1: ‹⟦P; Q⟧ ⟹ R›
    and r2: ‹⟦¬ P; ¬ Q⟧ ⟹ R›
  shows ‹R›
  apply (rule major [unfolded iff_def, THEN conjE])
  apply (elim impCE)
     apply (erule (1) r2)
    apply (erule (1) notE)+
  apply (erule (1) r1)
  done


(*Better for fast_tac: needs no quantifier duplication!*)
lemma alt_ex1E:
  assumes major: ‹∃! x. P(x)›
    and r: ‹⋀x. ⟦P(x);  ∀y y'. P(y) ∧ P(y') ⟶ y = y'⟧ ⟹ R›
  shows ‹R›
  using major
proof (rule ex1E)
  fix x
  assume * : ‹∀y. P(y) ⟶ y = x›
  assume ‹P(x)›
  then show ‹R›
  proof (rule r)
    {
      fix y y'
      assume ‹P(y)› and ‹P(y')›
      with * have ‹x = y› and ‹x = y'›
        by - (tactic "IntPr.fast_tac \<^context> 1")+
      then have ‹y = y'› by (rule subst)
    } note r' = this
    show ‹∀y y'. P(y) ∧ P(y') ⟶ y = y'›
      by (intro strip, elim conjE) (rule r')
  qed
qed

lemma imp_elim: ‹P ⟶ Q ⟹ (¬ R ⟹ P) ⟹ (Q ⟹ R) ⟹ R›
  by (rule classical) iprover

lemma swap: ‹¬ P ⟹ (¬ R ⟹ P) ⟹ R›
  by (rule classical) iprover


section ‹Classical Reasoner›

ML ‹
structure Cla = Classical
(
  val imp_elim = @{thm imp_elim}
  val not_elim = @{thm notE}
  val swap = @{thm swap}
  val classical = @{thm classical}
  val sizef = size_of_thm
  val hyp_subst_tacs = [hyp_subst_tac]
);

structure Basic_Classical: BASIC_CLASSICAL = Cla;
open Basic_Classical;
›

(*Propositional rules*)
lemmas [intro!] = refl TrueI conjI disjCI impI notI iffI
  and [elim!] = conjE disjE impCE FalseE iffCE
ML ‹val prop_cs = claset_of \<^context>›

(*Quantifier rules*)
lemmas [intro!] = allI ex_ex1I
  and [intro] = exI
  and [elim!] = exE alt_ex1E
  and [elim] = allE
ML ‹val FOL_cs = claset_of \<^context>›

ML ‹
  structure Blast = Blast
  (
    structure Classical = Cla
    val Trueprop_const = dest_Const \<^const>‹Trueprop›
    val equality_name = \<^const_name>‹eq›
    val not_name = \<^const_name>‹Not›
    val notE = @{thm notE}
    val ccontr = @{thm ccontr}
    val hyp_subst_tac = Hypsubst.blast_hyp_subst_tac
  );
  val blast_tac = Blast.blast_tac;
›


lemma ex1_functional: ‹⟦∃! z. P(a,z); P(a,b); P(a,c)⟧ ⟹ b = c›
  by blast

text ‹Elimination of ‹True› from assumptions:›
lemma True_implies_equals: ‹(True ⟹ PROP P) ≡ PROP P›
proof
  assume ‹True ⟹ PROP P›
  from this and TrueI show ‹PROP P› .
next
  assume ‹PROP P›
  then show ‹PROP P› .
qed

lemma uncurry: ‹P ⟶ Q ⟶ R ⟹ P ∧ Q ⟶ R›
  by blast

lemma iff_allI: ‹(⋀x. P(x) ⟷ Q(x)) ⟹ (∀x. P(x)) ⟷ (∀x. Q(x))›
  by blast

lemma iff_exI: ‹(⋀x. P(x) ⟷ Q(x)) ⟹ (∃x. P(x)) ⟷ (∃x. Q(x))›
  by blast

lemma all_comm: ‹(∀x y. P(x,y)) ⟷ (∀y x. P(x,y))›
  by blast

lemma ex_comm: ‹(∃x y. P(x,y)) ⟷ (∃y x. P(x,y))›
  by blast



subsection ‹Classical simplification rules›

text ‹
  Avoids duplication of subgoals after ‹expand_if›, when the true and
  false cases boil down to the same thing.
›

lemma cases_simp: ‹(P ⟶ Q) ∧ (¬ P ⟶ Q) ⟷ Q›
  by blast


subsubsection ‹Miniscoping: pushing quantifiers in›

text ‹
  We do NOT distribute of ‹∀› over ‹∧›, or dually that of
  ‹∃› over ‹∨›.

  Baaz and Leitsch, On Skolemization and Proof Complexity (1994) show that
  this step can increase proof length!
›

text ‹Existential miniscoping.›
lemma int_ex_simps:
  ‹⋀P Q. (∃x. P(x) ∧ Q) ⟷ (∃x. P(x)) ∧ Q›
  ‹⋀P Q. (∃x. P ∧ Q(x)) ⟷ P ∧ (∃x. Q(x))›
  ‹⋀P Q. (∃x. P(x) ∨ Q) ⟷ (∃x. P(x)) ∨ Q›
  ‹⋀P Q. (∃x. P ∨ Q(x)) ⟷ P ∨ (∃x. Q(x))›
  by iprover+

text ‹Classical rules.›
lemma cla_ex_simps:
  ‹⋀P Q. (∃x. P(x) ⟶ Q) ⟷ (∀x. P(x)) ⟶ Q›
  ‹⋀P Q. (∃x. P ⟶ Q(x)) ⟷ P ⟶ (∃x. Q(x))›
  by blast+

lemmas ex_simps = int_ex_simps cla_ex_simps

text ‹Universal miniscoping.›
lemma int_all_simps:
  ‹⋀P Q. (∀x. P(x) ∧ Q) ⟷ (∀x. P(x)) ∧ Q›
  ‹⋀P Q. (∀x. P ∧ Q(x)) ⟷ P ∧ (∀x. Q(x))›
  ‹⋀P Q. (∀x. P(x) ⟶ Q) ⟷ (∃ x. P(x)) ⟶ Q›
  ‹⋀P Q. (∀x. P ⟶ Q(x)) ⟷ P ⟶ (∀x. Q(x))›
  by iprover+

text ‹Classical rules.›
lemma cla_all_simps:
  ‹⋀P Q. (∀x. P(x) ∨ Q) ⟷ (∀x. P(x)) ∨ Q›
  ‹⋀P Q. (∀x. P ∨ Q(x)) ⟷ P ∨ (∀x. Q(x))›
  by blast+

lemmas all_simps = int_all_simps cla_all_simps


subsubsection ‹Named rewrite rules proved for IFOL›

lemma imp_disj1: ‹(P ⟶ Q) ∨ R ⟷ (P ⟶ Q ∨ R)› by blast
lemma imp_disj2: ‹Q ∨ (P ⟶ R) ⟷ (P ⟶ Q ∨ R)› by blast

lemma de_Morgan_conj: ‹(¬ (P ∧ Q)) ⟷ (¬ P ∨ ¬ Q)› by blast

lemma not_imp: ‹¬ (P ⟶ Q) ⟷ (P ∧ ¬ Q)› by blast
lemma not_iff: ‹¬ (P ⟷ Q) ⟷ (P ⟷ ¬ Q)› by blast

lemma not_all: ‹(¬ (∀x. P(x))) ⟷ (∃x. ¬ P(x))› by blast
lemma imp_all: ‹((∀x. P(x)) ⟶ Q) ⟷ (∃x. P(x) ⟶ Q)› by blast


lemmas meta_simps =
  triv_forall_equality  ― ‹prunes params›
  True_implies_equals  ― ‹prune asms ‹True››

lemmas IFOL_simps =
  refl [THEN P_iff_T] conj_simps disj_simps not_simps
  imp_simps iff_simps quant_simps

lemma notFalseI: ‹¬ False› by iprover

lemma cla_simps_misc:
  ‹¬ (P ∧ Q) ⟷ ¬ P ∨ ¬ Q›
  ‹P ∨ ¬ P›
  ‹¬ P ∨ P›
  ‹¬ ¬ P ⟷ P›
  ‹(¬ P ⟶ P) ⟷ P›
  ‹(¬ P ⟷ ¬ Q) ⟷ (P ⟷ Q)› by blast+

lemmas cla_simps =
  de_Morgan_conj de_Morgan_disj imp_disj1 imp_disj2
  not_imp not_all not_ex cases_simp cla_simps_misc


ML_file ‹simpdata.ML›

simproc_setup defined_Ex (‹∃x. P(x)›) = ‹fn _ => Quantifier1.rearrange_ex›
simproc_setup defined_All (‹∀x. P(x)›) = ‹fn _ => Quantifier1.rearrange_all›

ML ‹
(*intuitionistic simprules only*)
val IFOL_ss =
  put_simpset FOL_basic_ss \<^context>
  addsimps @{thms meta_simps IFOL_simps int_ex_simps int_all_simps}
  addsimprocs [\<^simproc>‹defined_All›, \<^simproc>‹defined_Ex›]
  |> Simplifier.add_cong @{thm imp_cong}
  |> simpset_of;

(*classical simprules too*)
val FOL_ss =
  put_simpset IFOL_ss \<^context>
  addsimps @{thms cla_simps cla_ex_simps cla_all_simps}
  |> simpset_of;
›

setup ‹
  map_theory_simpset (put_simpset FOL_ss) #>
  Simplifier.method_setup Splitter.split_modifiers
›

ML_file ‹~~/src/Tools/eqsubst.ML›


subsection ‹Other simple lemmas›

lemma [simp]: ‹((P ⟶ R) ⟷ (Q ⟶ R)) ⟷ ((P ⟷ Q) ∨ R)›
  by blast

lemma [simp]: ‹((P ⟶ Q) ⟷ (P ⟶ R)) ⟷ (P ⟶ (Q ⟷ R))›
  by blast

lemma not_disj_iff_imp: ‹¬ P ∨ Q ⟷ (P ⟶ Q)›
  by blast


subsubsection ‹Monotonicity of implications›

lemma conj_mono: ‹⟦P1 ⟶ Q1; P2 ⟶ Q2⟧ ⟹ (P1 ∧ P2) ⟶ (Q1 ∧ Q2)›
  by fast (*or (IntPr.fast_tac 1)*)

lemma disj_mono: ‹⟦P1 ⟶ Q1; P2 ⟶ Q2⟧ ⟹ (P1 ∨ P2) ⟶ (Q1 ∨ Q2)›
  by fast (*or (IntPr.fast_tac 1)*)

lemma imp_mono: ‹⟦Q1 ⟶ P1; P2 ⟶ Q2⟧ ⟹ (P1 ⟶ P2) ⟶ (Q1 ⟶ Q2)›
  by fast (*or (IntPr.fast_tac 1)*)

lemma imp_refl: ‹P ⟶ P›
  by (rule impI)

text ‹The quantifier monotonicity rules are also intuitionistically valid.›
lemma ex_mono: ‹(⋀x. P(x) ⟶ Q(x)) ⟹ (∃x. P(x)) ⟶ (∃x. Q(x))›
  by blast

lemma all_mono: ‹(⋀x. P(x) ⟶ Q(x)) ⟹ (∀x. P(x)) ⟶ (∀x. Q(x))›
  by blast


subsection ‹Proof by cases and induction›

text ‹Proper handling of non-atomic rule statements.›

context
begin

qualified definition ‹induct_forall(P) ≡ ∀x. P(x)›
qualified definition ‹induct_implies(A, B) ≡ A ⟶ B›
qualified definition ‹induct_equal(x, y) ≡ x = y›
qualified definition ‹induct_conj(A, B) ≡ A ∧ B›

lemma induct_forall_eq: ‹(⋀x. P(x)) ≡ Trueprop(induct_forall(λx. P(x)))›
  unfolding atomize_all induct_forall_def .

lemma induct_implies_eq: ‹(A ⟹ B) ≡ Trueprop(induct_implies(A, B))›
  unfolding atomize_imp induct_implies_def .

lemma induct_equal_eq: ‹(x ≡ y) ≡ Trueprop(induct_equal(x, y))›
  unfolding atomize_eq induct_equal_def .

lemma induct_conj_eq: ‹(A &&& B) ≡ Trueprop(induct_conj(A, B))›
  unfolding atomize_conj induct_conj_def .

lemmas induct_atomize = induct_forall_eq induct_implies_eq induct_equal_eq induct_conj_eq
lemmas induct_rulify [symmetric] = induct_atomize
lemmas induct_rulify_fallback =
  induct_forall_def induct_implies_def induct_equal_def induct_conj_def

text ‹Method setup.›

ML_file ‹~~/src/Tools/induct.ML›
ML ‹
  structure Induct = Induct
  (
    val cases_default = @{thm case_split}
    val atomize = @{thms induct_atomize}
    val rulify = @{thms induct_rulify}
    val rulify_fallback = @{thms induct_rulify_fallback}
    val equal_def = @{thm induct_equal_def}
    fun dest_def _ = NONE
    fun trivial_tac _ _ = no_tac
  );
›

declare case_split [cases type: o]

end

ML_file ‹~~/src/Tools/case_product.ML›


hide_const (open) eq

end