src/HOL/Tools/sat_solver.ML
changeset 14965 7155b319eafa
parent 14805 eff7b9df27e9
child 14999 2c39efba8f67
equal deleted inserted replaced
14964:2c1456d705e9 14965:7155b319eafa
     6 Interface to external SAT solvers, and (simple) built-in SAT solvers.
     6 Interface to external SAT solvers, and (simple) built-in SAT solvers.
     7 *)
     7 *)
     8 
     8 
     9 signature SAT_SOLVER =
     9 signature SAT_SOLVER =
    10 sig
    10 sig
    11 	type solver  (* PropLogic.prop_formula -> (int -> bool) option option *)
    11 	exception NOT_CONFIGURED
    12 
    12 
    13 	(* external SAT solvers *)
    13 	type assignment = int -> bool option
       
    14 	datatype result = SATISFIABLE of assignment
       
    15 	                | UNSATISFIABLE
       
    16 	                | UNKNOWN
       
    17 	type solver     = PropLogic.prop_formula -> result
       
    18 
       
    19 	(* auxiliary functions to create external SAT solvers *)
    14 	val write_dimacs_cnf_file : Path.T -> PropLogic.prop_formula -> unit
    20 	val write_dimacs_cnf_file : Path.T -> PropLogic.prop_formula -> unit
    15 	val write_dimacs_sat_file : Path.T -> PropLogic.prop_formula -> unit
    21 	val write_dimacs_sat_file : Path.T -> PropLogic.prop_formula -> unit
    16 	val parse_std_result_file : Path.T -> string -> (int -> bool) option
    22 	val parse_std_result_file : Path.T -> string * string * string -> result
    17 	val make_external_solver  : string -> (PropLogic.prop_formula -> unit) -> (unit -> (int -> bool) option)
    23 	val make_external_solver  : string -> (PropLogic.prop_formula -> unit) -> (unit -> result) -> solver
    18 			-> PropLogic.prop_formula -> (int -> bool) option
       
    19 
    24 
    20 	(* generic interface *)
    25 	(* generic interface *)
    21 	val solvers       : (string * solver) list ref
    26 	val solvers       : (string * solver) list ref
    22 	val add_solver    : string * solver -> unit
    27 	val add_solver    : string * solver -> unit
    23 	val invoke_solver : string -> solver  (* exception OPTION *)
    28 	val invoke_solver : string -> solver  (* exception OPTION *)
    27 struct
    32 struct
    28 
    33 
    29 	open PropLogic;
    34 	open PropLogic;
    30 
    35 
    31 (* ------------------------------------------------------------------------- *)
    36 (* ------------------------------------------------------------------------- *)
    32 (* Type of SAT solvers: given a propositional formula, a satisfying          *)
    37 (* should be raised by an external SAT solver to indicate that the solver is *)
       
    38 (* not configured properly                                                   *)
       
    39 (* ------------------------------------------------------------------------- *)
       
    40 
       
    41 	exception NOT_CONFIGURED;
       
    42 
       
    43 (* ------------------------------------------------------------------------- *)
       
    44 (* type of partial (satisfying) assignments: 'a i = None' means that 'a' is  *)
       
    45 (*      a satisfying assigment regardless of the value of variable 'i'       *)
       
    46 (* ------------------------------------------------------------------------- *)
       
    47 
       
    48 	type assignment = int -> bool option;
       
    49 
       
    50 (* ------------------------------------------------------------------------- *)
       
    51 (* return type of SAT solvers: if the result is 'SATISFIABLE', a satisfying  *)
       
    52 (*      assignment must be returned as well                                  *)
       
    53 (* ------------------------------------------------------------------------- *)
       
    54 
       
    55 	datatype result = SATISFIABLE of assignment
       
    56 	                | UNSATISFIABLE
       
    57 	                | UNKNOWN;
       
    58 
       
    59 (* ------------------------------------------------------------------------- *)
       
    60 (* type of SAT solvers: given a propositional formula, a satisfying          *)
    33 (*      assignment may be returned                                           *)
    61 (*      assignment may be returned                                           *)
    34 (*      - 'Some None' means that no satisfying assignment was found          *)
    62 (* ------------------------------------------------------------------------- *)
    35 (*      - 'None' means that the solver was not configured/installed properly *)
    63 
    36 (* ------------------------------------------------------------------------- *)
    64 	type solver = prop_formula -> result;
    37 
       
    38 	type solver = prop_formula -> (int -> bool) option option;
       
    39 
    65 
    40 (* ------------------------------------------------------------------------- *)
    66 (* ------------------------------------------------------------------------- *)
    41 (* write_dimacs_cnf_file: serializes a formula 'fm' of propositional logic   *)
    67 (* write_dimacs_cnf_file: serializes a formula 'fm' of propositional logic   *)
    42 (*      to a file in DIMACS CNF format (see "Satisfiability Suggested        *)
    68 (*      to a file in DIMACS CNF format (see "Satisfiability Suggested        *)
    43 (*      Format", May 8 1993, Section 2.1)                                    *)
    69 (*      Format", May 8 1993, Section 2.1)                                    *)
    44 (* Note: 'fm' must not contain a variable index less than 1.                 *)
    70 (* Note: 'fm' must not contain a variable index less than 1.                 *)
    45 (* Note: 'fm' is converted into (definitional) CNF.                          *)
    71 (* Note: 'fm' must be given in CNF.                                          *)
    46 (* ------------------------------------------------------------------------- *)
    72 (* ------------------------------------------------------------------------- *)
    47 
    73 
    48 	(* Path.T -> prop_formula -> unit *)
    74 	(* Path.T -> prop_formula -> unit *)
    49 
    75 
    50 	fun write_dimacs_cnf_file path fm =
    76 	fun write_dimacs_cnf_file path fm =
    68 			error "formula is not in CNF"
    94 			error "formula is not in CNF"
    69 		  | cnf_string (BoolVar i) =
    95 		  | cnf_string (BoolVar i) =
    70 			(assert (i>=1) "formula contains a variable index less than 1";
    96 			(assert (i>=1) "formula contains a variable index less than 1";
    71 			string_of_int i)
    97 			string_of_int i)
    72 		  | cnf_string (Not fm) =
    98 		  | cnf_string (Not fm) =
    73 			"-" ^ (cnf_string fm)
    99 			"-" ^ cnf_string fm
    74 		  | cnf_string (Or (fm1,fm2)) =
   100 		  | cnf_string (Or (fm1,fm2)) =
    75 			(cnf_string fm1) ^ " " ^ (cnf_string fm2)
   101 			cnf_string fm1 ^ " " ^ cnf_string fm2
    76 		  | cnf_string (And (fm1,fm2)) =
   102 		  | cnf_string (And (fm1,fm2)) =
    77 			(cnf_string fm1) ^ " 0\n" ^ (cnf_string fm2)
   103 			cnf_string fm1 ^ " 0\n" ^ cnf_string fm2
    78 	in
   104 	in
    79 		File.write path
   105 		File.write path
    80 			(let
   106 			(let
    81 				val cnf               = (cnf_True_False_elim o defcnf) fm  (* conversion into def. CNF *)
   107 				val fm'               = cnf_True_False_elim fm
    82 				val number_of_vars    = maxidx cnf
   108 				val number_of_vars    = maxidx fm'
    83 				val number_of_clauses = cnf_number_of_clauses cnf
   109 				val number_of_clauses = cnf_number_of_clauses fm'
       
   110 				val cnfstring         = cnf_string fm'
    84 			in
   111 			in
    85 				"c This file was generated by SatSolver.write_dimacs_cnf_file\n" ^
   112 				"c This file was generated by SatSolver.write_dimacs_cnf_file\n" ^
    86 				"c (c) Tjark Weber\n" ^
   113 				"c (c) Tjark Weber\n" ^
    87 				"p cnf " ^ (string_of_int number_of_vars) ^ " " ^ (string_of_int number_of_clauses) ^ "\n" ^
   114 				"p cnf " ^ string_of_int number_of_vars ^ " " ^ string_of_int number_of_clauses ^ "\n" ^
    88 				(cnf_string cnf) ^ "\n"
   115 				cnfstring ^ " 0\n"
    89 			end)
   116 			end)
    90 	end;
   117 	end;
    91 
   118 
    92 (* ------------------------------------------------------------------------- *)
   119 (* ------------------------------------------------------------------------- *)
    93 (* write_dimacs_sat_file: serializes a formula 'fm' of propositional logic   *)
   120 (* write_dimacs_sat_file: serializes a formula 'fm' of propositional logic   *)
   107 			"+()"
   134 			"+()"
   108 		  | sat_string (BoolVar i) =
   135 		  | sat_string (BoolVar i) =
   109 			(assert (i>=1) "formula contains a variable index less than 1";
   136 			(assert (i>=1) "formula contains a variable index less than 1";
   110 			string_of_int i)
   137 			string_of_int i)
   111 		  | sat_string (Not fm) =
   138 		  | sat_string (Not fm) =
   112 			"-(" ^ (sat_string fm) ^ ")"
   139 			"-(" ^ sat_string fm ^ ")"
   113 		  | sat_string (Or (fm1,fm2)) =
   140 		  | sat_string (Or (fm1,fm2)) =
   114 			"+(" ^ (sat_string fm1) ^ " " ^ (sat_string fm2) ^ ")"
   141 			"+(" ^ sat_string fm1 ^ " " ^ sat_string fm2 ^ ")"
   115 		  | sat_string (And (fm1,fm2)) =
   142 		  | sat_string (And (fm1,fm2)) =
   116 			"*(" ^ (sat_string fm1) ^ " " ^ (sat_string fm2) ^ ")"
   143 			"*(" ^ sat_string fm1 ^ " " ^ sat_string fm2 ^ ")"
   117 	in
   144 	in
   118 		File.write path
   145 		File.write path
   119 			(let
   146 			(let
   120 				val number_of_vars = Int.max (maxidx fm, 1)
   147 				val number_of_vars = Int.max (maxidx fm, 1)
   121 			in
   148 			in
   122 				"c This file was generated by SatSolver.write_dimacs_sat_file\n" ^
   149 				"c This file was generated by SatSolver.write_dimacs_sat_file\n" ^
   123 				"c (c) Tjark Weber\n" ^
   150 				"c (c) Tjark Weber\n" ^
   124 				"p sat " ^ (string_of_int number_of_vars) ^ "\n" ^
   151 				"p sat " ^ string_of_int number_of_vars ^ "\n" ^
   125 				"(" ^ (sat_string fm) ^ ")\n"
   152 				"(" ^ sat_string fm ^ ")\n"
   126 			end)
   153 			end)
   127 	end;
   154 	end;
   128 
   155 
   129 (* ------------------------------------------------------------------------- *)
   156 (* ------------------------------------------------------------------------- *)
   130 (* parse_std_result_file: scans a SAT solver's output file for a satisfying  *)
   157 (* parse_std_result_file: scans a SAT solver's output file for a satisfying  *)
   131 (*      variable assignment.  Returns the assignment, or 'None' if the SAT   *)
   158 (*      variable assignment.  Returns the assignment, or 'UNSATISFIABLE' if  *)
   132 (*      solver did not find one.  The file format must be as follows:        *)
   159 (*      the file contains 'unsatisfiable', or 'UNKNOWN' if the file contains *)
   133 (*      ,-----                                                               *)
   160 (*      neither 'satisfiable' nor 'unsatisfiable'.  Empty lines are ignored. *)
   134 (*      | 0 or more lines not containing 'success'                           *)
   161 (*      The assignment must be given in one or more lines immediately after  *)
   135 (*      | A line containing 'success' as a substring                         *)
   162 (*      the line that contains 'satisfiable'.  These lines must begin with   *)
   136 (*      | A line ASSIGNMENT containing integers, separated by " "            *)
   163 (*      'assignment_prefix'.  Variables must be separated by " ".  Non-      *)
   137 (*      | 0 or more lines                                                    *)
   164 (*      integer strings are ignored.  If variable i is contained in the      *)
   138 (*      `-----                                                               *)
   165 (*      assignment, then i is interpreted as 'true'.  If ~i is contained in  *)
   139 (*      If variable i is contained in ASSIGNMENT, then i is interpreted as   *)
   166 (*      the assignment, then i is interpreted as 'false'.  Otherwise the     *)
   140 (*      'true'.  If ~i is contained in ASSIGNMENT, then i is interpreted as  *)
   167 (*      value of i is taken to be unspecified.                               *)
   141 (*      'false'.                                                             *)
   168 (* ------------------------------------------------------------------------- *)
   142 (* ------------------------------------------------------------------------- *)
   169 
   143 
   170 	(* Path.T -> string * string * string -> result *)
   144 	(* Path.T -> string -> (int -> bool) option *)
   171 
   145 
   172 	fun parse_std_result_file path (satisfiable, assignment_prefix, unsatisfiable) =
   146 	fun parse_std_result_file path success =
       
   147 	let
   173 	let
   148 		(* 'a option -> 'a Library.option *)
   174 		(* 'a option -> 'a Library.option *)
   149 		fun option (SOME a) =
   175 		fun option (SOME a) =
   150 			Some a
   176 			Some a
   151 		  | option NONE =
   177 		  | option NONE =
   152 			None
   178 			None
   153 		(* string -> int list *)
   179 		(* string -> int list *)
   154 		fun int_list_from_string s =
   180 		fun int_list_from_string s =
   155 			mapfilter (option o Int.fromString) (space_explode " " s)
   181 			mapfilter (option o Int.fromString) (space_explode " " s)
   156 		(* int list -> int -> bool *)
   182 		(* int list -> assignment *)
   157 		fun assignment_from_list [] i =
   183 		fun assignment_from_list [] i =
   158 			false  (* could be 'true' just as well; the SAT solver didn't provide a value for this variable *)
   184 			None  (* the SAT solver didn't provide a value for this variable *)
   159 		  | assignment_from_list (x::xs) i =
   185 		  | assignment_from_list (x::xs) i =
   160 			if x=i then true
   186 			if x=i then (Some true)
   161 			else if x=(~i) then false
   187 			else if x=(~i) then (Some false)
   162 			else assignment_from_list xs i
   188 			else assignment_from_list xs i
       
   189 		(* int list -> string list -> assignment *)
       
   190 		fun parse_assignment xs [] =
       
   191 			assignment_from_list xs
       
   192 		  | parse_assignment xs (line::lines) =
       
   193 			if String.isPrefix assignment_prefix line then
       
   194 				parse_assignment (xs @ int_list_from_string line) lines
       
   195 			else
       
   196 				assignment_from_list xs
   163 		(* string -> string -> bool *)
   197 		(* string -> string -> bool *)
   164 		fun is_substring needle haystack =
   198 		fun is_substring needle haystack =
   165 		let
   199 		let
   166 			val length1 = String.size needle
   200 			val length1 = String.size needle
   167 			val length2 = String.size haystack
   201 			val length2 = String.size haystack
   170 				false
   204 				false
   171 			else if needle = String.substring (haystack, 0, length1) then
   205 			else if needle = String.substring (haystack, 0, length1) then
   172 				true
   206 				true
   173 			else is_substring needle (String.substring (haystack, 1, length2-1))
   207 			else is_substring needle (String.substring (haystack, 1, length2-1))
   174 		end
   208 		end
   175 		(* string list -> int list option *)
   209 		(* string list -> result *)
   176 		fun parse_lines [] =
   210 		fun parse_lines [] =
   177 			None
   211 			UNKNOWN
   178 		  | parse_lines (line::lines) =
   212 		  | parse_lines (line::lines) =
   179 			if is_substring success line then
   213 			if is_substring satisfiable line then
   180 				(* the next line must be a list of integers *)
   214 				SATISFIABLE (parse_assignment [] lines)
   181 				(Some o assignment_from_list o int_list_from_string o hd) lines
   215 			else if is_substring unsatisfiable line then
       
   216 				UNSATISFIABLE
   182 			else
   217 			else
   183 				parse_lines lines
   218 				parse_lines lines
   184 	in
   219 	in
   185 		(parse_lines o split_lines o File.read) path
   220 		(parse_lines o (filter (fn l => l <> "")) o split_lines o File.read) path
   186 	end;
   221 	end;
   187 
   222 
   188 (* ------------------------------------------------------------------------- *)
   223 (* ------------------------------------------------------------------------- *)
   189 (* make_external_solver: call 'writefn', execute 'cmd', call 'readfn'        *)
   224 (* make_external_solver: call 'writefn', execute 'cmd', call 'readfn'        *)
   190 (* ------------------------------------------------------------------------- *)
   225 (* ------------------------------------------------------------------------- *)
   191 
   226 
   192 	(* string -> (prop_formula -> unit) -> (unit -> (int -> bool) option) -> prop_formula -> (int -> bool) option *)
   227 	(* string -> (PropLogic.prop_formula -> unit) -> (unit -> result) -> solver *)
   193 
   228 
   194 	fun make_external_solver cmd writefn readfn fm =
   229 	fun make_external_solver cmd writefn readfn fm =
   195 		(writefn fm;
   230 		(writefn fm; system cmd; readfn ());
   196 		assert ((system cmd)=0) ("system command " ^ quote cmd ^ " failed (make sure the SAT solver is installed)");
       
   197 		readfn ());
       
   198 
   231 
   199 (* ------------------------------------------------------------------------- *)
   232 (* ------------------------------------------------------------------------- *)
   200 (* solvers: a (reference to a) table of all registered SAT solvers           *)
   233 (* solvers: a (reference to a) table of all registered SAT solvers           *)
   201 (* ------------------------------------------------------------------------- *)
   234 (* ------------------------------------------------------------------------- *)
   202 
   235 
   253 				(* set the lowest bit that wasn't set before, keep the higher bits *)
   286 				(* set the lowest bit that wasn't set before, keep the higher bits *)
   254 				Some (y::x::xs)
   287 				Some (y::x::xs)
   255 		(* int list -> int -> bool *)
   288 		(* int list -> int -> bool *)
   256 		fun assignment_from_list xs i =
   289 		fun assignment_from_list xs i =
   257 			i mem xs
   290 			i mem xs
   258 		(* int list -> (int -> bool) option *)
   291 		(* int list -> SatSolver.result *)
   259 		fun solver_loop xs =
   292 		fun solver_loop xs =
   260 			if PropLogic.eval (assignment_from_list xs) fm then
   293 			if PropLogic.eval (assignment_from_list xs) fm then
   261 				Some (assignment_from_list xs)
   294 				SatSolver.SATISFIABLE (Some o (assignment_from_list xs))
   262 			else
   295 			else
   263 				(case next_list xs indices of
   296 				(case next_list xs indices of
   264 				  Some xs' => solver_loop xs'
   297 				  Some xs' => solver_loop xs'
   265 				| None     => None)
   298 				| None     => SatSolver.UNSATISFIABLE)
   266 	in
   299 	in
   267 		(* start with the "first" assignment (all variables are interpreted as 'False') *)
   300 		(* start with the "first" assignment (all variables are interpreted as 'false') *)
   268 		solver_loop []
   301 		solver_loop []
   269 	end
   302 	end
   270 in
   303 in
   271 	SatSolver.add_solver ("enumerate", Some o enum_solver)
   304 	SatSolver.add_solver ("enumerate", enum_solver)
   272 end;
   305 end;
   273 
   306 
   274 (* ------------------------------------------------------------------------- *)
   307 (* ------------------------------------------------------------------------- *)
   275 (* Internal SAT solver, available as 'SatSolver.invoke_solver "dpll"' -- a   *)
   308 (* Internal SAT solver, available as 'SatSolver.invoke_solver "dpll"' -- a   *)
   276 (* simple implementation of the DPLL algorithm (cf. L. Zhang, S. Malik: "The *)
   309 (* simple implementation of the DPLL algorithm (cf. L. Zhang, S. Malik: "The *)
   281 	local
   314 	local
   282 		open PropLogic
   315 		open PropLogic
   283 	in
   316 	in
   284 		fun dpll_solver fm =
   317 		fun dpll_solver fm =
   285 		let
   318 		let
   286 			(* We could use 'PropLogic.defcnf fm' instead of 'nnf fm', but that *)
   319 			(* We could use 'PropLogic.defcnf fm' instead of 'PropLogic.nnf fm' *)
   287 			(* sometimes introduces more boolean variables than we can handle   *)
   320 			(* but that sometimes introduces more boolean variables than we can *)
   288 			(* efficiently.                                                     *)
   321 			(* handle efficiently.                                              *)
   289 			val fm' = PropLogic.nnf fm
   322 			val fm' = PropLogic.nnf fm
   290 			(* int list *)
   323 			(* int list *)
   291 			val indices = PropLogic.indices fm'
   324 			val indices = PropLogic.indices fm'
   292 			(* int list -> int -> prop_formula *)
   325 			(* int list -> int -> prop_formula *)
   293 			fun partial_var_eval []      i = BoolVar i
   326 			fun partial_var_eval []      i = BoolVar i
   340 						case dpll (x::xs') fm' of  (* passing fm' rather than fm should save some simplification work *)
   373 						case dpll (x::xs') fm' of  (* passing fm' rather than fm should save some simplification work *)
   341 						  Some xs'' => Some xs''
   374 						  Some xs'' => Some xs''
   342 						| None      => dpll ((~x)::xs') fm'  (* now try interpreting 'x' as 'False' *)
   375 						| None      => dpll ((~x)::xs') fm'  (* now try interpreting 'x' as 'False' *)
   343 					end
   376 					end
   344 			end
   377 			end
   345 			(* int list -> int -> bool *)
   378 			(* int list -> assignment *)
   346 			fun assignment_from_list [] i =
   379 			fun assignment_from_list [] i =
   347 				false  (* could be 'true' just as well; the DPLL procedure didn't provide a value for this variable *)
   380 				None  (* the DPLL procedure didn't provide a value for this variable *)
   348 			  | assignment_from_list (x::xs) i =
   381 			  | assignment_from_list (x::xs) i =
   349 				if x=i then true
   382 				if x=i then (Some true)
   350 				else if x=(~i) then false
   383 				else if x=(~i) then (Some false)
   351 				else assignment_from_list xs i
   384 				else assignment_from_list xs i
   352 		in
   385 		in
   353 			(* initially, no variable is interpreted yet *)
   386 			(* initially, no variable is interpreted yet *)
   354 			apsome assignment_from_list (dpll [] fm')
   387 			case dpll [] fm' of
       
   388 			  Some assignment => SatSolver.SATISFIABLE (assignment_from_list assignment)
       
   389 			| None            => SatSolver.UNSATISFIABLE
   355 		end
   390 		end
   356 	end  (* local *)
   391 	end  (* local *)
   357 in
   392 in
   358 	SatSolver.add_solver ("dpll", Some o dpll_solver)
   393 	SatSolver.add_solver ("dpll", dpll_solver)
   359 end;
   394 end;
   360 
   395 
   361 (* ------------------------------------------------------------------------- *)
   396 (* ------------------------------------------------------------------------- *)
   362 (* Internal SAT solver, available as 'SatSolver.invoke_solver "auto"': uses  *)
   397 (* Internal SAT solver, available as 'SatSolver.invoke_solver "auto"': uses  *)
   363 (* the first installed solver (other than "auto" itself)                     *)
   398 (* all installed solvers (other than "auto" itself) until one returns either *)
       
   399 (* SATISFIABLE or UNSATISFIABLE                                              *)
   364 (* ------------------------------------------------------------------------- *)
   400 (* ------------------------------------------------------------------------- *)
   365 
   401 
   366 let
   402 let
   367 	fun auto_solver fm =
   403 	fun auto_solver fm =
   368 		get_first (fn (name,solver) =>
   404 	let
       
   405 		fun loop [] =
       
   406 			SatSolver.UNKNOWN
       
   407 		  | loop ((name, solver)::solvers) =
   369 			if name="auto" then
   408 			if name="auto" then
   370 				None
   409 				(* do not call solver "auto" from within "auto" *)
       
   410 				loop solvers
   371 			else
   411 			else
   372 				((if name="dpll" orelse name="enumerate" then
   412 			(
       
   413 				(if name="dpll" orelse name="enumerate" then
   373 					warning ("Using SAT solver " ^ quote name ^ "; for better performance, consider using an external solver.")
   414 					warning ("Using SAT solver " ^ quote name ^ "; for better performance, consider using an external solver.")
   374 				else
   415 				else
   375 					());
   416 					());
   376 				solver fm)) (rev (!SatSolver.solvers))
   417 				(* apply 'solver' to 'fm' *)
       
   418 				(case solver fm of
       
   419 				  SatSolver.SATISFIABLE a => SatSolver.SATISFIABLE a
       
   420 				| SatSolver.UNSATISFIABLE => SatSolver.UNSATISFIABLE
       
   421 				| SatSolver.UNKNOWN       => loop solvers)
       
   422 				handle SatSolver.NOT_CONFIGURED => loop solvers
       
   423 			)
       
   424 	in
       
   425 		loop (rev (!SatSolver.solvers))
       
   426 	end
   377 in
   427 in
   378 	SatSolver.add_solver ("auto", auto_solver)
   428 	SatSolver.add_solver ("auto", auto_solver)
   379 end;
   429 end;
   380 
   430 
   381 (* ------------------------------------------------------------------------- *)
   431 (* ------------------------------------------------------------------------- *)
   382 (* ZChaff, Version 2003.12.04                                                *)
   432 (* ZChaff, Version 2003.12.04 (http://www.princeton.edu/~chaff/zchaff.html)  *)
   383 (* ------------------------------------------------------------------------- *)
   433 (* ------------------------------------------------------------------------- *)
   384 
   434 
   385 let
   435 let
   386 	fun zchaff fm =
   436 	fun zchaff fm =
   387 	let
   437 	let
   388 		val inname     = "isabelle.cnf"
   438 		val _          = if (getenv "ZCHAFF_HOME") = "" then raise SatSolver.NOT_CONFIGURED else ()
   389 		val outname    = "result"
   439 		val inpath     = File.tmp_path (Path.unpack "isabelle.cnf")
   390 		val inpath     = File.tmp_path (Path.unpack inname)
   440 		val outpath    = File.tmp_path (Path.unpack "result")
   391 		val outpath    = File.tmp_path (Path.unpack outname)
       
   392 		val cmd        = (getenv "ZCHAFF_HOME") ^ "/zchaff " ^ (Path.pack inpath) ^ " > " ^ (Path.pack outpath)
   441 		val cmd        = (getenv "ZCHAFF_HOME") ^ "/zchaff " ^ (Path.pack inpath) ^ " > " ^ (Path.pack outpath)
   393 		fun writefn fm = SatSolver.write_dimacs_cnf_file inpath fm
   442 		fun writefn fm = SatSolver.write_dimacs_cnf_file inpath (PropLogic.defcnf fm)
   394 		fun readfn ()  = SatSolver.parse_std_result_file outpath "Verify Solution successful. Instance satisfiable"
   443 		fun readfn ()  = SatSolver.parse_std_result_file outpath ("Verify Solution successful. Instance satisfiable", "", "Instance unsatisfiable")
   395 		val _          = assert (not (File.exists inpath)) ("file " ^ quote (Path.pack inpath) ^ " exists, please delete (will not overwrite)")
   444 		val _          = assert (not (File.exists inpath)) ("file " ^ quote (Path.pack inpath) ^ " exists, please delete (will not overwrite)")
   396 		val _          = assert (not (File.exists outpath)) ("file " ^ quote (Path.pack outpath) ^ " exists, please delete (will not overwrite)")
   445 		val _          = assert (not (File.exists outpath)) ("file " ^ quote (Path.pack outpath) ^ " exists, please delete (will not overwrite)")
   397 		val assignment = SatSolver.make_external_solver cmd writefn readfn fm
   446 		val result     = SatSolver.make_external_solver cmd writefn readfn fm
   398 		val _          = (File.rm inpath handle _ => ())
   447 		val _          = (File.rm inpath handle _ => ())
   399 		val _          = (File.rm outpath handle _ => ())
   448 		val _          = (File.rm outpath handle _ => ())
   400 	in
   449 	in
   401 		assignment
   450 		result
   402 	end
   451 	end
   403 in
   452 in
   404 		SatSolver.add_solver ("zchaff", (fn fm => if getenv "ZCHAFF_HOME" <> "" then Some (zchaff fm) else None))
   453 	SatSolver.add_solver ("zchaff", zchaff)
       
   454 end;
       
   455 
       
   456 (* ------------------------------------------------------------------------- *)
       
   457 (* BerkMin 561 (http://eigold.tripod.com/BerkMin.html)                       *)
       
   458 (* ------------------------------------------------------------------------- *)
       
   459 
       
   460 let
       
   461 	fun berkmin fm =
       
   462 	let
       
   463 		val _          = if (getenv "BERKMIN_HOME") = "" orelse (getenv "BERKMIN_EXE") = "" then raise SatSolver.NOT_CONFIGURED else ()
       
   464 		val inpath     = File.tmp_path (Path.unpack "isabelle.cnf")
       
   465 		val outpath    = File.tmp_path (Path.unpack "result")
       
   466 		val cmd        = (getenv "BERKMIN_HOME") ^ "/" ^ (getenv "BERKMIN_EXE") ^ " " ^ (Path.pack inpath) ^ " > " ^ (Path.pack outpath)
       
   467 		fun writefn fm = SatSolver.write_dimacs_cnf_file inpath (PropLogic.defcnf fm)
       
   468 		fun readfn ()  = SatSolver.parse_std_result_file outpath ("Satisfiable          !!", "solution =", "UNSATISFIABLE          !!")
       
   469 		val _          = assert (not (File.exists inpath)) ("file " ^ quote (Path.pack inpath) ^ " exists, please delete (will not overwrite)")
       
   470 		val _          = assert (not (File.exists outpath)) ("file " ^ quote (Path.pack outpath) ^ " exists, please delete (will not overwrite)")
       
   471 		val result     = SatSolver.make_external_solver cmd writefn readfn fm
       
   472 		val _          = (File.rm inpath handle _ => ())
       
   473 		val _          = (File.rm outpath handle _ => ())
       
   474 	in
       
   475 		result
       
   476 	end
       
   477 in
       
   478 	SatSolver.add_solver ("berkmin", berkmin)
       
   479 end;
       
   480 
       
   481 (* ------------------------------------------------------------------------- *)
       
   482 (* Jerusat 1.3 (http://www.cs.tau.ac.il/~ale1/)                              *)
       
   483 (* ------------------------------------------------------------------------- *)
       
   484 
       
   485 let
       
   486 	fun jerusat fm =
       
   487 	let
       
   488 		val _          = if (getenv "JERUSAT_HOME") = "" then raise SatSolver.NOT_CONFIGURED else ()
       
   489 		val inpath     = File.tmp_path (Path.unpack "isabelle.cnf")
       
   490 		val outpath    = File.tmp_path (Path.unpack "result")
       
   491 		val cmd        = (getenv "JERUSAT_HOME") ^ "/Jerusat1.3 " ^ (Path.pack inpath) ^ " > " ^ (Path.pack outpath)
       
   492 		fun writefn fm = SatSolver.write_dimacs_cnf_file inpath (PropLogic.defcnf fm)
       
   493 		fun readfn ()  = SatSolver.parse_std_result_file outpath ("s SATISFIABLE", "v ", "s UNSATISFIABLE")
       
   494 		val _          = assert (not (File.exists inpath)) ("file " ^ quote (Path.pack inpath) ^ " exists, please delete (will not overwrite)")
       
   495 		val _          = assert (not (File.exists outpath)) ("file " ^ quote (Path.pack outpath) ^ " exists, please delete (will not overwrite)")
       
   496 		val result     = SatSolver.make_external_solver cmd writefn readfn fm
       
   497 		val _          = (File.rm inpath handle _ => ())
       
   498 		val _          = (File.rm outpath handle _ => ())
       
   499 	in
       
   500 		result
       
   501 	end
       
   502 in
       
   503 	SatSolver.add_solver ("jerusat", jerusat)
   405 end;
   504 end;
   406 
   505 
   407 (* ------------------------------------------------------------------------- *)
   506 (* ------------------------------------------------------------------------- *)
   408 (* Add code for other SAT solvers below.                                     *)
   507 (* Add code for other SAT solvers below.                                     *)
   409 (* ------------------------------------------------------------------------- *)
   508 (* ------------------------------------------------------------------------- *)
   410 
   509 
   411 (*
   510 (*
   412 let
   511 let
   413 	fun mysolver fm = ...
   512 	fun mysolver fm = ...
   414 in
   513 in
   415 	SatSolver.add_solver ("myname", (fn fm => if mysolver_is_installed then Some (mysolver fm) else None));  -- register the solver
   514 	SatSolver.add_solver ("myname", (fn fm => if mysolver_is_configured then mysolver fm else raise SatSolver.NOT_CONFIGURED));  -- register the solver
   416 end;
   515 end;
   417 
   516 
   418 -- the solver is now available as SatSolver.invoke_solver "myname"
   517 -- the solver is now available as SatSolver.invoke_solver "myname"
   419 *)
   518 *)