1 (* Title: HOL/Tools/ATP/atp_proof_reconstruct.ML
2 Author: Lawrence C. Paulson, Cambridge University Computer Laboratory
3 Author: Claire Quigley, Cambridge University Computer Laboratory
4 Author: Jasmin Blanchette, TU Muenchen
6 Basic proof reconstruction from ATP proofs.
9 signature ATP_PROOF_RECONSTRUCT =
11 type ('a, 'b) atp_term = ('a, 'b) ATP_Problem.atp_term
12 type ('a, 'b, 'c, 'd) atp_formula = ('a, 'b, 'c, 'd) ATP_Problem.atp_formula
13 type stature = ATP_Problem_Generate.stature
14 type ('a, 'b) atp_step = ('a, 'b) ATP_Proof.atp_step
15 type 'a atp_proof = 'a ATP_Proof.atp_proof
18 val full_typesN : string
19 val partial_typesN : string
20 val no_typesN : string
21 val really_full_type_enc : string
22 val full_type_enc : string
23 val partial_type_enc : string
24 val no_type_enc : string
25 val full_type_encs : string list
26 val partial_type_encs : string list
27 val default_metis_lam_trans : string
28 val metis_call : string -> string -> string
29 val forall_of : term -> term -> term
30 val exists_of : term -> term -> term
31 val unalias_type_enc : string -> string list
32 val term_of_atp : Proof.context -> bool -> int Symtab.table -> typ option ->
33 (string, string) atp_term -> term
34 val prop_of_atp : Proof.context -> bool -> int Symtab.table ->
35 (string, string, (string, string) atp_term, string) atp_formula -> term
37 val used_facts_in_atp_proof :
38 Proof.context -> (string * stature) list vector -> string atp_proof -> (string * stature) list
39 val used_facts_in_unsound_atp_proof : Proof.context -> (string * stature) list vector ->
40 'a atp_proof -> string list option
41 val lam_trans_of_atp_proof : string atp_proof -> string -> string
42 val is_typed_helper_used_in_atp_proof : string atp_proof -> bool
43 val termify_atp_proof : Proof.context -> string Symtab.table -> (string * term) list ->
44 int Symtab.table -> string atp_proof -> (term, string) atp_step list
45 val factify_atp_proof : (string * 'a) list vector -> term list -> term ->
46 (term, string) atp_step list -> (term, string) atp_step list
49 structure ATP_Proof_Reconstruct : ATP_PROOF_RECONSTRUCT =
55 open ATP_Problem_Generate
59 val full_typesN = "full_types"
60 val partial_typesN = "partial_types"
61 val no_typesN = "no_types"
63 val really_full_type_enc = "mono_tags"
64 val full_type_enc = "poly_guards_query"
65 val partial_type_enc = "poly_args"
66 val no_type_enc = "erased"
68 val full_type_encs = [full_type_enc, really_full_type_enc]
69 val partial_type_encs = partial_type_enc :: full_type_encs
71 val type_enc_aliases =
72 [(full_typesN, full_type_encs),
73 (partial_typesN, partial_type_encs),
74 (no_typesN, [no_type_enc])]
76 fun unalias_type_enc s =
77 AList.lookup (op =) type_enc_aliases s |> the_default [s]
79 val default_metis_lam_trans = combsN
81 fun metis_call type_enc lam_trans =
84 case AList.find (fn (enc, encs) => enc = hd encs) type_enc_aliases
88 val opts = [] |> type_enc <> partial_typesN ? cons type_enc
89 |> lam_trans <> default_metis_lam_trans ? cons lam_trans
90 in metisN ^ (if null opts then "" else " (" ^ commas opts ^ ")") end
92 fun term_name' (Var ((s, _), _)) = perhaps (try Name.dest_skolem) s
95 fun lambda' v = Term.lambda_name (term_name' v, v)
97 fun forall_of v t = HOLogic.all_const (fastype_of v) $ lambda' v t
98 fun exists_of v t = HOLogic.exists_const (fastype_of v) $ lambda' v t
100 fun make_tfree ctxt w =
101 let val ww = "'" ^ w in
102 TFree (ww, the_default HOLogic.typeS (Variable.def_sort ctxt (ww, ~1)))
105 exception ATP_TERM of (string, string) atp_term list
106 exception ATP_FORMULA of
107 (string, string, (string, string) atp_term, string) atp_formula list
108 exception SAME of unit
110 (* Type variables are given the basic sort "HOL.type". Some will later be
111 constrained by information from type literals, or by type inference. *)
112 fun typ_of_atp ctxt (u as ATerm ((a, _), us)) =
113 let val Ts = map (typ_of_atp ctxt) us in
114 case unprefix_and_unascii type_const_prefix a of
115 SOME b => Type (invert_const b, Ts)
117 if not (null us) then
118 raise ATP_TERM [u] (* only "tconst"s have type arguments *)
119 else case unprefix_and_unascii tfree_prefix a of
120 SOME b => make_tfree ctxt b
122 (* Could be an Isabelle variable or a variable from the ATP, say "X1"
123 or "_5018". Sometimes variables from the ATP are indistinguishable
124 from Isabelle variables, which forces us to use a type parameter in
126 (a |> perhaps (unprefix_and_unascii tvar_prefix), HOLogic.typeS)
127 |> Type_Infer.param 0
130 (* Type class literal applied to a type. Returns triple of polarity, class,
132 fun type_constraint_of_term ctxt (u as ATerm ((a, _), us)) =
133 case (unprefix_and_unascii class_prefix a, map (typ_of_atp ctxt) us) of
134 (SOME b, [T]) => (b, T)
135 | _ => raise ATP_TERM [u]
137 (* Accumulate type constraints in a formula: negative type literals. *)
138 fun add_var (key, z) = Vartab.map_default (key, []) (cons z)
139 fun add_type_constraint false (cl, TFree (a ,_)) = add_var ((a, ~1), cl)
140 | add_type_constraint false (cl, TVar (ix, _)) = add_var (ix, cl)
141 | add_type_constraint _ _ = I
143 fun repair_var_name s =
145 fun subscript_name s n = s ^ nat_subscript n
146 val s = s |> String.map Char.toLower
148 case space_explode "_" s of
149 [_] => (case take_suffix Char.isDigit (String.explode s) of
150 (cs1 as _ :: _, cs2 as _ :: _) =>
151 subscript_name (String.implode cs1)
152 (the (Int.fromString (String.implode cs2)))
154 | [s1, s2] => (case Int.fromString s2 of
155 SOME n => subscript_name s1 n
160 (* The number of type arguments of a constant, zero if it's monomorphic. For
161 (instances of) Skolem pseudoconstants, this information is encoded in the
163 fun robust_const_num_type_args thy s =
164 if String.isPrefix skolem_const_prefix s then
165 s |> Long_Name.explode |> List.last |> Int.fromString |> the
166 else if String.isPrefix lam_lifted_prefix s then
167 if String.isPrefix lam_lifted_poly_prefix s then 2 else 0
169 (s, Sign.the_const_type thy s) |> Sign.const_typargs thy |> length
171 fun slack_fastype_of t = fastype_of t handle TERM _ => HOLogic.typeT
173 (* Cope with "tt(X) = X" atoms, where "X" is existentially quantified. *)
174 fun loose_aconv (Free (s, _), Free (s', _)) = s = s'
175 | loose_aconv (t, t') = t aconv t'
177 val vampire_skolem_prefix = "sK"
179 (* First-order translation. No types are known for variables. "HOLogic.typeT"
180 should allow them to be inferred. *)
181 fun term_of_atp ctxt textual sym_tab =
183 val thy = Proof_Context.theory_of ctxt
184 (* For Metis, we use 1 rather than 0 because variable references in clauses
185 may otherwise conflict with variable constraints in the goal. At least,
186 type inference often fails otherwise. See also "axiom_inference" in
187 "Metis_Reconstruct". *)
188 val var_index = if textual then 0 else 1
189 fun do_term extra_ts opt_T u =
191 ATerm ((s, _), us) =>
193 then error "Isar proof reconstruction failed because the ATP proof \
194 \contains unparsable material."
195 else if String.isPrefix native_type_prefix s then
196 @{const True} (* ignore TPTP type information *)
197 else if s = tptp_equal then
198 let val ts = map (do_term [] NONE) us in
199 if textual andalso length ts = 2 andalso
200 loose_aconv (hd ts, List.last ts) then
203 list_comb (Const (@{const_name HOL.eq}, HOLogic.typeT), ts)
205 else case unprefix_and_unascii const_prefix s of
208 val ((s', s''), mangled_us) =
209 s' |> unmangled_const |>> `invert_const
211 if s' = type_tag_name then
212 case mangled_us @ us of
214 do_term extra_ts (SOME (typ_of_atp ctxt typ_u)) term_u
215 | _ => raise ATP_TERM us
216 else if s' = predicator_name then
217 do_term [] (SOME @{typ bool}) (hd us)
218 else if s' = app_op_name then
219 let val extra_t = do_term [] NONE (List.last us) in
220 do_term (extra_t :: extra_ts)
222 SOME T => SOME (slack_fastype_of extra_t --> T)
224 (nth us (length us - 2))
226 else if s' = type_guard_name then
227 @{const True} (* ignore type predicates *)
230 val new_skolem = String.isPrefix new_skolem_const_prefix s''
232 length us - the_default 0 (Symtab.lookup sym_tab s)
233 val (type_us, term_us) =
234 chop num_ty_args us |>> append mangled_us
235 val term_ts = map (do_term [] NONE) term_us
237 (if not (null type_us) andalso
238 robust_const_num_type_args thy s' = length type_us then
239 let val Ts = type_us |> map (typ_of_atp ctxt) in
241 SOME (Type_Infer.paramify_vars (tl Ts ---> hd Ts))
243 try (Sign.const_instance thy) (s', Ts)
250 | NONE => map slack_fastype_of term_ts --->
253 | NONE => HOLogic.typeT))
256 Var ((new_skolem_var_name_of_const s'', var_index), T)
258 Const (unproxify_const s', T)
259 in list_comb (t, term_ts @ extra_ts) end
261 | NONE => (* a free or schematic variable *)
263 (* This assumes that distinct names are mapped to distinct names by
264 "Variable.variant_frees". This does not hold in general but
265 should hold for ATP-generated Skolem function names, since these
266 end with a digit and "variant_frees" appends letters. *)
268 [(s, ())] |> Variable.variant_frees ctxt [] |> hd |> fst
270 map (do_term [] NONE) us
271 (* Vampire (2.6) passes arguments to Skolem functions in reverse
273 |> String.isPrefix vampire_skolem_prefix s ? rev
274 val ts = term_ts @ extra_ts
277 SOME T => map slack_fastype_of term_ts ---> T
278 | NONE => map slack_fastype_of ts ---> HOLogic.typeT
280 case unprefix_and_unascii fixed_var_prefix s of
281 SOME s => Free (s, T)
283 case unprefix_and_unascii schematic_var_prefix s of
284 SOME s => Var ((s, var_index), T)
286 if textual andalso not (is_tptp_variable s) then
287 Free (s |> textual ? (repair_var_name #> fresh_up), T)
289 Var ((s |> textual ? repair_var_name, var_index), T)
290 in list_comb (t, ts) end
293 fun term_of_atom ctxt textual sym_tab pos (u as ATerm ((s, _), _)) =
294 if String.isPrefix class_prefix s then
295 add_type_constraint pos (type_constraint_of_term ctxt u)
296 #> pair @{const True}
298 pair (term_of_atp ctxt textual sym_tab (SOME @{typ bool}) u)
300 (* Update schematic type variables with detected sort constraints. It's not
301 totally clear whether this code is necessary. *)
302 fun repair_tvar_sorts (t, tvar_tab) =
304 fun do_type (Type (a, Ts)) = Type (a, map do_type Ts)
305 | do_type (TVar (xi, s)) =
306 TVar (xi, the_default s (Vartab.lookup tvar_tab xi))
307 | do_type (TFree z) = TFree z
308 fun do_term (Const (a, T)) = Const (a, do_type T)
309 | do_term (Free (a, T)) = Free (a, do_type T)
310 | do_term (Var (xi, T)) = Var (xi, do_type T)
311 | do_term (t as Bound _) = t
312 | do_term (Abs (a, T, t)) = Abs (a, do_type T, do_term t)
313 | do_term (t1 $ t2) = do_term t1 $ do_term t2
314 in t |> not (Vartab.is_empty tvar_tab) ? do_term end
316 fun quantify_over_var quant_of var_s t =
318 val vars = [] |> Term.add_vars t |> filter (fn ((s, _), _) => s = var_s)
319 val normTs = vars |> AList.group (op =) |> map (apsnd hd)
320 fun norm_var_types (Var (x, T)) =
321 Var (x, case AList.lookup (op =) normTs x of
324 | norm_var_types t = t
325 in t |> map_aterms norm_var_types |> fold_rev quant_of (map Var normTs) end
327 (* Interpret an ATP formula as a HOL term, extracting sort constraints as they
328 appear in the formula. *)
329 fun prop_of_atp ctxt textual sym_tab phi =
331 fun do_formula pos phi =
333 AQuant (_, [], phi) => do_formula pos phi
334 | AQuant (q, (s, _) :: xs, phi') =>
335 do_formula pos (AQuant (q, xs, phi'))
337 #>> quantify_over_var
338 (case q of AForall => forall_of | AExists => exists_of)
339 (s |> textual ? repair_var_name)
340 | AConn (ANot, [phi']) => do_formula (not pos) phi' #>> s_not
341 | AConn (c, [phi1, phi2]) =>
342 do_formula (pos |> c = AImplies ? not) phi1
343 ##>> do_formula pos phi2
349 | ANot => raise Fail "impossible connective")
350 | AAtom tm => term_of_atom ctxt textual sym_tab pos tm
351 | _ => raise ATP_FORMULA [phi]
352 in repair_tvar_sorts (do_formula true phi Vartab.empty) end
354 fun find_first_in_list_vector vec key =
355 Vector.foldl (fn (ps, NONE) => AList.lookup (op =) ps key
356 | (_, value) => value) NONE vec
358 val unprefix_fact_number = space_implode "_" o tl o space_explode "_"
360 fun resolve_one_named_fact fact_names s =
361 case try (unprefix fact_prefix) s of
363 let val s' = s' |> unprefix_fact_number |> unascii_of in
364 s' |> find_first_in_list_vector fact_names |> Option.map (pair s')
368 fun resolve_fact fact_names = map_filter (resolve_one_named_fact fact_names)
370 fun resolve_one_named_conjecture s =
371 case try (unprefix conjecture_prefix) s of
372 SOME s' => Int.fromString s'
375 val resolve_conjecture = map_filter resolve_one_named_conjecture
377 fun is_axiom_used_in_proof pred =
378 exists (fn ((_, ss), _, _, _, []) => exists pred ss | _ => false)
380 fun add_non_rec_defs fact_names accum =
381 Vector.foldl (fn (facts, facts') =>
382 union (op =) (filter (fn (_, (_, status)) => status = Non_Rec_Def) facts)
386 val isa_ext = Thm.get_name_hint @{thm ext}
387 val isa_short_ext = Long_Name.base_name isa_ext
390 if Thm.eq_thm_prop (@{thm ext},
391 singleton (Attrib.eval_thms ctxt) (Facts.named isa_short_ext, [])) then
396 val leo2_extcnf_equal_neg_rule = "extcnf_equal_neg"
397 val leo2_unfold_def_rule = "unfold_def"
399 fun add_fact ctxt fact_names ((_, ss), _, _, rule, deps) =
400 (if rule = leo2_extcnf_equal_neg_rule then
401 insert (op =) (ext_name ctxt, (Global, General))
402 else if rule = leo2_unfold_def_rule then
403 (* LEO 1.3.3 does not record definitions properly, leading to missing
404 dependencies in the TSTP proof. Remove the next line once this is
406 add_non_rec_defs fact_names
407 else if rule = agsyhol_coreN orelse rule = satallax_coreN then
409 (* agsyHOL and Satallax don't include definitions in their
410 unsatisfiable cores, so we assume the worst and include them all
412 [(ext_name ctxt, (Global, General))] |> add_non_rec_defs fact_names
416 #> (if null deps then union (op =) (resolve_fact fact_names ss) else I)
418 fun used_facts_in_atp_proof ctxt fact_names atp_proof =
419 if null atp_proof then Vector.foldl (uncurry (union (op =))) [] fact_names
420 else fold (add_fact ctxt fact_names) atp_proof []
422 fun used_facts_in_unsound_atp_proof _ _ [] = NONE
423 | used_facts_in_unsound_atp_proof ctxt fact_names atp_proof =
424 let val used_facts = used_facts_in_atp_proof ctxt fact_names atp_proof in
425 if forall (fn (_, (sc, _)) => sc = Global) used_facts andalso
426 not (is_axiom_used_in_proof (not o null o resolve_conjecture o single) atp_proof) then
427 SOME (map fst used_facts)
432 val ascii_of_lam_fact_prefix = ascii_of lam_fact_prefix
434 (* overapproximation (good enough) *)
435 fun is_lam_lifted s =
436 String.isPrefix fact_prefix s andalso
437 String.isSubstring ascii_of_lam_fact_prefix s
439 val is_combinator_def = String.isPrefix (helper_prefix ^ combinator_prefix)
441 fun lam_trans_of_atp_proof atp_proof default =
442 case (is_axiom_used_in_proof is_combinator_def atp_proof,
443 is_axiom_used_in_proof is_lam_lifted atp_proof) of
444 (false, false) => default
445 | (false, true) => liftingN
446 (* | (true, true) => combs_and_liftingN -- not supported by "metis" *)
447 | (true, _) => combsN
449 val is_typed_helper_name =
450 String.isPrefix helper_prefix andf String.isSuffix typed_helper_suffix
452 fun is_typed_helper_used_in_atp_proof atp_proof =
453 is_axiom_used_in_proof is_typed_helper_name atp_proof
455 fun repair_name "$true" = "c_True"
456 | repair_name "$false" = "c_False"
457 | repair_name "$$e" = tptp_equal (* seen in Vampire proofs *)
459 if is_tptp_equal s orelse
460 (* seen in Vampire proofs *)
461 (String.isPrefix "sQ" s andalso String.isSuffix "_eqProxy" s) then
466 fun infer_formula_types ctxt =
467 Type.constraint HOLogic.boolT
469 (Proof_Context.set_mode Proof_Context.mode_schematic ctxt)
471 val combinator_table =
472 [(@{const_name Meson.COMBI}, @{thm Meson.COMBI_def [abs_def]}),
473 (@{const_name Meson.COMBK}, @{thm Meson.COMBK_def [abs_def]}),
474 (@{const_name Meson.COMBB}, @{thm Meson.COMBB_def [abs_def]}),
475 (@{const_name Meson.COMBC}, @{thm Meson.COMBC_def [abs_def]}),
476 (@{const_name Meson.COMBS}, @{thm Meson.COMBS_def [abs_def]})]
478 fun uncombine_term thy =
480 fun aux (t1 $ t2) = betapply (pairself aux (t1, t2))
481 | aux (Abs (s, T, t')) = Abs (s, T, aux t')
482 | aux (t as Const (x as (s, _))) =
483 (case AList.lookup (op =) combinator_table s of
484 SOME thm => thm |> prop_of |> specialize_type thy x
485 |> Logic.dest_equals |> snd
490 fun unlift_term lifted =
491 map_aterms (fn t as Const (s, _) =>
492 if String.isPrefix lam_lifted_prefix s then
493 case AList.lookup (op =) lifted s of
495 (* FIXME: do something about the types *)
502 fun decode_line ctxt lifted sym_tab (name, role, u, rule, deps) =
504 val thy = Proof_Context.theory_of ctxt
506 u |> prop_of_atp ctxt true sym_tab
507 |> uncombine_term thy
508 |> unlift_term lifted
509 |> infer_formula_types ctxt
510 in (name, role, t, rule, deps) end
512 val waldmeister_conjecture_num = "1.0.0.0"
514 fun repair_waldmeister_endgame arg =
516 fun do_tail (name, _, t, rule, deps) =
517 (name, Negated_Conjecture, s_not t, rule, deps)
519 | do_body ((line as ((num, _), _, _, _, _)) :: lines) =
520 if num = waldmeister_conjecture_num then map do_tail (line :: lines)
521 else line :: do_body lines
524 fun termify_atp_proof ctxt pool lifted sym_tab =
525 clean_up_atp_proof_dependencies
526 #> nasty_atp_proof pool
527 #> map_term_names_in_atp_proof repair_name
528 #> map (decode_line ctxt lifted sym_tab)
529 #> repair_waldmeister_endgame
531 fun factify_atp_proof fact_names hyp_ts concl_t atp_proof =
533 fun factify_step ((num, ss), role, t, rule, deps) =
535 val (ss', role', t') =
536 (case resolve_conjecture ss of
538 if j = length hyp_ts then ([], Conjecture, concl_t) else ([], Hypothesis, nth hyp_ts j)
540 (case resolve_fact fact_names ss of
542 | facts => (map fst facts, Axiom, t)))
544 ((num, ss'), role', t', rule, deps)
547 val atp_proof = map factify_step atp_proof
548 val names = map #1 atp_proof
550 fun repair_dep (num, ss) = (num, the_default ss (AList.lookup (op =) names num))
551 fun repair_deps (name, role, t, rule, deps) = (name, role, t, rule, map repair_dep deps)
553 in map repair_deps atp_proof end