src/HOL/Tools/SMT/smtlib_interface.ML
author boehmes
Wed May 12 23:54:02 2010 +0200 (2010-05-12)
changeset 36898 8e55aa1306c5
child 36899 bcd6fce5bf06
permissions -rw-r--r--
integrated SMT into the HOL image
boehmes@36898
     1
(*  Title:      HOL/Tools/SMT/smtlib_interface.ML
boehmes@36898
     2
    Author:     Sascha Boehme, TU Muenchen
boehmes@36898
     3
boehmes@36898
     4
Interface to SMT solvers based on the SMT-LIB format.
boehmes@36898
     5
*)
boehmes@36898
     6
boehmes@36898
     7
signature SMTLIB_INTERFACE =
boehmes@36898
     8
sig
boehmes@36898
     9
  val interface: SMT_Solver.interface
boehmes@36898
    10
end
boehmes@36898
    11
boehmes@36898
    12
structure SMTLIB_Interface: SMTLIB_INTERFACE =
boehmes@36898
    13
struct
boehmes@36898
    14
boehmes@36898
    15
structure N = SMT_Normalize
boehmes@36898
    16
structure T = SMT_Translate
boehmes@36898
    17
boehmes@36898
    18
boehmes@36898
    19
boehmes@36898
    20
(** facts about uninterpreted constants **)
boehmes@36898
    21
boehmes@36898
    22
infix 2 ??
boehmes@36898
    23
fun (ex ?? f) thms = if exists (ex o Thm.prop_of) thms then f thms else thms
boehmes@36898
    24
boehmes@36898
    25
boehmes@36898
    26
(* pairs *)
boehmes@36898
    27
boehmes@36898
    28
val pair_rules = [@{thm fst_conv}, @{thm snd_conv}, @{thm pair_collapse}]
boehmes@36898
    29
boehmes@36898
    30
val pair_type = (fn Type (@{type_name "*"}, _) => true | _ => false)
boehmes@36898
    31
val exists_pair_type = Term.exists_type (Term.exists_subtype pair_type)
boehmes@36898
    32
boehmes@36898
    33
val add_pair_rules = exists_pair_type ?? append pair_rules
boehmes@36898
    34
boehmes@36898
    35
boehmes@36898
    36
(* function update *)
boehmes@36898
    37
boehmes@36898
    38
val fun_upd_rules = [@{thm fun_upd_same}, @{thm fun_upd_apply}]
boehmes@36898
    39
boehmes@36898
    40
val is_fun_upd = (fn Const (@{const_name fun_upd}, _) => true | _ => false)
boehmes@36898
    41
val exists_fun_upd = Term.exists_subterm is_fun_upd
boehmes@36898
    42
boehmes@36898
    43
val add_fun_upd_rules = exists_fun_upd ?? append fun_upd_rules
boehmes@36898
    44
boehmes@36898
    45
boehmes@36898
    46
(* abs/min/max *)
boehmes@36898
    47
boehmes@36898
    48
val exists_abs_min_max = Term.exists_subterm (fn
boehmes@36898
    49
    Const (@{const_name abs}, _) => true
boehmes@36898
    50
  | Const (@{const_name min}, _) => true
boehmes@36898
    51
  | Const (@{const_name max}, _) => true
boehmes@36898
    52
  | _ => false)
boehmes@36898
    53
boehmes@36898
    54
val unfold_abs_conv = Conv.rewr_conv @{thm abs_if[THEN eq_reflection]}
boehmes@36898
    55
val unfold_min_conv = Conv.rewr_conv @{thm min_def[THEN eq_reflection]}
boehmes@36898
    56
val unfold_max_conv = Conv.rewr_conv @{thm max_def[THEN eq_reflection]}
boehmes@36898
    57
boehmes@36898
    58
fun expand_conv cv = N.eta_expand_conv (K cv)
boehmes@36898
    59
fun expand2_conv cv = N.eta_expand_conv (N.eta_expand_conv (K cv))
boehmes@36898
    60
boehmes@36898
    61
