src/HOL/Tools/atp_manager.ML
author wenzelm
Fri Oct 03 16:37:09 2008 +0200 (2008-10-03 ago)
changeset 28477 9339d4dcec8b
child 28478 855ca2dcc03d
permissions -rw-r--r--
version of sledgehammer using threads instead of processes, misc cleanup;
(by Fabian Immler);
wenzelm@28477
     1
(*  Title:      HOL/Tools/atp_manager.ML
wenzelm@28477
     2
    ID:         $Id$
wenzelm@28477
     3
    Author:     Fabian Immler, TU Muenchen
wenzelm@28477
     4
wenzelm@28477
     5
ATP threads have to be registered here.  Threads with the same
wenzelm@28477
     6
birth-time are seen as one group.  All threads of a group are killed
wenzelm@28477
     7
when one thread of it has been successful, or after a certain time, or
wenzelm@28477
     8
when the maximum number of threads exceeds; then the oldest thread is
wenzelm@28477
     9
killed.
wenzelm@28477
    10
*)
wenzelm@28477
    11
wenzelm@28477
    12
signature PROVERS =
wenzelm@28477
    13
sig
wenzelm@28477
    14
  type T
wenzelm@28477
    15
  val get : Context.theory -> T
wenzelm@28477
    16
  val init : Context.theory -> Context.theory
wenzelm@28477
    17
  val map :
wenzelm@28477
    18
     (T -> T) ->
wenzelm@28477
    19
     Context.theory -> Context.theory
wenzelm@28477
    20
  val put : T -> Context.theory -> Context.theory
wenzelm@28477
    21
end;
wenzelm@28477
    22
wenzelm@28477
    23
signature ATP_MANAGER =
wenzelm@28477
    24
sig
wenzelm@28477
    25
  val kill_all: unit -> unit
wenzelm@28477
    26
  val info: unit -> unit
wenzelm@28477
    27
  val set_atps: string -> unit
wenzelm@28477
    28
  val set_max_atp: int -> unit
wenzelm@28477
    29
  val set_timeout: int -> unit
wenzelm@28477
    30
  val set_groupkilling: bool -> unit
wenzelm@28477
    31
  val start: unit -> unit
wenzelm@28477
    32
  val register: Time.time -> Time.time -> (Thread.thread * string) -> unit
wenzelm@28477
    33
  val unregister: bool -> unit
wenzelm@28477
    34
wenzelm@28477
    35
  structure Provers : PROVERS
wenzelm@28477
    36
  val sledgehammer: Toplevel.state -> unit
wenzelm@28477
    37
end;
wenzelm@28477
    38
wenzelm@28477
    39
structure AtpManager : ATP_MANAGER =
wenzelm@28477
    40
struct
wenzelm@28477
    41
wenzelm@28477
    42
  structure ThreadHeap = HeapFun (
wenzelm@28477
    43
  struct
wenzelm@28477
    44
    type elem = (Time.time * Thread.thread);
wenzelm@28477
    45
    fun ord ((a,_),(b,_)) = Time.compare (a, b);
wenzelm@28477
    46
  end);
wenzelm@28477
    47
wenzelm@28477
    48
  (* create global state of threadmanager *)
wenzelm@28477
    49
  val timeout_heap = ref ThreadHeap.empty
wenzelm@28477
    50
  val oldest_heap = ref ThreadHeap.empty
wenzelm@28477
    51
  (* managed threads *)
wenzelm@28477
    52
  val active = ref ([] : (Thread.thread * Time.time * Time.time * string) list)
wenzelm@28477
    53
  val cancelling = ref ([] : (Thread.thread * Time.time * Time.time * string) list)
wenzelm@28477
    54
  (* settings *)
wenzelm@28477
    55
  val atps = ref "e,spass"
wenzelm@28477
    56
  val maximum_atps = ref 5   (* ~1 means infinite number of atps*)
wenzelm@28477
    57
  val timeout = ref 60
wenzelm@28477
    58
  val groupkilling = ref true
wenzelm@28477
    59
  (* synchronizing *)
