src/HOL/Tools/Sledgehammer/sledgehammer_provers.ML
author blanchet
Fri, 18 Nov 2011 11:47:12 +0100
changeset 46445 7a39df11bcf6
parent 46437 da05ce2de5a8
child 46454 dc9a7ff13e37
permissions -rw-r--r--
be more silent when auto minimizing
     1 (*  Title:      HOL/Tools/Sledgehammer/sledgehammer_provers.ML
     2     Author:     Fabian Immler, TU Muenchen
     3     Author:     Makarius
     4     Author:     Jasmin Blanchette, TU Muenchen
     5 
     6 Generic prover abstraction for Sledgehammer.
     7 *)
     8 
     9 signature SLEDGEHAMMER_PROVERS =
    10 sig
    11   type failure = ATP_Proof.failure
    12   type locality = ATP_Translate.locality
    13   type type_enc = ATP_Translate.type_enc
    14   type reconstructor = ATP_Reconstruct.reconstructor
    15   type play = ATP_Reconstruct.play
    16   type minimize_command = ATP_Reconstruct.minimize_command
    17   type relevance_fudge = Sledgehammer_Filter.relevance_fudge
    18 
    19   datatype mode = Auto_Try | Try | Normal | Auto_Minimize | Minimize
    20 
    21   type params =
    22     {debug: bool,
    23      verbose: bool,
    24      overlord: bool,
    25      blocking: bool,
    26      provers: string list,
    27      type_enc: string option,
    28      sound: bool,
    29      lam_trans: string option,
    30      relevance_thresholds: real * real,
    31      max_relevant: int option,
    32      max_mono_iters: int,
    33      max_new_mono_instances: int,
    34      isar_proof: bool,
    35      isar_shrink_factor: int,
    36      slicing: bool,
    37      timeout: Time.time,
    38      preplay_timeout: Time.time,
    39      expect: string}
    40 
    41   datatype prover_fact =
    42     Untranslated_Fact of (string * locality) * thm |
    43     SMT_Weighted_Fact of (string * locality) * (int option * thm)
    44 
    45   type prover_problem =
    46     {state: Proof.state,
    47      goal: thm,
    48      subgoal: int,
    49      subgoal_count: int,
    50      facts: prover_fact list,
    51      smt_filter: (string * locality) SMT_Solver.smt_filter_data option}
    52 
    53   type prover_result =
    54     {outcome: failure option,
    55      used_facts: (string * locality) list,
    56      run_time: Time.time,
    57      preplay: unit -> play,
    58      message: play -> string,
    59      message_tail: string}
    60 
    61   type prover =
    62     params -> ((string * string list) list -> string -> minimize_command)
    63     -> prover_problem -> prover_result
    64 
    65   val dest_dir : string Config.T
    66   val problem_prefix : string Config.T
    67   val atp_full_names : bool Config.T
    68   val smt_triggers : bool Config.T
    69   val smt_weights : bool Config.T
    70   val smt_weight_min_facts : int Config.T
    71   val smt_min_weight : int Config.T
    72   val smt_max_weight : int Config.T
    73   val smt_max_weight_index : int Config.T
    74   val smt_weight_curve : (int -> int) Unsynchronized.ref
    75   val smt_max_slices : int Config.T
    76   val smt_slice_fact_frac : real Config.T
    77   val smt_slice_time_frac : real Config.T
    78   val smt_slice_min_secs : int Config.T
    79   val das_tool : string
    80   val plain_metis : reconstructor
    81   val select_smt_solver : string -> Proof.context -> Proof.context
    82   val extract_reconstructor :
    83     params -> reconstructor -> string * (string * string list) list
    84   val is_reconstructor : string -> bool
    85   val is_atp : theory -> string -> bool
    86   val is_smt_prover : Proof.context -> string -> bool
    87   val is_ho_atp: Proof.context -> string -> bool  
    88   val is_unit_equational_atp : Proof.context -> string -> bool
    89   val is_prover_supported : Proof.context -> string -> bool
    90   val is_prover_installed : Proof.context -> string -> bool
    91   val default_max_relevant_for_prover : Proof.context -> bool -> string -> int
    92   val is_unit_equality : term -> bool
    93   val is_appropriate_prop_for_prover : Proof.context -> string -> term -> bool
    94   val is_built_in_const_for_prover :
    95     Proof.context -> string -> string * typ -> term list -> bool * term list
    96   val atp_relevance_fudge : relevance_fudge
    97   val smt_relevance_fudge : relevance_fudge
    98   val relevance_fudge_for_prover : Proof.context -> string -> relevance_fudge
    99   val weight_smt_fact :
   100     Proof.context -> int -> ((string * locality) * thm) * int
   101     -> (string * locality) * (int option * thm)
   102   val untranslated_fact : prover_fact -> (string * locality) * thm
   103   val smt_weighted_fact :
   104     Proof.context -> int -> prover_fact * int
   105     -> (string * locality) * (int option * thm)
   106   val supported_provers : Proof.context -> unit
   107   val kill_provers : unit -> unit
   108   val running_provers : unit -> unit
   109   val messages : int option -> unit
   110   val filter_used_facts : ''a list -> (''a * 'b) list -> (''a * 'b) list
   111   val get_prover : Proof.context -> mode -> string -> prover
   112 end;
   113 
   114 structure Sledgehammer_Provers : SLEDGEHAMMER_PROVERS =
   115 struct
   116 
   117 open ATP_Util
   118 open ATP_Problem
   119 open ATP_Proof
   120 open ATP_Systems
   121 open ATP_Translate
   122 open ATP_Reconstruct
   123 open Metis_Tactic
   124 open Sledgehammer_Util
   125 open Sledgehammer_Filter
   126 
   127 (** The Sledgehammer **)
   128 
   129 datatype mode = Auto_Try | Try | Normal | Auto_Minimize | Minimize
   130 
   131 (* Identifier that distinguishes Sledgehammer from other tools that could use
   132    "Async_Manager". *)
   133 val das_tool = "Sledgehammer"
   134 
   135 val reconstructor_names = [metisN, smtN]
   136 val plain_metis = Metis (hd partial_type_encs, combinatorsN)
   137 val is_reconstructor = member (op =) reconstructor_names
   138 
   139 val is_atp = member (op =) o supported_atps
   140 
   141 val select_smt_solver = Context.proof_map o SMT_Config.select_solver
   142 
   143 fun is_smt_prover ctxt = member (op =) (SMT_Solver.available_solvers_of ctxt)
   144 
   145 fun is_atp_for_format is_format ctxt name =
   146   let val thy = Proof_Context.theory_of ctxt in
   147     case try (get_atp thy) name of
   148       SOME {best_slices, ...} =>
   149       exists (fn (_, (_, (_, format, _, _, _))) => is_format format)
   150              (best_slices ctxt)
   151     | NONE => false
   152   end
   153 
   154 val is_unit_equational_atp = is_atp_for_format (curry (op =) CNF_UEQ)
   155 val is_ho_atp = is_atp_for_format is_format_higher_order
   156 
   157 fun is_prover_supported ctxt =
   158   let val thy = Proof_Context.theory_of ctxt in
   159     is_reconstructor orf is_atp thy orf is_smt_prover ctxt
   160   end
   161 
   162 fun is_prover_installed ctxt =
   163   is_reconstructor orf is_smt_prover ctxt orf
   164   is_atp_installed (Proof_Context.theory_of ctxt)
   165 
   166 fun get_slices slicing slices =
   167   (0 upto length slices - 1) ~~ slices |> not slicing ? (List.last #> single)
   168 
   169 val reconstructor_default_max_relevant = 20
   170 
   171 fun default_max_relevant_for_prover ctxt slicing name =
   172   let val thy = Proof_Context.theory_of ctxt in
   173     if is_reconstructor name then
   174       reconstructor_default_max_relevant
   175     else if is_atp thy name then
   176       fold (Integer.max o #1 o snd o snd o snd)
   177            (get_slices slicing (#best_slices (get_atp thy name) ctxt)) 0
   178     else (* is_smt_prover ctxt name *)
   179       SMT_Solver.default_max_relevant ctxt name
   180   end
   181 
   182 fun is_if (@{const_name If}, _) = true
   183   | is_if _ = false
   184 
   185 (* Beware of "if and only if" (which is translated as such) and "If" (which is
   186    translated to conditional equations). *)
   187 fun is_good_unit_equality T t u =
   188   T <> @{typ bool} andalso not (exists (exists_Const is_if) [t, u])
   189 
   190 fun is_unit_equality (@{const Trueprop} $ t) = is_unit_equality t
   191   | is_unit_equality (Const (@{const_name all}, _) $ Abs (_, _, t)) =
   192     is_unit_equality t
   193   | is_unit_equality (Const (@{const_name All}, _) $ Abs (_, _, t)) =
   194     is_unit_equality t
   195   | is_unit_equality (Const (@{const_name "=="}, Type (_, [T, _])) $ t $ u) =
   196     is_good_unit_equality T t u
   197   | is_unit_equality (Const (@{const_name HOL.eq}, Type (_ , [T, _])) $ t $ u) =
   198     is_good_unit_equality T t u
   199   | is_unit_equality _ = false
   200 
   201 fun is_appropriate_prop_for_prover ctxt name =
   202   if is_unit_equational_atp ctxt name then is_unit_equality else K true
   203 
   204 fun is_built_in_const_for_prover ctxt name =
   205   if is_smt_prover ctxt name then
   206     let val ctxt = ctxt |> select_smt_solver name in
   207       fn x => fn ts =>
   208          if SMT_Builtin.is_builtin_num_ext ctxt (list_comb (Const x, ts)) then
   209            (true, [])
   210          else if SMT_Builtin.is_builtin_fun_ext ctxt x ts then
   211            (true, ts)
   212          else
   213            (false, ts)
   214     end
   215   else
   216     fn (s, _) => fn ts => (member (op =) atp_irrelevant_consts s, ts)
   217 
   218 (* FUDGE *)
   219 val atp_relevance_fudge =
   220   {local_const_multiplier = 1.5,
   221    worse_irrel_freq = 100.0,
   222    higher_order_irrel_weight = 1.05,
   223    abs_rel_weight = 0.5,
   224    abs_irrel_weight = 2.0,
   225    skolem_irrel_weight = 0.25,
   226    theory_const_rel_weight = 0.5,
   227    theory_const_irrel_weight = 0.25,
   228    chained_const_irrel_weight = 0.25,
   229    intro_bonus = 0.15,
   230    elim_bonus = 0.15,
   231    simp_bonus = 0.15,
   232    local_bonus = 0.55,
   233    assum_bonus = 1.05,
   234    chained_bonus = 1.5,
   235    max_imperfect = 11.5,
   236    max_imperfect_exp = 1.0,
   237    threshold_divisor = 2.0,
   238    ridiculous_threshold = 0.01}
   239 
   240 (* FUDGE (FIXME) *)
   241 val smt_relevance_fudge =
   242   {local_const_multiplier = #local_const_multiplier atp_relevance_fudge,
   243    worse_irrel_freq = #worse_irrel_freq atp_relevance_fudge,
   244    higher_order_irrel_weight = #higher_order_irrel_weight atp_relevance_fudge,
   245    abs_rel_weight = #abs_rel_weight atp_relevance_fudge,
   246    abs_irrel_weight = #abs_irrel_weight atp_relevance_fudge,
   247    skolem_irrel_weight = #skolem_irrel_weight atp_relevance_fudge,
   248    theory_const_rel_weight = #theory_const_rel_weight atp_relevance_fudge,
   249    theory_const_irrel_weight = #theory_const_irrel_weight atp_relevance_fudge,
   250    chained_const_irrel_weight = #chained_const_irrel_weight atp_relevance_fudge,
   251    intro_bonus = #intro_bonus atp_relevance_fudge,
   252    elim_bonus = #elim_bonus atp_relevance_fudge,
   253    simp_bonus = #simp_bonus atp_relevance_fudge,
   254    local_bonus = #local_bonus atp_relevance_fudge,
   255    assum_bonus = #assum_bonus atp_relevance_fudge,
   256    chained_bonus = #chained_bonus atp_relevance_fudge,
   257    max_imperfect = #max_imperfect atp_relevance_fudge,
   258    max_imperfect_exp = #max_imperfect_exp atp_relevance_fudge,
   259    threshold_divisor = #threshold_divisor atp_relevance_fudge,
   260    ridiculous_threshold = #ridiculous_threshold atp_relevance_fudge}
   261 
   262 fun relevance_fudge_for_prover ctxt name =
   263   if is_smt_prover ctxt name then smt_relevance_fudge else atp_relevance_fudge
   264 
   265 fun supported_provers ctxt =
   266   let
   267     val thy = Proof_Context.theory_of ctxt
   268     val (remote_provers, local_provers) =
   269       reconstructor_names @
   270       sort_strings (supported_atps thy) @
   271       sort_strings (SMT_Solver.available_solvers_of ctxt)
   272       |> List.partition (String.isPrefix remote_prefix)
   273   in
   274     Output.urgent_message ("Supported provers: " ^
   275                            commas (local_provers @ remote_provers) ^ ".")
   276   end
   277 
   278 fun kill_provers () = Async_Manager.kill_threads das_tool "prover"
   279 fun running_provers () = Async_Manager.running_threads das_tool "prover"
   280 val messages = Async_Manager.thread_messages das_tool "prover"
   281 
   282 (** problems, results, ATPs, etc. **)
   283 
   284 type params =
   285   {debug: bool,
   286    verbose: bool,
   287    overlord: bool,
   288    blocking: bool,
   289    provers: string list,
   290    type_enc: string option,
   291    sound: bool,
   292    lam_trans: string option,
   293    relevance_thresholds: real * real,
   294    max_relevant: int option,
   295    max_mono_iters: int,
   296    max_new_mono_instances: int,
   297    isar_proof: bool,
   298    isar_shrink_factor: int,
   299    slicing: bool,
   300    timeout: Time.time,
   301    preplay_timeout: Time.time,
   302    expect: string}
   303 
   304 datatype prover_fact =
   305   Untranslated_Fact of (string * locality) * thm |
   306   SMT_Weighted_Fact of (string * locality) * (int option * thm)
   307 
   308 type prover_problem =
   309   {state: Proof.state,
   310    goal: thm,
   311    subgoal: int,
   312    subgoal_count: int,
   313    facts: prover_fact list,
   314    smt_filter: (string * locality) SMT_Solver.smt_filter_data option}
   315 
   316 type prover_result =
   317   {outcome: failure option,
   318    used_facts: (string * locality) list,
   319    run_time: Time.time,
   320    preplay: unit -> play,
   321    message: play -> string,
   322    message_tail: string}
   323 
   324 type prover =
   325   params -> ((string * string list) list -> string -> minimize_command)
   326   -> prover_problem -> prover_result
   327 
   328 (* configuration attributes *)
   329 
   330 (* Empty string means create files in Isabelle's temporary files directory. *)
   331 val dest_dir =
   332   Attrib.setup_config_string @{binding sledgehammer_dest_dir} (K "")
   333 val problem_prefix =
   334   Attrib.setup_config_string @{binding sledgehammer_problem_prefix} (K "prob")
   335 
   336 (* In addition to being easier to read, readable names are often much shorter,
   337    especially if types are mangled in names. This makes a difference for some
   338    provers (e.g., E). For these reason, short names are enabled by default. *)
   339 val atp_full_names =
   340   Attrib.setup_config_bool @{binding sledgehammer_atp_full_names} (K false)
   341 
   342 val smt_triggers =
   343   Attrib.setup_config_bool @{binding sledgehammer_smt_triggers} (K true)
   344 val smt_weights =
   345   Attrib.setup_config_bool @{binding sledgehammer_smt_weights} (K true)
   346 val smt_weight_min_facts =
   347   Attrib.setup_config_int @{binding sledgehammer_smt_weight_min_facts} (K 20)
   348 
   349 (* FUDGE *)
   350 val smt_min_weight =
   351   Attrib.setup_config_int @{binding sledgehammer_smt_min_weight} (K 0)
   352 val smt_max_weight =
   353   Attrib.setup_config_int @{binding sledgehammer_smt_max_weight} (K 10)
   354 val smt_max_weight_index =
   355   Attrib.setup_config_int @{binding sledgehammer_smt_max_weight_index} (K 200)
   356 val smt_weight_curve = Unsynchronized.ref (fn x : int => x * x)
   357 
   358 fun smt_fact_weight ctxt j num_facts =
   359   if Config.get ctxt smt_weights andalso
   360      num_facts >= Config.get ctxt smt_weight_min_facts then
   361     let
   362       val min = Config.get ctxt smt_min_weight
   363       val max = Config.get ctxt smt_max_weight
   364       val max_index = Config.get ctxt smt_max_weight_index
   365       val curve = !smt_weight_curve
   366     in
   367       SOME (max - (max - min + 1) * curve (Int.max (0, max_index - j - 1))
   368             div curve max_index)
   369     end
   370   else
   371     NONE
   372 
   373 fun weight_smt_fact ctxt num_facts ((info, th), j) =
   374   let val thy = Proof_Context.theory_of ctxt in
   375     (info, (smt_fact_weight ctxt j num_facts, th |> Thm.transfer thy))
   376   end
   377 
   378 fun untranslated_fact (Untranslated_Fact p) = p
   379   | untranslated_fact (SMT_Weighted_Fact (info, (_, th))) = (info, th)
   380 fun smt_weighted_fact _ _ (SMT_Weighted_Fact p, _) = p
   381   | smt_weighted_fact ctxt num_facts (fact, j) =
   382     (untranslated_fact fact, j) |> weight_smt_fact ctxt num_facts
   383 
   384 fun overlord_file_location_for_prover prover =
   385   (getenv "ISABELLE_HOME_USER", "prob_" ^ prover)
   386 
   387 fun with_path cleanup after f path =
   388   Exn.capture f path
   389   |> tap (fn _ => cleanup path)
   390   |> Exn.release
   391   |> tap (after path)
   392 
   393 fun proof_banner mode name =
   394   case mode of
   395     Auto_Try => "Auto Sledgehammer (" ^ quote name ^ ") found a proof"
   396   | Try => "Sledgehammer (" ^ quote name ^ ") found a proof"
   397   | _ => "Try this"
   398 
   399 fun bunch_of_reconstructors needs_full_types lam_trans =
   400   [(false, Metis (hd partial_type_encs, lam_trans true)),
   401    (true, Metis (full_typesN, lam_trans false)),
   402    (false, Metis (no_typesN, lam_trans false)),
   403    (true, SMT)]
   404   |> map_filter (fn (full_types, reconstr) =>
   405                     if needs_full_types andalso not full_types then NONE
   406                     else SOME reconstr)
   407 
   408 fun extract_reconstructor ({type_enc, lam_trans, ...} : params)
   409                           (Metis (type_enc', lam_trans')) =
   410     let
   411       val override_params =
   412         (if is_none type_enc andalso type_enc' = hd partial_type_encs then
   413            []
   414          else
   415            [("type_enc", [hd (unalias_type_enc type_enc')])]) @
   416         (if is_none lam_trans andalso lam_trans' = metis_default_lam_trans then
   417            []
   418          else
   419            [("lam_trans", [lam_trans'])])
   420     in (metisN, override_params) end
   421   | extract_reconstructor _ SMT = (smtN, [])
   422 
   423 (* based on "Mirabelle.can_apply" and generalized *)
   424 fun timed_apply timeout tac state i =
   425   let
   426     val {context = ctxt, facts, goal} = Proof.goal state
   427     val full_tac = Method.insert_tac facts i THEN tac ctxt i
   428   in TimeLimit.timeLimit timeout (try (Seq.pull o full_tac)) goal end
   429 
   430 fun tac_for_reconstructor (Metis (type_enc, lam_trans)) =
   431     metis_tac [type_enc] lam_trans
   432   | tac_for_reconstructor SMT = SMT_Solver.smt_tac
   433 
   434 fun timed_reconstructor reconstr debug timeout ths =
   435   (Config.put Metis_Tactic.verbose debug
   436    #> Config.put SMT_Config.verbose debug
   437    #> (fn ctxt => tac_for_reconstructor reconstr ctxt ths))
   438   |> timed_apply timeout
   439 
   440 fun filter_used_facts used = filter (member (op =) used o fst)
   441 
   442 fun play_one_line_proof mode debug verbose timeout ths state i preferred
   443                         reconstrs =
   444   let
   445     val _ =
   446       if mode = Minimize andalso Time.> (timeout, Time.zeroTime) then
   447         Output.urgent_message "Preplaying proof..."
   448       else
   449         ()
   450     fun get_preferred reconstrs =
   451       if member (op =) reconstrs preferred then preferred
   452       else List.last reconstrs
   453     fun play [] [] = Failed_to_Play (get_preferred reconstrs)
   454       | play timed_outs [] =
   455         Trust_Playable (get_preferred timed_outs, SOME timeout)
   456       | play timed_out (reconstr :: reconstrs) =
   457         let
   458           val _ =
   459             if verbose then
   460               "Trying \"" ^ string_for_reconstructor reconstr ^ "\" for " ^
   461               string_from_time timeout ^ "..."
   462               |> Output.urgent_message
   463             else
   464               ()
   465           val timer = Timer.startRealTimer ()
   466         in
   467           case timed_reconstructor reconstr debug timeout ths state i of
   468             SOME (SOME _) => Played (reconstr, Timer.checkRealTimer timer)
   469           | _ => play timed_out reconstrs
   470         end
   471         handle TimeLimit.TimeOut => play (reconstr :: timed_out) reconstrs
   472   in
   473     if timeout = Time.zeroTime then
   474       Trust_Playable (get_preferred reconstrs, NONE)
   475     else
   476       play [] reconstrs
   477   end
   478 
   479 
   480 (* generic TPTP-based ATPs *)
   481 
   482 (* Too general means, positive equality literal with a variable X as one
   483    operand, when X does not occur properly in the other operand. This rules out
   484    clearly inconsistent facts such as X = a | X = b, though it by no means
   485    guarantees soundness. *)
   486 
   487 (* Unwanted equalities are those between a (bound or schematic) variable that
   488    does not properly occur in the second operand. *)
   489 val is_exhaustive_finite =
   490   let
   491     fun is_bad_equal (Var z) t =
   492         not (exists_subterm (fn Var z' => z = z' | _ => false) t)
   493       | is_bad_equal (Bound j) t = not (loose_bvar1 (t, j))
   494       | is_bad_equal _ _ = false
   495     fun do_equals t1 t2 = is_bad_equal t1 t2 orelse is_bad_equal t2 t1
   496     fun do_formula pos t =
   497       case (pos, t) of
   498         (_, @{const Trueprop} $ t1) => do_formula pos t1
   499       | (true, Const (@{const_name all}, _) $ Abs (_, _, t')) =>
   500         do_formula pos t'
   501       | (true, Const (@{const_name All}, _) $ Abs (_, _, t')) =>
   502         do_formula pos t'
   503       | (false, Const (@{const_name Ex}, _) $ Abs (_, _, t')) =>
   504         do_formula pos t'
   505       | (_, @{const "==>"} $ t1 $ t2) =>
   506         do_formula (not pos) t1 andalso
   507         (t2 = @{prop False} orelse do_formula pos t2)
   508       | (_, @{const HOL.implies} $ t1 $ t2) =>
   509         do_formula (not pos) t1 andalso
   510         (t2 = @{const False} orelse do_formula pos t2)
   511       | (_, @{const Not} $ t1) => do_formula (not pos) t1
   512       | (true, @{const HOL.disj} $ t1 $ t2) => forall (do_formula pos) [t1, t2]
   513       | (false, @{const HOL.conj} $ t1 $ t2) => forall (do_formula pos) [t1, t2]
   514       | (true, Const (@{const_name HOL.eq}, _) $ t1 $ t2) => do_equals t1 t2
   515       | (true, Const (@{const_name "=="}, _) $ t1 $ t2) => do_equals t1 t2
   516       | _ => false
   517   in do_formula true end
   518 
   519 fun has_bound_or_var_of_type pred =
   520   exists_subterm (fn Var (_, T as Type _) => pred T
   521                    | Abs (_, T as Type _, _) => pred T
   522                    | _ => false)
   523 
   524 (* Facts are forbidden to contain variables of these types. The typical reason
   525    is that they lead to unsoundness. Note that "unit" satisfies numerous
   526    equations like "?x = ()". The resulting clauses will have no type constraint,
   527    yielding false proofs. Even "bool" leads to many unsound proofs, though only
   528    for higher-order problems. *)
   529 
   530 (* Facts containing variables of type "unit" or "bool" or of the form
   531    "ALL x. x = A | x = B | x = C" are likely to lead to unsound proofs if types
   532    are omitted. *)
   533 fun is_dangerous_prop ctxt =
   534   transform_elim_prop
   535   #> (has_bound_or_var_of_type (is_type_surely_finite ctxt) orf
   536       is_exhaustive_finite)
   537 
   538 (* Important messages are important but not so important that users want to see
   539    them each time. *)
   540 val atp_important_message_keep_quotient = 25
   541 
   542 fun choose_type_enc soundness best_type_enc format =
   543   the_default best_type_enc
   544   #> type_enc_from_string soundness
   545   #> adjust_type_enc format
   546 
   547 val metis_minimize_max_time = seconds 2.0
   548 
   549 fun choose_minimize_command params minimize_command name preplay =
   550   let
   551     val (name, override_params) =
   552       case preplay of
   553         Played (reconstr, time) =>
   554         if Time.<= (time, metis_minimize_max_time) then
   555           extract_reconstructor params reconstr
   556         else
   557           (name, [])
   558       | _ => (name, [])
   559   in minimize_command override_params name end
   560 
   561 fun repair_monomorph_context max_iters max_new_instances =
   562   Config.put Monomorph.max_rounds max_iters
   563   #> Config.put Monomorph.max_new_instances max_new_instances
   564   #> Config.put Monomorph.keep_partial_instances false
   565 
   566 fun suffix_for_mode Auto_Try = "_auto_try"
   567   | suffix_for_mode Try = "_try"
   568   | suffix_for_mode Normal = ""
   569   | suffix_for_mode Auto_Minimize = "_auto_min"
   570   | suffix_for_mode Minimize = "_min"
   571 
   572 (* Give the ATPs some slack before interrupting them the hard way. "z3_tptp" on
   573    Linux appears to be the only ATP that does not honor its time limit. *)
   574 val atp_timeout_slack = seconds 1.0
   575 
   576 fun run_atp mode name
   577         ({exec, required_execs, arguments, proof_delims, known_failures,
   578           conj_sym_kind, prem_kind, best_slices, ...} : atp_config)
   579         (params as {debug, verbose, overlord, type_enc, sound, lam_trans,
   580                     max_relevant, max_mono_iters, max_new_mono_instances,
   581                     isar_proof, isar_shrink_factor, slicing, timeout,
   582                     preplay_timeout, ...})
   583         minimize_command
   584         ({state, goal, subgoal, subgoal_count, facts, ...} : prover_problem) =
   585   let
   586     val thy = Proof.theory_of state
   587     val ctxt = Proof.context_of state
   588     val (_, hyp_ts, concl_t) = strip_subgoal ctxt goal subgoal
   589     val (dest_dir, problem_prefix) =
   590       if overlord then overlord_file_location_for_prover name
   591       else (Config.get ctxt dest_dir, Config.get ctxt problem_prefix)
   592     val problem_file_name =
   593       Path.basic (problem_prefix ^ (if overlord then "" else serial_string ()) ^
   594                   suffix_for_mode mode ^ "_" ^ string_of_int subgoal)
   595     val problem_path_name =
   596       if dest_dir = "" then
   597         File.tmp_path problem_file_name
   598       else if File.exists (Path.explode dest_dir) then
   599         Path.append (Path.explode dest_dir) problem_file_name
   600       else
   601         error ("No such directory: " ^ quote dest_dir ^ ".")
   602     val command = Path.explode (getenv (fst exec) ^ "/" ^ snd exec)
   603     fun split_time s =
   604       let
   605         val split = String.tokens (fn c => str c = "\n")
   606         val (output, t) = s |> split |> split_last |> apfst cat_lines
   607         fun as_num f = f >> (fst o read_int)
   608         val num = as_num (Scan.many1 Symbol.is_ascii_digit)
   609         val digit = Scan.one Symbol.is_ascii_digit
   610         val num3 = as_num (digit ::: digit ::: (digit >> single))
   611         val time = num --| Scan.$$ "." -- num3 >> (fn (a, b) => a * 1000 + b)
   612         val as_time =
   613           raw_explode #> Scan.read Symbol.stopper time #> the_default 0
   614       in
   615         (output,
   616          as_time t |> Time.fromMilliseconds)
   617       end
   618     fun run_on prob_file =
   619       case filter (curry (op =) "" o getenv o fst) (exec :: required_execs) of
   620         (home_var, _) :: _ =>
   621         error ("The environment variable " ^ quote home_var ^ " is not set.")
   622       | [] =>
   623         if File.exists command then
   624           let
   625             (* If slicing is disabled, we expand the last slice to fill the
   626                entire time available. *)
   627             val actual_slices = get_slices slicing (best_slices ctxt)
   628             val num_actual_slices = length actual_slices
   629             fun monomorphize_facts facts =
   630               let
   631                 val ctxt =
   632                   ctxt |> repair_monomorph_context max_mono_iters
   633                                                    max_new_mono_instances
   634                 (* pseudo-theorem involving the same constants as the subgoal *)
   635                 val subgoal_th =
   636                   Logic.list_implies (hyp_ts, concl_t)
   637                   |> Skip_Proof.make_thm thy
   638                 val rths =
   639                   facts |> chop (length facts div 4)
   640                         |>> map (pair 1 o snd)
   641                         ||> map (pair 2 o snd)
   642                         |> op @
   643                         |> cons (0, subgoal_th)
   644               in
   645                 Monomorph.monomorph atp_schematic_consts_of rths ctxt
   646                 |> fst |> tl
   647                 |> curry ListPair.zip (map fst facts)
   648                 |> maps (fn (name, rths) => map (pair name o snd) rths)
   649               end
   650             fun run_slice (slice, (time_frac, (complete,
   651                                     (best_max_relevant, format, best_type_enc,
   652                                      best_lam_trans, extra))))
   653                           time_left =
   654               let
   655                 val num_facts =
   656                   length facts |> is_none max_relevant
   657                                   ? Integer.min best_max_relevant
   658                 val soundness = if sound then Sound else Sound_Modulo_Infinity
   659                 val type_enc =
   660                   type_enc |> choose_type_enc soundness best_type_enc format
   661                 val fairly_sound = is_type_enc_fairly_sound type_enc
   662                 val facts =
   663                   facts |> map untranslated_fact
   664                         |> not fairly_sound
   665                            ? filter_out (is_dangerous_prop ctxt o prop_of o snd)
   666                         |> take num_facts
   667                         |> polymorphism_of_type_enc type_enc <> Polymorphic
   668                            ? monomorphize_facts
   669                         |> map (apsnd prop_of)
   670                 val real_ms = Real.fromInt o Time.toMilliseconds
   671                 val slice_timeout =
   672                   ((real_ms time_left
   673                     |> (if slice < num_actual_slices - 1 then
   674                           curry Real.min (time_frac * real_ms timeout)
   675                         else
   676                           I))
   677                    * 0.001) |> seconds
   678                 val generous_slice_timeout =
   679                   Time.+ (slice_timeout, atp_timeout_slack)
   680                 val _ =
   681                   if debug then
   682                     quote name ^ " slice #" ^ string_of_int (slice + 1) ^
   683                     " with " ^ string_of_int num_facts ^ " fact" ^
   684                     plural_s num_facts ^ " for " ^
   685                     string_from_time slice_timeout ^ "..."
   686                     |> Output.urgent_message
   687                   else
   688                     ()
   689                 val readable_names = not (Config.get ctxt atp_full_names)
   690                 val lam_trans =
   691                   case lam_trans of
   692                     SOME s => s
   693                   | NONE => best_lam_trans
   694                 val (atp_problem, pool, fact_names, _, sym_tab) =
   695                   prepare_atp_problem ctxt format conj_sym_kind prem_kind
   696                       type_enc false lam_trans readable_names true hyp_ts
   697                       concl_t facts
   698                 fun weights () = atp_problem_weights atp_problem
   699                 val full_proof = debug orelse isar_proof
   700                 val core =
   701                   File.shell_path command ^ " " ^
   702                   arguments ctxt full_proof extra slice_timeout weights ^ " " ^
   703                   File.shell_path prob_file
   704                 val command = "TIMEFORMAT='%3R'; { time " ^ core ^ " ; } 2>&1"
   705                 val _ = atp_problem |> lines_for_atp_problem format
   706                                     |> cons ("% " ^ command ^ "\n")
   707                                     |> File.write_list prob_file
   708                 val ((output, run_time), (atp_proof, outcome)) =
   709                   TimeLimit.timeLimit generous_slice_timeout
   710                                       Isabelle_System.bash_output command
   711                   |>> (if overlord then
   712                          prefix ("% " ^ command ^ "\n% " ^ timestamp () ^ "\n")
   713                        else
   714                          I)
   715                   |> fst |> split_time
   716                   |> (fn accum as (output, _) =>
   717                          (accum,
   718                           extract_tstplike_proof_and_outcome verbose complete
   719                               proof_delims known_failures output
   720                           |>> atp_proof_from_tstplike_proof atp_problem output
   721                           handle UNRECOGNIZED_ATP_PROOF () =>
   722                                  ([], SOME ProofIncomplete)))
   723                   handle TimeLimit.TimeOut =>
   724                          (("", slice_timeout), ([], SOME TimedOut))
   725                 val outcome =
   726                   case outcome of
   727                     NONE =>
   728                     (case used_facts_in_unsound_atp_proof ctxt fact_names
   729                                                           atp_proof
   730                           |> Option.map (sort string_ord) of
   731                        SOME facts =>
   732                        let
   733                          val failure =
   734                            UnsoundProof (is_type_enc_quasi_sound type_enc,
   735                                          facts)
   736                        in
   737                          if debug then
   738                            (warning (string_for_failure failure); NONE)
   739                          else
   740                            SOME failure
   741                        end
   742                      | NONE => NONE)
   743                   | _ => outcome
   744               in
   745                 ((pool, fact_names, sym_tab),
   746                  (output, run_time, atp_proof, outcome))
   747               end
   748             val timer = Timer.startRealTimer ()
   749             fun maybe_run_slice slice
   750                                 (result as (_, (_, run_time0, _, SOME _))) =
   751                 let
   752                   val time_left = Time.- (timeout, Timer.checkRealTimer timer)
   753                 in
   754                   if Time.<= (time_left, Time.zeroTime) then
   755                     result
   756                   else
   757                     run_slice slice time_left
   758                     |> (fn (stuff, (output, run_time, atp_proof, outcome)) =>
   759                            (stuff, (output, Time.+ (run_time0, run_time),
   760                                     atp_proof, outcome)))
   761                 end
   762               | maybe_run_slice _ result = result
   763           in
   764             ((Symtab.empty, Vector.fromList [], Symtab.empty),
   765              ("", Time.zeroTime, [], SOME InternalError))
   766             |> fold maybe_run_slice actual_slices
   767           end
   768         else
   769           error ("Bad executable: " ^ Path.print command)
   770 
   771     (* If the problem file has not been exported, remove it; otherwise, export
   772        the proof file too. *)
   773     fun cleanup prob_file =
   774       if dest_dir = "" then try File.rm prob_file else NONE
   775     fun export prob_file (_, (output, _, _, _)) =
   776       if dest_dir = "" then
   777         ()
   778       else
   779         File.write (Path.explode (Path.implode prob_file ^ "_proof")) output
   780     val ((pool, fact_names, sym_tab), (output, run_time, atp_proof, outcome)) =
   781       with_path cleanup export run_on problem_path_name
   782     val important_message =
   783       if mode = Normal andalso
   784          random_range 0 (atp_important_message_keep_quotient - 1) = 0 then
   785         extract_important_message output
   786       else
   787         ""
   788     val (used_facts, preplay, message, message_tail) =
   789       case outcome of
   790         NONE =>
   791         let
   792           val used_facts = used_facts_in_atp_proof ctxt fact_names atp_proof
   793           val needs_full_types = is_typed_helper_used_in_proof atp_proof
   794           val reconstrs =
   795             bunch_of_reconstructors needs_full_types
   796                 (lam_trans_from_atp_proof atp_proof
   797                  o (fn plain =>
   798                        if plain then metis_default_lam_trans else hide_lamsN))
   799         in
   800           (used_facts,
   801            fn () =>
   802               let
   803                 val used_ths =
   804                   facts |> map untranslated_fact |> filter_used_facts used_facts
   805                         |> map snd
   806               in
   807                 play_one_line_proof mode debug verbose preplay_timeout used_ths
   808                                     state subgoal (hd reconstrs) reconstrs
   809               end,
   810            fn preplay =>
   811               let
   812                 val isar_params =
   813                   (debug, isar_shrink_factor, pool, fact_names, sym_tab,
   814                    atp_proof, goal)
   815                 val one_line_params =
   816                   (preplay, proof_banner mode name, used_facts,
   817                    choose_minimize_command params minimize_command name preplay,
   818                    subgoal, subgoal_count)
   819               in proof_text ctxt isar_proof isar_params one_line_params end,
   820            (if verbose then
   821               "\nATP real CPU time: " ^ string_from_time run_time ^ "."
   822             else
   823               "") ^
   824            (if important_message <> "" then
   825               "\n\nImportant message from Dr. Geoff Sutcliffe:\n" ^
   826               important_message
   827             else
   828               ""))
   829         end
   830       | SOME failure =>
   831         ([], K (Failed_to_Play plain_metis),
   832          fn _ => string_for_failure failure, "")
   833   in
   834     {outcome = outcome, used_facts = used_facts, run_time = run_time,
   835      preplay = preplay, message = message, message_tail = message_tail}
   836   end
   837 
   838 (* "SMT_Failure.Abnormal_Termination" carries the solver's return code. Until
   839    these are sorted out properly in the SMT module, we have to interpret these
   840    ourselves. *)
   841 val remote_smt_failures =
   842   [(2, NoLibwwwPerl),
   843    (22, CantConnect)]
   844 val z3_failures =
   845   [(101, OutOfResources),
   846    (103, MalformedInput),
   847    (110, MalformedInput)]
   848 val unix_failures =
   849   [(139, Crashed)]
   850 val smt_failures = remote_smt_failures @ z3_failures @ unix_failures
   851 
   852 fun failure_from_smt_failure (SMT_Failure.Counterexample {is_real_cex, ...}) =
   853     if is_real_cex then Unprovable else GaveUp
   854   | failure_from_smt_failure SMT_Failure.Time_Out = TimedOut
   855   | failure_from_smt_failure (SMT_Failure.Abnormal_Termination code) =
   856     (case AList.lookup (op =) smt_failures code of
   857        SOME failure => failure
   858      | NONE => UnknownError ("Abnormal termination with exit code " ^
   859                              string_of_int code ^ "."))
   860   | failure_from_smt_failure SMT_Failure.Out_Of_Memory = OutOfResources
   861   | failure_from_smt_failure (SMT_Failure.Other_Failure msg) =
   862     UnknownError msg
   863 
   864 (* FUDGE *)
   865 val smt_max_slices =
   866   Attrib.setup_config_int @{binding sledgehammer_smt_max_slices} (K 8)
   867 val smt_slice_fact_frac =
   868   Attrib.setup_config_real @{binding sledgehammer_smt_slice_fact_frac} (K 0.5)
   869 val smt_slice_time_frac =
   870   Attrib.setup_config_real @{binding sledgehammer_smt_slice_time_frac} (K 0.5)
   871 val smt_slice_min_secs =
   872   Attrib.setup_config_int @{binding sledgehammer_smt_slice_min_secs} (K 5)
   873 
   874 fun smt_filter_loop ctxt name
   875                     ({debug, verbose, overlord, max_mono_iters,
   876                       max_new_mono_instances, timeout, slicing, ...} : params)
   877                     state i smt_filter =
   878   let
   879     val max_slices = if slicing then Config.get ctxt smt_max_slices else 1
   880     val repair_context =
   881       select_smt_solver name
   882       #> Config.put SMT_Config.verbose debug
   883       #> (if overlord then
   884             Config.put SMT_Config.debug_files
   885                        (overlord_file_location_for_prover name
   886                         |> (fn (path, name) => path ^ "/" ^ name))
   887           else
   888             I)
   889       #> Config.put SMT_Config.infer_triggers (Config.get ctxt smt_triggers)
   890     val state = state |> Proof.map_context repair_context
   891     fun do_slice timeout slice outcome0 time_so_far facts =
   892       let
   893         val timer = Timer.startRealTimer ()
   894         val state =
   895           state |> Proof.map_context
   896                        (repair_monomorph_context max_mono_iters
   897                                                  max_new_mono_instances)
   898         val ms = timeout |> Time.toMilliseconds
   899         val slice_timeout =
   900           if slice < max_slices then
   901             Int.min (ms,
   902                 Int.max (1000 * Config.get ctxt smt_slice_min_secs,
   903                     Real.ceil (Config.get ctxt smt_slice_time_frac
   904                                * Real.fromInt ms)))
   905             |> Time.fromMilliseconds
   906           else
   907             timeout
   908         val num_facts = length facts
   909         val _ =
   910           if debug then
   911             quote name ^ " slice " ^ string_of_int slice ^ " with " ^
   912             string_of_int num_facts ^ " fact" ^ plural_s num_facts ^ " for " ^
   913             string_from_time slice_timeout ^ "..."
   914             |> Output.urgent_message
   915           else
   916             ()
   917         val birth = Timer.checkRealTimer timer
   918         val _ =
   919           if debug then Output.urgent_message "Invoking SMT solver..." else ()
   920         val (outcome, used_facts) =
   921           (case (slice, smt_filter) of
   922              (1, SOME head) => head |> apsnd (apsnd repair_context)
   923            | _ => SMT_Solver.smt_filter_preprocess state facts i)
   924           |> SMT_Solver.smt_filter_apply slice_timeout
   925           |> (fn {outcome, used_facts} => (outcome, used_facts))
   926           handle exn => if Exn.is_interrupt exn then
   927                           reraise exn
   928                         else
   929                           (ML_Compiler.exn_message exn
   930                            |> SMT_Failure.Other_Failure |> SOME, [])
   931         val death = Timer.checkRealTimer timer
   932         val outcome0 = if is_none outcome0 then SOME outcome else outcome0
   933         val time_so_far = Time.+ (time_so_far, Time.- (death, birth))
   934         val too_many_facts_perhaps =
   935           case outcome of
   936             NONE => false
   937           | SOME (SMT_Failure.Counterexample _) => false
   938           | SOME SMT_Failure.Time_Out => slice_timeout <> timeout
   939           | SOME (SMT_Failure.Abnormal_Termination _) => true (* kind of *)
   940           | SOME SMT_Failure.Out_Of_Memory => true
   941           | SOME (SMT_Failure.Other_Failure _) => true
   942         val timeout = Time.- (timeout, Timer.checkRealTimer timer)
   943       in
   944         if too_many_facts_perhaps andalso slice < max_slices andalso
   945            num_facts > 0 andalso Time.> (timeout, Time.zeroTime) then
   946           let
   947             val new_num_facts =
   948               Real.ceil (Config.get ctxt smt_slice_fact_frac
   949                          * Real.fromInt num_facts)
   950             val _ =
   951               if verbose andalso is_some outcome then
   952                 quote name ^ " invoked with " ^ string_of_int num_facts ^
   953                 " fact" ^ plural_s num_facts ^ ": " ^
   954                 string_for_failure (failure_from_smt_failure (the outcome)) ^
   955                 " Retrying with " ^ string_of_int new_num_facts ^ " fact" ^
   956                 plural_s new_num_facts ^ "..."
   957                 |> Output.urgent_message
   958               else
   959                 ()
   960           in
   961             facts |> take new_num_facts
   962                   |> do_slice timeout (slice + 1) outcome0 time_so_far
   963           end
   964         else
   965           {outcome = if is_none outcome then NONE else the outcome0,
   966            used_facts = used_facts, run_time = time_so_far}
   967       end
   968   in do_slice timeout 1 NONE Time.zeroTime end
   969 
   970 fun run_smt_solver mode name (params as {debug, verbose, preplay_timeout, ...})
   971         minimize_command
   972         ({state, subgoal, subgoal_count, facts, smt_filter, ...}
   973          : prover_problem) =
   974   let
   975     val ctxt = Proof.context_of state
   976     val num_facts = length facts
   977     val facts = facts ~~ (0 upto num_facts - 1)
   978                 |> map (smt_weighted_fact ctxt num_facts)
   979     val {outcome, used_facts, run_time} =
   980       smt_filter_loop ctxt name params state subgoal smt_filter facts
   981     val (used_facts, used_ths) = used_facts |> ListPair.unzip
   982     val outcome = outcome |> Option.map failure_from_smt_failure
   983     val (preplay, message, message_tail) =
   984       case outcome of
   985         NONE =>
   986         (fn () =>
   987             play_one_line_proof mode debug verbose preplay_timeout used_ths
   988                 state subgoal SMT
   989                 (bunch_of_reconstructors false
   990                      (fn plain =>
   991                          if plain then metis_default_lam_trans
   992                          else lam_liftingN)),
   993          fn preplay =>
   994             let
   995               val one_line_params =
   996                 (preplay, proof_banner mode name, used_facts,
   997                  choose_minimize_command params minimize_command name preplay,
   998                  subgoal, subgoal_count)
   999             in one_line_proof_text one_line_params end,
  1000          if verbose then
  1001            "\nSMT solver real CPU time: " ^ string_from_time run_time ^ "."
  1002          else
  1003            "")
  1004       | SOME failure =>
  1005         (K (Failed_to_Play plain_metis), fn _ => string_for_failure failure, "")
  1006   in
  1007     {outcome = outcome, used_facts = used_facts, run_time = run_time,
  1008      preplay = preplay, message = message, message_tail = message_tail}
  1009   end
  1010 
  1011 fun run_reconstructor mode name
  1012         (params as {debug, verbose, timeout, type_enc, lam_trans, ...})
  1013         minimize_command
  1014         ({state, subgoal, subgoal_count, facts, ...} : prover_problem) =
  1015   let
  1016     val reconstr =
  1017       if name = metisN then
  1018         Metis (type_enc |> the_default (hd partial_type_encs),
  1019                lam_trans |> the_default metis_default_lam_trans)
  1020       else if name = smtN then
  1021         SMT
  1022       else
  1023         raise Fail ("unknown reconstructor: " ^ quote name)
  1024     val (used_facts, used_ths) =
  1025       facts |> map untranslated_fact |> ListPair.unzip
  1026   in
  1027     case play_one_line_proof (if mode = Minimize then Normal else mode) debug
  1028                              verbose timeout used_ths state subgoal reconstr
  1029                              [reconstr] of
  1030       play as Played (_, time) =>
  1031       {outcome = NONE, used_facts = used_facts, run_time = time,
  1032        preplay = K play,
  1033        message =
  1034          fn play =>
  1035             let
  1036               val (_, override_params) = extract_reconstructor params reconstr
  1037               val one_line_params =
  1038                 (play, proof_banner mode name, used_facts,
  1039                  minimize_command override_params name, subgoal,
  1040                  subgoal_count)
  1041             in one_line_proof_text one_line_params end,
  1042        message_tail = ""}
  1043     | play =>
  1044       let
  1045         val failure = case play of Failed_to_Play _ => GaveUp | _ => TimedOut
  1046       in
  1047         {outcome = SOME failure, used_facts = [], run_time = Time.zeroTime,
  1048          preplay = K play, message = fn _ => string_for_failure failure,
  1049          message_tail = ""}
  1050       end
  1051   end
  1052 
  1053 fun get_prover ctxt mode name =
  1054   let val thy = Proof_Context.theory_of ctxt in
  1055     if is_reconstructor name then run_reconstructor mode name
  1056     else if is_atp thy name then run_atp mode name (get_atp thy name)
  1057     else if is_smt_prover ctxt name then run_smt_solver mode name
  1058     else error ("No such prover: " ^ name ^ ".")
  1059   end
  1060 
  1061 end;