src/HOL/TPTP/atp_problem_import.ML
author blanchet
Wed, 31 Oct 2012 11:23:21 +0100
changeset 49985 5b4b0e4e5205
parent 49983 33e18e9916a8
child 51552 c713c9505f68
permissions -rw-r--r--
moved Refute to "HOL/Library" to speed up building "Main" even more

(*  Title:      HOL/TPTP/atp_problem_import.ML
    Author:     Jasmin Blanchette, TU Muenchen
    Copyright   2012

Import TPTP problems as Isabelle terms or goals.
*)

signature ATP_PROBLEM_IMPORT =
sig
  val read_tptp_file :
    theory -> (term -> term) -> string
    -> term list * (term list * term list) * Proof.context
  val nitpick_tptp_file : theory -> int -> string -> unit
  val refute_tptp_file : theory -> int -> string -> unit
  val can_tac : Proof.context -> tactic -> term -> bool
  val SOLVE_TIMEOUT :  int -> string -> tactic -> tactic
  val atp_tac :
    Proof.context -> int -> (string * string) list -> int -> string -> int
    -> tactic
  val smt_solver_tac : string -> Proof.context -> int -> tactic
  val freeze_problem_consts : theory -> term -> term
  val make_conj : term list * term list -> term list -> term
  val sledgehammer_tptp_file : theory -> int -> string -> unit
  val isabelle_tptp_file : theory -> int -> string -> unit
  val isabelle_hot_tptp_file : theory -> int -> string -> unit
  val translate_tptp_file : string -> string -> string -> unit
end;

structure ATP_Problem_Import : ATP_PROBLEM_IMPORT =
struct

open ATP_Util
open ATP_Problem
open ATP_Proof

val debug = false
val overlord = false


(** TPTP parsing **)

fun read_tptp_file thy postproc file_name =
  let
    fun has_role role (_, role', _, _) = (role' = role)
    fun get_prop (_, _, P, _) =
      P |> Logic.varify_global |> close_form |> postproc
    val path =
      Path.explode file_name
      |> (fn path =>
             path |> not (Path.is_absolute path)
                     ? Path.append (Path.explode "$PWD"))
    val ((_, _, problem), thy) =
      TPTP_Interpret.interpret_file true [Path.dir path, Path.explode "$TPTP"]
                                    path [] [] thy
    val (conjs, defs_and_nondefs) =
      problem |> List.partition (has_role TPTP_Syntax.Role_Conjecture)
              ||> List.partition (has_role TPTP_Syntax.Role_Definition)
  in
    (map get_prop conjs, pairself (map get_prop) defs_and_nondefs,
     thy |> Theory.checkpoint |> Proof_Context.init_global)
  end


(** Nitpick (alias Nitrox) **)

fun aptrueprop f ((t0 as @{const Trueprop}) $ t1) = t0 $ f t1
  | aptrueprop f t = f t

