src/HOL/Tools/Sledgehammer/sledgehammer_fact.ML
author blanchet
Mon, 23 Jul 2012 15:32:30 +0200
changeset 49453 3e45c98fe127
parent 49452 82b9feeab1ef
child 49455 159e320ef851
permissions -rw-r--r--
distinguish between recursive and nonrecursive definitions + clean up typedef dependencies in MaSh
     1 (*  Title:      HOL/Tools/Sledgehammer/sledgehammer_fact.ML
     2     Author:     Jia Meng, Cambridge University Computer Laboratory and NICTA
     3     Author:     Jasmin Blanchette, TU Muenchen
     4 
     5 Sledgehammer fact handling.
     6 *)
     7 
     8 signature SLEDGEHAMMER_FACT =
     9 sig
    10   type status = ATP_Problem_Generate.status
    11   type stature = ATP_Problem_Generate.stature
    12 
    13   type fact = ((unit -> string) * stature) * thm
    14 
    15   type fact_override =
    16     {add : (Facts.ref * Attrib.src list) list,
    17      del : (Facts.ref * Attrib.src list) list,
    18      only : bool}
    19 
    20   val ignore_no_atp : bool Config.T
    21   val instantiate_inducts : bool Config.T
    22   val no_fact_override : fact_override
    23   val fact_from_ref :
    24     Proof.context -> unit Symtab.table -> thm list -> status Termtab.table
    25     -> Facts.ref * Attrib.src list -> ((string * stature) * thm) list
    26   val backquote_thm : thm -> string
    27   val clasimpset_rule_table_of : Proof.context -> status Termtab.table
    28   val maybe_instantiate_inducts :
    29     Proof.context -> term list -> term -> (((unit -> string) * 'a) * thm) list
    30     -> (((unit -> string) * 'a) * thm) list
    31   val maybe_filter_no_atps : Proof.context -> ('a * thm) list -> ('a * thm) list
    32   val all_facts_of : Proof.context -> status Termtab.table -> fact list
    33   val nearly_all_facts :
    34     Proof.context -> bool -> fact_override -> unit Symtab.table
    35     -> status Termtab.table -> thm list -> term list -> term -> fact list
    36 end;
    37 
    38 structure Sledgehammer_Fact : SLEDGEHAMMER_FACT =
    39 struct
    40 
    41 open ATP_Problem_Generate
    42 open Metis_Tactic
    43 open Sledgehammer_Util
    44 
    45 type fact = ((unit -> string) * stature) * thm
    46 
    47 type fact_override =
    48   {add : (Facts.ref * Attrib.src list) list,
    49    del : (Facts.ref * Attrib.src list) list,
    50    only : bool}
    51 
    52 val sledgehammer_prefixes =
    53   ["ATP", "Meson", "Metis", "Sledgehammer"] |> map (suffix Long_Name.separator)
    54 
    55 (* experimental features *)
    56 val ignore_no_atp =
    57   Attrib.setup_config_bool @{binding sledgehammer_ignore_no_atp} (K false)
    58 val instantiate_inducts =
    59   Attrib.setup_config_bool @{binding sledgehammer_instantiate_inducts} (K false)
    60 
    61 val no_fact_override = {add = [], del = [], only = false}
    62 
    63 fun needs_quoting reserved s =
    64   Symtab.defined reserved s orelse
    65   exists (not o Lexicon.is_identifier) (Long_Name.explode s)
    66 
    67 fun make_name reserved multi j name =
    68   (name |> needs_quoting reserved name ? quote) ^
    69   (if multi then "(" ^ string_of_int j ^ ")" else "")
    70 
    71 fun explode_interval _ (Facts.FromTo (i, j)) = i upto j
    72   | explode_interval max (Facts.From i) = i upto i + max - 1
    73   | explode_interval _ (Facts.Single i) = [i]
    74 
    75 val backquote =
    76   raw_explode #> map (fn "`" => "\\`" | s => s) #> implode #> enclose "`" "`"
    77 
    78 (* unfolding these can yield really huge terms *)
    79 val risky_defs = @{thms Bit0_def Bit1_def}
    80 
    81 fun is_rec_eq lhs = Term.exists_subterm (curry (op =) (head_of lhs))
    82 fun is_rec_def (@{const Trueprop} $ t) = is_rec_def t
    83   | is_rec_def (@{const ==>} $ _ $ t2) = is_rec_def t2
    84   | is_rec_def (Const (@{const_name "=="}, _) $ t1 $ t2) = is_rec_eq t1 t2
    85   | is_rec_def (Const (@{const_name HOL.eq}, _) $ t1 $ t2) = is_rec_eq t1 t2
    86   | is_rec_def _ = false
    87 
    88 fun is_assum assms th = exists (fn ct => prop_of th aconv term_of ct) assms
    89 fun is_chained chained = member Thm.eq_thm_prop chained
    90 
    91 fun scope_of_thm global assms chained th =
    92   if is_chained chained th then Chained
    93   else if global then Global
    94   else if is_assum assms th then Assum
    95   else Local
    96 
    97 val may_be_induction =
    98   exists_subterm (fn Var (_, Type (@{type_name fun}, [_, T])) =>
    99                      body_type T = @{typ bool}
   100                    | _ => false)
   101 
   102 fun status_of_thm css name th =
   103   (* FIXME: use structured name *)
   104   if (String.isSubstring ".induct" name orelse
   105       String.isSubstring ".inducts" name) andalso
   106      may_be_induction (prop_of th) then
   107     Induction
   108   else case Termtab.lookup css (prop_of th) of
   109     SOME status => status
   110   | NONE => General
   111 
   112 fun stature_of_thm global assms chained css name th =
   113   (scope_of_thm global assms chained th, status_of_thm css name th)
   114 
   115 fun fact_from_ref ctxt reserved chained css (xthm as (xref, args)) =
   116   let
   117     val ths = Attrib.eval_thms ctxt [xthm]
   118     val bracket =
   119       map (enclose "[" "]" o Pretty.str_of o Args.pretty_src ctxt) args
   120       |> implode
   121     fun nth_name j =
   122       case xref of
   123         Facts.Fact s => backquote s ^ bracket
   124       | Facts.Named (("", _), _) => "[" ^ bracket ^ "]"
   125       | Facts.Named ((name, _), NONE) =>
   126         make_name reserved (length ths > 1) (j + 1) name ^ bracket
   127       | Facts.Named ((name, _), SOME intervals) =>
   128         make_name reserved true
   129                  (nth (maps (explode_interval (length ths)) intervals) j) name ^
   130         bracket
   131     fun add_nth th (j, rest) =
   132       let val name = nth_name j in
   133         (j + 1, ((name, stature_of_thm false [] chained css name th), th)
   134                 :: rest)
   135       end
   136   in (0, []) |> fold add_nth ths |> snd end
   137 
   138 (* Reject theorems with names like "List.filter.filter_list_def" or
   139   "Accessible_Part.acc.defs", as these are definitions arising from packages. *)
   140 fun is_package_def a =
   141   let val names = Long_Name.explode a in
   142     (length names > 2 andalso not (hd names = "local") andalso
   143      String.isSuffix "_def" a) orelse String.isSuffix "_defs" a
   144   end
   145 
   146 (* FIXME: put other record thms here, or declare as "no_atp" *)
   147 fun multi_base_blacklist ctxt ho_atp =
   148   ["defs", "select_defs", "update_defs", "split", "splits", "split_asm",
   149    "cases", "ext_cases", "eq.simps", "eq.refl", "nchotomy", "case_cong",
   150    "weak_case_cong"]
   151   |> not (ho_atp orelse (Config.get ctxt instantiate_inducts)) ?
   152         append ["induct", "inducts"]
   153   |> map (prefix ".")
   154 
   155 val max_lambda_nesting = 3 (*only applies if not ho_atp*)
   156 
   157 fun term_has_too_many_lambdas max (t1 $ t2) =
   158     exists (term_has_too_many_lambdas max) [t1, t2]
   159   | term_has_too_many_lambdas max (Abs (_, _, t)) =
   160     max = 0 orelse term_has_too_many_lambdas (max - 1) t
   161   | term_has_too_many_lambdas _ _ = false
   162 
   163 (* Don't count nested lambdas at the level of formulas, since they are
   164    quantifiers. *)
   165 fun formula_has_too_many_lambdas Ts (Abs (_, T, t)) =
   166     formula_has_too_many_lambdas (T :: Ts) t
   167   | formula_has_too_many_lambdas Ts t =
   168     if member (op =) [HOLogic.boolT, propT] (fastype_of1 (Ts, t)) then
   169       exists (formula_has_too_many_lambdas Ts) (#2 (strip_comb t))
   170     else
   171       term_has_too_many_lambdas max_lambda_nesting t
   172 
   173 (* The max apply depth of any "metis" call in "Metis_Examples" (on 2007-10-31)
   174    was 11. *)
   175 val max_apply_depth = 15
   176 
   177 fun apply_depth (f $ t) = Int.max (apply_depth f, apply_depth t + 1)
   178   | apply_depth (Abs (_, _, t)) = apply_depth t
   179   | apply_depth _ = 0
   180 
   181 fun is_formula_too_complex ho_atp t =
   182   apply_depth t > max_apply_depth orelse
   183   (not ho_atp andalso formula_has_too_many_lambdas [] t)
   184 
   185 val exists_sledgehammer_const =
   186   exists_Const (fn (s, _) =>
   187       exists (fn pref => String.isPrefix pref s) sledgehammer_prefixes)
   188 
   189 (* FIXME: make more reliable *)
   190 val exists_low_level_class_const =
   191   exists_Const (fn (s, _) =>
   192      s = @{const_name equal_class.equal} orelse
   193      String.isSubstring (Long_Name.separator ^ "class" ^ Long_Name.separator) s)
   194 
   195 fun is_that_fact th =
   196   String.isSuffix (Long_Name.separator ^ Obtain.thatN) (Thm.get_name_hint th)
   197   andalso exists_subterm (fn Free (s, _) => s = Name.skolem Auto_Bind.thesisN
   198                            | _ => false) (prop_of th)
   199 
   200 fun is_likely_tautology_or_too_meta th =
   201   let
   202     val is_boring_const = member (op =) atp_widely_irrelevant_consts
   203     fun is_boring_bool t =
   204       not (exists_Const (not o is_boring_const o fst) t) orelse
   205       exists_type (exists_subtype (curry (op =) @{typ prop})) t
   206     fun is_boring_prop (@{const Trueprop} $ t) = is_boring_bool t
   207       | is_boring_prop (@{const "==>"} $ t $ u) =
   208         is_boring_prop t andalso is_boring_prop u
   209       | is_boring_prop (Const (@{const_name all}, _) $ (Abs (_, _, t)) $ u) =
   210         is_boring_prop t andalso is_boring_prop u
   211       | is_boring_prop (Const (@{const_name "=="}, _) $ t $ u) =
   212         is_boring_bool t andalso is_boring_bool u
   213       | is_boring_prop _ = true
   214   in
   215     is_boring_prop (prop_of th) andalso not (Thm.eq_thm_prop (@{thm ext}, th))
   216   end
   217 
   218 fun is_theorem_bad_for_atps ho_atp th =
   219   is_likely_tautology_or_too_meta th orelse
   220   let val t = prop_of th in
   221     is_formula_too_complex ho_atp t orelse
   222     exists_type type_has_top_sort t orelse
   223     exists_sledgehammer_const t orelse exists_low_level_class_const t orelse
   224     is_that_fact th
   225   end
   226 
   227 fun hackish_string_for_term thy t =
   228   Print_Mode.setmp (filter (curry (op =) Symbol.xsymbolsN)
   229                    (print_mode_value ())) (Syntax.string_of_term_global thy) t
   230   |> String.translate (fn c => if Char.isPrint c then str c else "")
   231   |> simplify_spaces
   232 
   233 (* This is a terrible hack. Free variables are sometimes coded as "M__" when
   234    they are displayed as "M" and we want to avoid clashes with these. But
   235    sometimes it's even worse: "Ma__" encodes "M". So we simply reserve all
   236    prefixes of all free variables. In the worse case scenario, where the fact
   237    won't be resolved correctly, the user can fix it manually, e.g., by naming
   238    the fact in question. Ideally we would need nothing of it, but backticks
   239    simply don't work with schematic variables. *)
   240 fun all_prefixes_of s =
   241   map (fn i => String.extract (s, 0, SOME i)) (1 upto size s - 1)
   242 
   243 fun close_form t =
   244   (t, [] |> Term.add_free_names t |> maps all_prefixes_of)
   245   |> fold (fn ((s, i), T) => fn (t', taken) =>
   246               let val s' = singleton (Name.variant_list taken) s in
   247                 ((if fastype_of t' = HOLogic.boolT then HOLogic.all_const
   248                   else Logic.all_const) T
   249                  $ Abs (s', T, abstract_over (Var ((s, i), T), t')),
   250                  s' :: taken)
   251               end)
   252           (Term.add_vars t [] |> sort_wrt (fst o fst))
   253   |> fst
   254 
   255 fun backquote_term thy t =
   256   t |> close_form
   257     |> hackish_string_for_term thy
   258     |> backquote
   259 
   260 fun backquote_thm th = backquote_term (theory_of_thm th) (prop_of th)
   261 
   262 fun clasimpset_rule_table_of ctxt =
   263   let
   264     val thy = Proof_Context.theory_of ctxt
   265     val atomize = HOLogic.mk_Trueprop o Object_Logic.atomize_term thy
   266     fun add stature normalizers get_th =
   267       fold (fn rule =>
   268                let
   269                  val th = rule |> get_th
   270                  val t =
   271                    th |> Thm.maxidx_of th > 0 ? zero_var_indexes |> prop_of
   272                in
   273                  fold (fn normalize => Termtab.update (normalize t, stature))
   274                       (I :: normalizers)
   275                end)
   276     val {safeIs, (* safeEs, *) hazIs, (* hazEs, *) ...} =
   277       ctxt |> claset_of |> Classical.rep_cs
   278     val intros = Item_Net.content safeIs @ Item_Net.content hazIs
   279 (* Add once it is used:
   280     val elims =
   281       Item_Net.content safeEs @ Item_Net.content hazEs
   282       |> map Classical.classical_rule
   283 *)
   284     val simps = ctxt |> simpset_of |> dest_ss |> #simps
   285     val specs = ctxt |> Spec_Rules.get
   286     val (rec_defs, nonrec_defs) =
   287       specs |> filter (curry (op =) Spec_Rules.Equational o fst)
   288             |> maps (snd o snd)
   289             |> filter_out (member Thm.eq_thm_prop risky_defs)
   290             |> List.partition (is_rec_def o prop_of)
   291     val spec_intros =
   292       specs |> filter (member (op =) [Spec_Rules.Inductive,
   293                                       Spec_Rules.Co_Inductive] o fst)
   294             |> maps (snd o snd)
   295   in
   296     Termtab.empty |> add Simp [atomize] snd simps
   297                   |> add Rec_Def [] I rec_defs
   298                   |> add Non_Rec_Def [] I nonrec_defs
   299 (* Add once it is used:
   300                   |> add Elim [] I elims
   301 *)
   302                   |> add Intro [] I intros
   303                   |> add Inductive [] I spec_intros
   304   end
   305 
   306 fun uniquify xs =
   307   Termtab.fold (cons o snd)
   308                (fold (Termtab.update o `(prop_of o snd)) xs Termtab.empty) []
   309 
   310 fun struct_induct_rule_on th =
   311   case Logic.strip_horn (prop_of th) of
   312     (prems, @{const Trueprop}
   313             $ ((p as Var ((p_name, 0), _)) $ (a as Var (_, ind_T)))) =>
   314     if not (is_TVar ind_T) andalso length prems > 1 andalso
   315        exists (exists_subterm (curry (op aconv) p)) prems andalso
   316        not (exists (exists_subterm (curry (op aconv) a)) prems) then
   317       SOME (p_name, ind_T)
   318     else
   319       NONE
   320   | _ => NONE
   321 
   322 fun instantiate_induct_rule ctxt concl_prop p_name ((name, stature), th) ind_x =
   323   let
   324     val thy = Proof_Context.theory_of ctxt
   325     fun varify_noninducts (t as Free (s, T)) =
   326         if (s, T) = ind_x orelse can dest_funT T then t else Var ((s, 0), T)
   327       | varify_noninducts t = t
   328     val p_inst =
   329       concl_prop |> map_aterms varify_noninducts |> close_form
   330                  |> lambda (Free ind_x)
   331                  |> hackish_string_for_term thy
   332   in
   333     ((fn () => name () ^ "[where " ^ p_name ^ " = " ^ quote p_inst ^ "]",
   334       stature), th |> read_instantiate ctxt [((p_name, 0), p_inst)])
   335   end
   336 
   337 fun type_match thy (T1, T2) =
   338   (Sign.typ_match thy (T2, T1) Vartab.empty; true)
   339   handle Type.TYPE_MATCH => false
   340 
   341 fun instantiate_if_induct_rule ctxt stmt stmt_xs (ax as (_, th)) =
   342   case struct_induct_rule_on th of
   343     SOME (p_name, ind_T) =>
   344     let val thy = Proof_Context.theory_of ctxt in
   345       stmt_xs |> filter (fn (_, T) => type_match thy (T, ind_T))
   346               |> map_filter (try (instantiate_induct_rule ctxt stmt p_name ax))
   347     end
   348   | NONE => [ax]
   349 
   350 fun external_frees t =
   351   [] |> Term.add_frees t |> filter_out (can Name.dest_internal o fst)
   352 
   353 fun maybe_instantiate_inducts ctxt hyp_ts concl_t =
   354   if Config.get ctxt instantiate_inducts then
   355     let
   356       val thy = Proof_Context.theory_of ctxt
   357       val ind_stmt =
   358         (hyp_ts |> filter_out (null o external_frees), concl_t)
   359         |> Logic.list_implies |> Object_Logic.atomize_term thy
   360       val ind_stmt_xs = external_frees ind_stmt
   361     in maps (instantiate_if_induct_rule ctxt ind_stmt ind_stmt_xs) end
   362   else
   363     I
   364 
   365 fun maybe_filter_no_atps ctxt =
   366   not (Config.get ctxt ignore_no_atp) ? filter_out (No_ATPs.member ctxt o snd)
   367 
   368 fun all_facts ctxt ho_atp reserved add_ths chained css =
   369   let
   370     val thy = Proof_Context.theory_of ctxt
   371     val global_facts = Global_Theory.facts_of thy
   372     val local_facts = Proof_Context.facts_of ctxt
   373     val named_locals = local_facts |> Facts.dest_static []
   374     val assms = Assumption.all_assms_of ctxt
   375     fun is_good_unnamed_local th =
   376       not (Thm.has_name_hint th) andalso
   377       forall (fn (_, ths) => not (member Thm.eq_thm_prop ths th)) named_locals
   378     val unnamed_locals =
   379       union Thm.eq_thm_prop (Facts.props local_facts) chained
   380       |> filter is_good_unnamed_local |> map (pair "" o single)
   381     val full_space =
   382       Name_Space.merge (Facts.space_of global_facts, Facts.space_of local_facts)
   383     fun add_facts global foldx facts =
   384       foldx (fn (name0, ths) =>
   385         if name0 <> "" andalso
   386            forall (not o member Thm.eq_thm_prop add_ths) ths andalso
   387            (Facts.is_concealed facts name0 orelse
   388             not (can (Proof_Context.get_thms ctxt) name0) orelse
   389             (not (Config.get ctxt ignore_no_atp) andalso
   390              is_package_def name0) orelse
   391             exists (fn s => String.isSuffix s name0)
   392                    (multi_base_blacklist ctxt ho_atp)) then
   393           I
   394         else
   395           let
   396             val multi = length ths > 1
   397             fun check_thms a =
   398               case try (Proof_Context.get_thms ctxt) a of
   399                 NONE => false
   400               | SOME ths' => eq_list Thm.eq_thm_prop (ths, ths')
   401           in
   402             pair 1
   403             #> fold (fn th => fn (j, (multis, unis)) =>
   404                         (j + 1,
   405                          if not (member Thm.eq_thm_prop add_ths th) andalso
   406                             is_theorem_bad_for_atps ho_atp th then
   407                            (multis, unis)
   408                          else
   409                            let
   410                              val new =
   411                                (((fn () =>
   412                                      if name0 = "" then
   413                                        backquote_thm th
   414                                      else
   415                                        [Facts.extern ctxt facts name0,
   416                                         Name_Space.extern ctxt full_space name0]
   417                                        |> find_first check_thms
   418                                        |> the_default name0
   419                                        |> make_name reserved multi j),
   420                                   stature_of_thm global assms chained css name0
   421                                                  th), th)
   422                            in
   423                              if multi then (new :: multis, unis)
   424                              else (multis, new :: unis)
   425                            end)) ths
   426             #> snd
   427           end)
   428   in
   429     (* The single-name theorems go after the multiple-name ones, so that single
   430        names are preferred when both are available. *)
   431     ([], []) |> add_facts false fold local_facts (unnamed_locals @ named_locals)
   432              |> add_facts true Facts.fold_static global_facts global_facts
   433              |> op @
   434   end
   435 
   436 fun all_facts_of ctxt css =
   437   all_facts ctxt false Symtab.empty [] [] css
   438   |> rev (* partly restore the original order of facts, for MaSh *)
   439 
   440 fun nearly_all_facts ctxt ho_atp {add, del, only} reserved css chained hyp_ts
   441                      concl_t =
   442   if only andalso null add then
   443     []
   444   else
   445     let
   446       val chained =
   447         chained
   448         |> maps (fn th => insert Thm.eq_thm_prop (zero_var_indexes th) [th])
   449     in
   450       (if only then
   451          maps (map (fn ((name, stature), th) => ((K name, stature), th))
   452                o fact_from_ref ctxt reserved chained css) add
   453        else
   454          let val (add, del) = pairself (Attrib.eval_thms ctxt) (add, del) in
   455            all_facts ctxt ho_atp reserved add chained css
   456            |> filter_out (member Thm.eq_thm_prop del o snd)
   457            |> maybe_filter_no_atps ctxt
   458            |> uniquify
   459          end)
   460       |> maybe_instantiate_inducts ctxt hyp_ts concl_t
   461     end
   462 
   463 end;