fun unfold_def_conv ctxt ct =
boehmes@36898
    62
  (case Thm.term_of ct of
boehmes@36898
    63
    Const (@{const_name abs}, _) $ _ => unfold_abs_conv
boehmes@36898
    64
  | Const (@{const_name abs}, _) => expand_conv unfold_abs_conv ctxt
boehmes@36898
    65
  | Const (@{const_name min}, _) $ _ $ _ => unfold_min_conv
boehmes@36898
    66
  | Const (@{const_name min}, _) $ _ => expand_conv unfold_min_conv ctxt
boehmes@36898
    67
  | Const (@{const_name min}, _) => expand2_conv unfold_min_conv ctxt
boehmes@36898
    68
  | Const (@{const_name max}, _) $ _ $ _ => unfold_max_conv
boehmes@36898
    69
  | Const (@{const_name max}, _) $ _ => expand_conv unfold_max_conv ctxt
boehmes@36898
    70
  | Const (@{const_name max}, _) => expand2_conv unfold_max_conv ctxt
boehmes@36898
    71
  | _ => Conv.all_conv) ct
boehmes@36898
    72
boehmes@36898
    73
fun unfold_abs_min_max_defs ctxt thm =
boehmes@36898
    74
  if exists_abs_min_max (Thm.prop_of thm)
boehmes@36898
    75
  then Conv.fconv_rule (More_Conv.top_conv unfold_def_conv ctxt) thm
boehmes@36898
    76
  else thm
boehmes@36898
    77
boehmes@36898
    78
boehmes@36898
    79
(* include additional facts *)
boehmes@36898
    80
boehmes@36898
    81
fun extra_norm thms ctxt =
boehmes@36898
    82
  thms
boehmes@36898
    83
  |> add_pair_rules
boehmes@36898
    84
  |> add_fun_upd_rules
boehmes@36898
    85
  |> map (unfold_abs_min_max_defs ctxt)
boehmes@36898
    86
  |> rpair ctxt
boehmes@36898
    87
boehmes@36898
    88
boehmes@36898
    89
boehmes@36898
    90
(** builtins **)
boehmes@36898
    91
boehmes@36898
    92
fun dest_binT T =
boehmes@36898
    93
  (case T of
boehmes@36898
    94
    Type (@{type_name "Numeral_Type.num0"}, _) => 0
boehmes@36898
    95
  | Type (@{type_name "Numeral_Type.num1"}, _) => 1
boehmes@36898
    96
  | Type (@{type_name "Numeral_Type.bit0"}, [T]) => 2 * dest_binT T
boehmes@36898
    97
  | Type (@{type_name "Numeral_Type.bit1"}, [T]) => 1 + 2 * dest_binT T
boehmes@36898
    98
  | _ => raise TYPE ("dest_binT", [T], []))
boehmes@36898
    99
boehmes@36898
   100
fun dest_wordT (Type (@{type_name word}, [T])) = dest_binT T
boehmes@36898
   101
  | dest_wordT T = raise TYPE ("dest_wordT", [T], [])
boehmes@36898
   102
boehmes@36898
   103
fun index1 n i = n ^ "[" ^ string_of_int i ^ "]"
boehmes@36898
   104
fun index2 n i j = n ^ "[" ^ string_of_int i ^ ":" ^ string_of_int j ^ "]"
boehmes@36898
   105
boehmes@36898
   106
fun builtin_typ @{typ int} = SOME "Int"
boehmes@36898
   107
  | builtin_typ @{typ real} = SOME "Real"
boehmes@36898
   108
  | builtin_typ (Type (@{type_name word}, [T])) =
boehmes@36898
   109
      Option.map (index1 "BitVec") (try dest_binT T)
boehmes@36898
   110
  | builtin_typ _ = NONE
boehmes@36898
   111
boehmes@36898
   112
fun builtin_num @{typ int} i = SOME (string_of_int i)
boehmes@36898
   113
  | builtin_num @{typ real} i = SOME (string_of_int i ^ ".0")
boehmes@36898
   114
  | builtin_num (Type (@{type_name word}, [T])) i =
boehmes@36898
   115
      Option.map (index1 ("bv" ^ string_of_int i)) (try dest_binT T)
boehmes@36898
   116
  | builtin_num _ _ = NONE
boehmes@36898
   117
boehmes@36898
   118
