{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%html\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Dependencies for this notebook: `graphviz` executable installed on the system and on the path, [networkx](https://anaconda.org/anaconda/networkx) and [graphviz python](https://anaconda.org/conda-forge/python-graphviz) anaconda packages. (See [here](https://graphviz.readthedocs.io/en/stable/index.html) for the documentation of the latter package.)\n",
"\n",
"Please run the utility code at the bottom of this notebook first.\n",
"\n",
"You should be able to upload this notebook to the JupyterHub instance on canvas.\n",
"\n",
"__comment:__ In this notebook I use `x_17` and not `x17` for 3SAT and 3NAND formula to be more consistent with our notation for NAND. This is of course not very important."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"%run \"Utilities.ipynb\"\n",
"from IPython.display import clear_output\n",
"clear_output()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
" _Useful unicode symbols:_ φ ∨ ∧ ¬ ≠ Ψ"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"def numvars(φ):\n",
" for n in range(len(φ)-1,0,-1):\n",
" if φ.find('x'+str(n))>= 0 or φ.find('z'+str(n))>= 0: return n+1\n",
" raise Exception\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"from collections import defaultdict"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"import networkx as nx\n",
"\n",
"import pydotplus\n",
"\n",
"def nxgraph(G):\n",
" P = pydotplus.graph_from_dot_data(G.source)\n",
" return nx.drawing.nx_pydot.from_pydot(P)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## NANDSAT\n",
"\n",
"__Input:__ $Q \\in \\{0,1\\}^*$: NAND-CIRC program of $n$ inputs and $1$ output\n",
"\n",
"__Output:__ $1$ iff $\\exists y\\in \\{0,1\\}^n$ s.t. $Q(y)=1$."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"xor5 = r'''\n",
"u = NAND(X[0],X[1])\n",
"v = NAND( X[0] , u)\n",
"w = NAND( X[1] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[2])\n",
"v = NAND( s , u)\n",
"w = NAND( X[2] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[3])\n",
"v = NAND( s , u)\n",
"w = NAND( X[3] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[4])\n",
"v = NAND( s , u)\n",
"w = NAND( X[4] , u)\n",
"Y[0] = NAND( v , w)\n",
"'''[1:]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": []
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"circuit(xor5)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"CONSTPREFIX = r'''\n",
"temp = NAND(X[0],X[0])\n",
"one = NAND(X[0],temp)\n",
"zero = NAND(one,one)\n",
"'''[1:]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"# \"hardwire\" an input x to a NAND program Q\n",
"# return Q' s.t. Q'(y)=Q(xy)\n",
"def hardwire(Q,x):\n",
" n = len(x)\n",
" for i in range(n): \n",
" Q = Q.replace(f'X[{i}]',('one' if x[i] else 'zero'))\n",
" for i in range(n,2*len(Q)): \n",
" Q = Q.replace(f'X[{i}]',f'X[{i-n}]')\n",
" return CONSTPREFIX+Q"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"def indexat(t): return t\n",
"\n",
"def unroll__(P,n,m,T):\n",
" result = CONSTPREFIX\n",
" for t in range(T // P.count('\\n')):\n",
" i = indexat(t) # value of i in T-th iteration\n",
" valid = ('one' if i < n else 'zero' )\n",
" inp = ('X[i]' if i < n else 'zero')\n",
" out = ('Y[i]' if i < m else 'nonsense')\n",
" result += P.replace('Xvalid[i]',valid).replace('X[i]',inp\n",
" ).replace('Y[i]',out).replace('[i]',f'[{i}]')\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"temp = NAND(X[0],X[0])\n",
"one = NAND(X[0],temp)\n",
"zero = NAND(one,one)\n",
"u = NAND(one,zero)\n",
"v = NAND( one , u)\n",
"w = NAND( zero , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , one)\n",
"v = NAND( s , u)\n",
"w = NAND( one , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[0])\n",
"v = NAND( s , u)\n",
"w = NAND( X[0] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[1])\n",
"v = NAND( s , u)\n",
"w = NAND( X[1] , u)\n",
"Y[0] = NAND( v , w)\n",
"\n"
]
}
],
"source": [
"Q = hardwire(xor5,[1,0,1])\n",
"print(Q)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": []
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"C = circuit(Q)\n",
"C"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"C(0,1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## 3NAND\n",
"\n",
"__Input:__ $\\psi \\in \\{0,1\\}^*$ representing 3NAND formula of form $(x_i = x_j NAND x_k) \\wedge \\cdots$\n",
"\n",
"__Output:__ $1$ iff $\\exists x\\in \\{0,1\\}^*$ s.t. $\\psi(x)=1$. "
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"Ψ = \"(z0 = NAND(z2,z3) ) ∧ (z3 = NAND(z2,z1) ) ∧ (z1 = NAND(z2,z3) ) \""
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# Evaluate 3CNAND Ψ on assignment x \n",
"# Both are represented as strings\n",
"def eval3NAND(Ψ,x):\n",
"\n",
" def varval(v):\n",
" return int(x[int(v[1:])]) # assume vars are x_###\n",
" \n",
" for (v0,v1,v2) in getnandclauses(Ψ):\n",
" if varval(v0) != 1-varval(v1)*varval(v2): return False\n",
" \n",
" return True\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"\n",
"# Clause list of a 3CNF/3NAND formula φ\n",
"def getcnfclauses(φ):\n",
" clauses = φ.split(\"∧\")\n",
" res = []\n",
" for c in clauses:\n",
" (v0,_,v1,_,v2) = c.strip()[1:-1].split()\n",
" res.append((v0.strip(),v1.strip(),v2.strip()))\n",
" return res\n",
"\n",
"def getnandclauses(φ):\n",
" clauses = φ.split(\"∧\")\n",
" res = []\n",
" for c in clauses:\n",
" foo,bar,blah, = filter(None,re.split('\\s*=\\s*NAND\\s*\\(\\s*|\\s*\\,\\s*|\\s*\\)\\s*|\\s+',c.strip()[1:-1]))\n",
" res.append((foo.strip(),bar.strip(),blah.strip()))\n",
" return res\n",
"\n",
"\n",
"\n",
"# number of variables of a formula φ\n",
"def numvars(φ):\n",
" for n in range(len(φ)-1,0,-1):\n",
" if φ.find(f'x{n}')>= 0 or φ.find(f'z{n}')>= 0: return n+1\n",
" raise Exception\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[('z0', 'z2', 'z3'), ('z3', 'z2', 'z1'), ('z1', 'z2', 'z3')]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"getnandclauses(Ψ)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"__Thm:__ $NANDSAT \\leq_p 3NAND$"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"# Reduce NANDSAT to 3NAND\n",
"# Input: NAND prog Q\n",
"# Output: 3NAND formula Ψ\n",
"# s.t. Ψ satisfiable iff Q is\n",
"def NANDSAT23NAND_(Q):\n",
" # if Q doesn't start with CONSTPREFIX: Q = CONSTPREFIX + Q\n",
" n, _ = numinout(Q)\n",
" Ψ = ''\n",
"\n",
" #varidx[u] is n+line where u is last written to\n",
" varidx = defaultdict(lambda : n+ 2) # line 2 corresponds to zero\n",
" \n",
" for i in range(n): varidx[f'X[{i}]'] = i # setup x_0...x_n-1\n",
" \n",
" j = n\n",
" for line in Q.split('\\n'):\n",
" if not line.strip(): continue\n",
" foo,_,bar,blah = parseline(line) # split \"foo = NAND(bar,blah)\"\n",
" Ψ += f\"(z{j} = NAND(z{varidx[bar]},z{varidx[blah]}) ) ∧ \"\n",
" varidx[foo] = j\n",
" j += 1\n",
" Ψ += f\"(z{varidx['Y[0]']} = NAND(z{varidx['zero']},z{varidx['zero']}) )\"\n",
" return Ψ"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# Version of input above that keeps track of satisfying assignment y if given\n",
"#\n",
"# Reduce NANDSAT to 3NAND\n",
"# Input: NAND prog Q\n",
"# Output: 3NAND formula Ψ\n",
"# s.t. Ψ satisfiable iff Q is\n",
"def NANDSAT23NAND(Q,y=[]):\n",
" if Q[:len(CONSTPREFIX)] != CONSTPREFIX:\n",
" Q = CONSTPREFIX + Q\n",
" n, _ = numinout(Q)\n",
" Ψ = ''\n",
" z = defaultdict(int)\n",
"\n",
" #varidx[u] is n+line where u is last written to\n",
" varidx = defaultdict(lambda : n+ 2) # line 2 corresponds to zero\n",
"\n",
" for i in range(n): \n",
" varidx[f'X[{i}]'] = i # setup x_0...x_n-1\n",
" if y: z[i] = y[i]\n",
" \n",
" j = n\n",
" for line in Q.split('\\n'):\n",
" if not line.strip(): continue\n",
" foo,_,bar,blah = parseline(line)\n",
" Ψ += f\"(z{j} = NAND(z{varidx[bar]},z{varidx[blah]}) ) ∧ \"\n",
" if y: z[j] = NAND(z[varidx[bar]],z[varidx[blah]])\n",
" varidx[foo] = j\n",
" j += 1\n",
" Ψ += f\"(z{varidx['Y[0]']} = NAND(z{varidx['zero']},z{varidx['zero']}) )\"\n",
" return Ψ if not y else (Ψ,[z[i] for i in range(j)])"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"u = NAND(X[0],X[1])\n",
"v = NAND( X[0] , u)\n",
"w = NAND( X[1] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[2])\n",
"v = NAND( s , u)\n",
"w = NAND( X[2] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[3])\n",
"v = NAND( s , u)\n",
"w = NAND( X[3] , u)\n",
"s = NAND( v , w)\n",
"u = NAND( s , X[4])\n",
"v = NAND( s , u)\n",
"w = NAND( X[4] , u)\n",
"Y[0] = NAND( v , w)\n",
"\n",
"Number of lines: 16, Number of variables: 5\n"
]
}
],
"source": [
"print(xor5) \n",
"lines = xor5.count('\\n')\n",
"n = max([i for i in range(2*lines) if xor5.find(f\"X[{i}]\")>=0])+1\n",
"print(f\"Number of lines: {lines}, Number of variables: {n}\")"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(z5 = NAND(z0,z1) ) ∧ (z6 = NAND(z0,z5) ) ∧ (z7 = NAND(z1,z5) ) ∧ (z8 = NAND(z6,z7) ) ∧ (z9 = NAND(z8,z2) ) ∧ (z10 = NAND(z8,z9) ) ∧ (z11 = NAND(z2,z9) ) ∧ (z12 = NAND(z10,z11) ) ∧ (z13 = NAND(z12,z3) ) ∧ (z14 = NAND(z12,z13) ) ∧ (z15 = NAND(z3,z13) ) ∧ (z16 = NAND(z14,z15) ) ∧ (z17 = NAND(z16,z4) ) ∧ (z18 = NAND(z16,z17) ) ∧ (z19 = NAND(z4,z17) ) ∧ (z20 = NAND(z18,z19) ) ∧ (z20 = NAND(z7,z7) )\n",
"Vars: 21, Clauses: 17\n"
]
}
],
"source": [
"Ψ = NANDSAT23NAND_(xor5)\n",
"print(Ψ)\n",
"print(f\"Vars: {numvars(Ψ)}, Clauses: {len(getnandclauses(Ψ))}\")"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1]"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NANDSAT23NAND(xor5,[1,0,0,1,1])[1]"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"eval3NAND(NANDSAT23NAND_(xor5),[1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1])"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Q = hardwire(xor5,[1,1])\n",
"(Ψ,y) = NANDSAT23NAND(Q,[1,1,1])\n",
"eval3NAND(Ψ,y)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## 3SAT \n",
"\n",
"__Input:__ 3CNF formula: AND of $m$ _clauses_. Clause = OR of three _literals_. Literal = variable or its negation.\n",
"\n",
"__Goal:__ Output 1 iif formula _satisfiable_."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"φ = \"(x0 ∨ ¬x3 ∨ x2 ) ∧ (¬x0 ∨ x1 ∨ ¬x2 ) ∧ (x1 ∨ x2 ∨ ¬x3 ) \""
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# Evaluate 3CNF φ on assignment x \n",
"# Both are represented as strings\n",
"def evalcnf(φ,x):\n",
"\n",
" def varval(v):\n",
" return (1-x[int(v[2:])]) if v[0]==\"¬\" else x[int(v[1:])]\n",
" \n",
" for (v0,v1,v2) in getcnfclauses(φ):\n",
" # print(c+str([varval(v0),varval(v1),varval(v2)]))\n",
" if not varval(v0)+varval(v1)+varval(v2): return False\n",
" \n",
" return True\n",
"\n",
"# Clause list of a 3CNF φ\n",
"def getcnfclauses(φ):\n",
" clauses = φ.split(\"∧\")\n",
" res = []\n",
" for c in clauses:\n",
" (v0,_,v1,_,v2) = c.strip()[1:-1].split()\n",
" res.append((v0.strip(),v1.strip(),v2.strip()))\n",
" return res"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"numvars(φ)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"__Thm:__ $3NAND \\leq_p 3SAT$"
]
},
{
"cell_type": "markdown",
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"__Proof:__ $a = NAND(b,c)$ iff $(\\neg a \\vee \\neg b \\vee \\neg c) \\wedge (a \\vee b) \\wedge (a \\vee c)$"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"# Reduce 3NAND to 3SAT\n",
"# Input: 3NAND formula Ψ\n",
"# Output: 3CNF formula φ\n",
"# s.t. φ satisfiable iff Ψ is\n",
"def NAND23SAT_(Ψ):\n",
" φ = \"\"\n",
" for (a,b,c) in getnandclauses(Ψ):\n",
" φ += f'(¬{a} ∨ ¬{b} ∨ ¬{c}) ∧ ({a} ∨ {b} ∨ {b}) ∧ ({a} ∨ {c} ∨ {c}) ∧ '\n",
" return φ[:-3] # chop off redundant ∧"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"'(¬x0 ∨ ¬x2 ∨ ¬x3) ∧ (x0 ∨ x2 ∨ x2) ∧ (x0 ∨ x3 ∨ x3) ∧ (¬x3 ∨ ¬x2 ∨ ¬x1) ∧ (x3 ∨ x2 ∨ x2) ∧ (x3 ∨ x1 ∨ x1) ∧ (¬x1 ∨ ¬x2 ∨ ¬x3) ∧ (x1 ∨ x2 ∨ x2) ∧ (x1 ∨ x3 ∨ x3)'"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Ψ = \"(x0 = NAND(x2,x3) ) ∧ (x3 = NAND(x2,x1) ) ∧ (x1 = NAND(x2,x3) ) \"\n",
"NAND23SAT_(Ψ)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# Same as above but keeping track of assignment\n",
"def NAND23SAT(Ψ,y=[]):\n",
" φ = \"\"\n",
" def makex(a): return \"x\"+a[1:]\n",
" for (a,b,c) in getnandclauses(Ψ):\n",
" a = makex(a)\n",
" b = makex(b)\n",
" c = makex(c)\n",
" φ += f'(¬{a} ∨ ¬{b} ∨ ¬{c}) ∧ ({a} ∨ {b} ∨ {b}) ∧ ({a} ∨ {c} ∨ {c}) ∧ '\n",
" return (φ[:-3],y) if y else φ[:-3] # chop off redundant ∧"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(¬x3 ∨ ¬x0 ∨ ¬x0) ∧ (x3 ∨ x0 ∨ x0) ∧ (x3 ∨ x0 ∨ x0) ∧ (¬x4 ∨ ¬x0 ∨ ¬x3) ∧ (x4 ∨ x0 ∨ x0) ∧ (x4 ∨ x3 ∨ x3) ∧ (¬x5 ∨ ¬x4 ∨ ¬x4) ∧ (x5 ∨ x4 ∨ x4) ∧ (x5 ∨ x4 ∨ x4) ∧ (¬x6 ∨ ¬x4 ∨ ¬x4) ∧ (x6 ∨ x4 ∨ x4) ∧ (x6 ∨ x4 ∨ x4) ∧ (¬x7 ∨ ¬x4 ∨ ¬x6) ∧ (x7 ∨ x4 ∨ x4) ∧ (x7 ∨ x6 ∨ x6) ∧ (¬x8 ∨ ¬x4 ∨ ¬x6) ∧ (x8 ∨ x4 ∨ x4) ∧ (x8 ∨ x6 ∨ x6) ∧ (¬x9 ∨ ¬x7 ∨ ¬x8) ∧ (x9 ∨ x7 ∨ x7) ∧ (x9 ∨ x8 ∨ x8) ∧ (¬x10 ∨ ¬x9 ∨ ¬x0) ∧ (x10 ∨ x9 ∨ x9) ∧ (x10 ∨ x0 ∨ x0) ∧ (¬x11 ∨ ¬x9 ∨ ¬x10) ∧ (x11 ∨ x9 ∨ x9) ∧ (x11 ∨ x10 ∨ x10) ∧ (¬x12 ∨ ¬x0 ∨ ¬x10) ∧ (x12 ∨ x0 ∨ x0) ∧ (x12 ∨ x10 ∨ x10) ∧ (¬x13 ∨ ¬x11 ∨ ¬x12) ∧ (x13 ∨ x11 ∨ x11) ∧ (x13 ∨ x12 ∨ x12) ∧ (¬x14 ∨ ¬x13 ∨ ¬x1) ∧ (x14 ∨ x13 ∨ x13) ∧ (x14 ∨ x1 ∨ x1) ∧ (¬x15 ∨ ¬x13 ∨ ¬x14) ∧ (x15 ∨ x13 ∨ x13) ∧ (x15 ∨ x14 ∨ x14) ∧ (¬x16 ∨ ¬x1 ∨ ¬x14) ∧ (x16 ∨ x1 ∨ x1) ∧ (x16 ∨ x14 ∨ x14) ∧ (¬x17 ∨ ¬x15 ∨ ¬x16) ∧ (x17 ∨ x15 ∨ x15) ∧ (x17 ∨ x16 ∨ x16) ∧ (¬x18 ∨ ¬x17 ∨ ¬x2) ∧ (x18 ∨ x17 ∨ x17) ∧ (x18 ∨ x2 ∨ x2) ∧ (¬x19 ∨ ¬x17 ∨ ¬x18) ∧ (x19 ∨ x17 ∨ x17) ∧ (x19 ∨ x18 ∨ x18) ∧ (¬x20 ∨ ¬x2 ∨ ¬x18) ∧ (x20 ∨ x2 ∨ x2) ∧ (x20 ∨ x18 ∨ x18) ∧ (¬x21 ∨ ¬x19 ∨ ¬x20) ∧ (x21 ∨ x19 ∨ x19) ∧ (x21 ∨ x20 ∨ x20) ∧ (¬x21 ∨ ¬x5 ∨ ¬x5) ∧ (x21 ∨ x5 ∨ x5) ∧ (x21 ∨ x5 ∨ x5)\n"
]
}
],
"source": [
"φ = NAND23SAT(NANDSAT23NAND(Q))\n",
"print(φ)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# Reduction φ ↦ G\n",
"def SAT2IS_(φ):\n",
" n = numvars(φ) \n",
" G =Graph(engine='neato')\n",
" \n",
" # add pairs \"x_i=0\" and \"x_i=1\"\n",
" for i in range(n): G.edge(f'x_{i}=0',f'x_{i}=1')\n",
" \n",
" # map \"x_7\" with index 5 to \"5)x_7≠0\", \"¬x_12\" with index 6 to \"6)x_12≠1\"\n",
" def nodename(v,c): return str(c)+')'+(v[1:]+\"≠1\" if v[0]==\"¬\" else v+\"≠0\")\n",
" \n",
" #map \"5)x_7≠0\" to its neighbor \"x_7=0\"\n",
" def neighbor(n): return n.split(')')[1].split('≠')[0]+\"=\"+n[-1]\n",
" \n",
" c = 0\n",
" for C in getcnfclauses(φ):\n",
" (u,v,w) = (nodename(C[0],c),nodename(C[1],c+1),nodename(C[2],c+2))\n",
" \n",
" # add triangle of clause\n",
" G.edges([(u,v),(v,w),(u,w)])\n",
" \n",
" # connect each vertex to inconsistent neighbor\n",
" G.edges([(u,neighbor(u)),(v,neighbor(v)),(w,neighbor(w))])\n",
" c += 3\n",
" \n",
" return G"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# same reduction but taking care of colors and keeping track what happens to an assignment\n",
"def SAT2IS(φ,x=\"\"):\n",
" S = []\n",
"# G.node(u,style='filled',fillcolor=('red' if x and x[i]==\"0\" else 'green'))\n",
" \n",
" n = numvars(φ) \n",
" G =Graph() # (engine='neato')\n",
" \n",
" def nname(c,v): return f\"({c},{v})\"\n",
" \n",
" c = 0\n",
" nodes = {}\n",
" for i in range(n):\n",
" nodes[\"x\"+str(i)] = []\n",
" nodes[\"¬x\"+str(i)] = []\n",
" \n",
" for C in getcnfclauses(φ):\n",
" sat = False\n",
" for u in C: \n",
" if x and (not sat) and (((u[0]==\"¬\") and (x[int(u[2:])]==\"0\")) or ((u[0]!=\"¬\") and (x[int(u[1:])]==\"1\"))):\n",
" G.node(nname(c,u),f\"{c},{subscript(u)}\",fontsize=\"10\",style='filled',fillcolor='green')\n",
" S.append(nname(c,u))\n",
" sat = True\n",
" else:\n",
" G.node(nname(c,u),f\"{c},{subscript(u)}\",fontsize=\"10\")\n",
" negu = u[1:] if u[0]==\"¬\" else \"¬\"+u\n",
" for v in nodes[negu]: G.edge(nname(c,u),v,color=\"red\") \n",
" nodes[u].append(nname(c,u))\n",
" G.edges([[nname(c,C[0]), nname(c,C[1])],[nname(c,C[1]), nname(c,C[2])],[nname(c,C[0]), nname(c,C[2])]])\n",
" c += 1\n",
" \n",
" return (scale(G) if not x else (scale(G),S))"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"# same reduction as above but handling colors and showing the resulting cut when the original graph has independent set\n",
"def IS2MAXCUT(G,I=[]):\n",
" G =nxgraph(G)\n",
" S = []\n",
" H =Graph() # engine='sfdp')\n",
" H.node(\"source\",style='filled',fillcolor='blue')\n",
" S.append(\"source\")\n",
" for v in G.nodes():\n",
" color = ('red' if I and v in I else 'lightblue')\n",
" H.node(v,label=subscript(v),fontsize=\"10\",style='filled',fillcolor=color, shape=\"square\")\n",
" ecol = 'black'\n",
" pwidth = '1'\n",
" if I and v in I: \n",
" S.append(v)\n",
" ecol = 'red'\n",
" pwidth = '2'\n",
" H.edge(\"source\",v,color=ecol,penwidth=pwidth) # len=\"2\"\n",
" \n",
" j =0\n",
" for (u,v) in G.edges():\n",
" g1 = \"e\"+str(j)+\"a\"\n",
" g2 = \"e\"+str(j)+\"b\"\n",
" c1 = 'green'\n",
" c2 = 'green'\n",
" if I and (not u in I):\n",
" c1 = 'red'\n",
" S.append(g1)\n",
" if I and (not v in I):\n",
" c2 = 'red'\n",
" S.append(g2)\n",
" gadget = Graph(\"gadget\"+str(j))\n",
" gadget.node(g1,subscript(\"e0\"),fontsize=\"10\",style='filled',fillcolor=c1)\n",
" gadget.node(g2,subscript(\"e1\"),fontsize=\"10\",style='filled',fillcolor=c2)\n",
" gadget.edge(g1,g2,color=('red' if (g1 in S) != (g2 in I) else 'black'), penwidth=('2' if (g1 in S) != (g2 in I) else '1')) # len=\"1\"\n",
" gadget.edge(u,g1, color=('red' if (g1 in S) != (u in I) else 'black'),penwidth =('2' if (g1 in S) != (u in S) else '1') ) # len=\"1\"\n",
" gadget.edge(v,g2, color=('red' if (g2 in S) != (v in I) else 'black'),penwidth= ('2' if (g2 in S) != (v in S) else '1') ) # len=\"1\"\n",
" \n",
" H.subgraph(gadget)\n",
" H.edge(g1,\"source\",color=('red' if (g1 in S) else 'black'),penwidth=('2' if (g1 in S) else '1')) # len=\"2.5\"\n",
" H.edge(g2,\"source\",color=('red' if (g2 in S) else 'black'),penwidth= ('2' if (g2 in S) else '1') )\n",
" j +=1\n",
" \n",
" \n",
" return (scale(H),S) if I else scale(H)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Putting everything together"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"inputHidden": false,
"outputHidden": false,
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"SAT2IS(*NAND23SAT(*NANDSAT23NAND(xor5,(1,0,1,0,1))))[0]"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"image/svg+xml": [
"\r\n",
"\r\n",
"\r\n",
"\r\n",
"\r\n"
],
"text/plain": [
""
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"IS2MAXCUT(SAT2IS(NAND23SAT(NANDSAT23NAND(xor5))))"
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernel_info": {
"name": "python3"
},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.12"
},
"livereveal": {
"start_slideshow_at": "selected",
"theme": "beige",
"transition": "none"
},
"nteract": {
"version": "0.3.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}