fun nitpick_tptp_file thy timeout file_name =
  let
    val (conjs, (defs, nondefs), ctxt) = read_tptp_file thy I file_name
    val thy = Proof_Context.theory_of ctxt
    val (defs, pseudo_defs) =
      defs |> map (ATP_Util.abs_extensionalize_term ctxt
                   #> aptrueprop (hol_open_form I))
           |> List.partition (ATP_Util.is_legitimate_tptp_def
                              o perhaps (try HOLogic.dest_Trueprop)
                              o ATP_Util.unextensionalize_def)
    val nondefs = pseudo_defs @ nondefs
    val state = Proof.init ctxt
    val params =
      [("card", "1\<emdash>100"),
       ("box", "false"),
       ("max_threads", "1"),
       ("batch_size", "5"),
       ("falsify", if null conjs then "false" else "true"),
       ("verbose", "true"),
       ("debug", if debug then "true" else "false"),
       ("overlord", if overlord then "true" else "false"),
       ("show_consts", "true"),
       ("format", "1"),
       ("max_potential", "0"),
       ("timeout", string_of_int timeout),
       ("tac_timeout", string_of_int ((timeout + 49) div 50))]
      |> Nitpick_Isar.default_params thy
    val i = 1
    val n = 1
    val step = 0
    val subst = []
  in
    Nitpick.pick_nits_in_term state params Nitpick.TPTP i n step subst
        defs nondefs (case conjs of conj :: _ => conj | [] => @{prop True});
    ()
  end


(** Refute **)

fun refute_tptp_file thy timeout file_name =
  let
    fun print_szs_from_outcome falsify s =
      "% SZS status " ^
      (if s = "genuine" then
         if falsify then "CounterSatisfiable" else "Satisfiable"
       else
         "Unknown")
      |> Output.urgent_message
    val (conjs, (defs, nondefs), ctxt) = read_tptp_file thy I file_name
    val params =
      [("maxtime", string_of_int timeout),
       ("maxvars", "100000")]
  in
    Refute.refute_term ctxt params (defs @ nondefs)
        (case conjs of conj :: _ => conj | [] => @{prop True})
    |> print_szs_from_outcome (not (null conjs))
  end


(** Sledgehammer and Isabelle (combination of provers) **)

fun can_tac ctxt tactic conj = can (Goal.prove ctxt [] [] conj) (K tactic)

fun SOLVE_TIMEOUT seconds name tac st =
  let
    val _ = Output.urgent_message ("running " ^ name ^ " for " ^
                                   string_of_int seconds ^ " s")
    val result =
      TimeLimit.timeLimit (Time.fromSeconds seconds)
        (fn () => SINGLE (SOLVE tac) st) ()
      handle TimeLimit.TimeOut => NONE
        | ERROR _ => NONE
  in
    case result of
      NONE => (Output.urgent_message ("FAILURE: " ^ name); Seq.empty)
    | SOME st' => (Output.urgent_message ("SUCCESS: " ^ name); Seq.single st')
  end

fun nitpick_finite_oracle_tac ctxt timeout i th =
  let
    fun is_safe (Type (@{type_name fun}, Ts)) = forall is_safe Ts
      | is_safe @{typ prop} = true
      | is_safe @{typ bool} = true
      | is_safe _ = false
    val conj = Thm.term_of (Thm.cprem_of th i)
  in
    if exists_type (not o is_safe) conj then
      Seq.empty
    else
      let
        val thy = Proof_Context.theory_of ctxt
        val state = Proof.init ctxt
        val params =
          [("box", "false"),
           ("max_threads", "1"),
           ("verbose", "true"),
           ("debug", if debug then "true" else "false"),
           ("overlord", if overlord then "true" else "false"),
           ("max_potential", "0"),
           ("timeout", string_of_int timeout)]
          |> Nitpick_Isar.default_params thy
        val i = 1
        val n = 1
        val step = 0
        val subst = []
        val (outcome, _) =
          Nitpick.pick_nits_in_term state params Nitpick.Normal i n step subst
                                    [] [] conj
      in if outcome = "none" then Skip_Proof.cheat_tac thy th else Seq.empty end
  end

fun atp_tac ctxt completeness override_params timeout prover =
  let
    val ctxt =
      ctxt |> Config.put Sledgehammer_Provers.completish (completeness > 0)
  in
    Sledgehammer_Tactics.sledgehammer_as_oracle_tac ctxt
        ([("debug", if debug then "true" else "false"),
          ("overlord", if overlord then "true" else "false"),
          ("provers", prover),
          ("timeout", string_of_int timeout)] @
         (if completeness > 0 then
            [("type_enc",
              if completeness = 1 then "mono_native" else "poly_guards??"),
             ("slicing", "false")]
          else
            []) @
         override_params)
        {add = [(Facts.named (Thm.derivation_name ext), [])],
         del = [], only = true}
  end

fun sledgehammer_tac demo ctxt timeout i =
  let
    val frac = if demo then 16 else 12
    fun slice mult completeness prover =
      SOLVE_TIMEOUT (mult * timeout div frac)
          (prover ^
           (if completeness > 0 then "(" ^ string_of_int completeness ^ ")"
            else ""))
          (atp_tac ctxt completeness [] (mult * timeout div frac) prover i)
  in
    slice 2 0 ATP_Systems.spassN
    ORELSE slice 2 0 ATP_Systems.vampireN
    ORELSE slice 2 0 ATP_Systems.eN
    ORELSE slice 2 0 ATP_Systems.z3_tptpN
    ORELSE slice 1 1 ATP_Systems.spassN
    ORELSE slice 1 2 ATP_Systems.eN
    ORELSE slice 1 1 ATP_Systems.vampireN
    ORELSE slice 1 2 ATP_Systems.vampireN
    ORELSE
      (if demo then
         slice 2 0 ATP_Systems.satallaxN
         ORELSE slice 2 0 ATP_Systems.leo2N
       else
         no_tac)
  end

