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