Paper
NP-Hardness of (3,4)-SAT and [2,3]-Bounded 3-SAT
6 pages · 19 marked passages · pdflatex · download PDF · lax-345332
-
The Cook–Levin Theorem
-
Classical Complexity Classes
-
The Word RAM
-
Computability and polynomial-time equivalence of Turing machines and word RAMs
-
NP-Hardness of (3,4)-SAT
(3,4)-SAT is satisfiability restricted to formulas in which every clause contains exactly three literals and every variable occurs at most four times. Tovey proved that it is NP-hard: every language in NP has a polynomial-time many-one reduction to it.
1 import Lax429075.Reductions 2 import Lax429075.Satisfiability 3 … module docstring, 15 lines 19 20 namespace Lax345332.ThreeFourSat 21 22 open Lax429075.CNF Lax434930.PolynomialTime 23 24 /-- The number of occurrences of the variable `i` in the formula `F`. -/ 25 def occurrences (F : Formula) (i : ℕ) : ℕ := 26 (F.flatMap fun C => C.filter fun l => l.index == i).length 27 28 /-- `F` is a *(3,4)* formula: every clause has exactly three literals, and every variable 29 occurs at most four times. -/ 30 def IsThreeFour (F : Formula) : Prop := 31 (∀ C ∈ F, C.length = 3) ∧ ∀ i, occurrences F i ≤ 4 32 33 /-- **(3,4)-SAT** as a language: the encodings of satisfiable (3,4) formulas. -/ 34 def SAT34 : Language := 35 {w | ∃ F : Formula, Lax429075.Encoding.encodeCNF F = w ∧ IsThreeFour F ∧ Satisfiable F} 36 37 /-- **Theorem 2.3 (Tovey).** (3,4)-SAT is NP-hard. -/ 38 axiom npHard : 39 ∀ A : Language, A ∈ Lax434930.NondeterministicPolynomialTime.NP → 40 Lax429075.Reductions.ManyOne A SAT34 41 42 end Lax345332.ThreeFourSat 43 -
NP-Hardness of [2,3]-Bounded 3-SAT
[2,3]-bounded 3-SAT asks whether a formula is satisfiable, where every clause has two or three literals and every literal, a variable together with a sign, occurs at most twice. A variable therefore occurs at most twice positively and at most twice negatively, so in at most four clauses. The problem is NP-hard: every language in NP has a polynomial-time many-one reduction to it.
Tovey's paper states its theorem for (3,4)-SAT (). This is the variant that the scheduling reductions start from, and it follows from the (3,4) case by one more reduction, given in .
1 import Lax345332.ThreeFourSat 2 import Mathlib.Data.List.FinRange 3 import Mathlib.Data.Nat.Bits 4 … module docstring, 35 lines 40 41 namespace Lax345332.TwoThreeSat 42 43 open Lax434930.PolynomialTime Lax434930.NondeterministicPolynomialTime 44 45 /-- A natural number as a binary word: its digits, least significant first, preceded by 46 their number in unary. -/ 47 def encodeNat (n : ℕ) : Word := 48 List.replicate n.bits.length true ++ [false] ++ n.bits 49 50 /-- A **[2,3]-bounded 3-SAT formula** over `vars` variables: `twoClauses` clauses of two 51 literals and `threeClauses` clauses of three literals, in which every literal — a variable 52 together with a sign — occupies at most two of the positions of the formula. -/ 53 structure Formula where 54 /-- The number of variables. -/ 55 vars : ℕ 56 /-- The number of clauses of two literals. -/ 57 twoClauses : ℕ 58 /-- The number of clauses of three literals. -/ 59 threeClauses : ℕ 60 /-- The literals of the clauses of two literals. -/ 61 aLit : Fin twoClauses → Fin 2 → Fin vars × Bool 62 /-- The literals of the clauses of three literals. -/ 63 bLit : Fin threeClauses → Fin 3 → Fin vars × Bool 64 /-- Every literal occurs at most twice in the formula. -/ 65 occ_le_two : ∀ l : Fin vars × Bool, 66 (Finset.univ.filter fun o : (Fin twoClauses × Fin 2) ⊕ (Fin threeClauses × Fin 3) => 67 Sum.elim (fun q => aLit q.1 q.2) (fun q => bLit q.1 q.2) o = l).card ≤ 2 68 69 namespace Formula 70 71 variable (φ : Formula) 72 73 /-- An assignment of a truth value to every variable. -/ 74 abbrev Assignment := Fin φ.vars → Bool 75 76 /-- The assignment `a` satisfies `φ`: every clause, of two or of three literals, contains a 77 literal that `a` makes true. -/ 78 def Satisfies (a : φ.Assignment) : Prop := 79 (∀ c, ∃ α, a (φ.aLit c α).1 = (φ.aLit c α).2) ∧ 80 (∀ c, ∃ α, a (φ.bLit c α).1 = (φ.bLit c α).2) 81 82 /-- The formula has a satisfying assignment. -/ 83 def Satisfiable : Prop := ∃ a : φ.Assignment, φ.Satisfies a 84 85 end Formula 86 87 /-- The number of literal positions: two per clause of two literals, three per clause of 88 three. -/ 89 def slots (φ : Formula) : ℕ := 2 * φ.twoClauses + 3 * φ.threeClauses 90 91 /-- A formula as a binary word: the number of variables, the numbers of clauses of two and of 92 three literals, then the variable and sign of each literal of each clause, the clauses of 93 two literals first. -/ 94 def encodeFormula (φ : Formula) : Word := 95 encodeNat φ.vars ++ encodeNat φ.twoClauses ++ encodeNat φ.threeClauses ++ 96 ((List.finRange φ.twoClauses).flatMap fun c => 97 (List.finRange 2).flatMap fun α => encodeNat (φ.aLit c α).1 ++ [(φ.aLit c α).2]) ++ 98 (List.finRange φ.threeClauses).flatMap fun c => 99 (List.finRange 3).flatMap fun α => encodeNat (φ.bLit c α).1 ++ [(φ.bLit c α).2] 100 101 /-- **[2,3]-bounded 3-SAT** as a language: the encodings of satisfiable formulas with at 102 most as many variables as literal positions. -/ 103 def SAT23 : Language := 104 {w | ∃ φ : Formula, encodeFormula φ = w ∧ φ.vars ≤ slots φ ∧ φ.Satisfiable} 105 106 /-- **[2,3]-bounded 3-SAT is NP-hard.** Every language in NP has a polynomial-time 107 many-one reduction to it. -/ 108 axiom npHard : 109 ∀ A : Language, A ∈ NP → Lax429075.Reductions.ManyOne A SAT23 110 111 end Lax345332.TwoThreeSat 112 -
The Reduction from Satisfiability to (3,4)-Satisfiability
A CNF formula with literal occurrences, over variables below , is turned into a (3,4) formula in two steps.
Splitting. The occurrence at position of a variable is replaced by a fresh variable . A clause is chained through link variables : the clause becomes , and an empty clause stays empty. For every , the implications , taken cyclically over all positions, force the copies of to agree. Afterwards every clause has at most three literals and every variable occurs at most three times.
Padding. Each clause with fewer than three literals is filled up with literals , where every is a fresh variable that a private copy of Tovey's gadget forces to be true. The gadget has thirteen clauses on ten variables: occurs three times in it and each of the other nine variables four times.
On words, the reduction decodes a formula, applies the two steps and encodes the result. A word that encodes no formula is treated as the formula consisting of one empty clause.
- def✓
Lax345332.Construction(1st statement) - def✓
Lax345332.Construction(2nd statement) - def✓
Lax345332.Construction(3rd statement) - def✓
Lax345332.Construction(4th statement)
1 import Lax345332.ThreeFourSat 2 … module docstring, 30 lines 33 34 namespace Lax345332.Construction 35 36 open Lax429075.CNF Lax429075.Encoding Lax434930.PolynomialTime Lax345332.ThreeFourSat 37 38 -- The gadget 39 40 /-- Tovey's thirteen clauses on the variables `g, g+1, …, g+9`; they force `g` to be true. -/ 41 def gadget (g : ℕ) : Formula := 42 let y := g 43 let a (j : ℕ) := g + 1 + 3 * j 44 let b (j : ℕ) := g + 2 + 3 * j 45 let d (j : ℕ) := g + 3 + 3 * j 46 ((List.range 3).map fun j => [⟨y, true⟩, ⟨a j, true⟩, ⟨b j, true⟩]) ++ 47 ((List.range 3).flatMap fun j => 48 [[⟨d j, true⟩, ⟨a j, true⟩, ⟨b j, false⟩], [⟨d j, true⟩, ⟨a j, false⟩, ⟨b j, true⟩], 49 [⟨d j, true⟩, ⟨a j, false⟩, ⟨b j, false⟩]]) ++ 50 [[⟨d 0, false⟩, ⟨d 1, false⟩, ⟨d 2, false⟩]] 51 52 -- Padding 53 54 /-- The base of the `t`-th gadget of the `k`-th clause, above the variables below `B`. -/ 55 def ybase (B k t : ℕ) : ℕ := B + 30 * k + 10 * t 56 57 /-- The padding literals of a clause of length `n`. -/ 58 def pads (B k n : ℕ) : Clause := (List.range (3 - n)).map fun t => ⟨ybase B k t, false⟩ 59 60 /-- The `k`-th clause, padded, with its three gadgets. -/ 61 def unit (B k : ℕ) (C : Clause) : Formula := 62 (C ++ pads B k C.length) :: 63 (gadget (ybase B k 0) ++ gadget (ybase B k 1) ++ gadget (ybase B k 2)) 64 65 /-- Every clause of `G`, padded to three literals and followed by its gadgets. `B` bounds the 66 variables in use. -/ 67 def pad (B : ℕ) (G : Formula) : Formula := 68 (List.range G.length).flatMap fun k => unit B k (G.getD k []) 69 70 -- Splitting 71 72 /-- The copy of the variable `v` at position `p`. -/ 73 def wv (V v p : ℕ) : ℕ := v + p * V 74 75 /-- The link variable after position `p`. -/ 76 def zv (V N p : ℕ) : ℕ := V * N + p 77 78 /-- A placeholder literal, used when an index is past the end of a list. -/ 79 def dflt : Literal := ⟨0, false⟩ 80 81 /-- The `i`-th clause of the chain of `C`, whose first literal sits at position `o`: the 82 negated link before it (except for `i = 0`), the copy of the `i`-th literal, and the link 83 after it (except for the last). -/ 84 def chainClause (V N o : ℕ) (C : Clause) (i : ℕ) : Clause := 85 (if i = 0 then [] else [⟨zv V N (o + i - 1), false⟩]) ++ 86 [⟨wv V (C.getD i dflt).index (o + i), (C.getD i dflt).positive⟩] ++ 87 (if i + 1 = C.length then [] else [⟨zv V N (o + i), true⟩]) 88 89 /-- The chain of the clause `C`, whose first literal sits at position `o`; an empty clause 90 becomes one empty clause. -/ 91 def chainClauses (V N o : ℕ) (C : Clause) : Formula := 92 if C.length = 0 then [[]] else (List.range C.length).map (chainClause V N o C) 93 94 /-- The chains of all clauses of a formula, the first literal of the formula being at 95 position `o`. -/ 96 def chains (V N : ℕ) : ℕ → Formula → Formula 97 | _, [] => [] 98 | o, C :: F => chainClauses V N o C ++ chains V N (o + C.length) F 99 100 /-- The cyclic successor on positions. -/ 101 def next (N p : ℕ) : ℕ := if p + 1 < N then p + 1 else 0 102 103 /-- The cycle clauses `w v (next p) → w v p`, one for every variable `v < V` and position 104 `p < N`. Their variables are numbered `k = w v p`. -/ 105 def cycles (V N : ℕ) : Formula := 106 (List.range (V * N)).map fun k => [⟨k, true⟩, ⟨wv V (k % V) (next N (k / V)), false⟩] 107 108 /-- The number of literal occurrences of a formula. -/ 109 def size (F : Formula) : ℕ := (F.map List.length).sum 110 111 /-- A strict bound on the variable indices of a formula (at least 1). -/ 112 def bound : Formula → ℕ 113 | [] => 1 114 | C :: F => max (C.foldr (fun l b => max (l.index + 1) b) 1) (bound F) 115 116 /-- The splitting step: the chains of all clauses, then the cycle clauses. -/ 117 def split (F : Formula) : Formula := 118 chains (bound F) (size F) 0 F ++ cycles (bound F) (size F) 119 120 -- The reduction 121 122 /-- The reduction on formulas: split, then pad above every variable in use. -/ 123 def transform (F : Formula) : Formula := pad (bound F * size F + size F) (split F) 124 125 /-- The formula a word stands for: the decoded one, or one empty clause if the word encodes 126 no formula. -/ 127 def parseF (w : Word) : Formula := (decodeCNF w).getD [[]] 128 129 /-- The reduction on words. -/ 130 def reduce (w : Word) : Word := encodeCNF (transform (parseF w)) 131 132 /-- The image is a (3,4) formula. -/ 133 axiom transform_isThreeFour (F : Formula) : IsThreeFour (transform F) 134 135 /-- The image is satisfiable exactly when the formula is. -/ 136 axiom transform_sat (F : Formula) : Satisfiable F ↔ Satisfiable (transform F) 137 138 /-- The reduction on words is correct. -/ 139 axiom reduce_correct (w : Word) : w ∈ Lax429075.Satisfiability.SAT ↔ reduce w ∈ SAT34 140 141 /-- The reduction on words is computable in polynomial time. -/ 142 axiom reduce_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id reduce) 143 144 end Lax345332.Construction 145 - def✓
-
no assumptions
Padding makes every clause three literals long. Below the padding base the occurrence counts are those of the split formula, at most three; above it a variable belongs to one gadget of one clause, where it occurs at most four times, the forced variable three times in the gadget and once as padding.
-
no assumptions
Splitting: an assignment of is copied to every position and the link after a literal is set to "no literal so far is true"; conversely the cycle makes all copies of a variable equal, and a chain with all literals false propagates a true link into its last clause. Padding: the gadgets force the padding literals false, and can always be satisfied.
-
The encoding is injective and a decoded word is the encoding of what it decodes to, so the statement is that of the reduction on formulas; a word that encodes nothing is sent to the image of an unsatisfiable formula.
-
The reduction is a word RAM program on the zeros and ones of its input: a finite-state scan decodes the formula into arrays of literal indices, signs and clause numbers; one pass tabulates where each clause ends; the chain clauses are then written clause by clause and the cycle clauses position by position, each with its padding and its three gadgets, in the encoding's unary code. Polynomial time on the word RAM transfers to a Turing machine.
-
Satisfiability is NP-hard by the Cook–Levin theorem, the reduction is correct and runs in polynomial time, and polynomial-time many-one reductions compose.
-
def✓
Lax345332.CopiesThe Reduction from (3,4)-Satisfiability to [2,3]-Bounded 3-SAT
A (3,4) formula with clauses has literal positions; position holds the -th literal of clause . Let be a strict bound on its variables. The construction gives every position its own variable , where is the variable at that position, and produces two kinds of clauses.
- The clauses of three literals are the original clauses, with each literal replaced by the copy for its position and keeping its sign.
- The clauses of two literals are the implications , that is , for every variable and every position , with positions taken cyclically. These chain the copies of around a cycle, so they all take the same value. They are the cycle clauses of the first reduction.
Every literal of the result occurs at most twice. A copy belongs to one position only, so it appears in at most one clause of three literals, and with one sign. In the cycle of its variable it is the consequent of one implication and the antecedent of another, which gives it one positive and one negative occurrence there.
On words, the reduction is the first reduction followed by this construction: a word is sent to the encoding of a (3,4) formula, that formula is decoded, and the construction is applied to it and written in the self-delimiting code.
- def✓
Lax345332.Copies(1st statement) - def✓
Lax345332.Copies(2nd statement) - def✓
Lax345332.Copies(3rd statement) - def✓
Lax345332.Copies(4th statement) - def✓
Lax345332.Copies(5th statement)
1 import Lax345332.TwoThreeSat 2 import Lax345332.Construction 3 … module docstring, 41 lines 45 46 namespace Lax345332.Copies 47 48 open Lax429075.CNF Lax345332.Construction Lax345332.TwoThreeSat Lax434930.PolynomialTime 49 50 -- Positions and copies 51 52 /-- The literal at position `p` of the flattened formula, and a default literal past its 53 end. -/ 54 def litAt (F : Lax429075.CNF.Formula) (p : ℕ) : Literal := (F.flatMap id).getD p dflt 55 56 /-- The number of literal positions: three per clause. -/ 57 def npos (F : Lax429075.CNF.Formula) : ℕ := 3 * F.length 58 59 /-- The number of variables of the result: one copy for every variable and position. -/ 60 def nvars (F : Lax429075.CNF.Formula) : ℕ := bound F * npos F 61 62 /-- The copy of the variable at position `p`. -/ 63 def copyVar (F : Lax429075.CNF.Formula) (p : ℕ) : ℕ := wv (bound F) (litAt F p).index p 64 65 /-- The copy that the cycle clause of `kk` mentions negatively: the same variable at the 66 next position. -/ 67 def succVar (V N kk : ℕ) : ℕ := wv V (kk % V) (next N (kk / V)) 68 69 /-- The bound on the variables is positive, which the `Fin` indices need. -/ 70 theorem bound_pos (F : Lax429075.CNF.Formula) : 0 < bound F := by 71 induction F with 72 | nil => exact Nat.one_pos 73 | cons C F ih => exact lt_of_lt_of_le ih (le_max_right _ _) 74 75 -- The clauses 76 77 /-- The literals of the cycle clause `kk`: the copy `kk`, and the negation of its 78 successor. -/ 79 def twoLit (F : Lax429075.CNF.Formula) (kk : Fin (nvars F)) (α : Fin 2) : 80 Fin (nvars F) × Bool := 81 if α.val = 0 then (kk, true) 82 else (⟨succVar (bound F) (npos F) kk.val % nvars F, Nat.mod_lt _ kk.pos⟩, false) 83 84 /-- The literals of clause `c`: for `t < 3`, the copy at position `3c + t`, with the sign of 85 the original literal. -/ 86 def threeLit (F : Lax429075.CNF.Formula) (c : Fin F.length) (t : Fin 3) : 87 Fin (nvars F) × Bool := 88 (⟨copyVar F (3 * c.val + t.val) % nvars F, 89 Nat.mod_lt _ (Nat.mul_pos (bound_pos F) (Nat.mul_pos (by decide) c.pos))⟩, 90 (litAt F (3 * c.val + t.val)).positive) 91 92 /-- The occurrence condition on the construction: every literal occupies at most two 93 positions. -/ 94 def Occ (F : Lax429075.CNF.Formula) : Prop := 95 ∀ l : Fin (nvars F) × Bool, 96 (Finset.univ.filter fun o : (Fin (nvars F) × Fin 2) ⊕ (Fin F.length × Fin 3) => 97 Sum.elim (fun q => twoLit F q.1 q.2) (fun q => threeLit F q.1 q.2) o = l).card ≤ 2 98 99 instance (F : Lax429075.CNF.Formula) : Decidable (Occ F) := by unfold Occ; infer_instance 100 101 /-- The formula with no variable and no clause. -/ 102 def empty : Lax345332.TwoThreeSat.Formula := 103 ⟨0, 0, 0, fun c => c.elim0, fun c => c.elim0, fun l => l.1.elim0⟩ 104 105 /-- **The construction.** It has `V·N` variables, where `V` bounds the variables of `F` and 106 `N = 3C` is its number of positions; the `V·N` cycle clauses; and the `C` clauses of three 107 literals on the copies. It is the empty formula if the occurrence condition fails. -/ 108 def build (F : Lax429075.CNF.Formula) : Lax345332.TwoThreeSat.Formula := 109 if h : Occ F then ⟨nvars F, nvars F, F.length, twoLit F, threeLit F, h⟩ else empty 110 111 -- The reduction 112 113 /-- The construction on words: decode a formula, apply `build`, and encode the result. -/ 114 def toBounded (w : Word) : Word := encodeFormula (build (parseF w)) 115 116 /-- The reduction from satisfiability: the first reduction, then the construction. -/ 117 def reduceB : Word → Word := toBounded ∘ reduce 118 119 /-- Every clause of the formula has exactly three literals. -/ 120 def Three (F : Lax429075.CNF.Formula) : Prop := ∀ C ∈ F, C.length = 3 121 122 /-- Every literal of the construction occurs at most twice. -/ 123 axiom build_occ (F : Lax429075.CNF.Formula) : Occ F 124 125 /-- The result has at most as many variables as literal positions. -/ 126 axiom build_vars_le_slots (F : Lax429075.CNF.Formula) : (build F).vars ≤ slots (build F) 127 128 /-- On a formula whose clauses all have three literals, the construction is satisfiable 129 exactly when the formula is. -/ 130 axiom build_sat (F : Lax429075.CNF.Formula) (h : Three F) : 131 Satisfiable F ↔ (build F).Satisfiable 132 133 /-- The reduction from satisfiability is correct on words. -/ 134 axiom reduceB_correct (w : Word) : 135 w ∈ Lax429075.Satisfiability.SAT ↔ reduceB w ∈ SAT23 136 137 /-- The construction on words is computable in polynomial time. -/ 138 axiom toBounded_polyTime : Nonempty (Turing.TM2ComputableInPolyTime id id toBounded) 139 140 end Lax345332.Copies 141 -
no assumptions
Two positions carrying the same literal lie on the same side, cycle or clause, by the sign pattern and the injectivity of the copy and successor maps, and there they coincide.
-
no assumptions
There are as many variables as cycle clauses, each of two positions.
-
no assumptions
Forwards, every copy takes the value of its variable; backwards, the cycle clauses force the copies of a variable to agree, and the common value satisfies the original clause.
-
The first step is correct and its image is a formula of three-literal clauses, on which the copy construction preserves and reflects satisfiability; the encoding is injective, so a word in the language is the encoding of the formula built.
-
A word RAM program computes the zeros and ones of the second step: the scan of the first program decodes the (3,4) formula into arrays, and the header, the cycle clauses and the copied clauses are written in the self-delimiting binary code, within a fourth-degree polynomial number of instructions. Polynomial time on the word RAM transfers to a Turing machine.
-
Satisfiability is NP-hard by the Cook–Levin theorem; the reduction to (3,4)-SAT followed by the copy construction is correct and, as the composition of two polynomial-time computations, runs in polynomial time; polynomial-time many-one reductions compose.
Loading the paper…