wenzelm@28477
    60
  val lock = Mutex.mutex () (* to be aquired for changing state *)
wenzelm@28477
    61
  val state_change = ConditionVar.conditionVar () (* signal when state changes *)
wenzelm@28477
    62
  (* watches over running threads and interrupts them if required *)
wenzelm@28477
    63
  val managing_thread = ref (Thread.fork (fn () => (), []))
wenzelm@28477
    64
wenzelm@28477
    65
  (* move a thread from active to cancelling
wenzelm@28477
    66
    managing_thread trys to interrupt all threads in cancelling
wenzelm@28477
    67
wenzelm@28477
    68
   call from an environment where a lock has already been aquired *)
wenzelm@28477
    69
  fun unregister_locked thread =
wenzelm@28477
    70
    let val entrys = (filter (fn (t,_,_,_) => Thread.equal (t, thread))) (! active)
wenzelm@28477
    71
    val entrys_update = map (fn (th, tb, _, desc) => (th, tb, Time.now(), desc)) entrys
wenzelm@28477
    72
    val _ = change cancelling (append entrys_update)
wenzelm@28477
    73
    val _ = change active (filter_out (fn (t,_,_,_) => Thread.equal (t, thread)))
wenzelm@28477
    74
    in () end;
wenzelm@28477
    75
wenzelm@28477
    76
  (* start a watching thread which runs forever *)
wenzelm@28477
    77
  (* must *not* be called more than once!! => problem with locks *)
wenzelm@28477
    78
  fun start () =
wenzelm@28477
    79
    let
wenzelm@28477
    80
    val new_thread = Thread.fork (fn () =>
wenzelm@28477
    81
      let
wenzelm@28477
    82
      (* never give up lock except for waiting *)
wenzelm@28477
    83
      val _ = Mutex.lock lock
wenzelm@28477
    84
      fun wait_for_next_event time =
wenzelm@28477
    85
        let
wenzelm@28477
    86
        (* wait for signal or next timeout, give up lock meanwhile *)
wenzelm@28477
    87
        val _ = ConditionVar.waitUntil (state_change, lock, time)
wenzelm@28477
    88
        (* move threads with priority less than Time.now() to cancelling *)
wenzelm@28477
    89
        fun cancelolder heap =
wenzelm@28477
    90
          if ThreadHeap.is_empty heap then heap else
wenzelm@28477
    91
          let val (mintime, minthread) = ThreadHeap.min heap
wenzelm@28477
    92
          in
wenzelm@28477
    93
            if mintime > Time.now() then heap
wenzelm@28477
    94
            else (unregister_locked minthread;
wenzelm@28477
    95
            cancelolder (ThreadHeap.delete_min heap))
wenzelm@28477
    96
          end
wenzelm@28477
    97
        val _ = change timeout_heap cancelolder
wenzelm@28477
    98
        (* try to interrupt threads that are to cancel*)
wenzelm@28477
    99
        fun interrupt t = Thread.interrupt t handle Thread _ => ()
wenzelm@28477
   100
        val _ = change cancelling (filter (fn (t,_,_,_) => Thread.isActive t))
wenzelm@28477
   101
        val _ = map (fn (t, _, _, _) => interrupt t) (! cancelling)
wenzelm@28477
   102
        (* if there are threads to cancel, send periodic interrupts *)
wenzelm@28477
   103
        (* TODO: find out what realtime-values are appropriate *)
wenzelm@28477
   104
        val next_time =
wenzelm@28477
   105
          if length (! cancelling) > 0 then
wenzelm@28477
   106
           Time.now() + Time.fromMilliseconds 300
wenzelm@28477
   107
          else if ThreadHeap.is_empty (! timeout_heap) then
wenzelm@28477
   108
            Time.now() + Time.fromSeconds 10
wenzelm@28477
   109
          else
wenzelm@28477
   110
            #1 (ThreadHeap.min (! timeout_heap))
wenzelm@28477
   111
          in
wenzelm@28477
   112
            wait_for_next_event next_time
wenzelm@28477
   113
          end
wenzelm@28477
   114
        in wait_for_next_event Time.zeroTime end,
wenzelm@28477
   115
        [Thread.InterruptState Thread.InterruptDefer])
