1(* ========================================================================= *)
2(* A HOL INTERFACE TO THE METIS FIRST-ORDER PROVER.                          *)
3(* Created by Joe Hurd, October 2001                                         *)
4(* ========================================================================= *)
5
6(*
7app load ["mlibUseful", "mlibMetis", "folTools"];
8*)
9
10(*
11*)
12structure metisTools :> metisTools =
13struct
14
15open HolKernel Parse boolLib folTools folMapping;
16
17(* ------------------------------------------------------------------------- *)
18(* Chatting and error handling.                                              *)
19(* ------------------------------------------------------------------------- *)
20
21(* "Metis" trace levels:
22   0: No output
23   1: The equivalent of Meson dots
24   2: Timing information
25   3: More detailed prover information: slice by slice
26   4: Log of each step in proof translation
27   5: Log of each inference during proof search *)
28
29local
30  open mlibUseful;
31  val module = "metisTools";
32  val aligned_traces =
33    [{module = "mlibClause",     alignment = fn n => n + 4},
34     {module = "mlibSolver",     alignment = I},
35     {module = "mlibMeson",      alignment = I},
36     {module = "mlibClauseset",  alignment = I},
37     {module = "mlibSupport",    alignment = I},
38     {module = "mlibResolution", alignment = I},
39     {module = "folMapping",     alignment = fn n => n + 3},
40     {module = "folTools",       alignment = I},
41     {module = "metisTools",     alignment = I},
42     {module = "metisLib",       alignment = I}];
43  val () = register_trace ("metis", trace_level, 10)
44  val () = trace_level := (if !Globals.interactive then 1 else 0) (* OK *)
45  val () = set_traces aligned_traces
46in
47  fun chatting l = tracing {module = module, level = l};
48  fun chat s = (trace s; true)
49  val ERR = mk_HOL_ERR module;
50  fun BUG f m = Bug (f ^ ": " ^ m);
51end;
52
53(* ------------------------------------------------------------------------- *)
54(* Making the Metis prover HOL specific.                                     *)
55(* ------------------------------------------------------------------------- *)
56
57fun bool_map ":" = SOME "fst"      (* Force types to be ignored in the model *)
58  | bool_map "combin.I" = SOME "id"
59  | bool_map "pair.FST" = SOME "id"
60  | bool_map "pair.," = SOME "fst"
61  | bool_map _ = NONE;
62
63fun num_map "num.0" = SOME "0"
64  | num_map "num.SUC" = SOME "suc"
65  | num_map "prim_rec.PRE" = SOME "pre"
66  | num_map "arithmetic.+" = SOME "+"
67  | num_map "arithmetic.-" = SOME "-"
68  | num_map "arithmetic.*" = SOME "*"
69  | num_map "arithmetic.EXP" = SOME "exp"
70  | num_map "arithmetic.MOD" = SOME "mod"
71  | num_map "arithmetic.<=" = SOME "<="
72  | num_map "prim_rec.<" = SOME "<"
73  | num_map "arithmetic.>" = SOME ">"
74  | num_map "arithmetic.>=" = SOME ">="
75  | num_map "divides.divides" = SOME "divides"
76  | num_map "arithmetic.ODD" = SOME "odd"
77  | num_map "arithmetic.EVEN" = SOME "even"
78  | num_map "arithmetic.NUMERAL" = SOME "id"
79  | num_map "arithmetic.ALT_ZERO" = SOME "0"
80  | num_map "arithmetic.NUMERAL_BIT1" = SOME "BIT1"
81  | num_map "arithmetic.NUMERAL_BIT2" = SOME "BIT2"
82  | num_map _ = NONE;
83
84fun holify_num fix N =
85  let
86    val {func,pred} = fix N
87    val funk = partial (BUG "holify_num" "missing function alert!") func
88    val one = funk ("1",[]) and two = funk ("2",[])
89    fun func' ("BIT1",[m]) = SOME (funk ("+", [funk ("*",[two,m]), one]))
90      | func' ("BIT2",[m]) = SOME (funk ("+", [funk ("*",[two,m]), two]))
91      | func' x = func x
92  in
93    {func = func', pred = pred}
94  end;
95
96fun int_map "integer.int_neg" = SOME "~"
97  | int_map "integer.int_add" = SOME "+"
98  | int_map "integer.int_sub" = SOME "-"
99  | int_map "integer.int_mul" = SOME "*"
100  | int_map "integer.int_exp" = SOME "exp"
101  | int_map "integer.int_le" = SOME "<="
102  | int_map "integer.int_lt" = SOME "<"
103  | int_map "integer.int_gt" = SOME ">"
104  | int_map "integer.int_ge" = SOME ">="
105  | int_map "integer.int_of_num" = SOME "id"
106  | int_map _ = NONE;
107
108fun list_map "list.NIL" = SOME "0"
109  | list_map "list.CONS" = SOME "suc"
110  | list_map "list.TL" = SOME "pre"
111  | list_map "list.APPEND" = SOME "+"
112  | list_map "list.NULL" = SOME "is_0"
113  | list_map "list.LENGTH" = SOME "id"
114  | list_map _ = NONE;
115
116fun set_map "pred_set.EMPTY" = SOME "empty"
117  | set_map "pred_set.UNIV" = SOME "univ"
118  | set_map "pred_set.UNION" = SOME "union"
119  | set_map "pred_set.INTER" = SOME "inter"
120  | set_map "pred_set.COMPL" = SOME "compl"
121  | set_map "pred_set.CARD" = SOME "card"
122  | set_map "bool.IN" = SOME "in"
123  | set_map "pred_set.SUBSET" = SOME "subset"
124  | set_map _ = NONE;
125
126val modulo_basic_fix =
127  mlibModel.fix_merge mlibModel.modulo_fix mlibModel.basic_fix;
128
129val hol_fix = mlibModel.fix_mergel
130  [mlibModel.pure_fix,
131   mlibModel.map_fix bool_map mlibModel.basic_fix,
132   mlibModel.map_fix num_map (holify_num modulo_basic_fix),
133   mlibModel.map_fix int_map modulo_basic_fix,
134   mlibModel.map_fix list_map modulo_basic_fix,
135   mlibModel.map_fix set_map mlibModel.set_fix];
136
137local
138  val METIS_PROVER = [mlibMetis.ordered_resolution];
139  val MODEL_SIZE = 8;
140  val MODEL_PERTS = 5;
141
142  fun hol_parm n =
143    (mlibModel.update_fix (K hol_fix) o
144     mlibModel.update_size (K n)) mlibModel.defaults;
145
146  fun holify_res ms =
147    mlibResolution.update_sos_parm
148    (mlibSupport.update_model_perts (K MODEL_PERTS) o
149     mlibSupport.update_model_parms (K ms));
150
151  fun holify ms (mlibMetis.mlibResolution r, f, c) =
152    (mlibMetis.mlibResolution (holify_res ms r), f, c)
153    | holify _ x = x;
154
155  fun update_models ms = map (holify ms) METIS_PROVER;
156in
157  val fo_solver = update_models [hol_parm MODEL_SIZE];
158  val ho_solver = update_models [];
159end;
160
161(* ------------------------------------------------------------------------- *)
162(* Parameters.                                                               *)
163(* ------------------------------------------------------------------------- *)
164
165type Fparm = folTools.parameters;
166type Mparm = mlibMetis.parameters;
167type parameters = {interface : Fparm, solver : Mparm, limit : limit};
168
169val defaults =
170  {interface = folTools.defaults,
171   limit = mlibMeter.unlimited,
172   solver = fo_solver};
173
174fun update_interface f {interface, solver, limit} =
175  {interface = f interface, solver = solver, limit = limit};
176
177fun update_solver f {interface, solver, limit} =
178  {interface = interface, solver = f solver, limit = limit};
179
180fun update_limit f {interface, solver, limit} =
181  {interface = interface, solver = solver, limit = f limit};
182
183(* ------------------------------------------------------------------------- *)
184(* Helper functions.                                                         *)
185(* ------------------------------------------------------------------------- *)
186
187fun timed_fn s f a =
188  let
189    val (t, r) = mlibUseful.timed f a
190    val _ = chatting 2 andalso chat
191      ("metis: " ^ s ^ " time: " ^ mlibUseful.real_to_string t ^ "\n")
192  in
193    r
194  end;
195
196fun contains s =
197  let
198    fun g [] _ = true | g _ [] = false | g (a::b) (c::d) = a = c andalso g b d
199    val k = explode s
200    fun f [] = false | f (l as _ :: ys) = g k l orelse f ys
201  in
202    f o explode
203  end;
204
205fun const_scheme c =
206  let val {Thy, Name, ...} = dest_thy_const c
207  in prim_mk_const {Name = Name, Thy = Thy}
208  end;
209
210fun trap f g x =
211  f x
212  handle e as HOL_ERR {message, ...} =>
213    (if not (contains "proof translation error" message) then raise e else
214       (chatting 1 andalso
215        chat "metis: proof translation error: trying again with types.\n";
216        g x));
217
218(* ------------------------------------------------------------------------- *)
219(* Prolog solver.                                                            *)
220(* ------------------------------------------------------------------------- *)
221
222local
223  val prolog_parm =
224    {equality = false,
225     boolean = false,
226     combinator = false,
227     mapping_parm = {higher_order = false, with_types = true}};
228in
229  fun PROLOG_SOLVE ths =
230    let
231      val _ = (chatting 1 andalso chat "prolog: "; chatting 2 andalso chat "\n")
232      val (cs,ths) = FOL_NORM ths
233      val lmap = build_map (prolog_parm,cs,ths)
234    in
235      FOL_SOLVE mlibMeson.prolog lmap mlibMeter.unlimited
236    end;
237end;
238
239(* ------------------------------------------------------------------------- *)
240(* Metis solver.                                                             *)
241(* ------------------------------------------------------------------------- *)
242
243fun GEN_METIS_SOLVE parm ths =
244  let
245    val {interface, solver, limit, ...} : parameters = parm
246    val _ = (chatting 1 andalso chat "metis solver: ";
247             chatting 2 andalso chat "\n")
248    val (cs,ths) = FOL_NORM ths
249    val lmap = build_map (interface,cs,ths)
250  in
251    FOL_SOLVE (mlibMetis.metis' solver) lmap limit
252  end;
253
254(* ------------------------------------------------------------------------- *)
255(* Tactic interface to the metis prover.                                     *)
256(* ------------------------------------------------------------------------- *)
257
258fun X_METIS_TAC ttac ths goal =
259  (chatting 1 andalso chat "metis: "; chatting 2 andalso chat "\n";
260   FOL_NORM_TTAC ttac ths goal);
261
262fun GEN_METIS_TTAC parm (cs,ths) =
263  let
264    val {interface, solver, limit, ...} : parameters = parm
265    val lmap = timed_fn "logic map build" build_map (interface,cs,ths)
266  in
267    FOL_TACTIC (mlibMetis.metis' solver) lmap limit
268  end;
269
270val GEN_METIS_TAC = X_METIS_TAC o GEN_METIS_TTAC;
271
272(* ------------------------------------------------------------------------- *)
273(* All the following use this limit.                                         *)
274(* ------------------------------------------------------------------------- *)
275
276val limit : limit ref = ref (#limit defaults);
277
278(* ------------------------------------------------------------------------- *)
279(* Canned parameters for common situations.                                  *)
280(* ------------------------------------------------------------------------- *)
281
282fun inc_limit p = update_limit (K (!limit)) p;
283fun set_limit f p t g = f (inc_limit p) t g;
284
285(* First-order *)
286
287val fo_parm =
288  update_interface
289  (update_mapping_parm (K {higher_order = false, with_types = false}))
290  (update_solver (K fo_solver) defaults);
291
292val fot_parm =
293  update_interface (update_mapping_parm (update_with_types (K true))) fo_parm;
294
295val FOT_METIS_TTAC = set_limit GEN_METIS_TTAC fot_parm;
296
297fun FO_METIS_TTAC ths =
298  trap (set_limit GEN_METIS_TTAC fo_parm ths) (FOT_METIS_TTAC ths);
299
300(* Higher-order *)
301
302val ho_parm =
303  update_interface
304  (update_mapping_parm (K {higher_order = true, with_types = false}))
305  (update_solver (K ho_solver) defaults);
306
307val hot_parm =
308  update_interface (update_mapping_parm (update_with_types (K true))) ho_parm;
309
310val HOT_METIS_TTAC = set_limit GEN_METIS_TTAC hot_parm;
311
312fun HO_METIS_TTAC ths =
313  trap (set_limit GEN_METIS_TTAC ho_parm ths) (HOT_METIS_TTAC ths);
314
315(* Higher-order with rules for combinator reductions and booleans *)
316
317val full_parm =
318  (update_interface (update_combinator (K true) o update_boolean (K true)))
319  hot_parm;
320
321val FULL_METIS_TTAC = set_limit GEN_METIS_TTAC full_parm;
322
323(* ------------------------------------------------------------------------- *)
324(* Canned tactics.                                                           *)
325(* ------------------------------------------------------------------------- *)
326
327val FO_METIS_TAC   = X_METIS_TAC FO_METIS_TTAC;
328val FOT_METIS_TAC  = X_METIS_TAC FOT_METIS_TTAC;
329val HO_METIS_TAC   = X_METIS_TAC HO_METIS_TTAC;
330val HOT_METIS_TAC  = X_METIS_TAC HOT_METIS_TTAC;
331val FULL_METIS_TAC = X_METIS_TAC FULL_METIS_TTAC;
332
333(* ------------------------------------------------------------------------- *)
334(* Simple user interface to the metis prover.                                *)
335(* ------------------------------------------------------------------------- *)
336
337fun prover ttac ths goal = Tactical.default_prover (goal, ttac ths);
338
339val FO_METIS_PROVE   = prover FO_METIS_TAC;
340val FOT_METIS_PROVE  = prover FOT_METIS_TAC;
341val HO_METIS_PROVE   = prover HO_METIS_TAC;
342val HOT_METIS_PROVE  = prover HOT_METIS_TAC;
343val FULL_METIS_PROVE = prover FULL_METIS_TAC;
344
345(* ------------------------------------------------------------------------- *)
346(* Uses heuristics to apply one of FO_, HO_ or FULL_.                        *)
347(* ------------------------------------------------------------------------- *)
348
349datatype class = First | Higher | Full;
350
351fun class_str First = "first-order"
352  | class_str Higher = "higher-order"
353  | class_str Full = "higher-order + combinators";
354
355fun class_tac First = FO_METIS_TTAC
356  | class_tac Higher = HO_METIS_TTAC
357  | class_tac Full = FULL_METIS_TTAC;
358
359local
360  fun mk_set ts = Binaryset.addList (Binaryset.empty compare, ts);
361  fun member x s = Binaryset.member (s,x);
362
363  val empty : (term, bool * int) Binarymap.dict = Binarymap.mkDict compare;
364  fun update ts x n = Binarymap.insert (ts,x,n);
365  fun lookup ts x = Binarymap.peek (ts,x);
366
367  fun bump Full _ = Full
368    | bump Higher Full = Full
369    | bump Higher _ = Higher
370    | bump First cl = cl;
371
372  fun ord _ [] state = state
373    | ord top (tm :: tms) (c,g,l,t,v) =
374    let
375      val (f,xs) = strip_comb tm
376      val f = if is_const f then const_scheme f else f
377      val n = length xs
378      val tn = (top,n)
379      val tms = xs @ tms
380    in
381      ord false tms
382      (if member f v then
383         ((if n <> 0 orelse top then bump Higher c else c), g, l, t, v)
384       else if member f t then
385         (case lookup l f of NONE => (c, g, update l f tn, t, v)
386          | SOME tn' => ((if tn = tn' then c else bump Higher c), g, l, t, v))
387       else
388         (case lookup g f of NONE => (c, update g f tn, l, t, v)
389          | SOME tn' => ((if tn = tn' then c else bump Higher c), g, l, t, v)))
390    end;
391
392  fun order_lit [] state = state
393    | order_lit (lit :: lits) (state as (c,g,l,t,v)) =
394    let
395      val atom = case total dest_neg lit of NONE => lit | SOME lit => lit
396    in
397      order_lit lits (ord true [atom] state)
398    end;
399
400  fun order_cl (cl,gs,_,_,_) [] = (cl,gs)
401    | order_cl state (fm :: fms) =
402    order_cl (order_lit (strip_disj fm) state) fms;
403
404  fun order (cl,_) [] = cl
405    | order (cl,cs) (fm :: fms) =
406    let
407      val (ts,fm) = strip_exists fm
408      val (vs,fm) = strip_forall fm
409      val cls = strip_conj fm
410    in
411      order (order_cl (cl, cs, empty, mk_set ts, mk_set vs) cls) fms
412    end;
413in
414  fun classify fms = order (First,empty) fms
415    handle HOL_ERR _ => raise BUG "metisTools.classify" "shouldn't fail";
416end;
417
418fun METIS_TTAC (cs,ths) =
419  let
420    val class = timed_fn "problem classification" classify (map concl ths)
421    val _ = chatting 2 andalso
422            chat ("metis: a " ^ class_str class ^ " problem.\n")
423  in
424    class_tac class (cs,ths)
425  end;
426
427val METIS_TAC = X_METIS_TAC METIS_TTAC;
428
429(*
430val METIS_TAC = fn ths => fn goal =>
431    let
432      val met = Count.mk_meter ()
433      val _ = chat "\nMETIS_TAC: new\n"
434      val res = METIS_TAC ths goal
435      val {prims,...} = Count.read met
436      val _ = chat ("METIS_TAC: " ^ Int.toString prims ^ "\n")
437    in
438      res
439    end;
440*)
441
442val METIS_PROVE = prover METIS_TAC;
443
444end
445