shithub: riscv

ref: dfca0be219074f8473683256b1b9ca986ec37c43
dir: /sys/man/2/sat/

View raw version
.TH SAT 2
.SH NAME
satnew, satadd1, sataddv, satrange1, satrangev, satsolve, satmore, satval, satreset, satfree \- boolean satisfiability (SAT) solver
.SH SYNOPSIS
.de PB
.PP
.ft L
.nf
..
.PB
#include <u.h>
#include <libc.h>
#include <sat.h>
.PB
struct SATParam {
	void (*errfun)(char *msg, void *erraux);
	void *erraux;
	long (*randfn)(void *randaux);
	void *randaux;
	/* + finetuning parameters, see sat.h */
};
.PB
struct SATSolve {
	SATParam;
	/* + internals */
};
.PB
.ta +\w'\fLSATSolve* \fP'u
SATSolve*	satnew(void);
void	satfree(SATSolve *s);
SATSolve*	satadd1(SATSolve *s, int *lit, int nlit);
SATSolve*	sataddv(SATSolve *s, ...);
SATSolve*	satrange1(SATSolve *s, int *lit, int nlit,
			int min, int max);
SATSolve*	satrangev(SATSolve *s, int min, int max, ...);
int	satsolve(SATSolve *s);
int	satmore(SATSolve *s);
int	satval(SATSolve *s, int lit);
int	satget(SATSolve *s, int i, int *lit, int nlit);
void	satreset(SATSolve *s);
.SH DESCRIPTION
.PP
.I Libsat
is a solver for the boolean satisfiability problem, i.e. given a boolean formula it will either find an assignment to the variables that makes it true, or report that this is impossible.
The input formula must be in conjunctive normal form (CNF), i.e. of the form
.IP
.if t (x\d\s71\s10\u ∨ x\d\s72\s10\u ∨ x\d\s73\s10\u ∨ …) ∧ (y\d\s71\s10\u ∨ y\d\s72\s10\u ∨ y \d\s73\s10\u ∨ …) ∧ …,
.if n (x1 ∨ x2 ∨ x3 ∨ ...) ∧ (y1 ∨ y2 ∨ y3 ∨ ...) ∧ ...,
.PP
where each
.if t x\d\s7i\s10\u or y\d\s7i\s10\u
.if n x_i or y_i
can optionally be negated.
.PP
For example, consider
.IP
.if t (x\d\s71\s10\u ∨ x\d\s72\s10\u ∨ x\d\s73\s10\u) ∧ (¬x\d\s71\s10\u ∨ ¬x\d\s72\s10\u) ∧ (¬x\d\s72\s10\u ∨ ¬x\d\s73\s10\u) ∧ (¬x\d\s71\s10\u ∨ ¬x\d\s73\s10\u).
.if n (x1 ∨ x2 ∨ x3) ∧ (¬x1 ∨ ¬x2) ∧ (¬x2 ∨ ¬x3) ∧ (¬x1 ∨ ¬x3).
.PP
This formula encodes the constraint that exactly one of the three variables be true. To represent this as input for
.I libsat
we assign positive integers to each variable.
Negation is represented by the corresponding negative number, hence our example corresponds to the set of "clauses"
.IP
1, 2, 3
.br
-1, -2
.br
-1, -3
.br
-2, -3
.PP
To actually solve this problem we would create a
.B SATSolve
structure and add clauses one by one using
.I satadd1
or
.I sataddv
(the former takes an
.B int
array, the latter a variadic list terminated by 0).
The
.B SATSolve
is modified inplace but returned for convenience.
Passing
.B nil
as a first argument will create and return a new structure.
Alternatively,
.I satnew
will create an empty structure.
.PP
Once clauses have been added,
.I satsolve
will invoke the actual solver.
It returns 1 if it found an assignment and 0 if there is no assignment (the formula is unsatisfiable).
If an assignment has been found, further clauses can be added to constrain it further and
.I satsolve
rerun.
.I Satmore
performs this automatically, excluding the current values of the variables.
It is equivalent to
.I satsolve
if no variables have assigned values.
.PP
Once a solution has been found,
.I satval
returns the value of literal
.I lit.
It returns 1 for true, 0 for false and -1 for undetermined.
If the formula is satisfiable, an undetermined variable is one where either value will satisfy the formula.
If the formula is unsatisfiable, all variables are undetermined.
.PP
.I Satrange1
and
.I satrangev
function like their
.I satadd
brethren but rather than adding a single clause they add multiple clauses corresponding to the constraint that at least
.I min
and at most
.I max
literals from the provided array be true.
For example, the clause from above corresponds to
.IP
.B "satrangev(s, 1, 1, 1, 2, 3, 0);"
.PP
For debugging purposes, clauses can be retrieved using
.IR satget .
It stores the literals of the clause with index
.I i
(starting from 0) at location
.IR lit .
If there are more than
.I nlit
literals, only the first
.I nlit
literals are stored.
If it was successful, it returns the total number of literals in the clause (which may exceed
.IR nlit ).
Otherwise (if
.I idx
was out of bounds) it returns -1.
.PP
.I Satreset
resets all solver state, deleting all learned clauses and variable assignments.
It retains all user provided clauses.
.I Satfree
deletes a solver structure and frees all associated storage.
.PP
There are a number of user-adjustable parameters in the 
.B SATParam
structure embedded in
.BR SATSolve .
.I Randfun
is called with argument
.I randaux
to generate random numbers between 0 and
.if t 2\u\s731\s10\d-1;
.if n 2^31-1;
it defaults to
.I lrand
(see
.IR rand (2)).
.I Errfun
is called on fatal errors (see DIAGNOSTICS).
Additionally, a number of finetuning parameters are defined in
.BR sat.h .
By tweaking their values, the run-time for a given problem can be reduced.
.SH EXAMPLE
Find all solutions to the example clause from above:
.PB
.ta .5i 1i 1.5i
	SATSolve *s;
	
	s = nil;
	s = sataddv(s, 1, 2, 3, 0);
	s = sataddv(s, -1, -2, 0);
	s = sataddv(s, -1, -3, 0);
	s = sataddv(s, -2, -3, 0);
	while(satmore(s) > 0)
		print("x1=%d x2=%d x3=%d\\n",
			satval(s, 1),
			satval(s, 2),
			satval(s, 3));
	satfree(s);
