1 (* Title: HOL/Tools/Metis/metis_reconstruct.ML
2 Author: Kong W. Susanto, Cambridge University Computer Laboratory
3 Author: Lawrence C. Paulson, Cambridge University Computer Laboratory
4 Author: Jasmin Blanchette, TU Muenchen
5 Copyright Cambridge University 2007
7 Proof reconstruction for Metis.
10 signature METIS_RECONSTRUCT =
12 type mode = Metis_Translate.mode
14 exception METIS of string * string
16 val trace : bool Config.T
17 val trace_msg : Proof.context -> (unit -> string) -> unit
18 val verbose : bool Config.T
19 val verbose_warning : Proof.context -> string -> unit
20 val hol_clause_from_metis :
21 Proof.context -> int Symtab.table -> Metis_Thm.thm -> term
22 val lookth : (Metis_Thm.thm * 'a) list -> Metis_Thm.thm -> 'a
23 val untyped_aconv : term * term -> bool
24 val replay_one_inference :
25 Proof.context -> mode -> (string * term) list -> int Symtab.table
26 -> Metis_Thm.thm * Metis_Proof.inference -> (Metis_Thm.thm * thm) list
27 -> (Metis_Thm.thm * thm) list
28 val discharge_skolem_premises :
29 Proof.context -> (thm * term) option list -> thm -> thm
32 structure Metis_Reconstruct : METIS_RECONSTRUCT =
40 exception METIS of string * string
42 val trace = Attrib.setup_config_bool @{binding metis_trace} (K false)
43 val verbose = Attrib.setup_config_bool @{binding metis_verbose} (K true)
45 fun trace_msg ctxt msg = if Config.get ctxt trace then tracing (msg ()) else ()
46 fun verbose_warning ctxt msg =
47 if Config.get ctxt verbose then warning ("Metis: " ^ msg) else ()
49 datatype term_or_type = SomeTerm of term | SomeType of typ
52 | terms_of (SomeTerm t :: tts) = t :: terms_of tts
53 | terms_of (SomeType _ :: tts) = terms_of tts;
56 | types_of (SomeTerm (Var ((a, idx), _)) :: tts) =
58 |> (if String.isPrefix metis_generated_var_prefix a then
59 (* Variable generated by Metis, which might have been a type
61 cons (TVar (("'" ^ a, idx), HOLogic.typeS))
64 | types_of (SomeTerm _ :: tts) = types_of tts
65 | types_of (SomeType T :: tts) = T :: types_of tts
67 fun apply_list rator nargs rands =
68 let val trands = terms_of rands
69 in if length trands = nargs then SomeTerm (list_comb(rator, trands))
71 ("apply_list: wrong number of arguments: " ^ Syntax.string_of_term_global Pure.thy rator ^
72 " expected " ^ string_of_int nargs ^
73 " received " ^ commas (map (Syntax.string_of_term_global Pure.thy) trands))
76 fun infer_types ctxt =
77 Syntax.check_terms (Proof_Context.set_mode Proof_Context.mode_pattern ctxt);
79 (* We use 1 rather than 0 because variable references in clauses may otherwise
80 conflict with variable constraints in the goal...at least, type inference
81 often fails otherwise. See also "axiom_inf" below. *)
82 fun make_var (w, T) = Var ((w, 1), T)
84 (*Remove the "apply" operator from an HO term*)
85 fun strip_happ args (Metis_Term.Fn (".", [t, u])) = strip_happ (u :: args) t
86 | strip_happ args x = (x, args)
88 fun hol_type_from_metis_term _ (Metis_Term.Var v) =
89 (case strip_prefix_and_unascii tvar_prefix v of
91 | NONE => make_tvar v)
92 | hol_type_from_metis_term ctxt (Metis_Term.Fn(x, tys)) =
93 (case strip_prefix_and_unascii type_const_prefix x of
94 SOME tc => Type (invert_const tc,
95 map (hol_type_from_metis_term ctxt) tys)
97 case strip_prefix_and_unascii tfree_prefix x of
98 SOME tf => make_tfree ctxt tf
99 | NONE => raise Fail ("hol_type_from_metis_term: " ^ x));
101 (*Maps metis terms to isabelle terms*)
102 fun hol_term_from_metis_PT ctxt fol_tm =
103 let val thy = Proof_Context.theory_of ctxt
104 val _ = trace_msg ctxt (fn () => "hol_term_from_metis_PT: " ^
105 Metis_Term.toString fol_tm)
106 fun tm_to_tt (Metis_Term.Var v) =
107 (case strip_prefix_and_unascii tvar_prefix v of
108 SOME w => SomeType (make_tvar w)
110 case strip_prefix_and_unascii schematic_var_prefix v of
111 SOME w => SomeTerm (make_var (w, HOLogic.typeT))
112 | NONE => SomeTerm (make_var (v, HOLogic.typeT)))
113 (*Var from Metis with a name like _nnn; possibly a type variable*)
114 | tm_to_tt (Metis_Term.Fn ("{}", [arg])) = tm_to_tt arg (*hBOOL*)
115 | tm_to_tt (t as Metis_Term.Fn (".", _)) =
116 let val (rator,rands) = strip_happ [] t in
118 Metis_Term.Fn(fname,ts) => applic_to_tt (fname, ts @ rands)
119 | _ => case tm_to_tt rator of
120 SomeTerm t => SomeTerm (list_comb(t, terms_of (map tm_to_tt rands)))
121 | _ => raise Fail "tm_to_tt: HO application"
123 | tm_to_tt (Metis_Term.Fn (fname, args)) = applic_to_tt (fname,args)
124 and applic_to_tt ("=",ts) =
125 SomeTerm (list_comb(Const (@{const_name HOL.eq}, HOLogic.typeT), terms_of (map tm_to_tt ts)))
126 | applic_to_tt (a,ts) =
127 case strip_prefix_and_unascii const_prefix a of
130 val c = b |> invert_const |> unproxify_const
131 val ntypes = num_type_args thy c
132 val nterms = length ts - ntypes
133 val tts = map tm_to_tt ts
134 val tys = types_of (List.take(tts,ntypes))
136 if String.isPrefix new_skolem_const_prefix c then
137 Var ((new_skolem_var_name_from_const c, 1),
138 Type_Infer.paramify_vars (tl tys ---> hd tys))
141 in if length tys = ntypes then
142 apply_list t nterms (List.drop(tts,ntypes))
144 raise Fail ("Constant " ^ c ^ " expects " ^ string_of_int ntypes ^
145 " but gets " ^ string_of_int (length tys) ^
146 " type arguments\n" ^
147 cat_lines (map (Syntax.string_of_typ ctxt) tys) ^
148 " the terms are \n" ^
149 cat_lines (map (Syntax.string_of_term ctxt) (terms_of tts)))
151 | NONE => (*Not a constant. Is it a type constructor?*)
152 case strip_prefix_and_unascii type_const_prefix a of
154 SomeType (Type (invert_const b, types_of (map tm_to_tt ts)))
155 | NONE => (*Maybe a TFree. Should then check that ts=[].*)
156 case strip_prefix_and_unascii tfree_prefix a of
157 SOME b => SomeType (make_tfree ctxt b)
158 | NONE => (*a fixed variable? They are Skolem functions.*)
159 case strip_prefix_and_unascii fixed_var_prefix a of
161 let val opr = Free (b, HOLogic.typeT)
162 in apply_list opr (length ts) (map tm_to_tt ts) end
163 | NONE => raise Fail ("unexpected metis function: " ^ a)
165 case tm_to_tt fol_tm of
167 | SomeType T => raise TYPE ("fol_tm_to_tt: Term expected", [T], [])
170 (*Maps fully-typed metis terms to isabelle terms*)
171 fun hol_term_from_metis_FT ctxt fol_tm =
172 let val _ = trace_msg ctxt (fn () => "hol_term_from_metis_FT: " ^
173 Metis_Term.toString fol_tm)
175 let val c = c |> invert_const |> unproxify_const in
176 if String.isPrefix new_skolem_const_prefix c then
177 Var ((new_skolem_var_name_from_const c, 1), dummyT)
181 fun cvt (Metis_Term.Fn (":", [Metis_Term.Var v, _])) =
182 (case strip_prefix_and_unascii schematic_var_prefix v of
183 SOME w => make_var (w, dummyT)
184 | NONE => make_var (v, dummyT))
185 | cvt (Metis_Term.Fn (":", [Metis_Term.Fn ("=",[]), _])) =
186 Const (@{const_name HOL.eq}, HOLogic.typeT)
187 | cvt (Metis_Term.Fn (":", [Metis_Term.Fn (x,[]), ty])) =
188 (case strip_prefix_and_unascii const_prefix x of
190 | NONE => (*Not a constant. Is it a fixed variable??*)
191 case strip_prefix_and_unascii fixed_var_prefix x of
192 SOME v => Free (v, hol_type_from_metis_term ctxt ty)
193 | NONE => raise Fail ("hol_term_from_metis_FT bad constant: " ^ x))
194 | cvt (Metis_Term.Fn (":", [Metis_Term.Fn (".", [tm1,tm2]), _])) =
196 | cvt (Metis_Term.Fn (".",[tm1,tm2])) = (*untyped application*)
198 | cvt (Metis_Term.Fn ("{}", [arg])) = cvt arg (*hBOOL*)
199 | cvt (Metis_Term.Fn ("=", [tm1,tm2])) =
200 list_comb(Const (@{const_name HOL.eq}, HOLogic.typeT), map cvt [tm1,tm2])
201 | cvt (t as Metis_Term.Fn (x, [])) =
202 (case strip_prefix_and_unascii const_prefix x of
204 | NONE => (*Not a constant. Is it a fixed variable??*)
205 case strip_prefix_and_unascii fixed_var_prefix x of
206 SOME v => Free (v, dummyT)
207 | NONE => (trace_msg ctxt (fn () =>
208 "hol_term_from_metis_FT bad const: " ^ x);
209 hol_term_from_metis_PT ctxt t))
210 | cvt t = (trace_msg ctxt (fn () =>
211 "hol_term_from_metis_FT bad term: " ^ Metis_Term.toString t);
212 hol_term_from_metis_PT ctxt t)
215 fun atp_name_from_metis s =
216 case find_first (fn (_, (s', _)) => s' = s) metis_name_table of
217 SOME ((s, _), (_, swap)) => (s, swap)
219 fun atp_term_from_metis (Metis_Term.Fn (s, tms)) =
220 let val (s, swap) = atp_name_from_metis s in
221 ATerm (s, tms |> map atp_term_from_metis |> swap ? rev)
223 | atp_term_from_metis (Metis_Term.Var s) = ATerm (s, [])
225 fun hol_term_from_metis_MX ctxt sym_tab =
226 atp_term_from_metis #> term_from_atp ctxt false sym_tab NONE
228 fun hol_term_from_metis ctxt FO _ = hol_term_from_metis_PT ctxt
229 | hol_term_from_metis ctxt HO _ = hol_term_from_metis_PT ctxt
230 | hol_term_from_metis ctxt FT _ = hol_term_from_metis_FT ctxt
231 | hol_term_from_metis ctxt MX sym_tab = hol_term_from_metis_MX ctxt sym_tab
233 fun atp_literal_from_metis (pos, atom) =
234 atom |> Metis_Term.Fn |> atp_term_from_metis |> AAtom |> not pos ? mk_anot
235 fun atp_clause_from_metis [] = AAtom (ATerm (tptp_false, []))
236 | atp_clause_from_metis lits =
237 lits |> map atp_literal_from_metis |> mk_aconns AOr
239 fun hol_clause_from_metis ctxt sym_tab =
241 #> Metis_LiteralSet.toList
242 #> atp_clause_from_metis
243 #> prop_from_atp ctxt false sym_tab
245 fun hol_terms_from_metis ctxt mode old_skolems sym_tab fol_tms =
246 let val ts = map (hol_term_from_metis ctxt mode sym_tab) fol_tms
247 val _ = trace_msg ctxt (fn () => " calling type inference:")
248 val _ = app (fn t => trace_msg ctxt
249 (fn () => Syntax.string_of_term ctxt t)) ts
250 val ts' = ts |> map (reveal_old_skolem_terms old_skolems)
252 val _ = app (fn t => trace_msg ctxt
253 (fn () => " final term: " ^ Syntax.string_of_term ctxt t ^
254 " of type " ^ Syntax.string_of_typ ctxt (type_of t)))
258 (* ------------------------------------------------------------------------- *)
259 (* FOL step Inference Rules *)
260 (* ------------------------------------------------------------------------- *)
262 (*for debugging only*)
264 fun print_thpair ctxt (fth,th) =
265 (trace_msg ctxt (fn () => "=============================================");
266 trace_msg ctxt (fn () => "Metis: " ^ Metis_Thm.toString fth);
267 trace_msg ctxt (fn () => "Isabelle: " ^ Display.string_of_thm_without_context th));
270 fun lookth th_pairs fth =
271 the (AList.lookup (uncurry Metis_Thm.equal) th_pairs fth)
272 handle Option.Option =>
273 raise Fail ("Failed to find Metis theorem " ^ Metis_Thm.toString fth)
275 fun cterm_incr_types thy idx = cterm_of thy o (map_types (Logic.incr_tvar idx));
277 (* INFERENCE RULE: AXIOM *)
279 (* This causes variables to have an index of 1 by default. See also "make_var"
281 fun axiom_inf th_pairs th = Thm.incr_indexes 1 (lookth th_pairs th)
283 (* INFERENCE RULE: ASSUME *)
285 val EXCLUDED_MIDDLE = @{lemma "P ==> ~ P ==> False" by (rule notE)}
287 fun inst_excluded_middle thy i_atm =
288 let val th = EXCLUDED_MIDDLE
289 val [vx] = Term.add_vars (prop_of th) []
290 val substs = [(cterm_of thy (Var vx), cterm_of thy i_atm)]
291 in cterm_instantiate substs th end;
293 fun assume_inf ctxt mode old_skolems sym_tab atm =
295 (Proof_Context.theory_of ctxt)
296 (singleton (hol_terms_from_metis ctxt mode old_skolems sym_tab)
299 (* INFERENCE RULE: INSTANTIATE (SUBST). Type instantiations are ignored. Trying
300 to reconstruct them admits new possibilities of errors, e.g. concerning
301 sorts. Instead we try to arrange that new TVars are distinct and that types
302 can be inferred from terms. *)
304 fun inst_inf ctxt mode old_skolems sym_tab th_pairs fsubst th =
305 let val thy = Proof_Context.theory_of ctxt
306 val i_th = lookth th_pairs th
307 val i_th_vars = Term.add_vars (prop_of i_th) []
308 fun find_var x = the (List.find (fn ((a,_),_) => a=x) i_th_vars)
309 fun subst_translation (x,y) =
310 let val v = find_var x
311 (* We call "reveal_old_skolem_terms" and "infer_types" below. *)
312 val t = hol_term_from_metis ctxt mode sym_tab y
313 in SOME (cterm_of thy (Var v), t) end
314 handle Option.Option =>
315 (trace_msg ctxt (fn () => "\"find_var\" failed for " ^ x ^
316 " in " ^ Display.string_of_thm ctxt i_th);
319 (trace_msg ctxt (fn () => "\"hol_term_from_metis\" failed for " ^ x ^
320 " in " ^ Display.string_of_thm ctxt i_th);
322 fun remove_typeinst (a, t) =
323 case strip_prefix_and_unascii schematic_var_prefix a of
324 SOME b => SOME (b, t)
325 | NONE => case strip_prefix_and_unascii tvar_prefix a of
326 SOME _ => NONE (*type instantiations are forbidden!*)
327 | NONE => SOME (a,t) (*internal Metis var?*)
328 val _ = trace_msg ctxt (fn () => " isa th: " ^ Display.string_of_thm ctxt i_th)
329 val substs = map_filter remove_typeinst (Metis_Subst.toList fsubst)
330 val (vars,rawtms) = ListPair.unzip (map_filter subst_translation substs)
331 val tms = rawtms |> map (reveal_old_skolem_terms old_skolems)
333 val ctm_of = cterm_incr_types thy (1 + Thm.maxidx_of i_th)
334 val substs' = ListPair.zip (vars, map ctm_of tms)
335 val _ = trace_msg ctxt (fn () =>
336 cat_lines ("subst_translations:" ::
337 (substs' |> map (fn (x, y) =>
338 Syntax.string_of_term ctxt (term_of x) ^ " |-> " ^
339 Syntax.string_of_term ctxt (term_of y)))));
340 in cterm_instantiate substs' i_th end
341 handle THM (msg, _, _) => raise METIS ("inst_inf", msg)
342 | ERROR msg => raise METIS ("inst_inf", msg)
344 (* INFERENCE RULE: RESOLVE *)
346 (* Like RSN, but we rename apart only the type variables. Vars here typically
347 have an index of 1, and the use of RSN would increase this typically to 3.
348 Instantiations of those Vars could then fail. See comment on "make_var". *)
349 fun resolve_inc_tyvars thy tha i thb =
351 val tha = Drule.incr_type_indexes (1 + Thm.maxidx_of thb) tha
353 case Thm.bicompose false (false, tha, nprems_of tha) i thb
354 |> Seq.list_of |> distinct Thm.eq_thm of
356 | _ => raise THM ("resolve_inc_tyvars: unique result expected", i,
361 (* The unifier, which is invoked from "Thm.bicompose", will sometimes
362 refuse to unify "?a::?'a" with "?a::?'b" or "?a::nat" and throw a
363 "TERM" exception (with "add_ffpair" as first argument). We then
364 perform unification of the types of variables by hand and try
365 again. We could do this the first time around but this error
366 occurs seldom and we don't want to break existing proofs in subtle
367 ways or slow them down needlessly. *)
368 case [] |> fold (Term.add_vars o prop_of) [tha, thb]
369 |> AList.group (op =)
370 |> maps (fn ((s, _), T :: Ts) =>
371 map (fn T' => (Free (s, T), Free (s, T'))) Ts)
372 |> rpair (Envir.empty ~1)
373 |-> fold (Pattern.unify thy)
374 |> Envir.type_env |> Vartab.dest
375 |> map (fn (x, (S, T)) =>
376 pairself (ctyp_of thy) (TVar (x, S), T)) of
378 | ps => aux (instantiate (ps, []) tha) (instantiate (ps, []) thb)
381 fun s_not (@{const Not} $ t) = t
382 | s_not t = HOLogic.mk_not t
383 fun simp_not_not (@{const Not} $ t) = s_not (simp_not_not t)
386 (* Match untyped terms. *)
387 fun untyped_aconv (Const (a, _), Const(b, _)) = (a = b)
388 | untyped_aconv (Free (a, _), Free (b, _)) = (a = b)
389 | untyped_aconv (Var ((a, _), _), Var ((b, _), _)) = (a = b)
390 | untyped_aconv (Bound i, Bound j) = (i = j)
391 | untyped_aconv (Abs (_, _, t), Abs (_, _, u)) = untyped_aconv (t, u)
392 | untyped_aconv (t1 $ t2, u1 $ u2) =
393 untyped_aconv (t1, u1) andalso untyped_aconv (t2, u2)
394 | untyped_aconv _ = false
396 (* Finding the relative location of an untyped term within a list of terms *)
397 fun index_of_literal lit haystack =
399 val normalize = simp_not_not o Envir.eta_contract
401 HOLogic.dest_Trueprop #> normalize
402 #> curry untyped_aconv (lit |> normalize)
403 in case find_index match_lit haystack of ~1 => raise Empty | n => n + 1 end
405 (* Permute a rule's premises to move the i-th premise to the last position. *)
407 let val n = nprems_of th
408 in if 1 <= i andalso i <= n
409 then Thm.permute_prems (i-1) 1 th
410 else raise THM("select_literal", i, [th])
413 (* Maps a rule that ends "... ==> P ==> False" to "... ==> ~ P" while avoiding
414 to create double negations. The "select" wrapper is a trick to ensure that
415 "P ==> ~ False ==> False" is rewritten to "P ==> False", not to "~ P". We
416 don't use this trick in general because it makes the proof object uglier than
419 if exists (fn t => t aconv @{prop "~ False"}) (prems_of th) then
420 (th RS @{thm select_FalseI})
421 |> fold (rewrite_rule o single)
422 @{thms not_atomize_select atomize_not_select}
424 th |> fold (rewrite_rule o single) @{thms not_atomize atomize_not}
426 (* Maps the clause [P1,...Pn]==>False to [P1,...,P(i-1),P(i+1),...Pn] ==> ~P *)
427 val select_literal = negate_head oo make_last
429 fun resolve_inf ctxt mode old_skolems sym_tab th_pairs atm th1 th2 =
431 val thy = Proof_Context.theory_of ctxt
432 val (i_th1, i_th2) = pairself (lookth th_pairs) (th1, th2)
433 val _ = trace_msg ctxt (fn () => " isa th1 (pos): " ^ Display.string_of_thm ctxt i_th1)
434 val _ = trace_msg ctxt (fn () => " isa th2 (neg): " ^ Display.string_of_thm ctxt i_th2)
436 (* Trivial cases where one operand is type info *)
437 if Thm.eq_thm (TrueI, i_th1) then
439 else if Thm.eq_thm (TrueI, i_th2) then
444 singleton (hol_terms_from_metis ctxt mode old_skolems sym_tab)
446 val _ = trace_msg ctxt (fn () => " atom: " ^ Syntax.string_of_term ctxt i_atm)
447 val prems_th1 = prems_of i_th1
448 val prems_th2 = prems_of i_th2
450 index_of_literal (s_not i_atm) prems_th1
451 handle Empty => raise Fail "Failed to find literal in th1"
452 val _ = trace_msg ctxt (fn () => " index_th1: " ^ string_of_int index_th1)
454 index_of_literal i_atm prems_th2
455 handle Empty => raise Fail "Failed to find literal in th2"
456 val _ = trace_msg ctxt (fn () => " index_th2: " ^ string_of_int index_th2)
458 resolve_inc_tyvars thy (select_literal index_th1 i_th1) index_th2 i_th2
459 handle TERM (s, _) => raise METIS ("resolve_inf", s)
463 (* INFERENCE RULE: REFL *)
465 val REFL_THM = Thm.incr_indexes 2 @{lemma "t ~= t ==> False" by simp}
467 val refl_x = cterm_of @{theory} (Var (hd (Term.add_vars (prop_of REFL_THM) [])));
468 val refl_idx = 1 + Thm.maxidx_of REFL_THM;
470 fun refl_inf ctxt mode old_skolems sym_tab t =
472 val thy = Proof_Context.theory_of ctxt
473 val i_t = singleton (hol_terms_from_metis ctxt mode old_skolems sym_tab) t
474 val _ = trace_msg ctxt (fn () => " term: " ^ Syntax.string_of_term ctxt i_t)
475 val c_t = cterm_incr_types thy refl_idx i_t
476 in cterm_instantiate [(refl_x, c_t)] REFL_THM end
478 (* INFERENCE RULE: EQUALITY *)
480 val subst_em = @{lemma "s = t ==> P s ==> ~ P t ==> False" by simp}
481 val ssubst_em = @{lemma "s = t ==> P t ==> ~ P s ==> False" by simp}
483 val metis_eq = Metis_Term.Fn ("=", []);
485 (* Equality has no type arguments *)
486 fun get_ty_arg_size _ (Const (@{const_name HOL.eq}, _)) = 0
487 | get_ty_arg_size thy (Const (s, _)) =
488 (num_type_args thy s handle TYPE _ => 0)
489 | get_ty_arg_size _ _ = 0
491 fun equality_inf ctxt mode old_skolems sym_tab (pos, atm) fp fr =
492 let val thy = Proof_Context.theory_of ctxt
493 val m_tm = Metis_Term.Fn atm
495 hol_terms_from_metis ctxt mode old_skolems sym_tab [m_tm, fr]
496 val _ = trace_msg ctxt (fn () => "sign of the literal: " ^ Bool.toString pos)
497 fun replace_item_list lx 0 (_::ls) = lx::ls
498 | replace_item_list lx i (l::ls) = l :: replace_item_list lx (i-1) ls
499 fun path_finder_fail mode tm ps t =
500 raise Fail ("Cannot replay Metis proof in Isabelle:\n" ^
501 "equality_inf, path_finder_" ^ string_of_mode mode ^
502 ": path = " ^ space_implode " " (map string_of_int ps) ^
503 " isa-term: " ^ Syntax.string_of_term ctxt tm ^
505 SOME t => " fol-term: " ^ Metis_Term.toString t
507 fun path_finder_FO tm [] = (tm, Bound 0)
508 | path_finder_FO tm (p::ps) =
509 let val (tm1,args) = strip_comb tm
510 val adjustment = get_ty_arg_size thy tm1
511 val p' = if adjustment > p then p else p - adjustment
512 val tm_p = nth args p'
514 raise METIS ("equality_inf",
515 string_of_int p ^ " adj " ^
516 string_of_int adjustment ^ " term " ^
517 Syntax.string_of_term ctxt tm)
518 val _ = trace_msg ctxt (fn () => "path_finder: " ^ string_of_int p ^
519 " " ^ Syntax.string_of_term ctxt tm_p)
520 val (r,t) = path_finder_FO tm_p ps
522 (r, list_comb (tm1, replace_item_list t p' args))
524 fun path_finder_HO tm [] = (tm, Bound 0)
525 | path_finder_HO (t$u) (0::ps) = (fn(x,y) => (x, y$u)) (path_finder_HO t ps)
526 | path_finder_HO (t$u) (_::ps) = (fn(x,y) => (x, t$y)) (path_finder_HO u ps)
527 | path_finder_HO tm ps = path_finder_fail HO tm ps NONE
528 fun path_finder_FT tm [] _ = (tm, Bound 0)
529 | path_finder_FT tm (0::ps) (Metis_Term.Fn (":", [t1, _])) =
530 path_finder_FT tm ps t1
531 | path_finder_FT (t$u) (0::ps) (Metis_Term.Fn (".", [t1, _])) =
532 (fn(x,y) => (x, y$u)) (path_finder_FT t ps t1)
533 | path_finder_FT (t$u) (1::ps) (Metis_Term.Fn (".", [_, t2])) =
534 (fn(x,y) => (x, t$y)) (path_finder_FT u ps t2)
535 | path_finder_FT tm ps t = path_finder_fail FT tm ps (SOME t)
536 fun path_finder_MX tm [] _ = (tm, Bound 0)
537 | path_finder_MX tm (p :: ps) (t as Metis_Term.Fn (s, ts)) =
538 let val s = s |> unmangled_const_name in
539 if s = metis_predicator orelse s = prefixed_predicator_name orelse
540 s = metis_type_tag orelse s = prefixed_type_tag_name then
541 path_finder_MX tm ps (nth ts p)
542 else if s = metis_app_op orelse s = prefixed_app_op_name then
544 val (tm1, tm2) = dest_comb tm
545 val p' = p - (length ts - 2)
548 path_finder_MX tm1 ps (nth ts p) ||> (fn y => y $ tm2)
550 path_finder_MX tm2 ps (nth ts p) ||> (fn y => tm1 $ y)
554 val (tm1, args) = strip_comb tm
555 val adjustment = length ts - length args
556 val p' = if adjustment > p then p else p - adjustment
557 val tm_p = nth args p'
559 path_finder_fail MX tm (p :: ps) (SOME t)
560 val _ = trace_msg ctxt (fn () =>
561 "path_finder: " ^ string_of_int p ^ " " ^
562 Syntax.string_of_term ctxt tm_p)
563 val (r, t) = path_finder_MX tm_p ps (nth ts p)
564 in (r, list_comb (tm1, replace_item_list t p' args)) end
566 | path_finder_MX tm ps t = path_finder_fail MX tm ps (SOME t)
567 fun path_finder FO tm ps _ = path_finder_FO tm ps
568 | path_finder HO (tm as Const(@{const_name HOL.eq},_) $ _ $ _) (p::ps) _ =
569 (*equality: not curried, as other predicates are*)
570 if p=0 then path_finder_HO tm (0::1::ps) (*select first operand*)
571 else path_finder_HO tm (p::ps) (*1 selects second operand*)
572 | path_finder HO tm (_ :: ps) (Metis_Term.Fn ("{}", [_])) =
573 path_finder_HO tm ps (*if not equality, ignore head to skip hBOOL*)
574 | path_finder FT (tm as Const(@{const_name HOL.eq}, _) $ _ $ _) (p::ps)
575 (Metis_Term.Fn ("=", [t1,t2])) =
576 (*equality: not curried, as other predicates are*)
577 if p=0 then path_finder_FT tm (0::1::ps)
578 (Metis_Term.Fn (metis_app_op, [Metis_Term.Fn (metis_app_op, [metis_eq,t1]), t2]))
579 (*select first operand*)
580 else path_finder_FT tm (p::ps)
581 (Metis_Term.Fn (metis_app_op, [metis_eq, t2]))
582 (*1 selects second operand*)
583 | path_finder FT tm (_ :: ps) (Metis_Term.Fn ("{}", [t1])) = path_finder_FT tm ps t1
584 (*if not equality, ignore head to skip the hBOOL predicate*)
585 | path_finder FT tm ps t = path_finder_FT tm ps t (*really an error case!*)
586 | path_finder MX tm ps t = path_finder_MX tm ps t
587 fun path_finder_lit ((nt as Const (@{const_name Not}, _)) $ tm_a) idx =
588 let val (tm, tm_rslt) = path_finder mode tm_a idx m_tm
589 in (tm, nt $ tm_rslt) end
590 | path_finder_lit tm_a idx = path_finder mode tm_a idx m_tm
591 val (tm_subst, body) = path_finder_lit i_atm fp
592 val tm_abs = Abs ("x", type_of tm_subst, body)
593 val _ = trace_msg ctxt (fn () => "abstraction: " ^ Syntax.string_of_term ctxt tm_abs)
594 val _ = trace_msg ctxt (fn () => "i_tm: " ^ Syntax.string_of_term ctxt i_tm)
595 val _ = trace_msg ctxt (fn () => "located term: " ^ Syntax.string_of_term ctxt tm_subst)
596 val imax = maxidx_of_term (i_tm $ tm_abs $ tm_subst) (*ill typed but gives right max*)
597 val subst' = Thm.incr_indexes (imax+1) (if pos then subst_em else ssubst_em)
598 val _ = trace_msg ctxt (fn () => "subst' " ^ Display.string_of_thm ctxt subst')
599 val eq_terms = map (pairself (cterm_of thy))
600 (ListPair.zip (OldTerm.term_vars (prop_of subst'), [tm_abs, tm_subst, i_tm]))
601 in cterm_instantiate eq_terms subst' end;
603 val factor = Seq.hd o distinct_subgoals_tac
605 fun one_step ctxt mode old_skolems sym_tab th_pairs p =
607 (fol_th, Metis_Proof.Axiom _) => axiom_inf th_pairs fol_th |> factor
608 | (_, Metis_Proof.Assume f_atm) =>
609 assume_inf ctxt mode old_skolems sym_tab f_atm
610 | (_, Metis_Proof.Metis_Subst (f_subst, f_th1)) =>
611 inst_inf ctxt mode old_skolems sym_tab th_pairs f_subst f_th1 |> factor
612 | (_, Metis_Proof.Resolve(f_atm, f_th1, f_th2)) =>
613 resolve_inf ctxt mode old_skolems sym_tab th_pairs f_atm f_th1 f_th2
615 | (_, Metis_Proof.Refl f_tm) => refl_inf ctxt mode old_skolems sym_tab f_tm
616 | (_, Metis_Proof.Equality (f_lit, f_p, f_r)) =>
617 equality_inf ctxt mode old_skolems sym_tab f_lit f_p f_r
619 fun flexflex_first_order th =
620 case Thm.tpairs_of th of
623 let val thy = theory_of_thm th
625 fold (Pattern.first_order_match thy) pairs (Vartab.empty, Vartab.empty)
626 val t_pairs = map Meson.term_pair_of (Vartab.dest tenv)
627 val th' = Thm.instantiate ([], map (pairself (cterm_of thy)) t_pairs) th
631 fun is_metis_literal_genuine (_, (s, _)) = not (String.isPrefix class_prefix s)
632 fun is_isabelle_literal_genuine t =
633 case t of _ $ (Const (@{const_name Meson.skolem}, _) $ _) => false | _ => true
635 fun count p xs = fold (fn x => if p x then Integer.add 1 else I) xs 0
637 (* Seldomly needed hack. A Metis clause is represented as a set, so duplicate
638 disjuncts are impossible. In the Isabelle proof, in spite of efforts to
639 eliminate them, duplicates sometimes appear with slightly different (but
641 fun resynchronize ctxt fol_th th =
644 count is_metis_literal_genuine
645 (Metis_LiteralSet.toList (Metis_Thm.clause fol_th))
646 val num_isabelle_lits = count is_isabelle_literal_genuine (prems_of th)
648 if num_metis_lits >= num_isabelle_lits then
652 val (prems0, concl) = th |> prop_of |> Logic.strip_horn
653 val prems = prems0 |> distinct untyped_aconv
654 val goal = Logic.list_implies (prems, concl)
656 if length prems = length prems0 then
657 raise METIS ("resynchronize", "Out of sync")
659 Goal.prove ctxt [] [] goal (K (cut_rules_tac [th] 1
660 THEN ALLGOALS assume_tac))
661 |> resynchronize ctxt fol_th
665 fun replay_one_inference ctxt mode old_skolems sym_tab (fol_th, inf) th_pairs =
666 if not (null th_pairs) andalso
667 prop_of (snd (hd th_pairs)) aconv @{prop False} then
668 (* Isabelle sometimes identifies literals (premises) that are distinct in
669 Metis (e.g., because of type variables). We give the Isabelle proof the
670 benefice of the doubt. *)
674 val _ = trace_msg ctxt
675 (fn () => "=============================================")
676 val _ = trace_msg ctxt
677 (fn () => "METIS THM: " ^ Metis_Thm.toString fol_th)
678 val _ = trace_msg ctxt
679 (fn () => "INFERENCE: " ^ Metis_Proof.inferenceToString inf)
680 val th = one_step ctxt mode old_skolems sym_tab th_pairs (fol_th, inf)
681 |> flexflex_first_order
682 |> resynchronize ctxt fol_th
683 val _ = trace_msg ctxt
684 (fn () => "ISABELLE THM: " ^ Display.string_of_thm ctxt th)
685 val _ = trace_msg ctxt
686 (fn () => "=============================================")
687 in (fol_th, th) :: th_pairs end
689 (* It is normally sufficient to apply "assume_tac" to unify the conclusion with
690 one of the premises. Unfortunately, this sometimes yields "Variable
691 ?SK_a_b_c_x has two distinct types" errors. To avoid this, we instantiate the
692 variables before applying "assume_tac". Typical constraints are of the form
693 ?SK_a_b_c_x SK_d_e_f_y ... SK_a_b_c_x ... SK_g_h_i_z =?= SK_a_b_c_x,
694 where the nonvariables are goal parameters. *)
695 fun unify_first_prem_with_concl thy i th =
697 val goal = Logic.get_goal (prop_of th) i |> Envir.beta_eta_contract
698 val prem = goal |> Logic.strip_assums_hyp |> hd
699 val concl = goal |> Logic.strip_assums_concl
700 fun pair_untyped_aconv (t1, t2) (u1, u2) =
701 untyped_aconv (t1, u1) andalso untyped_aconv (t2, u2)
702 fun add_terms tp inst =
703 if exists (pair_untyped_aconv tp) inst then inst
704 else tp :: map (apsnd (subst_atomic [tp])) inst
707 (Var _, args) => forall is_Bound args
709 fun unify_flex flex rigid =
710 case strip_comb flex of
711 (Var (z as (_, T)), args) =>
713 fold_rev (curry absdummy) (take (length args) (binder_types T)) rigid)
715 fun unify_potential_flex comb atom =
716 if is_flex comb then unify_flex comb atom
717 else if is_Var atom then add_terms (atom, comb)
719 fun unify_terms (t, u) =
721 (t1 $ t2, u1 $ u2) =>
722 if is_flex t then unify_flex t u
723 else if is_flex u then unify_flex u t
724 else fold unify_terms [(t1, u1), (t2, u2)]
725 | (_ $ _, _) => unify_potential_flex t u
726 | (_, _ $ _) => unify_potential_flex u t
727 | (Var _, _) => add_terms (t, u)
728 | (_, Var _) => add_terms (u, t)
731 [] |> try (unify_terms (prem, concl) #> map (pairself (cterm_of thy)))
732 |> the_default [] (* FIXME *)
733 in th |> cterm_instantiate t_inst end
735 val copy_prem = @{lemma "P ==> (P ==> P ==> Q) ==> Q" by fast}
737 fun copy_prems_tac [] ns i =
738 if forall (curry (op =) 1) ns then all_tac else copy_prems_tac (rev ns) [] i
739 | copy_prems_tac (1 :: ms) ns i =
740 rotate_tac 1 i THEN copy_prems_tac ms (1 :: ns) i
741 | copy_prems_tac (m :: ms) ns i =
742 etac copy_prem i THEN copy_prems_tac ms (m div 2 :: (m + 1) div 2 :: ns) i
744 (* Metis generates variables of the form _nnn. *)
745 val is_metis_fresh_variable = String.isPrefix "_"
747 fun instantiate_forall_tac thy t i st =
749 val params = Logic.strip_params (Logic.get_goal (prop_of st) i) |> rev
750 fun repair (t as (Var ((s, _), _))) =
751 (case find_index (fn (s', _) => s' = s) params of
755 (case (repair t, repair u) of
756 (t as Bound j, u as Bound k) =>
757 (* This is a rather subtle trick to repair the discrepancy between
758 the fully skolemized term that MESON gives us (where existentials
759 were pulled out) and the reality. *)
760 if k > j then t else t $ u
763 val t' = t |> repair |> fold (curry absdummy) (map snd params)
764 fun do_instantiate th =
765 case Term.add_vars (prop_of th) []
766 |> filter_out ((Meson_Clausify.is_zapped_var_name orf
767 is_metis_fresh_variable) o fst o fst) of
771 val var_binder_Ts = T |> binder_types |> take (length params) |> rev
772 val var_body_T = T |> funpow (length params) range_type
774 Vartab.empty |> Type.raw_unifys (fastype_of t :: map snd params,
775 var_body_T :: var_binder_Ts)
777 Envir.Envir {maxidx = Vartab.fold (Integer.max o snd o fst) tyenv 0,
778 tenv = Vartab.empty, tyenv = tyenv}
780 Vartab.fold (fn (x, (S, T)) =>
781 cons (pairself (ctyp_of thy) (TVar (x, S), T)))
784 [pairself (cterm_of thy o Envir.norm_term env) (Var var, t')]
785 in th |> instantiate (ty_inst, t_inst) end
786 | _ => raise Fail "expected a single non-zapped, non-Metis Var"
788 (DETERM (etac @{thm allE} i THEN rotate_tac ~1 i)
789 THEN PRIMITIVE do_instantiate) st
792 fun fix_exists_tac t =
793 etac @{thm exE} THEN' rename_tac [t |> dest_Var |> fst |> fst]
795 fun release_quantifier_tac thy (skolem, t) =
796 (if skolem then fix_exists_tac else instantiate_forall_tac thy) t
798 fun release_clusters_tac _ _ _ [] = K all_tac
799 | release_clusters_tac thy ax_counts substs
800 ((ax_no, cluster_no) :: clusters) =
803 Meson_Clausify.cluster_of_zapped_var_name o fst o fst o dest_Var
804 fun in_right_cluster ((_, (cluster_no', _)), _) = cluster_no' = cluster_no
807 |> map_filter (fn (ax_no', (_, (_, tsubst))) =>
808 if ax_no' = ax_no then
809 tsubst |> map (apfst cluster_of_var)
810 |> filter (in_right_cluster o fst)
815 fun do_cluster_subst cluster_subst =
816 map (release_quantifier_tac thy) cluster_subst @ [rotate_tac 1]
817 val first_prem = find_index (fn (ax_no', _) => ax_no' = ax_no) substs
819 rotate_tac first_prem
820 THEN' (EVERY' (maps do_cluster_subst cluster_substs))
821 THEN' rotate_tac (~ first_prem - length cluster_substs)
822 THEN' release_clusters_tac thy ax_counts substs clusters
825 fun cluster_key ((ax_no, (cluster_no, index_no)), skolem) =
826 (ax_no, (cluster_no, (skolem, index_no)))
828 prod_ord int_ord (prod_ord int_ord (prod_ord bool_ord int_ord))
829 (pairself cluster_key p)
832 list_ord (prod_ord Term_Ord.fast_indexname_ord
833 (prod_ord Term_Ord.sort_ord Term_Ord.typ_ord))
835 structure Int_Tysubst_Table =
836 Table(type key = int * (indexname * (sort * typ)) list
837 val ord = prod_ord int_ord tysubst_ord)
839 structure Int_Pair_Graph =
840 Graph(type key = int * int val ord = prod_ord int_ord int_ord)
842 fun shuffle_key (((axiom_no, (_, index_no)), _), _) = (axiom_no, index_no)
843 fun shuffle_ord p = prod_ord int_ord int_ord (pairself shuffle_key p)
845 (* Attempts to derive the theorem "False" from a theorem of the form
846 "P1 ==> ... ==> Pn ==> False", where the "Pi"s are to be discharged using the
847 specified axioms. The axioms have leading "All" and "Ex" quantifiers, which
848 must be eliminated first. *)
849 fun discharge_skolem_premises ctxt axioms prems_imp_false =
850 if prop_of prems_imp_false aconv @{prop False} then
854 val thy = Proof_Context.theory_of ctxt
858 Pattern.first_order_match thy p (Vartab.empty, Vartab.empty)
861 |> filter (Meson_Clausify.is_zapped_var_name o fst o fst)
863 o pairself (Meson_Clausify.cluster_of_zapped_var_name
865 |> map (Meson.term_pair_of
866 #> pairself (Envir.subst_term_types tyenv))
867 val tysubst = tyenv |> Vartab.dest
868 in (tysubst, tsubst) end
869 fun subst_info_for_prem subgoal_no prem =
871 _ $ (Const (@{const_name Meson.skolem}, _) $ (_ $ t $ num)) =>
872 let val ax_no = HOLogic.dest_nat num in
874 match_term (nth axioms ax_no |> the |> snd, t)))
876 | _ => raise TERM ("discharge_skolem_premises: Malformed premise",
878 fun cluster_of_var_name skolem s =
879 case try Meson_Clausify.cluster_of_zapped_var_name s of
881 | SOME ((ax_no, (cluster_no, _)), skolem') =>
882 if skolem' = skolem andalso cluster_no > 0 then
883 SOME (ax_no, cluster_no)
886 fun clusters_in_term skolem t =
887 Term.add_var_names t [] |> map_filter (cluster_of_var_name skolem o fst)
888 fun deps_for_term_subst (var, t) =
889 case clusters_in_term false var of
891 | [(ax_no, cluster_no)] =>
892 SOME ((ax_no, cluster_no),
893 clusters_in_term true t
894 |> cluster_no > 1 ? cons (ax_no, cluster_no - 1))
895 | _ => raise TERM ("discharge_skolem_premises: Expected Var", [var])
896 val prems = Logic.strip_imp_prems (prop_of prems_imp_false)
897 val substs = prems |> map2 subst_info_for_prem (1 upto length prems)
898 |> sort (int_ord o pairself fst)
899 val depss = maps (map_filter deps_for_term_subst o snd o snd o snd) substs
900 val clusters = maps (op ::) depss
901 val ordered_clusters =
903 |> fold Int_Pair_Graph.default_node (map (rpair ()) clusters)
904 |> fold Int_Pair_Graph.add_deps_acyclic depss
905 |> Int_Pair_Graph.topological_order
906 handle Int_Pair_Graph.CYCLES _ =>
907 error "Cannot replay Metis proof in Isabelle without \
908 \\"Hilbert_Choice\"."
910 Int_Tysubst_Table.empty
911 |> fold (fn (ax_no, (_, (tysubst, _))) =>
912 Int_Tysubst_Table.map_default ((ax_no, tysubst), 0)
913 (Integer.add 1)) substs
914 |> Int_Tysubst_Table.dest
915 val needed_axiom_props =
916 0 upto length axioms - 1 ~~ axioms
917 |> map_filter (fn (_, NONE) => NONE
918 | (ax_no, SOME (_, t)) =>
919 if exists (fn ((ax_no', _), n) =>
920 ax_no' = ax_no andalso n > 0)
925 val outer_param_names =
926 [] |> fold Term.add_var_names needed_axiom_props
927 |> filter (Meson_Clausify.is_zapped_var_name o fst)
928 |> map (`(Meson_Clausify.cluster_of_zapped_var_name o fst))
929 |> filter (fn (((_, (cluster_no, _)), skolem), _) =>
930 cluster_no = 0 andalso skolem)
931 |> sort shuffle_ord |> map (fst o snd)
932 (* for debugging only:
933 fun string_for_subst_info (ax_no, (subgoal_no, (tysubst, tsubst))) =
934 "ax: " ^ string_of_int ax_no ^ "; asm: " ^ string_of_int subgoal_no ^
935 "; tysubst: " ^ PolyML.makestring tysubst ^ "; tsubst: {" ^
936 commas (map ((fn (s, t) => s ^ " |-> " ^ t)
937 o pairself (Syntax.string_of_term ctxt)) tsubst) ^ "}"
938 val _ = tracing ("ORDERED CLUSTERS: " ^ PolyML.makestring ordered_clusters)
939 val _ = tracing ("AXIOM COUNTS: " ^ PolyML.makestring ax_counts)
940 val _ = tracing ("OUTER PARAMS: " ^ PolyML.makestring outer_param_names)
941 val _ = tracing ("SUBSTS (" ^ string_of_int (length substs) ^ "):\n" ^
942 cat_lines (map string_for_subst_info substs))
944 fun cut_and_ex_tac axiom =
945 cut_rules_tac [axiom] 1
946 THEN TRY (REPEAT_ALL_NEW (etac @{thm exE}) 1)
947 fun rotation_for_subgoal i =
948 find_index (fn (_, (subgoal_no, _)) => subgoal_no = i) substs
950 Goal.prove ctxt [] [] @{prop False}
951 (K (DETERM (EVERY (map (cut_and_ex_tac o fst o the o nth axioms o fst
953 THEN rename_tac outer_param_names 1
954 THEN copy_prems_tac (map snd ax_counts) [] 1)
955 THEN release_clusters_tac thy ax_counts substs ordered_clusters 1
956 THEN match_tac [prems_imp_false] 1
957 THEN ALLGOALS (fn i =>
958 rtac @{thm Meson.skolem_COMBK_I} i
959 THEN rotate_tac (rotation_for_subgoal i) i
960 THEN PRIMITIVE (unify_first_prem_with_concl thy i)
964 error ("Cannot replay Metis proof in Isabelle:\n\
965 \Error when discharging Skolem assumptions.")