blanchet@36375: (* Title: HOL/Tools/Sledgehammer/sledgehammer_isar.ML blanchet@35866: Author: Jasmin Blanchette, TU Muenchen blanchet@35866: blanchet@35866: Adds "sledgehammer" and related commands to Isabelle/Isar's outer syntax. blanchet@35866: *) blanchet@35866: blanchet@35961: signature SLEDGEHAMMER_ISAR = blanchet@35961: sig blanchet@41335: type params = Sledgehammer_Provers.params blanchet@35961: blanchet@39564: val auto : bool Unsynchronized.ref blanchet@40240: val provers : string Unsynchronized.ref blanchet@39564: val timeout : int Unsynchronized.ref blanchet@39564: val full_types : bool Unsynchronized.ref blanchet@40250: val default_params : Proof.context -> (string * string) list -> params blanchet@39564: val setup : theory -> theory blanchet@35961: end; blanchet@35961: blanchet@35969: structure Sledgehammer_Isar : SLEDGEHAMMER_ISAR = blanchet@35866: struct blanchet@35866: blanchet@38262: open ATP_Systems blanchet@35969: open Sledgehammer_Util blanchet@41386: open Sledgehammer_ATP_Translate blanchet@41335: open Sledgehammer_Provers blanchet@39232: open Sledgehammer_Minimize blanchet@41335: open Sledgehammer_Run blanchet@35866: blanchet@39564: val auto = Unsynchronized.ref false blanchet@39564: blanchet@39564: val _ = blanchet@39564: ProofGeneralPgip.add_preference Preferences.category_tracing blanchet@39564: (Preferences.bool_pref auto "auto-sledgehammer" blanchet@39573: "Run Sledgehammer automatically.") blanchet@39564: blanchet@36394: (** Sledgehammer commands **) blanchet@36394: blanchet@39564: val no_relevance_override = {add = [], del = [], only = false} blanchet@39564: fun add_relevance_override ns : relevance_override = blanchet@35964: {add = ns, del = [], only = false} blanchet@39564: fun del_relevance_override ns : relevance_override = blanchet@35964: {add = [], del = ns, only = false} blanchet@35964: fun only_relevance_override ns : relevance_override = blanchet@35964: {add = ns, del = [], only = true} blanchet@36003: fun merge_relevance_override_pairwise (r1 : relevance_override) blanchet@36003: (r2 : relevance_override) = blanchet@35964: {add = #add r1 @ #add r2, del = #del r1 @ #del r2, blanchet@36183: only = #only r1 andalso #only r2} blanchet@36188: fun merge_relevance_overrides rs = blanchet@36188: fold merge_relevance_override_pairwise rs (only_relevance_override []) blanchet@35963: blanchet@36371: (*** parameters ***) blanchet@36371: blanchet@40240: val provers = Unsynchronized.ref "" blanchet@39581: val timeout = Unsynchronized.ref 30 blanchet@36371: val full_types = Unsynchronized.ref false blanchet@36371: blanchet@36371: val _ = blanchet@36371: ProofGeneralPgip.add_preference Preferences.category_proof blanchet@40240: (Preferences.string_pref provers blanchet@40240: "Sledgehammer: Provers" blanchet@36371: "Default automatic provers (separated by whitespace)") blanchet@36371: blanchet@36371: val _ = blanchet@36371: ProofGeneralPgip.add_preference Preferences.category_proof blanchet@36371: (Preferences.int_pref timeout blanchet@36373: "Sledgehammer: Time Limit" blanchet@36371: "ATPs will be interrupted after this time (in seconds)") blanchet@36371: blanchet@36371: val _ = blanchet@36371: ProofGeneralPgip.add_preference Preferences.category_proof blanchet@36371: (Preferences.bool_pref full_types blanchet@36373: "Sledgehammer: Full Types" "ATPs will use full type information") blanchet@36371: blanchet@35961: type raw_param = string * string list blanchet@35866: blanchet@35961: val default_default_params = blanchet@41456: [("debug", "false"), blanchet@35961: ("verbose", "false"), blanchet@36141: ("overlord", "false"), blanchet@41456: ("blocking", "false"), blanchet@43394: ("type_sys", "smart"), blanchet@43051: ("relevance_thresholds", "0.45 0.85"), blanchet@43051: ("max_relevant", "smart"), blanchet@43654: ("max_mono_iters", "3"), blanchet@43654: ("max_new_mono_instances", "400"), blanchet@36235: ("explicit_apply", "false"), blanchet@35961: ("isar_proof", "false"), blanchet@43314: ("isar_shrink_factor", "1"), blanchet@43314: ("slicing", "true")] blanchet@35866: blanchet@36138: val alias_params = blanchet@40240: [("prover", "provers"), blanchet@40240: ("atps", "provers"), (* FIXME: legacy *) blanchet@40240: ("atp", "provers")] (* FIXME: legacy *) blanchet@36138: val negated_alias_params = blanchet@41456: [("no_debug", "debug"), blanchet@35961: ("quiet", "verbose"), blanchet@36141: ("no_overlord", "overlord"), blanchet@41456: ("non_blocking", "blocking"), blanchet@36399: ("partial_types", "full_types"), blanchet@36235: ("implicit_apply", "explicit_apply"), blanchet@43314: ("no_isar_proof", "isar_proof"), blanchet@43314: ("no_slicing", "slicing")] blanchet@35866: blanchet@36281: val params_for_minimize = blanchet@43589: ["debug", "verbose", "overlord", "type_sys", "full_types", "max_mono_iters", blanchet@43605: "max_new_mono_instances", "isar_proof", "isar_shrink_factor", "timeout"] blanchet@36281: blanchet@40240: val property_dependent_params = ["provers", "full_types", "timeout"] blanchet@35866: blanchet@35961: fun is_known_raw_param s = blanchet@35961: AList.defined (op =) default_default_params s orelse blanchet@36138: AList.defined (op =) alias_params s orelse blanchet@36138: AList.defined (op =) negated_alias_params s orelse blanchet@39229: member (op =) property_dependent_params s orelse s = "expect" blanchet@35866: blanchet@41504: fun is_prover_list ctxt s = blanchet@41504: case space_explode " " s of blanchet@42591: ss as _ :: _ => forall (is_prover_supported ctxt) ss blanchet@41504: | _ => false blanchet@41504: blanchet@43647: (* Secret feature: "provers =" and "type_sys =" can be left out. *) blanchet@41504: fun check_and_repair_raw_param ctxt (name, value) = blanchet@43647: if is_known_raw_param name then blanchet@43647: (name, value) blanchet@43647: else if is_prover_list ctxt name andalso null value then blanchet@43647: ("provers", [name]) blanchet@43647: else if can type_sys_from_string name andalso null value then blanchet@43647: ("type_sys", [name]) blanchet@43647: else blanchet@43647: error ("Unknown parameter: " ^ quote name ^ ".") blanchet@35961: blanchet@36138: fun unalias_raw_param (name, value) = blanchet@36138: case AList.lookup (op =) alias_params name of blanchet@36138: SOME name' => (name', value) blanchet@36138: | NONE => blanchet@36138: case AList.lookup (op =) negated_alias_params name of blanchet@36138: SOME name' => (name', case value of blanchet@36138: ["false"] => ["true"] blanchet@36138: | ["true"] => ["false"] blanchet@36138: | [] => ["false"] blanchet@36138: | _ => value) blanchet@36138: | NONE => (name, value) blanchet@35961: blanchet@43850: (* Ensure that type systems such as "simple!" and "mangled_preds?" are handled blanchet@43850: correctly. *) blanchet@43850: fun implode_param [s, "?"] = s ^ "?" blanchet@43850: | implode_param [s, "!"] = s ^ "!" blanchet@43850: | implode_param ss = space_implode " " ss blanchet@43850: wenzelm@41720: structure Data = Theory_Data wenzelm@41720: ( blanchet@35961: type T = raw_param list blanchet@35961: val empty = default_default_params |> map (apsnd single) blanchet@35961: val extend = I wenzelm@41720: fun merge data : T = AList.merge (op =) (K true) data wenzelm@41720: ) blanchet@35961: blanchet@42591: fun remotify_prover_if_supported_and_not_already_remote ctxt name = blanchet@40253: if String.isPrefix remote_prefix name then blanchet@40253: SOME name blanchet@40253: else blanchet@40253: let val remote_name = remote_prefix ^ name in blanchet@42591: if is_prover_supported ctxt remote_name then SOME remote_name else NONE blanchet@40253: end blanchet@40253: blanchet@40253: fun remotify_prover_if_not_installed ctxt name = blanchet@42591: if is_prover_supported ctxt name andalso is_prover_installed ctxt name then blanchet@41189: SOME name blanchet@41189: else blanchet@42591: remotify_prover_if_supported_and_not_already_remote ctxt name blanchet@40253: blanchet@43850: fun avoid_too_many_threads _ _ [] = [] blanchet@43850: | avoid_too_many_threads _ (0, 0) _ = [] blanchet@43850: | avoid_too_many_threads ctxt (0, max_remote) provers = blanchet@43850: provers blanchet@43850: |> map_filter (remotify_prover_if_supported_and_not_already_remote ctxt) blanchet@43850: |> take max_remote blanchet@43850: | avoid_too_many_threads _ (max_local, 0) provers = blanchet@43850: provers blanchet@43850: |> filter_out (String.isPrefix remote_prefix) blanchet@43850: |> take max_local blanchet@43850: | avoid_too_many_threads ctxt max_local_and_remote (prover :: provers) = blanchet@43850: let blanchet@43850: val max_local_and_remote = blanchet@43850: max_local_and_remote blanchet@43850: |> (if String.isPrefix remote_prefix prover then apsnd else apfst) blanchet@43850: (Integer.add ~1) blanchet@43850: in prover :: avoid_too_many_threads ctxt max_local_and_remote provers end blanchet@40240: blanchet@43850: val max_default_remote_threads = 4 blanchet@43558: blanchet@40240: (* The first ATP of the list is used by Auto Sledgehammer. Because of the low blanchet@40240: timeout, it makes sense to put SPASS first. *) blanchet@40250: fun default_provers_param_value ctxt = blanchet@43850: [spassN, eN, vampireN, SMT_Solver.solver_name_of ctxt, sine_eN, waldmeisterN] blanchet@41189: |> map_filter (remotify_prover_if_not_installed ctxt) blanchet@43850: |> avoid_too_many_threads ctxt (Multithreading.max_threads_value (), blanchet@43850: max_default_remote_threads) blanchet@43647: |> implode_param blanchet@40240: blanchet@36138: val set_default_raw_param = Data.map o AList.update (op =) o unalias_raw_param blanchet@40250: fun default_raw_params ctxt = wenzelm@43232: let val thy = Proof_Context.theory_of ctxt in blanchet@40250: Data.get thy blanchet@40250: |> fold (AList.default (op =)) blanchet@40250: [("provers", [case !provers of blanchet@40250: "" => default_provers_param_value ctxt blanchet@40250: | s => s]), blanchet@40250: ("full_types", [if !full_types then "true" else "false"]), blanchet@40250: ("timeout", let val timeout = !timeout in blanchet@40250: [if timeout <= 0 then "none" blanchet@40582: else string_of_int timeout] blanchet@40250: end)] blanchet@40250: end blanchet@35961: blanchet@35961: val infinity_time_in_secs = 60 * 60 * 24 * 365 blanchet@35961: val the_timeout = the_default (Time.fromSeconds infinity_time_in_secs) blanchet@35961: blanchet@39564: fun extract_params auto default_params override_params = blanchet@35961: let blanchet@36138: val override_params = map unalias_raw_param override_params blanchet@35961: val raw_params = rev override_params @ rev default_params blanchet@43647: val lookup = Option.map implode_param o AList.lookup (op =) raw_params blanchet@35961: val lookup_string = the_default "" o lookup blanchet@35961: fun general_lookup_bool option default_value name = blanchet@35961: case lookup name of blanchet@35968: SOME s => parse_bool_option option name s blanchet@35961: | NONE => default_value blanchet@35961: val lookup_bool = the o general_lookup_bool false (SOME false) blanchet@35961: fun lookup_time name = blanchet@39564: case lookup name of blanchet@39564: SOME s => parse_time_option name s blanchet@39564: | NONE => NONE blanchet@35964: fun lookup_int name = blanchet@35961: case lookup name of blanchet@35964: NONE => 0 blanchet@35964: | SOME s => case Int.fromString s of blanchet@35964: SOME n => n blanchet@35964: | NONE => error ("Parameter " ^ quote name ^ blanchet@35964: " must be assigned an integer value.") blanchet@40584: fun lookup_real_pair name = blanchet@38984: case lookup name of blanchet@40584: NONE => (0.0, 0.0) blanchet@40584: | SOME s => case s |> space_explode " " |> map Real.fromString of blanchet@40584: [SOME r1, SOME r2] => (r1, r2) blanchet@38984: | _ => error ("Parameter " ^ quote name ^ blanchet@40584: "must be assigned a pair of floating-point \ blanchet@40584: \values (e.g., \"0.6 0.95\")") blanchet@38812: fun lookup_int_option name = blanchet@38812: case lookup name of blanchet@38812: SOME "smart" => NONE blanchet@38812: | _ => SOME (lookup_int name) blanchet@39564: val debug = not auto andalso lookup_bool "debug" blanchet@39564: val verbose = debug orelse (not auto andalso lookup_bool "verbose") blanchet@36141: val overlord = lookup_bool "overlord" blanchet@43836: val blocking = blanchet@43836: Isabelle_Process.is_active () orelse auto orelse debug orelse blanchet@43836: lookup_bool "blocking" blanchet@40240: val provers = lookup_string "provers" |> space_explode " " blanchet@40240: |> auto ? single o hd blanchet@43419: val type_sys = blanchet@43601: if auto then blanchet@43601: Smart_Type_Sys true blanchet@43601: else case lookup_string "type_sys" of blanchet@43484: "smart" => Smart_Type_Sys (lookup_bool "full_types") blanchet@43484: | s => Dumb_Type_Sys (type_sys_from_string s) blanchet@43051: val relevance_thresholds = lookup_real_pair "relevance_thresholds" blanchet@43051: val max_relevant = lookup_int_option "max_relevant" blanchet@43589: val max_mono_iters = lookup_int "max_mono_iters" blanchet@43605: val max_new_mono_instances = lookup_int "max_new_mono_instances" blanchet@36235: val explicit_apply = lookup_bool "explicit_apply" blanchet@35961: val isar_proof = lookup_bool "isar_proof" blanchet@36916: val isar_shrink_factor = Int.max (1, lookup_int "isar_shrink_factor") blanchet@43315: val slicing = not auto andalso lookup_bool "slicing" blanchet@39564: val timeout = (if auto then NONE else lookup_time "timeout") |> the_timeout blanchet@39229: val expect = lookup_string "expect" blanchet@35961: in blanchet@41456: {debug = debug, verbose = verbose, overlord = overlord, blocking = blocking, blanchet@43051: provers = provers, relevance_thresholds = relevance_thresholds, blanchet@43589: max_relevant = max_relevant, max_mono_iters = max_mono_iters, blanchet@43605: max_new_mono_instances = max_new_mono_instances, type_sys = type_sys, blanchet@43589: explicit_apply = explicit_apply, isar_proof = isar_proof, blanchet@43589: isar_shrink_factor = isar_shrink_factor, slicing = slicing, blanchet@43589: timeout = timeout, expect = expect} blanchet@35961: end blanchet@35961: blanchet@40250: fun get_params auto ctxt = extract_params auto (default_raw_params ctxt) blanchet@39564: fun default_params thy = get_params false thy o map (apsnd single) blanchet@35866: blanchet@36373: (* Sledgehammer the given subgoal *) blanchet@36373: blanchet@36373: val sledgehammerN = "sledgehammer" blanchet@36373: val sledgehammer_paramsN = "sledgehammer_params" blanchet@36373: blanchet@35963: val runN = "run" blanchet@43849: val minN = "min" blanchet@35963: val messagesN = "messages" blanchet@42591: val supported_proversN = "supported_provers" blanchet@40240: val running_proversN = "running_provers" blanchet@40240: val kill_proversN = "kill_provers" blanchet@35963: val refresh_tptpN = "refresh_tptp" blanchet@35866: blanchet@36281: val is_raw_param_relevant_for_minimize = blanchet@36281: member (op =) params_for_minimize o fst o unalias_raw_param blanchet@36281: fun string_for_raw_param (key, values) = blanchet@43647: key ^ (case implode_param values of "" => "" | value => " = " ^ value) blanchet@36281: blanchet@40240: fun minimize_command override_params i prover_name fact_names = blanchet@43849: sledgehammerN ^ " " ^ minN ^ " [prover = " ^ prover_name ^ blanchet@36281: (override_params |> filter is_raw_param_relevant_for_minimize blanchet@36281: |> implode o map (prefix ", " o string_for_raw_param)) ^ blanchet@37165: "] (" ^ space_implode " " fact_names ^ ")" ^ blanchet@36281: (if i = 1 then "" else " " ^ string_of_int i) blanchet@36281: blanchet@35964: fun hammer_away override_params subcommand opt_i relevance_override state = blanchet@35961: let blanchet@40250: val ctxt = Proof.context_of state blanchet@41504: val override_params = blanchet@41504: override_params |> map (check_and_repair_raw_param ctxt) blanchet@35963: in blanchet@35963: if subcommand = runN then blanchet@36281: let val i = the_default 1 opt_i in blanchet@43315: run_sledgehammer (get_params false ctxt override_params) false i blanchet@39564: relevance_override (minimize_command override_params i) blanchet@39564: state blanchet@39564: |> K () blanchet@36281: end blanchet@43849: else if subcommand = minN then blanchet@40250: run_minimize (get_params false ctxt override_params) (the_default 1 opt_i) blanchet@38813: (#add relevance_override) state blanchet@35963: else if subcommand = messagesN then blanchet@35963: messages opt_i blanchet@42591: else if subcommand = supported_proversN then blanchet@42591: supported_provers ctxt blanchet@40240: else if subcommand = running_proversN then blanchet@40240: running_provers () blanchet@40240: else if subcommand = kill_proversN then blanchet@40240: kill_provers () blanchet@35963: else if subcommand = refresh_tptpN then blanchet@35963: refresh_systems_on_tptp () blanchet@35963: else blanchet@35963: error ("Unknown subcommand: " ^ quote subcommand ^ ".") blanchet@35963: end blanchet@35961: blanchet@35964: fun sledgehammer_trans (((subcommand, params), relevance_override), opt_i) = blanchet@35964: Toplevel.keep (hammer_away params subcommand opt_i relevance_override blanchet@35964: o Toplevel.proof_of) blanchet@35961: blanchet@43647: fun string_for_raw_param (name, value) = name ^ " = " ^ implode_param value blanchet@35961: blanchet@35963: fun sledgehammer_params_trans params = blanchet@35961: Toplevel.theory blanchet@35963: (fold set_default_raw_param params blanchet@39564: #> tap (fn thy => wenzelm@43232: let val ctxt = Proof_Context.init_global thy in blanchet@40250: writeln ("Default parameters for Sledgehammer:\n" ^ blanchet@40250: (case default_raw_params ctxt |> rev of blanchet@40250: [] => "none" blanchet@40250: | params => blanchet@41504: params |> map (check_and_repair_raw_param ctxt) blanchet@41504: |> map string_for_raw_param blanchet@41504: |> sort_strings |> cat_lines)) blanchet@40250: end)) blanchet@35961: blanchet@43647: val parse_key = blanchet@43647: Scan.repeat1 (Parse.typ_group || Parse.$$$ "?" || Parse.$$$ "!") blanchet@43647: >> implode_param blanchet@43452: val parse_value = blanchet@43460: Scan.repeat1 (Parse.xname || Parse.float_number || Parse.$$$ "?" blanchet@43452: || Parse.$$$ "!") wenzelm@36970: val parse_param = parse_key -- Scan.optional (Parse.$$$ "=" |-- parse_value) [] wenzelm@36970: val parse_params = Scan.optional (Args.bracks (Parse.list parse_param)) [] blanchet@35963: val parse_fact_refs = blanchet@39240: Scan.repeat1 (Scan.unless (Parse.name -- Args.colon) Parse_Spec.xthm) blanchet@35964: val parse_relevance_chunk = blanchet@39564: (Args.add |-- Args.colon |-- parse_fact_refs >> add_relevance_override) blanchet@39564: || (Args.del |-- Args.colon |-- parse_fact_refs >> del_relevance_override) blanchet@36188: || (parse_fact_refs >> only_relevance_override) blanchet@35964: val parse_relevance_override = blanchet@36188: Scan.optional (Args.parens (Scan.repeat parse_relevance_chunk blanchet@36188: >> merge_relevance_overrides)) blanchet@39564: no_relevance_override blanchet@35961: val parse_sledgehammer_command = blanchet@37374: (Scan.optional Parse.short_ident runN -- parse_params blanchet@37374: -- parse_relevance_override -- Scan.option Parse.nat) #>> sledgehammer_trans blanchet@35961: val parse_sledgehammer_params_command = blanchet@35961: parse_params #>> sledgehammer_params_trans blanchet@35961: blanchet@35961: val _ = wenzelm@36970: Outer_Syntax.improper_command sledgehammerN wenzelm@36970: "search for first-order proof using automatic theorem provers" Keyword.diag blanchet@36394: parse_sledgehammer_command blanchet@35961: val _ = wenzelm@36970: Outer_Syntax.command sledgehammer_paramsN wenzelm@36970: "set and display the default parameters for Sledgehammer" Keyword.thy_decl blanchet@36394: parse_sledgehammer_params_command blanchet@36394: blanchet@39564: fun auto_sledgehammer state = blanchet@41175: let val ctxt = Proof.context_of state in blanchet@43318: run_sledgehammer (get_params true ctxt []) true 1 no_relevance_override blanchet@43318: (minimize_command [] 1) state blanchet@41175: end blanchet@39564: blanchet@41175: val setup = Auto_Tools.register_tool (auto, auto_sledgehammer) blanchet@39564: blanchet@35866: end;