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; |