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@41382: ("type_sys", "smart"), blanchet@36235: ("explicit_apply", "false"), blanchet@40584: ("relevance_thresholds", "0.45 0.85"), blanchet@38983: ("max_relevant", "smart"), blanchet@35961: ("isar_proof", "false"), blanchet@38813: ("isar_shrink_factor", "1")] 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@36900: ("no_isar_proof", "isar_proof")] blanchet@35866: blanchet@36281: val params_for_minimize = blanchet@41384: ["debug", "verbose", "overlord", "type_sys", "full_types", "isar_proof", blanchet@38813: "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@41504: ss as _ :: _ => forall (is_prover_available ctxt) ss blanchet@41504: | _ => false blanchet@41504: blanchet@41504: fun check_and_repair_raw_param ctxt (name, value) = blanchet@41504: if is_known_raw_param name then (name, value) blanchet@41504: else if is_prover_list ctxt name andalso null value then ("provers", [name]) blanchet@41504: else 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: 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@41189: fun remotify_prover_if_available_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@41189: if is_prover_available 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@41189: if is_prover_available ctxt name andalso is_prover_installed ctxt name then blanchet@41189: SOME name blanchet@41189: else blanchet@41189: remotify_prover_if_available_and_not_already_remote ctxt name blanchet@40253: blanchet@40253: fun avoid_too_many_local_threads _ _ [] = [] blanchet@41189: | avoid_too_many_local_threads ctxt 0 provers = blanchet@41189: map_filter (remotify_prover_if_available_and_not_already_remote ctxt) blanchet@41189: provers blanchet@41189: | avoid_too_many_local_threads ctxt n (prover :: provers) = blanchet@40253: let val n = if String.isPrefix remote_prefix prover then n else n - 1 in blanchet@41189: prover :: avoid_too_many_local_threads ctxt n provers blanchet@40253: end blanchet@40240: 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@41189: [spassN, eN, vampireN, sine_eN, SMT_Solver.solver_name_of ctxt] blanchet@41189: |> map_filter (remotify_prover_if_not_installed ctxt) blanchet@41189: |> avoid_too_many_local_threads ctxt (Multithreading.max_threads_value ()) blanchet@41189: |> space_implode " " 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 = blanchet@40250: let val thy = ProofContext.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@41401: val smart_full_type_sys = Tags true blanchet@41401: val smart_partial_type_sys = Const_Args blanchet@41401: 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@35961: val lookup = Option.map (space_implode " ") 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@41456: val blocking = auto orelse debug orelse lookup_bool "blocking" blanchet@40240: val provers = lookup_string "provers" |> space_explode " " blanchet@40240: |> auto ? single o hd blanchet@41401: val type_sys = blanchet@41386: case (lookup_string "type_sys", lookup_bool "full_types") of blanchet@41386: ("tags", full_types) => Tags full_types blanchet@41386: | ("preds", full_types) => Preds full_types blanchet@41401: | ("const_args", false) => Const_Args blanchet@41401: | ("none", false) => No_Types blanchet@41401: | (type_sys, full_types) => blanchet@41401: if member (op =) ["const_args", "none", "smart"] type_sys then blanchet@41401: if full_types then smart_full_type_sys else smart_partial_type_sys blanchet@41401: else blanchet@41401: error ("Unknown type system: " ^ quote type_sys ^ ".") blanchet@36235: val explicit_apply = lookup_bool "explicit_apply" blanchet@40584: val relevance_thresholds = lookup_real_pair "relevance_thresholds" blanchet@38983: val max_relevant = lookup_int_option "max_relevant" blanchet@35961: val isar_proof = lookup_bool "isar_proof" blanchet@36916: val isar_shrink_factor = Int.max (1, lookup_int "isar_shrink_factor") 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@41386: provers = provers, type_sys = type_sys, explicit_apply = explicit_apply, blanchet@38984: relevance_thresholds = relevance_thresholds, max_relevant = max_relevant, blanchet@39241: isar_proof = isar_proof, isar_shrink_factor = isar_shrink_factor, blanchet@39241: 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@35963: val minimizeN = "minimize" blanchet@35963: val messagesN = "messages" blanchet@40240: val available_proversN = "available_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@36477: key ^ (case space_implode " " values of "" => "" | value => " = " ^ value) blanchet@36281: blanchet@40240: fun minimize_command override_params i prover_name fact_names = blanchet@40240: sledgehammerN ^ " " ^ minimizeN ^ " [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@40250: 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@35963: else if subcommand = minimizeN 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@40240: else if subcommand = available_proversN then blanchet@41189: available_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@35961: fun string_for_raw_param (name, value) = name ^ " = " ^ space_implode " " 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 => blanchet@40250: let val ctxt = ProofContext.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: wenzelm@36970: val parse_key = Scan.repeat1 Parse.typ_group >> space_implode " " blanchet@40582: val parse_value = Scan.repeat1 (Parse.xname || Parse.float_number) 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@41175: run_sledgehammer (get_params true ctxt []) true 1 no_relevance_override blanchet@41175: (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;