val is_propT = (fn @{typ prop} => true | _ => false)
boehmes@36898
   119
fun is_connT T = Term.strip_type T |> (fn (Us, U) => forall is_propT (U :: Us))
boehmes@36898
   120
fun is_predT T = is_propT (Term.body_type T)
boehmes@36898
   121
boehmes@36898
   122
fun just c ts = SOME (c, ts)
boehmes@36898
   123
boehmes@36898
   124
val is_arith_type = member (op =) [@{typ int}, @{typ real}] o Term.domain_type
boehmes@36898
   125
boehmes@36898
   126
fun fixed_bvT (Ts, T) x =
boehmes@36898
   127
  if forall (can dest_wordT) (T :: Ts) then SOME x else NONE
boehmes@36898
   128
boehmes@36898
   129
fun if_fixed_bvT' T = fixed_bvT ([], Term.domain_type T)
boehmes@36898
   130
fun if_fixed_bvT T = curry (fixed_bvT ([], Term.domain_type T))
boehmes@36898
   131
fun if_full_fixed_bvT T = curry (fixed_bvT (Term.strip_type T))
boehmes@36898
   132
boehmes@36898
   133
fun dest_word_funT (Type ("fun", [T, U])) = (dest_wordT T, dest_wordT U)
boehmes@36898
   134
  | dest_word_funT T = raise TYPE ("dest_word_funT", [T], [])
boehmes@36898
   135
fun dest_nat (@{term nat} $ n :: ts) = (snd (HOLogic.dest_number n), ts)
boehmes@36898
   136
  | dest_nat ts = raise TERM ("dest_nat", ts)
boehmes@36898
   137
fun dest_nat_word_funT (T, ts) =
boehmes@36898
   138
  (dest_word_funT (Term.range_type T), dest_nat ts)
boehmes@36898
   139
boehmes@36898
   140
fun bv_extend n T ts =
boehmes@36898
   141
  (case try dest_word_funT T of
boehmes@36898
   142
    SOME (i, j) => if j-i >= 0 then SOME (index1 n (j-i), ts) else NONE
boehmes@36898
   143
  | _ => NONE)
boehmes@36898
   144
boehmes@36898
   145
fun bv_rotate n T ts =
boehmes@36898
   146
  try dest_nat ts
boehmes@36898
   147
  |> Option.map (fn (i, ts') => (index1 n i, ts'))
boehmes@36898
   148
boehmes@36898
   149
fun bv_extract n T ts =
boehmes@36898
   150
  try dest_nat_word_funT (T, ts)
