Justus Polzin

Justus Polzin

Mixed Boolean-Arithmetic (Part 1.5): The Fundamental Theorem of Mixed Boolean-Arithmetic

This part was originally an appendix to Part 1. We will treat MBA very formally and prove the "Fundamental Theorem". I don't recommend reading this part, unless you

  • want to see the proof
  • are trying to implement this and are confused about details (though you could just read my implementation)

It is totally expected for you to not understand all the details from Part 1 and I didn't even show the details as they require formality (in my opinion). If all you took away from last part is that we can find a linear combination of bitwise functions, that equal some given bitwise function, exponentially faster than with general functions, then I did my job and you can skip this part.

In my opinion, this is one of those times where being formal really pays off and helps organize everything. It also allows us to clarify some things that confused me when I first learned about MBA, which I will talk about at the end.

Definitions

Let be the ring of integers mod 2. Let be the integer width, i.e. the number of bits in an integer. Let be the ring of integers mod , i.e. the -bit integers. To be completely formally correct, we will define the function which basically lets us interpret an element of as an element of . This helps to avoid some confusion, e.g. (for ).

Let be the ring of -sequences of length , i.e. vectors/lists of bits. Note that although and have the same number of elements, they are not isomorphic (for ), since their additive groups (and multiplicative monoids) are not isomorphic. Addition in is just integer addition mod , whereas in addition is the XOR operation. (Multiplication in is the logical AND). However, we can define the obvious bijection between them: This is just interpreting the sequence of bits as the binary representation of an integer, e.g. for , or . It is not a ring homomorphism.

Extending Boolean Functions to the Integers

allows us to extend an -ary function to a function . The notation is slightly confusing here (though it is completely consistent). A function should be thought of as taking inputs in and mapping them to a value in , instead of a function taking a single sequence of bits, although both views are of course equivalent. The idea of this extension is that the function will be separately applied to each bit. These are the bitwise functions that were informally introduced in the post. For example the logical and is a binary (2-ary, i.e. it takes two inputs) function on bits (i.e. elements of ). E.g. . We want to extend this to a function on -bit integers, e.g. . The extension will be in two steps.

The extension to is defined as This definition looks more confusing than it actually is. It just means we apply to the first bit of each input and the result will be the first bit of the output and so on. The extension to is then defined as Again, this simply means converting each integer into its binary representation, applying to the sequence, which means applying to each bit, and converting the resulting sequence to an integer again. Using our example on 2-bit integers: We can now formally define the meaning of bitwise functions. A function is called a bitwise function, if there exists an such that .

More importantly, we are now ready to formally define linear MBA functions.

Definition: Linear Mixed Boolean-Arithmetic Function
A function is called a linear mixed boolean-arithmetic function, if it can written in the form for some constants and some functions and . In other words, it is a linear combination of bitwise functions.

It is time for the most important theorem.

Proof of the "Fundamental Theorem"

Theorem: The Fundamental Theorem of Mixed Boolean-Arithmetic
A linear MBA function is uniquely determined by its values on (or ). In other words, if we know the values of on , then we know the value of everywhere.

To give a concrete example of how this is useful. Suppose we are given two linear MBA functions . We only have to check the following equalities to conclude they are equal everywhere: , , , . A priori, we have to compare the values for all inputs. This is what allows us to reduce the size of the linear system when generating linear MBA expressions.

Proof: Note that 0 and -1 are the numbers that only have 0 or 1 in their binary representation. Thus an input has the form that , i.e. each input number consists only of the same bit . We will define to be the -bit integer whose binary representation is just s. The notation is sort of overloaded because could also be interpreted as , but we will need it just for this proof and we will never use the other interpretation.

So for inputs of that form, the result is just minus one times the sum of those where . We will need this result in a second.

We will now consider an arbitrary input, i.e. .

The last equality is the previous result. This means that the value of on any input depends only on the value of at inputs of the form discussed before.

In the original paper on MBA, the set was used. (Kinda, we will discuss this in a bit). Let us quickly prove that the values of the function on this set also uniquely determine the function. This idea is the same, the algebra only slightly more annoying. The inputs now have the form . The last equality stems from the fact that we view as a subset of .

Overall, we have

Now we will consider general inputs again, i.e. . As we saw before So again, at any input only depends on the values of with 0, 1 inputs.

Things that confused me

We now have the language to clarify some things that were confusing to me when I first started learning about MBA.

The linear system

The first thing is how to construct the matrix/table, because a lot of papers do it differently. There are basically three different ways, which we will compare with an example. Let's rewrite x + y using !x, !y, x & y and !(x | y) on 2-bit numbers.

