Skip to content

Commit 4ba72ae

Browse files
authored
feat: missing normalization rules in grind (#8413)
This PR implements normalization rules that pull universal quantifiers across disjunctions. This is a common normalization step performed by first-order theorem provers.
1 parent e984473 commit 4ba72ae

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

src/Init/Grind/Norm.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ theorem flip_bool_eq (a b : Bool) : (a = b) = (b = a) := by
104104
theorem bool_eq_to_prop (a b : Bool) : (a = b) = ((a = true) = (b = true)) := by
105105
simp
106106

107+
theorem forall_or_forall {α : Sort u} {β : α → Sort v} (p : α → Prop) (q : (a : α) → β a → Prop)
108+
: (∀ a : α, p a ∨ ∀ b : β a, q a b) =
109+
(∀ (a : α) (b : β a), p a ∨ q a b) := by
110+
apply propext; constructor
111+
· intro h a b; cases h a <;> simp [*]
112+
· intro h a
113+
apply Classical.byContradiction
114+
intro h'; simp at h'; have ⟨h₁, b, h₂⟩ := h'
115+
replace h := h a b; simp [h₁, h₂] at h
116+
117+
theorem forall_forall_or {α : Sort u} {β : α → Sort v} (p : α → Prop) (q : (a : α) → β a → Prop)
118+
: (∀ a : α, (∀ b : β a, q a b) ∨ p a) =
119+
(∀ (a : α) (b : β a), q a b ∨ p a) := by
120+
apply propext; constructor
121+
· intro h a b; cases h a <;> simp [*]
122+
· intro h a
123+
apply Classical.byContradiction
124+
intro h'; simp at h'; have ⟨⟨b, h₁⟩, h₂⟩ := h'
125+
replace h := h a b; simp [h₁, h₂] at h
126+
107127
init_grind_norm
108128
/- Pre theorems -/
109129
not_and not_or not_ite not_forall not_exists
@@ -113,6 +133,7 @@ init_grind_norm
113133
/- Post theorems -/
114134
Classical.not_not
115135
ne_eq iff_eq eq_self heq_eq_eq
136+
forall_or_forall forall_forall_or
116137
-- Prop equality
117138
eq_true_eq eq_false_eq not_eq_prop
118139
-- True

tests/lean/run/grind_palindrome2.lean

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,4 @@ theorem checkPalin1_correct' : checkPalin1 xs = true ↔ IsPalindrome xs := by
2525
-- TODO: `IsPalindrome` (without `.eq_1`) produces bad error message.
2626
grind +extAll [IsPalindrome.eq_1]
2727
intro i
28-
fun_induction checkPalin1.go
29-
case case1 j h₁ h₂ ih =>
30-
-- TODO: make sure we don't need `constructor` here. This is a normalization issue.
31-
constructor <;> grind
32-
case case2 j h₁ h₂ =>
33-
-- TODO: fix normalization
34-
simp only [Bool.false_eq_true, false_iff, Classical.not_forall]
35-
grind
36-
case case3 x h =>
37-
grind
28+
fun_induction checkPalin1.go <;> grind

tests/lean/run/grind_t1.lean

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,21 @@ example [BEq α] [LawfulBEq α] (a b : α) : a ≠ b → foo a b = 0 := by
439439

440440
example (p q : Prop) : (p → q) → (¬ p → q) → (p → ¬ q) → (¬p → ¬q) → False := by
441441
grind (splitImp := true)
442+
443+
444+
/-! Pull universal over disjunction -/
445+
446+
opaque p : (i : Nat) → i ≠ 10Prop
447+
448+
-- This example does not require pulling quantifiers
449+
example (h : ∀ i, i > 0 → ∀ h : i ≠ 10, p i h) : p 5 (by decide) := by
450+
grind
451+
452+
-- This one is semantically equivalent to the previous example, but can only be proved by `grind` after
453+
-- we pull universal over disjunctions during normalization.
454+
example (h : ∀ i, (¬i > 0) ∨ ∀ h : i ≠ 10, p i h) : p 5 (by decide) := by
455+
grind
456+
457+
-- Similar to previous test.
458+
example (h : ∀ i, (∀ h : i ≠ 10, p i h) ∨ (¬i > 0)) : p 5 (by decide) := by
459+
grind

0 commit comments

Comments
 (0)