src/HOL/Tools/SMT/smt_config.ML
author blanchet
Wed, 02 Oct 2013 22:54:42 +0200
changeset 55178 227908156cd2
parent 53836 abed4121c17e
child 57550 06cc31dff138
permissions -rw-r--r--
make SMT integration slacker w.r.t. bad apples (facts)
     1 (*  Title:      HOL/Tools/SMT/smt_config.ML
     2     Author:     Sascha Boehme, TU Muenchen
     3 
     4 Configuration options and diagnostic tools for SMT.
     5 *)
     6 
     7 signature SMT_CONFIG =
     8 sig
     9   (*solver*)
    10   type solver_info = {
    11     name: string,
    12     class: Proof.context -> SMT_Utils.class,
    13     avail: unit -> bool,
    14     options: Proof.context -> string list }
    15   val add_solver: solver_info -> Context.generic -> Context.generic
    16   val set_solver_options: string * string -> Context.generic -> Context.generic
    17   val is_available: Proof.context -> string -> bool
    18   val available_solvers_of: Proof.context -> string list
    19   val select_solver: string -> Context.generic -> Context.generic
    20   val solver_of: Proof.context -> string
    21   val solver_class_of: Proof.context -> SMT_Utils.class
    22   val solver_options_of: Proof.context -> string list
    23 
    24   (*options*)
    25   val oracle: bool Config.T
    26   val datatypes: bool Config.T
    27   val timeout: real Config.T
    28   val random_seed: int Config.T
    29   val read_only_certificates: bool Config.T
    30   val verbose: bool Config.T
    31   val trace: bool Config.T
    32   val trace_used_facts: bool Config.T
    33   val monomorph_limit: int Config.T
    34   val monomorph_instances: int Config.T
    35   val infer_triggers: bool Config.T
    36   val filter_only_facts: bool Config.T
    37   val debug_files: string Config.T
    38 
    39   (*tools*)
    40   val with_timeout: Proof.context -> ('a -> 'b) -> 'a -> 'b
    41 
    42   (*diagnostics*)
    43   val trace_msg: Proof.context -> ('a -> string) -> 'a -> unit
    44   val verbose_msg: Proof.context -> ('a -> string) -> 'a -> unit
    45 
    46   (*certificates*)
    47   val select_certificates: string -> Context.generic -> Context.generic
    48   val certificates_of: Proof.context -> Cache_IO.cache option
    49 
    50   (*setup*)
    51   val setup: theory -> theory
    52   val print_setup: Proof.context -> unit
    53 end
    54 
    55 structure SMT_Config: SMT_CONFIG =
    56 struct
    57 
    58 (* solver *)
    59 
    60 type solver_info = {
    61   name: string,
    62   class: Proof.context -> SMT_Utils.class,
    63   avail: unit -> bool,
    64   options: Proof.context -> string list }
    65 
    66 (* FIXME just one data slot (record) per program unit *)
    67 structure Solvers = Generic_Data
    68 (
    69   type T = (solver_info * string list) Symtab.table * string option
    70   val empty = (Symtab.empty, NONE)
    71   val extend = I
    72   fun merge ((ss1, s1), (ss2, s2)) =
    73     (Symtab.merge (K true) (ss1, ss2), merge_options (s1, s2))
    74 )
    75 
    76 fun set_solver_options (name, options) =
    77   let val opts = String.tokens (Symbol.is_ascii_blank o str) options
    78   in Solvers.map (apfst (Symtab.map_entry name (apsnd (K opts)))) end
    79 
    80 fun add_solver (info as {name, ...} : solver_info) context =
    81   if Symtab.defined (fst (Solvers.get context)) name then
    82     error ("Solver already registered: " ^ quote name)
    83   else
    84     context
    85     |> Solvers.map (apfst (Symtab.update (name, (info, []))))
    86     |> Context.map_theory (Attrib.setup (Binding.name (name ^ "_options"))
    87         (Scan.lift (@{keyword "="} |-- Args.name) >>
    88           (Thm.declaration_attribute o K o set_solver_options o pair name))
    89         ("Additional command line options for SMT solver " ^ quote name))
    90 
    91 fun all_solvers_of ctxt = Symtab.keys (fst (Solvers.get (Context.Proof ctxt)))
    92 
    93 fun solver_name_of ctxt = snd (Solvers.get (Context.Proof ctxt))
    94 
    95 fun is_available ctxt name =
    96   (case Symtab.lookup (fst (Solvers.get (Context.Proof ctxt))) name of
    97     SOME ({avail, ...}, _) => avail ()
    98   | NONE => false)
    99 
   100 fun available_solvers_of ctxt =
   101   filter (is_available ctxt) (all_solvers_of ctxt)
   102 
   103 fun warn_solver (Context.Proof ctxt) name =
   104       Context_Position.if_visible ctxt
   105         warning ("The SMT solver " ^ quote name ^ " is not installed.")
   106   | warn_solver _ _ = ();
   107 
   108 fun select_solver name context =
   109   let
   110     val ctxt = Context.proof_of context
   111     val upd = Solvers.map (apsnd (K (SOME name)))
   112   in
   113     if not (member (op =) (all_solvers_of ctxt) name) then
   114       error ("Trying to select unknown solver: " ^ quote name)
   115     else if not (is_available ctxt name) then
   116       (warn_solver context name; upd context)
   117     else upd context
   118   end
   119 
   120 fun no_solver_err () = error "No SMT solver selected"
   121 
   122 fun solver_of ctxt =
   123   (case solver_name_of ctxt of
   124     SOME name => name
   125   | NONE => no_solver_err ())
   126 
   127 fun solver_info_of default select ctxt =
   128   (case Solvers.get (Context.Proof ctxt) of
   129     (solvers, SOME name) => select (Symtab.lookup solvers name)
   130   | (_, NONE) => default ())
   131 
   132 fun solver_class_of ctxt =
   133   let fun class_of ({class, ...}: solver_info, _) = class ctxt
   134   in solver_info_of no_solver_err (class_of o the) ctxt end
   135 
   136 fun solver_options_of ctxt =
   137   let
   138     fun all_options NONE = []
   139       | all_options (SOME ({options, ...} : solver_info, opts)) =
   140           opts @ options ctxt
   141   in solver_info_of (K []) all_options ctxt end
   142 
   143 val setup_solver =
   144   Attrib.setup @{binding smt_solver}
   145     (Scan.lift (@{keyword "="} |-- Args.name) >>
   146       (Thm.declaration_attribute o K o select_solver))
   147     "SMT solver configuration"
   148 
   149 
   150 (* options *)
   151 
   152 val oracle = Attrib.setup_config_bool @{binding smt_oracle} (K true)
   153 val datatypes = Attrib.setup_config_bool @{binding smt_datatypes} (K false)
   154 val timeout = Attrib.setup_config_real @{binding smt_timeout} (K 30.0)
   155 val random_seed = Attrib.setup_config_int @{binding smt_random_seed} (K 1)
   156 val read_only_certificates = Attrib.setup_config_bool @{binding smt_read_only_certificates} (K false)
   157 val verbose = Attrib.setup_config_bool @{binding smt_verbose} (K true)
   158 val trace = Attrib.setup_config_bool @{binding smt_trace} (K false)
   159 val trace_used_facts = Attrib.setup_config_bool @{binding smt_trace_used_facts} (K false)
   160 val monomorph_limit = Attrib.setup_config_int @{binding smt_monomorph_limit} (K 10)
   161 val monomorph_instances = Attrib.setup_config_int @{binding smt_monomorph_instances} (K 500)
   162 val infer_triggers = Attrib.setup_config_bool @{binding smt_infer_triggers} (K false)
   163 val filter_only_facts = Attrib.setup_config_bool @{binding smt_filter_only_facts} (K false)
   164 val debug_files = Attrib.setup_config_string @{binding smt_debug_files} (K "")
   165 
   166 
   167 (* diagnostics *)
   168 
   169 fun cond_trace flag f x = if flag then tracing ("SMT: " ^ f x) else ()
   170 
   171 fun verbose_msg ctxt = cond_trace (Config.get ctxt verbose)
   172 
   173 fun trace_msg ctxt = cond_trace (Config.get ctxt trace)
   174 
   175 
   176 (* tools *)
   177 
   178 fun with_timeout ctxt f x =
   179   TimeLimit.timeLimit (seconds (Config.get ctxt timeout)) f x
   180   handle TimeLimit.TimeOut => raise SMT_Failure.SMT SMT_Failure.Time_Out
   181 
   182 
   183 (* certificates *)
   184 
   185 (* FIXME just one data slot (record) per program unit *)
   186 structure Certificates = Generic_Data
   187 (
   188   type T = Cache_IO.cache option
   189   val empty = NONE
   190   val extend = I
   191   fun merge (s, _) = s  (* FIXME merge options!? *)
   192 )
   193 
   194 val get_certificates_path =
   195   Option.map (Cache_IO.cache_path_of) o Certificates.get o Context.Proof
   196 
   197 fun select_certificates name context = context |> Certificates.put (
   198   if name = "" then NONE
   199   else
   200     Path.explode name
   201     |> Path.append (Thy_Load.master_directory (Context.theory_of context))
   202     |> SOME o Cache_IO.unsynchronized_init)
   203 
   204 val certificates_of = Certificates.get o Context.Proof
   205 
   206 val setup_certificates =
   207   Attrib.setup @{binding smt_certificates}
   208     (Scan.lift (@{keyword "="} |-- Args.name) >>
   209       (Thm.declaration_attribute o K o select_certificates))
   210     "SMT certificates configuration"
   211 
   212 
   213 (* setup *)
   214 
   215 val setup =
   216   setup_solver #>
   217   setup_certificates
   218 
   219 fun print_setup ctxt =
   220   let
   221     fun string_of_bool b = if b then "true" else "false"
   222 
   223     val names = available_solvers_of ctxt
   224     val ns = if null names then ["(none)"] else sort_strings names
   225     val n = the_default "(none)" (solver_name_of ctxt)
   226     val opts = solver_options_of ctxt
   227     
   228     val t = string_of_real (Config.get ctxt timeout)
   229 
   230     val certs_filename =
   231       (case get_certificates_path ctxt of
   232         SOME path => Path.print path
   233       | NONE => "(disabled)")
   234   in
   235     Pretty.writeln (Pretty.big_list "SMT setup:" [
   236       Pretty.str ("Current SMT solver: " ^ n),
   237       Pretty.str ("Current SMT solver options: " ^ space_implode " " opts),
   238       Pretty.str_list "Available SMT solvers: "  "" ns,
   239       Pretty.str ("Current timeout: " ^ t ^ " seconds"),
   240       Pretty.str ("With proofs: " ^
   241         string_of_bool (not (Config.get ctxt oracle))),
   242       Pretty.str ("Certificates cache: " ^ certs_filename),
   243       Pretty.str ("Fixed certificates: " ^
   244         string_of_bool (Config.get ctxt read_only_certificates))])
   245   end
   246 
   247 val _ =
   248   Outer_Syntax.improper_command @{command_spec "smt_status"}
   249     "show the available SMT solvers, the currently selected SMT solver, \
   250     \and the values of SMT configuration options"
   251     (Scan.succeed (Toplevel.keep (print_setup o Toplevel.context_of)))
   252 
   253 end