src/HOL/Tools/SMT/verit_proof.ML
author fleury <Mathias.Fleury@mpi-inf.mpg.de>
Tue Nov 10 17:49:54 2015 +0100 (2015-11-10)
changeset 61611 a9c0572109af
parent 59014 cc5e34575354
child 69205 8050734eee3e
permissions -rw-r--r--
fixing premises in veriT proof reconstruction
     1 (*  Title:      HOL/Tools/SMT/verit_proof.ML
     2     Author:     Mathias Fleury, ENS Rennes
     3     Author:     Sascha Boehme, TU Muenchen
     4 
     5 VeriT proofs: parsing and abstract syntax tree.
     6 *)
     7 
     8 signature VERIT_PROOF =
     9 sig
    10   (*proofs*)
    11   datatype veriT_step = VeriT_Step of {
    12     id: string,
    13     rule: string,
    14     prems: string list,
    15     concl: term,
    16     fixes: string list}
    17 
    18   (*proof parser*)
    19   val parse: typ Symtab.table -> term Symtab.table -> string list ->
    20     Proof.context -> veriT_step list * Proof.context
    21 
    22   val veriT_step_prefix : string
    23   val veriT_input_rule: string
    24   val veriT_la_generic_rule : string
    25   val veriT_rewrite_rule : string
    26   val veriT_simp_arith_rule : string
    27   val veriT_tmp_ite_elim_rule : string
    28   val veriT_tmp_skolemize_rule : string
    29 end;
    30 
    31 structure VeriT_Proof: VERIT_PROOF =
    32 struct
    33 
    34 open SMTLIB_Proof
    35 
    36 datatype veriT_node = VeriT_Node of {
    37   id: string,
    38   rule: string,
    39   prems: string list,
    40   concl: term,
    41   bounds: string list}
    42 
    43 fun mk_node id rule prems concl bounds =
    44   VeriT_Node {id = id, rule = rule, prems = prems, concl = concl, bounds = bounds}
    45 
    46 datatype veriT_step = VeriT_Step of {
    47   id: string,
    48   rule: string,
    49   prems: string list,
    50   concl: term,
    51   fixes: string list}
    52 
    53 fun mk_step id rule prems concl fixes =
    54   VeriT_Step {id = id, rule = rule, prems = prems, concl = concl, fixes = fixes}
    55 
    56 val veriT_step_prefix = ".c"
    57 val veriT_input_rule = "input"
    58 val veriT_la_generic_rule = "la_generic"
    59 val veriT_rewrite_rule = "__rewrite" (* arbitrary *)
    60 val veriT_simp_arith_rule = "simp_arith"
    61 val veriT_tmp_alphaconv_rule = "tmp_alphaconv"
    62 val veriT_tmp_ite_elim_rule = "tmp_ite_elim"
    63 val veriT_tmp_skolemize_rule = "tmp_skolemize"
    64 
    65 (* proof parser *)
    66 
    67 fun node_of p cx =
    68   ([], cx)
    69   ||>> `(with_fresh_names (term_of p))
    70   |>> snd
    71 
    72 (*in order to get Z3-style quantification*)
    73 fun repair_quantification (SMTLIB.S (SMTLIB.Sym "forall" :: l)) =
    74     let val (quantified_vars, t) = split_last (map repair_quantification l)
    75     in
    76       SMTLIB.S (SMTLIB.Sym "forall" :: SMTLIB.S quantified_vars :: t :: [])
    77     end
    78   | repair_quantification (SMTLIB.S (SMTLIB.Sym "exists" :: l)) =
    79     let val (quantified_vars, t) = split_last (map repair_quantification l)
    80     in
    81       SMTLIB.S (SMTLIB.Sym "exists" :: SMTLIB.S quantified_vars :: t :: [])
    82     end
    83   | repair_quantification (SMTLIB.S l) = SMTLIB.S (map repair_quantification l)
    84   | repair_quantification x = x
    85 
    86 fun replace_bound_var_by_free_var (q $ Abs (var, ty, u)) free_var =
    87     (case List.find (fn v => String.isPrefix v var) free_var of
    88       NONE => q $ Abs (var, ty, replace_bound_var_by_free_var u free_var)
    89     | SOME _ => replace_bound_var_by_free_var (Term.subst_bound (Free (var, ty), u)) free_var)
    90   | replace_bound_var_by_free_var (u $ v) free_vars = replace_bound_var_by_free_var u free_vars $
    91      replace_bound_var_by_free_var v free_vars
    92   | replace_bound_var_by_free_var u _ = u
    93 
    94 fun find_type_in_formula (Abs (v, T, u)) var_name =
    95     if String.isPrefix var_name v then SOME T else find_type_in_formula u var_name
    96   | find_type_in_formula (u $ v) var_name =
    97     (case find_type_in_formula u var_name of
    98       NONE => find_type_in_formula v var_name
    99     | some_T => some_T)
   100   | find_type_in_formula _ _ = NONE
   101 
   102 fun add_bound_variables_to_ctxt concl =
   103   fold (update_binding o
   104     (fn s => (s, Term (Free (s, the_default dummyT (find_type_in_formula concl s))))))
   105 
   106 fun update_step_and_cx (node as VeriT_Node {id, rule, prems, concl, bounds}) cx =
   107   if rule = veriT_tmp_ite_elim_rule then
   108     (mk_node id rule prems concl bounds, add_bound_variables_to_ctxt concl bounds cx)
   109   else if rule = veriT_tmp_skolemize_rule then
   110     let val concl' = replace_bound_var_by_free_var concl bounds in
   111       (mk_node id rule prems concl' [], add_bound_variables_to_ctxt concl bounds cx)
   112     end
   113   else
   114     (node, cx)
   115 
   116 fun fix_subproof_steps ((((id_of_father_step, rule), prems), subproof), ((step_concl, bounds),
   117     cx)) =
   118   let
   119     fun mk_prop_of_term concl =
   120       concl |> fastype_of concl = @{typ bool} ? curry (op $) @{term Trueprop}
   121     fun update_prems assumption_id prems =
   122       map (fn prem => id_of_father_step ^ prem)
   123           (filter_out (curry (op =) assumption_id) prems)
   124     fun inline_assumption assumption assumption_id
   125         (VeriT_Node {id, rule, prems, concl, bounds}) =
   126       mk_node id rule (update_prems assumption_id prems)
   127         (@{const Pure.imp} $ mk_prop_of_term assumption $ mk_prop_of_term concl) bounds
   128     fun find_input_steps_and_inline [] last_step = ([], last_step)
   129       | find_input_steps_and_inline (VeriT_Node {id = id', rule, prems, concl, bounds} :: steps)
   130           last_step =
   131         if rule = veriT_input_rule then
   132           find_input_steps_and_inline (map (inline_assumption concl id') steps) last_step
   133         else
   134           apfst (cons (mk_node (id_of_father_step ^ id') rule prems concl bounds))
   135             (find_input_steps_and_inline steps (id_of_father_step ^ id'))
   136     val (subproof', last_step_id) = find_input_steps_and_inline subproof ""
   137     val prems' =
   138       if last_step_id = "" then
   139         prems
   140       else
   141         (case prems of
   142           NONE => SOME [last_step_id]
   143         | SOME l => SOME (last_step_id :: l))
   144   in
   145     (subproof', (((((id_of_father_step, rule), prems'), step_concl), bounds), cx))
   146   end
   147 
   148 (*
   149 (set id rule :clauses(...) :args(..) :conclusion (...)).
   150 or
   151 (set id subproof (set ...) :conclusion (...)).
   152 *)
   153 
   154 fun parse_proof_step cx =
   155   let
   156     fun rotate_pair (a, (b, c)) = ((a, b), c)
   157     fun get_id (SMTLIB.S [SMTLIB.Sym "set", SMTLIB.Sym id, SMTLIB.S l]) = (id, l)
   158       | get_id t = raise Fail ("unrecognized VeriT proof " ^ @{make_string} t)
   159     fun parse_rule (SMTLIB.Sym rule :: l) = (rule, l)
   160     fun parse_source (SMTLIB.Key "clauses" :: SMTLIB.S source ::l) =
   161         (SOME (map (fn (SMTLIB.Sym id) => id) source), l)
   162       | parse_source l = (NONE, l)
   163     fun parse_subproof cx id_of_father_step ((subproof_step as SMTLIB.S (SMTLIB.Sym "set" :: _)) :: l) =
   164         let val (subproof_steps, cx') = parse_proof_step cx subproof_step in
   165           apfst (apfst (curry (op @) subproof_steps)) (parse_subproof cx' id_of_father_step l)
   166         end
   167       | parse_subproof cx _ l = (([], cx), l)
   168     fun skip_args (SMTLIB.Key "args" :: SMTLIB.S _ :: l) = l
   169       | skip_args l = l
   170     fun parse_conclusion (SMTLIB.Key "conclusion" :: SMTLIB.S concl :: []) = concl
   171     fun make_or_from_clausification l =
   172       foldl1 (fn ((concl1, bounds1), (concl2, bounds2)) =>
   173         (HOLogic.mk_disj (perhaps (try HOLogic.dest_Trueprop) concl1,
   174          perhaps (try HOLogic.dest_Trueprop) concl2), bounds1 @ bounds2)) l
   175     fun to_node (((((id, rule), prems), concl), bounds), cx) =
   176       (mk_node id rule (the_default [] prems) concl bounds, cx)
   177   in
   178     get_id
   179     ##> parse_rule
   180     #> rotate_pair
   181     ##> parse_source
   182     #> rotate_pair
   183     ##> skip_args
   184     #> (fn (((id, rule), prems), sub) => (((id, rule), prems), parse_subproof cx id sub))
   185     #> rotate_pair
   186     ##> parse_conclusion
   187     ##> map repair_quantification
   188     #> (fn ((((id, rule), prems), (subproof, cx)), terms) =>
   189          (((((id, rule), prems), subproof), fold_map (fn t => fn cx => node_of t cx) terms cx)))
   190     ##> apfst (fn [] => (@{const False}, []) | concls => make_or_from_clausification concls)
   191     #> fix_subproof_steps
   192     ##> to_node
   193     #> (fn (subproof, (step, cx)) => (subproof @ [step], cx))
   194     #-> fold_map update_step_and_cx
   195   end
   196 
   197 (*subproofs are written on multiple lines: SMTLIB can not parse then, because parentheses are
   198 unbalanced on each line*)
   199 fun seperate_into_steps lines =
   200   let
   201     fun count ("(" :: l) n = count l (n + 1)
   202       | count (")" :: l) n = count l (n - 1)
   203       | count (_ :: l) n = count l n
   204       | count [] n = n
   205     fun seperate (line :: l) actual_lines m =
   206         let val n = count (raw_explode line) 0 in
   207           if m + n = 0 then
   208             [actual_lines ^ line] :: seperate l "" 0
   209           else
   210             seperate l (actual_lines ^ line) (m + n)
   211         end
   212       | seperate [] _ 0 = []
   213   in
   214     seperate lines "" 0
   215   end
   216 
   217 (* VeriT adds "@" before every variable. *)
   218 fun remove_all_at (SMTLIB.Sym v :: l) =
   219     SMTLIB.Sym (perhaps (try (unprefix "@")) v) :: remove_all_at l
   220   | remove_all_at (SMTLIB.S l :: l') = SMTLIB.S (remove_all_at l) :: remove_all_at l'
   221   | remove_all_at (SMTLIB.Key v :: l) = SMTLIB.Key v :: remove_all_at l
   222   | remove_all_at (v :: l) = v :: remove_all_at l
   223   | remove_all_at [] = []
   224 
   225 fun find_in_which_step_defined var (VeriT_Node {id, bounds, ...} :: l) =
   226     (case List.find (fn v => String.isPrefix v var) bounds of
   227       NONE => find_in_which_step_defined var l
   228     | SOME _ => id)
   229   | find_in_which_step_defined var _ = raise Fail ("undefined " ^ var)
   230 
   231 (*Yes every case is possible: the introduced var is not on a special size of the equality sign.*)
   232 fun find_ite_var_in_term (Const (@{const_name If}, _) $ _ $
   233       (Const (@{const_name HOL.eq}, _) $ Free (var1, _) $ Free (var2, _) ) $
   234       (Const (@{const_name HOL.eq}, _) $ Free (var3, _) $ Free (var4, _) )) =
   235     let
   236       fun get_number_of_ite_transformed_var var =
   237         perhaps (try (unprefix "ite")) var
   238         |> Int.fromString
   239       fun is_equal_and_has_correct_substring var var' var'' =
   240         if var = var' andalso String.isPrefix "ite" var then SOME var'
   241         else if var = var'' andalso String.isPrefix "ite" var then SOME var'' else NONE
   242       val var1_introduced_var = is_equal_and_has_correct_substring var1 var3 var4
   243       val var2_introduced_var = is_equal_and_has_correct_substring var3 var1 var2
   244     in
   245       (case (var1_introduced_var, var2_introduced_var) of
   246         (SOME a, SOME b) =>
   247           (*ill-generated case, might be possible when applying the rule to max a a. Only if the
   248           variable have been introduced before. Probably an impossible edge case*)
   249           (case (get_number_of_ite_transformed_var a, get_number_of_ite_transformed_var b) of
   250             (SOME a, SOME b) => if a < b then var2_introduced_var else var1_introduced_var
   251             (*Otherwise, it is a name clase between a parameter name and the introduced variable.
   252              Or the name convention has been changed.*)
   253           | (NONE, SOME _) => var2_introduced_var
   254           | (SOME _, NONE) => var2_introduced_var)
   255       | (_, SOME _) => var2_introduced_var
   256       | (SOME _, _) => var1_introduced_var)
   257     end
   258   | find_ite_var_in_term (Const (@{const_name If}, _) $ _ $
   259       (Const (@{const_name HOL.eq}, _) $ Free (var, _) $ _ ) $
   260       (Const (@{const_name HOL.eq}, _) $ Free (var', _) $ _ )) =
   261     if var = var' then SOME var else NONE
   262   | find_ite_var_in_term (Const (@{const_name If}, _) $ _ $
   263       (Const (@{const_name HOL.eq}, _) $ _ $ Free (var, _)) $
   264       (Const (@{const_name HOL.eq}, _) $ _ $ Free (var', _))) =
   265     if var = var' then SOME var else NONE
   266   | find_ite_var_in_term (p $ q) =
   267     (case find_ite_var_in_term p of
   268       NONE => find_ite_var_in_term q
   269     | x => x)
   270   | find_ite_var_in_term (Abs (_, _, body)) = find_ite_var_in_term body
   271   | find_ite_var_in_term _ = NONE
   272 
   273 fun correct_veriT_step steps (node as VeriT_Node {id, rule, prems, concl, bounds}) =
   274   if rule = veriT_tmp_ite_elim_rule then
   275     if bounds = [] then
   276       (*if the introduced var has already been defined, adding the definition as a dependency*)
   277       let
   278         val new_prems = prems
   279           |> (case find_ite_var_in_term concl of
   280                NONE => I
   281              | SOME var => cons (find_in_which_step_defined var steps))
   282       in
   283         VeriT_Node {id = id, rule = rule, prems = new_prems, concl = concl, bounds = bounds}
   284       end
   285     else
   286       (*some new variables are created*)
   287       let val concl' = replace_bound_var_by_free_var concl bounds in
   288         mk_node id rule prems concl' []
   289       end
   290   else
   291     node
   292 
   293 fun remove_alpha_conversion _ [] = []
   294   | remove_alpha_conversion replace_table (VeriT_Node {id, rule, prems, concl, bounds} :: steps) =
   295     let
   296       val correct_dependency = map (perhaps (Symtab.lookup replace_table))
   297       val find_predecessor = perhaps (Symtab.lookup replace_table)
   298     in
   299       if rule = veriT_tmp_alphaconv_rule then
   300         remove_alpha_conversion (Symtab.update (id, find_predecessor (hd prems))
   301           replace_table) steps
   302       else
   303         VeriT_Node {id = id, rule = rule, prems = correct_dependency prems,
   304           concl = concl, bounds = bounds} :: remove_alpha_conversion replace_table steps
   305     end
   306 
   307 fun correct_veriT_steps steps =
   308   steps
   309   |> map (correct_veriT_step steps)
   310   |> remove_alpha_conversion Symtab.empty
   311 
   312 fun parse typs funs lines ctxt =
   313   let
   314     val smtlib_lines_without_at = remove_all_at (map SMTLIB.parse (seperate_into_steps lines))
   315     val (u, env) = apfst flat (fold_map (fn l => fn cx => parse_proof_step cx l)
   316       smtlib_lines_without_at (empty_context ctxt typs funs))
   317     val t = correct_veriT_steps u
   318     fun node_to_step (VeriT_Node {id, rule, prems, concl, bounds, ...}) =
   319       mk_step id rule prems concl bounds
   320   in
   321     (map node_to_step t, ctxt_of env)
   322   end
   323 
   324 end;