Skip to content

Commit f7ecf06

Browse files
authored
feat: normalization and ordered IntModule helper theorems (#8645)
This PR adds many helper theorems for the future `IntModule` linear arithmetic procedure in `grind`. It also adds helper theorems for normalizing input atoms and support for disequality in the new linear arithmetic procedure in `grind`.
1 parent b97d35d commit f7ecf06

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

src/Init/Grind/Ordered/Linarith.lean

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,204 @@ instance : LawfulBEq Poly where
229229

230230
attribute [local simp] Poly.denote'_eq_denote
231231

232+
def Poly.leadCoeff (p : Poly) : Int :=
233+
match p with
234+
| .add a _ _ => a
235+
| _ => 1
236+
237+
open IntModule.IsOrdered
238+
239+
/-!
240+
Helper theorems for conflict resolution during model construction.
241+
-/
242+
243+
private theorem le_add_le {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
244+
(h₁ : a ≤ 0) (h₂ : b ≤ 0) : a + b ≤ 0 := by
245+
replace h₁ := add_le_left h₁ b; simp at h₁
246+
exact Preorder.le_trans h₁ h₂
247+
248+
private theorem le_add_lt {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
249+
(h₁ : a ≤ 0) (h₂ : b < 0) : a + b < 0 := by
250+
replace h₁ := add_le_left h₁ b; simp at h₁
251+
exact Preorder.lt_of_le_of_lt h₁ h₂
252+
253+
private theorem lt_add_lt {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] {a b : α}
254+
(h₁ : a < 0) (h₂ : b < 0) : a + b < 0 := by
255+
replace h₁ := add_lt_left h₁ b; simp at h₁
256+
exact Preorder.lt_trans h₁ h₂
257+
258+
private theorem coe_natAbs_nonneg (a : Int) : (a.natAbs : Int) ≥ 0 := by
259+
exact Int.natCast_nonneg a.natAbs
260+
261+
def le_le_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
262+
let a₁ := p₁.leadCoeff.natAbs
263+
let a₂ := p₂.leadCoeff.natAbs
264+
p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
265+
266+
theorem le_le_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
267+
: le_le_combine_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx ≤ 0 → p₃.denote' ctx ≤ 0 := by
268+
simp [le_le_combine_cert]; intro _ h₁ h₂; subst p₃; simp
269+
replace h₁ := hmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
270+
replace h₂ := hmul_nonpos (coe_natAbs_nonneg p₁.leadCoeff) h₂
271+
exact le_add_le h₁ h₂
272+
273+
def le_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
274+
let a₁ := p₁.leadCoeff.natAbs
275+
let a₂ := p₂.leadCoeff.natAbs
276+
a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
277+
278+
theorem le_lt_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
279+
: le_lt_combine_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx < 0 → p₃.denote' ctx < 0 := by
280+
simp [-Int.natAbs_pos, -Int.ofNat_pos, le_lt_combine_cert]; intro hp _ h₁ h₂; subst p₃; simp
281+
replace h₁ := hmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
282+
replace h₂ := hmul_neg (↑p₁.leadCoeff.natAbs) h₂ |>.mp hp
283+
exact le_add_lt h₁ h₂
284+
285+
theorem le_eq_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
286+
: le_le_combine_cert p₁ p₂ p₃ → p₁.denote' ctx ≤ 0 → p₂.denote' ctx = 0 → p₃.denote' ctx ≤ 0 := by
287+
simp [le_le_combine_cert]; intro _ h₁ h₂; subst p₃; simp [h₂]
288+
replace h₁ := hmul_nonpos (coe_natAbs_nonneg p₂.leadCoeff) h₁
289+
assumption
290+
291+
def lt_lt_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
292+
let a₁ := p₁.leadCoeff.natAbs
293+
let a₂ := p₂.leadCoeff.natAbs
294+
a₂ > (0 : Int) && a₁ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
295+
296+
theorem lt_lt_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
297+
: lt_lt_combine_cert p₁ p₂ p₃ → p₁.denote' ctx < 0 → p₂.denote' ctx < 0 → p₃.denote' ctx < 0 := by
298+
simp [-Int.natAbs_pos, -Int.ofNat_pos, lt_lt_combine_cert]; intro hp₁ hp₂ _ h₁ h₂; subst p₃; simp
299+
replace h₁ := hmul_neg (↑p₂.leadCoeff.natAbs) h₁ |>.mp hp₁
300+
replace h₂ := hmul_neg (↑p₁.leadCoeff.natAbs) h₂ |>.mp hp₂
301+
exact lt_add_lt h₁ h₂
302+
303+
def lt_eq_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
304+
let a₁ := p₁.leadCoeff.natAbs
305+
let a₂ := p₂.leadCoeff.natAbs
306+
a₂ > (0 : Int) && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
307+
308+
theorem lt_eq_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
309+
: lt_eq_combine_cert p₁ p₂ p₃ → p₁.denote' ctx < 0 → p₂.denote' ctx = 0 → p₃.denote' ctx < 0 := by
310+
simp [-Int.natAbs_pos, -Int.ofNat_pos, lt_eq_combine_cert]; intro hp₁ _ h₁ h₂; subst p₃; simp [h₂]
311+
replace h₁ := hmul_neg (↑p₂.leadCoeff.natAbs) h₁ |>.mp hp₁
312+
assumption
313+
314+
theorem eq_eq_combine {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
315+
: le_le_combine_cert p₁ p₂ p₃ → p₁.denote' ctx = 0 → p₂.denote' ctx = 0 → p₃.denote' ctx = 0 := by
316+
simp [le_le_combine_cert]; intro _ h₁ h₂; subst p₃; simp [h₁, h₂]
317+
318+
def diseq_split_cert (p₁ p₂ : Poly) : Bool :=
319+
p₂ == p₁.mul (-1)
320+
321+
-- We need `LinearOrder` to use `trichotomy`
322+
theorem diseq_split {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (p₁ p₂ : Poly)
323+
: diseq_split_cert p₁ p₂ → p₁.denote' ctx ≠ 0 → p₁.denote' ctx < 0 ∨ p₂.denote' ctx < 0 := by
324+
simp [diseq_split_cert]; intro _ h₁; subst p₂; simp
325+
cases LinearOrder.trichotomy (p₁.denote ctx) 0
326+
next h => exact Or.inl h
327+
next h =>
328+
apply Or.inr
329+
simp [h₁] at h
330+
rw [← neg_pos_iff, neg_hmul, neg_neg, one_hmul]; assumption
331+
332+
def eq_diseq_combine_cert (p₁ p₂ p₃ : Poly) : Bool :=
333+
let a₁ := p₁.leadCoeff.natAbs
334+
let a₂ := p₂.leadCoeff.natAbs
335+
a₁ ≠ 0 && p₃ == (p₁.mul a₂ |>.combine (p₂.mul a₁))
336+
337+
theorem eq_diseq_combine {α} [IntModule α] [NoNatZeroDivisors α] (ctx : Context α) (p₁ p₂ p₃ : Poly)
338+
: eq_diseq_combine_cert p₁ p₂ p₃ → p₁.denote' ctx = 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≠ 0 := by
339+
simp [- Int.natAbs_eq_zero, -Int.natCast_eq_zero, eq_diseq_combine_cert]; intro hne _ h₁ h₂; subst p₃
340+
simp [h₁, h₂]; intro h
341+
have := no_nat_zero_divisors (p₁.leadCoeff.natAbs) (p₂.denote ctx) hne h
342+
contradiction
343+
344+
def eq_diseq_combine_cert' (p₁ p₂ p₃ : Poly) (k : Int) : Bool :=
345+
p₃ == (p₁.mul k |>.combine p₂)
346+
347+
/-
348+
Special case of `eq_diseq_combine` where leading coefficient `c₁` of `p₁` is `-k*c₂`, where
349+
`c₂` is the leading coefficient of `p₂`.
350+
-/
351+
theorem eq_diseq_combine' {α} [IntModule α] (ctx : Context α) (p₁ p₂ p₃ : Poly) (k : Int)
352+
: eq_diseq_combine_cert' p₁ p₂ p₃ k → p₁.denote' ctx = 0 → p₂.denote' ctx ≠ 0 → p₃.denote' ctx ≠ 0 := by
353+
simp [eq_diseq_combine_cert']; intro _ h₁ h₂; subst p₃
354+
simp [h₁, h₂]
355+
356+
/-!
357+
Helper theorems for internalizing facts into the linear arithmetic procedure
358+
-/
359+
360+
def norm_cert (lhs rhs : Expr) (p : Poly) :=
361+
p == (lhs.sub rhs).norm
362+
363+
theorem eq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
364+
: norm_cert lhs rhs p → lhs.denote ctx = rhs.denote ctx → p.denote ctx = 0 := by
365+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
366+
367+
theorem diseq_norm {α} [IntModule α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
368+
: norm_cert lhs rhs p → lhs.denote ctx ≠ rhs.denote ctx → p.denote ctx ≠ 0 := by
369+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
370+
intro h
371+
replace h := congrArg (rhs.denote ctx + ·) h; simp [sub_eq_add_neg] at h
372+
rw [add_left_comm, ← sub_eq_add_neg, sub_self, add_zero] at h
373+
contradiction
374+
375+
theorem le_norm {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
376+
: norm_cert lhs rhs p → lhs.denote ctx ≤ rhs.denote ctx → p.denote ctx ≤ 0 := by
377+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
378+
replace h₁ := add_le_left h₁ (-rhs.denote ctx)
379+
simp [← sub_eq_add_neg, sub_self] at h₁
380+
assumption
381+
382+
theorem lt_norm {α} [IntModule α] [Preorder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
383+
: norm_cert lhs rhs p → lhs.denote ctx < rhs.denote ctx → p.denote ctx < 0 := by
384+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
385+
replace h₁ := add_lt_left h₁ (-rhs.denote ctx)
386+
simp [← sub_eq_add_neg, sub_self] at h₁
387+
assumption
388+
389+
private theorem le_of_not_lt {α} [LinearOrder α] {a b : α} (h : ¬ a < b) : b ≤ a := by
390+
cases LinearOrder.trichotomy a b
391+
next => contradiction
392+
next h => apply PartialOrder.le_iff_lt_or_eq.mpr; cases h <;> simp [*]
393+
394+
private theorem lt_of_not_le {α} [LinearOrder α] {a b : α} (h : ¬ a ≤ b) : b < a := by
395+
cases LinearOrder.trichotomy a b
396+
next h₁ h₂ => have := Preorder.lt_iff_le_not_le.mp h₂; simp [h] at this
397+
next h =>
398+
cases h
399+
next h => subst a; exact False.elim <| h (Preorder.le_refl b)
400+
next => assumption
401+
402+
theorem not_le_norm {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
403+
: norm_cert rhs lhs p → ¬ lhs.denote ctx ≤ rhs.denote ctx → p.denote ctx < 0 := by
404+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
405+
replace h₁ := lt_of_not_le h₁
406+
replace h₁ := add_lt_left h₁ (-lhs.denote ctx)
407+
simp [← sub_eq_add_neg, sub_self] at h₁
408+
assumption
409+
410+
theorem not_lt_norm {α} [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
411+
: norm_cert rhs lhs p → ¬ lhs.denote ctx < rhs.denote ctx → p.denote ctx ≤ 0 := by
412+
simp [norm_cert]; intro _ h₁; subst p; simp [Expr.denote, h₁, sub_self]
413+
replace h₁ := le_of_not_lt h₁
414+
replace h₁ := add_le_left h₁ (-lhs.denote ctx)
415+
simp [← sub_eq_add_neg, sub_self] at h₁
416+
assumption
417+
418+
/-!
419+
Helper theorems for closing the goal
420+
-/
421+
422+
theorem diseq_unsat {α} [IntModule α] (ctx : Context α) : (Poly.nil).denote ctx ≠ 0 → False := by
423+
simp [Poly.denote]
424+
425+
theorem lt_unsat {α} [IntModule α] [Preorder α] (ctx : Context α) : (Poly.nil).denote ctx < 0 → False := by
426+
simp [Poly.denote]; intro h
427+
have := Preorder.lt_iff_le_not_le.mp h
428+
simp at this
429+
430+
-- TODO: support for the special variable representing (1 : α). Example: `3 * (1 : α) ≤ 0`
431+
232432
end Lean.Grind.Linarith

0 commit comments

Comments
 (0)