From Wikipedia I knew a CNF formula for 2-input XOR: \( (\overline{A} \vee \overline{B} \vee C) \wedge (A \vee B \vee C) \wedge (A \vee \overline{B} \vee \overline{C}) \wedge (\overline{A} \vee B \vee \overline{C}) \)
I wanted to generalize it to multi-input XOR. Let's first try to use Wolfram Mathematica's CNF generator. What it will say for 2/3/4/5-input XOR functions?
In[65]:= tbl2=BooleanTable[{a,b,output}->Xor[a,b]==output,{a,b,output}]; In[66]:= BooleanConvert[BooleanFunction[tbl2,{a,b,output}],"CNF"] Out[66]= (! a || ! b || ! output) && (! a || b || output) && ( a || ! b || output) && ( a || b || ! output) In[67]:= tbl3=BooleanTable[{a,b,c,output}->Xor[a,b,c]==output,{a,b,c,output}]; In[68]:= BooleanConvert[BooleanFunction[tbl3,{a,b,c,output}],"CNF"] Out[68]= (! a || ! b || ! c || output) && (! a || ! b || c || ! output) && (! a || b || ! c || ! output) && (! a || b || c || output) && ( a || ! b || ! c || ! output) && ( a || ! b || c || output) && ( a || b || ! c || output) && ( a || b || c || ! output) In[69]:= tbl4=BooleanTable[{a,b,c,d,output}->Xor[a,b,c,d]==output,{a,b,c,d,output}]; In[71]:= BooleanConvert[BooleanFunction[tbl4,{a,b,c,d,output}],"CNF"] Out[71]= (! a || ! b || ! c || ! d || ! output) && (! a || ! b || ! c || d || output) && (! a || ! b || c || ! d || output) && (! a || ! b || c || d || ! output) && (! a || b || ! c || ! d || output) && (! a || b || ! c || d || ! output) && (! a || b || c || ! d || ! output) && (! a || b || c || d || output) && ( a || ! b || ! c || ! d || output) && ( a || ! b || ! c || d || ! output) && ( a || ! b || c || ! d || ! output) && ( a || ! b || c || d || output) && ( a || b || ! c || ! d || ! output) && ( a || b || ! c || d || output) && ( a || b || c || ! d || output) && ( a || b || c || d || ! output) In[72]:= tbl5=BooleanTable[{a,b,c,d,e,output}->Xor[a,b,c,d,e]==output,{a,b,c,d,e,output}]; In[73]:= BooleanConvert[BooleanFunction[tbl5,{a,b,c,d,e,output}],"CNF"] Out[73]= (! a || ! b || ! c || ! d || ! e || output) && (! a || ! b || ! c || ! d || e || ! output) && (! a || ! b || ! c || d || ! e || ! output) && (! a || ! b || ! c || d || e || output) && (! a || ! b || c || ! d || ! e || ! output) && (! a || ! b || c || ! d || e || output) && (! a || ! b || c || d || ! e || output) && (! a || ! b || c || d || e || ! output) && (! a || b || ! c || ! d || ! e || ! output) && (! a || b || ! c || ! d || e || output) && (! a || b || ! c || d || ! e || output) && (! a || b || ! c || d || e || ! output) && (! a || b || c || ! d || ! e || output) && (! a || b || c || ! d || e || ! output) && (! a || b || c || d || ! e || ! output) && (! a || b || c || d || e || output) && ( a || ! b || ! c || ! d || ! e || ! output) && ( a || ! b || ! c || ! d || e || output) && ( a || ! b || ! c || d || ! e || output) && ( a || ! b || ! c || d || e || ! output) && ( a || ! b || c || ! d || ! e || output) && ( a || ! b || c || ! d || e || ! output) && ( a || ! b || c || d || ! e || ! output) && ( a || ! b || c || d || e || output) && ( a || b || ! c || ! d || ! e || output) && ( a || b || ! c || ! d || e || ! output) && ( a || b || ! c || d || ! e || ! output) && ( a || b || ! c || d || e || output) && ( a || b || c || ! d || ! e || ! output) && ( a || b || c || ! d || e || output) && ( a || b || c || d || ! e || output) && ( a || b || c || d || e || ! output)
Clearly, there are patterns here. "Mathematics is the study of patterns.", as they say. Can I reverse engineer Wolfram Mathematica's output?
See the last one, CNF for 5-input XOR. a/b/c/d/e are came in order, but 'output' isn't. 'output' alternating between 4-bit series like "! !" and " !! ".
My first idea was that output=~(a^b^c^d^e). This it true for 5-input XOR and 3-input XOR. But not true for 2-input XOR, 4-input XOR, etc, correct would be (a^b^c^d) without NOT. See the Jupyter notebook.
So my hypothesis was: ~(inputs XORed) for XOR function with odd number of inputs and (inputs XORed) (without NOT) for even number of inputs. This was proved by my test. The final function is:
# to be used only for small number of inputs (less that ~12) def XOR_list(self, inputs:List[int]) -> int: ... # see the book for explanation of this slightly esoteric function for row in range(2**inputs_t): #print ("XOR_list, row", row) cls=[] tmp=[] for col in range(inputs_t): if (row>>col)&1==0: cls.append(self.neg(inputs[col])) tmp.append(False) else: cls.append(inputs[col]) tmp.append(True) tmp2=functools.reduce (operator.xor, tmp) if (inputs_t&1)==0: tmp2=not(tmp2) if tmp2==False: cls.append(out) else: cls.append(self.neg(out)) self.add_clause(cls) ... return out
( src )
I needed multi-input XOR for the program that can factorize GF(2)/CRC polynomials. Failed. CNF grows exponentially even for small XOR functions. It's still wiser to construct a multi-input XOR function using 2-input XORs. Nevertheless, my code worked correctly.
Yes, I know about these lousy Disqus ads. Please use adblocker. I would consider to subscribe to 'pro' version of Disqus if the signal/noise ratio in comments would be good enough.