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