2 (*open satCommonTools;;*)
4 (* translation from terms to DIMACS cnf and back *)
6 (* mapping from HOL variable names to DIMACS variable numbers
7 is stored in a global assignable (i.e. reference) variable sat_var_map.
8 The type of sat_var_map is (int * (term * int) map) ref and
9 the integer first component is the next available number
10 (i.e. it is one plus the number of elements in the map)
11 in th second component (t,n), if n<0 then the literal represented
12 is ~t (the stored t is never negated)
16 initialise sat_var_map to integer 1 paired with the empty map
17 (in DIMACS variable numbering starts from 1 because 0
18 is the clause separator)
21 let sat_var_map = ref(1, Termmap.empty)
22 let sat_var_arr = ref(Array.make 0 t_tm) (* varnum->+ve lit. *)
25 Reinitialise sat_var_map.
26 Needs to be done for each translation of a term to DIMACS
27 as numbers must be an initial segment of 1,2,3,...
28 (otherwise grasp, zchaff etc may crash)
31 (*+1 'cos var numbers start at 1*)
32 let initSatVarMap var_count =
33 (sat_var_map := (1, Termmap.empty);
34 sat_var_arr := Array.make (var_count+1) t_tm)
38 Lookup the var number corresponding to a +ve literal s, possibly extending sat_var_map
41 let lookup_sat_var s =
42 let (c,svm) = !sat_var_map in
43 snd (try Termmap.find s svm with
45 let svm' = Termmap.add s (s,c) svm in
46 let _ = (sat_var_map := (c+1,svm')) in
48 try (Array.set (!sat_var_arr) c s)
49 with Invalid_argument _ ->
50 failwith ("lookup_sat_varError: "^(string_of_term s)^"::"^(string_of_int c)^"\n") in
55 Lookup the +ve lit corresponding to a var number
57 let lookup_sat_num n =
58 try (Array.get (!sat_var_arr) n)
59 with Invalid_argument _ ->
60 failwith ("lookup_sat_numError: "^(string_of_int n)^"\n")
64 Show sat_var_map as a list of its elements
67 let showSatVarMap () =
68 let (c,st) = !sat_var_map in
69 (c, List.map snd (tm_listItems st))
72 Print a term showing types
75 let all_string_of_term t =
76 ((string_of_term) t^" : "^(string_of_type (type_of t)))
78 let print_all_term t =
79 print_string (all_string_of_term t);;
82 Convert a literal to a (bool * integer) pair, where
83 the boolean is true iff the literal is negated,
84 if necessary extend sat_var_map
87 exception Lit_to_int_err of string
93 let t1 = dest_neg t in
94 if type_of t1 = bool_ty
96 else raise (Lit_to_int_err (all_string_of_term t))
98 if type_of t = bool_ty
100 else raise (Lit_to_int_err (all_string_of_term t)) in
101 let v_num = lookup_sat_var v in
105 Convert an integer (a possibly negated var number) to a literal,
106 raising lookup_sat_numError if the absolute value of
107 the integer isn't in sat_var_map
110 let t = lookup_sat_num (abs n) in
111 if n>=0 then t else mk_neg t
115 checks t is CNF of the form
116 ``(v11 \/ ... \/ v1p) /\ (v21 \/ ... \/ v2q) /\ ... /\ (vr1 \/ ... \/vrt)``
117 where vij is a literal, i.e. a boolean variable or a negated
119 If t is such a CNF then termToDimacs t returns a list of lists of integers
120 [[n11,...,n1p],[n21,...,n2q], ... , [nr1,...,nrt]]
121 If vij is a boolean variable ``v`` then nij is the entry
122 for v in sat_var_map. If vij is ``~v``, then nij is the negation
123 of the entry for v in sat_var_map
124 N.B. Definition of termToDimacs processes last clause first,
125 so variables are not numbered in the left-to-right order.
126 Not clear if this matters.
131 (fun c d -> (List.map literalToInt (disjuncts c)) :: d)
137 val t3 = ``x \/ y \/ ~z \/ w``;
138 val t4 = ``(x \/ y \/ ~z \/ w) /\ (~w \/ ~x \/ y)``;
139 val t5 = ``(x \/ y \/ ~z \/ w) /\ !x. (~w \/ ~x \/ y)``;
140 val t6 = ``(x \/ y \/ ~z \/ w) /\ (~w)``;
141 val t7 = ``(x \/ y \/ ~z \/ w) /\ (~w) /\ (w \/ x) /\ (p /\ q /\ r)``;
145 reference containing prefix used to make variables from numbers
152 intToPrefixedLiteral n = ``(!prefix)n``
153 intToPrefixedLiteral (~n) = ``~(!prefix)n``
156 let intToPrefixedLiteral n =
158 then mk_var(((!prefix) ^ (string_of_int n)), bool_ty)
159 else mk_neg(mk_var((!prefix) ^ (string_of_int(abs n)), bool_ty))
162 buildClause [n1,...,np] builds
163 ``(!prefix)np /\ ... /\ (!prefix)n1``
164 Raises exception Empty on the empty list
169 (fun t n -> mk_disj(intToPrefixedLiteral n, t))
170 (intToPrefixedLiteral (hd l))
175 converts a list of integers
176 [n11,...,n1p,0,n21,...,n2q,0, ... , 0,nr1,...,nrt,0]
177 into a term in CNF of the form
178 ``(v11 \/ ... \/ v1p) /\ (v21 \/ ... \/ v2q) /\ ... /\ (vr1 \/ ... \/vrt)``
179 where vij is a literal, i.e. a boolean variable or a negated boolena variable.
180 If nij is non-negative then vij is ``(!prefix)nij``;
181 If nij is negative ~mij then vij is ``~(!prefix)mij``;
184 (* dimacsToTerm_aux splits off one clause, dimacsToTerm iterates it *)
185 let rec dimacsToTerm_aux acc = function
186 [] -> (buildClause acc,[])
187 | (0::l) -> (buildClause acc,l)
188 | (x::l) -> dimacsToTerm_aux (x::acc) l
190 let rec dimacsToTerm l =
191 let (t,l1) = dimacsToTerm_aux [] l in
192 if List.length l1 = 0
194 else mk_conj(t, dimacsToTerm l1)
197 Convert (true,n) to "-n" and (false,n) to "n"
200 let literalToString b n =
202 then ("-" ^ (string_of_int n))
207 converts t to DIMACS and then writes out a
208 file into the temporary directory.
209 the name of the temporary file (without extension ".cnf") is returned.
213 Refererence containing name of temporary file used
214 for last invocation of a SAT solver
217 let tmp_name = ref "undefined"
219 let termToDimacsFile fname t var_count =
220 let clause_count = List.length(conjuncts t) in
221 let _ = initSatVarMap var_count in
222 let dlist = termToDimacs t in
223 let tmp = Filename.temp_file "sat" "" in
226 (Some fname) -> fname^".cnf"
227 | None -> tmp^".cnf" in
228 let outstr = open_out tmpname in
229 let out s = output_string outstr s in
230 let res = (out "c File "; out tmpname; out " generated by HolSatLib\n";
233 out (string_of_int var_count); out " ";
234 out (string_of_int clause_count); out "\n";
236 (fun l -> (List.iter (fun (x,y) ->
237 (out(literalToString x y); out " ")) l;
249 reads a DIMACS file called filename and returns
250 a term in CNF in which each number n in the DIMACS file
251 is a boolean variable (!prefix)n
252 Code below by Ken Larsen (replaces earlier implementation by MJCG)
254 exception Read_dimacs_error;;
256 let rec dropLine ins =
257 match Stream.peek ins with
258 Some '\n' -> Stream.junk ins
259 | Some _ -> (Stream.junk ins; dropLine ins)
260 | None -> raise Read_dimacs_error
262 let rec stripPreamble ins =
263 match Stream.peek ins with
264 Some 'c' -> (dropLine ins; stripPreamble ins)
265 | Some 'p' -> (dropLine ins; stripPreamble ins)
269 let rec getIntClause lex acc =
271 (try Stream.next lex with
272 Stream.Failure -> Genlex.Kwd "EOF" (* EOF *))
274 (Genlex.Int 0) -> Some acc
275 | (Genlex.Int i) -> getIntClause lex (i::acc)
276 | (Genlex.Kwd "EOF") ->
277 if List.length acc = 0
280 | _ -> raise Read_dimacs_error
283 (* This implementation is inspired by
284 (and hopefully faithful to) dimacsToTerm.
288 match getIntClause lex [] with
289 Some ns -> loop (mk_conj(buildClause ns, acc))
290 | None -> Some acc in
291 match getIntClause lex [] with
292 Some ns -> loop (buildClause ns)
296 match stripPreamble ins with
298 let lex = (Genlex.make_lexer ["EOF"] ins) in
302 let readDimacs filename =
303 (*let val fullfilename = Path.mkAbsolute(filename, FileSys.getDir())*)
304 let inf = Pervasives.open_in filename in
305 let ins = Stream.of_channel inf in
306 let term = readTerms ins in
308 match term with Some t -> t | None -> raise Read_dimacs_error)