boehmes@36898
   151
  |> Option.map (fn ((_, i), (lb, ts')) => (index2 n (i + lb - 1) lb, ts'))
boehmes@36898
   152
boehmes@36898
   153
boehmes@36898
   154
fun conn @{const_name True} = SOME "true"
boehmes@36898
   155
  | conn @{const_name False} = SOME "false"
boehmes@36898
   156
  | conn @{const_name Not} = SOME "not"
boehmes@36898
   157
  | conn @{const_name "op &"} = SOME "and"
boehmes@36898
   158
  | conn @{const_name "op |"} = SOME "or"
boehmes@36898
   159
  | conn @{const_name "op -->"} = SOME "implies"
boehmes@36898
   160
  | conn @{const_name "op ="} = SOME "iff"
boehmes@36898
   161
  | conn @{const_name If} = SOME "if_then_else"
boehmes@36898
   162
  | conn _ = NONE
boehmes@36898
   163
boehmes@36898
   164
fun pred @{const_name distinct} _ = SOME "distinct"
boehmes@36898
   165
  | pred @{const_name "op ="} _ = SOME "="
boehmes@36898
   166
  | pred @{const_name term_eq} _ = SOME "="
boehmes@36898
   167
  | pred @{const_name less} T =
boehmes@36898
   168
      if is_arith_type T then SOME "<"
boehmes@36898
   169
      else if_fixed_bvT' T "bvult"
boehmes@36898
   170
  | pred @{const_name less_eq} T =
boehmes@36898
   171
      if is_arith_type T then SOME "<="
boehmes@36898
   172
      else if_fixed_bvT' T "bvule"
boehmes@36898
   173
  | pred @{const_name word_sless} T = if_fixed_bvT' T "bvslt"
boehmes@36898
   174
  | pred @{const_name word_sle} T = if_fixed_bvT' T "bvsle"
boehmes@36898
   175
  | pred _ _ = NONE
boehmes@36898
   176
boehmes@36898
   177
fun func @{const_name If} _ = just "ite"
boehmes@36898
   178
  | func @{const_name uminus} T =
boehmes@36898
   179
      if is_arith_type T then just "~"
boehmes@36898
   180
      else if_fixed_bvT T "bvneg"
boehmes@36898
   181
  | func @{const_name plus} T = 
boehmes@36898
   182
      if is_arith_type T then just "+"
boehmes@36898
   183
      else if_fixed_bvT T "bvadd"
boehmes@36898
   184
  | func @{const_name minus} T =
boehmes@36898
   185
      if is_arith_type T then just "-"
boehmes@36898
   186
      else if_fixed_bvT T "bvsub"
boehmes@36898
   187
  | func @{const_name times} T = 
boehmes@36898
   188
      if is_arith_type T then just "*"
boehmes@36898
   189
      else if_fixed_bvT T "bvmul"
boehmes@36898
   190
  | func @{const_name bitNOT} T = if_fixed_bvT T "bvnot"
boehmes@36898
   191
  | func @{const_name bitAND} T = if_fixed_bvT T "bvand"
boehmes@36898
   192
  | func @{const_name bitOR} T = if_fixed_bvT T "bvor"
boehmes@36898
   193
  | func @{const_name bitXOR} T = if_fixed_bvT T "bvxor"
boehmes@36898
   194
  | func @{const_name div} T = if_fixed_bvT T "bvudiv"
boehmes@36898
   195
  | func @{const_name mod} T = if_fixed_bvT T "bvurem"
boehmes@36898
   196
  | func @{const_name sdiv} T = if_fixed_bvT T "bvsdiv"
boehmes@36898
   197
  | func @{const_name smod} T = if_fixed_bvT T "bvsmod"
boehmes@36898
   198
  | func @{const_name srem} T = if_fixed_bvT T "bvsrem"
boehmes@36898
   199
  | func @{const_name word_cat} T = if_full_fixed_bvT T "concat"
boehmes@36898
   200
  | func @{const_name bv_shl} T = if_full_fixed_bvT T "bvshl"
boehmes@36898
   201
  | func @{const_name bv_lshr} T = if_full_fixed_bvT T "bvlshr"
boehmes@36898
   202
  | func @{const_name bv_ashr} T = if_full_fixed_bvT T "bvashr"
boehmes@36898
   203
  | func @{const_name slice} T = bv_extract "extract" T
boehmes@36898
   204
  | func @{const_name ucast} T = bv_extend "zero_extend" T
boehmes@36898
   205
  | func @{const_name scast} T = bv_extend "sign_extend" T
boehmes@36898
   206
  | func @{const_name word_rotl} T = bv_rotate "rotate_left" T
boehmes@36898
   207
  | func @{const_name word_rotr} T = bv_rotate "rotate_right" T
boehmes@36898
   208
  | func _ _ = K NONE
boehmes@36898
   209
boehmes@36898
   210
fun is_builtin_conn (n, T) = is_connT T andalso is_some (conn n)
boehmes@36898
   211
fun is_builtin_pred (n, T) = is_predT T andalso is_some (pred n T)
boehmes@36898
   212
boehmes@36898
   213
fun builtin_fun (n, T) ts =
boehmes@36898
   214
  if is_connT T then conn n |> Option.map (rpair ts)
boehmes@36898
   215
  else if is_predT T then pred n T |> Option.map (rpair ts)
boehmes@36898
   216
  else func n T ts
boehmes@36898
   217
boehmes@36898
   218
boehmes@36898
   219
boehmes@36898
   220
(** serialization **)
boehmes@36898
   221
boehmes@36898
   222
val add = Buffer.add
boehmes@36898
   223
fun sep f = add " " #> f
boehmes@36898
   224
fun enclose l r f = sep (add l #> f #> add r)
boehmes@36898
   225
val par = enclose "(" ")"
boehmes@36898
   226
fun app n f = (fn [] => sep (add n) | xs => par (add n #> fold f xs))
boehmes@36898
   227
fun line f = f #> add "\n"
boehmes@36898
   228
boehmes@36898
   229
fun var i = add "?v" #> add (string_of_int i)
boehmes@36898
   230
boehmes@36898
   231
fun sterm l (T.SVar i) = sep (var (l - i - 1))
boehmes@36898
   232
  | sterm l (T.SApp (n, ts)) = app n (sterm l) ts
boehmes@36898
   233
  | sterm _ (T.SLet _) = raise Fail "SMT-LIB: unsupported let expression"
boehmes@36898
   234
  | sterm l (T.SQua (q, ss, ps, t)) =
boehmes@36898
   235
      let
boehmes@36898
   236
        val quant = add o (fn T.SForall => "forall" | T.SExists => "exists")
boehmes@36898
   237
        val vs = map_index (apfst (Integer.add l)) ss
boehmes@36898
   238
        fun var_decl (i, s) = par (var i #> sep (add s))
boehmes@36898
   239
        val sub = sterm (l + length ss)
boehmes@36898
   240
        fun pat kind ts = sep (add kind #> enclose "{" " }" (fold sub ts))
boehmes@36898
   241
        fun pats (T.SPat ts) = pat ":pat" ts
boehmes@36898
   242
          | pats (T.SNoPat ts) = pat ":nopat" ts
boehmes@36898
   243
      in par (quant q #> fold var_decl vs #> sub t #> fold pats ps) end
boehmes@36898
   244
boehmes@36898
   245
fun choose_logic theories =
boehmes@36898
   246
  if member (op =) theories T.Bitvector then "QF_AUFBV"
boehmes@36898
   247
  else if member (op =) theories T.Real then "AUFLIRA"
boehmes@36898
   248
  else "AUFLIA"
boehmes@36898
   249
boehmes@36898
   250
fun serialize comments {theories, sorts, funcs} ts =
boehmes@36898
   251
  Buffer.empty
boehmes@36898
   252
  |> line (add "(benchmark Isabelle")
boehmes@36898
   253
  |> line (add ":status unknown")
boehmes@36898
   254
  |> line (add ":logic " #> add (choose_logic theories))
boehmes@36898
   255
  |> length sorts > 0 ?
boehmes@36898
   256
       line (add ":extrasorts" #> par (fold (sep o add) sorts))
boehmes@36898
   257
  |> length funcs > 0 ? (
boehmes@36898
   258
       line (add ":extrafuns" #> add " (") #>
boehmes@36898
   259
       fold (fn (f, (ss, s)) =>
boehmes@36898
   260
         line (sep (app f (sep o add) (ss @ [s])))) funcs #>
boehmes@36898
   261
       line (add ")"))
boehmes@36898
   262
  |> fold (fn t => line (add ":assumption" #> sterm 0 t)) ts
boehmes@36898
   263
  |> line (add ":formula true)")
boehmes@36898
   264
  |> fold (fn str => line (add "; " #> add str)) comments
boehmes@36898
   265
  |> Buffer.content
boehmes@36898
   266
boehmes@36898
   267
boehmes@36898
   268
boehmes@36898
   269
(** interface **)
boehmes@36898
   270
boehmes@36898
   271
val interface = {
boehmes@36898
   272
  extra_norm = extra_norm,
boehmes@36898
   273
  translate = {
boehmes@36898
   274
    prefixes = {
boehmes@36898
   275
      sort_prefix = "S",
boehmes@36898
   276
      func_prefix = "f"},
boehmes@36898
   277
    strict = SOME {
boehmes@36898
   278
      is_builtin_conn = is_builtin_conn,
boehmes@36898
   279
      is_builtin_pred = is_builtin_pred,
boehmes@36898
   280
      is_builtin_distinct = true},
boehmes@36898
   281
    builtins = {
boehmes@36898
   282
      builtin_typ = builtin_typ,
boehmes@36898
   283
      builtin_num = builtin_num,
boehmes@36898
   284
      builtin_fun = builtin_fun},
boehmes@36898
   285
    serialize = serialize}}
boehmes@36898
   286
boehmes@36898
   287
end