wenzelm@28477
   116
      in managing_thread := new_thread end
wenzelm@28477
   117
wenzelm@28477
   118
  (* calling thread registers itself to be managed here with a relative timeout *)
wenzelm@28477
   119
  fun register birthtime deadtime (thread, name) =
wenzelm@28477
   120
    let
wenzelm@28477
   121
    val _ = Mutex.lock lock
wenzelm@28477
   122
    (* create the atp-managing-thread if this is the first call to register *)
wenzelm@28477
   123
    val _ = if Thread.isActive (! managing_thread) then () else start ()
wenzelm@28477
   124
    (* insertion *)
wenzelm@28477
   125
    val _ = change timeout_heap (ThreadHeap.insert (deadtime, thread))
wenzelm@28477
   126
    val _ = change oldest_heap (ThreadHeap.insert (birthtime, thread))
wenzelm@28477
   127
    val _ = change active (cons (thread, birthtime, deadtime, name))
wenzelm@28477
   128
    (*maximum number of atps must not exceed*)
wenzelm@28477
   129
    val _ = let
wenzelm@28477
   130
      fun kill_oldest () =
wenzelm@28477
   131
        let val (_, oldest_thread) = ThreadHeap.min (!oldest_heap)
wenzelm@28477
   132
        val _ = change oldest_heap (ThreadHeap.delete_min)
wenzelm@28477
   133
        in unregister_locked oldest_thread end
wenzelm@28477
   134
      in
wenzelm@28477
   135
        while ! maximum_atps > ~1 andalso length (! active) > ! maximum_atps
wenzelm@28477
   136
        do kill_oldest ()
wenzelm@28477
   137
      end
wenzelm@28477
   138
    (* state of threadmanager changed => signal *)
wenzelm@28477
   139
    val _ = ConditionVar.signal state_change
wenzelm@28477
   140
    val _ = Mutex.unlock lock
wenzelm@28477
   141
    in () end
wenzelm@28477
   142
wenzelm@28477
   143
  (* calling Thread unregisters itself from Threadmanager; thread is responsible
wenzelm@28477
   144
    to terminate after calling this method *)
wenzelm@28477
   145
  fun unregister success =
wenzelm@28477
   146
    let val _ = Mutex.lock lock
wenzelm@28477
   147
    val thread = Thread.self ()
wenzelm@28477
   148
    (* get birthtime of unregistering thread - for group-killing*)
wenzelm@28477
   149
    fun get_birthtime [] = Time.zeroTime
wenzelm@28477
   150
      | get_birthtime ((t,tb,td,desc)::actives) = if Thread.equal (thread, t)
wenzelm@28477
   151
      then tb
wenzelm@28477
   152
      else get_birthtime actives
wenzelm@28477
   153
    val birthtime = get_birthtime (! active)
wenzelm@28477
   154
    (* remove unregistering thread *)
wenzelm@28477
   155
    val _ = change active (filter_out (fn (t,_,_,_) => Thread.equal (t, thread)))
wenzelm@28477
   156
    val _ = if (! groupkilling) andalso success
wenzelm@28477
   157
      then (* remove all threads of the same group *)
wenzelm@28477
   158
      let
wenzelm@28477
   159
      val group_threads = filter (fn (_, tb, _, _) => tb = birthtime) (! active)
wenzelm@28477
   160
      val _ = change cancelling (append group_threads)
wenzelm@28477
   161
      val _ = change active (filter_out (fn (_, tb, _, _) => tb = birthtime))
wenzelm@28477
   162
      in () end
wenzelm@28477
   163
      else ()
wenzelm@28477
   164
    val _ = ConditionVar.signal state_change
wenzelm@28477
   165
    val _ = Mutex.unlock lock
wenzelm@28477
   166
    in () end;
wenzelm@28477
   167
wenzelm@28477
   168
  (* Move all threads to cancelling *)
