author | wenzelm |
Wed, 12 Mar 2025 11:39:00 +0100 | |
changeset 82265 | 4b875a4c83b0 |
parent 80910 | 406a85a25189 |
permissions | -rw-r--r-- |
55596 | 1 |
(* Title: HOL/TPTP/TPTP_Proof_Reconstruction.thy |
2 |
Author: Nik Sultana, Cambridge University Computer Laboratory |
|
3 |
||
4 |
Proof reconstruction for Leo-II. |
|
5 |
||
55597
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
6 |
USAGE: |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
7 |
* Simple call the "reconstruct_leo2" function. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
8 |
* For more advanced use, you could use the component functions used in |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
9 |
"reconstruct_leo2" -- see TPTP_Proof_Reconstruction_Test.thy for |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
10 |
examples of this. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
11 |
|
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
12 |
This file contains definitions describing how to interpret LEO-II's |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
13 |
calculus in Isabelle/HOL, as well as more general proof-handling |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
14 |
functions. The definitions in this file serve to build an intermediate |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
15 |
proof script which is then evaluated into a tactic -- both these steps |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
16 |
are independent of LEO-II, and are defined in the TPTP_Reconstruct SML |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
17 |
module. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
18 |
|
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
19 |
CONFIG: |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
20 |
The following attributes are mainly useful for debugging: |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
21 |
tptp_unexceptional_reconstruction | |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
22 |
unexceptional_reconstruction |-- when these are true, a low-level exception |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
23 |
is allowed to float to the top (instead of |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
24 |
triggering a higher-level exception, or |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
25 |
simply indicating that the reconstruction failed). |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
26 |
tptp_max_term_size --- fail of a term exceeds this size. "0" is taken |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
27 |
to mean infinity. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
28 |
tptp_informative_failure | |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
29 |
informative_failure |-- produce more output during reconstruction. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
30 |
tptp_trace_reconstruction | |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
31 |
|
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
32 |
There are also two attributes, independent of the code here, that |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
33 |
influence the success of reconstruction: blast_depth_limit and |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
34 |
unify_search_bound. These are documented in their respective modules, |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
35 |
but in summary, if unify_search_bound is increased then we can |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
36 |
handle larger terms (at the cost of performance), since the unification |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
37 |
engine takes longer to give up the search; blast_depth_limit is |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
38 |
a limit on proof search performed by Blast. Blast is used for |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
39 |
the limited proof search that needs to be done to interpret |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
40 |
instances of LEO-II's inference rules. |
25d7b485df81
added a function that carries out all the reconstruction steps, for improved usability;
sultana
parents:
55596
diff
changeset
|
41 |
|
55596 | 42 |
TODO: |
43 |
use RemoveRedundantQuantifications instead of the ad hoc use of |
|
44 |
remove_redundant_quantification_in_lit and remove_redundant_quantification |
|
45 |
*) |
|
46 |
||
47 |
theory TPTP_Proof_Reconstruction |
|
48 |
imports TPTP_Parser TPTP_Interpret |
|
49 |
(* keywords "import_leo2_proof" :: thy_decl *) (*FIXME currently unused*) |
|
50 |
begin |
|
51 |
||
52 |
||
53 |
section "Setup" |
|
54 |
||
63167 | 55 |
ML \<open> |
69597 | 56 |
val tptp_unexceptional_reconstruction = Attrib.setup_config_bool \<^binding>\<open>tptp_unexceptional_reconstruction\<close> (K false) |
55596 | 57 |
fun unexceptional_reconstruction ctxt = Config.get ctxt tptp_unexceptional_reconstruction |
69597 | 58 |
val tptp_informative_failure = Attrib.setup_config_bool \<^binding>\<open>tptp_informative_failure\<close> (K false) |
55596 | 59 |
fun informative_failure ctxt = Config.get ctxt tptp_informative_failure |
69597 | 60 |
val tptp_trace_reconstruction = Attrib.setup_config_bool \<^binding>\<open>tptp_trace_reconstruction\<close> (K false) |
61 |
val tptp_max_term_size = Attrib.setup_config_int \<^binding>\<open>tptp_max_term_size\<close> (K 0) (*0=infinity*) |
|
55596 | 62 |
|
63 |
fun exceeds_tptp_max_term_size ctxt size = |
|
64 |
let |
|
65 |
val max = Config.get ctxt tptp_max_term_size |
|
66 |
in |
|
67 |
if max = 0 then false |
|
68 |
else size > max |
|
69 |
end |
|
63167 | 70 |
\<close> |
55596 | 71 |
|
72 |
(*FIXME move to TPTP_Proof_Reconstruction_Test_Units*) |
|
73 |
declare [[ |
|
74 |
tptp_unexceptional_reconstruction = false, (*NOTE should be "false" while testing*) |
|
75 |
tptp_informative_failure = true |
|
76 |
]] |
|
77 |
||
78727 | 78 |
ML \<open> |
79 |
exception UNSUPPORTED_ROLE |
|
80 |
exception INTERPRET_INFERENCE |
|
81 |
\<close> |
|
82 |
||
69605 | 83 |
ML_file \<open>TPTP_Parser/tptp_reconstruct_library.ML\<close> |
55596 | 84 |
ML "open TPTP_Reconstruct_Library" |
69605 | 85 |
ML_file \<open>TPTP_Parser/tptp_reconstruct.ML\<close> |
55596 | 86 |
|
87 |
(*FIXME fudge*) |
|
88 |
declare [[ |
|
89 |
blast_depth_limit = 10, |
|
90 |
unify_search_bound = 5 |
|
91 |
]] |
|
92 |
||
93 |
||
94 |
section "Proof reconstruction" |
|
63167 | 95 |
text \<open>There are two parts to proof reconstruction: |
55596 | 96 |
\begin{itemize} |
97 |
\item interpreting the inferences |
|
98 |
\item building the skeleton, which indicates how to compose |
|
99 |
individual inferences into subproofs, and then compose the |
|
100 |
subproofs to give the proof). |
|
101 |
\end{itemize} |
|
102 |
||
103 |
One step detects unsound inferences, and the other step detects |
|
104 |
unsound composition of inferences. The two parts can be weakly |
|
105 |
coupled. They rely on a "proof index" which maps nodes to the |
|
106 |
inference information. This information consists of the (usually |
|
107 |
prover-specific) name of the inference step, and the Isabelle |
|
108 |
formalisation of the inference as a term. The inference interpretation |
|
109 |
then maps these terms into meta-theorems, and the skeleton is used to |
|
110 |
compose the inference-level steps into a proof. |
|
111 |
||
112 |
Leo2 operates on conjunctions of clauses. Each Leo2 inference has the |
|
113 |
following form, where Cx are clauses: |
|
114 |
||
115 |
C1 && ... && Cn |
|
116 |
----------------- |
|
117 |
C'1 && ... && C'n |
|
118 |
||
119 |
Clauses consist of disjunctions of literals (shown as Px below), and might |
|
120 |
have a prefix of !-bound variables, as shown below. |
|
121 |
||
122 |
! X... { P1 || ... || Pn} |
|
123 |
||
124 |
Literals are usually assigned a polarity, but this isn't always the |
|
125 |
case; you can come across inferences looking like this (where A is an |
|
126 |
object-level formula): |
|
127 |
||
128 |
F |
|
129 |
-------- |
|
130 |
F = true |
|
131 |
||
132 |
The symbol "||" represents literal-level disjunction and "&&" is |
|
133 |
clause-level conjunction. Rules will typically lift formula-level |
|
134 |
conjunctions; for instance the following rule lifts object-level |
|
135 |
disjunction: |
|
136 |
||
137 |
{ (A | B) = true || ... } && ... |
|
138 |
-------------------------------------- |
|
139 |
{ A = true || B = true || ... } && ... |
|
140 |
||
141 |
||
142 |
Using this setup, efficiency might be gained by only interpreting |
|
143 |
inferences once, merging identical inference steps, and merging |
|
144 |
identical subproofs into single inferences thus avoiding some effort. |
|
145 |
We can also attempt to minimising proof search when interpreting |
|
146 |
inferences. |
|
147 |
||
148 |
It is hoped that this setup can target other provers by modifying the |
|
149 |
clause representation to fit them, and adapting the inference |
|
150 |
interpretation to handle the rules used by the prover. It should also |
|
151 |
facilitate composing together proofs found by different provers. |
|
63167 | 152 |
\<close> |
55596 | 153 |
|
154 |
||
155 |
subsection "Instantiation" |
|
156 |
||
157 |
lemma polar_allE [rule_format]: |
|
158 |
"\<lbrakk>(\<forall>x. P x) = True; (P x) = True \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R" |
|
159 |
"\<lbrakk>(\<exists>x. P x) = False; (P x) = False \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R" |
|
160 |
by auto |
|
161 |
||
162 |
lemma polar_exE [rule_format]: |
|
163 |
"\<lbrakk>(\<exists>x. P x) = True; \<And>x. (P x) = True \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R" |
|
164 |
"\<lbrakk>(\<forall>x. P x) = False; \<And>x. (P x) = False \<Longrightarrow> R\<rbrakk> \<Longrightarrow> R" |
|
165 |
by auto |
|
166 |
||
63167 | 167 |
ML \<open> |
55596 | 168 |
(*This carries out an allE-like rule but on (polarised) literals. |
169 |
Instead of yielding a free variable (which is a hell for the |
|
170 |
matcher) it seeks to use one of the subgoals' parameters. |
|
171 |
This ought to be sufficient for emulating extcnf_combined, |
|
172 |
but note that the complexity of the problem can be enormous.*) |
|
173 |
fun inst_parametermatch_tac ctxt thms i = fn st => |
|
174 |
let |
|
175 |
val gls = |
|
59582 | 176 |
Thm.prop_of st |
55596 | 177 |
|> Logic.strip_horn |
178 |
|> fst |
|
179 |
||
180 |
val parameters = |
|
181 |
if null gls then [] |
|
182 |
else |
|
183 |
rpair (i - 1) gls |
|
184 |
|> uncurry nth |
|
185 |
|> strip_top_all_vars [] |
|
186 |
|> fst |
|
187 |
|> map fst (*just get the parameter names*) |
|
188 |
in |
|
189 |
if null parameters then no_tac st |
|
190 |
else |
|
191 |
let |
|
192 |
fun instantiate param = |
|
59780 | 193 |
(map (Rule_Insts.eres_inst_tac ctxt [((("x", 0), Position.none), param)] []) thms |
55596 | 194 |
|> FIRST') |
195 |
val attempts = map instantiate parameters |
|
196 |
in |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
197 |
(fold (curry (op APPEND')) attempts (K no_tac)) i st |
55596 | 198 |
end |
199 |
end |
|
200 |
||
201 |
(*Attempts to use the polar_allE theorems on a specific subgoal.*) |
|
202 |
fun forall_pos_tac ctxt = inst_parametermatch_tac ctxt @{thms polar_allE} |
|
63167 | 203 |
\<close> |
55596 | 204 |
|
63167 | 205 |
ML \<open> |
55596 | 206 |
(*This is similar to inst_parametermatch_tac, but prefers to |
207 |
match variables having identical names. Logically, this is |
|
208 |
a hack. But it reduces the complexity of the problem.*) |
|
209 |
fun nominal_inst_parametermatch_tac ctxt thm i = fn st => |
|
210 |
let |
|
211 |
val gls = |
|
59582 | 212 |
Thm.prop_of st |
55596 | 213 |
|> Logic.strip_horn |
214 |
|> fst |
|
215 |
||
216 |
val parameters = |
|
217 |
if null gls then [] |
|
218 |
else |
|
219 |
rpair (i - 1) gls |
|
220 |
|> uncurry nth |
|
221 |
|> strip_top_all_vars [] |
|
222 |
|> fst |
|
223 |
|> map fst (*just get the parameter names*) |
|
224 |
in |
|
225 |
if null parameters then no_tac st |
|
226 |
else |
|
227 |
let |
|
228 |
fun instantiates param = |
|
59780 | 229 |
Rule_Insts.eres_inst_tac ctxt [((("x", 0), Position.none), param)] [] thm |
55596 | 230 |
|
59533 | 231 |
val quantified_var = head_quantified_variable ctxt i st |
55596 | 232 |
in |
233 |
if is_none quantified_var then no_tac st |
|
234 |
else |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
235 |
if member (op =) parameters (the quantified_var |> fst) then |
55596 | 236 |
instantiates (the quantified_var |> fst) i st |
237 |
else |
|
238 |
K no_tac i st |
|
239 |
end |
|
240 |
end |
|
63167 | 241 |
\<close> |
55596 | 242 |
|
243 |
||
244 |
subsection "Prefix massaging" |
|
245 |
||
63167 | 246 |
ML \<open> |
55596 | 247 |
exception NO_GOALS |
248 |
||
249 |
(*Get quantifier prefix of the hypothesis and conclusion, reorder |
|
250 |
the hypothesis' quantifiers to have the ones appearing in the |
|
251 |
conclusion first.*) |
|
252 |
fun canonicalise_qtfr_order ctxt i = fn st => |
|
253 |
let |
|
254 |
val gls = |
|
59582 | 255 |
Thm.prop_of st |
55596 | 256 |
|> Logic.strip_horn |
257 |
|> fst |
|
258 |
in |
|
259 |
if null gls then raise NO_GOALS |
|
260 |
else |
|
261 |
let |
|
262 |
val (params, (hyp_clause, conc_clause)) = |
|
263 |
rpair (i - 1) gls |
|
264 |
|> uncurry nth |
|
265 |
|> strip_top_all_vars [] |
|
266 |
|> apsnd Logic.dest_implies |
|
267 |
||
268 |
val (hyp_quants, hyp_body) = |
|
269 |
HOLogic.dest_Trueprop hyp_clause |
|
270 |
|> strip_top_All_vars |
|
271 |
|> apfst rev |
|
272 |
||
273 |
val conc_quants = |
|
274 |
HOLogic.dest_Trueprop conc_clause |
|
275 |
|> strip_top_All_vars |
|
276 |
|> fst |
|
277 |
||
278 |
val new_hyp = |
|
279 |
(* fold absfree new_hyp_prefix hyp_body *) |
|
280 |
(*HOLogic.list_all*) |
|
281 |
fold_rev (fn (v, ty) => fn t => HOLogic.mk_all (v, ty, t)) |
|
282 |
(prefix_intersection_list |
|
283 |
hyp_quants conc_quants) |
|
284 |
hyp_body |
|
285 |
|> HOLogic.mk_Trueprop |
|
286 |
||
287 |
val thm = Goal.prove ctxt [] [] |
|
288 |
(Logic.mk_implies (hyp_clause, new_hyp)) |
|
289 |
(fn _ => |
|
60754 | 290 |
(REPEAT_DETERM (HEADGOAL (resolve_tac ctxt @{thms allI}))) |
55596 | 291 |
THEN (REPEAT_DETERM |
292 |
(HEADGOAL |
|
293 |
(nominal_inst_parametermatch_tac ctxt @{thm allE}))) |
|
60754 | 294 |
THEN HEADGOAL (assume_tac ctxt)) |
55596 | 295 |
in |
60754 | 296 |
dresolve_tac ctxt [thm] i st |
55596 | 297 |
end |
298 |
end |
|
63167 | 299 |
\<close> |
55596 | 300 |
|
301 |
||
302 |
subsection "Some general rules and congruences" |
|
303 |
||
304 |
(*this isn't an actual rule used in Leo2, but it seems to be |
|
305 |
applied implicitly during some Leo2 inferences.*) |
|
306 |
lemma polarise: "P ==> P = True" by auto |
|
307 |
||
63167 | 308 |
ML \<open> |
55596 | 309 |
fun is_polarised t = |
310 |
(TPTP_Reconstruct.remove_polarity true t; true) |
|
311 |
handle TPTP_Reconstruct.UNPOLARISED _ => false |
|
312 |
||
60754 | 313 |
fun polarise_subgoal_hyps ctxt = |
314 |
COND' (SOME #> TERMPRED is_polarised (fn _ => true)) (K no_tac) (dresolve_tac ctxt @{thms polarise}) |
|
63167 | 315 |
\<close> |
55596 | 316 |
|
317 |
lemma simp_meta [rule_format]: |
|
318 |
"(A --> B) == (~A | B)" |
|
319 |
"(A | B) | C == A | B | C" |
|
320 |
"(A & B) & C == A & B & C" |
|
321 |
"(~ (~ A)) == A" |
|
322 |
(* "(A & B) == (~ (~A | ~B))" *) |
|
323 |
"~ (A & B) == (~A | ~B)" |
|
324 |
"~(A | B) == (~A) & (~B)" |
|
325 |
by auto |
|
326 |
||
327 |
||
328 |
subsection "Emulation of Leo2's inference rules" |
|
329 |
||
330 |
(*this is not included in simp_meta since it would make a mess of the polarities*) |
|
331 |
lemma expand_iff [rule_format]: |
|
332 |
"((A :: bool) = B) \<equiv> (~ A | B) & (~ B | A)" |
|
333 |
by (rule eq_reflection, auto) |
|
334 |
||
335 |
lemma polarity_switch [rule_format]: |
|
336 |
"(\<not> P) = True \<Longrightarrow> P = False" |
|
337 |
"(\<not> P) = False \<Longrightarrow> P = True" |
|
338 |
"P = False \<Longrightarrow> (\<not> P) = True" |
|
339 |
"P = True \<Longrightarrow> (\<not> P) = False" |
|
340 |
by auto |
|
341 |
||
342 |
lemma solved_all_splits: "False = True \<Longrightarrow> False" by simp |
|
63167 | 343 |
ML \<open> |
60754 | 344 |
fun solved_all_splits_tac ctxt = |
345 |
TRY (eresolve_tac ctxt @{thms conjE} 1) |
|
346 |
THEN resolve_tac ctxt @{thms solved_all_splits} 1 |
|
347 |
THEN assume_tac ctxt 1 |
|
63167 | 348 |
\<close> |
55596 | 349 |
|
350 |
lemma lots_of_logic_expansions_meta [rule_format]: |
|
351 |
"(((A :: bool) = B) = True) == (((A \<longrightarrow> B) = True) & ((B \<longrightarrow> A) = True))" |
|
352 |
"((A :: bool) = B) = False == (((~A) | B) = False) | (((~B) | A) = False)" |
|
353 |
||
67613 | 354 |
"((F = G) = True) == (\<forall>x. (F x = G x)) = True" |
355 |
"((F = G) = False) == (\<forall>x. (F x = G x)) = False" |
|
55596 | 356 |
|
357 |
"(A | B) = True == (A = True) | (B = True)" |
|
358 |
"(A & B) = False == (A = False) | (B = False)" |
|
359 |
"(A | B) = False == (A = False) & (B = False)" |
|
360 |
"(A & B) = True == (A = True) & (B = True)" |
|
361 |
"(~ A) = True == A = False" |
|
362 |
"(~ A) = False == A = True" |
|
363 |
"~ (A = True) == A = False" |
|
364 |
"~ (A = False) == A = True" |
|
365 |
by (rule eq_reflection, auto)+ |
|
366 |
||
367 |
(*this is used in extcnf_combined handler*) |
|
368 |
lemma eq_neg_bool: "((A :: bool) = B) = False ==> ((~ (A | B)) | ~ ((~ A) | (~ B))) = False" |
|
369 |
by auto |
|
370 |
||
371 |
lemma eq_pos_bool: |
|
372 |
"((A :: bool) = B) = True ==> ((~ (A | B)) | ~ (~ A | ~ B)) = True" |
|
373 |
"(A = B) = True \<Longrightarrow> A = True \<or> B = False" |
|
374 |
"(A = B) = True \<Longrightarrow> A = False \<or> B = True" |
|
375 |
by auto |
|
376 |
||
377 |
(*next formula is more versatile than |
|
378 |
"(F = G) = True \<Longrightarrow> \<forall>x. ((F x = G x) = True)" |
|
379 |
since it doesn't assume that clause is singleton. After splitqtfr, |
|
380 |
and after applying allI exhaustively to the conclusion, we can |
|
381 |
use the existing functions to find the "(F x = G x) = True" |
|
382 |
disjunct in the conclusion*) |
|
383 |
lemma eq_pos_func: "\<And> x. (F = G) = True \<Longrightarrow> (F x = G x) = True" |
|
384 |
by auto |
|
385 |
||
386 |
(*make sure the conclusion consists of just "False"*) |
|
387 |
lemma flip: |
|
388 |
"((A = True) ==> False) ==> A = False" |
|
389 |
"((A = False) ==> False) ==> A = True" |
|
390 |
by auto |
|
391 |
||
392 |
(*FIXME try to use Drule.equal_elim_rule1 directly for this*) |
|
393 |
lemma equal_elim_rule1: "(A \<equiv> B) \<Longrightarrow> A \<Longrightarrow> B" by auto |
|
394 |
lemmas leo2_rules = |
|
395 |
lots_of_logic_expansions_meta[THEN equal_elim_rule1] |
|
396 |
||
397 |
(*FIXME is there any overlap with lots_of_logic_expansions_meta or leo2_rules?*) |
|
398 |
lemma extuni_bool2 [rule_format]: "(A = B) = False \<Longrightarrow> (A = True) | (B = True)" by auto |
|
399 |
lemma extuni_bool1 [rule_format]: "(A = B) = False \<Longrightarrow> (A = False) | (B = False)" by auto |
|
400 |
lemma extuni_triv [rule_format]: "(A = A) = False \<Longrightarrow> R" by auto |
|
401 |
||
402 |
(*Order (of A, B, C, D) matters*) |
|
403 |
lemma dec_commut_eq [rule_format]: |
|
404 |
"((A = B) = (C = D)) = False \<Longrightarrow> (B = C) = False | (A = D) = False" |
|
405 |
"((A = B) = (C = D)) = False \<Longrightarrow> (B = D) = False | (A = C) = False" |
|
406 |
by auto |
|
407 |
lemma dec_commut_disj [rule_format]: |
|
408 |
"((A \<or> B) = (C \<or> D)) = False \<Longrightarrow> (B = C) = False \<or> (A = D) = False" |
|
409 |
by auto |
|
410 |
||
67613 | 411 |
lemma extuni_func [rule_format]: "(F = G) = False \<Longrightarrow> (\<forall>X. (F X = G X)) = False" by auto |
55596 | 412 |
|
413 |
||
414 |
subsection "Emulation: tactics" |
|
415 |
||
63167 | 416 |
ML \<open> |
55596 | 417 |
(*Instantiate a variable according to the info given in the |
418 |
proof annotation. Through this we avoid having to come up |
|
419 |
with instantiations during reconstruction.*) |
|
420 |
fun bind_tac ctxt prob_name ordered_binds = |
|
421 |
let |
|
422 |
val thy = Proof_Context.theory_of ctxt |
|
423 |
fun term_to_string t = |
|
80866
8c67b14fdd48
clarified internal tool output: prefer Pretty.pure_string_of over manipulation of print_mode;
wenzelm
parents:
80849
diff
changeset
|
424 |
Pretty.pure_string_of (Syntax.pretty_term ctxt t) |
55596 | 425 |
val ordered_instances = |
426 |
TPTP_Reconstruct.interpret_bindings prob_name thy ordered_binds [] |
|
427 |
|> map (snd #> term_to_string) |
|
428 |
|> permute |
|
429 |
||
430 |
(*instantiate a list of variables, order matters*) |
|
431 |
fun instantiate_vars ctxt vars : tactic = |
|
432 |
map (fn var => |
|
433 |
Rule_Insts.eres_inst_tac ctxt |
|
59780 | 434 |
[((("x", 0), Position.none), var)] [] @{thm allE} 1) |
55596 | 435 |
vars |
436 |
|> EVERY |
|
437 |
||
438 |
fun instantiate_tac vars = |
|
439 |
instantiate_vars ctxt vars |
|
60754 | 440 |
THEN (HEADGOAL (assume_tac ctxt)) |
55596 | 441 |
in |
442 |
HEADGOAL (canonicalise_qtfr_order ctxt) |
|
60754 | 443 |
THEN (REPEAT_DETERM (HEADGOAL (resolve_tac ctxt @{thms allI}))) |
55596 | 444 |
THEN REPEAT_DETERM (HEADGOAL (nominal_inst_parametermatch_tac ctxt @{thm allE})) |
445 |
(*now only the variable to instantiate should be left*) |
|
446 |
THEN FIRST (map instantiate_tac ordered_instances) |
|
447 |
end |
|
63167 | 448 |
\<close> |
55596 | 449 |
|
63167 | 450 |
ML \<open> |
55596 | 451 |
(*Simplification tactics*) |
452 |
local |
|
453 |
fun rew_goal_tac thms ctxt i = |
|
454 |
rewrite_goal_tac ctxt thms i |
|
455 |
|> CHANGED |
|
456 |
in |
|
457 |
val expander_animal = |
|
458 |
rew_goal_tac (@{thms simp_meta} @ @{thms lots_of_logic_expansions_meta}) |
|
459 |
||
460 |
val simper_animal = |
|
461 |
rew_goal_tac @{thms simp_meta} |
|
462 |
end |
|
63167 | 463 |
\<close> |
55596 | 464 |
|
465 |
lemma prop_normalise [rule_format]: |
|
466 |
"(A | B) | C == A | B | C" |
|
467 |
"(A & B) & C == A & B & C" |
|
468 |
"A | B == ~(~A & ~B)" |
|
469 |
"~~ A == A" |
|
470 |
by auto |
|
63167 | 471 |
ML \<open> |
55596 | 472 |
(*i.e., break_conclusion*) |
473 |
fun flip_conclusion_tac ctxt = |
|
474 |
let |
|
475 |
val default_tac = |
|
476 |
(TRY o CHANGED o (rewrite_goal_tac ctxt @{thms prop_normalise})) |
|
60754 | 477 |
THEN' resolve_tac ctxt @{thms notI} |
478 |
THEN' (REPEAT_DETERM o eresolve_tac ctxt @{thms conjE}) |
|
55596 | 479 |
THEN' (TRY o (expander_animal ctxt)) |
480 |
in |
|
59498
50b60f501b05
proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents:
58941
diff
changeset
|
481 |
default_tac ORELSE' resolve_tac ctxt @{thms flip} |
55596 | 482 |
end |
63167 | 483 |
\<close> |
55596 | 484 |
|
485 |
||
486 |
subsection "Skolemisation" |
|
487 |
||
488 |
lemma skolemise [rule_format]: |
|
67613 | 489 |
"\<forall> P. (\<not> (\<forall>x. P x)) \<longrightarrow> \<not> (P (SOME x. ~ P x))" |
55596 | 490 |
proof - |
67613 | 491 |
have "\<And> P. (\<not> (\<forall>x. P x)) \<Longrightarrow> \<not> (P (SOME x. ~ P x))" |
55596 | 492 |
proof - |
493 |
fix P |
|
67613 | 494 |
assume ption: "\<not> (\<forall>x. P x)" |
495 |
hence a: "\<exists>x. \<not> P x" by force |
|
55596 | 496 |
|
67613 | 497 |
have hilbert : "\<And>P. (\<exists>x. P x) \<Longrightarrow> (P (SOME x. P x))" |
55596 | 498 |
proof - |
499 |
fix P |
|
67613 | 500 |
assume "(\<exists>x. P x)" |
55596 | 501 |
thus "(P (SOME x. P x))" |
502 |
apply auto |
|
503 |
apply (rule someI) |
|
504 |
apply auto |
|
505 |
done |
|
506 |
qed |
|
507 |
||
67613 | 508 |
from a show "\<not> P (SOME x. \<not> P x)" |
55596 | 509 |
proof - |
67613 | 510 |
assume "\<exists>x. \<not> P x" |
55596 | 511 |
hence "\<not> P (SOME x. \<not> P x)" by (rule hilbert) |
512 |
thus ?thesis . |
|
513 |
qed |
|
514 |
qed |
|
515 |
thus ?thesis by blast |
|
516 |
qed |
|
517 |
||
518 |
lemma polar_skolemise [rule_format]: |
|
67613 | 519 |
"\<forall>P. (\<forall>x. P x) = False \<longrightarrow> (P (SOME x. \<not> P x)) = False" |
55596 | 520 |
proof - |
67613 | 521 |
have "\<And>P. (\<forall>x. P x) = False \<Longrightarrow> (P (SOME x. \<not> P x)) = False" |
55596 | 522 |
proof - |
523 |
fix P |
|
67613 | 524 |
assume ption: "(\<forall>x. P x) = False" |
525 |
hence "\<not> (\<forall>x. P x)" by force |
|
55596 | 526 |
hence "\<not> All P" by force |
527 |
hence "\<not> (P (SOME x. \<not> P x))" by (rule skolemise) |
|
528 |
thus "(P (SOME x. \<not> P x)) = False" by force |
|
529 |
qed |
|
530 |
thus ?thesis by blast |
|
531 |
qed |
|
532 |
||
533 |
lemma leo2_skolemise [rule_format]: |
|
67613 | 534 |
"\<forall>P sk. (\<forall>x. P x) = False \<longrightarrow> (sk = (SOME x. \<not> P x)) \<longrightarrow> (P sk) = False" |
55596 | 535 |
by (clarify, rule polar_skolemise) |
536 |
||
537 |
lemma lift_forall [rule_format]: |
|
67613 | 538 |
"\<And>x. (\<forall>x. A x) = True \<Longrightarrow> (A x) = True" |
539 |
"\<And>x. (\<exists>x. A x) = False \<Longrightarrow> (A x) = False" |
|
55596 | 540 |
by auto |
541 |
lemma lift_exists [rule_format]: |
|
542 |
"\<lbrakk>(All P) = False; sk = (SOME x. \<not> P x)\<rbrakk> \<Longrightarrow> P sk = False" |
|
543 |
"\<lbrakk>(Ex P) = True; sk = (SOME x. P x)\<rbrakk> \<Longrightarrow> P sk = True" |
|
544 |
apply (drule polar_skolemise, simp) |
|
545 |
apply (simp, drule someI_ex, simp) |
|
546 |
done |
|
547 |
||
63167 | 548 |
ML \<open> |
55596 | 549 |
(*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*) |
550 |
fun conc_is_skolem_def t = |
|
551 |
case t of |
|
69597 | 552 |
Const (\<^const_name>\<open>HOL.eq\<close>, _) $ t' $ (Const (\<^const_name>\<open>Hilbert_Choice.Eps\<close>, _) $ _) => |
55596 | 553 |
let |
554 |
val (h, args) = |
|
555 |
strip_comb t' |
|
556 |
|> apfst (strip_abs #> snd #> strip_comb #> fst) |
|
557 |
val h_property = |
|
558 |
is_Free h orelse |
|
559 |
is_Var h orelse |
|
560 |
(is_Const h |
|
80636
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
561 |
andalso (dest_Const_name h <> dest_Const_name \<^term>\<open>HOL.Ex\<close>) |
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
562 |
andalso (dest_Const_name h <> dest_Const_name \<^term>\<open>HOL.All\<close>) |
69597 | 563 |
andalso (h <> \<^term>\<open>Hilbert_Choice.Eps\<close>) |
564 |
andalso (h <> \<^term>\<open>HOL.conj\<close>) |
|
565 |
andalso (h <> \<^term>\<open>HOL.disj\<close>) |
|
566 |
andalso (h <> \<^term>\<open>HOL.eq\<close>) |
|
567 |
andalso (h <> \<^term>\<open>HOL.implies\<close>) |
|
568 |
andalso (h <> \<^term>\<open>HOL.The\<close>) |
|
569 |
andalso (h <> \<^term>\<open>HOL.Ex1\<close>) |
|
570 |
andalso (h <> \<^term>\<open>HOL.Not\<close>) |
|
571 |
andalso (h <> \<^term>\<open>HOL.iff\<close>) |
|
572 |
andalso (h <> \<^term>\<open>HOL.not_equal\<close>)) |
|
55596 | 573 |
val args_property = |
574 |
fold (fn t => fn b => |
|
575 |
b andalso is_Free t) args true |
|
576 |
in |
|
577 |
h_property andalso args_property |
|
578 |
end |
|
579 |
| _ => false |
|
63167 | 580 |
\<close> |
55596 | 581 |
|
63167 | 582 |
ML \<open> |
55596 | 583 |
(*Hack used to detect if a Skolem definition, with an LHS Var, has had the LHS instantiated into an unacceptable term.*) |
584 |
fun conc_is_bad_skolem_def t = |
|
585 |
case t of |
|
69597 | 586 |
Const (\<^const_name>\<open>HOL.eq\<close>, _) $ t' $ (Const (\<^const_name>\<open>Hilbert_Choice.Eps\<close>, _) $ _) => |
55596 | 587 |
let |
588 |
val (h, args) = strip_comb t' |
|
589 |
val const_h_test = |
|
590 |
if is_Const h then |
|
80636
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
591 |
(dest_Const_name h = dest_Const_name \<^term>\<open>HOL.Ex\<close>) |
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
592 |
orelse (dest_Const_name h = dest_Const_name \<^term>\<open>HOL.All\<close>) |
69597 | 593 |
orelse (h = \<^term>\<open>Hilbert_Choice.Eps\<close>) |
594 |
orelse (h = \<^term>\<open>HOL.conj\<close>) |
|
595 |
orelse (h = \<^term>\<open>HOL.disj\<close>) |
|
596 |
orelse (h = \<^term>\<open>HOL.eq\<close>) |
|
597 |
orelse (h = \<^term>\<open>HOL.implies\<close>) |
|
598 |
orelse (h = \<^term>\<open>HOL.The\<close>) |
|
599 |
orelse (h = \<^term>\<open>HOL.Ex1\<close>) |
|
600 |
orelse (h = \<^term>\<open>HOL.Not\<close>) |
|
601 |
orelse (h = \<^term>\<open>HOL.iff\<close>) |
|
602 |
orelse (h = \<^term>\<open>HOL.not_equal\<close>) |
|
55596 | 603 |
else true |
604 |
val h_property = |
|
605 |
not (is_Free h) andalso |
|
606 |
not (is_Var h) andalso |
|
607 |
const_h_test |
|
608 |
val args_property = |
|
609 |
fold (fn t => fn b => |
|
610 |
b andalso is_Free t) args true |
|
611 |
in |
|
612 |
h_property andalso args_property |
|
613 |
end |
|
614 |
| _ => false |
|
63167 | 615 |
\<close> |
55596 | 616 |
|
63167 | 617 |
ML \<open> |
55596 | 618 |
fun get_skolem_conc t = |
619 |
let |
|
620 |
val t' = |
|
621 |
strip_top_all_vars [] t |
|
622 |
|> snd |
|
623 |
|> try_dest_Trueprop |
|
624 |
in |
|
625 |
case t' of |
|
69597 | 626 |
Const (\<^const_name>\<open>HOL.eq\<close>, _) $ t' $ (Const (\<^const_name>\<open>Hilbert_Choice.Eps\<close>, _) $ _) => SOME t' |
55596 | 627 |
| _ => NONE |
628 |
end |
|
629 |
||
630 |
fun get_skolem_conc_const t = |
|
631 |
lift_option |
|
632 |
(fn t' => |
|
633 |
head_of t' |
|
634 |
|> strip_abs_body |
|
635 |
|> head_of |
|
636 |
|> dest_Const) |
|
637 |
(get_skolem_conc t) |
|
63167 | 638 |
\<close> |
55596 | 639 |
|
640 |
(* |
|
641 |
Technique for handling quantifiers: |
|
642 |
Principles: |
|
643 |
* allE should always match with a !! |
|
644 |
* exE should match with a constant, |
|
645 |
or bind a fresh !! -- currently not doing the latter since it never seems to arised in normal Leo2 proofs. |
|
646 |
*) |
|
647 |
||
63167 | 648 |
ML \<open> |
55596 | 649 |
fun forall_neg_tac candidate_consts ctxt i = fn st => |
650 |
let |
|
651 |
val gls = |
|
59582 | 652 |
Thm.prop_of st |
55596 | 653 |
|> Logic.strip_horn |
654 |
|> fst |
|
655 |
||
656 |
val parameters = |
|
657 |
if null gls then "" |
|
658 |
else |
|
659 |
rpair (i - 1) gls |
|
660 |
|> uncurry nth |
|
661 |
|> strip_top_all_vars [] |
|
662 |
|> fst |
|
663 |
|> map fst (*just get the parameter names*) |
|
664 |
|> (fn l => |
|
665 |
if null l then "" |
|
666 |
else |
|
80910 | 667 |
implode_space l |
55596 | 668 |
|> pair " " |
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
669 |
|> (op ^)) |
55596 | 670 |
|
671 |
in |
|
672 |
if null gls orelse null candidate_consts then no_tac st |
|
673 |
else |
|
674 |
let |
|
675 |
fun instantiate const_name = |
|
59780 | 676 |
Rule_Insts.dres_inst_tac ctxt [((("sk", 0), Position.none), const_name ^ parameters)] [] |
59755 | 677 |
@{thm leo2_skolemise} |
55596 | 678 |
val attempts = map instantiate candidate_consts |
679 |
in |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
680 |
(fold (curry (op APPEND')) attempts (K no_tac)) i st |
55596 | 681 |
end |
682 |
end |
|
63167 | 683 |
\<close> |
55596 | 684 |
|
63167 | 685 |
ML \<open> |
55596 | 686 |
exception SKOLEM_DEF of term (*The tactic wasn't pointed at a skolem definition*) |
687 |
exception NO_SKOLEM_DEF of (*skolem const name*)string * Binding.binding * term (*The tactic could not find a skolem definition in the theory*) |
|
688 |
fun absorb_skolem_def ctxt prob_name_opt i = fn st => |
|
689 |
let |
|
690 |
val thy = Proof_Context.theory_of ctxt |
|
691 |
||
692 |
val gls = |
|
59582 | 693 |
Thm.prop_of st |
55596 | 694 |
|> Logic.strip_horn |
695 |
|> fst |
|
696 |
||
697 |
val conclusion = |
|
698 |
if null gls then |
|
699 |
(*this should never be thrown*) |
|
700 |
raise NO_GOALS |
|
701 |
else |
|
702 |
rpair (i - 1) gls |
|
703 |
|> uncurry nth |
|
704 |
|> strip_top_all_vars [] |
|
705 |
|> snd |
|
706 |
|> Logic.strip_horn |
|
707 |
|> snd |
|
708 |
||
709 |
fun skolem_const_info_of t = |
|
710 |
case t of |
|
69597 | 711 |
Const (\<^const_name>\<open>HOL.Trueprop\<close>, _) $ (Const (\<^const_name>\<open>HOL.eq\<close>, _) $ t' $ (Const (\<^const_name>\<open>Hilbert_Choice.Eps\<close>, _) $ _)) => |
55596 | 712 |
head_of t' |
713 |
|> 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)*) |
|
714 |
|> head_of |
|
715 |
|> dest_Const |
|
716 |
| _ => raise SKOLEM_DEF t |
|
717 |
||
718 |
val const_name = |
|
719 |
skolem_const_info_of conclusion |
|
720 |
|> fst |
|
721 |
||
722 |
val def_name = const_name ^ "_def" |
|
723 |
||
724 |
val bnd_def = (*FIXME consts*) |
|
725 |
const_name |
|
56220
4c43a2881b25
more explicit Long_Name operations (NB: analyzing qualifiers is inherently fragile);
wenzelm
parents:
55597
diff
changeset
|
726 |
|> Long_Name.implode o tl o Long_Name.explode (*FIXME hack to drop theory-name prefix*) |
55596 | 727 |
|> Binding.qualified_name |
728 |
|> Binding.suffix_name "_def" |
|
729 |
||
730 |
val bnd_name = |
|
731 |
case prob_name_opt of |
|
732 |
NONE => bnd_def |
|
733 |
| SOME prob_name => |
|
734 |
(* Binding.qualify false |
|
735 |
(TPTP_Problem_Name.mangle_problem_name prob_name) |
|
736 |
*) |
|
737 |
bnd_def |
|
738 |
||
739 |
val thm = |
|
59882 | 740 |
(case try (Thm.axiom thy) def_name of |
741 |
SOME thm => thm |
|
742 |
| NONE => |
|
743 |
if is_none prob_name_opt then |
|
744 |
(*This mode is for testing, so we can be a bit |
|
745 |
looser with theories*) |
|
746 |
(* FIXME bad theory context!? *) |
|
747 |
Thm.add_axiom_global (bnd_name, conclusion) thy |
|
748 |
|> fst |> snd |
|
749 |
else |
|
750 |
raise (NO_SKOLEM_DEF (def_name, bnd_name, conclusion))) |
|
55596 | 751 |
in |
60754 | 752 |
resolve_tac ctxt [Drule.export_without_context thm] i st |
55596 | 753 |
end |
754 |
handle SKOLEM_DEF _ => no_tac st |
|
63167 | 755 |
\<close> |
55596 | 756 |
|
63167 | 757 |
ML \<open> |
55596 | 758 |
(* |
759 |
In current system, there should only be 2 subgoals: the one where |
|
760 |
the skolem definition is being built (with a Var in the LHS), and the other subgoal using Var. |
|
761 |
*) |
|
762 |
(*arity must be greater than 0. if arity=0 then |
|
763 |
there's no need to use this expensive matching.*) |
|
764 |
fun find_skolem_term ctxt consts_candidate arity = fn st => |
|
765 |
let |
|
69597 | 766 |
val _ = \<^assert> (arity > 0) |
55596 | 767 |
|
768 |
val gls = |
|
59582 | 769 |
Thm.prop_of st |
55596 | 770 |
|> Logic.strip_horn |
771 |
|> fst |
|
772 |
||
773 |
(*extract the conclusion of each subgoal*) |
|
774 |
val conclusions = |
|
775 |
if null gls then |
|
776 |
raise NO_GOALS |
|
777 |
else |
|
778 |
map (strip_top_all_vars [] #> snd #> Logic.strip_horn #> snd) gls |
|
779 |
(*Remove skolem-definition conclusion, to avoid wasting time analysing it*) |
|
58412 | 780 |
|> filter (try_dest_Trueprop #> conc_is_skolem_def #> not) |
55596 | 781 |
(*There should only be a single goal*) (*FIXME this might not always be the case, in practice*) |
782 |
(* |> tap (fn x => @{assert} (is_some (try the_single x))) *) |
|
783 |
||
784 |
(*look for subterms headed by a skolem constant, and whose |
|
785 |
arguments are all parameter Vars*) |
|
786 |
fun get_skolem_terms args (acc : term list) t = |
|
787 |
case t of |
|
788 |
(c as Const _) $ (v as Free _) => |
|
789 |
if c = consts_candidate andalso |
|
790 |
arity = length args + 1 then |
|
791 |
(list_comb (c, v :: args)) :: acc |
|
792 |
else acc |
|
793 |
| t1 $ (v as Free _) => |
|
794 |
get_skolem_terms (v :: args) acc t1 @ |
|
795 |
get_skolem_terms [] acc t1 |
|
796 |
| t1 $ t2 => |
|
797 |
get_skolem_terms [] acc t1 @ |
|
798 |
get_skolem_terms [] acc t2 |
|
799 |
| Abs (_, _, t') => get_skolem_terms [] acc t' |
|
800 |
| _ => acc |
|
801 |
in |
|
802 |
map (strip_top_All_vars #> snd) conclusions |
|
58411 | 803 |
|> maps (get_skolem_terms [] []) |
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
804 |
|> distinct (op =) |
55596 | 805 |
end |
63167 | 806 |
\<close> |
55596 | 807 |
|
63167 | 808 |
ML \<open> |
55596 | 809 |
fun instantiate_skols ctxt consts_candidates i = fn st => |
810 |
let |
|
811 |
val gls = |
|
59582 | 812 |
Thm.prop_of st |
55596 | 813 |
|> Logic.strip_horn |
814 |
|> fst |
|
815 |
||
816 |
val (params, conclusion) = |
|
817 |
if null gls then |
|
818 |
raise NO_GOALS |
|
819 |
else |
|
820 |
rpair (i - 1) gls |
|
821 |
|> uncurry nth |
|
822 |
|> strip_top_all_vars [] |
|
823 |
|> apsnd (Logic.strip_horn #> snd) |
|
824 |
||
825 |
fun skolem_const_info_of t = |
|
826 |
case t of |
|
69597 | 827 |
Const (\<^const_name>\<open>HOL.Trueprop\<close>, _) $ (Const (\<^const_name>\<open>HOL.eq\<close>, _) $ lhs $ (Const (\<^const_name>\<open>Hilbert_Choice.Eps\<close>, _) $ rhs)) => |
55596 | 828 |
let |
829 |
(*the parameters we will concern ourselves with*) |
|
830 |
val params' = |
|
831 |
Term.add_frees lhs [] |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
832 |
|> distinct (op =) |
55596 | 833 |
(*check to make sure that params' <= params*) |
69597 | 834 |
val _ = \<^assert> (forall (member (op =) params) params') |
55596 | 835 |
val skolem_const_ty = |
836 |
let |
|
837 |
val (skolem_const_prety, no_params) = |
|
838 |
Term.strip_comb lhs |
|
839 |
|> apfst (dest_Var #> snd) (*head of lhs consists of a logical variable. we just want its type.*) |
|
840 |
|> apsnd length |
|
841 |
||
69597 | 842 |
val _ = \<^assert> (length params = no_params) |
55596 | 843 |
|
844 |
(*get value type of a function type after n arguments have been supplied*) |
|
845 |
fun get_val_ty n ty = |
|
846 |
if n = 0 then ty |
|
847 |
else get_val_ty (n - 1) (dest_funT ty |> snd) |
|
848 |
in |
|
849 |
get_val_ty no_params skolem_const_prety |
|
850 |
end |
|
851 |
||
852 |
in |
|
853 |
(skolem_const_ty, params') |
|
854 |
end |
|
855 |
| _ => raise (SKOLEM_DEF t) |
|
856 |
||
857 |
(* |
|
858 |
find skolem const candidates which, after applying distinct members of params' we end up with, give us something of type skolem_const_ty. |
|
859 |
||
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. |
|
861 |
*) |
|
862 |
(* |
|
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. |
|
865 |
*) |
|
866 |
fun use_candidate target_ty params acc cur_ty = |
|
867 |
if null params then |
|
58941 | 868 |
if cur_ty = target_ty then |
55596 | 869 |
SOME (rev acc) |
870 |
else NONE |
|
871 |
else |
|
78727 | 872 |
\<^try>\<open> |
873 |
let |
|
874 |
val (arg_ty, val_ty) = Term.dest_funT cur_ty |
|
875 |
(*now find a param of type arg_ty*) |
|
876 |
val (candidate_param, params') = |
|
877 |
find_and_remove (snd #> pair arg_ty #> (op =)) params |
|
878 |
in |
|
879 |
use_candidate target_ty params' (candidate_param :: acc) val_ty |
|
880 |
end |
|
881 |
catch TYPE ("dest_funT", _, _) => NONE (* FIXME fragile *) |
|
62243 | 882 |
| _ => NONE (* FIXME avoid catch-all handler *) |
78727 | 883 |
\<close> |
55596 | 884 |
|
885 |
val (skolem_const_ty, params') = skolem_const_info_of conclusion |
|
886 |
||
887 |
(* |
|
888 |
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 |
||
890 |
Big picture: |
|
891 |
we run the following: |
|
892 |
drule leo2_skolemise THEN' this_tactic |
|
893 |
||
894 |
NOTE: remember to APPEND' instead of ORELSE' the two tactics relating to skolemisation |
|
895 |
*) |
|
896 |
||
897 |
val filtered_candidates = |
|
80636
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
898 |
map (dest_Const_type #> use_candidate skolem_const_ty params' []) |
55596 | 899 |
consts_candidates (* prefiltered_candidates *) |
900 |
|> pair consts_candidates (* prefiltered_candidates *) |
|
901 |
|> ListPair.zip |
|
902 |
|> filter (snd #> is_none #> not) |
|
903 |
|> map (apsnd the) |
|
904 |
||
905 |
val skolem_terms = |
|
906 |
let |
|
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) |
|
911 |
else t |
|
912 |
in |
|
913 |
map make_result_t filtered_candidates |
|
914 |
end |
|
915 |
||
916 |
(*prefix a skolem term with bindings for the parameters*) |
|
917 |
(* val contextualise = fold absdummy (map snd params) *) |
|
918 |
val contextualise = fold absfree params |
|
919 |
||
59639 | 920 |
val skolem_cts = map (contextualise #> Thm.cterm_of ctxt) skolem_terms |
55596 | 921 |
|
922 |
||
923 |
(*now the instantiation code*) |
|
924 |
||
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.*) |
|
926 |
val var_opt = |
|
927 |
let |
|
928 |
val pre_var = |
|
929 |
gls |
|
930 |
|> map |
|
931 |
(strip_top_all_vars [] #> snd #> |
|
932 |
Logic.strip_horn #> snd #> |
|
933 |
get_skolem_conc) |
|
934 |
|> switch (fold (fn x => fn l => if is_some x then the x :: l else l)) [] |
|
58411 | 935 |
|> maps (switch Term.add_vars []) |
55596 | 936 |
|
937 |
fun make_var pre_var = |
|
938 |
the_single pre_var |
|
939 |
|> Var |
|
59639 | 940 |
|> Thm.cterm_of ctxt |
55596 | 941 |
|> SOME |
942 |
in |
|
943 |
if null pre_var then NONE |
|
944 |
else make_var pre_var |
|
945 |
end |
|
946 |
||
947 |
fun instantiate_tac from to = |
|
77879 | 948 |
PRIMITIVE (Thm.instantiate (TVars.empty, Vars.make1 (from, to))) |
55596 | 949 |
|
59760 | 950 |
val tactic = |
55596 | 951 |
if is_none var_opt then no_tac |
952 |
else |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
953 |
fold (curry (op APPEND)) |
60642
48dd1cefb4ae
simplified Thm.instantiate and derivatives: the LHS refers to non-certified variables -- this merely serves as index into already certified structures (or is ignored);
wenzelm
parents:
60319
diff
changeset
|
954 |
(map (instantiate_tac (dest_Var (Thm.term_of (the var_opt)))) skolem_cts) no_tac |
55596 | 955 |
in |
59760 | 956 |
tactic st |
55596 | 957 |
end |
63167 | 958 |
\<close> |
55596 | 959 |
|
63167 | 960 |
ML \<open> |
55596 | 961 |
fun new_skolem_tac ctxt consts_candidates = |
962 |
let |
|
60754 | 963 |
fun tac thm = |
964 |
dresolve_tac ctxt [thm] |
|
55596 | 965 |
THEN' instantiate_skols ctxt consts_candidates |
966 |
in |
|
967 |
if null consts_candidates then K no_tac |
|
60754 | 968 |
else FIRST' (map tac @{thms lift_exists}) |
55596 | 969 |
end |
63167 | 970 |
\<close> |
55596 | 971 |
|
972 |
(* |
|
973 |
need a tactic to expand "? x . P" to "~ ! x. ~ P" |
|
974 |
*) |
|
63167 | 975 |
ML \<open> |
55596 | 976 |
fun ex_expander_tac ctxt i = |
977 |
let |
|
978 |
val simpset = |
|
979 |
empty_simpset ctxt (*NOTE for some reason, Bind exception gets raised if ctxt's simpset isn't emptied*) |
|
67613 | 980 |
|> Simplifier.add_simp @{lemma "Ex P == (\<not> (\<forall>x. \<not> P x))" by auto} |
55596 | 981 |
in |
982 |
CHANGED (asm_full_simp_tac simpset i) |
|
983 |
end |
|
63167 | 984 |
\<close> |
55596 | 985 |
|
986 |
||
987 |
subsubsection "extuni_dec" |
|
988 |
||
63167 | 989 |
ML \<open> |
55596 | 990 |
(*n-ary decomposition. Code is based on the n-ary arg_cong generator*) |
991 |
fun extuni_dec_n ctxt arity = |
|
992 |
let |
|
69597 | 993 |
val _ = \<^assert> (arity > 0) |
55596 | 994 |
val is = |
67404 | 995 |
1 upto arity |
55596 | 996 |
|> map Int.toString |
69597 | 997 |
val arg_tys = map (fn i => TFree ("arg" ^ i ^ "_ty", \<^sort>\<open>type\<close>)) is |
998 |
val res_ty = TFree ("res" ^ "_ty", \<^sort>\<open>type\<close>) |
|
55596 | 999 |
val f_ty = arg_tys ---> res_ty |
1000 |
val f = Free ("f", f_ty) |
|
1001 |
val xs = map (fn i => |
|
69597 | 1002 |
Free ("x" ^ i, TFree ("arg" ^ i ^ "_ty", \<^sort>\<open>type\<close>))) is |
55596 | 1003 |
(*FIXME DRY principle*) |
1004 |
val ys = map (fn i => |
|
69597 | 1005 |
Free ("y" ^ i, TFree ("arg" ^ i ^ "_ty", \<^sort>\<open>type\<close>))) is |
55596 | 1006 |
|
1007 |
val hyp_lhs = list_comb (f, xs) |
|
1008 |
val hyp_rhs = list_comb (f, ys) |
|
1009 |
val hyp_eq = |
|
1010 |
HOLogic.eq_const res_ty $ hyp_lhs $ hyp_rhs |
|
1011 |
val hyp = |
|
69597 | 1012 |
HOLogic.eq_const HOLogic.boolT $ hyp_eq $ \<^term>\<open>False\<close> |
55596 | 1013 |
|> HOLogic.mk_Trueprop |
1014 |
fun conc_eq i = |
|
1015 |
let |
|
69597 | 1016 |
val ty = TFree ("arg" ^ i ^ "_ty", \<^sort>\<open>type\<close>) |
55596 | 1017 |
val x = Free ("x" ^ i, ty) |
1018 |
val y = Free ("y" ^ i, ty) |
|
1019 |
val eq = HOLogic.eq_const ty $ x $ y |
|
1020 |
in |
|
69597 | 1021 |
HOLogic.eq_const HOLogic.boolT $ eq $ \<^term>\<open>False\<close> |
55596 | 1022 |
end |
1023 |
||
1024 |
val conc_disjs = map conc_eq is |
|
1025 |
||
1026 |
val conc = |
|
1027 |
if length conc_disjs = 1 then |
|
1028 |
the_single conc_disjs |
|
1029 |
else |
|
1030 |
fold |
|
1031 |
(fn t => fn t_conc => HOLogic.mk_disj (t_conc, t)) |
|
1032 |
(tl conc_disjs) (hd conc_disjs) |
|
1033 |
||
1034 |
val t = |
|
1035 |
Logic.mk_implies (hyp, HOLogic.mk_Trueprop conc) |
|
1036 |
in |
|
1037 |
Goal.prove ctxt [] [] t (fn _ => auto_tac ctxt) |
|
1038 |
|> Drule.export_without_context |
|
1039 |
end |
|
63167 | 1040 |
\<close> |
55596 | 1041 |
|
63167 | 1042 |
ML \<open> |
55596 | 1043 |
(*Determine the arity of a function which the "dec" |
1044 |
unification rule is about to be applied. |
|
1045 |
NOTE: |
|
1046 |
* Assumes that there is a single hypothesis |
|
1047 |
*) |
|
1048 |
fun find_dec_arity i = fn st => |
|
1049 |
let |
|
1050 |
val gls = |
|
59582 | 1051 |
Thm.prop_of st |
55596 | 1052 |
|> Logic.strip_horn |
1053 |
|> fst |
|
1054 |
in |
|
1055 |
if null gls then raise NO_GOALS |
|
1056 |
else |
|
1057 |
let |
|
1058 |
val (params, (literal, conc_clause)) = |
|
1059 |
rpair (i - 1) gls |
|
1060 |
|> uncurry nth |
|
1061 |
|> strip_top_all_vars [] |
|
1062 |
|> apsnd Logic.strip_horn |
|
1063 |
|> apsnd (apfst the_single) |
|
1064 |
||
1065 |
val get_ty = |
|
1066 |
HOLogic.dest_Trueprop |
|
1067 |
#> strip_top_All_vars |
|
1068 |
#> snd |
|
1069 |
#> HOLogic.dest_eq (*polarity's "="*) |
|
1070 |
#> fst |
|
1071 |
#> HOLogic.dest_eq (*the unification constraint's "="*) |
|
1072 |
#> fst |
|
1073 |
#> head_of |
|
80636
4041e7c8059d
tuned: more explicit dest_Const_name and dest_Const_type;
wenzelm
parents:
78727
diff
changeset
|
1074 |
#> dest_Const_type |
55596 | 1075 |
|
1076 |
fun arity_of ty = |
|
1077 |
let |
|
1078 |
val (_, res_ty) = dest_funT ty |
|
1079 |
||
1080 |
in |
|
1081 |
1 + arity_of res_ty |
|
1082 |
end |
|
1083 |
handle (TYPE ("dest_funT", _, _)) => 0 |
|
1084 |
||
1085 |
in |
|
1086 |
arity_of (get_ty literal) |
|
1087 |
end |
|
1088 |
end |
|
1089 |
||
1090 |
(*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*) |
|
1091 |
fun breakdown_inference i = fn st => |
|
1092 |
let |
|
1093 |
val gls = |
|
59582 | 1094 |
Thm.prop_of st |
55596 | 1095 |
|> Logic.strip_horn |
1096 |
|> fst |
|
1097 |
in |
|
1098 |
if null gls then raise NO_GOALS |
|
1099 |
else |
|
1100 |
rpair (i - 1) gls |
|
1101 |
|> uncurry nth |
|
1102 |
|> strip_top_all_vars [] |
|
1103 |
end |
|
1104 |
||
1105 |
(*build a custom elimination rule for extuni_dec, and instantiate it to match a specific subgoal*) |
|
1106 |
fun extuni_dec_elim_rule ctxt arity i = fn st => |
|
1107 |
let |
|
1108 |
val rule = extuni_dec_n ctxt arity |
|
1109 |
||
1110 |
val rule_hyp = |
|
59582 | 1111 |
Thm.prop_of rule |
55596 | 1112 |
|> Logic.dest_implies |
1113 |
|> fst (*assuming that rule has single hypothesis*) |
|
1114 |
||
1115 |
(*having run break_hypothesis earlier, we know that the hypothesis |
|
1116 |
now consists of a single literal. We can (and should) |
|
1117 |
disregard the conclusion, since it hasn't been "broken", |
|
1118 |
and it might include some unwanted literals -- the latter |
|
1119 |
could cause "diff" to fail (since they won't agree with the |
|
1120 |
rule we have generated.*) |
|
1121 |
||
1122 |
val inference_hyp = |
|
1123 |
snd (breakdown_inference i st) |
|
1124 |
|> Logic.dest_implies |
|
1125 |
|> fst (*assuming that inference has single hypothesis, |
|
1126 |
as explained above.*) |
|
1127 |
in |
|
1128 |
TPTP_Reconstruct_Library.diff_and_instantiate ctxt rule rule_hyp inference_hyp |
|
1129 |
end |
|
1130 |
||
1131 |
fun extuni_dec_tac ctxt i = fn st => |
|
1132 |
let |
|
1133 |
val arity = find_dec_arity i st |
|
1134 |
||
1135 |
fun elim_tac i st = |
|
1136 |
let |
|
1137 |
val rule = |
|
1138 |
extuni_dec_elim_rule ctxt arity i st |
|
1139 |
(*in case we itroduced free variables during |
|
1140 |
instantiation, we generalise the rule to make |
|
1141 |
those free variables into logical variables.*) |
|
1142 |
|> Thm.forall_intr_frees |
|
1143 |
|> Drule.export_without_context |
|
60754 | 1144 |
in dresolve_tac ctxt [rule] i st end |
55596 | 1145 |
handle NO_GOALS => no_tac st |
1146 |
||
1147 |
fun closure tac = |
|
1148 |
(*batter fails if there's no toplevel disjunction in the |
|
1149 |
hypothesis, so we also try atac*) |
|
59533 | 1150 |
SOLVE o (tac THEN' (batter_tac ctxt ORELSE' assume_tac ctxt)) |
55596 | 1151 |
val search_tac = |
1152 |
ASAP |
|
60754 | 1153 |
(resolve_tac ctxt @{thms disjI1} APPEND' resolve_tac ctxt @{thms disjI2}) |
55596 | 1154 |
(FIRST' (map closure |
59498
50b60f501b05
proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents:
58941
diff
changeset
|
1155 |
[dresolve_tac ctxt @{thms dec_commut_eq}, |
60754 | 1156 |
dresolve_tac ctxt @{thms dec_commut_disj}, |
55596 | 1157 |
elim_tac])) |
1158 |
in |
|
1159 |
(CHANGED o search_tac) i st |
|
1160 |
end |
|
63167 | 1161 |
\<close> |
55596 | 1162 |
|
1163 |
||
1164 |
subsubsection "standard_cnf" |
|
1165 |
(*Given a standard_cnf inference, normalise it |
|
1166 |
e.g. ((A & B) & C \<longrightarrow> D & E \<longrightarrow> F \<longrightarrow> G) = False |
|
1167 |
is changed to |
|
1168 |
(A & B & C & D & E & F \<longrightarrow> G) = False |
|
1169 |
then custom-build a metatheorem which validates this: |
|
1170 |
(A & B & C & D & E & F \<longrightarrow> G) = False |
|
1171 |
------------------------------------------- |
|
1172 |
(A = True) & (B = True) & (C = True) & |
|
1173 |
(D = True) & (E = True) & (F = True) & (G = False) |
|
1174 |
and apply this metatheorem. |
|
1175 |
||
1176 |
There aren't any "positive" standard_cnfs in Leo2's calculus: |
|
1177 |
e.g., "(A \<longrightarrow> B) = True \<Longrightarrow> A = False | (A = True & B = True)" |
|
1178 |
since "standard_cnf" seems to be applied at the preprocessing |
|
1179 |
stage, together with splitting. |
|
1180 |
*) |
|
1181 |
||
63167 | 1182 |
ML \<open> |
55596 | 1183 |
(*Conjunctive counterparts to Term.disjuncts_aux and Term.disjuncts*) |
69597 | 1184 |
fun conjuncts_aux (Const (\<^const_name>\<open>HOL.conj\<close>, _) $ t $ t') conjs = |
55596 | 1185 |
conjuncts_aux t (conjuncts_aux t' conjs) |
1186 |
| conjuncts_aux t conjs = t :: conjs |
|
1187 |
||
1188 |
fun conjuncts t = conjuncts_aux t [] |
|
1189 |
||
1190 |
(*HOL equivalent of Logic.strip_horn*) |
|
1191 |
local |
|
69597 | 1192 |
fun imp_strip_horn' acc (Const (\<^const_name>\<open>HOL.implies\<close>, _) $ A $ B) = |
55596 | 1193 |
imp_strip_horn' (A :: acc) B |
1194 |
| imp_strip_horn' acc t = (acc, t) |
|
1195 |
in |
|
1196 |
fun imp_strip_horn t = |
|
1197 |
imp_strip_horn' [] t |
|
1198 |
|> apfst rev |
|
1199 |
end |
|
63167 | 1200 |
\<close> |
55596 | 1201 |
|
63167 | 1202 |
ML \<open> |
55596 | 1203 |
(*Returns whether the antecedents are separated by conjunctions |
1204 |
or implications; the number of antecedents; and the polarity |
|
1205 |
of the original clause -- I think this will always be "false".*) |
|
1206 |
fun standard_cnf_type ctxt i : thm -> (TPTP_Reconstruct.formula_kind * int * bool) option = fn st => |
|
1207 |
let |
|
1208 |
val gls = |
|
59582 | 1209 |
Thm.prop_of st |
55596 | 1210 |
|> Logic.strip_horn |
1211 |
|> fst |
|
1212 |
||
1213 |
val hypos = |
|
1214 |
if null gls then raise NO_GOALS |
|
1215 |
else |
|
1216 |
rpair (i - 1) gls |
|
1217 |
|> uncurry nth |
|
1218 |
|> TPTP_Reconstruct.strip_top_all_vars [] |
|
1219 |
|> snd |
|
1220 |
|> Logic.strip_horn |
|
1221 |
|> fst |
|
1222 |
||
1223 |
(*hypothesis clause should be singleton*) |
|
69597 | 1224 |
val _ = \<^assert> (length hypos = 1) |
55596 | 1225 |
|
1226 |
val (t, pol) = the_single hypos |
|
1227 |
|> try_dest_Trueprop |
|
1228 |
|> TPTP_Reconstruct.strip_top_All_vars |
|
1229 |
|> snd |
|
1230 |
|> TPTP_Reconstruct.remove_polarity true |
|
1231 |
||
1232 |
(*literal is negative*) |
|
69597 | 1233 |
val _ = \<^assert> (not pol) |
55596 | 1234 |
|
1235 |
val (antes, conc) = imp_strip_horn t |
|
1236 |
||
1237 |
val (ante_type, antes') = |
|
1238 |
if length antes = 1 then |
|
1239 |
let |
|
1240 |
val conjunctive_antes = |
|
1241 |
the_single antes |
|
1242 |
|> conjuncts |
|
1243 |
in |
|
1244 |
if length conjunctive_antes > 1 then |
|
1245 |
(TPTP_Reconstruct.Conjunctive NONE, |
|
1246 |
conjunctive_antes) |
|
1247 |
else |
|
1248 |
(TPTP_Reconstruct.Implicational NONE, |
|
1249 |
antes) |
|
1250 |
end |
|
1251 |
else |
|
1252 |
(TPTP_Reconstruct.Implicational NONE, |
|
1253 |
antes) |
|
1254 |
in |
|
1255 |
if null antes then NONE |
|
1256 |
else SOME (ante_type, length antes', pol) |
|
1257 |
end |
|
63167 | 1258 |
\<close> |
55596 | 1259 |
|
63167 | 1260 |
ML \<open> |
55596 | 1261 |
(*Given a certain standard_cnf type, build a metatheorem that would |
1262 |
validate it*) |
|
1263 |
fun mk_standard_cnf ctxt kind arity = |
|
1264 |
let |
|
69597 | 1265 |
val _ = \<^assert> (arity > 0) |
55596 | 1266 |
val vars = |
67404 | 1267 |
1 upto (arity + 1) |
55596 | 1268 |
|> map (fn i => Free ("x" ^ Int.toString i, HOLogic.boolT)) |
1269 |
||
1270 |
val consequent = hd vars |
|
1271 |
val antecedents = tl vars |
|
1272 |
||
1273 |
val conc = |
|
1274 |
fold |
|
1275 |
(curry HOLogic.mk_conj) |
|
69597 | 1276 |
(map (fn var => HOLogic.mk_eq (var, \<^term>\<open>True\<close>)) antecedents) |
1277 |
(HOLogic.mk_eq (consequent, \<^term>\<open>False\<close>)) |
|
55596 | 1278 |
|
1279 |
val pre_hyp = |
|
1280 |
case kind of |
|
1281 |
TPTP_Reconstruct.Conjunctive NONE => |
|
1282 |
curry HOLogic.mk_imp |
|
1283 |
(if length antecedents = 1 then the_single antecedents |
|
1284 |
else |
|
1285 |
fold (curry HOLogic.mk_conj) (tl antecedents) (hd antecedents)) |
|
1286 |
(hd vars) |
|
1287 |
| TPTP_Reconstruct.Implicational NONE => |
|
1288 |
fold (curry HOLogic.mk_imp) antecedents consequent |
|
1289 |
||
69597 | 1290 |
val hyp = HOLogic.mk_eq (pre_hyp, \<^term>\<open>False\<close>) |
55596 | 1291 |
|
1292 |
val t = |
|
1293 |
Logic.mk_implies (HOLogic.mk_Trueprop hyp, HOLogic.mk_Trueprop conc) |
|
1294 |
in |
|
1295 |
Goal.prove ctxt [] [] t (fn _ => HEADGOAL (blast_tac ctxt)) |
|
1296 |
|> Drule.export_without_context |
|
1297 |
end |
|
63167 | 1298 |
\<close> |
55596 | 1299 |
|
63167 | 1300 |
ML \<open> |
55596 | 1301 |
(*Applies a d-tactic, then breaks it up conjunctively. |
1302 |
This can be used to transform subgoals as follows: |
|
1303 |
(A \<longrightarrow> B) = False \<Longrightarrow> R |
|
1304 |
| |
|
1305 |
v |
|
1306 |
\<lbrakk>A = True; B = False\<rbrakk> \<Longrightarrow> R |
|
1307 |
*) |
|
60754 | 1308 |
fun weak_conj_tac ctxt drule = |
1309 |
dresolve_tac ctxt [drule] THEN' |
|
1310 |
(REPEAT_DETERM o eresolve_tac ctxt @{thms conjE}) |
|
63167 | 1311 |
\<close> |
55596 | 1312 |
|
63167 | 1313 |
ML \<open> |
60754 | 1314 |
fun uncurry_lit_neg_tac ctxt = |
1315 |
REPEAT_DETERM o |
|
1316 |
dresolve_tac ctxt [@{lemma "(A \<longrightarrow> B \<longrightarrow> C) = False \<Longrightarrow> (A & B \<longrightarrow> C) = False" by auto}] |
|
63167 | 1317 |
\<close> |
55596 | 1318 |
|
63167 | 1319 |
ML \<open> |
55596 | 1320 |
fun standard_cnf_tac ctxt i = fn st => |
1321 |
let |
|
1322 |
fun core_tactic i = fn st => |
|
1323 |
case standard_cnf_type ctxt i st of |
|
1324 |
NONE => no_tac st |
|
1325 |
| SOME (kind, arity, _) => |
|
1326 |
let |
|
1327 |
val rule = mk_standard_cnf ctxt kind arity; |
|
1328 |
in |
|
60754 | 1329 |
(weak_conj_tac ctxt rule THEN' assume_tac ctxt) i st |
55596 | 1330 |
end |
1331 |
in |
|
60754 | 1332 |
(uncurry_lit_neg_tac ctxt |
55596 | 1333 |
THEN' TPTP_Reconstruct_Library.reassociate_conjs_tac ctxt |
1334 |
THEN' core_tactic) i st |
|
1335 |
end |
|
63167 | 1336 |
\<close> |
55596 | 1337 |
|
1338 |
||
1339 |
subsubsection "Emulator prep" |
|
1340 |
||
63167 | 1341 |
ML \<open> |
55596 | 1342 |
datatype cleanup_feature = |
1343 |
RemoveHypothesesFromSkolemDefs |
|
1344 |
| RemoveDuplicates |
|
1345 |
||
1346 |
datatype loop_feature = |
|
1347 |
Close_Branch |
|
1348 |
| ConjI |
|
1349 |
| King_Cong |
|
1350 |
| Break_Hypotheses |
|
1351 |
| Donkey_Cong (*simper_animal + ex_expander_tac*) |
|
1352 |
| RemoveRedundantQuantifications |
|
1353 |
| Assumption |
|
1354 |
||
1355 |
(*Closely based on Leo2 calculus*) |
|
1356 |
| Existential_Free |
|
1357 |
| Existential_Var |
|
1358 |
| Universal |
|
1359 |
| Not_pos |
|
1360 |
| Not_neg |
|
1361 |
| Or_pos |
|
1362 |
| Or_neg |
|
1363 |
| Equal_pos |
|
1364 |
| Equal_neg |
|
1365 |
| Extuni_Bool2 |
|
1366 |
| Extuni_Bool1 |
|
1367 |
| Extuni_Dec |
|
1368 |
| Extuni_Bind |
|
1369 |
| Extuni_Triv |
|
1370 |
| Extuni_FlexRigid |
|
1371 |
| Extuni_Func |
|
1372 |
| Polarity_switch |
|
1373 |
| Forall_special_pos |
|
1374 |
||
1375 |
datatype feature = |
|
1376 |
ConstsDiff |
|
1377 |
| StripQuantifiers |
|
1378 |
| Flip_Conclusion |
|
1379 |
| Loop of loop_feature list |
|
1380 |
| LoopOnce of loop_feature list |
|
1381 |
| InnerLoopOnce of loop_feature list |
|
1382 |
| CleanUp of cleanup_feature list |
|
1383 |
| AbsorbSkolemDefs |
|
63167 | 1384 |
\<close> |
55596 | 1385 |
|
63167 | 1386 |
ML \<open> |
55596 | 1387 |
fun can_feature x l = |
1388 |
let |
|
1389 |
fun sublist_of_clean_up el = |
|
1390 |
case el of |
|
1391 |
CleanUp l'' => SOME l'' |
|
1392 |
| _ => NONE |
|
1393 |
fun sublist_of_loop el = |
|
1394 |
case el of |
|
1395 |
Loop l'' => SOME l'' |
|
1396 |
| _ => NONE |
|
1397 |
fun sublist_of_loop_once el = |
|
1398 |
case el of |
|
1399 |
LoopOnce l'' => SOME l'' |
|
1400 |
| _ => NONE |
|
1401 |
fun sublist_of_inner_loop_once el = |
|
1402 |
case el of |
|
1403 |
InnerLoopOnce l'' => SOME l'' |
|
1404 |
| _ => NONE |
|
1405 |
||
1406 |
fun check_sublist sought_sublist opt_list = |
|
58412 | 1407 |
if forall is_none opt_list then false |
55596 | 1408 |
else |
1409 |
fold_options opt_list |
|
58411 | 1410 |
|> flat |
55596 | 1411 |
|> pair sought_sublist |
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
1412 |
|> subset (op =) |
55596 | 1413 |
in |
1414 |
case x of |
|
1415 |
CleanUp l' => |
|
1416 |
map sublist_of_clean_up l |
|
1417 |
|> check_sublist l' |
|
1418 |
| Loop l' => |
|
1419 |
map sublist_of_loop l |
|
1420 |
|> check_sublist l' |
|
1421 |
| LoopOnce l' => |
|
1422 |
map sublist_of_loop_once l |
|
1423 |
|> check_sublist l' |
|
1424 |
| InnerLoopOnce l' => |
|
1425 |
map sublist_of_inner_loop_once l |
|
1426 |
|> check_sublist l' |
|
67405
e9ab4ad7bd15
uniform use of Standard ML op-infix -- eliminated warnings;
wenzelm
parents:
67404
diff
changeset
|
1427 |
| _ => exists (curry (op =) x) l |
55596 | 1428 |
end; |
1429 |
||
1430 |
fun loop_can_feature loop_feats l = |
|
1431 |
can_feature (Loop loop_feats) l orelse |
|
1432 |
can_feature (LoopOnce loop_feats) l orelse |
|
1433 |
can_feature (InnerLoopOnce loop_feats) l; |
|
1434 |
||
69597 | 1435 |
\<^assert> (can_feature ConstsDiff [StripQuantifiers, ConstsDiff]); |
55596 | 1436 |
|
69597 | 1437 |
\<^assert> |
55596 | 1438 |
(can_feature (CleanUp [RemoveHypothesesFromSkolemDefs]) |
1439 |
[CleanUp [RemoveHypothesesFromSkolemDefs, RemoveDuplicates]]); |
|
1440 |
||
69597 | 1441 |
\<^assert> |
55596 | 1442 |
(can_feature (Loop []) [Loop [Existential_Var]]); |
1443 |
||
69597 | 1444 |
\<^assert> |
55596 | 1445 |
(not (can_feature (Loop []) [InnerLoopOnce [Existential_Var]])); |
63167 | 1446 |
\<close> |
55596 | 1447 |
|
63167 | 1448 |
ML \<open> |
55596 | 1449 |
exception NO_LOOP_FEATS |
1450 |
fun get_loop_feats (feats : feature list) = |
|
1451 |
let |
|
1452 |
val loop_find = |
|
1453 |
fold (fn x => fn loop_feats_acc => |
|
1454 |
if is_some loop_feats_acc then loop_feats_acc |
|
1455 |
else |
|
1456 |
case x of |
|
1457 |
Loop loop_feats => SOME loop_feats |
|
1458 |
| LoopOnce loop_feats => SOME loop_feats |
|
1459 |
| InnerLoopOnce loop_feats => SOME loop_feats |
|
1460 |
| _ => NONE) |
|
1461 |
feats |
|
1462 |
NONE |
|
1463 |
in |
|
1464 |
if is_some loop_find then the loop_find |
|
1465 |
else raise NO_LOOP_FEATS |
|
1466 |
end; |
|
1467 |
||
69597 | 1468 |
\<^assert> |
55596 | 1469 |
(get_loop_feats [Loop [King_Cong, Break_Hypotheses, Existential_Free, Existential_Var, Universal]] = |
1470 |
[King_Cong, Break_Hypotheses, Existential_Free, Existential_Var, Universal]) |
|
63167 | 1471 |
\<close> |
55596 | 1472 |
|
1473 |
(*use as elim rule to remove premises*) |
|
1474 |
lemma insa_prems: "\<lbrakk>Q; P\<rbrakk> \<Longrightarrow> P" by auto |
|
63167 | 1475 |
ML \<open> |
60754 | 1476 |
fun cleanup_skolem_defs ctxt feats = |
55596 | 1477 |
let |
1478 |
(*remove hypotheses from skolem defs, |
|
1479 |
after testing that they look like skolem defs*) |
|
1480 |
val dehypothesise_skolem_defs = |
|
1481 |
COND' (SOME #> TERMPRED (fn _ => true) conc_is_skolem_def) |
|
60754 | 1482 |
(REPEAT_DETERM o eresolve_tac ctxt @{thms insa_prems}) |
55596 | 1483 |
(K no_tac) |
1484 |
in |
|
1485 |
if can_feature (CleanUp [RemoveHypothesesFromSkolemDefs]) feats then |
|
1486 |
ALLGOALS (TRY o dehypothesise_skolem_defs) |
|
1487 |
else all_tac |
|
1488 |
end |
|
63167 | 1489 |
\<close> |
55596 | 1490 |
|
63167 | 1491 |
ML \<open> |
55596 | 1492 |
fun remove_duplicates_tac feats = |
1493 |
(if can_feature (CleanUp [RemoveDuplicates]) feats then |
|
68825
8207c67d9ef4
clarified signature: do not expose internal operation;
wenzelm
parents:
67613
diff
changeset
|
1494 |
distinct_subgoals_tac |
55596 | 1495 |
else all_tac) |
63167 | 1496 |
\<close> |
55596 | 1497 |
|
63167 | 1498 |
ML \<open> |
55596 | 1499 |
(*given a goal state, indicates the skolem constants committed-to in it (i.e. appearing in LHS of a skolem definition)*) |
60754 | 1500 |
fun which_skolem_concs_used ctxt = fn st => |
55596 | 1501 |
let |
1502 |
val feats = [CleanUp [RemoveHypothesesFromSkolemDefs, RemoveDuplicates]] |
|
1503 |
val scrubup_tac = |
|
60754 | 1504 |
cleanup_skolem_defs ctxt feats |
55596 | 1505 |
THEN remove_duplicates_tac feats |