src/HOL/Tools/SMT2/z3_new_proof.ML
author blanchet
Wed Jun 11 19:15:54 2014 +0200 (2014-06-11)
changeset 57219 34018603e0d0
parent 56811 b66639331db5
child 57220 853557cf2efa
permissions -rw-r--r--
factor out SMT-LIB 2 type/term parsing from Z3-specific code
blanchet@56078
     1
(*  Title:      HOL/Tools/SMT2/z3_new_proof.ML
blanchet@56078
     2
    Author:     Sascha Boehme, TU Muenchen
blanchet@56078
     3
blanchet@56078
     4
Z3 proofs: parsing and abstract syntax tree.
blanchet@56078
     5
*)
blanchet@56078
     6
blanchet@56078
     7
signature Z3_NEW_PROOF =
blanchet@56078
     8
sig
blanchet@56078
     9
  (*proof rules*)
blanchet@56078
    10
  datatype z3_rule = True_Axiom | Asserted | Goal | Modus_Ponens | Reflexivity |
blanchet@56078
    11
    Symmetry | Transitivity | Transitivity_Star | Monotonicity | Quant_Intro |
blanchet@56078
    12
    Distributivity | And_Elim | Not_Or_Elim | Rewrite | Rewrite_Star |
blanchet@56078
    13
    Pull_Quant | Pull_Quant_Star | Push_Quant | Elim_Unused_Vars |
blanchet@56078
    14
    Dest_Eq_Res | Quant_Inst | Hypothesis | Lemma | Unit_Resolution |
blanchet@56078
    15
    Iff_True | Iff_False | Commutativity | Def_Axiom | Intro_Def | Apply_Def |
blanchet@56078
    16
    Iff_Oeq | Nnf_Pos | Nnf_Neg | Nnf_Star | Cnf_Star | Skolemize |
blanchet@56078
    17
    Modus_Ponens_Oeq | Th_Lemma of string
blanchet@56078
    18
  val string_of_rule: z3_rule -> string
blanchet@56078
    19
blanchet@56078
    20
  (*proofs*)
blanchet@56078
    21
  datatype z3_step = Z3_Step of {
blanchet@56078
    22
    id: int,
blanchet@56078
    23
    rule: z3_rule,
blanchet@56078
    24
    prems: int list,
blanchet@56078
    25
    concl: term,
blanchet@56078
    26
    fixes: string list,
blanchet@56078
    27
    is_fix_step: bool}
blanchet@56078
    28
blanchet@56078
    29
  (*proof parser*)
blanchet@56078
    30
  val parse: typ Symtab.table -> term Symtab.table -> string list ->
blanchet@56078
    31
    Proof.context -> z3_step list * Proof.context
blanchet@57219
    32
end;
blanchet@56078
    33
blanchet@56078
    34
structure Z3_New_Proof: Z3_NEW_PROOF =
blanchet@56078
    35
struct
blanchet@56078
    36
blanchet@57219
    37
open SMTLIB2_Proof
blanchet@57219
    38
blanchet@57219
    39
blanchet@57219
    40
(* proof parser context *)
blanchet@57219
    41
blanchet@57219
    42
fun next_id ({ctxt, id, syms, typs, funs, extra}: ('a, 'b) context) =
blanchet@57219
    43
  (id, mk_context ctxt (id + 1) syms typs funs extra)
blanchet@57219
    44
blanchet@57219
    45
fun with_fresh_names f ({ctxt, id, syms, typs, funs, extra}: ('a, 'b) context) =
blanchet@57219
    46
  let
blanchet@57219
    47
    fun bind (_, v as (_, T)) t = Logic.all_const T $ Term.absfree v t
blanchet@57219
    48
blanchet@57219
    49
    val needs_inferT = equal Term.dummyT orf Term.is_TVar
blanchet@57219
    50
    val needs_infer = Term.exists_type (Term.exists_subtype needs_inferT)
blanchet@57219
    51
    fun infer_types ctxt =