wenzelm@28477
   169
  fun kill_all () =
wenzelm@28477
   170
    let
wenzelm@28477
   171
    val _ = Mutex.lock lock
wenzelm@28477
   172
    val _ = change active (map (fn (th, tb, _, desc) => (th, tb, Time.now(), desc)))
wenzelm@28477
   173
    val _ = change cancelling (append (! active))
wenzelm@28477
   174
    val _ = change active (fn _ => [])
wenzelm@28477
   175
    val _ = ConditionVar.signal state_change
wenzelm@28477
   176
    val _ = Mutex.unlock lock
wenzelm@28477
   177
    in () end;
wenzelm@28477
   178
wenzelm@28477
   179
  fun info () =
wenzelm@28477
   180
    let
wenzelm@28477
   181
    val _ = Mutex.lock lock
wenzelm@28477
   182
    fun running_info (_, birth_time, dead_time, desc) =
wenzelm@28477
   183
      priority ("Running: "
wenzelm@28477
   184
        ^ ((Int.toString o Time.toSeconds) (Time.now() - birth_time))
wenzelm@28477
   185
        ^ " s  --  "
wenzelm@28477
   186
        ^ ((Int.toString o Time.toSeconds) (dead_time - Time.now()))
wenzelm@28477
   187
        ^ " s to live:\n" ^ desc)
wenzelm@28477
   188
    fun cancelling_info (_, _, dead_time, desc) =
wenzelm@28477
   189
      priority ("Trying to interrupt thread since "
wenzelm@28477
   190
        ^ (Int.toString o Time.toSeconds) (Time.now() - dead_time)
wenzelm@28477
   191
        ^ " s:\n" ^ desc )
wenzelm@28477
   192
    val _ = if length (! active) = 0 then [priority "No ATPs running."]
wenzelm@28477
   193
      else (priority "--- RUNNING ATPs ---";
wenzelm@28477
   194
      map (fn entry => running_info entry) (! active))
wenzelm@28477
   195
    val _ = if length (! cancelling) = 0 then []
wenzelm@28477
   196
      else (priority "--- TRYING TO INTERRUPT FOLLOWING ATPs ---";
wenzelm@28477
   197
      map (fn entry => cancelling_info entry) (! cancelling))
wenzelm@28477
   198
    val _ = Mutex.unlock lock
wenzelm@28477
   199
    in () end;
wenzelm@28477
   200
wenzelm@28477
   201
    (* integration into ProofGeneral *)
wenzelm@28477
   202
wenzelm@28477
   203
    fun set_max_atp number = CRITICAL (fn () => maximum_atps := number);
wenzelm@28477
   204
    fun set_atps str = CRITICAL (fn () => atps := str);
wenzelm@28477
   205
    fun set_timeout time = CRITICAL (fn () => timeout := time);
wenzelm@28477
   206
    fun set_groupkilling boolean = CRITICAL (fn () => groupkilling := boolean);
wenzelm@28477
   207
wenzelm@28477
   208
    (* some settings will be accessible via Isabelle -> Settings *)
wenzelm@28477
   209
    val _ = ProofGeneralPgip.add_preference "Proof"
wenzelm@28477
   210
        {name = "ATP - Provers (e,vampire,spass)",
wenzelm@28477
   211
         descr = "Which external automatic provers (seperated by commas)",
wenzelm@28477
   212
         default = !atps,
wenzelm@28477
   213
         pgiptype = PgipTypes.Pgipstring,
wenzelm@28477
   214
         get = fn () => !atps,
wenzelm@28477
   215
         set = set_atps} handle Error => warning "Preference already exists";
wenzelm@28477
   216
    val _ = ProofGeneralPgip.add_preference "Proof"
