Skip to content

Commit 817d8a6

Browse files
committed
feat: introduce Std.Sat.CNF
1 parent 93ab691 commit 817d8a6

File tree

6 files changed

+504
-0
lines changed

6 files changed

+504
-0
lines changed

src/Std/Sat.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ Authors: Henrik Böving
55
-/
66
prelude
77
import Std.Sat.Basic
8+
import Std.Sat.CNF

src/Std/Sat/CNF.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/-
2+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Henrik Böving
5+
-/
6+
prelude
7+
import Std.Sat.CNF.Basic
8+
import Std.Sat.CNF.Literal
9+
import Std.Sat.CNF.Relabel
10+
import Std.Sat.CNF.RelabelFin

src/Std/Sat/CNF/Basic.lean

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/-
2+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Scott Morrison
5+
-/
6+
prelude
7+
import Init.Data.List.Lemmas
8+
import Std.Sat.CNF.Literal
9+
10+
namespace Std
11+
namespace Sat
12+
13+
/--
14+
A clause in a CNF.
15+
16+
The literal `(i, b)` is satisfied is the assignment to `i` agrees with `b`.
17+
-/
18+
abbrev CNF.Clause (α : Type u) : Type u := List (Literal α)
19+
20+
/--
21+
A CNF formula.
22+
23+
Literals are identified by members of `α`.
24+
-/
25+
abbrev CNF (α : Type u) : Type u := List (CNF.Clause α)
26+
27+
namespace CNF
28+
29+
/--
30+
Evaluating a `Clause` with respect to an assignment `f`.
31+
-/
32+
def Clause.eval (f : α → Bool) (c : Clause α) : Bool := c.any fun (i, n) => f i == n
33+
34+
@[simp] theorem Clause.eval_nil (f : α → Bool) : Clause.eval f [] = false := rfl
35+
@[simp] theorem Clause.eval_succ (f : α → Bool) :
36+
Clause.eval f (i :: c) = (f i.1 == i.2 || Clause.eval f c) := rfl
37+
38+
/--
39+
Evaluating a `CNF` formula with respect to an assignment `f`.
40+
-/
41+
def eval (f : α → Bool) (g : CNF α) : Bool := g.all fun c => c.eval f
42+
43+
@[simp] theorem eval_nil (f : α → Bool) : eval f [] = true := rfl
44+
@[simp] theorem eval_succ (f : α → Bool) : eval f (c :: g) = (c.eval f && eval f g) := rfl
45+
46+
@[simp] theorem eval_append (f : α → Bool) (g h : CNF α) :
47+
eval f (g ++ h) = (eval f g && eval f h) := List.all_append
48+
49+
instance : HSat α (Clause α) where
50+
eval assign clause := Clause.eval assign clause
51+
52+
instance : HSat α (CNF α) where
53+
eval assign cnf := eval assign cnf
54+
55+
@[simp] theorem unsat_nil_iff_false : unsatisfiable α ([] : CNF α) ↔ False :=
56+
fun h => by simp [unsatisfiable, (· ⊨ ·)] at h, by simp⟩
57+
58+
@[simp] theorem sat_nil {assign : α → Bool} : assign ⊨ ([] : CNF α) ↔ True := by
59+
simp [(· ⊨ ·)]
60+
61+
@[simp] theorem unsat_nil_cons {g : CNF α} : unsatisfiable α ([] :: g) ↔ True := by
62+
simp [unsatisfiable, (· ⊨ ·)]
63+
64+
namespace Clause
65+
66+
/--
67+
Literal `a` occurs in `Clause` `c`.
68+
-/
69+
def mem (a : α) (c : Clause α) : Prop := (a, false) ∈ c ∨ (a, true) ∈ c
70+
71+
instance {a : α} {c : Clause α} [DecidableEq α] : Decidable (mem a c) :=
72+
inferInstanceAs <| Decidable (_ ∨ _)
73+
74+
@[simp] theorem not_mem_nil {a : α} : mem a ([] : Clause α) ↔ False := by simp [mem]
75+
@[simp] theorem mem_cons {a : α} : mem a (i :: c : Clause α) ↔ (a = i.1 ∨ mem a c) := by
76+
rcases i with ⟨b, (_|_)⟩
77+
· simp [mem, or_assoc]
78+
· simp [mem]
79+
rw [or_left_comm]
80+
81+
theorem mem_of (h : (a, b) ∈ c) : mem a c := by
82+
cases b
83+
· left; exact h
84+
· right; exact h
85+
86+
theorem eval_congr (f g : α → Bool) (c : Clause α) (w : ∀ i, mem i c → f i = g i) :
87+
eval f c = eval g c := by
88+
induction c
89+
case nil => rfl
90+
case cons i c ih =>
91+
simp only [eval_succ]
92+
rw [ih, w]
93+
· rcases i with ⟨b, (_|_)⟩ <;> simp [mem]
94+
· intro j h
95+
apply w
96+
rcases h with h | h
97+
· left
98+
apply List.mem_cons_of_mem _ h
99+
· right
100+
apply List.mem_cons_of_mem _ h
101+
102+
end Clause
103+
104+
/--
105+
Literal `a` occurs in `CNF` formula `g`.
106+
-/
107+
def mem (a : α) (g : CNF α) : Prop := ∃ c, c ∈ g ∧ c.mem a
108+
109+
instance {a : α} {g : CNF α} [DecidableEq α] : Decidable (mem a g) :=
110+
inferInstanceAs <| Decidable (∃ _, _)
111+
112+
theorem any_nonEmpty_iff_exists_mem {g : CNF α} :
113+
(List.any g fun c => !List.isEmpty c) = true ↔ ∃ a, mem a g := by
114+
simp only [List.any_eq_true, Bool.not_eq_true', List.isEmpty_false_iff_exists_mem, mem,
115+
Clause.mem]
116+
constructor
117+
. intro h
118+
rcases h with ⟨clause, ⟨hclause1, hclause2⟩⟩
119+
rcases hclause2 with ⟨lit, hlit⟩
120+
exists lit.fst, clause
121+
constructor
122+
. assumption
123+
. rcases lit with ⟨_, ⟨_ | _⟩⟩ <;> simp_all
124+
. intro h
125+
rcases h with ⟨lit, clause, ⟨hclause1, hclause2⟩⟩
126+
exists clause
127+
constructor
128+
. assumption
129+
. cases hclause2 with
130+
| inl hl => exact Exists.intro _ hl
131+
| inr hr => exact Exists.intro _ hr
132+
133+
@[simp] theorem not_mem_cons : (¬ ∃ a, mem a g) ↔ ∃ n, g = List.replicate n [] := by
134+
simp only [← any_nonEmpty_iff_exists_mem]
135+
simp only [List.any_eq_true, Bool.not_eq_true', not_exists, not_and, Bool.not_eq_false]
136+
induction g with
137+
| nil =>
138+
simp only [List.not_mem_nil, List.isEmpty_iff, false_implies, forall_const, true_iff]
139+
exact ⟨0, rfl⟩
140+
| cons c g ih =>
141+
simp_all [ih, List.isEmpty_iff]
142+
constructor
143+
· rintro ⟨rfl, n, rfl⟩
144+
exact ⟨n+1, rfl⟩
145+
· rintro ⟨n, h⟩
146+
cases n
147+
· simp at h
148+
· simp_all only [List.replicate, List.cons.injEq, true_and]
149+
exact ⟨_, rfl⟩
150+
151+
instance {g : CNF α} [DecidableEq α] : Decidable (∃ a, mem a g) :=
152+
decidable_of_iff (g.any fun c => !c.isEmpty) any_nonEmpty_iff_exists_mem
153+
154+
@[simp] theorem not_mem_nil {a : α} : mem a ([] : CNF α) ↔ False := by simp [mem]
155+
@[simp] theorem mem_cons {a : α} {i} {c : CNF α} :
156+
mem a (i :: c : CNF α) ↔ (Clause.mem a i ∨ mem a c) := by simp [mem]
157+
158+
theorem mem_of (h : c ∈ g) (w : Clause.mem a c) : mem a g := by
159+
apply Exists.intro c
160+
constructor <;> assumption
161+
162+
@[simp] theorem mem_append {a : α} {x y : CNF α} : mem a (x ++ y) ↔ mem a x ∨ mem a y := by
163+
simp [mem, List.mem_append]
164+
constructor
165+
· rintro ⟨c, (mx | my), mc⟩
166+
· left
167+
exact ⟨c, mx, mc⟩
168+
· right
169+
exact ⟨c, my, mc⟩
170+
· rintro (⟨c, mx, mc⟩ | ⟨c, my, mc⟩)
171+
· exact ⟨c, Or.inl mx, mc⟩
172+
· exact ⟨c, Or.inr my, mc⟩
173+
174+
theorem eval_congr (f g : α → Bool) (x : CNF α) (w : ∀ i, mem i x → f i = g i) :
175+
eval f x = eval g x := by
176+
induction x
177+
case nil => rfl
178+
case cons c x ih =>
179+
simp only [eval_succ]
180+
rw [ih, Clause.eval_congr] <;>
181+
· intro i h
182+
apply w
183+
simp [h]
184+
185+
end CNF
186+
187+
end Sat
188+
end Std

src/Std/Sat/CNF/Literal.lean

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/-
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Josh Clune
5+
-/
6+
prelude
7+
import Init.Data.ToString
8+
import Std.Sat.Basic
9+
10+
namespace Std
11+
namespace Sat
12+
13+
/--
14+
CNF literals identified by some type `α`.
15+
-/
16+
abbrev Literal (α : Type u) := α × Bool
17+
18+
namespace Literal
19+
20+
instance [Hashable α] : Hashable (Literal α) where
21+
hash := fun x => if x.2 then hash x.1 else hash x.1 + 1
22+
23+
instance : HSat α (Literal α) where
24+
eval := fun p l => (p l.1) = l.2
25+
26+
instance (p : α → Bool) (l : Literal α) : Decidable (p ⊨ l) := by
27+
rw [HSat.eval, instHSat]
28+
exact Bool.decEq (p l.fst) l.snd
29+
30+
/--
31+
Flip the polarity of `l`.
32+
-/
33+
def negate (l : Literal α) : Literal α := (l.1, not l.2)
34+
35+
/--
36+
Output `l` as a DIMACS literal identifier.
37+
-/
38+
def dimacs [ToString α] (l : Literal α) : String :=
39+
if l.2 then
40+
s!"{l.1}"
41+
else
42+
s!"-{l.1}"
43+
44+
end Literal
45+
46+
end Sat
47+
end Std

src/Std/Sat/CNF/Relabel.lean

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/-
2+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Scott Morrison
5+
-/
6+
prelude
7+
import Std.Sat.CNF.Basic
8+
9+
namespace Std
10+
namespace Sat
11+
12+
namespace CNF
13+
14+
namespace Clause
15+
16+
/--
17+
Change the literal type in a `Clause` from `α` to `β` by using `f`.
18+
-/
19+
def relabel (f : α → β) (c : Clause α) : Clause β := c.map (fun (i, n) => (f i, n))
20+
21+
@[simp] theorem eval_relabel {f : α → β} {g : β → Bool} {x : Clause α} :
22+
(relabel f x).eval g = x.eval (g ∘ f) := by
23+
induction x <;> simp_all [relabel]
24+
25+
@[simp] theorem relabel_id' : relabel (id : α → α) = id := by funext; simp [relabel]
26+
27+
theorem relabel_congr {x : Clause α} {f g : α → β} (w : ∀ a, mem a x → f a = g a) :
28+
relabel f x = relabel g x := by
29+
simp only [relabel]
30+
rw [List.map_congr_left]
31+
intro ⟨i, b⟩ h
32+
congr
33+
apply w _ (mem_of h)
34+
35+
-- We need the unapplied equality later.
36+
@[simp] theorem relabel_relabel' : relabel f ∘ relabel g = relabel (f ∘ g) := by
37+
funext i
38+
simp only [Function.comp_apply, relabel, List.map_map]
39+
rfl
40+
41+
end Clause
42+
43+
/-! ### Relabelling
44+
45+
It is convenient to be able to construct a CNF using a more complicated literal type,
46+
but eventually we need to embed in `Nat`.
47+
-/
48+
49+
/--
50+
Change the literal type in a `CNF` formula from `α` to `β` by using `f`.
51+
-/
52+
def relabel (f : α → β) (g : CNF α) : CNF β := g.map (Clause.relabel f)
53+
54+
@[simp] theorem eval_relabel (f : α → β) (g : β → Bool) (x : CNF α) :
55+
(relabel f x).eval g = x.eval (g ∘ f) := by
56+
induction x <;> simp_all [relabel]
57+
58+
@[simp] theorem relabel_append : relabel f (g ++ h) = relabel f g ++ relabel f h :=
59+
List.map_append _ _ _
60+
61+
@[simp] theorem relabel_relabel : relabel f (relabel g x) = relabel (f ∘ g) x := by
62+
simp only [relabel, List.map_map, Clause.relabel_relabel']
63+
64+
@[simp] theorem relabel_id : relabel id x = x := by simp [relabel]
65+
66+
theorem relabel_congr {x : CNF α} {f g : α → β} (w : ∀ a, mem a x → f a = g a) :
67+
relabel f x = relabel g x := by
68+
dsimp only [relabel]
69+
rw [List.map_congr_left]
70+
intro c h
71+
apply Clause.relabel_congr
72+
intro a m
73+
exact w _ (mem_of h m)
74+
75+
theorem sat_relabel {x : CNF α} (h : (g ∘ f) ⊨ x) : g ⊨ (relabel f x) := by
76+
simp_all [(· ⊨ ·)]
77+
78+
theorem unsat_relabel {x : CNF α} (f : α → β) (h : unsatisfiable α x)
79+
: unsatisfiable β (relabel f x) := by
80+
simp_all [unsatisfiable, (· ⊨ ·)]
81+
82+
theorem nonempty_or_impossible (x : CNF α) : Nonempty α ∨ ∃ n, x = List.replicate n [] := by
83+
induction x with
84+
| nil => exact Or.inr ⟨0, rfl⟩
85+
| cons c x ih => match c with
86+
| [] => cases ih with
87+
| inl h => left; exact h
88+
| inr h =>
89+
obtain ⟨n, rfl⟩ := h
90+
right
91+
exact ⟨n + 1, rfl⟩
92+
| ⟨a, b⟩ :: c => exact Or.inl ⟨a⟩
93+
94+
theorem unsat_relabel_iff {x : CNF α} {f : α → β}
95+
(w : ∀ {a b}, mem a x → mem b x → f a = f b → a = b) :
96+
unsatisfiable β (relabel f x) ↔ unsatisfiable α x := by
97+
rcases nonempty_or_impossible x with (⟨⟨a₀⟩⟩ | ⟨n, rfl⟩)
98+
· refine ⟨fun h => ?_, unsat_relabel f⟩
99+
have em := Classical.propDecidable
100+
let g : β → α := fun b =>
101+
if h : ∃ a, mem a x ∧ f a = b then h.choose else a₀
102+
have h' := unsat_relabel g h
103+
suffices w : relabel g (relabel f x) = x by
104+
rwa [w] at h'
105+
have : ∀ a, mem a x → g (f a) = a := by
106+
intro a h
107+
dsimp [g]
108+
rw [dif_pos ⟨a, h, rfl⟩]
109+
apply w _ h
110+
· exact (Exists.choose_spec (⟨a, h, rfl⟩ : ∃ a', mem a' x ∧ f a' = f a)).2
111+
· exact (Exists.choose_spec (⟨a, h, rfl⟩ : ∃ a', mem a' x ∧ f a' = f a)).1
112+
rw [relabel_relabel, relabel_congr, relabel_id]
113+
exact this
114+
· cases n <;> simp [unsatisfiable, (· ⊨ ·), relabel, Clause.relabel, List.replicate_succ]
115+
116+
end CNF
117+
118+
end Sat
119+
end Std

0 commit comments

Comments
 (0)