blanchet@57219
    52
      singleton (Type_Infer_Context.infer_types ctxt) #>
blanchet@57219
    53
      singleton (Proof_Context.standard_term_check_finish ctxt)
blanchet@57219
    54
    fun infer ctxt t = if needs_infer t then infer_types ctxt t else t
blanchet@57219
    55
blanchet@57219
    56
    type bindings = (string * (string * typ)) list
blanchet@57219
    57
    val (t, {ctxt=ctxt', extra=names, ...}: (bindings, 'b) context) =
blanchet@57219
    58
      f (mk_context ctxt id syms typs funs [])
blanchet@57219
    59
    val t' = infer ctxt' (fold_rev bind names (HOLogic.mk_Trueprop t))
blanchet@57219
    60
blanchet@57219
    61
  in ((t', map fst names), mk_context ctxt id syms typs funs extra) end
blanchet@57219
    62
blanchet@57219
    63
blanchet@56078
    64
(* proof rules *)
blanchet@56078
    65
blanchet@56078
    66
datatype z3_rule = True_Axiom | Asserted | Goal | Modus_Ponens | Reflexivity |
blanchet@56078
    67
  Symmetry | Transitivity | Transitivity_Star | Monotonicity | Quant_Intro |
blanchet@56078
    68
  Distributivity | And_Elim | Not_Or_Elim | Rewrite | Rewrite_Star |
blanchet@56078
    69
  Pull_Quant | Pull_Quant_Star | Push_Quant | Elim_Unused_Vars | Dest_Eq_Res |
blanchet@56078
    70
  Quant_Inst | Hypothesis | Lemma | Unit_Resolution | Iff_True | Iff_False |
blanchet@56078
    71
  Commutativity | Def_Axiom | Intro_Def | Apply_Def | Iff_Oeq | Nnf_Pos |
blanchet@56078
    72
  Nnf_Neg | Nnf_Star | Cnf_Star | Skolemize | Modus_Ponens_Oeq |
blanchet@56078
    73
  Th_Lemma of string
blanchet@56078
    74
  (* TODO: some proof rules come with further information
blanchet@56078
    75
     that is currently dropped by the parser *)
blanchet@56078
    76
blanchet@56078
    77
val rule_names = Symtab.make [
blanchet@56078
    78
  ("true-axiom", True_Axiom),
blanchet@56078
    79
  ("asserted", Asserted),
blanchet@56078
    80
  ("goal", Goal),
blanchet@56078
    81
  ("mp", Modus_Ponens),
blanchet@56078
    82
  ("refl", Reflexivity),
blanchet@56078
    83
  ("symm", Symmetry),
blanchet@56078
    84
  ("trans", Transitivity),
blanchet@56078
    85
  ("trans*", Transitivity_Star),
blanchet@56078
    86
  ("monotonicity", Monotonicity),
blanchet@56078
    87
  ("quant-intro", Quant_Intro),
blanchet@56078
    88
  ("distributivity", Distributivity),
blanchet@56078
    89
  ("and-elim", And_Elim),
blanchet@56078
    90
  ("not-or-elim", Not_Or_Elim),
blanchet@56078
    91
  ("rewrite", Rewrite),
blanchet@56078
    92
  ("rewrite*", Rewrite_Star),
blanchet@56078
    93
  ("pull-quant", Pull_Quant),
blanchet@56078
    94
  ("pull-quant*", Pull_Quant_Star),
blanchet@56078
    95
  ("push-quant", Push_Quant),
blanchet@56078
    96
  ("elim-unused", Elim_Unused_Vars),
blanchet@56078
    97
  ("der", Dest_Eq_Res),
blanchet@56078
    98
  ("quant-inst", Quant_Inst),
blanchet@56078
    99
  ("hypothesis", Hypothesis),
blanchet@56078
   100
  ("lemma", Lemma),
blanchet@56078
   101
  ("unit-resolution", Unit_Resolution),
blanchet@56078
   102
  ("iff-true", Iff_True),
blanchet@56078
   103
  ("iff-false", Iff_False),
blanchet@56078
   104
  ("commutativity", Commutativity),
blanchet@56078
   105
  ("def-axiom", Def_Axiom),
blanchet@56078
   106
  ("intro-def", Intro_Def),
blanchet@56078
   107
  ("apply-def", Apply_Def),
blanchet@56078
   108
  ("iff~", Iff_Oeq),
blanchet@56078
   109
  ("nnf-pos", Nnf_Pos),
blanchet@56078
   110
  ("nnf-neg", Nnf_Neg),
blanchet@56078
   111
  ("nnf*", Nnf_Star),
blanchet@56078
   112
  ("cnf*", Cnf_Star),
blanchet@56078
   113
  ("sk", Skolemize),
blanchet@56078
   114
  ("mp~", Modus_Ponens_Oeq)]
blanchet@56078
   115
blanchet@56078
   116
fun rule_of_string name =
blanchet@56078
   117
  (case Symtab.lookup rule_names name of
blanchet@56078
   118
    SOME rule => rule
blanchet@56078
   119
  | NONE => error ("unknown Z3 proof rule " ^ quote name))
blanchet@56078
   120
blanchet@56078
   121
fun string_of_rule (Th_Lemma kind) = "th-lemma " ^ kind
blanchet@56078
   122
  | string_of_rule r =
blanchet@57219
   123
      let fun eq_rule (s, r') = if r = r' then SOME s else NONE
blanchet@56078
   124
      in the (Symtab.get_first eq_rule rule_names) end
blanchet@56078
   125
blanchet@56078
   126
blanchet@56078
   127
(* proofs *)
blanchet@56078
   128
blanchet@56078
   129
datatype z3_node = Z3_Node of {
blanchet@56078
   130
  id: int,
blanchet@56078
   131
  rule: z3_rule,
blanchet@56078
   132
  prems: z3_node list,
blanchet@56078
   133
  concl: term,
blanchet@56078
   134
  bounds: string list}
blanchet@56078
   135
blanchet@56078
   136
fun mk_node id rule prems concl bounds =
blanchet@56078
   137
  Z3_Node {id=id, rule=rule, prems=prems, concl=concl, bounds=bounds}
blanchet@56078
   138
blanchet@56078
   139
datatype z3_step = Z3_Step of {
blanchet@56078
   140
  id: int,
blanchet@56078
   141
  rule: z3_rule,
blanchet@56078
   142
  prems: int list,
blanchet@56078
   143
  concl: term,
blanchet@56078
   144
  fixes: string list,
blanchet@56078
   145
  is_fix_step: bool}
blanchet@56078
   146
blanchet@56078
   147
fun mk_step id rule prems concl fixes is_fix_step =
blanchet@56078
   148
  Z3_Step {id=id, rule=rule, prems=prems, concl=concl, fixes=fixes,
blanchet@56078
   149
    is_fix_step=is_fix_step}
blanchet@56078
   150
blanchet@56078
   151
blanchet@56078
   152
(* proof parser *)
blanchet@56078
   153
blanchet@56078
   154
fun rule_of (SMTLIB2.Sym name) = rule_of_string name
blanchet@56078
   155
  | rule_of (SMTLIB2.S (SMTLIB2.Sym "_" :: SMTLIB2.Sym name :: args)) =
blanchet@56078
   156
      (case (name, args) of
blanchet@56078
   157
        ("th-lemma", SMTLIB2.Sym kind :: _) => Th_Lemma kind
blanchet@56078
   158
      | _ => rule_of_string name)
blanchet@57219
   159
  | rule_of r = raise SMTLIB2_PARSE ("bad Z3 proof rule format", r)
blanchet@56078
   160
blanchet@56078
   161
fun node_of p cx =
blanchet@56078
   162
  (case p of
blanchet@56078
   163
    SMTLIB2.Sym name =>
blanchet@56078
   164
      (case lookup_binding cx name of
blanchet@56078
   165
        Proof node => (node, cx)
blanchet@56078
   166
      | Tree p' =>
blanchet@56078
   167
          cx
blanchet@56078
   168
          |> node_of p'
blanchet@56078
   169
          |-> (fn node => pair node o update_binding (name, Proof node))
blanchet@57219
   170
      | _ => raise SMTLIB2_PARSE ("bad Z3 proof format", p))
blanchet@56078
   171
  | SMTLIB2.S [SMTLIB2.Sym "let", SMTLIB2.S bindings, p] =>
blanchet@56078
   172
      with_bindings (map dest_binding bindings) (node_of p) cx
blanchet@56078
   173
  | SMTLIB2.S (name :: parts) =>
blanchet@56078
   174
      let
blanchet@56078
   175
        val (ps, p) = split_last parts
blanchet@56078
   176
        val r = rule_of name
blanchet@56078
   177
      in
blanchet@56078
   178
        cx
blanchet@56078
   179
        |> fold_map node_of ps
blanchet@56078
   180
        ||>> with_fresh_names (term_of p)
blanchet@56078
   181
        ||>> next_id
blanchet@56078
   182
        |>> (fn ((prems, (t, ns)), id) => mk_node id r prems t ns)
blanchet@56078
   183
      end
blanchet@57219
   184
  | _ => raise SMTLIB2_PARSE ("bad Z3 proof format", p))
blanchet@56078
   185
blanchet@56078
   186
fun dest_name (SMTLIB2.Sym name) = name
blanchet@57219
   187
  | dest_name t = raise SMTLIB2_PARSE ("bad name", t)
blanchet@56078
   188
blanchet@56078
   189
fun dest_seq (SMTLIB2.S ts) = ts
blanchet@57219
   190
  | dest_seq t = raise SMTLIB2_PARSE ("bad Z3 proof format", t)
blanchet@56078
   191
blanchet@56078
   192
fun parse' (SMTLIB2.S (SMTLIB2.Sym "set-logic" :: _) :: ts) cx = parse' ts cx
blanchet@56078
   193
  | parse' (SMTLIB2.S [SMTLIB2.Sym "declare-fun", n, tys, ty] :: ts) cx =
blanchet@56078
   194
      let
blanchet@56078
   195
        val name = dest_name n
blanchet@56078
   196
        val Ts = map (type_of cx) (dest_seq tys)
blanchet@56078
   197
        val T = type_of cx ty
blanchet@57219
   198
      in parse' ts (declare_fun name (Ts ---> T) cx) end
blanchet@56078
   199
  | parse' (SMTLIB2.S [SMTLIB2.Sym "proof", p] :: _) cx = node_of p cx
blanchet@57219
   200
  | parse' ts _ = raise SMTLIB2_PARSE ("bad Z3 proof declarations", SMTLIB2.S ts)
blanchet@56078
   201
blanchet@56078
   202
fun parse_proof typs funs lines ctxt =
blanchet@56078
   203
  let
blanchet@56078
   204
    val ts = dest_seq (SMTLIB2.parse lines)
blanchet@56078
   205
    val (node, cx) = parse' ts (empty_context ctxt typs funs)
blanchet@56078
   206
  in (node, ctxt_of cx) end
blanchet@56078
   207
  handle SMTLIB2.PARSE (l, msg) =>
blanchet@56078
   208
           error ("parsing error at line " ^ string_of_int l ^ ": " ^ msg)
blanchet@57219
   209
       | SMTLIB2_PARSE (msg, t) =>
blanchet@56078
   210
           error (msg ^ ": " ^ SMTLIB2.str_of t)
blanchet@56078
   211
blanchet@56078
   212
blanchet@56078
   213
blanchet@56078
   214
(* handling of bound variables *)
blanchet@56078
   215
blanchet@56078
   216
fun subst_of tyenv =
blanchet@56078
   217
  let fun add (ix, (S, T)) = cons (TVar (ix, S), T)
blanchet@56078
   218
  in Vartab.fold add tyenv [] end
blanchet@56078
   219
blanchet@57219
   220
fun substTs_same subst =
blanchet@56078
   221
  let val applyT = Same.function (AList.lookup (op =) subst)
blanchet@56078
   222
  in Term_Subst.map_atypsT_same applyT end
blanchet@56078
   223
blanchet@56078
   224
fun subst_types ctxt env bounds t =
blanchet@56078
   225
  let
blanchet@56078
   226
    val match = Sign.typ_match (Proof_Context.theory_of ctxt)
blanchet@56078
   227
blanchet@56078
   228
    val t' = singleton (Variable.polymorphic ctxt) t
wenzelm@56245
   229
    val patTs = map snd (Term.strip_qnt_vars @{const_name Pure.all} t')
blanchet@56078
   230
    val objTs = map (the o Symtab.lookup env) bounds
blanchet@56078
   231
    val subst = subst_of (fold match (patTs ~~ objTs) Vartab.empty)
blanchet@56078
   232
  in Same.commit (Term_Subst.map_types_same (substTs_same subst)) t' end
blanchet@56078
   233
blanchet@56078
   234
fun eq_quant (@{const_name HOL.All}, _) (@{const_name HOL.All}, _) = true
blanchet@56078
   235
  | eq_quant (@{const_name HOL.Ex}, _) (@{const_name HOL.Ex}, _) = true
blanchet@56078
   236
  | eq_quant _ _ = false
blanchet@56078
   237
blanchet@56078
   238
fun opp_quant (@{const_name HOL.All}, _) (@{const_name HOL.Ex}, _) = true
blanchet@56078
   239
  | opp_quant (@{const_name HOL.Ex}, _) (@{const_name HOL.All}, _) = true
blanchet@56078
   240
  | opp_quant _ _ = false
blanchet@56078
   241
blanchet@56078
   242
fun with_quant pred i (Const q1 $ Abs (_, T1, t1), Const q2 $ Abs (_, T2, t2)) =
blanchet@56078
   243
      if pred q1 q2 andalso T1 = T2 then
blanchet@56078
   244
        let val t = Var (("", i), T1)
blanchet@56078
   245
        in SOME (pairself Term.subst_bound ((t, t1), (t, t2))) end
blanchet@56078
   246
      else NONE
blanchet@56078
   247
  | with_quant _ _ _ = NONE
blanchet@56078
   248
blanchet@56078
   249
fun dest_quant_pair i (@{term HOL.Not} $ t1, t2) =
blanchet@56078
   250
      Option.map (apfst HOLogic.mk_not) (with_quant opp_quant i (t1, t2))
blanchet@56078
   251
  | dest_quant_pair i (t1, t2) = with_quant eq_quant i (t1, t2)
blanchet@56078
   252
blanchet@56078
   253
fun dest_quant i t =
blanchet@56078
   254
  (case dest_quant_pair i (HOLogic.dest_eq (HOLogic.dest_Trueprop t)) of
blanchet@56078
   255
    SOME (t1, t2) => HOLogic.mk_Trueprop (HOLogic.mk_eq (t1, t2))
blanchet@56078
   256
  | NONE => raise TERM ("lift_quant", [t]))
blanchet@56078
   257
blanchet@56078
   258
fun match_types ctxt pat obj =
blanchet@56078
   259
  (Vartab.empty, Vartab.empty)
blanchet@56078
   260
  |> Pattern.first_order_match (Proof_Context.theory_of ctxt) (pat, obj)
blanchet@56078
   261
blanchet@56078
   262
fun strip_match ctxt pat i obj =
blanchet@56078
   263
  (case try (match_types ctxt pat) obj of
blanchet@56078
   264
    SOME (tyenv, _) => subst_of tyenv
blanchet@56078
   265
  | NONE => strip_match ctxt pat (i + 1) (dest_quant i obj))
blanchet@56078
   266
wenzelm@56245
   267
fun dest_all i (Const (@{const_name Pure.all}, _) $ (a as Abs (_, T, _))) =
blanchet@56078
   268
      dest_all (i + 1) (Term.betapply (a, Var (("", i), T)))
blanchet@56078
   269
  | dest_all i t = (i, t)
blanchet@56078
   270
blanchet@56078
   271
fun dest_alls t = dest_all (Term.maxidx_of_term t + 1) t
blanchet@56078
   272
blanchet@56078
   273
fun match_rule ctxt env (Z3_Node {bounds=bs', concl=t', ...}) bs t =
blanchet@56078
   274
  let
blanchet@56078
   275
    val t'' = singleton (Variable.polymorphic ctxt) t'
blanchet@56078
   276
    val (i, obj) = dest_alls (subst_types ctxt env bs t)
blanchet@56078
   277
  in
blanchet@56078
   278
    (case try (strip_match ctxt (snd (dest_alls t'')) i) obj of
blanchet@56078
   279
      NONE => NONE
blanchet@56078
   280
    | SOME subst =>
blanchet@56078
   281
        let
blanchet@56078
   282
          val applyT = Same.commit (substTs_same subst)
wenzelm@56245
   283
          val patTs = map snd (Term.strip_qnt_vars @{const_name Pure.all} t'')
blanchet@56078
   284
        in SOME (Symtab.make (bs' ~~ map applyT patTs)) end)
blanchet@56078
   285
  end
blanchet@56078
   286
blanchet@56078
   287
blanchet@56078
   288
blanchet@56078
   289
(* linearizing proofs and resolving types of bound variables *)
blanchet@56078
   290
blanchet@56078
   291
fun has_step (tab, _) = Inttab.defined tab
blanchet@56078
   292
blanchet@56078
   293
fun add_step id rule bounds concl is_fix_step ids (tab, sts) =
blanchet@56078
   294
  let val step = mk_step id rule ids concl bounds is_fix_step
blanchet@56078
   295
  in (id, (Inttab.update (id, ()) tab, step :: sts)) end
blanchet@56078
   296
blanchet@56078
   297
fun is_fix_rule rule prems =
blanchet@56078
   298
  member (op =) [Quant_Intro, Nnf_Pos, Nnf_Neg] rule andalso length prems = 1
blanchet@56078
   299
blanchet@56078
   300
fun lin_proof ctxt env (Z3_Node {id, rule, prems, concl, bounds}) steps =
blanchet@56078
   301
  if has_step steps id then (id, steps)
blanchet@56078
   302
  else
blanchet@56078
   303
    let
blanchet@56078
   304
      val t = subst_types ctxt env bounds concl
blanchet@56078
   305
      val add = add_step id rule bounds t
blanchet@56078
   306
      fun rec_apply e b = fold_map (lin_proof ctxt e) prems #-> add b
blanchet@56078
   307
    in
blanchet@56078
   308
      if is_fix_rule rule prems then
blanchet@56078
   309
        (case match_rule ctxt env (hd prems) bounds t of
blanchet@56078
   310
          NONE => rec_apply env false steps
blanchet@56078
   311
        | SOME env' => rec_apply env' true steps)
blanchet@56078
   312
      else rec_apply env false steps
blanchet@56078
   313
    end
blanchet@56078
   314
blanchet@56078
   315
fun linearize ctxt node =
blanchet@56078
   316
  rev (snd (snd (lin_proof ctxt Symtab.empty node (Inttab.empty, []))))
blanchet@56078
   317
blanchet@56078
   318
blanchet@56078
   319
blanchet@56078
   320
(* overall proof parser *)
blanchet@56078
   321
blanchet@56078
   322
fun parse typs funs lines ctxt =
blanchet@56078
   323
  let val (node, ctxt') = parse_proof typs funs lines ctxt
blanchet@56078
   324
  in (linearize ctxt' node, ctxt') end
blanchet@56078
   325
blanchet@57219
   326
end;