src/HOL/Tools/atp_minimal.ML
changeset 32327 0971cc0b6a57
parent 32111 30e2ffbba718
equal deleted inserted replaced
32326:9d70ecf11b7a 32327:0971cc0b6a57
     1 (*  Title:      HOL/Tools/atp_minimal.ML
       
     2     Author:     Philipp Meyer, TU Muenchen
       
     3 
       
     4 Minimalization of theorem list for metis by using an external automated theorem prover
       
     5 *)
       
     6 
       
     7 structure AtpMinimal: sig end =
       
     8 struct
       
     9 
       
    10 (* output control *)
       
    11 
       
    12 fun debug str = Output.debug (fn () => str)
       
    13 fun debug_fn f = if ! Output.debugging then f () else ()
       
    14 fun answer str = Output.writeln str
       
    15 fun println str = Output.priority str
       
    16 
       
    17 fun order_unique name_list = OrdList.make (String.collate Char.compare) name_list
       
    18 fun length_string namelist = Int.toString (length namelist)
       
    19 
       
    20 fun print_names name_thms_pairs =
       
    21   let
       
    22     val names = map fst name_thms_pairs
       
    23     val ordered = order_unique names
       
    24   in
       
    25     app (fn name => (debug ("  " ^ name))) ordered
       
    26   end
       
    27 
       
    28 
       
    29 (* minimalization algorithm *)
       
    30 
       
    31 local
       
    32   fun isplit (l,r) [] = (l,r)
       
    33     | isplit (l,r) (h::[]) = (h::l, r)
       
    34     | isplit (l,r) (h1::h2::t) = isplit (h1::l, h2::r) t
       
    35 in
       
    36   fun split lst = isplit ([],[]) lst
       
    37 end
       
    38 
       
    39 local
       
    40   fun min p sup [] = raise Empty
       
    41     | min p sup [s0] = [s0]
       
    42     | min p sup s =
       
    43       let
       
    44         val (l0, r0) = split s
       
    45       in
       
    46         if p (sup @ l0)
       
    47         then min p sup l0
       
    48         else
       
    49           if p (sup @ r0)
       
    50           then min p sup r0
       
    51           else
       
    52             let
       
    53               val l = min p (sup @ r0) l0
       
    54               val r = min p (sup @ l) r0
       
    55             in
       
    56               l @ r
       
    57             end
       
    58       end
       
    59 in
       
    60   (* return a minimal subset v of s that satisfies p
       
    61    @pre p(s) & ~p([]) & monotone(p)
       
    62    @post v subset s & p(v) &
       
    63          forall e in v. ~p(v \ e)
       
    64    *)
       
    65   fun minimal p s = min p [] s
       
    66 end
       
    67 
       
    68 
       
    69 (* failure check and producing answer *)
       
    70 
       
    71 datatype 'a prove_result = Success of 'a | Failure | Timeout | Error
       
    72 
       
    73 val string_of_result =
       
    74   fn Success _ => "Success"
       
    75    | Failure => "Failure"
       
    76    | Timeout => "Timeout"
       
    77    | Error => "Error"
       
    78 
       
    79 val failure_strings =
       
    80   [("SPASS beiseite: Ran out of time.", Timeout),
       
    81    ("Timeout", Timeout),
       
    82    ("time limit exceeded", Timeout),
       
    83    ("# Cannot determine problem status within resource limit", Timeout),
       
    84    ("Error", Error)]
       
    85 
       
    86 fun produce_answer (success, message, result_string, thm_name_vec, filtered) =
       
    87   if success then
       
    88     (Success (Vector.foldr op:: [] thm_name_vec, filtered), result_string)
       
    89   else
       
    90     let
       
    91       val failure = failure_strings |> get_first (fn (s, t) =>
       
    92           if String.isSubstring s result_string then SOME (t, result_string) else NONE)
       
    93     in
       
    94       if is_some failure then
       
    95         the failure
       
    96       else
       
    97         (Failure, result_string)
       
    98     end
       
    99 
       
   100 
       
   101 (* wrapper for calling external prover *)
       
   102 
       
   103 fun sh_test_thms prover prover_name time_limit subgoalno state filtered name_thms_pairs =
       
   104   let
       
   105     val _ = println ("Testing " ^ (length_string name_thms_pairs) ^ " theorems... ")
       
   106     val name_thm_pairs =
       
   107       flat (map (fn (n, ths) => map_index (fn (i, th) => (n, th)) ths) name_thms_pairs)
       
   108     val _ = debug_fn (fn () => print_names name_thm_pairs)
       
   109     val axclauses = ResAxioms.cnf_rules_pairs (Proof.theory_of state) name_thm_pairs
       
   110     val (result, proof) =
       
   111       produce_answer
       
   112         (prover time_limit (SOME axclauses) filtered prover_name subgoalno (Proof.get_goal state))
       
   113     val _ = println (string_of_result result)
       
   114     val _ = debug proof
       
   115   in
       
   116     (result, proof)
       
   117   end
       
   118 
       
   119 
       
   120 (* minimalization of thms *)
       
   121 
       
   122 fun minimalize prover prover_name time_limit state name_thms_pairs =
       
   123   let
       
   124     val _ =
       
   125       println ("Minimize called with " ^ length_string name_thms_pairs ^ " theorems, prover: "
       
   126         ^ prover_name ^ ", time limit: " ^ Int.toString time_limit ^ " seconds")
       
   127     val _ = debug_fn (fn () => app (fn (n, tl) =>
       
   128         (debug n; app (fn t =>
       
   129           debug ("  " ^ Display.string_of_thm (Proof.context_of state) t)) tl)) name_thms_pairs)
       
   130     val test_thms_fun = sh_test_thms prover prover_name time_limit 1 state
       
   131     fun test_thms filtered thms =
       
   132       case test_thms_fun filtered thms of (Success _, _) => true | _ => false
       
   133   in
       
   134     (* try prove first to check result and get used theorems *)
       
   135     (case test_thms_fun NONE name_thms_pairs of
       
   136       (Success (used, filtered), _) =>
       
   137         let
       
   138           val ordered_used = order_unique used
       
   139           val to_use =
       
   140             if length ordered_used < length name_thms_pairs then
       
   141               filter (fn (name1, _) => List.exists (equal name1) ordered_used) name_thms_pairs
       
   142             else
       
   143               name_thms_pairs
       
   144           val min_thms = (minimal (test_thms (SOME filtered)) to_use)
       
   145           val min_names = order_unique (map fst min_thms)
       
   146           val _ = println ("Minimal " ^ (length_string min_thms) ^ " theorems")
       
   147           val _ = debug_fn (fn () => print_names min_thms)
       
   148         in
       
   149           answer ("Try this command: " ^
       
   150             Markup.markup Markup.sendback ("apply (metis " ^ space_implode " " min_names ^ ")"))
       
   151         end
       
   152     | (Timeout, _) =>
       
   153         answer ("Timeout: You may need to increase the time limit of " ^
       
   154           Int.toString time_limit ^ " seconds. Call atp_minimize [time=...] ")
       
   155     | (Error, msg) =>
       
   156         answer ("Error in prover: " ^ msg)
       
   157     | (Failure, _) =>
       
   158         answer "Failure: No proof with the theorems supplied")
       
   159     handle ResHolClause.TOO_TRIVIAL =>
       
   160         answer ("Trivial: Try this command: " ^ Markup.markup Markup.sendback "apply metis")
       
   161     | ERROR msg =>
       
   162         answer ("Error: " ^ msg)
       
   163   end
       
   164 
       
   165 
       
   166 (* Isar command and parsing input *)
       
   167 
       
   168 local structure K = OuterKeyword and P = OuterParse and T = OuterLex in
       
   169 
       
   170 fun get_thms context =
       
   171   map (fn (name, interval) =>
       
   172     let
       
   173       val thmref = Facts.Named ((name, Position.none), interval)
       
   174       val ths = ProofContext.get_fact context thmref
       
   175       val name' = Facts.string_of_ref thmref
       
   176     in
       
   177       (name', ths)
       
   178     end)
       
   179 
       
   180 val default_prover = "remote_vampire"
       
   181 val default_time_limit = 5
       
   182 
       
   183 fun get_time_limit_arg time_string =
       
   184   (case Int.fromString time_string of
       
   185     SOME t => t
       
   186   | NONE => error ("Invalid time limit: " ^ quote time_string))
       
   187 
       
   188 val get_options =
       
   189   let
       
   190     val def = (default_prover, default_time_limit)
       
   191   in
       
   192     foldl (fn ((name, a), (p, t)) =>
       
   193       (case name of
       
   194         "time" => (p, (get_time_limit_arg a))
       
   195       | "atp" => (a, t)
       
   196       | n => error ("Invalid argument: " ^ n))) def
       
   197   end
       
   198 
       
   199 fun sh_min_command args thm_names state =
       
   200   let
       
   201     val (prover_name, time_limit) = get_options args
       
   202     val prover =
       
   203       case AtpManager.get_prover prover_name (Proof.theory_of state) of
       
   204         SOME prover => prover
       
   205       | NONE => error ("Unknown prover: " ^ quote prover_name)
       
   206     val name_thms_pairs = get_thms (Proof.context_of state) thm_names
       
   207   in
       
   208     minimalize prover prover_name time_limit state name_thms_pairs
       
   209   end
       
   210 
       
   211 val parse_args = Scan.optional (Args.bracks (P.list (P.xname --| P.$$$ "=" -- P.xname))) []
       
   212 val parse_thm_names = Scan.repeat (P.xname -- Scan.option Attrib.thm_sel)
       
   213 
       
   214 val _ =
       
   215   OuterSyntax.command "atp_minimize" "minimize theorem list with external prover" K.diag
       
   216     (parse_args -- parse_thm_names >> (fn (args, thm_names) =>
       
   217       Toplevel.no_timing o Toplevel.unknown_proof o
       
   218         Toplevel.keep (sh_min_command args thm_names o Toplevel.proof_of)))
       
   219 
       
   220 end
       
   221 
       
   222 end
       
   223