.SH SOURCE
.B /sys/src/libsat
.SH "SEE ALSO"
Donald Knuth, ``The Art of Computer Programming'', Volume 4, Fascicle 6.
.SH DIAGNOSTICS
.I Satnew
returns
.B nil
on certain fatal error conditions (such as
.IR malloc (2)
failure).
Other routines will call
.I errfun
with an error string and
.IR erraux .
If no
.I errfun
is provided or if it returns,
.I sysfatal
(see
.IR perror (2))
is called.
It is permissible to use
.IR setjmp (2)
to return from an error condition.
Call
.I satfree
to clean up the
.B SATSolve
structure in this case.
Note that calling the
.I satadd
or
.I satrange
routines with
.B nil
first argument will invoke
.I sysfatal
on error, since no
.I errfun
has been defined yet.
.SH BUGS
Variable numbers should be consecutive numbers starting from 1, since variable data is kept in arrays internally.
.PP
Large clauses of several thousand literals are probably inefficient and should be split up using auxiliary variables.
Very large clauses exceeding about 16,000 literals will not work at all.
.PP
There is no way to remove clauses (since it's unclear what the semantics should be).
.PP
The details about the tuning parameters are subject to change.
.PP
Calling
.I satadd
or
.I satrange
after 
.I satsolve
or
.I satmore
may reset variable values.
.PP
.I Satmore
will always return 1 when there are no assigned variables in the solution.
.P
Some debugging routines called under "shouldn't happen" conditions are non-reentrant.
.SH HISTORY
.I Libsat
first appeared in 9front in March, 2018.