wenzelm@28477
   217
        {name = "ATP - Maximum number",
wenzelm@28477
   218
         descr = "How many provers may run parallel",
wenzelm@28477
   219
         default = Int.toString (! maximum_atps),
wenzelm@28477
   220
         pgiptype = PgipTypes.Pgipstring,
wenzelm@28477
   221
         get = fn () => Int.toString (! maximum_atps),
wenzelm@28477
   222
         set = (fn str => let val int_opt = Int.fromString str
wenzelm@28477
   223
            val nr = if isSome int_opt then valOf int_opt else 1
wenzelm@28477
   224
         in set_max_atp nr end)} handle Error => warning "Preference already exists";
wenzelm@28477
   225
    val _ = ProofGeneralPgip.add_preference "Proof"
wenzelm@28477
   226
        {name = "ATP - Timeout",
wenzelm@28477
   227
         descr = "ATPs will be interrupted after this time (in seconds)",
wenzelm@28477
   228
         default = Int.toString (! timeout),
wenzelm@28477
   229
         pgiptype = PgipTypes.Pgipstring,
wenzelm@28477
   230
         get = fn () => Int.toString (! timeout),
wenzelm@28477
   231
         set = (fn str => let val int_opt = Int.fromString str
wenzelm@28477
   232
            val nr = if isSome int_opt then valOf int_opt else 60
wenzelm@28477
   233
         in set_timeout nr end)} handle Error => warning "Preference already exists";
wenzelm@28477
   234
wenzelm@28477
   235
  (* Additional Provers can be added as follows:
wenzelm@28477
   236
  SPASS with max_new 40 and theory_const false, timeout 60
wenzelm@28477
   237
  setup{* AtpManager.Provers.map (Symtab.update ("spass2", AtpThread.spass 40 false 60)) *}
wenzelm@28477
   238
  arbitrary prover supporting tptp-input/output
wenzelm@28477
   239
  setup{* AtpManagerProvers.map (Symtab.update ("tptp", AtpThread.tptp_prover "path/to/prover -options" 60)) *}
wenzelm@28477
   240
  *)
wenzelm@28477
   241
  structure Provers = TheoryDataFun
wenzelm@28477
   242
  (
wenzelm@28477
   243
    type T = (Toplevel.state -> (Thread.thread * string)) Symtab.table
wenzelm@28477
   244
    val empty = Symtab.empty
wenzelm@28477
   245
    val copy = I
wenzelm@28477
   246
    val extend = I
wenzelm@28477
   247
    fun merge _ = Symtab.merge (K true)
wenzelm@28477
   248
  );
wenzelm@28477
   249
wenzelm@28477
   250
  (* sledghammer: *)
wenzelm@28477
   251
  fun sledgehammer state =
wenzelm@28477
   252
    let
wenzelm@28477
   253
    val proverids = String.tokens (fn c => c = #",") (! atps)
wenzelm@28477
   254
    (* get context *)
wenzelm@28477
   255
    val (ctxt, _) = Proof.get_goal (Toplevel.proof_of state)
wenzelm@28477
   256
    val thy = ProofContext.theory_of ctxt
wenzelm@28477
   257
    (* get prover-functions *)
wenzelm@28477
   258
    val lookups = map (fn prover => Symtab.lookup (Provers.get thy) prover)
wenzelm@28477
   259
      proverids
wenzelm@28477
   260
    val filtered_calls = filter (fn call => isSome call) lookups
wenzelm@28477
   261
    val calls = map (fn some => valOf some) filtered_calls
wenzelm@28477
   262
    (* call provers *)
wenzelm@28477
   263
    val threads_names = map (fn call => call state) calls
wenzelm@28477
   264
    val birthtime = Time.now()
wenzelm@28477
   265
    val deadtime = Time.now() + (Time.fromSeconds (! timeout))
wenzelm@28477
   266
    val _ = map (register birthtime deadtime) threads_names
wenzelm@28477
   267
    in () end
wenzelm@28477
   268
wenzelm@28477
   269
  val _ =
wenzelm@28477
   270
    OuterSyntax.command "sledgehammer" "call all automatic theorem provers" OuterKeyword.diag
wenzelm@28477
   271
    (Scan.succeed (Toplevel.no_timing o Toplevel.unknown_proof o Toplevel.keep sledgehammer));
wenzelm@28477
   272
end;