src/HOL/Tools/Sledgehammer/sledgehammer_filter_mash.ML
changeset 48332 271a4a6af734
parent 48330 192444b53e86
child 48377 4a11914fd530
equal deleted inserted replaced
48331:f190a6dbb29b 48332:271a4a6af734
    35     Proof.context -> string -> theory -> status -> term list -> string list
    35     Proof.context -> string -> theory -> status -> term list -> string list
    36   val isabelle_dependencies_of : unit Symtab.table -> thm -> string list
    36   val isabelle_dependencies_of : unit Symtab.table -> thm -> string list
    37   val goal_of_thm : theory -> thm -> thm
    37   val goal_of_thm : theory -> thm -> thm
    38   val run_prover_for_mash :
    38   val run_prover_for_mash :
    39     Proof.context -> params -> string -> fact list -> thm -> prover_result
    39     Proof.context -> params -> string -> fact list -> thm -> prover_result
    40   val mash_RESET : Proof.context -> unit
    40   val mash_CLEAR : Proof.context -> unit
    41   val mash_INIT :
    41   val mash_INIT :
    42     Proof.context -> bool
    42     Proof.context -> bool
    43     -> (string * string list * string list * string list) list -> unit
    43     -> (string * string list * string list * string list) list -> unit
    44   val mash_ADD :
    44   val mash_ADD :
    45     Proof.context -> bool
    45     Proof.context -> bool
    46     -> (string * string list * string list * string list) list -> unit
    46     -> (string * string list * string list * string list) list -> unit
    47   val mash_QUERY :
    47   val mash_QUERY :
    48     Proof.context -> bool -> int -> string list * string list -> string list
    48     Proof.context -> bool -> int -> string list * string list -> string list
    49   val mash_reset : Proof.context -> unit
    49   val mash_unlearn : Proof.context -> unit
    50   val mash_could_suggest_facts : unit -> bool
    50   val mash_could_suggest_facts : unit -> bool
    51   val mash_can_suggest_facts : Proof.context -> bool
    51   val mash_can_suggest_facts : Proof.context -> bool
    52   val mash_suggest_facts :
    52   val mash_suggest_facts :
    53     Proof.context -> params -> string -> int -> term list -> term
    53     Proof.context -> params -> string -> int -> term list -> term
    54     -> ('a * thm) list -> ('a * thm) list * ('a * thm) list
    54     -> ('a * thm) list -> ('a * thm) list * ('a * thm) list
   243 (* TODO: Generate type classes for types? *)
   243 (* TODO: Generate type classes for types? *)
   244 fun features_of ctxt prover thy status ts =
   244 fun features_of ctxt prover thy status ts =
   245   thy_feature_name_of (Context.theory_name thy) ::
   245   thy_feature_name_of (Context.theory_name thy) ::
   246   interesting_terms_types_and_classes ctxt prover term_max_depth type_max_depth
   246   interesting_terms_types_and_classes ctxt prover term_max_depth type_max_depth
   247                                       ts
   247                                       ts
   248   |> exists (not o is_lambda_free) ts ? cons "lambdas"
   248   |> forall is_lambda_free ts ? cons "no_lams"
   249   |> exists (exists_Const is_exists) ts ? cons "skolems"
   249   |> forall (not o exists_Const is_exists) ts ? cons "no_skos"
   250   |> (case status of
   250   |> (case status of
   251         General => I
   251         General => I
   252       | Induction => cons "induction"
   252       | Induction => cons "induction"
   253       | Intro => cons "intro"
   253       | Intro => cons "intro"
   254       | Inductive => cons "inductive"
   254       | Inductive => cons "inductive"
   290 
   290 
   291 fun mash_info overlord =
   291 fun mash_info overlord =
   292   if overlord then (getenv "ISABELLE_HOME_USER", "")
   292   if overlord then (getenv "ISABELLE_HOME_USER", "")
   293   else (getenv "ISABELLE_TMP", serial_string ())
   293   else (getenv "ISABELLE_TMP", serial_string ())
   294 
   294 
   295 fun run_mash ctxt (temp_dir, serial) core =
   295 fun run_mash ctxt overlord (temp_dir, serial) core =
   296   let
   296   let
   297     val log_file = temp_dir ^ "/mash_log" ^ serial
   297     val log_file = temp_dir ^ "/mash_log" ^ serial
   298     val err_file = temp_dir ^ "/mash_err" ^ serial
   298     val err_file = temp_dir ^ "/mash_err" ^ serial
   299     val command =
   299     val command =
   300       mash_home () ^ "/mash.py --quiet --outputDir " ^ mash_state_dir () ^
   300       mash_home () ^ "/mash.py --quiet --outputDir " ^ mash_state_dir () ^
   301       " --log " ^ log_file ^ " " ^ core ^ " 2>&1 > " ^ err_file
   301       " --log " ^ log_file ^ " " ^ core ^ " 2>&1 > " ^ err_file
   302   in
   302   in
   303     trace_msg ctxt (fn () => "Running " ^ command);
   303     trace_msg ctxt (fn () => "Running " ^ command);
   304     write_file ([], K "") log_file;
   304     write_file ([], K "") log_file;
   305     write_file ([], K "") err_file;
   305     write_file ([], K "") err_file;
   306     Isabelle_System.bash command; ()
   306     Isabelle_System.bash command;
   307   end
   307     if overlord then ()
   308 
   308     else (map (File.rm o Path.explode) [log_file, err_file]; ());
       
   309     trace_msg ctxt (K "Done")
       
   310   end
       
   311 
       
   312 (* TODO: Eliminate code once "mash.py" handles sequences of ADD commands as fast
       
   313    as a single INIT. *)
   309 fun run_mash_init ctxt overlord write_access write_feats write_deps =
   314 fun run_mash_init ctxt overlord write_access write_feats write_deps =
   310   let
   315   let
   311     val info as (temp_dir, serial) = mash_info overlord
   316     val info as (temp_dir, serial) = mash_info overlord
   312     val in_dir = temp_dir ^ "/mash_init" ^ serial
   317     val in_dir = temp_dir ^ "/mash_init" ^ serial
   313                  |> tap (Isabelle_System.mkdir o Path.explode)
   318     val in_dir_path = in_dir |> Path.explode |> tap Isabelle_System.mkdir
   314   in
   319   in
   315     write_file write_access (in_dir ^ "/mash_accessibility");
   320     write_file write_access (in_dir ^ "/mash_accessibility");
   316     write_file write_feats (in_dir ^ "/mash_features");
   321     write_file write_feats (in_dir ^ "/mash_features");
   317     write_file write_deps (in_dir ^ "/mash_dependencies");
   322     write_file write_deps (in_dir ^ "/mash_dependencies");
   318     run_mash ctxt info ("--init --inputDir " ^ in_dir)
   323     run_mash ctxt overlord info ("--init --inputDir " ^ in_dir);
       
   324     (* FIXME: temporary hack *)
       
   325     if overlord then ()
       
   326     else (Isabelle_System.bash ("rm -r -f " ^ File.shell_path in_dir_path); ())
   319   end
   327   end
   320 
   328 
   321 fun run_mash_commands ctxt overlord save max_suggs write_cmds read_suggs =
   329 fun run_mash_commands ctxt overlord save max_suggs write_cmds read_suggs =
   322   let
   330   let
   323     val info as (temp_dir, serial) = mash_info overlord
   331     val info as (temp_dir, serial) = mash_info overlord
   324     val sugg_file = temp_dir ^ "/mash_suggs" ^ serial
   332     val sugg_file = temp_dir ^ "/mash_suggs" ^ serial
   325     val cmd_file = temp_dir ^ "/mash_commands" ^ serial
   333     val cmd_file = temp_dir ^ "/mash_commands" ^ serial
   326   in
   334   in
   327     write_file ([], K "") sugg_file;
   335     write_file ([], K "") sugg_file;
   328     write_file write_cmds cmd_file;
   336     write_file write_cmds cmd_file;
   329     run_mash ctxt info
   337     run_mash ctxt overlord info
   330              ("--inputFile " ^ cmd_file ^ " --predictions " ^ sugg_file ^
   338              ("--inputFile " ^ cmd_file ^ " --predictions " ^ sugg_file ^
   331               " --numberOfPredictions " ^ string_of_int max_suggs ^
   339               " --numberOfPredictions " ^ string_of_int max_suggs ^
   332               (if save then " --saveModel" else ""));
   340               (if save then " --saveModel" else ""));
   333     read_suggs (fn () => File.read_lines (Path.explode sugg_file))
   341     read_suggs (fn () => File.read_lines (Path.explode sugg_file))
       
   342     |> tap (fn _ =>
       
   343                if overlord then ()
       
   344                else (map (File.rm o Path.explode) [sugg_file, cmd_file]; ()))
   334   end
   345   end
   335 
   346 
   336 fun str_of_update (name, parents, feats, deps) =
   347 fun str_of_update (name, parents, feats, deps) =
   337   "! " ^ escape_meta name ^ ": " ^ escape_metas parents ^ "; " ^
   348   "! " ^ escape_meta name ^ ": " ^ escape_metas parents ^ "; " ^
   338   escape_metas feats ^ "; " ^ escape_metas deps ^ "\n"
   349   escape_metas feats ^ "; " ^ escape_metas deps ^ "\n"
   341   "? " ^ escape_metas parents ^ "; " ^ escape_metas feats
   352   "? " ^ escape_metas parents ^ "; " ^ escape_metas feats
   342 
   353 
   343 fun init_str_of_update get (upd as (name, _, _, _)) =
   354 fun init_str_of_update get (upd as (name, _, _, _)) =
   344   escape_meta name ^ ": " ^ escape_metas (get upd) ^ "\n"
   355   escape_meta name ^ ": " ^ escape_metas (get upd) ^ "\n"
   345 
   356 
   346 fun mash_RESET ctxt =
   357 fun mash_CLEAR ctxt =
   347   let val path = mash_state_dir () |> Path.explode in
   358   let val path = mash_state_dir () |> Path.explode in
   348     trace_msg ctxt (K "MaSh RESET");
   359     trace_msg ctxt (K "MaSh CLEAR");
   349     File.fold_dir (fn file => fn () =>
   360     File.fold_dir (fn file => fn () =>
   350                       File.rm (Path.append path (Path.basic file)))
   361                       File.rm (Path.append path (Path.basic file)))
   351                   path ()
   362                   path ()
   352   end
   363   end
   353 
   364 
   354 fun mash_INIT ctxt _ [] = mash_RESET ctxt
   365 fun mash_INIT ctxt _ [] = mash_CLEAR ctxt
   355   | mash_INIT ctxt overlord upds =
   366   | mash_INIT ctxt overlord upds =
   356     (trace_msg ctxt (fn () => "MaSh INIT " ^
   367     (trace_msg ctxt (fn () => "MaSh INIT " ^
   357          elide_string 1000 (space_implode " " (map #1 upds)));
   368          elide_string 1000 (space_implode " " (map #1 upds)));
   358      run_mash_init ctxt overlord (upds, init_str_of_update #2)
   369      run_mash_init ctxt overlord (upds, init_str_of_update #2)
   359                    (upds, init_str_of_update #3) (upds, init_str_of_update #4))
   370                    (upds, init_str_of_update #3) (upds, init_str_of_update #4))
   442   Synchronized.change global_state (mash_load ctxt ##> (f #> tap mash_save))
   453   Synchronized.change global_state (mash_load ctxt ##> (f #> tap mash_save))
   443 
   454 
   444 fun mash_get ctxt =
   455 fun mash_get ctxt =
   445   Synchronized.change_result global_state (mash_load ctxt #> `snd)
   456   Synchronized.change_result global_state (mash_load ctxt #> `snd)
   446 
   457 
   447 fun mash_reset ctxt =
   458 fun mash_unlearn ctxt =
   448   Synchronized.change global_state (fn _ =>
   459   Synchronized.change global_state (fn _ =>
   449       (mash_RESET ctxt; File.write (mash_state_path ()) "";
   460       (mash_CLEAR ctxt; File.write (mash_state_path ()) "";
   450        (true, empty_state)))
   461        (true, empty_state)))
   451 
   462 
   452 end
   463 end
   453 
   464 
   454 fun mash_could_suggest_facts () = mash_home () <> ""
   465 fun mash_could_suggest_facts () = mash_home () <> ""
   455 fun mash_can_suggest_facts ctxt =
   466 fun mash_can_suggest_facts ctxt =
   456   not (Graph.is_empty (#fact_graph (mash_get ctxt)))
   467   not (Graph.is_empty (#fact_graph (mash_get ctxt)))
   457 
   468 
   458 fun parents_wrt_facts ctxt facts fact_graph =
   469 fun parents_wrt_facts facts fact_graph =
   459   let
   470   let
   460     val facts = [] |> fold (cons o Thm.get_name_hint o snd) facts
   471     val facts = [] |> fold (cons o Thm.get_name_hint o snd) facts
   461     val tab = Symtab.empty |> fold (fn name => Symtab.update (name, ())) facts
   472     val tab = Symtab.empty |> fold (fn name => Symtab.update (name, ())) facts
   462     val maxs = Graph.maximals fact_graph
   473     fun insert_not_parent parents name =
   463   in
   474       not (member (op =) parents name) ? insert (op =) name
   464     if forall (Symtab.defined tab) maxs then
   475     fun parents_of parents [] = parents
   465       maxs
   476       | parents_of parents (name :: names) =
   466     else
   477         if Symtab.defined tab name then
   467       let
   478           parents_of (name :: parents) names
   468         val graph_facts = Symtab.make (map (rpair ()) (Graph.keys fact_graph))
   479         else
   469         val ancestors =
   480           parents_of parents (Graph.Keys.fold (insert_not_parent parents)
   470           try_graph ctxt "when computing ancestor facts" [] (fn () =>
   481                                   (Graph.imm_preds fact_graph name) names)
   471               facts |> filter (Symtab.defined graph_facts)
   482   in parents_of [] (Graph.maximals fact_graph) end
   472                     |> Graph.all_preds fact_graph)
       
   473         val ancestors =
       
   474           Symtab.empty |> fold (fn name => Symtab.update (name, ())) ancestors
       
   475       in
       
   476         try_graph ctxt "when computing parent facts" [] (fn () =>
       
   477             fact_graph |> Graph.restrict (Symtab.defined ancestors)
       
   478                        |> Graph.maximals)
       
   479       end
       
   480   end
       
   481 
   483 
   482 (* Generate more suggestions than requested, because some might be thrown out
   484 (* Generate more suggestions than requested, because some might be thrown out
   483    later for various reasons and "meshing" gives better results with some
   485    later for various reasons and "meshing" gives better results with some
   484    slack. *)
   486    slack. *)
   485 fun max_suggs_of max_facts = max_facts + Int.min (200, max_facts)
   487 fun max_suggs_of max_facts = max_facts + Int.min (200, max_facts)
   488   can (Graph.get_node fact_graph) (Thm.get_name_hint th)
   490   can (Graph.get_node fact_graph) (Thm.get_name_hint th)
   489 
   491 
   490 fun mash_suggest_facts ctxt ({overlord, ...} : params) prover max_facts hyp_ts
   492 fun mash_suggest_facts ctxt ({overlord, ...} : params) prover max_facts hyp_ts
   491                        concl_t facts =
   493                        concl_t facts =
   492   let
   494   let
   493 val timer = Timer.startRealTimer ()
       
   494     val thy = Proof_Context.theory_of ctxt
   495     val thy = Proof_Context.theory_of ctxt
   495 val _ = tracing ("A1: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   496     val fact_graph = #fact_graph (mash_get ctxt)
   496     val fact_graph = #fact_graph (mash_get ctxt)
   497 val _ = tracing ("A2: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
   497     val parents = parents_wrt_facts facts fact_graph
   498     val parents = parents_wrt_facts ctxt facts fact_graph
       
   499 val _ = tracing ("A3: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   500     val feats = features_of ctxt prover thy General (concl_t :: hyp_ts)
   498     val feats = features_of ctxt prover thy General (concl_t :: hyp_ts)
   501 val _ = tracing ("A4: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   502     val suggs =
   499     val suggs =
   503       if Graph.is_empty fact_graph then []
   500       if Graph.is_empty fact_graph then []
   504       else mash_QUERY ctxt overlord (max_suggs_of max_facts) (parents, feats)
   501       else mash_QUERY ctxt overlord (max_suggs_of max_facts) (parents, feats)
   505 val _ = tracing ("A5: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   506     val selected = facts |> suggested_facts suggs
   502     val selected = facts |> suggested_facts suggs
   507 val _ = tracing ("A6: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   508     val unknown = facts |> filter_out (is_fact_in_graph fact_graph)
   503     val unknown = facts |> filter_out (is_fact_in_graph fact_graph)
   509 val _ = tracing ("END: " ^ PolyML.makestring (Timer.checkRealTimer timer)) (*###*)
       
   510   in (selected, unknown) end
   504   in (selected, unknown) end
   511 
   505 
   512 fun add_thys_for thy =
   506 fun add_thys_for thy =
   513   let fun add comp thy = Symtab.update (Context.theory_name thy, comp) in
   507   let fun add comp thy = Symtab.update (Context.theory_name thy, comp) in
   514     add false thy #> fold (add true) (Theory.ancestors_of thy)
   508     add false thy #> fold (add true) (Theory.ancestors_of thy)
   523     val (parents, graph) = ([], graph) |> fold maybe_add_from parents
   517     val (parents, graph) = ([], graph) |> fold maybe_add_from parents
   524     val (deps, graph) = ([], graph) |> fold maybe_add_from deps
   518     val (deps, graph) = ([], graph) |> fold maybe_add_from deps
   525   in ((name, parents, feats, deps) :: upds, graph) end
   519   in ((name, parents, feats, deps) :: upds, graph) end
   526 
   520 
   527 val pass1_learn_timeout_factor = 0.5
   521 val pass1_learn_timeout_factor = 0.5
       
   522 
       
   523 (* Too many dependencies is a sign that a decision procedure is at work. There
       
   524    isn't much too learn from such proofs. *)
       
   525 val max_dependencies = 10
   528 
   526 
   529 (* The timeout is understood in a very slack fashion. *)
   527 (* The timeout is understood in a very slack fashion. *)
   530 fun mash_learn_thy ctxt ({provers, verbose, overlord, ...} : params) thy timeout
   528 fun mash_learn_thy ctxt ({provers, verbose, overlord, ...} : params) thy timeout
   531                    facts =
   529                    facts =
   532   let
   530   let
   546         val ths = facts |> map snd
   544         val ths = facts |> map snd
   547         val all_names =
   545         val all_names =
   548           ths |> filter_out is_likely_tautology_or_too_meta
   546           ths |> filter_out is_likely_tautology_or_too_meta
   549               |> map (rpair () o Thm.get_name_hint)
   547               |> map (rpair () o Thm.get_name_hint)
   550               |> Symtab.make
   548               |> Symtab.make
       
   549         fun trim_deps deps = if length deps > max_dependencies then [] else deps
   551         fun do_fact _ (accum as (_, true)) = accum
   550         fun do_fact _ (accum as (_, true)) = accum
   552           | do_fact ((_, (_, status)), th) ((parents, upds), false) =
   551           | do_fact ((_, (_, status)), th) ((parents, upds), false) =
   553             let
   552             let
   554               val name = Thm.get_name_hint th
   553               val name = Thm.get_name_hint th
   555               val feats = features_of ctxt prover thy status [prop_of th]
   554               val feats =
   556               val deps = isabelle_dependencies_of all_names th
   555                 features_of ctxt prover (theory_of_thm th) status [prop_of th]
       
   556               val deps = isabelle_dependencies_of all_names th |> trim_deps
   557               val upd = (name, parents, feats, deps)
   557               val upd = (name, parents, feats, deps)
   558             in (([name], upd :: upds), timed_out pass1_learn_timeout_factor) end
   558             in (([name], upd :: upds), timed_out pass1_learn_timeout_factor) end
   559         val parents = parents_wrt_facts ctxt facts fact_graph
   559         val parents = parents_wrt_facts facts fact_graph
   560         val ((_, upds), _) =
   560         val ((_, upds), _) =
   561           ((parents, []), false) |> fold do_fact new_facts |>> apsnd rev
   561           ((parents, []), false) |> fold do_fact new_facts |>> apsnd rev
   562         val n = length upds
   562         val n = length upds
   563         fun trans {thys, fact_graph} =
   563         fun trans {thys, fact_graph} =
   564           let
   564           let
   592     val feats = features_of ctxt prover thy General [t]
   592     val feats = features_of ctxt prover thy General [t]
   593     val deps = used_ths |> map Thm.get_name_hint
   593     val deps = used_ths |> map Thm.get_name_hint
   594   in
   594   in
   595     mash_map ctxt (fn {thys, fact_graph} =>
   595     mash_map ctxt (fn {thys, fact_graph} =>
   596         let
   596         let
   597           val parents = parents_wrt_facts ctxt facts fact_graph
   597           val parents = parents_wrt_facts facts fact_graph
   598           val upds = [(name, parents, feats, deps)]
   598           val upds = [(name, parents, feats, deps)]
   599           val (upds, fact_graph) =
   599           val (upds, fact_graph) =
   600             ([], fact_graph) |> fold (update_fact_graph ctxt) upds
   600             ([], fact_graph) |> fold (update_fact_graph ctxt) upds
   601         in
   601         in
   602           mash_ADD ctxt overlord upds;
   602           mash_ADD ctxt overlord upds;