src/HOL/Nunchaku/Tools/nunchaku_commands.ML
changeset 64389 6273d4c8325b
child 66163 45d3d43abee7
equal deleted inserted replaced
64388:14571c9e1d50 64389:6273d4c8325b
       
     1 (*  Title:      HOL/Nunchaku/Tools/nunchaku_commands.ML
       
     2     Author:     Jasmin Blanchette, Inria Nancy, LORIA, MPII
       
     3     Copyright   2015, 2016
       
     4 
       
     5 Adds the "nunchaku" and "nunchaku_params" commands to Isabelle/Isar's outer syntax.
       
     6 *)
       
     7 
       
     8 signature NUNCHAKU_COMMANDS =
       
     9 sig
       
    10   type params = Nunchaku.params
       
    11 
       
    12   val default_params: theory -> (string * string) list -> params
       
    13 end;
       
    14 
       
    15 structure Nunchaku_Commands : NUNCHAKU_COMMANDS =
       
    16 struct
       
    17 
       
    18 open Nunchaku_Util;
       
    19 open Nunchaku;
       
    20 
       
    21 type raw_param = string * string list;
       
    22 
       
    23 val default_default_params =
       
    24   [("assms", "true"),
       
    25    ("debug", "false"),
       
    26    ("falsify", "true"),
       
    27    ("max_genuine", "1"),
       
    28    ("max_potential", "1"),
       
    29    ("overlord", "false"),
       
    30    ("specialize", "true"),
       
    31    ("spy", "false"),
       
    32    ("timeout", "30"),
       
    33    ("verbose", "false"),
       
    34    ("wf_timeout", "0.5")];
       
    35 
       
    36 val negated_params =
       
    37   [("dont_whack", "whack"),
       
    38    ("dont_specialize", "specialize"),
       
    39    ("dont_spy", "spy"),
       
    40    ("no_assms", "assms"),
       
    41    ("no_debug", "debug"),
       
    42    ("no_overlord", "overlord"),
       
    43    ("non_mono", "mono"),
       
    44    ("non_wf", "wf"),
       
    45    ("quiet", "verbose"),
       
    46    ("satisfy", "falsify")];
       
    47 
       
    48 fun is_known_raw_param s =
       
    49   AList.defined (op =) default_default_params s orelse
       
    50   AList.defined (op =) negated_params s orelse
       
    51   member (op =) ["atoms", "card", "eval", "expect"] s orelse
       
    52   exists (fn p => String.isPrefix (p ^ " ") s)
       
    53     ["atoms", "card", "dont_whack", "mono", "non_mono", "non_wf", "wf", "whack"];
       
    54 
       
    55 fun check_raw_param (s, _) =
       
    56   if is_known_raw_param s then () else error ("Unknown parameter: " ^ quote s);
       
    57 
       
    58 fun unnegate_param_name name =
       
    59   (case AList.lookup (op =) negated_params name of
       
    60     NONE =>
       
    61     if String.isPrefix "dont_" name then SOME (unprefix "dont_" name)
       
    62     else if String.isPrefix "non_" name then SOME (unprefix "non_" name)
       
    63     else NONE
       
    64   | some_name => some_name);
       
    65 
       
    66 fun normalize_raw_param (name, value) =
       
    67   (case unnegate_param_name name of
       
    68     SOME name' =>
       
    69     [(name',
       
    70       (case value of
       
    71         ["false"] => ["true"]
       
    72       | ["true"] => ["false"]
       
    73       | [] => ["false"]
       
    74       | _ => value))]
       
    75   | NONE => [(name, value)]);
       
    76 
       
    77 structure Data = Theory_Data
       
    78 (
       
    79   type T = raw_param list
       
    80   val empty = default_default_params |> map (apsnd single)
       
    81   val extend = I
       
    82   fun merge data = AList.merge (op =) (K true) data
       
    83 );
       
    84 
       
    85 val set_default_raw_param = Data.map o fold (AList.update (op =)) o normalize_raw_param;
       
    86 val default_raw_params = Data.get;
       
    87 
       
    88 fun is_punctuation s = (s = "," orelse s = "-");
       
    89 
       
    90 fun stringify_raw_param_value [] = ""
       
    91   | stringify_raw_param_value [s] = s
       
    92   | stringify_raw_param_value (s1 :: s2 :: ss) =
       
    93     s1 ^ (if is_punctuation s1 orelse is_punctuation s2 then "" else " ") ^
       
    94     stringify_raw_param_value (s2 :: ss);
       
    95 
       
    96 fun extract_params ctxt mode default_params override_params =
       
    97   let
       
    98     val override_params = maps normalize_raw_param override_params;
       
    99     val raw_params = rev override_params @ rev default_params;
       
   100     val raw_lookup = AList.lookup (op =) raw_params;
       
   101     val lookup = Option.map stringify_raw_param_value o raw_lookup;
       
   102     val lookup_string = the_default "" o lookup;
       
   103 
       
   104     fun general_lookup_bool option default_value name =
       
   105       (case lookup name of
       
   106         SOME s => parse_bool_option option name s
       
   107       | NONE => default_value);
       
   108 
       
   109     val lookup_bool = the o general_lookup_bool false (SOME false);
       
   110 
       
   111     fun lookup_int name =
       
   112       (case lookup name of
       
   113         SOME s =>
       
   114         (case Int.fromString s of
       
   115           SOME i => i
       
   116         | NONE => error ("Parameter " ^ quote name ^ " must be assigned an integer value"))
       
   117       | NONE => 0);
       
   118 
       
   119     fun int_range_from_string name s =
       
   120       (case space_explode "-" s of
       
   121          [s] => (s, s)
       
   122        | [s1, s2] => (s1, s2)
       
   123        | _ => error ("Parameter " ^ quote name ^ " must be assigned a range of integers"))
       
   124       |> apply2 Int.fromString;
       
   125 
       
   126     fun lookup_assigns read pre of_str =
       
   127       (case lookup pre of
       
   128         SOME s => [(NONE, of_str s)]
       
   129       | NONE => []) @
       
   130       map (fn (name, value) => (SOME (read (String.extract (name, size pre + 1, NONE))),
       
   131           of_str (stringify_raw_param_value value)))
       
   132         (filter (String.isPrefix (pre ^ " ") o fst) raw_params);
       
   133 
       
   134     fun lookup_int_range_assigns read pre =
       
   135       lookup_assigns read pre (int_range_from_string pre);
       
   136 
       
   137     fun lookup_bool_assigns read pre =
       
   138       lookup_assigns read pre (the o parse_bool_option false pre);
       
   139 
       
   140     fun lookup_bool_option_assigns read pre =
       
   141       lookup_assigns read pre (parse_bool_option true pre);
       
   142 
       
   143     fun lookup_strings_assigns read pre =
       
   144       lookup_assigns read pre (space_explode " ");
       
   145 
       
   146     fun lookup_time name =
       
   147       (case lookup name of
       
   148         SOME s => parse_time name s
       
   149       | NONE => Time.zeroTime);
       
   150 
       
   151     val read_type_polymorphic =
       
   152       Syntax.read_typ ctxt #> Logic.mk_type
       
   153       #> singleton (Variable.polymorphic ctxt) #> Logic.dest_type;
       
   154     val read_term_polymorphic =
       
   155       Syntax.read_term ctxt #> singleton (Variable.polymorphic ctxt);
       
   156     val lookup_term_list_option_polymorphic =
       
   157       AList.lookup (op =) raw_params #> Option.map (map read_term_polymorphic);
       
   158 
       
   159     fun read_const_polymorphic s =
       
   160       (case read_term_polymorphic s of
       
   161         Const x => x
       
   162       | t => error ("Not a constant: " ^ Syntax.string_of_term ctxt t));
       
   163 
       
   164     val wfs = lookup_bool_option_assigns read_const_polymorphic "wf";
       
   165     val whacks = lookup_bool_assigns read_term_polymorphic "whack";
       
   166     val cards = lookup_int_range_assigns read_type_polymorphic "card";
       
   167     val monos = lookup_bool_option_assigns read_type_polymorphic "mono";
       
   168     val falsify = lookup_bool "falsify";
       
   169     val debug = (mode <> Auto_Try andalso lookup_bool "debug");
       
   170     val verbose = debug orelse (mode <> Auto_Try andalso lookup_bool "verbose");
       
   171     val overlord = lookup_bool "overlord";
       
   172     val spy = getenv "NUNCHAKU_SPY" = "yes" orelse lookup_bool "spy";
       
   173     val assms = lookup_bool "assms";
       
   174     val specialize = lookup_bool "specialize";
       
   175     val timeout = lookup_time "timeout";
       
   176     val wf_timeout = lookup_time "wf_timeout";
       
   177     val multithread = mode = Normal andalso lookup_bool "multithread";
       
   178     val evals = these (lookup_term_list_option_polymorphic "eval");
       
   179     val atomss = lookup_strings_assigns read_type_polymorphic "atoms";
       
   180     val max_potential = if mode = Normal then Int.max (0, lookup_int "max_potential") else 0;
       
   181     val max_genuine = Int.max (0, lookup_int "max_genuine");
       
   182     val expect = lookup_string "expect";
       
   183 
       
   184     val mode_of_operation_params =
       
   185       {falsify = falsify, assms = assms, spy = spy, overlord = overlord,
       
   186        expect = expect};
       
   187 
       
   188     val scope_of_search_params =
       
   189       {wfs = wfs, whacks = whacks, cards = cards, monos = monos};
       
   190 
       
   191     val output_format_params =
       
   192       {verbose = verbose, debug = debug, max_potential = max_potential, max_genuine = max_genuine,
       
   193        evals = evals, atomss = atomss};
       
   194 
       
   195     val optimization_params =
       
   196       {specialize = specialize, multithread = multithread};
       
   197 
       
   198     val timeout_params =
       
   199       {timeout = timeout, wf_timeout = wf_timeout};
       
   200   in
       
   201     {mode_of_operation_params = mode_of_operation_params,
       
   202      scope_of_search_params = scope_of_search_params,
       
   203      output_format_params = output_format_params,
       
   204      optimization_params = optimization_params,
       
   205      timeout_params = timeout_params}
       
   206   end;
       
   207 
       
   208 fun default_params thy =
       
   209   extract_params (Proof_Context.init_global thy) Normal (default_raw_params thy)
       
   210   o map (apsnd single);
       
   211 
       
   212 val parse_key = Scan.repeat1 Parse.embedded >> space_implode " ";
       
   213 val parse_value =
       
   214   Scan.repeat1 (Parse.minus >> single
       
   215     || Scan.repeat1 (Scan.unless Parse.minus (Parse.name || Parse.float_number))
       
   216     || @{keyword ","} |-- Parse.number >> prefix "," >> single)
       
   217   >> flat;
       
   218 val parse_param = parse_key -- Scan.optional (@{keyword "="} |-- parse_value) [];
       
   219 val parse_params = Scan.optional (@{keyword "["} |-- Parse.list parse_param --| @{keyword "]"}) [];
       
   220 
       
   221 fun run_chaku override_params mode i state0 =
       
   222   let
       
   223     val state = Proof.map_contexts (Try0.silence_methods false) state0;
       
   224     val thy = Proof.theory_of state;
       
   225     val ctxt = Proof.context_of state;
       
   226     val _ = List.app check_raw_param override_params;
       
   227     val params = extract_params ctxt mode (default_raw_params thy) override_params;
       
   228   in
       
   229     (if mode = Auto_Try then perhaps o try else fn f => fn x => f x)
       
   230       (fn _ => run_chaku_on_subgoal state params mode i) (unknownN, NONE)
       
   231     |> `(fn (outcome_code, _) => outcome_code = genuineN)
       
   232   end;
       
   233 
       
   234 fun string_for_raw_param (name, value) =
       
   235   name ^ " = " ^ stringify_raw_param_value value;
       
   236 
       
   237 fun nunchaku_params_trans params =
       
   238   Toplevel.theory (fold set_default_raw_param params
       
   239     #> tap (fn thy =>
       
   240       let val params = rev (default_raw_params thy) in
       
   241         List.app check_raw_param params;
       
   242         writeln ("Default parameters for Nunchaku:\n" ^
       
   243           (params |> map string_for_raw_param |> sort_strings |> cat_lines))
       
   244       end));
       
   245 
       
   246 val _ =
       
   247   Outer_Syntax.command @{command_keyword nunchaku}
       
   248     "try to find a countermodel using Nunchaku"
       
   249     (parse_params -- Scan.optional Parse.nat 1 >> (fn (params, i) =>
       
   250        Toplevel.keep_proof (fn state =>
       
   251          ignore (run_chaku params Normal i (Toplevel.proof_of state)))));
       
   252 
       
   253 val _ =
       
   254   Outer_Syntax.command @{command_keyword nunchaku_params}
       
   255     "set and display the default parameters for Nunchaku"
       
   256     (parse_params #>> nunchaku_params_trans);
       
   257 
       
   258 end;