src/HOL/Tools/Sledgehammer/sledgehammer_isar.ML
author blanchet
Fri, 28 May 2010 13:49:21 +0200
changeset 37165 fc1e20373e6a
parent 36970 01594f816e3a
child 37323 bf5d936bb985
permissions -rw-r--r--
make sure chained facts appear in Isar proofs generated by Sledgehammer -- otherwise the proof won't work
     1 (*  Title:      HOL/Tools/Sledgehammer/sledgehammer_isar.ML
     2     Author:     Jasmin Blanchette, TU Muenchen
     3 
     4 Adds "sledgehammer" and related commands to Isabelle/Isar's outer syntax.
     5 *)
     6 
     7 signature SLEDGEHAMMER_ISAR =
     8 sig
     9   type params = ATP_Manager.params
    10 
    11   val atps: string Unsynchronized.ref
    12   val timeout: int Unsynchronized.ref
    13   val full_types: bool Unsynchronized.ref
    14   val default_params : theory -> (string * string) list -> params
    15   val setup: theory -> theory
    16 end;
    17 
    18 structure Sledgehammer_Isar : SLEDGEHAMMER_ISAR =
    19 struct
    20 
    21 open Sledgehammer_Util
    22 open Sledgehammer_Fact_Preprocessor
    23 open ATP_Manager
    24 open ATP_Systems
    25 open Sledgehammer_Fact_Minimizer
    26 
    27 (** Proof method used in Isar proofs **)
    28 
    29 val neg_clausify_setup =
    30   Method.setup @{binding neg_clausify}
    31                (Scan.succeed (SIMPLE_METHOD' o neg_clausify_tac))
    32                "conversion of goal to negated conjecture clauses"
    33 
    34 (** Attribute for converting a theorem into clauses **)
    35 
    36 val parse_clausify_attribute : attribute context_parser =
    37   Scan.lift Parse.nat
    38   >> (fn i => Thm.rule_attribute (fn context => fn th =>
    39                   let val thy = Context.theory_of context in
    40                     Meson.make_meta_clause (nth (cnf_axiom thy th) i)
    41                   end))
    42 
    43 val clausify_setup =
    44   Attrib.setup @{binding clausify} parse_clausify_attribute
    45                "conversion of theorem to clauses"
    46 
    47 (** Sledgehammer commands **)
    48 
    49 fun add_to_relevance_override ns : relevance_override =
    50   {add = ns, del = [], only = false}
    51 fun del_from_relevance_override ns : relevance_override =
    52   {add = [], del = ns, only = false}
    53 fun only_relevance_override ns : relevance_override =
    54   {add = ns, del = [], only = true}
    55 val no_relevance_override = add_to_relevance_override []
    56 fun merge_relevance_override_pairwise (r1 : relevance_override)
    57                                       (r2 : relevance_override) =
    58   {add = #add r1 @ #add r2, del = #del r1 @ #del r2,
    59    only = #only r1 andalso #only r2}
    60 fun merge_relevance_overrides rs =
    61   fold merge_relevance_override_pairwise rs (only_relevance_override [])
    62 
    63 (*** parameters ***)
    64 
    65 val atps = Unsynchronized.ref ""
    66 val timeout = Unsynchronized.ref 60
    67 val full_types = Unsynchronized.ref false
    68 
    69 val _ =
    70   ProofGeneralPgip.add_preference Preferences.category_proof
    71       (Preferences.string_pref atps
    72           "Sledgehammer: ATPs"
    73           "Default automatic provers (separated by whitespace)")
    74 
    75 val _ =
    76   ProofGeneralPgip.add_preference Preferences.category_proof
    77       (Preferences.int_pref timeout
    78           "Sledgehammer: Time Limit"
    79           "ATPs will be interrupted after this time (in seconds)")
    80 
    81 val _ =
    82   ProofGeneralPgip.add_preference Preferences.category_proof
    83       (Preferences.bool_pref full_types
    84           "Sledgehammer: Full Types" "ATPs will use full type information")
    85 
    86 type raw_param = string * string list
    87 
    88 val default_default_params =
    89   [("debug", "false"),
    90    ("verbose", "false"),
    91    ("overlord", "false"),
    92    ("explicit_apply", "false"),
    93    ("respect_no_atp", "true"),
    94    ("relevance_threshold", "50"),
    95    ("relevance_convergence", "320"),
    96    ("theory_relevant", "smart"),
    97    ("defs_relevant", "false"),
    98    ("isar_proof", "false"),
    99    ("isar_shrink_factor", "1"),
   100    ("minimize_timeout", "5 s")]
   101 
   102 val alias_params =
   103   [("atp", "atps")]
   104 val negated_alias_params =
   105   [("no_debug", "debug"),
   106    ("quiet", "verbose"),
   107    ("no_overlord", "overlord"),
   108    ("partial_types", "full_types"),
   109    ("implicit_apply", "explicit_apply"),
   110    ("ignore_no_atp", "respect_no_atp"),
   111    ("theory_irrelevant", "theory_relevant"),
   112    ("defs_irrelevant", "defs_relevant"),
   113    ("no_isar_proof", "isar_proof")]
   114 
   115 val params_for_minimize =
   116   ["debug", "verbose", "overlord", "full_types", "explicit_apply",
   117    "isar_proof", "isar_shrink_factor", "minimize_timeout"]
   118 
   119 val property_dependent_params = ["atps", "full_types", "timeout"]
   120 
   121 fun is_known_raw_param s =
   122   AList.defined (op =) default_default_params s orelse
   123   AList.defined (op =) alias_params s orelse
   124   AList.defined (op =) negated_alias_params s orelse
   125   member (op =) property_dependent_params s
   126 
   127 fun check_raw_param (s, _) =
   128   if is_known_raw_param s then ()
   129   else error ("Unknown parameter: " ^ quote s ^ ".")
   130 
   131 fun unalias_raw_param (name, value) =
   132   case AList.lookup (op =) alias_params name of
   133     SOME name' => (name', value)
   134   | NONE =>
   135     case AList.lookup (op =) negated_alias_params name of
   136       SOME name' => (name', case value of
   137                               ["false"] => ["true"]
   138                             | ["true"] => ["false"]
   139                             | [] => ["false"]
   140                             | _ => value)
   141     | NONE => (name, value)
   142 
   143 structure Data = Theory_Data(
   144   type T = raw_param list
   145   val empty = default_default_params |> map (apsnd single)
   146   val extend = I
   147   fun merge p : T = AList.merge (op =) (K true) p)
   148 
   149 val set_default_raw_param = Data.map o AList.update (op =) o unalias_raw_param
   150 fun default_raw_params thy =
   151   Data.get thy
   152   |> fold (AList.default (op =))
   153           [("atps", [case !atps of "" => default_atps_param_value () | s => s]),
   154            ("full_types", [if !full_types then "true" else "false"]),
   155            ("timeout", let val timeout = !timeout in
   156                          [if timeout <= 0 then "none"
   157                           else string_of_int timeout ^ " s"]
   158                        end)]
   159 
   160 val infinity_time_in_secs = 60 * 60 * 24 * 365
   161 val the_timeout = the_default (Time.fromSeconds infinity_time_in_secs)
   162 
   163 fun extract_params thy default_params override_params =
   164   let
   165     val override_params = map unalias_raw_param override_params
   166     val raw_params = rev override_params @ rev default_params
   167     val lookup = Option.map (space_implode " ") o AList.lookup (op =) raw_params
   168     val lookup_string = the_default "" o lookup
   169     fun general_lookup_bool option default_value name =
   170       case lookup name of
   171         SOME s => parse_bool_option option name s
   172       | NONE => default_value
   173     val lookup_bool = the o general_lookup_bool false (SOME false)
   174     val lookup_bool_option = general_lookup_bool true NONE
   175     fun lookup_time name =
   176       the_timeout (case lookup name of
   177                      NONE => NONE
   178                    | SOME s => parse_time_option name s)
   179     fun lookup_int name =
   180       case lookup name of
   181         NONE => 0
   182       | SOME s => case Int.fromString s of
   183                     SOME n => n
   184                   | NONE => error ("Parameter " ^ quote name ^
   185                                    " must be assigned an integer value.")
   186     val debug = lookup_bool "debug"
   187     val verbose = debug orelse lookup_bool "verbose"
   188     val overlord = lookup_bool "overlord"
   189     val atps = lookup_string "atps" |> space_explode " "
   190     val full_types = lookup_bool "full_types"
   191     val explicit_apply = lookup_bool "explicit_apply"
   192     val respect_no_atp = lookup_bool "respect_no_atp"
   193     val relevance_threshold =
   194       0.01 * Real.fromInt (lookup_int "relevance_threshold")
   195     val relevance_convergence =
   196       0.01 * Real.fromInt (lookup_int "relevance_convergence")
   197     val theory_relevant = lookup_bool_option "theory_relevant"
   198     val defs_relevant = lookup_bool "defs_relevant"
   199     val isar_proof = lookup_bool "isar_proof"
   200     val isar_shrink_factor = Int.max (1, lookup_int "isar_shrink_factor")
   201     val timeout = lookup_time "timeout"
   202     val minimize_timeout = lookup_time "minimize_timeout"
   203   in
   204     {debug = debug, verbose = verbose, overlord = overlord, atps = atps,
   205      full_types = full_types, explicit_apply = explicit_apply,
   206      respect_no_atp = respect_no_atp, relevance_threshold = relevance_threshold,
   207      relevance_convergence = relevance_convergence,
   208      theory_relevant = theory_relevant, defs_relevant = defs_relevant,
   209      isar_proof = isar_proof, isar_shrink_factor = isar_shrink_factor,
   210      timeout = timeout, minimize_timeout = minimize_timeout}
   211   end
   212 
   213 fun get_params thy = extract_params thy (default_raw_params thy)
   214 fun default_params thy = get_params thy o map (apsnd single)
   215 
   216 val subgoal_count = Logic.count_prems o prop_of o #goal o Proof.goal
   217 
   218 (* Sledgehammer the given subgoal *)
   219 
   220 fun run {atps = [], ...} _ _ _ _ = error "No ATP is set."
   221   | run (params as {atps, timeout, ...}) i relevance_override minimize_command
   222         state =
   223     case subgoal_count state of
   224       0 => priority "No subgoal!"
   225     | n =>
   226       let
   227         val birth_time = Time.now ()
   228         val death_time = Time.+ (birth_time, timeout)
   229         val _ = kill_atps ()  (* race w.r.t. other Sledgehammer invocations *)
   230         val _ = priority "Sledgehammering..."
   231         val _ = app (start_prover_thread params birth_time death_time i n
   232                                          relevance_override minimize_command
   233                                          state) atps
   234       in () end
   235 
   236 fun minimize override_params i frefs state =
   237   let
   238     val thy = Proof.theory_of state
   239     val ctxt = Proof.context_of state
   240     val chained_ths = #facts (Proof.goal state)
   241     fun theorems_from_ref ctxt fref =
   242       let
   243         val ths = ProofContext.get_fact ctxt fref
   244         val name = Facts.string_of_ref fref
   245                    |> forall (member Thm.eq_thm chained_ths) ths
   246                       ? prefix chained_prefix
   247       in (name, ths) end
   248     val name_thms_pairs = map (theorems_from_ref ctxt) frefs
   249   in
   250     case subgoal_count state of
   251       0 => priority "No subgoal!"
   252     | n => priority (#2 (minimize_theorems (get_params thy override_params) i n
   253                                            state name_thms_pairs))
   254   end
   255 
   256 val sledgehammerN = "sledgehammer"
   257 val sledgehammer_paramsN = "sledgehammer_params"
   258 
   259 val runN = "run"
   260 val minimizeN = "minimize"
   261 val messagesN = "messages"
   262 val available_atpsN = "available_atps"
   263 val running_atpsN = "running_atps"
   264 val kill_atpsN = "kill_atps"
   265 val refresh_tptpN = "refresh_tptp"
   266 val helpN = "help"
   267 
   268 fun minimizize_raw_param_name "timeout" = "minimize_timeout"
   269   | minimizize_raw_param_name name = name
   270 
   271 val is_raw_param_relevant_for_minimize =
   272   member (op =) params_for_minimize o fst o unalias_raw_param
   273 fun string_for_raw_param (key, values) =
   274   key ^ (case space_implode " " values of "" => "" | value => " = " ^ value)
   275 
   276 fun minimize_command override_params i atp_name fact_names =
   277   sledgehammerN ^ " " ^ minimizeN ^ " [atp = " ^ atp_name ^
   278   (override_params |> filter is_raw_param_relevant_for_minimize
   279                    |> implode o map (prefix ", " o string_for_raw_param)) ^
   280   "] (" ^ space_implode " " fact_names ^ ")" ^
   281   (if i = 1 then "" else " " ^ string_of_int i)
   282 
   283 fun hammer_away override_params subcommand opt_i relevance_override state =
   284   let
   285     val thy = Proof.theory_of state
   286     val _ = app check_raw_param override_params
   287   in
   288     if subcommand = runN then
   289       let val i = the_default 1 opt_i in
   290         run (get_params thy override_params) i relevance_override
   291             (minimize_command override_params i) state
   292       end
   293     else if subcommand = minimizeN then
   294       minimize (map (apfst minimizize_raw_param_name) override_params)
   295                (the_default 1 opt_i) (#add relevance_override) state
   296     else if subcommand = messagesN then
   297       messages opt_i
   298     else if subcommand = available_atpsN then
   299       available_atps thy
   300     else if subcommand = running_atpsN then
   301       running_atps ()
   302     else if subcommand = kill_atpsN then
   303       kill_atps ()
   304     else if subcommand = refresh_tptpN then
   305       refresh_systems_on_tptp ()
   306     else
   307       error ("Unknown subcommand: " ^ quote subcommand ^ ".")
   308   end
   309 
   310 fun sledgehammer_trans (((subcommand, params), relevance_override), opt_i) =
   311   Toplevel.keep (hammer_away params subcommand opt_i relevance_override
   312                  o Toplevel.proof_of)
   313 
   314 fun string_for_raw_param (name, value) = name ^ " = " ^ space_implode " " value
   315 
   316 fun sledgehammer_params_trans params =
   317   Toplevel.theory
   318       (fold set_default_raw_param params
   319        #> tap (fn thy => 
   320                   writeln ("Default parameters for Sledgehammer:\n" ^
   321                            (case rev (default_raw_params thy) of
   322                               [] => "none"
   323                             | params =>
   324                               (map check_raw_param params;
   325                                params |> map string_for_raw_param
   326                                       |> sort_strings |> cat_lines)))))
   327 
   328 val parse_key = Scan.repeat1 Parse.typ_group >> space_implode " "
   329 val parse_value = Scan.repeat1 Parse.xname
   330 val parse_param = parse_key -- Scan.optional (Parse.$$$ "=" |-- parse_value) []
   331 val parse_params = Scan.optional (Args.bracks (Parse.list parse_param)) []
   332 val parse_fact_refs =
   333   Scan.repeat1 (Scan.unless (Parse.name -- Args.colon)
   334                             (Parse.xname -- Scan.option Attrib.thm_sel)
   335                 >> (fn (name, interval) =>
   336                        Facts.Named ((name, Position.none), interval)))
   337 val parse_relevance_chunk =
   338   (Args.add |-- Args.colon |-- parse_fact_refs >> add_to_relevance_override)
   339   || (Args.del |-- Args.colon |-- parse_fact_refs
   340       >> del_from_relevance_override)
   341   || (parse_fact_refs >> only_relevance_override)
   342 val parse_relevance_override =
   343   Scan.optional (Args.parens (Scan.repeat parse_relevance_chunk
   344                               >> merge_relevance_overrides))
   345                 (add_to_relevance_override [])
   346 val parse_sledgehammer_command =
   347   (Scan.optional Parse.short_ident runN -- parse_params -- parse_relevance_override
   348    -- Scan.option Parse.nat) #>> sledgehammer_trans
   349 val parse_sledgehammer_params_command =
   350   parse_params #>> sledgehammer_params_trans
   351 
   352 val _ =
   353   Outer_Syntax.improper_command sledgehammerN
   354       "search for first-order proof using automatic theorem provers" Keyword.diag
   355       parse_sledgehammer_command
   356 val _ =
   357   Outer_Syntax.command sledgehammer_paramsN
   358       "set and display the default parameters for Sledgehammer" Keyword.thy_decl
   359       parse_sledgehammer_params_command
   360 
   361 val setup =
   362   neg_clausify_setup
   363   #> clausify_setup
   364 
   365 end;