1(* ========================================================================= *) 2(* A HOL INTERFACE TO THE FIRST-ORDER PROVERS. *) 3(* Created by Joe Hurd, October 2001 *) 4(* ========================================================================= *) 5 6structure folTools :> folTools = 7struct 8 9open HolKernel Parse boolLib combinTheory simpLib 10 normalFormsTheory normalForms folMapping; 11 12structure Parse = struct 13 open Parse 14 val (Type,Term) = parse_from_grammars normalFormsTheory.normalForms_grammars 15end 16open Parse 17 18type 'a pp = 'a mlibUseful.pp; 19type 'a stream = 'a mlibStream.stream; 20type term1 = mlibTerm.term; 21type formula1 = mlibTerm.formula; 22type thm1 = mlibThm.thm; 23type limit = mlibMeter.limit 24type solver_node = mlibSolver.solver_node; 25type vars = term list * hol_type list; 26 27(* ------------------------------------------------------------------------- *) 28(* Chatting and error handling. *) 29(* ------------------------------------------------------------------------- *) 30 31local 32 open mlibUseful; 33 val module = "folTools"; 34in 35 val () = add_trace {module = module, alignment = I} 36 fun chatting l = tracing {module = module, level = l}; 37 fun chat s = (trace s; true) 38 val ERR = mk_HOL_ERR module; 39 fun BUG f m = Bug (f ^ ": " ^ m); 40end; 41 42(* ------------------------------------------------------------------------- *) 43(* Mapping parameters. *) 44(* ------------------------------------------------------------------------- *) 45 46type parameters = 47 {equality : bool, combinator : bool, boolean : bool, 48 mapping_parm : folMapping.parameters}; 49 50type 'a parmupdate = ('a -> 'a) -> parameters -> parameters; 51 52val defaults = 53 {equality = false, combinator = false, boolean = false, 54 mapping_parm = folMapping.defaults}; 55 56fun update_equality f (parm : parameters) : parameters = 57 let 58 val {equality,combinator,boolean,mapping_parm} = parm 59 in 60 {equality = f equality, combinator = combinator, boolean = boolean, 61 mapping_parm = mapping_parm} 62 end; 63 64fun update_combinator f (parm : parameters) : parameters = 65 let 66 val {equality,combinator,boolean,mapping_parm} = parm 67 in 68 {equality = equality, combinator = f combinator, boolean = boolean, 69 mapping_parm = mapping_parm} 70 end; 71 72fun update_boolean f (parm : parameters) : parameters = 73 let 74 val {equality,combinator,boolean,mapping_parm} = parm 75 in 76 {equality = equality, combinator = combinator, boolean = f boolean, 77 mapping_parm = mapping_parm} 78 end; 79 80fun update_mapping_parm f (parm : parameters) : parameters = 81 let 82 val {equality,combinator,boolean,mapping_parm} = parm 83 in 84 {equality = equality, combinator = combinator, boolean = boolean, 85 mapping_parm = f mapping_parm} 86 end; 87 88(* ------------------------------------------------------------------------- *) 89(* Helper functions. *) 90(* ------------------------------------------------------------------------- *) 91 92fun possibly f x = f x handle HOL_ERR _ => x; 93 94fun timed_fn s f a = 95 let 96 val (t,r) = mlibUseful.timed f a 97 val _ = chatting 2 andalso chat 98 ("metis: " ^ s ^ " time: " ^ mlibUseful.real_to_string t ^ "\n") 99 in 100 r 101 end; 102 103fun map_thk f = 104 let 105 fun m mlibStream.NIL = mlibStream.NIL 106 | m (mlibStream.CONS (h,t)) = mlibStream.CONS (h, m' t) 107 and m' t () = m (f t ()) 108 in 109 m' 110 end; 111 112val type_vars_in_terms = foldl (fn (h,t) => union (type_vars_in_term h) t) []; 113 114fun variant_tys avoid = 115 let 116 fun f acc _ 0 = acc 117 | f acc i n = 118 let val v = mk_vartype ("'ty" ^ int_to_string i) 119 in if mem v avoid then f acc (i + 1) n else f (v :: acc) (i + 1) (n - 1) 120 end 121 in 122 f [] 0 123 end; 124 125fun strip_dom_rng ty = 126 case total dom_rng ty of NONE => ([], ty) 127 | SOME (d, r) => (cons d ## I) (strip_dom_rng r); 128 129fun strip_dom_rng_n 0 ty = ([], ty) 130 | strip_dom_rng_n n ty = 131 let val (d, r) = dom_rng ty in (cons d ## I) (strip_dom_rng_n (n - 1) r) end; 132 133fun const_scheme c = 134 let val {Thy, Name, ...} = dest_thy_const c 135 in prim_mk_const {Name = Name, Thy = Thy} 136 end; 137 138fun const_scheme_n n f = 139 let 140 val c = const_scheme f 141 val (ds, r) = strip_dom_rng (type_of c) 142 val diff = n - length ds 143 in 144 if diff <= 0 then c else 145 let 146 val _ = is_vartype r orelse raise ERR "const_scheme_n" "inflexible" 147 val xs = variant_tys (type_varsl (r :: ds)) diff 148 in 149 inst [r |-> foldl op--> r xs] c 150 end 151 end; 152 153fun mk_vthm_ty tyV th = 154 let 155 val (tmV, _) = strip_forall (concl th) 156 val gtmV = map (genvar o type_of) tmV 157 in 158 ((gtmV, tyV), SPECL gtmV th) 159 end; 160 161fun mk_vthm th = 162 let 163 val tyV_c = type_vars_in_term (concl th) 164 val tyV_h = type_vars_in_terms (hyp th) 165 in 166 mk_vthm_ty (subtract tyV_c tyV_h) th 167 end; 168 169fun list_mk_conj' [] = T 170 | list_mk_conj' tms = list_mk_conj tms; 171 172fun strip_disj' tm = if tm = F then [] else strip_disj tm; 173 174fun CONJUNCTS' th = if concl th = T then [] else CONJUNCTS th; 175 176fun GSPEC' th = 177 let 178 val (vs,_) = strip_forall (concl th) 179 val gvs = map (genvar o type_of) vs 180 in 181 (gvs, SPECL gvs th) 182 end; 183 184val thm_atoms = map (possibly dest_neg) o strip_disj' o concl; 185 186val varnames = matchTools.vfree_names is_var o list_mk_conj'; 187 188(* ------------------------------------------------------------------------- *) 189(* If recent_fol_problems is set to NONE then nothing happens (the default). *) 190(* If it is set to SOME l then every compiled FOL problem is cons'ed to l. *) 191(* ------------------------------------------------------------------------- *) 192 193type fol_problem = {thms : thm1 list, hyps : thm1 list, query : formula1 list}; 194 195val recent_fol_problems : fol_problem list option ref = ref NONE; 196 197(* no code actually sets this reference to SOME, but it may of course be useful 198 for debugging *) 199fun save_fol_problem (t, h, q) = 200 case !recent_fol_problems of NONE => () 201 | SOME l 202 => recent_fol_problems := SOME ({thms = t, hyps = h, query = q} :: l);(*OK*) 203 204(* ------------------------------------------------------------------------- *) 205(* Logic maps manage the interface between HOL and first-order logic. *) 206(* ------------------------------------------------------------------------- *) 207 208type logic_map = 209 {parm : parameters, 210 axioms : (thm1 * (vars * thm)) list, 211 thms : thm1 list, 212 hyps : thm1 list, 213 consts : string list}; 214 215fun new_map parm : logic_map = 216 {parm = parm, axioms = [], thms = [], hyps = [], consts = []}; 217 218val empty_map = new_map defaults; 219 220fun add_thm vth lmap : logic_map = 221 let 222 val _ = chatting 5 andalso 223 chat ("adding thm:\n" ^ thm_to_string (snd vth) ^ "\n") 224 val {parm, axioms, thms, hyps, consts} = lmap 225 val th = hol_thm_to_fol (#mapping_parm parm) vth 226 in 227 {parm = parm, axioms = (th, vth) :: axioms, 228 thms = mlibThm.FRESH_VARS th :: thms, hyps = hyps, consts = consts} 229 end; 230 231fun add_hyp vth lmap : logic_map = 232 let 233 val _ = chatting 5 andalso 234 chat ("adding hyp:\n" ^ thm_to_string (snd vth) ^ "\n") 235 val {parm, axioms, thms, hyps, consts} = lmap 236 val th = hol_thm_to_fol (#mapping_parm parm) vth 237 in 238 {parm = parm, axioms = (th, vth) :: axioms, thms = thms, 239 hyps = mlibThm.FRESH_VARS th :: hyps, consts = consts} 240 end; 241 242fun add_const s {parm, axioms, thms, hyps, consts} : logic_map = 243 {parm = parm, axioms = axioms, thms = thms, hyps = hyps, 244 consts = insert s consts}; 245 246val add_thms = C (foldl (uncurry add_thm)); 247 248local 249 fun is_def cs tm = 250 case total (fst o dest_var o lhs) tm of NONE => false | SOME c => mem c cs; 251 252 fun iconst (cs,th) = 253 let val c = (genvar o type_of o fst o dest_exists o concl) th 254 in (fst (dest_var c) :: cs, SKOLEM_CONST_RULE c th) 255 end; 256 257 fun ithm (th,(cs,vths)) = 258 let 259 val h = List.filter (not o is_def cs) (hyp th) 260 val tyV = subtract (type_vars_in_term (concl th)) (type_vars_in_terms h) 261 val (cs,th) = repeat iconst (cs,th) 262 val (tmV,th) = GSPEC' th 263 val vths' = map (pair (tmV,tyV)) (CONJUNCTS' th) 264 in 265 (cs, vths @ vths') 266 end; 267 268 fun carefully s f t m = f t m 269 handle HOL_ERR _ => 270 (chatting 2 andalso chat 271 ("metis: raised exception adding " ^ s ^ ": dropping.\n"); m); 272in 273 fun build_map (parm,cs,thmhyps) = 274 let 275 val _ = chatting 5 andalso chat "metis: beginning build_map.\n" 276 val (thms, hyps) = partition (null o hyp) thmhyps 277 val _ = chatting 5 andalso chat "metis: partitioned thms and hyps.\n" 278 val (cs,thms) = foldl ithm (cs,[]) thms 279 val _ = chatting 5 andalso chat "metis: applied ithm to theorems.\n" 280 val (cs,hyps) = foldl ithm (cs,[]) hyps 281 val _ = chatting 5 andalso chat "metis: applied ithm to hypotheses.\n" 282 val lmap = new_map parm 283 val lmap = foldl (fn(t,m)=>carefully"a theorem"add_thm t m) lmap thms 284 val lmap = foldl (fn(t,m)=>carefully"an assumption"add_hyp t m) lmap hyps 285 val lmap = foldl (fn(c,m)=>add_const c m) lmap cs 286 in 287 lmap 288 end 289 handle HOL_ERR _ => raise BUG "build_map" "shouldn't fail"; 290end; 291 292val pp_logic_map : logic_map pp = 293 mlibUseful.pp_map (fn {hyps, thms, ...} => (rev hyps, rev thms)) 294 (mlibUseful.pp_pair (mlibUseful.pp_list mlibThm.pp_thm) (mlibUseful.pp_list mlibThm.pp_thm)); 295 296(* ------------------------------------------------------------------------- *) 297(* Equality axioms. *) 298(* ------------------------------------------------------------------------- *) 299 300val EQ_SYMTRANS = prove 301 (``!x y z. ~(x:'a = y) \/ ~(x = z) \/ (y = z)``, 302 REPEAT STRIP_TAC THEN 303 ASM_CASES_TAC ``x:'a = y`` THEN 304 ASM_REWRITE_TAC 305 [ONCE_REWRITE_RULE [boolTheory.DISJ_SYM] 306 (REWRITE_RULE[] boolTheory.BOOL_CASES_AX)]); 307 308val EQ_COMB = prove 309 (``!f g x y. ~(f:'a->'b = g) \/ ~(x = y) \/ (f x = g y)``, 310 REPEAT GEN_TAC THEN 311 ASM_CASES_TAC ``x:'a = y`` THEN 312 ASM_CASES_TAC ``f:'a->'b = g`` THEN 313 ASM_REWRITE_TAC []); 314 315val EQ_EXTENSION = CONV_RULE CNF_CONV EXT_POINT_DEF; 316 317local 318 fun break tm (res, subtms) = 319 let 320 val (f, args) = strip_comb tm 321 val n = length args 322 val f = if is_const f then const_scheme_n n f else f 323 in 324 (insert (f, n) res, args @ subtms) 325 end; 326 327 fun boolify (tm, len) = 328 let val (_, ty) = strip_dom_rng_n len (type_of tm) 329 in (if ty = bool then tm else inst [ty |-> bool] tm, len) 330 end; 331 332 val raw_relations = (map boolify ## I) o foldl (uncurry break) ([], []); 333 334 fun harvest (res, []) = res 335 | harvest (res, tm :: others) = harvest (break tm (res, others)); 336 337 fun plumb (r, s) = {rels = r, funs = harvest ([], s)}; 338in 339 val rels_in_atoms = fst o raw_relations; 340 val relfuns_in_atoms = plumb o raw_relations; 341end; 342 343local 344 val imp_elim_CONV = REWR_CONV (tautLib.TAUT `(a ==> b) = ~a \/ b`); 345 val eq_elim_RULE = MATCH_MP (tautLib.TAUT `(a = b) ==> b \/ ~a`); 346 fun paired_C f (x, y) = f (y, x); 347 348 fun mk_axiom pflag (tm, len) = 349 let 350 val (ctys, rty) = strip_dom_rng_n len (type_of tm) 351 val largs = map genvar ctys 352 val rargs = map genvar ctys 353 val th1 = foldl (paired_C MK_COMB) (REFL tm) 354 (map (ASSUME o mk_eq) (zip largs rargs)) 355 val th2 = if pflag then try eq_elim_RULE th1 else th1 356 val th3 = 357 foldr (fn (e, th) => CONV_RULE imp_elim_CONV (DISCH e th)) th2 (hyp th2) 358 in 359 GENL (largs @ rargs) th3 360 end; 361 362 fun mk_axioms pflag = map (mk_axiom pflag) o filter (fn (_, x) => 0 < x); 363in 364 fun substitution_thms skols {rels, funs} = 365 let 366 fun is_skol v = is_var v andalso mem (fst (dest_var v)) skols 367 fun is_eql (t,2) = same_const equality t | is_eql _ = false 368 val funs = filter (not o is_skol o fst) funs 369 val rels = filter (not o is_eql) rels 370 in 371 map mk_vthm (mk_axioms true rels @ mk_axioms false funs) 372 end; 373end; 374 375local 376 val eq_tm = ``$= :'a->'a->bool``; 377 val neq_fo = [mk_vthm EQ_REFL] 378 and xeq_fo = [mk_vthm EQ_SYMTRANS]; 379 val eq_fo = neq_fo @ xeq_fo; 380 381 val neq_ho = neq_fo @ [mk_vthm EQ_EXTENSION] 382 and xeq_ho = xeq_fo @ map mk_vthm [EQ_COMB]; 383 val eq_ho = neq_ho @ xeq_ho; 384in 385 fun add_equality_thms (lmap : logic_map) = 386 C add_thms lmap 387 let 388 val {parm,axioms,consts,...} = lmap 389 val {equality,mapping_parm,...} = parm 390 val {higher_order,...} = mapping_parm 391 in 392 if not equality then if higher_order then neq_ho else neq_fo 393 else 394 let 395 val atoms = flatten (map (thm_atoms o snd o snd) axioms) 396 in 397 if not (exists (equal eq_tm o fst) (rels_in_atoms atoms)) then [] 398 else if higher_order then eq_ho 399 else eq_fo @ substitution_thms consts (relfuns_in_atoms atoms) 400 end 401 end 402 handle HOL_ERR _ => raise BUG "add_equality_thms" "shouldn't fail"; 403end; 404 405(* ------------------------------------------------------------------------- *) 406(* Combinator reduction theorems. *) 407(* ------------------------------------------------------------------------- *) 408 409local 410 val fo_thms = map mk_vthm [K_THM, I_THM]; 411 val ho_thms = fo_thms @ map mk_vthm [S_THM, C_THM, o_THM]; 412in 413 fun add_combinator_thms lmap : logic_map = 414 let 415 val {parm as {combinator, mapping_parm, ...}, ...} = lmap 416 val {higher_order,...} = mapping_parm 417 in 418 if not combinator then lmap 419 else add_thms (if higher_order then ho_thms else fo_thms) lmap 420 end; 421end; 422 423(* ------------------------------------------------------------------------- *) 424(* Boolean theorems. *) 425(* ------------------------------------------------------------------------- *) 426 427val FALSITY' = prove (``~F``, REWRITE_TAC []); 428 429val EQ_BOOL = (CONJUNCTS o prove) 430 (``(!x y. ~x \/ ~(x = y) \/ y) /\ 431 (!x y. x \/ (x = y) \/ y) /\ 432 (!x y. ~x \/ (x = y) \/ ~y)``, 433 REPEAT CONJ_TAC THEN 434 REPEAT GEN_TAC THEN 435 ASM_CASES_TAC ``x:bool`` THEN 436 ASM_CASES_TAC ``y:bool`` THEN 437 ASM_REWRITE_TAC []); 438 439local 440 val simple_bool = map mk_vthm [TRUTH, FALSITY']; 441 val gen_bool = simple_bool @ map mk_vthm EQ_BOOL; 442in 443 fun add_boolean_thms lmap : logic_map = 444 C add_thms lmap 445 let 446 val {parm,...} = lmap 447 val {boolean,mapping_parm,...} = parm 448 val {higher_order,...} = mapping_parm 449 in 450 if not higher_order then [] 451 else if boolean then gen_bool else simple_bool 452 end; 453end; 454 455(* ------------------------------------------------------------------------- *) 456(* A pure interface to the first-order prover: zero normalization. *) 457(* ------------------------------------------------------------------------- *) 458 459type Query = vars * term list; 460type Result = vars * thm list; 461 462fun initialize_solver solv init = 463 mlibStream.partial_map I o 464 ((mlibSolver.solved_filter o mlibSolver.subsumed_filter) 465 (mlibSolver.initialize solv init)); 466 467fun eliminate consts = 468 mlibStream.filter 469 (fn (_, ths) => 470 null (intersect consts (varnames (map concl ths))) orelse 471 (chatting 2 andalso 472 chat "metis: solution contained a skolem const: dropping.\n"; 473 chatting 4 andalso 474 chat (foldl (fn (h,t) => t ^ " " ^ thm_to_string h ^ "\n") "" ths); 475 false)); 476 477fun FOL_SOLVE solv lmap lim = 478 let 479 val {parm, axioms, thms, hyps, consts} = 480 (add_equality_thms o add_boolean_thms o add_combinator_thms) lmap 481 val (thms, hyps) = (rev thms, rev hyps) 482 val solver = 483 timed_fn "solver initialization" 484 (initialize_solver solv) {thms = thms, hyps = hyps, limit = lim} 485 fun exn_handler f x = f x 486 handle Time => raise ERR "FOL_SOLVER" "Time exception raised" 487 in 488 fn query => 489 let 490 val q = 491 if snd query = [F] then [mlibTerm.False] 492 else hol_literals_to_fol (#mapping_parm parm) query 493 val () = save_fol_problem (thms, hyps, q) 494 val lift = fol_thms_to_hol (#mapping_parm parm) (C assoc axioms) query 495 val timed_lift = timed_fn "proof translation" lift 496 val timed_stream = map_thk (timed_fn "proof search" o exn_handler) 497 in 498 eliminate consts 499 (mlibStream.map timed_lift (timed_stream (fn () => solver q) ())) 500 end 501 end; 502 503local 504 fun end_find s = 505 mlibStream.hd s handle Empty => raise ERR "FOL_FIND" "no solution found"; 506in 507 fun FOL_FIND slv lmap lim = end_find o FOL_SOLVE slv lmap lim; 508end; 509 510local 511 fun end_refute (_, [t]) = CLEANUP_SKOLEM_CONSTS_RULE t 512 | end_refute _ = raise BUG "FOL_REFUTE" "weird"; 513in 514 fun FOL_REFUTE slv lmap lim = 515 (end_refute o FOL_FIND slv lmap lim) (([], []), [F]); 516end; 517 518fun FOL_TACTIC slv lmap lim = ACCEPT_TAC (FOL_REFUTE slv lmap lim); 519 520(* 521val FOL_TACTIC = fn slv => fn lmap => fn lim => fn goal => 522 let 523 val met = Count.mk_meter () 524 val res = FOL_TACTIC slv lmap lim goal 525 val {prims,...} = Count.read met 526 val _ = chat ("FOL_TACTIC: " ^ Int.toString prims ^ "\n") 527 in 528 res 529 end; 530*) 531 532(* ------------------------------------------------------------------------- *) 533(* HOL normalization to first-order form. *) 534(* ------------------------------------------------------------------------- *) 535 536val FOL_NORM = (map (fst o dest_var) ## I) o MIN_CNF; 537 538(* Normalization tactic = Stripping + Elimination of @ *) 539 540fun NEW_CHOOSE_THEN ttac th = 541 (X_CHOOSE_THEN o genvar o type_of o bvar o rand o concl) th ttac th; 542 543val FOL_STRIP_THM_THEN = 544 FIRST_TCL [CONJUNCTS_THEN, NEW_CHOOSE_THEN, DISJ_CASES_THEN]; 545 546val FOL_STRIP_ASSUME_TAC = REPEAT_TCL FOL_STRIP_THM_THEN CHECK_ASSUME_TAC; 547 548val NEW_GEN_TAC = W (X_GEN_TAC o genvar o type_of o fst o dest_forall o snd); 549 550val FOL_STRIP_TAC = 551 EQ_TAC 552 ORELSE NEW_GEN_TAC 553 ORELSE CONJ_TAC 554 ORELSE DISCH_THEN FOL_STRIP_ASSUME_TAC; 555 556val FOL_NORM_TAC = 557 (REPEAT FOL_STRIP_TAC 558 THEN CCONTR_TAC 559 THEN REPEAT (POP_ASSUM MP_TAC) 560 THEN SELECT_TAC 561 THEN REMOVE_ABBR_TAC 562 THEN REPEAT FOL_STRIP_TAC); 563 564(* A flexible tactic that performs normalization of theorems and goal. *) 565 566fun FOL_NORM_TTAC tac ths = 567 let 568 fun f asms = FOL_NORM (asms @ ths) 569 in 570 timed_fn "tactic normalization" FOL_NORM_TAC 571 THEN POP_ASSUM_LIST (tac o timed_fn "theorem normalization" f) 572 end; 573 574(* ------------------------------------------------------------------------- *) 575(* Reading in TPTP problems *) 576(* ------------------------------------------------------------------------- *) 577 578(* Mapping first-order formulas to HOL terms *) 579 580local 581 open mlibTerm boolSyntax; 582 583 fun h v = Term.mk_var (v, Type.alpha); 584 585 fun g _ (Var v) = h v 586 | g ty (Fn (f,a)) = 587 let 588 val a' = map (g Type.alpha) a 589 val ty = Lib.funpow (length a) (fn x => Type.--> (Type.alpha,x)) ty 590 val f' = Term.mk_var (f,ty) 591 in 592 Term.list_mk_comb (f',a') 593 end; 594 595 fun f True = T 596 | f False = F 597 | f (Atom (Fn ("=",[x,y]))) = mk_eq (g Type.alpha x, g Type.alpha y) 598 | f (Atom tm) = g Type.bool tm 599 | f (Not fm) = mk_neg (f fm) 600 | f (And (p,q)) = mk_conj (f p, f q) 601 | f (Or (p,q)) = mk_disj (f p, f q) 602 | f (Imp (p,q)) = mk_imp (f p, f q) 603 | f (Iff (p,q)) = mk_eq (f p, f q) 604 | f (Forall (v,p)) = mk_forall (h v, f p) 605 | f (Exists (v,p)) = mk_exists (h v, f p); 606 607 val fol_to_hol = f; 608in 609 val tptp_read = fol_to_hol o mlibTptp.read; 610end; 611 612end 613