1 /****************************************************************************
2 Copyright (c) 2011-2014, Armin Biere, Johannes Kepler University.
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to
6 deal in the Software without restriction, including without limitation the
7 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 sell copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 ****************************************************************************/
31 #define MAXNONREDROUNDS 3
32 #define MINCOREROUNDS 5
33 #define MAXCOREROUNDS 100
35 typedef struct Cls
{ int lit
, red
, * lits
; } Cls
;
37 static int verbose
, nowitness
;
38 static int fclose_input
, pclose_input
, close_output
;
39 static FILE * input_file
, * output_file
;
40 static const char * input_name
, * output_name
;
41 static int lineno
= 1;
42 static int nvars
, nclauses
;
44 static int * lits
, nlits
, szlits
;
46 static int reductions
;
50 static int next (void) {
51 int res
= fgetc (input_file
);
52 if (res
== '\n') lineno
++;
56 static void msg (int level
, const char * fmt
, ...) {
58 if (verbose
< level
) return;
59 fputs ("c [picomus] ", stdout
);
61 vfprintf (stdout
, fmt
, ap
);
67 static void warn (const char * fmt
, ...) {
69 if (verbose
< 0) return;
70 fputs ("c [picomus] WARNING: ", stdout
);
72 vfprintf (stdout
, fmt
, ap
);
78 static const char * parse (void) {
79 int ch
, n
, lit
, sign
, i
;
84 while ((ch
= next ()) != '\n')
85 if (ch
== EOF
) return "EOF after 'c'";
88 if (ch
== '\r') goto HEADER
;
89 if (ch
!= 'p') return "expected 'c' or 'p'";
90 if (fscanf (input_file
, " cnf %d %d", &nvars
, &nclauses
) != 2)
91 return "invalid header";
92 msg (1, "p cnf %d %d", nvars
, nclauses
);
93 clauses
= calloc (nclauses
, sizeof *clauses
);
97 if (ch
== ' ' || ch
== '\n' || ch
== '\t' || ch
== '\r') goto LIT
;
99 while ((ch
= next ()) != '\n')
100 if (ch
== EOF
) return "EOF after 'c'";
104 if (lit
) return "zero missing";
105 if (n
< nclauses
) return "clauses missing";
108 if (n
== nclauses
) return "too many clauses";
112 if (!isdigit (ch
)) return "expected digit after '-'";
114 if (!isdigit (ch
)) return "expected digit";
116 while (isdigit (ch
= next ()))
117 lit
= 10 * lit
+ (ch
- '0');
118 if (lit
> nvars
) return "maximum variable index exceeded";
121 if (nlits
== szlits
) {
122 szlits
= szlits
? 2 * szlits
: 1;
123 lits
= realloc (lits
, szlits
* sizeof *lits
);
128 c
->lits
= malloc ((nlits
+ 1) * sizeof *c
->lits
);
129 for (i
= 0; i
< nlits
; i
++)
130 c
->lits
[i
] = lits
[i
];
137 static void die (const char * fmt
, ...) {
139 fputs ("*** picomus: ", stdout
);
141 vfprintf (stdout
, fmt
, ap
);
143 fputc ('\n', stdout
);
148 static double percent (double a
, double b
) { return b
?100.0*a
/b
:0.0; }
150 static void callback (void * dummy
, const int * mus
) {
154 if (verbose
<= 0) return;
156 for (p
= mus
; *p
; p
++) remaining
++;
157 assert (remaining
<= nclauses
);
159 msg (1, "reduction %d to %d = %.0f%% out of %d after %.1f sec",
161 remaining
, percent (remaining
, nclauses
), nclauses
,
162 picosat_time_stamp () - start
);
165 static const char * USAGE
=
166 "picomus [-h][-v][-q] [ <input> [ <output> ] ]\n"
168 " -h print this command line option summary\n"
169 " -v increase verbosity level (default 0 = no messages)\n"
170 " -q be quiet (no warnings nor messages)\n"
172 "This tool is a SAT solver that uses the PicoSAT library to\n"
173 "generate a 'minimal unsatisfiable core' also known as 'minimal\n"
174 "unsatisfiable set' (MUS) of a CNF in DIMACS format.\n"
176 "Both file arguments can be \"-\" and then denote <stdin> and\n"
177 "<stdout> respectively. If no input file is given <stdin> is used.\n"
178 "If no output file is specified the MUS is computed and only printed\n"
179 "to <stdout> in the format of the SAT competition 2011 MUS track.\n"
181 "Note, that the 's ...' lines and in case the instance is satisfiable\n"
182 "also the 'v ...' lines for the satisfying assignment are always\n"
183 "printed to <stdout> (or not printed at all with '-q').\n"
185 "If '-n' is specified satisfying assignment and MUS printing\n"
186 "on <stdout> (using the 'v ...' format) is suppressed.\n"
187 "The 's ...' line is still printed unless '-q' is specified.\n"
188 "If <output> is specified an MUS is written to this file,\n"
189 "even if '-n' or '-q' is used.\n"
192 "WARNING: PicosSAT is compiled without trace support.\n"
194 "This typically slows down this MUS extractor, since\n"
195 "it only relies on clause selector variables and\n"
196 "can not make use of core extraction. To enable\n"
197 "trace generation use './configure.sh --trace' or\n"
198 "'./configure.sh -O --trace' when building PicoSAT.\n"
200 "Since trace generation code is included, this binary\n"
201 "uses also core extraction in addition to clause selector\n"
206 int main (int argc
, char ** argv
) {
207 int i
, * p
, n
, oldn
, red
, nonred
, res
, round
, printed
, len
;
215 start
= picosat_time_stamp ();
216 for (i
= 1; i
< argc
; i
++) {
217 if (!strcmp (argv
[i
], "-h")) {
218 fputs (USAGE
, stdout
);
220 } else if (!strcmp (argv
[i
], "-v")) {
221 if (verbose
< 0) die ("'-v' option after '-q'");
223 } else if (!strcmp (argv
[i
], "-q")) {
224 if (verbose
< 0) die ("two '-q' options");
225 if (verbose
> 0) die ("'-q' option after '-v'");
227 } else if (!strcmp (argv
[i
], "-n")) nowitness
= 1;
228 else if (argv
[i
][0] == '-' && argv
[i
][1])
229 die ("invalid command line option '%s'", argv
[i
]);
230 else if (output_name
) die ("too many arguments");
231 else if (!input_name
) input_name
= argv
[i
];
232 else output_name
= argv
[i
];
234 if (!output_name
) warn ("no output file given");
235 if (input_name
&& strcmp (input_name
, "-")) {
236 len
= strlen (input_name
);
237 if (len
>= 3 && !strcmp (input_name
+ len
- 3, ".gz")) {
238 cmd
= malloc (len
+ 20);
239 sprintf (cmd
, "gunzip -c %s 2>/dev/null", input_name
);
240 input_file
= popen (cmd
, "r");
243 } else input_file
= fopen (input_name
, "r"), fclose_input
= 1;
244 if (!input_file
) die ("can not read '%s'", input_name
);
245 } else input_file
= stdin
, input_name
= "-";
246 if ((err
= parse ())) {
247 fprintf (stdout
, "%s:%d: %s\n", input_name
, lineno
, err
);
251 if (fclose_input
) fclose (input_file
);
252 if (pclose_input
) pclose (input_file
);
253 ps
= picosat_init ();
254 picosat_set_prefix (ps
, "c [picosat] ");
255 picosat_set_output (ps
, stdout
);
256 if (verbose
> 1) picosat_set_verbosity (ps
, verbose
- 1);
258 if (!picosat_enable_trace_generation (ps
))
259 warn ("PicoSAT compiled without trace generation"),
260 warn ("core extraction disabled");
264 for (round
= 1; round
<= MAXCOREROUNDS
; round
++) {
266 msg (1, "starting core extraction round %d", round
);
267 picosat_set_seed (ps
, round
);
268 for (i
= 0; i
< nclauses
; i
++) {
272 picosat_add (ps
, -1);
274 for (p
= c
->lits
; *p
; p
++)
275 picosat_add (ps
, *p
);
283 res
= picosat_sat (ps
, -1);
284 if (res
== 10) { assert (round
== 1); goto SAT
; }
290 printf ("s UNSATISFIABLE\n"),
293 for (i
= 0; i
< nclauses
; i
++) {
295 if (c
->red
) { assert (!picosat_coreclause (ps
, i
)); continue; }
296 if (picosat_coreclause (ps
, i
)) continue;
301 for (i
= 0; i
< nclauses
; i
++) if (!clauses
[i
].red
) n
++;
302 msg (1, "extracted core %d of size %d = %0.f%% out of %d after %.1f sec",
303 round
, n
, percent (n
, nclauses
), nclauses
,
304 picosat_time_stamp () - start
);
307 ps
= picosat_init ();
308 picosat_set_prefix (ps
, "c [picosat] ");
309 picosat_set_output (ps
, stdout
);
310 if (round
>= MINCOREROUNDS
) {
312 if (red
< 10 && (100*red
+ 99)/oldn
< 2) {
314 if (nonred
> MAXNONREDROUNDS
) break;
317 if (round
< MAXCOREROUNDS
) picosat_enable_trace_generation (ps
);
320 for (i
= 0; i
< nclauses
; i
++) {
324 picosat_add (ps
, -1);
332 c
->lit
= nvars
+ i
+ 1;
333 picosat_add (ps
, -c
->lit
);
334 for (p
= c
->lits
; *p
; p
++)
335 (void) picosat_add (ps
, *p
);
342 for (i
= 0; i
< nclauses
; i
++) {
344 if (c
->red
) continue;
345 picosat_assume (ps
, c
->lit
);
347 res
= picosat_sat (ps
, -1);
349 if (!printed
&& verbose
>= 0)
350 printf ("s UNSATISFIABLE\n"), fflush (stdout
);
351 for (i
= 0; i
< nclauses
; i
++) clauses
[i
].red
= 1;
352 q
= picosat_mus_assumptions (ps
, 0, callback
, 1);
355 assert (0 <= i
&& i
< nclauses
);
361 if (!printed
&& verbose
>= 0)
362 printf ("s SATISFIABLE\n"), fflush (stdout
);
363 if (!nowitness
&& verbose
>= 0) {
364 for (i
= 1; i
<= nvars
; i
++)
365 printf ("v %d\n", ((picosat_deref (ps
, i
) < 0) ? -1 : 1) * i
);
369 if (verbose
> 0) picosat_stats (ps
);
372 for (i
= 0; i
< nclauses
; i
++) if (!clauses
[i
].red
) n
++;
374 msg (1, "found %d redundant clauses %.0f%%", red
, percent (red
, nclauses
));
376 msg (0, "computed MUS of size %d out of %d (%.0f%%)",
377 n
, nclauses
, percent (n
, nclauses
));
378 if (output_name
&& strcmp (output_name
, "-")) {
379 output_file
= fopen (output_name
, "w");
380 if (!output_file
) die ("can not write '%s'", output_name
);
382 } else if (output_name
&& !strcmp (output_name
, "-")) output_file
= stdout
;
384 fprintf (output_file
, "p cnf %d %d\n", nvars
, n
);
385 for (i
= 0; i
< nclauses
; i
++)
386 if (!clauses
[i
].red
) {
387 for (p
= clauses
[i
].lits
; *p
; p
++) fprintf (output_file
, "%d ", *p
);
388 fprintf (output_file
, "0\n");
390 if (close_output
) fclose (output_file
);
393 if (!nowitness
&& verbose
>= 0) {
394 for (i
= 0; i
< nclauses
; i
++)
395 if (!clauses
[i
].red
) printf ("v %d\n", i
+1);
399 msg (1, "%s %d irredundant clauses %.0f%%",
400 output_file
? "printed" : "computed", n
, percent (n
, nclauses
));
401 for (i
= 0; i
< nclauses
; i
++) free (clauses
[i
].lits
);
404 msg (1, "%d reductions in %.1f seconds",
405 reductions
, picosat_time_stamp () - start
);