fun smt_solver_tac solver ctxt =
  let
    val ctxt = ctxt |> Context.proof_map (SMT_Config.select_solver solver)
  in SMT_Solver.smt_tac ctxt [] end

fun auto_etc_tac ctxt timeout i =
  SOLVE_TIMEOUT (timeout div 20) "nitpick"
      (nitpick_finite_oracle_tac ctxt (timeout div 20) i)
  ORELSE SOLVE_TIMEOUT (timeout div 10) "simp"
      (asm_full_simp_tac (simpset_of ctxt) i)
  ORELSE SOLVE_TIMEOUT (timeout div 10) "blast" (blast_tac ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 5) "auto+spass"
      (auto_tac ctxt
       THEN ALLGOALS (atp_tac ctxt 0 [] (timeout div 5) ATP_Systems.spassN))
  ORELSE SOLVE_TIMEOUT (timeout div 10) "fast" (fast_tac ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 20) "z3" (smt_solver_tac "z3" ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 20) "cvc3" (smt_solver_tac "cvc3" ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 20) "best" (best_tac ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 10) "force" (force_tac ctxt i)
  ORELSE SOLVE_TIMEOUT (timeout div 10) "meson" (Meson.meson_tac ctxt [] i)
  ORELSE SOLVE_TIMEOUT (timeout div 10) "fastforce" (fast_force_tac ctxt i)

fun problem_const_prefix thy = Context.theory_name thy ^ Long_Name.separator

(* Isabelle's standard automatic tactics ("auto", etc.) are more eager to
   unfold "definitions" of free variables than of constants (cf. PUZ107^5). *)
fun freeze_problem_consts thy =
  let val is_problem_const = String.isPrefix (problem_const_prefix thy) in
    map_aterms (fn t as Const (s, T) =>
                   if is_problem_const s then Free (Long_Name.base_name s, T)
                   else t
                 | t => t)
  end

fun make_conj (defs, nondefs) conjs =
  Logic.list_implies (rev defs @ rev nondefs,
                      case conjs of conj :: _ => conj | [] => @{prop False})

fun print_szs_from_success conjs success =
  Output.urgent_message ("% SZS status " ^
                         (if success then
                            if null conjs then "Unsatisfiable" else "Theorem"
                          else
                            "Unknown"))

fun sledgehammer_tptp_file thy timeout file_name =
  let
    val (conjs, assms, ctxt) =
      read_tptp_file thy (freeze_problem_consts thy) file_name
    val conj = make_conj assms conjs
  in
    can_tac ctxt (sledgehammer_tac true ctxt timeout 1) conj
    |> print_szs_from_success conjs
  end

fun generic_isabelle_tptp_file demo thy timeout file_name =
  let
    val (conjs, assms, ctxt) =
      read_tptp_file thy (freeze_problem_consts thy) file_name
    val conj = make_conj assms conjs
    val (last_hope_atp, last_hope_completeness) =
      if demo then (ATP_Systems.satallaxN, 0) else (ATP_Systems.vampireN, 2)
  in
    (can_tac ctxt (auto_etc_tac ctxt (timeout div 2) 1) conj orelse
     can_tac ctxt (sledgehammer_tac demo ctxt (timeout div 2) 1) conj orelse
     can_tac ctxt (SOLVE_TIMEOUT timeout (last_hope_atp ^ "(*)")
         (atp_tac ctxt last_hope_completeness [] timeout last_hope_atp 1)) conj)
    |> print_szs_from_success conjs
  end

val isabelle_tptp_file = generic_isabelle_tptp_file false
val isabelle_hot_tptp_file = generic_isabelle_tptp_file true


(** Translator between TPTP(-like) file formats **)

fun translate_tptp_file format in_file_name out_file_name = ()

end;