Author : R. Scott McIntire
Version: 1.0
Overview:
DESCRIPTION: This file contains a Boolean solver that allows a user
to find a non-zero value (if it exists) of
a Boolean expression of the form f(x1,x2,...,xn).
Such a solver can be used, for instance, to
determine if two Boolean functions, f and g, are the
same by seeking to find a solution
of the Boolean function f ^ g (that is f XOR g).
The solver and related functions use a representation of
the function f as a "term list" which represents a Boolean
function as an XOR sum of terms, where each term is a
vector which represents the AND'ing of
variables, their negation, or 1. This representation is a
non-canonical one which, like a Boolean
expression, can be a relatively compact way to represent a
Boolean function. However, without
a canonical form, we must use simplification to compare
a given function with the zero function.
WORKING WITH THE REPRESENTATION:
Example of the representation: The "term list"
'(#(1 0 2) #(0 1 2)) represents the Boolean
function (X1 * X2') ^ (X1' * X2). We use the
following notation: "*" means AND ; "^" means XOR;
"'" appended to a variable, X, means NOT X; ~ <expression>
means NOT <expression>; and "+" means OR.
The expression is interpreted by XORing each Boolean
expression derived from each term. Each term is decoded
by doing the following: For each index in the array
(1 based index), examine the number at that index.
If the number is 1 take the corresponding variable and
AND it with with the result so far. If the number is 0
take the corresponding variable, negate it, and AND it
with the result so far.
Finally, if the number is 2, AND 1 with the result so far.
Example: So, for the term #(1 0 2), take the first
element of the array (at index 1) which is 1.
Take the variable X1 and AND it with the next.
The next is X2 negated since the second element of
the array is 0 and we have X1 * X2'. Finally, with an
index of 3, the value of the array is 2 which means we
AND X1 * X2' with 1. The result is: X1 * X2'.
Example: The "term list" '(#(1 0 2) #(0 1 2)) is
interpreted as "X1 * X2' ^ X1' * X2".
NOTE: Boolean expressions here and in the code are
interpreted with the following choice of
operator symbols and their precedence:
NOT -> ~ or ', AND -> *, XOR -> ^, OR -> +.
Therefore, X1 + X2*X3 means to AND X2 with X3 and
then OR that with X1.
ANOTHER INTERPRETATION OF THE REPRESENTATION:
Boolean functions of n variables can be represented by
bit-strings of length 2^n.
This representation is canonical but grows exponentially.
However, it is possible to
compactly represent certain Boolean functions as a
"tensor product" of binary pairs; and,
represent an arbitrary Boolean function as an XOR
sum of such terms.
One can translate a "term" in a "term list" to a
bit-string that represents the Boolean function
of that "term". This can be done as follows:
Interpret each of the 0, 1, 2 in the term array as
K, H, E respectively, where K = #(1 0), H = #(0 1),
E = #(1 1). Now take the tensor product of
each of these K, H, and E's. Example: #(1 0 2)
translates to H x K x E (here x means tensor product).
That is, #(0 1) x #(1 0) x #(1 1) = #(0 1 0 0) x #(1 1)
= #(0 1 0 0 0 1 0 0).
#(0 1 0 0 0 1 0 0) is the bit string representation of
the Boolean function of three variables
represented by #(1 0 2) namely, X1*X2'.
The bit-string is actually a listing of the values of the
truth table of the Boolean function X1*X2':
X1 X2 X3 -> X1*X2'
0 0 0 0
1 0 0 1
0 1 0 0
1 1 0 0
0 0 1 0
1 0 1 1
0 1 1 0
1 1 1 0
Notice that the values under X1*X2' in the table read top
to bottom are the same as the array #(0 1 0 0 0 1 0 0).
In fact, one can show that the functions X1, X2, X3
can be represented as H X E x E, E x H x E, and E X E X H
respectively. This view of Boolean functions as bit-strings
motivated the algorithms below.
NOTE: When there is a small number of variables,
a bit-string representation is used rather than the
"tensor product" term list representation. In the
functions below the bit-string representation is
used if a Boolean function uses 12 or fewer variables.
SYMMETRY: If a Boolean function of n variables is completely
symmetric, it can be determined
to be the zero function in at most n+1 evaluations.
The solver function is-function-non-zero? takes
advantage of symmetry information, as it finds it,
to simplify the complexity of the representation.
Export Summary:
is-function-non-zero? is a low-level function that is used
to find a non-zero value of a Boolean function.
It is low-level in the sense that it takes as input a
"term list" (defined above).
The return value is NIL, if the function *is* the
zero function; otherwise, it returns a vector of zero's
and one's that indicate the values that each variable should
take in order to get a non-zero value from the
Boolean function.
Example: (is-function-non-zero?
'(#(0 1 0 2) #(0 1 2 0) #(0 1 0 1) #(0 1 1 0)) 4)
nil
This means this example has no solution.
Example: (is-function-non-zero?
'(#(2 2 1 2 1 0 1 2 0 1 1 1)
#(2 2 1 0 1 1 0 1 2 2 1 1)
#(2 2 2 2 1 1 0 2 1 1 1 2)) 12)
#(1 1 1 1 1 1 0 1 1 1 1 1)
This means that setting each variable to the successive
values in the array #(1 1 1 1 1 1 0 1 1 1 1 1) and
evaluating the Boolean function represented by the term
list will yield 1.
bool-solve is higher level version of is-function-non-zero?
but takes as input an ordinary Boolean expression.
Example: (bool-solve "X1 + X2*X3")
#(0 1 1)
This means that one way to get a non-zero value of the
expression "X1 + X2*X3" is to
evaluate this function with X1=0, X2=1, and X3=1.
Example: (bool-solve "(X1' * X2 + X1 * X2') ^
( (X1 + X2) * ( X1' + X2') )")
NIL
This means that there is no input that will yield a non-zero
value; that is, the expression is really the zero function.
bool-solve-from-file is like bool-solve but reads a
Boolean expression from a file.
Example: (bool-solve-from-file file-name)
If file-name is a file with contents X1 + X2*X3, this
function will return #(0 1 1)
rpn-eqn-tree->term-list takes an RPN Boolean expression
(a tree) and converts it to a tree of "terms".
Example: (rpn-eqn-tree->term-list '(+ x1 x2 (* x3 x4) x5) 5)
(+ #(1 2 2 2 2) #(2 1 2 2 2) (* #(2 2 1 2 2)
#(2 2 2 1 2)) #(2 2 2 2 1))
convert-tree->xor takes an RPN Boolean
equation (a tree) of "terms" and converts it to an
XOR "term list".
Example: (convert-tree->xor
'(+ #(1 1 2) #(0 2 1) (* #(1 2 1) #(2 0 2))) 3)
(#(0 2 1) #(1 1 2) #(1 0 1)
formula->rep takes a Boolean expression and converts it
to a "term list". The user need not worry about inserting
white space between operators or variables.
The function will also determine the number of variables.
Example: (formula->rep "X1 + X3")
(#(1 2 2) #(0 2 1))
rep-from-file reads a Boolean expression from a file and
converts it to a "term list".
The user need not worry about inserting white space
between operators or variables.
The function will also determine the number of variables.
list->eqn takes a "term list" and converts it to a
Boolean expression.
Example: (list->eqn '(#(1 0 2) #(0 1 2)))
X1*X2' ^ X1'*X2
NOTE: In the code below we use the word "orthogonal".
By the phrase "term t1 is orthogonal to term t2"
we mean that t1 ANDed with t2 is zero.
In bit-string terms, this means that the "dot-product"
of the two bit-strings is zero.
|
bool-solve  (eqn)Find an input value such that equation, <eqn>, yields the value 1.
Example: (rsm.bool-comp:bool-solve "X1 + X2*X3")
#(0 1 1)
That is, if X1=0, X2=1, and X3=1, then "X1 + X2*X3" is 1.
Assumes that a single variable name is used - in the above case, x.
bool-solve-from-file  (file-name)Find an input value such that the equation found in the file, <file-name>,
yeilds the value 1.
Example: (rsm.bool-comp:bool-solve-from-file "formula.lsp")
#(0 0 0 0 0)
In this case, if formula.lsp contains the text
(x1 + x2 + x3 + x4 + x5) ^
(x1' + x2' + x3' + x4')
then letting x1=x2=x3=x4=0 in the formula represented by the text yields 1.
Assumes that a single variable name is used - in the above case, x.
convert-tree->xor  (tree size)Convert an RPN tree of logical terms (representing a function) to
an XOR term list.
Example: (rsm.bool-comp:convert-tree->xor '(#.XOR (#.XOR #(2 1) #(1 2))
(#.XOR #(1 2) #(1 1))) 2)
(#(2 1) #(1 2) #(1 2) #(1 1))
Example: (rsm.bool-comp:convert-tree->xor '(#.OR (#.XOR #(2 1) #(1 2))
(#.XOR #(1 2) #(1 1))) 2)
(#(2 1) #(1 2))
formula->rep  (formula)Convert an algebraic expression, <formula>, into the boolean tensor
representation.
Example: (rsm.bool-comp:formula->rep "X1 + X3")
(#(1 2 2) #(0 2 1))
Assumes that a single variable name is used - in the above case, x.
is-function-non-zero?  (tl size)Predicate: Determines if the function represented by the "term list", <tl>,
is non-zero. Returns a value that generates a non-zero result if true;
otherwise, this function is the zero function and nil is returned.
Assumes that each member of <tl> has size <size>.
STRATEGY: - Try to find a non-zero value as though the function,
<tl>, is a symmetric function.
This involves n+1 (n - the number of variables) evaluations.
- If this fails then if there are 12 or fewer variables use
the bit-string representation to find the answer.
OTHERWISE:
- simplify <tl>.
- Try to show symmetry. If <tl> is completely symmetric,
then since we've already tested for a non-zero value for
symmetric functions, <tl> must be the zero function. Otherwise,
find the first index i (and projection) where swapping the
variable x1 with xi changes the function, <tl>; that is, we at
least can't easily show that it leaves <tl> the same.
- In the last step we get back (in the case of non-symmetry)
a sym-diff flipped version of <tl>.
Two cases: the flipped version is the same as <tl>, or it
is different. In either case, we recurse and move to the next
index which is not symmetric - or finish.
list->eqn  (tl)Prints a familiar algebraic representation of an XOR term list.
Example: (rsm.bool-comp:list->eqn '(#(1 2 0) #(1 1 1)))
X1*X3' ^ X1*X2*X3
rep-from-file  (file-name)Convert an algebraic expression contained in file, <file-name>, w
into the boolean tensor representation.
Example: (rsm.bool-comp:rep-from-file "formula.lsp")
(#(1 2 2) #(0 2 1))
The example assumes that the file formula.lsp contains the text: X1 + X3.
This function assumes that a single variable name is used - in the
above case, x. |