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