1 (* Title: HOL/TPTP/TPTP_Proof_Reconstruction.thy
2 Author: Nik Sultana, Cambridge University Computer Laboratory
4 Proof reconstruction for Leo-II.
7 * Simple call the "reconstruct_leo2" function.
8 * For more advanced use, you could use the component functions used in
9 "reconstruct_leo2" -- see TPTP_Proof_Reconstruction_Test.thy for
12 This file contains definitions describing how to interpret LEO-II's
13 calculus in Isabelle/HOL, as well as more general proof-handling
14 functions. The definitions in this file serve to build an intermediate
15 proof script which is then evaluated into a tactic -- both these steps
16 are independent of LEO-II, and are defined in the TPTP_Reconstruct SML
20 The following attributes are mainly useful for debugging:
21 tptp_unexceptional_reconstruction |
22 unexceptional_reconstruction |-- when these are true, a low-level exception
23 is allowed to float to the top (instead of
24 triggering a higher-level exception, or
25 simply indicating that the reconstruction failed).
26 tptp_max_term_size --- fail of a term exceeds this size. "0" is taken
28 tptp_informative_failure |
29 informative_failure |-- produce more output during reconstruction.
30 tptp_trace_reconstruction |
32 There are also two attributes, independent of the code here, that
33 influence the success of reconstruction: blast_depth_limit and
34 unify_search_bound. These are documented in their respective modules,
35 but in summary, if unify_search_bound is increased then we can
36 handle larger terms (at the cost of performance), since the unification
37 engine takes longer to give up the search; blast_depth_limit is
38 a limit on proof search performed by Blast. Blast is used for
39 the limited proof search that needs to be done to interpret
40 instances of LEO-II's inference rules.
43 use RemoveRedundantQuantifications instead of the ad hoc use of
44 remove_redundant_quantification_in_lit and remove_redundant_quantification
47 theory TPTP_Proof_Reconstruction
48 imports TPTP_Parser TPTP_Interpret
49 (* keywords "import_leo2_proof" :: thy_decl *) (*FIXME currently unused*)
56 val tptp_unexceptional_reconstruction = Attrib.setup_config_bool @{binding tptp_unexceptional_reconstruction} (K false)
57 fun unexceptional_reconstruction ctxt = Config.get ctxt tptp_unexceptional_reconstruction
58 val tptp_informative_failure = Attrib.setup_config_bool @{binding tptp_informative_failure} (K false)
59 fun informative_failure ctxt = Config.get ctxt tptp_informative_failure
60 val tptp_trace_reconstruction = Attrib.setup_config_bool @{binding tptp_trace_reconstruction} (K false)
61 val tptp_max_term_size = Attrib.setup_config_int @{binding tptp_max_term_size} (K 0) (*0=infinity*)
63 fun exceeds_tptp_max_term_size ctxt size =
65 val max = Config.get ctxt tptp_max_term_size
72 (*FIXME move to TPTP_Proof_Reconstruction_Test_Units*)
74 tptp_unexceptional_reconstruction = false, (*NOTE should be "false" while testing*)
75 tptp_informative_failure = true
78 ML_file "TPTP_Parser/tptp_reconstruct_library.ML"
79 ML "open TPTP_Reconstruct_Library"
80 ML_file "TPTP_Parser/tptp_reconstruct.ML"
84 blast_depth_limit = 10,
85 unify_search_bound = 5
89 section "Proof reconstruction"
90 text {*There are two parts to proof reconstruction:
92 \item interpreting the inferences
93 \item building the skeleton, which indicates how to compose
94 individual inferences into subproofs, and then compose the
95 subproofs to give the proof).
98 One step detects unsound inferences, and the other step detects
99 unsound composition of inferences. The two parts can be weakly
100 coupled. They rely on a "proof index" which maps nodes to the
101 inference information. This information consists of the (usually
102 prover-specific) name of the inference step, and the Isabelle
103 formalisation of the inference as a term. The inference interpretation
104 then maps these terms into meta-theorems, and the skeleton is used to
105 compose the inference-level steps into a proof.
107 Leo2 operates on conjunctions of clauses. Each Leo2 inference has the
108 following form, where Cx are clauses:
114 Clauses consist of disjunctions of literals (shown as Px below), and might
115 have a prefix of !-bound variables, as shown below.
117 ! X... { P1 || ... || Pn}
119 Literals are usually assigned a polarity, but this isn't always the
120 case; you can come across inferences looking like this (where A is an
121 object-level formula):
127 The symbol "||" represents literal-level disjunction and "&&" is
128 clause-level conjunction. Rules will typically lift formula-level
129 conjunctions; for instance the following rule lifts object-level
132 { (A | B) = true || ... } && ...
133 --------------------------------------
134 { A = true || B = true || ... } && ...
137 Using this setup, efficiency might be gained by only interpreting
138 inferences once, merging identical inference steps, and merging
139 identical subproofs into single inferences thus avoiding some effort.
140 We can also attempt to minimising proof search when interpreting
143 It is hoped that this setup can target other provers by modifying the
144 clause representation to fit them, and adapting the inference
145 interpretation to handle the rules used by the prover. It should also
146 facilitate composing together proofs found by different provers.
150 subsection "Instantiation"
152 lemma polar_allE [rule_format]:
153 "\<lbrakk>(\<forall>x. P x) = True; (P x) = True \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R"
154 "\<lbrakk>(\<exists>x. P x) = False; (P x) = False \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R"
157 lemma polar_exE [rule_format]:
158 "\<lbrakk>(\<exists>x. P x) = True; \<And>x. (P x) = True \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R"
159 "\<lbrakk>(\<forall>x. P x) = False; \<And>x. (P x) = False \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R"
163 (*This carries out an allE-like rule but on (polarised) literals.
164 Instead of yielding a free variable (which is a hell for the
165 matcher) it seeks to use one of the subgoals' parameters.
166 This ought to be sufficient for emulating extcnf_combined,
167 but note that the complexity of the problem can be enormous.*)
168 fun inst_parametermatch_tac ctxt thms i = fn st =>
180 |> strip_top_all_vars []
182 |> map fst (*just get the parameter names*)
184 if null parameters then no_tac st
187 fun instantiate param =
188 (map (eres_inst_tac ctxt [(("x", 0), param)]) thms
190 val attempts = map instantiate parameters
192 (fold (curry (op APPEND')) attempts (K no_tac)) i st
196 (*Attempts to use the polar_allE theorems on a specific subgoal.*)
197 fun forall_pos_tac ctxt = inst_parametermatch_tac ctxt @{thms polar_allE}
201 (*This is similar to inst_parametermatch_tac, but prefers to
202 match variables having identical names. Logically, this is
203 a hack. But it reduces the complexity of the problem.*)
204 fun nominal_inst_parametermatch_tac ctxt thm i = fn st =>
216 |> strip_top_all_vars []
218 |> map fst (*just get the parameter names*)
220 if null parameters then no_tac st
223 fun instantiates param =
224 eres_inst_tac ctxt [(("x", 0), param)] thm
226 val quantified_var = head_quantified_variable ctxt i st
228 if is_none quantified_var then no_tac st
230 if member (op =) parameters (the quantified_var |> fst) then
231 instantiates (the quantified_var |> fst) i st
239 subsection "Prefix massaging"
244 (*Get quantifier prefix of the hypothesis and conclusion, reorder
245 the hypothesis' quantifiers to have the ones appearing in the
247 fun canonicalise_qtfr_order ctxt i = fn st =>
254 if null gls then raise NO_GOALS
257 val (params, (hyp_clause, conc_clause)) =
260 |> strip_top_all_vars []
261 |> apsnd Logic.dest_implies
263 val (hyp_quants, hyp_body) =
264 HOLogic.dest_Trueprop hyp_clause
265 |> strip_top_All_vars
269 HOLogic.dest_Trueprop conc_clause
270 |> strip_top_All_vars
274 (* fold absfree new_hyp_prefix hyp_body *)
276 fold_rev (fn (v, ty) => fn t => HOLogic.mk_all (v, ty, t))
277 (prefix_intersection_list
278 hyp_quants conc_quants)
280 |> HOLogic.mk_Trueprop
282 val thm = Goal.prove ctxt [] []
283 (Logic.mk_implies (hyp_clause, new_hyp))
285 (REPEAT_DETERM (HEADGOAL (rtac @{thm allI})))
288 (nominal_inst_parametermatch_tac ctxt @{thm allE})))
297 subsection "Some general rules and congruences"
299 (*this isn't an actual rule used in Leo2, but it seems to be
300 applied implicitly during some Leo2 inferences.*)
301 lemma polarise: "P ==> P = True" by auto
305 (TPTP_Reconstruct.remove_polarity true t; true)
306 handle TPTP_Reconstruct.UNPOLARISED _ => false
308 val polarise_subgoal_hyps =
309 COND' (SOME #> TERMPRED is_polarised (fn _ => true)) (K no_tac) (dtac @{thm polarise})
312 lemma simp_meta [rule_format]:
313 "(A --> B) == (~A | B)"
314 "(A | B) | C == A | B | C"
315 "(A & B) & C == A & B & C"
317 (* "(A & B) == (~ (~A | ~B))" *)
318 "~ (A & B) == (~A | ~B)"
319 "~(A | B) == (~A) & (~B)"
323 subsection "Emulation of Leo2's inference rules"
325 (*this is not included in simp_meta since it would make a mess of the polarities*)
326 lemma expand_iff [rule_format]:
327 "((A :: bool) = B) \<equiv> (~ A | B) & (~ B | A)"
328 by (rule eq_reflection, auto)
330 lemma polarity_switch [rule_format]:
331 "(\<not> P) = True \<Longrightarrow> P = False"
332 "(\<not> P) = False \<Longrightarrow> P = True"
333 "P = False \<Longrightarrow> (\<not> P) = True"
334 "P = True \<Longrightarrow> (\<not> P) = False"
337 lemma solved_all_splits: "False = True \<Longrightarrow> False" by simp
339 val solved_all_splits_tac =
340 TRY (etac @{thm conjE} 1)
341 THEN rtac @{thm solved_all_splits} 1
345 lemma lots_of_logic_expansions_meta [rule_format]:
346 "(((A :: bool) = B) = True) == (((A \<longrightarrow> B) = True) & ((B \<longrightarrow> A) = True))"
347 "((A :: bool) = B) = False == (((~A) | B) = False) | (((~B) | A) = False)"
349 "((F = G) = True) == (! x. (F x = G x)) = True"
350 "((F = G) = False) == (! x. (F x = G x)) = False"
352 "(A | B) = True == (A = True) | (B = True)"
353 "(A & B) = False == (A = False) | (B = False)"
354 "(A | B) = False == (A = False) & (B = False)"
355 "(A & B) = True == (A = True) & (B = True)"
356 "(~ A) = True == A = False"
357 "(~ A) = False == A = True"
358 "~ (A = True) == A = False"
359 "~ (A = False) == A = True"
360 by (rule eq_reflection, auto)+
362 (*this is used in extcnf_combined handler*)
363 lemma eq_neg_bool: "((A :: bool) = B) = False ==> ((~ (A | B)) | ~ ((~ A) | (~ B))) = False"
367 "((A :: bool) = B) = True ==> ((~ (A | B)) | ~ (~ A | ~ B)) = True"
368 "(A = B) = True \<Longrightarrow> A = True \<or> B = False"
369 "(A = B) = True \<Longrightarrow> A = False \<or> B = True"
372 (*next formula is more versatile than
373 "(F = G) = True \<Longrightarrow> \<forall>x. ((F x = G x) = True)"
374 since it doesn't assume that clause is singleton. After splitqtfr,
375 and after applying allI exhaustively to the conclusion, we can
376 use the existing functions to find the "(F x = G x) = True"
377 disjunct in the conclusion*)
378 lemma eq_pos_func: "\<And> x. (F = G) = True \<Longrightarrow> (F x = G x) = True"
381 (*make sure the conclusion consists of just "False"*)
383 "((A = True) ==> False) ==> A = False"
384 "((A = False) ==> False) ==> A = True"
387 (*FIXME try to use Drule.equal_elim_rule1 directly for this*)
388 lemma equal_elim_rule1: "(A \<equiv> B) \<Longrightarrow> A \<Longrightarrow> B" by auto
390 lots_of_logic_expansions_meta[THEN equal_elim_rule1]
392 (*FIXME is there any overlap with lots_of_logic_expansions_meta or leo2_rules?*)
393 lemma extuni_bool2 [rule_format]: "(A = B) = False \<Longrightarrow> (A = True) | (B = True)" by auto
394 lemma extuni_bool1 [rule_format]: "(A = B) = False \<Longrightarrow> (A = False) | (B = False)" by auto
395 lemma extuni_triv [rule_format]: "(A = A) = False \<Longrightarrow> R" by auto
397 (*Order (of A, B, C, D) matters*)
398 lemma dec_commut_eq [rule_format]:
399 "((A = B) = (C = D)) = False \<Longrightarrow> (B = C) = False | (A = D) = False"
400 "((A = B) = (C = D)) = False \<Longrightarrow> (B = D) = False | (A = C) = False"
402 lemma dec_commut_disj [rule_format]:
403 "((A \<or> B) = (C \<or> D)) = False \<Longrightarrow> (B = C) = False \<or> (A = D) = False"
406 lemma extuni_func [rule_format]: "(F = G) = False \<Longrightarrow> (! X. (F X = G X)) = False" by auto
409 subsection "Emulation: tactics"
412 (*Instantiate a variable according to the info given in the
413 proof annotation. Through this we avoid having to come up
414 with instantiations during reconstruction.*)
415 fun bind_tac ctxt prob_name ordered_binds =
417 val thy = Proof_Context.theory_of ctxt
418 fun term_to_string t =
419 Print_Mode.with_modes [""]
420 (fn () => Output.output (Syntax.string_of_term ctxt t)) ()
421 val ordered_instances =
422 TPTP_Reconstruct.interpret_bindings prob_name thy ordered_binds []
423 |> map (snd #> term_to_string)
426 (*instantiate a list of variables, order matters*)
427 fun instantiate_vars ctxt vars : tactic =
429 Rule_Insts.eres_inst_tac ctxt
430 [(("x", 0), var)] @{thm allE} 1)
434 fun instantiate_tac vars =
435 instantiate_vars ctxt vars
438 HEADGOAL (canonicalise_qtfr_order ctxt)
439 THEN (REPEAT_DETERM (HEADGOAL (rtac @{thm allI})))
440 THEN REPEAT_DETERM (HEADGOAL (nominal_inst_parametermatch_tac ctxt @{thm allE}))
441 (*now only the variable to instantiate should be left*)
442 THEN FIRST (map instantiate_tac ordered_instances)
447 (*Simplification tactics*)
449 fun rew_goal_tac thms ctxt i =
450 rewrite_goal_tac ctxt thms i
453 val expander_animal =
454 rew_goal_tac (@{thms simp_meta} @ @{thms lots_of_logic_expansions_meta})
457 rew_goal_tac @{thms simp_meta}
461 lemma prop_normalise [rule_format]:
462 "(A | B) | C == A | B | C"
463 "(A & B) & C == A & B & C"
464 "A | B == ~(~A & ~B)"
468 (*i.e., break_conclusion*)
469 fun flip_conclusion_tac ctxt =
472 (TRY o CHANGED o (rewrite_goal_tac ctxt @{thms prop_normalise}))
473 THEN' rtac @{thm notI}
474 THEN' (REPEAT_DETERM o etac @{thm conjE})
475 THEN' (TRY o (expander_animal ctxt))
477 default_tac ORELSE' resolve_tac ctxt @{thms flip}
482 subsection "Skolemisation"
484 lemma skolemise [rule_format]:
485 "\<forall> P. (~ (! x. P x)) \<longrightarrow> ~ (P (SOME x. ~ P x))"
487 have "\<And> P. (~ (! x. P x)) \<Longrightarrow> ~ (P (SOME x. ~ P x))"
490 assume ption: "~ (! x. P x)"
491 hence a: "? x. ~ P x" by force
493 have hilbert : "\<And> P. (? x. P x) \<Longrightarrow> (P (SOME x. P x))"
497 thus "(P (SOME x. P x))"
504 from a show "~ P (SOME x. ~ P x)"
507 hence "\<not> P (SOME x. \<not> P x)" by (rule hilbert)
511 thus ?thesis by blast
514 lemma polar_skolemise [rule_format]:
515 "\<forall> P. (! x. P x) = False \<longrightarrow> (P (SOME x. ~ P x)) = False"
517 have "\<And> P. (! x. P x) = False \<Longrightarrow> (P (SOME x. ~ P x)) = False"
520 assume ption: "(! x. P x) = False"
521 hence "\<not> (\<forall> x. P x)" by force
522 hence "\<not> All P" by force
523 hence "\<not> (P (SOME x. \<not> P x))" by (rule skolemise)
524 thus "(P (SOME x. \<not> P x)) = False" by force
526 thus ?thesis by blast
529 lemma leo2_skolemise [rule_format]:
530 "\<forall> P sk. (! x. P x) = False \<longrightarrow> (sk = (SOME x. ~ P x)) \<longrightarrow> (P sk) = False"
531 by (clarify, rule polar_skolemise)
533 lemma lift_forall [rule_format]:
534 "!! x. (! x. A x) = True ==> (A x) = True"
535 "!! x. (? x. A x) = False ==> (A x) = False"
537 lemma lift_exists [rule_format]:
538 "\<lbrakk>(All P) = False; sk = (SOME x. \<not> P x)\<rbrakk> \<Longrightarrow> P sk = False"
539 "\<lbrakk>(Ex P) = True; sk = (SOME x. P x)\<rbrakk> \<Longrightarrow> P sk = True"
540 apply (drule polar_skolemise, simp)
541 apply (simp, drule someI_ex, simp)
545 (*FIXME LHS should be constant. Currently allow variables for testing. Probably should still allow Vars (but not Frees) since they'll act as intermediate values*)
546 fun conc_is_skolem_def t =
548 Const (@{const_name HOL.eq}, _) $ t' $ (Const (@{const_name Hilbert_Choice.Eps}, _) $ _) =>
552 |> apfst (strip_abs #> snd #> strip_comb #> fst)
553 val get_const_name = dest_Const #> fst
558 andalso (get_const_name h <> get_const_name @{term HOL.Ex})
559 andalso (get_const_name h <> get_const_name @{term HOL.All})
560 andalso (h <> @{term Hilbert_Choice.Eps})
561 andalso (h <> @{term HOL.conj})
562 andalso (h <> @{term HOL.disj})
563 andalso (h <> @{term HOL.eq})
564 andalso (h <> @{term HOL.implies})
565 andalso (h <> @{term HOL.The})
566 andalso (h <> @{term HOL.Ex1})
567 andalso (h <> @{term HOL.Not})
568 andalso (h <> @{term HOL.iff})
569 andalso (h <> @{term HOL.not_equal}))
571 fold (fn t => fn b =>
572 b andalso is_Free t) args true
574 h_property andalso args_property
580 (*Hack used to detect if a Skolem definition, with an LHS Var, has had the LHS instantiated into an unacceptable term.*)
581 fun conc_is_bad_skolem_def t =
583 Const (@{const_name HOL.eq}, _) $ t' $ (Const (@{const_name Hilbert_Choice.Eps}, _) $ _) =>
585 val (h, args) = strip_comb t'
586 val get_const_name = dest_Const #> fst
589 (get_const_name h = get_const_name @{term HOL.Ex})
590 orelse (get_const_name h = get_const_name @{term HOL.All})
591 orelse (h = @{term Hilbert_Choice.Eps})
592 orelse (h = @{term HOL.conj})
593 orelse (h = @{term HOL.disj})
594 orelse (h = @{term HOL.eq})
595 orelse (h = @{term HOL.implies})
596 orelse (h = @{term HOL.The})
597 orelse (h = @{term HOL.Ex1})
598 orelse (h = @{term HOL.Not})
599 orelse (h = @{term HOL.iff})
600 orelse (h = @{term HOL.not_equal})
603 not (is_Free h) andalso
604 not (is_Var h) andalso
607 fold (fn t => fn b =>
608 b andalso is_Free t) args true
610 h_property andalso args_property
616 fun get_skolem_conc t =
619 strip_top_all_vars [] t
624 Const (@{const_name HOL.eq}, _) $ t' $ (Const (@{const_name Hilbert_Choice.Eps}, _) $ _) => SOME t'
628 fun get_skolem_conc_const t =
639 Technique for handling quantifiers:
641 * allE should always match with a !!
642 * exE should match with a constant,
643 or bind a fresh !! -- currently not doing the latter since it never seems to arised in normal Leo2 proofs.
647 fun forall_neg_tac candidate_consts ctxt i = fn st =>
649 val thy = Proof_Context.theory_of ctxt
661 |> strip_top_all_vars []
663 |> map fst (*just get the parameter names*)
672 if null gls orelse null candidate_consts then no_tac st
675 fun instantiate const_name =
676 dres_inst_tac ctxt [(("sk", 0), const_name ^ parameters)] @{thm leo2_skolemise}
677 val attempts = map instantiate candidate_consts
679 (fold (curry (op APPEND')) attempts (K no_tac)) i st
685 exception SKOLEM_DEF of term (*The tactic wasn't pointed at a skolem definition*)
686 exception NO_SKOLEM_DEF of (*skolem const name*)string * Binding.binding * term (*The tactic could not find a skolem definition in the theory*)
687 fun absorb_skolem_def ctxt prob_name_opt i = fn st =>
689 val thy = Proof_Context.theory_of ctxt
698 (*this should never be thrown*)
703 |> strip_top_all_vars []
708 fun skolem_const_info_of t =
710 Const (@{const_name HOL.Trueprop}, _) $ (Const (@{const_name HOL.eq}, _) $ t' $ (Const (@{const_name Hilbert_Choice.Eps}, _) $ _)) =>
712 |> strip_abs_body (*since in general might have a skolem term, so we want to rip out the prefixing lambdas to get to the constant (which should be at head position)*)
715 | _ => raise SKOLEM_DEF t
718 skolem_const_info_of conclusion
721 val def_name = const_name ^ "_def"
723 val bnd_def = (*FIXME consts*)
725 |> Long_Name.implode o tl o Long_Name.explode (*FIXME hack to drop theory-name prefix*)
726 |> Binding.qualified_name
727 |> Binding.suffix_name "_def"
730 case prob_name_opt of
733 (* Binding.qualify false
734 (TPTP_Problem_Name.mangle_problem_name prob_name)
739 if Name_Space.defined_entry (Theory.axiom_space thy) def_name then
740 Thm.axiom thy def_name
742 if is_none prob_name_opt then
743 (*This mode is for testing, so we can be a bit
744 looser with theories*)
745 Thm.add_axiom_global (bnd_name, conclusion) thy
748 raise (NO_SKOLEM_DEF (def_name, bnd_name, conclusion))
750 rtac (Drule.export_without_context thm) i st
752 handle SKOLEM_DEF _ => no_tac st
757 In current system, there should only be 2 subgoals: the one where
758 the skolem definition is being built (with a Var in the LHS), and the other subgoal using Var.
760 (*arity must be greater than 0. if arity=0 then
761 there's no need to use this expensive matching.*)
762 fun find_skolem_term ctxt consts_candidate arity = fn st =>
764 val _ = @{assert} (arity > 0)
771 (*extract the conclusion of each subgoal*)
776 map (strip_top_all_vars [] #> snd #> Logic.strip_horn #> snd) gls
777 (*Remove skolem-definition conclusion, to avoid wasting time analysing it*)
778 |> filter (try_dest_Trueprop #> conc_is_skolem_def #> not)
779 (*There should only be a single goal*) (*FIXME this might not always be the case, in practice*)
780 (* |> tap (fn x => @{assert} (is_some (try the_single x))) *)
782 (*look for subterms headed by a skolem constant, and whose
783 arguments are all parameter Vars*)
784 fun get_skolem_terms args (acc : term list) t =
786 (c as Const _) $ (v as Free _) =>
787 if c = consts_candidate andalso
788 arity = length args + 1 then
789 (list_comb (c, v :: args)) :: acc
791 | t1 $ (v as Free _) =>
792 get_skolem_terms (v :: args) acc t1 @
793 get_skolem_terms [] acc t1
795 get_skolem_terms [] acc t1 @
796 get_skolem_terms [] acc t2
797 | Abs (_, _, t') => get_skolem_terms [] acc t'
800 map (strip_top_All_vars #> snd) conclusions
801 |> maps (get_skolem_terms [] [])
807 fun instantiate_skols ctxt consts_candidates i = fn st =>
809 val thy = Proof_Context.theory_of ctxt
816 val (params, conclusion) =
822 |> strip_top_all_vars []
823 |> apsnd (Logic.strip_horn #> snd)
825 fun skolem_const_info_of t =
827 Const (@{const_name HOL.Trueprop}, _) $ (Const (@{const_name HOL.eq}, _) $ lhs $ (Const (@{const_name Hilbert_Choice.Eps}, _) $ rhs)) =>
829 (*the parameters we will concern ourselves with*)
831 Term.add_frees lhs []
833 (*check to make sure that params' <= params*)
834 val _ = @{assert} (forall (member (op =) params) params')
835 val skolem_const_ty =
837 val (skolem_const_prety, no_params) =
839 |> apfst (dest_Var #> snd) (*head of lhs consists of a logical variable. we just want its type.*)
842 val _ = @{assert} (length params = no_params)
844 (*get value type of a function type after n arguments have been supplied*)
845 fun get_val_ty n ty =
847 else get_val_ty (n - 1) (dest_funT ty |> snd)
849 get_val_ty no_params skolem_const_prety
853 (skolem_const_ty, params')
855 | _ => raise (SKOLEM_DEF t)
858 find skolem const candidates which, after applying distinct members of params' we end up with, give us something of type skolem_const_ty.
860 given a candidate's type, skolem_const_ty, and params', we get some pemutations of params' (i.e. the order in which they can be given to the candidate in order to get skolem_const_ty). If the list of permutations is empty, then we cannot use that candidate.
863 only returns a single matching -- since terms are linear, and variable arguments are Vars, order shouldn't matter, so we can ignore permutations.
864 doesn't work with polymorphism (for which we'd need to use type unification) -- this is OK since no terms should be polymorphic, since Leo2 proofs aren't.
866 fun use_candidate target_ty params acc cur_ty =
868 if cur_ty = target_ty then
873 val (arg_ty, val_ty) = Term.dest_funT cur_ty
874 (*now find a param of type arg_ty*)
875 val (candidate_param, params') =
876 find_and_remove (snd #> pair arg_ty #> op =) params
878 use_candidate target_ty params' (candidate_param :: acc) val_ty
880 handle TYPE ("dest_funT", _, _) => NONE
883 val (skolem_const_ty, params') = skolem_const_info_of conclusion
886 For each candidate, build a term and pass it to Thm.instantiate, whic in turn is chained with PRIMITIVE to give us this_tactic.
889 we run the following:
890 drule leo2_skolemise THEN' this_tactic
892 NOTE: remember to APPEND' instead of ORELSE' the two tactics relating to skolemisation
895 val filtered_candidates =
898 #> use_candidate skolem_const_ty params' [])
899 consts_candidates (* prefiltered_candidates *)
900 |> pair consts_candidates (* prefiltered_candidates *)
902 |> filter (snd #> is_none #> not)
907 fun make_result_t (t, args) =
908 (* list_comb (t, map Free args) *)
909 if length args > 0 then
910 hd (find_skolem_term ctxt t (length args) st)
913 map make_result_t filtered_candidates
916 (*prefix a skolem term with bindings for the parameters*)
917 (* val contextualise = fold absdummy (map snd params) *)
918 val contextualise = fold absfree params
920 val skolem_cts = map (contextualise #> Thm.global_cterm_of thy) skolem_terms
923 (*now the instantiation code*)
925 (*there should only be one Var -- that is from the previous application of drule leo2_skolemise. We look for it at the head position in some equation at a conclusion of a subgoal.*)
931 (strip_top_all_vars [] #> snd #>
932 Logic.strip_horn #> snd #>
934 |> switch (fold (fn x => fn l => if is_some x then the x :: l else l)) []
935 |> maps (switch Term.add_vars [])
937 fun make_var pre_var =
940 |> Thm.global_cterm_of thy
943 if null pre_var then NONE
944 else make_var pre_var
947 fun instantiate_tac from to =
948 Thm.instantiate ([], [(from, to)])
952 if is_none var_opt then no_tac
954 fold (curry (op APPEND)) (map (instantiate_tac (the var_opt)) skolem_cts) no_tac
962 fun new_skolem_tac ctxt consts_candidates =
966 THEN' instantiate_skols ctxt consts_candidates
968 if null consts_candidates then K no_tac
969 else FIRST' (map tec @{thms lift_exists})
974 need a tactic to expand "? x . P" to "~ ! x. ~ P"
977 fun ex_expander_tac ctxt i =
980 empty_simpset ctxt (*NOTE for some reason, Bind exception gets raised if ctxt's simpset isn't emptied*)
981 |> Simplifier.add_simp @{lemma "Ex P == (~ (! x. ~ P x))" by auto}
983 CHANGED (asm_full_simp_tac simpset i)
988 subsubsection "extuni_dec"
991 (*n-ary decomposition. Code is based on the n-ary arg_cong generator*)
992 fun extuni_dec_n ctxt arity =
994 val _ = @{assert} (arity > 0)
998 val arg_tys = map (fn i => TFree ("arg" ^ i ^ "_ty", @{sort type})) is
999 val res_ty = TFree ("res" ^ "_ty", @{sort type})
1000 val f_ty = arg_tys ---> res_ty
1001 val f = Free ("f", f_ty)
1002 val xs = map (fn i =>
1003 Free ("x" ^ i, TFree ("arg" ^ i ^ "_ty", @{sort type}))) is
1004 (*FIXME DRY principle*)
1005 val ys = map (fn i =>
1006 Free ("y" ^ i, TFree ("arg" ^ i ^ "_ty", @{sort type}))) is
1008 val hyp_lhs = list_comb (f, xs)
1009 val hyp_rhs = list_comb (f, ys)
1011 HOLogic.eq_const res_ty $ hyp_lhs $ hyp_rhs
1013 HOLogic.eq_const HOLogic.boolT $ hyp_eq $ @{term False}
1014 |> HOLogic.mk_Trueprop
1017 val ty = TFree ("arg" ^ i ^ "_ty", @{sort type})
1018 val x = Free ("x" ^ i, ty)
1019 val y = Free ("y" ^ i, ty)
1020 val eq = HOLogic.eq_const ty $ x $ y
1022 HOLogic.eq_const HOLogic.boolT $ eq $ @{term False}
1025 val conc_disjs = map conc_eq is
1028 if length conc_disjs = 1 then
1029 the_single conc_disjs
1032 (fn t => fn t_conc => HOLogic.mk_disj (t_conc, t))
1033 (tl conc_disjs) (hd conc_disjs)
1036 Logic.mk_implies (hyp, HOLogic.mk_Trueprop conc)
1038 Goal.prove ctxt [] [] t (fn _ => auto_tac ctxt)
1039 |> Drule.export_without_context
1044 (*Determine the arity of a function which the "dec"
1045 unification rule is about to be applied.
1047 * Assumes that there is a single hypothesis
1049 fun find_dec_arity i = fn st =>
1056 if null gls then raise NO_GOALS
1059 val (params, (literal, conc_clause)) =
1062 |> strip_top_all_vars []
1063 |> apsnd Logic.strip_horn
1064 |> apsnd (apfst the_single)
1067 HOLogic.dest_Trueprop
1068 #> strip_top_All_vars
1070 #> HOLogic.dest_eq (*polarity's "="*)
1072 #> HOLogic.dest_eq (*the unification constraint's "="*)
1080 val (_, res_ty) = dest_funT ty
1085 handle (TYPE ("dest_funT", _, _)) => 0
1088 arity_of (get_ty literal)
1092 (*given an inference, it returns the parameters (i.e., we've already matched the leading & shared quantification in the hypothesis & conclusion clauses), and the "raw" inference*)
1093 fun breakdown_inference i = fn st =>
1100 if null gls then raise NO_GOALS
1104 |> strip_top_all_vars []
1107 (*build a custom elimination rule for extuni_dec, and instantiate it to match a specific subgoal*)
1108 fun extuni_dec_elim_rule ctxt arity i = fn st =>
1110 val rule = extuni_dec_n ctxt arity
1114 |> Logic.dest_implies
1115 |> fst (*assuming that rule has single hypothesis*)
1117 (*having run break_hypothesis earlier, we know that the hypothesis
1118 now consists of a single literal. We can (and should)
1119 disregard the conclusion, since it hasn't been "broken",
1120 and it might include some unwanted literals -- the latter
1121 could cause "diff" to fail (since they won't agree with the
1122 rule we have generated.*)
1125 snd (breakdown_inference i st)
1126 |> Logic.dest_implies
1127 |> fst (*assuming that inference has single hypothesis,
1128 as explained above.*)
1130 TPTP_Reconstruct_Library.diff_and_instantiate ctxt rule rule_hyp inference_hyp
1133 fun extuni_dec_tac ctxt i = fn st =>
1135 val arity = find_dec_arity i st
1140 extuni_dec_elim_rule ctxt arity i st
1141 (*in case we itroduced free variables during
1142 instantiation, we generalise the rule to make
1143 those free variables into logical variables.*)
1144 |> Thm.forall_intr_frees
1145 |> Drule.export_without_context
1146 in dtac rule i st end
1147 handle NO_GOALS => no_tac st
1150 (*batter fails if there's no toplevel disjunction in the
1151 hypothesis, so we also try atac*)
1152 SOLVE o (tac THEN' (batter_tac ctxt ORELSE' assume_tac ctxt))
1155 (rtac @{thm disjI1} APPEND' rtac @{thm disjI2})
1156 (FIRST' (map closure
1157 [dresolve_tac ctxt @{thms dec_commut_eq},
1158 dtac @{thm dec_commut_disj},
1161 (CHANGED o search_tac) i st
1166 subsubsection "standard_cnf"
1167 (*Given a standard_cnf inference, normalise it
1168 e.g. ((A & B) & C \<longrightarrow> D & E \<longrightarrow> F \<longrightarrow> G) = False
1170 (A & B & C & D & E & F \<longrightarrow> G) = False
1171 then custom-build a metatheorem which validates this:
1172 (A & B & C & D & E & F \<longrightarrow> G) = False
1173 -------------------------------------------
1174 (A = True) & (B = True) & (C = True) &
1175 (D = True) & (E = True) & (F = True) & (G = False)
1176 and apply this metatheorem.
1178 There aren't any "positive" standard_cnfs in Leo2's calculus:
1179 e.g., "(A \<longrightarrow> B) = True \<Longrightarrow> A = False | (A = True & B = True)"
1180 since "standard_cnf" seems to be applied at the preprocessing
1181 stage, together with splitting.
1185 (*Conjunctive counterparts to Term.disjuncts_aux and Term.disjuncts*)
1186 fun conjuncts_aux (Const (@{const_name HOL.conj}, _) $ t $ t') conjs =
1187 conjuncts_aux t (conjuncts_aux t' conjs)
1188 | conjuncts_aux t conjs = t :: conjs
1190 fun conjuncts t = conjuncts_aux t []
1192 (*HOL equivalent of Logic.strip_horn*)
1194 fun imp_strip_horn' acc (Const (@{const_name HOL.implies}, _) $ A $ B) =
1195 imp_strip_horn' (A :: acc) B
1196 | imp_strip_horn' acc t = (acc, t)
1198 fun imp_strip_horn t =
1199 imp_strip_horn' [] t
1205 (*Returns whether the antecedents are separated by conjunctions
1206 or implications; the number of antecedents; and the polarity
1207 of the original clause -- I think this will always be "false".*)
1208 fun standard_cnf_type ctxt i : thm -> (TPTP_Reconstruct.formula_kind * int * bool) option = fn st =>
1216 if null gls then raise NO_GOALS
1220 |> TPTP_Reconstruct.strip_top_all_vars []
1225 (*hypothesis clause should be singleton*)
1226 val _ = @{assert} (length hypos = 1)
1228 val (t, pol) = the_single hypos
1229 |> try_dest_Trueprop
1230 |> TPTP_Reconstruct.strip_top_All_vars
1232 |> TPTP_Reconstruct.remove_polarity true
1234 (*literal is negative*)
1235 val _ = @{assert} (not pol)
1237 val (antes, conc) = imp_strip_horn t
1239 val (ante_type, antes') =
1240 if length antes = 1 then
1242 val conjunctive_antes =
1246 if length conjunctive_antes > 1 then
1247 (TPTP_Reconstruct.Conjunctive NONE,
1250 (TPTP_Reconstruct.Implicational NONE,
1254 (TPTP_Reconstruct.Implicational NONE,
1257 if null antes then NONE
1258 else SOME (ante_type, length antes', pol)
1263 (*Given a certain standard_cnf type, build a metatheorem that would
1265 fun mk_standard_cnf ctxt kind arity =
1267 val _ = @{assert} (arity > 0)
1270 |> map (fn i => Free ("x" ^ Int.toString i, HOLogic.boolT))
1272 val consequent = hd vars
1273 val antecedents = tl vars
1277 (curry HOLogic.mk_conj)
1278 (map (fn var => HOLogic.mk_eq (var, @{term True})) antecedents)
1279 (HOLogic.mk_eq (consequent, @{term False}))
1283 TPTP_Reconstruct.Conjunctive NONE =>
1284 curry HOLogic.mk_imp
1285 (if length antecedents = 1 then the_single antecedents
1287 fold (curry HOLogic.mk_conj) (tl antecedents) (hd antecedents))
1289 | TPTP_Reconstruct.Implicational NONE =>
1290 fold (curry HOLogic.mk_imp) antecedents consequent
1292 val hyp = HOLogic.mk_eq (pre_hyp, @{term False})
1295 Logic.mk_implies (HOLogic.mk_Trueprop hyp, HOLogic.mk_Trueprop conc)
1297 Goal.prove ctxt [] [] t (fn _ => HEADGOAL (blast_tac ctxt))
1298 |> Drule.export_without_context
1303 (*Applies a d-tactic, then breaks it up conjunctively.
1304 This can be used to transform subgoals as follows:
1305 (A \<longrightarrow> B) = False \<Longrightarrow> R
1308 \<lbrakk>A = True; B = False\<rbrakk> \<Longrightarrow> R
1310 fun weak_conj_tac drule =
1311 dtac drule THEN' (REPEAT_DETERM o etac @{thm conjE})
1315 val uncurry_lit_neg_tac =
1316 dtac @{lemma "(A \<longrightarrow> B \<longrightarrow> C) = False \<Longrightarrow> (A & B \<longrightarrow> C) = False" by auto}
1321 fun standard_cnf_tac ctxt i = fn st =>
1323 fun core_tactic i = fn st =>
1324 case standard_cnf_type ctxt i st of
1326 | SOME (kind, arity, _) =>
1328 val rule = mk_standard_cnf ctxt kind arity;
1330 (weak_conj_tac rule THEN' atac) i st
1333 (uncurry_lit_neg_tac
1334 THEN' TPTP_Reconstruct_Library.reassociate_conjs_tac ctxt
1335 THEN' core_tactic) i st
1340 subsubsection "Emulator prep"
1343 datatype cleanup_feature =
1344 RemoveHypothesesFromSkolemDefs
1347 datatype loop_feature =
1352 | Donkey_Cong (*simper_animal + ex_expander_tac*)
1353 | RemoveRedundantQuantifications
1356 (*Closely based on Leo2 calculus*)
1374 | Forall_special_pos
1380 | Loop of loop_feature list
1381 | LoopOnce of loop_feature list
1382 | InnerLoopOnce of loop_feature list
1383 | CleanUp of cleanup_feature list
1388 fun can_feature x l =
1390 fun sublist_of_clean_up el =
1392 CleanUp l'' => SOME l''
1394 fun sublist_of_loop el =
1396 Loop l'' => SOME l''
1398 fun sublist_of_loop_once el =
1400 LoopOnce l'' => SOME l''
1402 fun sublist_of_inner_loop_once el =
1404 InnerLoopOnce l'' => SOME l''
1407 fun check_sublist sought_sublist opt_list =
1408 if forall is_none opt_list then false
1410 fold_options opt_list
1412 |> pair sought_sublist
1417 map sublist_of_clean_up l
1420 map sublist_of_loop l
1423 map sublist_of_loop_once l
1425 | InnerLoopOnce l' =>
1426 map sublist_of_inner_loop_once l
1428 | _ => exists (curry (op =) x) l
1431 fun loop_can_feature loop_feats l =
1432 can_feature (Loop loop_feats) l orelse
1433 can_feature (LoopOnce loop_feats) l orelse
1434 can_feature (InnerLoopOnce loop_feats) l;
1436 @{assert} (can_feature ConstsDiff [StripQuantifiers, ConstsDiff]);
1439 (can_feature (CleanUp [RemoveHypothesesFromSkolemDefs])
1440 [CleanUp [RemoveHypothesesFromSkolemDefs, RemoveDuplicates]]);
1443 (can_feature (Loop []) [Loop [Existential_Var]]);
1446 (not (can_feature (Loop []) [InnerLoopOnce [Existential_Var]]));
1450 exception NO_LOOP_FEATS
1451 fun get_loop_feats (feats : feature list) =
1454 fold (fn x => fn loop_feats_acc =>
1455 if is_some loop_feats_acc then loop_feats_acc
1458 Loop loop_feats => SOME loop_feats
1459 | LoopOnce loop_feats => SOME loop_feats
1460 | InnerLoopOnce loop_feats => SOME loop_feats
1465 if is_some loop_find then the loop_find
1466 else raise NO_LOOP_FEATS
1470 (get_loop_feats [Loop [King_Cong, Break_Hypotheses, Existential_Free, Existential_Var, Universal]] =
1471 [King_Cong, Break_Hypotheses, Existential_Free, Existential_Var, Universal])
1474 (*use as elim rule to remove premises*)
1475 lemma insa_prems: "\<lbrakk>Q; P\<rbrakk> \<Longrightarrow> P" by auto
1477 fun cleanup_skolem_defs feats =
1479 (*remove hypotheses from skolem defs,
1480 after testing that they look like skolem defs*)
1481 val dehypothesise_skolem_defs =
1482 COND' (SOME #> TERMPRED (fn _ => true) conc_is_skolem_def)
1483 (REPEAT_DETERM o etac @{thm insa_prems})
1486 if can_feature (CleanUp [RemoveHypothesesFromSkolemDefs]) feats then
1487 ALLGOALS (TRY o dehypothesise_skolem_defs)
1493 fun remove_duplicates_tac feats =
1494 (if can_feature (CleanUp [RemoveDuplicates]) feats then
1495 ALLGOALS distinct_subgoal_tac
1500 (*given a goal state, indicates the skolem constants committed-to in it (i.e. appearing in LHS of a skolem definition)*)
1501 val which_skolem_concs_used = fn st =>
1503 val feats = [CleanUp [RemoveHypothesesFromSkolemDefs, RemoveDuplicates]]
1505 cleanup_skolem_defs feats
1506 THEN remove_duplicates_tac feats
1510 |> tap (fn (_, rest) => @{assert} (null (Seq.list_of rest)))
1512 |> TERMFUN (snd (*discard hypotheses*)
1513 #> get_skolem_conc_const) NONE
1514 |> switch (fold (fn x => fn l => if is_some x then the x :: l else l)) []
1520 fun exists_tac ctxt feats consts_diff =
1523 if loop_can_feature [Existential_Var] feats andalso consts_diff <> [] then
1524 new_skolem_tac ctxt consts_diff
1525 (*We're making sure that each skolem constant is used once in instantiations.*)
1529 if loop_can_feature [Existential_Free] feats andalso consts_diff = [] then
1530 eresolve_tac ctxt @{thms polar_exE}
1533 ex_var APPEND' ex_free
1536 fun forall_tac ctxt feats =
1537 if loop_can_feature [Universal] feats then
1543 subsubsection "Finite types"
1544 (*lift quantification from a singleton literal to a singleton clause*)
1545 lemma forall_pos_lift:
1546 "\<lbrakk>(! X. P X) = True; ! X. (P X = True) \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R" by auto
1548 (*predicate over the type of the leading quantified variable*)
1551 val extcnf_forall_special_pos_tac =
1557 ["% _ . True", "% _ . False", "% x . x", "Not"]
1561 eres_inst_tac @{context} [(("x", 0), t_s)] @{thm allE}
1564 (TRY o etac @{thm forall_pos_lift})
1567 (*FIXME could check the type of the leading quantified variable, instead of trying everything*)
1568 (tecs (bool @ bool_to_bool)))
1573 subsubsection "Emulator"
1575 lemma efq: "[|A = True; A = False|] ==> R" by auto
1578 (etac @{thm efq} THEN' atac)
1583 (*This is applied to all subgoals, repeatedly*)
1584 fun extcnf_combined_main ctxt feats consts_diff =
1586 (*This is applied to subgoals which don't have a conclusion
1587 consisting of a Skolem definition*)
1588 fun extcnf_combined_tac' ctxt i = fn st =>
1590 val skolem_consts_used_so_far = which_skolem_concs_used st
1591 val consts_diff' = subtract (op =) skolem_consts_used_so_far consts_diff
1593 fun feat_to_tac feat =
1595 Close_Branch => trace_tac' ctxt "mark: closer" efq_tac
1596 | ConjI => trace_tac' ctxt "mark: conjI" (rtac @{thm conjI})
1597 | King_Cong => trace_tac' ctxt "mark: expander_animal" (expander_animal ctxt)
1598 | Break_Hypotheses => trace_tac' ctxt "mark: break_hypotheses" (break_hypotheses_tac ctxt)
1599 | RemoveRedundantQuantifications => K all_tac
1601 FIXME Building this into the loop instead.. maybe not the ideal choice
1602 | RemoveRedundantQuantifications =>
1603 trace_tac' ctxt "mark: strip_unused_variable_hyp"
1604 (REPEAT_DETERM o remove_redundant_quantification_in_lit)
1607 | Assumption => atac
1608 (*FIXME both Existential_Free and Existential_Var run same code*)
1609 | Existential_Free => trace_tac' ctxt "mark: forall_neg" (exists_tac ctxt feats consts_diff')
1610 | Existential_Var => trace_tac' ctxt "mark: forall_neg" (exists_tac ctxt feats consts_diff')
1611 | Universal => trace_tac' ctxt "mark: forall_pos" (forall_tac ctxt feats)
1612 | Not_pos => trace_tac' ctxt "mark: not_pos" (dtac @{thm leo2_rules(9)})
1613 | Not_neg => trace_tac' ctxt "mark: not_neg" (dtac @{thm leo2_rules(10)})
1614 | Or_pos => trace_tac' ctxt "mark: or_pos" (dtac @{thm leo2_rules(5)}) (*could add (6) for negated conjunction*)
1615 | Or_neg => trace_tac' ctxt "mark: or_neg" (dtac @{thm leo2_rules(7)})
1616 | Equal_pos => trace_tac' ctxt "mark: equal_pos" (dresolve_tac ctxt (@{thms eq_pos_bool} @ [@{thm leo2_rules(3)}, @{thm eq_pos_func}]))
1617 | Equal_neg => trace_tac' ctxt "mark: equal_neg" (dresolve_tac ctxt [@{thm eq_neg_bool}, @{thm leo2_rules(4)}])
1618 | Donkey_Cong => trace_tac' ctxt "mark: donkey_cong" (simper_animal ctxt THEN' ex_expander_tac ctxt)
1620 | Extuni_Bool2 => trace_tac' ctxt "mark: extuni_bool2" (dtac @{thm extuni_bool2})
1621 | Extuni_Bool1 => trace_tac' ctxt "mark: extuni_bool1" (dtac @{thm extuni_bool1})
1622 | Extuni_Bind => trace_tac' ctxt "mark: extuni_triv" (etac @{thm extuni_triv})
1623 | Extuni_Triv => trace_tac' ctxt "mark: extuni_triv" (etac @{thm extuni_triv})
1624 | Extuni_Dec => trace_tac' ctxt "mark: extuni_dec_tac" (extuni_dec_tac ctxt)
1625 | Extuni_FlexRigid => trace_tac' ctxt "mark: extuni_flex_rigid" (atac ORELSE' asm_full_simp_tac ctxt)
1626 | Extuni_Func => trace_tac' ctxt "mark: extuni_func" (dtac @{thm extuni_func})
1627 | Polarity_switch => trace_tac' ctxt "mark: polarity_switch" (eresolve_tac ctxt @{thms polarity_switch})
1628 | Forall_special_pos => trace_tac' ctxt "mark: dorall_special_pos" extcnf_forall_special_pos_tac
1631 get_loop_feats feats
1638 (*This is applied to all subgoals, repeatedly*)
1639 fun extcnf_combined_tac ctxt i =
1640 COND (TERMPRED (fn _ => true) conc_is_skolem_def (SOME i))
1642 (extcnf_combined_tac' ctxt i)
1644 val core_tac = CHANGED (ALLGOALS (IF_UNSOLVED o TRY o extcnf_combined_tac ctxt))
1646 val full_tac = REPEAT core_tac
1650 (if can_feature (InnerLoopOnce []) feats then
1655 val interpreted_consts =
1656 [@{const_name HOL.All}, @{const_name HOL.Ex},
1657 @{const_name Hilbert_Choice.Eps},
1658 @{const_name HOL.conj},
1659 @{const_name HOL.disj},
1660 @{const_name HOL.eq},
1661 @{const_name HOL.implies},
1662 @{const_name HOL.The},
1663 @{const_name HOL.Ex1},
1664 @{const_name HOL.Not},
1665 (* @{const_name HOL.iff}, *) (*FIXME do these exist?*)
1666 (* @{const_name HOL.not_equal}, *)
1667 @{const_name HOL.False},
1668 @{const_name HOL.True},
1669 @{const_name Pure.imp}]
1671 fun strip_qtfrs_tac ctxt =
1672 REPEAT_DETERM (HEADGOAL (rtac @{thm allI}))
1673 THEN REPEAT_DETERM (HEADGOAL (etac @{thm exE}))
1674 THEN HEADGOAL (canonicalise_qtfr_order ctxt)
1676 ((REPEAT (HEADGOAL (nominal_inst_parametermatch_tac ctxt @{thm allE})))
1677 APPEND (REPEAT (HEADGOAL (inst_parametermatch_tac ctxt [@{thm allE}]))))
1678 (*FIXME need to handle "@{thm exI}"?*)
1680 (*difference in constants between the hypothesis clause and the conclusion clause*)
1681 fun clause_consts_diff thm =
1685 |> Logic.dest_implies
1688 (*This bit should not be needed, since Leo2 inferences don't have parameters*)
1689 |> TPTP_Reconstruct.strip_top_all_vars []
1694 #> uncurry TPTP_Reconstruct.new_consts_between
1697 not (member (op =) interpreted_consts n))
1699 if head_of t = Logic.implies then do_diff t
1705 (*remove quantification in hypothesis clause (! X. t), if
1707 fun remove_redundant_quantification ctxt i = fn st =>
1714 if null gls then raise NO_GOALS
1717 val (params, (hyp_clauses, conc_clause)) =
1720 |> TPTP_Reconstruct.strip_top_all_vars []
1721 |> apsnd Logic.strip_horn
1723 (*this is to fail gracefully in case this tactic is applied to a goal which doesn't have a single hypothesis*)
1724 if length hyp_clauses > 1 then no_tac st
1727 val hyp_clause = the_single hyp_clauses
1729 HOLogic.dest_Trueprop
1730 #> TPTP_Reconstruct.strip_top_All_vars
1732 val (hyp_prefix, hyp_body) = sep_prefix hyp_clause
1733 val (conc_prefix, conc_body) = sep_prefix conc_clause
1735 if null hyp_prefix orelse
1736 member (op =) conc_prefix (hd hyp_prefix) orelse
1737 member (op =) (Term.add_frees hyp_body []) (hd hyp_prefix) then
1740 eres_inst_tac ctxt [(("x", 0), "(@X. False)")] @{thm allE} i st
1747 fun remove_redundant_quantification_ignore_skolems ctxt i =
1748 COND (TERMPRED (fn _ => true) conc_is_skolem_def (SOME i))
1750 (remove_redundant_quantification ctxt i)
1753 lemma drop_redundant_literal_qtfr:
1754 "(! X. P) = True \<Longrightarrow> P = True"
1755 "(? X. P) = True \<Longrightarrow> P = True"
1756 "(! X. P) = False \<Longrightarrow> P = False"
1757 "(? X. P) = False \<Longrightarrow> P = False"
1761 (*remove quantification in the literal "(! X. t) = True/False"
1762 in the singleton hypothesis clause, if X not free in t*)
1763 fun remove_redundant_quantification_in_lit ctxt i = fn st =>
1770 if null gls then raise NO_GOALS
1773 val (params, (hyp_clauses, conc_clause)) =
1776 |> TPTP_Reconstruct.strip_top_all_vars []
1777 |> apsnd Logic.strip_horn
1779 (*this is to fail gracefully in case this tactic is applied to a goal which doesn't have a single hypothesis*)
1780 if length hyp_clauses > 1 then no_tac st
1783 fun literal_content (Const (@{const_name HOL.eq}, _) $ lhs $ (rhs as @{term True})) = SOME (lhs, rhs)
1784 | literal_content (Const (@{const_name HOL.eq}, _) $ lhs $ (rhs as @{term False})) = SOME (lhs, rhs)
1785 | literal_content t = NONE
1788 the_single hyp_clauses
1789 |> HOLogic.dest_Trueprop
1793 if is_none hyp_clause then
1797 val (hyp_lit_prefix, hyp_lit_body) =
1799 |> (fn (t, polarity) =>
1800 TPTP_Reconstruct.strip_top_All_vars t
1803 if null hyp_lit_prefix orelse
1804 member (op =) (Term.add_frees hyp_lit_body []) (hd hyp_lit_prefix) then
1807 dresolve_tac ctxt @{thms drop_redundant_literal_qtfr} i st
1815 fun remove_redundant_quantification_in_lit_ignore_skolems ctxt i =
1816 COND (TERMPRED (fn _ => true) conc_is_skolem_def (SOME i))
1818 (remove_redundant_quantification_in_lit ctxt i)
1822 fun extcnf_combined_tac ctxt prob_name_opt feats skolem_consts = fn st =>
1824 val thy = Proof_Context.theory_of ctxt
1826 (*Initially, st consists of a single goal, showing the
1827 hypothesis clause implying the conclusion clause.
1828 There are no parameters.*)
1830 union (op =) skolem_consts
1831 (if can_feature ConstsDiff feats then
1832 clause_consts_diff st
1836 if can_feature (LoopOnce []) feats orelse can_feature (InnerLoopOnce []) feats then
1837 extcnf_combined_main ctxt feats consts_diff
1838 else if can_feature (Loop []) feats then
1839 BEST_FIRST (TERMPRED (fn _ => true) conc_is_skolem_def NONE, size_of_thm)
1840 (*FIXME maybe need to weaken predicate to include "solved form"?*)
1841 (extcnf_combined_main ctxt feats consts_diff)
1842 else all_tac (*to allow us to use the cleaning features*)
1844 (*Remove hypotheses from Skolem definitions,
1845 then remove duplicate subgoals,
1846 then we should be left with skolem definitions:
1847 absorb them as axioms into the theory.*)
1849 cleanup_skolem_defs feats
1850 THEN remove_duplicates_tac feats
1851 THEN (if can_feature AbsorbSkolemDefs feats then
1852 ALLGOALS (absorb_skolem_def ctxt prob_name_opt)
1855 val have_loop_feats =
1856 (get_loop_feats feats; true)
1857 handle NO_LOOP_FEATS => false
1860 (if can_feature StripQuantifiers feats then
1861 (REPEAT (CHANGED (strip_qtfrs_tac ctxt)))
1863 THEN (if can_feature Flip_Conclusion feats then
1864 HEADGOAL (flip_conclusion_tac ctxt)
1867 (*after stripping the quantifiers any remaining quantifiers
1868 can be simply eliminated -- they're redundant*)
1869 (*FIXME instead of just using allE, instantiate to a silly
1870 term, to remove opportunities for unification.*)
1871 THEN (REPEAT_DETERM (etac @{thm allE} 1))
1873 THEN (REPEAT_DETERM (rtac @{thm allI} 1))
1875 THEN (if have_loop_feats then
1877 ((ALLGOALS (TRY o clause_breaker_tac ctxt)) (*brush away literals which don't change*)
1879 (*FIXME move this to a different level?*)
1880 (if loop_can_feature [Polarity_switch] feats then
1883 (TRY (IF_UNSOLVED (HEADGOAL (remove_redundant_quantification_ignore_skolems ctxt))))
1884 THEN (TRY (IF_UNSOLVED (HEADGOAL (remove_redundant_quantification_in_lit_ignore_skolems ctxt)))))
1885 THEN (TRY main_tac)))
1888 THEN IF_UNSOLVED cleanup
1891 DEPTH_SOLVE (CHANGED tec) st
1896 subsubsection "unfold_def"
1898 (*this is used when handling unfold_tac, because the skeleton includes the definitions conjoined with the goal. it turns out that, for my tactic, the definitions are harmful. instead of modifying the skeleton (which may be nontrivial) i'm just dropping the information using this lemma. obviously, and from the name, order matters here.*)
1899 lemma drop_first_hypothesis [rule_format]: "\<lbrakk>A; B\<rbrakk> \<Longrightarrow> B" by auto
1901 (*Unfold_def works by reducing the goal to a meta equation,
1902 then working on it until it can be discharged by atac,
1903 or reflexive, or else turned back into an object equation
1904 and broken down further.*)
1905 lemma un_meta_polarise: "(X \<equiv> True) \<Longrightarrow> X" by auto
1906 lemma meta_polarise: "X \<Longrightarrow> X \<equiv> True" by auto
1909 fun unfold_def_tac ctxt depends_on_defs = fn st =>
1911 (*This is used when we end up with something like
1912 (A & B) \<equiv> True \<Longrightarrow> (B & A) \<equiv> True.
1913 It breaks down this subgoal until it can be trivially
1916 val kill_meta_eqs_tac =
1917 dtac @{thm un_meta_polarise}
1918 THEN' rtac @{thm meta_polarise}
1919 THEN' (REPEAT_DETERM o (etac @{thm conjE}))
1920 THEN' (REPEAT_DETERM o (rtac @{thm conjI} ORELSE' atac))
1922 val continue_reducing_tac =
1923 rtac @{thm meta_eq_to_obj_eq} 1
1924 THEN (REPEAT_DETERM (ex_expander_tac ctxt 1))
1925 THEN TRY (polarise_subgoal_hyps 1) (*no need to REPEAT_DETERM here, since there should only be one hypothesis*)
1926 THEN TRY (dtac @{thm eq_reflection} 1)
1927 THEN (TRY ((CHANGED o rewrite_goal_tac ctxt
1928 (@{thm expand_iff} :: @{thms simp_meta})) 1))
1929 THEN HEADGOAL (rtac @{thm reflexive}
1931 ORELSE' kill_meta_eqs_tac)
1934 (rtac @{thm polarise} 1 THEN atac 1)
1936 (REPEAT_DETERM (etac @{thm conjE} 1 THEN etac @{thm drop_first_hypothesis} 1)
1937 THEN PRIMITIVE (Conv.fconv_rule Drule.eta_long_conversion)
1938 THEN (REPEAT_DETERM (ex_expander_tac ctxt 1))
1939 THEN (TRY ((CHANGED o rewrite_goal_tac ctxt @{thms simp_meta}) 1))
1940 THEN PRIMITIVE (Conv.fconv_rule Drule.eta_long_conversion)
1944 (unfold_tac ctxt depends_on_defs
1945 THEN IF_UNSOLVED continue_reducing_tac)))
1952 subsection "Handling split 'preprocessing'"
1955 "! x. P x & Q x \<equiv> (! x. P x) & (! x. Q x)"
1956 "~ (~ A) \<equiv> A"
1958 "(A & B) & C \<equiv> A & B & C"
1959 "A = B \<equiv> (A --> B) & (B --> A)"
1960 by (rule eq_reflection, auto)+
1962 (*Same idiom as ex_expander_tac*)
1964 fun split_simp_tac (ctxt : Proof.context) i =
1967 fold Simplifier.add_simp @{thms split_tranfs} (empty_simpset ctxt)
1969 CHANGED (asm_full_simp_tac simpset i)
1974 subsection "Alternative reconstruction tactics"
1976 (*An "auto"-based proof reconstruction, where we attempt to reconstruct each inference
1977 using auto_tac. A realistic tactic would inspect the inference name and act
1979 fun auto_based_reconstruction_tac ctxt prob_name n =
1981 val thy = Proof_Context.theory_of ctxt
1982 val pannot = TPTP_Reconstruct.get_pannot_of_prob thy prob_name
1984 TPTP_Reconstruct.inference_at_node
1986 prob_name (#meta pannot) n
1988 |> (fn {inference_fmla, ...} =>
1989 Goal.prove ctxt [] [] inference_fmla
1990 (fn pdata => auto_tac (#context pdata)))
1994 (*An oracle-based reconstruction, which is only used to test the shunting part of the system*)
1995 oracle oracle_iinterp = "fn t => t"
1997 fun oracle_based_reconstruction_tac ctxt prob_name n =
1999 val thy = Proof_Context.theory_of ctxt
2000 val pannot = TPTP_Reconstruct.get_pannot_of_prob thy prob_name
2002 TPTP_Reconstruct.inference_at_node
2004 prob_name (#meta pannot) n
2006 |> (fn {inference_fmla, ...} => Thm.global_cterm_of thy inference_fmla)
2012 subsection "Leo2 reconstruction tactic"
2015 exception UNSUPPORTED_ROLE
2016 exception INTERPRET_INFERENCE
2018 (*Failure reports can be adjusted to avoid interrupting
2019 an overall reconstruction process*)
2021 if unexceptional_reconstruction ctxt then
2022 (warning x; raise INTERPRET_INFERENCE)
2025 fun interpret_leo2_inference_tac ctxt prob_name node =
2027 val thy = Proof_Context.theory_of ctxt
2030 if Config.get ctxt tptp_trace_reconstruction then
2031 tracing ("interpret_inference reconstructing node" ^ node ^ " of " ^ TPTP_Problem_Name.mangle_problem_name prob_name)
2034 val pannot = TPTP_Reconstruct.get_pannot_of_prob thy prob_name
2036 fun nonfull_extcnf_combined_tac feats =
2037 extcnf_combined_tac ctxt (SOME prob_name)
2040 InnerLoopOnce (Break_Hypotheses :: (*FIXME RemoveRedundantQuantifications :: *) feats),
2044 val source_inf_opt =
2045 AList.lookup (op =) (#meta pannot)
2049 (*FIXME integrate this with other lookup code, or in the early analysis*)
2051 fun node_is_of_role role node =
2052 AList.lookup (op =) (#meta pannot) node |> the
2054 |> (fn role' => role = role')
2056 fun roled_dependencies_names role =
2060 TPTP_Syntax.Role_Definition =>
2061 map (apsnd Binding.dest) (#defs pannot)
2062 | TPTP_Syntax.Role_Axiom =>
2063 map (apsnd Binding.dest) (#axs pannot)
2064 | _ => raise UNSUPPORTED_ROLE
2066 if is_none (source_inf_opt node) then []
2068 case the (source_inf_opt node) of
2069 TPTP_Proof.Inference (_, _, parent_inf) =>
2070 map TPTP_Proof.parent_name parent_inf
2071 |> filter (node_is_of_role role)
2072 |> (*FIXME currently definitions are not
2073 included in the proof annotations, so
2074 i'm using all the definitions available
2075 in the proof. ideally i should only
2076 use the ones in the proof annotation.*)
2078 if role = TPTP_Syntax.Role_Definition then
2079 let fun values () = map (apsnd Binding.dest) (#defs pannot)
2084 map (fn node => AList.lookup (op =) (values ()) node |> the) x)
2088 val roled_dependencies =
2089 roled_dependencies_names
2090 #> map (#3 #> Global_Theory.get_thm thy)
2092 val depends_on_defs = roled_dependencies TPTP_Syntax.Role_Definition
2093 val depends_on_axs = roled_dependencies TPTP_Syntax.Role_Axiom
2094 val depends_on_defs_names = roled_dependencies_names TPTP_Syntax.Role_Definition
2097 fun get_binds source_inf_opt =
2098 case the source_inf_opt of
2099 TPTP_Proof.Inference (_, _, parent_inf) =>
2101 (fn TPTP_Proof.Parent _ => []
2102 | TPTP_Proof.ParentWithDetails (_, parent_details) => parent_details)
2106 val inference_name =
2107 case TPTP_Reconstruct.inference_at_node thy prob_name (#meta pannot) node of
2108 NONE => fail ctxt "Cannot reconstruct rule: no information"
2109 | SOME {inference_name, ...} => inference_name
2110 val default_tac = HEADGOAL (blast_tac ctxt)
2112 case inference_name of
2114 HEADGOAL (Metis_Tactic.metis_tac [] ATP_Problem_Generate.combs_or_liftingN ctxt [])
2115 (*NOTE To treat E as an oracle use the following line:
2116 HEADGOAL (etac (oracle_based_reconstruction_tac ctxt prob_name node))
2122 (rtac @{thm polarise}
2124 | "polarity_switch" => nonfull_extcnf_combined_tac [Polarity_switch]
2125 | "solved_all_splits" => solved_all_splits_tac
2126 | "extcnf_not_pos" => nonfull_extcnf_combined_tac [Not_pos]
2127 | "extcnf_forall_pos" => nonfull_extcnf_combined_tac [Universal]
2128 | "negate_conjecture" => fail ctxt "Should not handle negate_conjecture here"
2129 | "unfold_def" => unfold_def_tac ctxt depends_on_defs
2130 | "extcnf_not_neg" => nonfull_extcnf_combined_tac [Not_neg]
2131 | "extcnf_or_neg" => nonfull_extcnf_combined_tac [Or_neg]
2132 | "extcnf_equal_pos" => nonfull_extcnf_combined_tac [Equal_pos]
2133 | "extcnf_equal_neg" => nonfull_extcnf_combined_tac [Equal_neg]
2134 | "extcnf_forall_special_pos" =>
2135 nonfull_extcnf_combined_tac [Forall_special_pos]
2136 ORELSE HEADGOAL (blast_tac ctxt)
2137 | "extcnf_or_pos" => nonfull_extcnf_combined_tac [Or_pos]
2138 | "extuni_bool2" => nonfull_extcnf_combined_tac [Extuni_Bool2]
2139 | "extuni_bool1" => nonfull_extcnf_combined_tac [Extuni_Bool1]
2142 ORELSE nonfull_extcnf_combined_tac [Extuni_Dec]
2143 | "extuni_bind" => nonfull_extcnf_combined_tac [Extuni_Bind]
2144 | "extuni_triv" => nonfull_extcnf_combined_tac [Extuni_Triv]
2145 | "extuni_flex_rigid" => nonfull_extcnf_combined_tac [Extuni_FlexRigid]
2146 | "prim_subst" => nonfull_extcnf_combined_tac [Assumption]
2149 val ordered_binds = get_binds (source_inf_opt node)
2151 bind_tac ctxt prob_name ordered_binds
2153 | "standard_cnf" => HEADGOAL (standard_cnf_tac ctxt)
2154 | "extcnf_forall_neg" =>
2155 nonfull_extcnf_combined_tac
2156 [Existential_Var(* , RemoveRedundantQuantifications *)] (*FIXME RemoveRedundantQuantifications*)
2158 nonfull_extcnf_combined_tac [Extuni_Func, Existential_Var]
2159 | "replace_leibnizEQ" => nonfull_extcnf_combined_tac [Assumption]
2160 | "replace_andrewsEQ" => nonfull_extcnf_combined_tac [Assumption]
2161 | "split_preprocessing" =>
2162 (REPEAT (HEADGOAL (split_simp_tac ctxt)))
2163 THEN TRY (PRIMITIVE (Conv.fconv_rule Drule.eta_long_conversion))
2166 (*FIXME some of these could eventually be handled specially*)
2167 | "fac_restr" => default_tac
2168 | "sim" => default_tac
2169 | "res" => default_tac
2170 | "rename" => default_tac
2171 | "flexflex" => default_tac
2172 | other => fail ctxt ("Unknown inference rule: " ^ other)
2177 fun interpret_leo2_inference ctxt prob_name node =
2179 val thy = Proof_Context.theory_of ctxt
2180 val pannot = TPTP_Reconstruct.get_pannot_of_prob thy prob_name
2182 val (inference_name, inference_fmla) =
2183 case TPTP_Reconstruct.inference_at_node thy prob_name (#meta pannot) node of
2184 NONE => fail ctxt "Cannot reconstruct rule: no information"
2185 | SOME {inference_name, inference_fmla, ...} =>
2186 (inference_name, inference_fmla)
2191 Goal.prove ctxt [] [] inference_fmla
2192 (fn pdata => interpret_leo2_inference_tac
2193 (#context pdata) prob_name node)
2195 if informative_failure ctxt then SOME (prove ())
2199 in case proof_outcome of
2200 NONE => fail ctxt (Pretty.string_of
2202 [Pretty.str ("Failed inference reconstruction for '" ^
2203 inference_name ^ "' at node " ^ node ^ ":\n"),
2204 Syntax.pretty_term ctxt inference_fmla]))
2210 (*filter a set of nodes based on which inference rule was used to
2212 fun nodes_by_inference (fms : TPTP_Reconstruct.formula_meaning list) inference_rule =
2215 case TPTP_Reconstruct.node_info fms #source_inf_opt n of
2217 | SOME (TPTP_Proof.File _) => l
2218 | SOME (TPTP_Proof.Inference (rule_name, _, _)) =>
2219 if rule_name = inference_rule then n :: l
2222 fold fold_fun (map fst fms) []
2227 section "Importing proofs and reconstructing theorems"
2230 (*Preprocessing carried out on a LEO-II proof.*)
2231 fun leo2_on_load (pannot : TPTP_Reconstruct.proof_annotation) thy =
2233 val ctxt = Proof_Context.init_global thy
2234 val dud = ("", Binding.empty, @{term False})
2235 val pre_skolem_defs =
2236 nodes_by_inference (#meta pannot) "extcnf_forall_neg" @
2237 nodes_by_inference (#meta pannot) "extuni_func"
2239 (interpret_leo2_inference ctxt (#problem_name pannot) x; dud)
2240 handle NO_SKOLEM_DEF (s, bnd, t) => (s, bnd, t))
2241 |> filter (fn (x, _, _) => x <> "") (*In case no skolem constants were introduced in that inference*)
2242 val skolem_defs = map (fn (x, y, _) => (x, y)) pre_skolem_defs
2244 fold (fn skolem_def => fn thy =>
2246 val ((s, thm), thy') = Thm.add_axiom_global skolem_def thy
2247 (* val _ = warning ("Added skolem definition " ^ s ^ ": " ^ @{make_string thm}) *) (*FIXME use of make_string*)
2249 (map (fn (_, y, z) => (y, z)) pre_skolem_defs)
2252 ({problem_name = #problem_name pannot,
2253 skolem_defs = skolem_defs,
2254 defs = #defs pannot,
2256 meta = #meta pannot},
2262 (*Imports and reconstructs a LEO-II proof.*)
2263 fun reconstruct_leo2 path thy =
2265 val prob_file = Path.base path
2266 val dir = Path.dir path
2267 val thy' = TPTP_Reconstruct.import_thm true [dir, prob_file] path leo2_on_load thy
2272 Path.implode prob_file
2273 |> TPTP_Problem_Name.parse_problem_name
2275 TPTP_Reconstruct.reconstruct ctxt
2276 (TPTP_Reconstruct.naive_reconstruct_tac ctxt interpret_leo2_inference)
2279 (*NOTE we could return the theorem value alone, since
2280 users could get the thy value from the thm value.*)