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