The way I view the table is as a restriction of the full table, which just lists the values of all expressions for all inputs. Even for 2-bit numbers it has 16 rows (for 32-bit numbers it would have rows), so I have omitted some rows, but here is what it would look like.

x y !x !y x & y !(x | y) x + y
0 0 3 3 0 3 0
0 1 3 2 0 2 1
0 2 3 1 0 1 2
.. .. .. .. .. .. ..
3 3 0 0 3 0 2

These tables work for any functions, i.e. also x * y, because all inputs are present. (That is, if we find a linear combination of some columns that equals another column, then we made sure that the linear combination always equals the target for all inputs). It is not easy to see from this table, but I can tell you that x + y == !x + !y + 2 * (x & y) - 2 * !(x | y).

But if we restrict the operations to be bitwise expressions, then the fundamental theorem tells us that we only need to consider the inputs 0/-1 (or 0/1), because equality everywhere is guaranteed.

Restricting the table two the inputs 0/1, we get this:

x y !x !y x & y !(x | y) x + y
0 0 3 3 0 3 0
0 1 3 2 0 2 1
1 0 2 3 0 2 1
1 1 2 2 1 2 2

To me this is a bit ugly, because e.g. , so if we had 8-bit numbers it would be 254. But this is (of course) correct and ultimately does not matter. In the example from Part 1, I intentionally chose rewrite operations without negation so we would not run into this.

I prefer and implement the method using the inputs 0/-1. The reason this is more beautiful is that all the bits have the same value. To illustrate, let me write 3 = -1 in the table.

x y !x !y x & y !(x | y) x + y
0 0 -1 -1 0 -1 0
0 -1 -1 0 0 0 -1
-1 0 0 -1 0 0 -1
-1 -1 0 0 -1 0 -2

We will call this the uniform table. This is not the version you will see in most papers (including the original one). They evaluate all the bitwise expression as if they were 1 bit or boolean expressions.

x y !x !y x & y !(x | y) x + y
0 0 1 1 0 1 0
0 1 1 0 0 0 1
1 0 0 1 0 0 1
1 1 0 0 1 0 2

We will call this the truth table. This looks very cool, but it is not immediately clear why this should work. It is not like the other two, in that we don't just restrict the full table and prove that the restricted table is enough. The reason this truth table works is simple if you look at the uniform table. It is just the negated table, so we are essentially solving , which, of course, has the same solutions. So while the table looks the best, it is conceptually a bit less clear (in my opinion) and the implementation perhaps not as nice.

The Number 1

We already encountered this problem when obfuscating constants in Part 1. Let's not be unnecessarily general and restrict ourselves to two variables. We will call the constant 1 function (on 1 bit) : Extending this function to the -bit integers , we just apply the function at each bit, so the result will be a 1 in each bit which is the constant (or ): If we write for the constant function that returns -1, then we get , which is ultimately the root of all confusion and sign errors.

So suppose we want to obfuscate the constant 1 with the rewrite operations x ^ y and !(x ^ y). We will use the truth table because it looks the cutest.

x y x ^ y !(x ^ y) 1
0 0 0 1 1
0 1 1 0 1
1 0 1 0 1
1 1 0 1 1

And we read off the solution (x ^ y) + !(x ^ y) == 1, right? This actually isn't correct, can you see why? We actually have (x ^ y) + !(x ^ y) == -1. The problem is that we found a linear combination such that

To avoid confusion, the function should be called something else. In my implementation, I called it Ones to suggest that there is a 1 in each bit, but you could also call it -1, because it returns the constant -1 for any integer width. Then the table would look the same except for the name of the function and produce the correct linear combination (for -1). (The table before also produced the correct linear combination, but we made the mistake when reading off the solution.)

x y x ^ y !(x ^ y) -1
0 0 0 1 1
0 1 1 0 1
1 0 1 0 1
1 1 0 1 1

Should we want to obfuscate any constant , we just multiply by , e.g. for

x y x ^ y !(x ^ y) -42 * (-1)
0 0 0 1 -42
0 1 1 0 -42
1 0 1 0 -42
1 1 0 1 -42

In my opinion this looks much more natural in the uniform table:

x y x ^ y !(x ^ y) -42 * (-1)
0 0 0 -1 42
0 -1 -1 0 42
-1 0 -1 0 42
-1 -1 0 -1 42

I am repeating myself, but in this table all the columns contain values of the expressions that would actually be computed during execution. Since we want the result to be 42, the last column has to be 42.