refactor: denote functions in grind (#11071)

This PR ensures that the `denote` functions used to implement
proof-by-reflection terms in `grind` are abbreviations. This change
eliminates the need for the `withAbstractAtoms` gadget.
This commit is contained in:
Leonardo de Moura 2025-11-04 18:34:17 -05:00 committed by GitHub
parent a4e073f565
commit 52e37e0d55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 279 additions and 324 deletions

View file

@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.Int.LemmasAux
public import Init.Data.Int.Cooper
@ -12,9 +11,7 @@ import all Init.Data.Int.Gcd
public import Init.Data.AC
import all Init.Data.AC
import Init.LawfulBEqTactics
public section
namespace Int.Linear
/-! Helper definitions and theorems for constructing linear arithmetic proofs. -/
@ -22,8 +19,7 @@ namespace Int.Linear
abbrev Var := Nat
abbrev Context := Lean.RArray Int
@[expose]
def Var.denote (ctx : Context) (v : Var) : Int :=
abbrev Var.denote (ctx : Context) (v : Var) : Int :=
ctx.get v
inductive Expr where
@ -36,8 +32,7 @@ inductive Expr where
| mulR (a : Expr) (k : Int)
deriving Inhabited, @[expose] BEq
@[expose]
def Expr.denote (ctx : Context) : Expr → Int
abbrev Expr.denote (ctx : Context) : Expr → Int
| .add a b => denote ctx a + denote ctx b
| .sub a b => denote ctx a - denote ctx b
| .neg a => - denote ctx a
@ -46,6 +41,9 @@ def Expr.denote (ctx : Context) : Expr → Int
| .mulL k e => k * denote ctx e
| .mulR e k => denote ctx e * k
set_option allowUnsafeReducibility true
attribute [semireducible] Var.denote Expr.denote
inductive Poly where
| num (k : Int)
| add (k : Int) (v : Var) (p : Poly)
@ -68,35 +66,36 @@ protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
intro _ _; subst k₁ v₁
simp [← ih p₂, ← Bool.and'_eq_and]; rfl
@[expose]
def Poly.denote (ctx : Context) (p : Poly) : Int :=
abbrev Poly.denote (ctx : Context) (p : Poly) : Int :=
match p with
| .num k => k
| .add k v p => k * v.denote ctx + denote ctx p
noncomputable abbrev Poly.denote'.go (ctx : Context) (p : Poly) : Int → Int :=
Poly.rec
(fun k r => Bool.rec
(r + k)
r
(Int.beq' k 0))
(fun k v _ ih r => Bool.rec
(ih (r + k * v.denote ctx))
(ih (r + v.denote ctx))
(Int.beq' k 1))
p
/--
Similar to `Poly.denote`, but produces a denotation better for `simp +arith`.
Remark: we used to convert `Poly` back into `Expr` to achieve that.
-/
@[expose] noncomputable def Poly.denote' (ctx : Context) (p : Poly) : Int :=
noncomputable abbrev Poly.denote' (ctx : Context) (p : Poly) : Int :=
Poly.rec (fun k => k)
(fun k v p _ => Bool.rec
(go p (k * v.denote ctx))
(go p (v.denote ctx))
(denote'.go ctx p (k * v.denote ctx))
(denote'.go ctx p (v.denote ctx))
(Int.beq' k 1))
p
where
go (p : Poly) : Int → Int :=
Poly.rec
(fun k r => Bool.rec
(r + k)
r
(Int.beq' k 0))
(fun k v _ ih r => Bool.rec
(ih (r + k * v.denote ctx))
(ih (r + v.denote ctx))
(Int.beq' k 1))
p
attribute [semireducible] Poly.denote Poly.denote' Poly.denote'.go
@[simp] theorem Poly.denote'_go_eq_denote (ctx : Context) (p : Poly) (r : Int) : denote'.go ctx p r = p.denote ctx + r := by
induction p generalizing r

View file

@ -28,10 +28,10 @@ abbrev Context := Lean.RArray Nat
/--
When encoding polynomials. We use `fixedVar` for encoding numerals.
The denotation of `fixedVar` is always `1`. -/
def fixedVar := 100000000 -- Any big number should work here
abbrev fixedVar := 100000000 -- Any big number should work here
def Var.denote (ctx : Context) (v : Var) : Nat :=
bif v == fixedVar then 1 else ctx.get v
noncomputable abbrev Var.denote (ctx : Context) (v : Var) : Nat :=
Bool.rec (ctx.get v) 1 (Nat.beq v fixedVar)
inductive Expr where
| num (v : Nat)
@ -41,7 +41,7 @@ inductive Expr where
| mulR (a : Expr) (k : Nat)
deriving Inhabited, BEq
def Expr.denote (ctx : Context) : Expr → Nat
noncomputable abbrev Expr.denote (ctx : Context) : Expr → Nat
| .add a b => Nat.add (denote ctx a) (denote ctx b)
| .num k => k
| .var v => v.denote ctx
@ -50,7 +50,7 @@ def Expr.denote (ctx : Context) : Expr → Nat
abbrev Poly := List (Nat × Var)
def Poly.denote (ctx : Context) (p : Poly) : Nat :=
noncomputable abbrev Poly.denote (ctx : Context) (p : Poly) : Nat :=
match p with
| [] => 0
| (k, v) :: p => Nat.add (Nat.mul k (v.denote ctx)) (denote ctx p)
@ -113,9 +113,14 @@ def Poly.isNonZero (p : Poly) : Bool :=
| [] => false
| (k, v) :: p => bif v == fixedVar then k > 0 else isNonZero p
def Poly.denote_eq (ctx : Context) (mp : Poly × Poly) : Prop := mp.1.denote ctx = mp.2.denote ctx
abbrev Poly.denote_eq (ctx : Context) (mp : Poly × Poly) : Prop :=
mp.1.denote ctx = mp.2.denote ctx
def Poly.denote_le (ctx : Context) (mp : Poly × Poly) : Prop := mp.1.denote ctx ≤ mp.2.denote ctx
abbrev Poly.denote_le (ctx : Context) (mp : Poly × Poly) : Prop :=
mp.1.denote ctx ≤ mp.2.denote ctx
set_option allowUnsafeReducibility true
attribute [semireducible] Poly.denote_eq Poly.denote_le
def Expr.toPoly (e : Expr) :=
go 1 e []
@ -146,7 +151,7 @@ structure ExprCnstr where
lhs : Expr
rhs : Expr
def PolyCnstr.denote (ctx : Context) (c : PolyCnstr) : Prop :=
abbrev PolyCnstr.denote (ctx : Context) (c : PolyCnstr) : Prop :=
bif c.eq then
Poly.denote_eq ctx (c.lhs, c.rhs)
else
@ -168,7 +173,7 @@ def PolyCnstr.isValid (c : PolyCnstr) : Bool :=
else
c.lhs.isZero
def ExprCnstr.denote (ctx : Context) (c : ExprCnstr) : Prop :=
abbrev ExprCnstr.denote (ctx : Context) (c : ExprCnstr) : Prop :=
bif c.eq then
c.lhs.denote ctx = c.rhs.denote ctx
else

View file

@ -4,12 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.List.BasicAux
public section
namespace Nat.SOM
open Linear (Var hugeFuel Context Var.denote)
@ -21,7 +18,9 @@ inductive Expr where
| mul (a b : Expr)
deriving Inhabited
def Expr.denote (ctx : Context) : Expr → Nat
set_option allowUnsafeReducibility true
noncomputable abbrev Expr.denote (ctx : Context) : Expr → Nat
| num n => n
| var v => v.denote ctx
| add a b => Nat.add (a.denote ctx) (b.denote ctx)
@ -29,10 +28,12 @@ def Expr.denote (ctx : Context) : Expr → Nat
abbrev Mon := List Var
def Mon.denote (ctx : Context) : Mon → Nat
noncomputable abbrev Mon.denote (ctx : Context) : Mon → Nat
| [] => 1
| v::vs => Nat.mul (v.denote ctx) (denote ctx vs)
attribute [semireducible] Expr.denote Mon.denote
def Mon.mul (m₁ m₂ : Mon) : Mon :=
go hugeFuel m₁ m₂
where
@ -53,10 +54,12 @@ where
abbrev Poly := List (Nat × Mon)
def Poly.denote (ctx : Context) : Poly → Nat
noncomputable abbrev Poly.denote (ctx : Context) : Poly → Nat
| [] => 0
| (k, m) :: p => Nat.add (Nat.mul k (m.denote ctx)) (denote ctx p)
attribute [semireducible] Poly.denote
def Poly.add (p₁ p₂ : Poly) : Poly :=
go hugeFuel p₁ p₂
where

View file

@ -36,7 +36,7 @@ inductive RArray (α : Type u) : Type u where
variable {α : Type u}
/-- The crucial operation, written with very little abstractional overhead -/
noncomputable def RArray.get (a : RArray α) (n : Nat) : α :=
noncomputable abbrev RArray.get (a : RArray α) (n : Nat) : α :=
RArray.rec (fun x => x) (fun p _ _ l r => (Nat.ble p n).rec l r) a
private theorem RArray.get_eq_def (a : RArray α) (n : Nat) :

View file

@ -39,10 +39,10 @@ inductive Expr where
abbrev Context (α : Type u) := RArray α
def Var.denote {α} (ctx : Context α) (v : Var) : α :=
abbrev Var.denote {α} (ctx : Context α) (v : Var) : α :=
ctx.get v
def Expr.denote {α} [IntModule α] (ctx : Context α) : Expr → α
abbrev Expr.denote {α} [IntModule α] (ctx : Context α) : Expr → α
| zero => 0
| .var v => v.denote ctx
| .add a b => denote ctx a + denote ctx b
@ -56,25 +56,25 @@ inductive Poly where
| add (k : Int) (v : Var) (p : Poly)
deriving BEq, ReflBEq, LawfulBEq, Repr
def Poly.denote {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
abbrev Poly.denote {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
match p with
| .nil => 0
| .add k v p => k • v.denote ctx + denote ctx p
abbrev Poly.denote'.go {α} [IntModule α] (ctx : Context α) (r : α) (p : Poly) : α :=
match p with
| .nil => r
| .add 1 v p => go ctx (r + v.denote ctx) p
| .add k v p => go ctx (r + k • v.denote ctx) p
/--
Similar to `Poly.denote`, but produces a denotation better for normalization.
-/
def Poly.denote' {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
abbrev Poly.denote' {α} [IntModule α] (ctx : Context α) (p : Poly) : α :=
match p with
| .nil => 0
| .add 1 v p => go (v.denote ctx) p
| .add k v p => go (k • v.denote ctx) p
where
go (r : α) (p : Poly) : α :=
match p with
| .nil => r
| .add 1 v p => go (r + v.denote ctx) p
| .add k v p => go (r + k • v.denote ctx) p
| .add 1 v p => denote'.go ctx (v.denote ctx) p
| .add k v p => denote'.go ctx (k • v.denote ctx) p
-- Helper instance for `ac_rfl`
local instance {α} [IntModule α] : Std.Associative (· + · : ααα) where
@ -83,6 +83,8 @@ local instance {α} [IntModule α] : Std.Associative (· + · : ααα
local instance {α} [IntModule α] : Std.Commutative (· + · : ααα) where
comm := AddCommMonoid.add_comm
set_option allowUnsafeReducibility true in
attribute [semireducible] Poly.denote' Poly.denote'.go in
private theorem Poly.denote'_go_eq_denote {α} [IntModule α] (ctx : Context α) (p : Poly) (r : α) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next ih => rw [ih]; ac_rfl

View file

@ -42,16 +42,18 @@ inductive Expr where
abbrev Context (α : Type u) := RArray α
def Var.denote {α} (ctx : Context α) (v : Var) : α :=
set_option allowUnsafeReducibility true
abbrev Var.denote {α} (ctx : Context α) (v : Var) : α :=
ctx.get v
noncomputable def denoteInt {α} [Ring α] (k : Int) : α :=
noncomputable abbrev denoteInt {α} [Ring α] (k : Int) : α :=
Bool.rec
(OfNat.ofNat (α := α) k.natAbs)
(- OfNat.ofNat (α := α) k.natAbs)
(Int.blt' k 0)
noncomputable def Expr.denote {α} [Ring α] (ctx : Context α) (e : Expr) : α :=
noncomputable abbrev Expr.denote {α} [Ring α] (ctx : Context α) (e : Expr) : α :=
Expr.rec
(fun k => denoteInt k)
(fun k => NatCast.natCast (R := α) k)
@ -64,6 +66,8 @@ noncomputable def Expr.denote {α} [Ring α] (ctx : Context α) (e : Expr) : α
(fun _ k ih => ih ^ k)
e
attribute [semireducible] Var.denote denoteInt Expr.denote
structure Power where
x : Var
k : Nat
@ -78,13 +82,15 @@ protected noncomputable def Power.beq' (pw₁ pw₂ : Power) : Bool :=
def Power.varLt (p₁ p₂ : Power) : Bool :=
p₁.x.blt p₂.x
def Power.denote {α} [Semiring α] (ctx : Context α) : Power → α
abbrev Power.denote {α} [Semiring α] (ctx : Context α) : Power → α
| {x, k} =>
match k with
| 0 => 1
| 1 => x.denote ctx
| k => x.denote ctx ^ k
attribute [semireducible] Power.denote
inductive Mon where
| unit
| mult (p : Power) (m : Mon)
@ -102,19 +108,21 @@ protected noncomputable def Mon.beq' (m₁ : Mon) : Mon → Bool :=
simp [← ih m₂, ← Bool.and'_eq_and]
rfl
def Mon.denote {α} [Semiring α] (ctx : Context α) : Mon → α
abbrev Mon.denote {α} [Semiring α] (ctx : Context α) : Mon → α
| unit => 1
| .mult p m => p.denote ctx * denote ctx m
def Mon.denote' {α} [Semiring α] (ctx : Context α) (m : Mon) : α :=
abbrev Mon.denote'.go [Semiring α] (ctx : Context α) (m : Mon) (acc : α) : α :=
match m with
| .unit => acc
| .mult pw m => go ctx m (acc * (pw.denote ctx))
abbrev Mon.denote' {α} [Semiring α] (ctx : Context α) (m : Mon) : α :=
match m with
| .unit => 1
| .mult pw m => go m (pw.denote ctx)
where
go (m : Mon) (acc : α) : α :=
match m with
| .unit => acc
| .mult pw m => go m (acc * (pw.denote ctx))
| .mult pw m => denote'.go ctx m (pw.denote ctx)
attribute [semireducible] Mon.denote Mon.denote' Mon.denote'.go
def Mon.ofVar (x : Var) : Mon :=
.mult { x, k := 1 } .unit
@ -328,27 +336,29 @@ protected noncomputable def Poly.beq' (p₁ : Poly) : Poly → Bool :=
intro _ _; subst k₁ m₁
simp [← ih p₂, ← Bool.and'_eq_and]; rfl
def Poly.denote [Ring α] (ctx : Context α) (p : Poly) : α :=
abbrev Poly.denote [Ring α] (ctx : Context α) (p : Poly) : α :=
match p with
| .num k => Int.cast k
| .add k m p => k • (m.denote ctx) + denote ctx p
def Poly.denote' [Ring α] (ctx : Context α) (p : Poly) : α :=
match p with
| .num k => Int.cast k
| .add k m p => go p (denoteTerm k m)
where
denoteTerm (k : Int) (m : Mon) : α :=
bif k == 1 then
m.denote' ctx
else
k • m.denote' ctx
abbrev denoteTerm [Ring α] (ctx : Context α) (k : Int) (m : Mon) : α :=
bif k == 1 then
m.denote' ctx
else
k • m.denote' ctx
go (p : Poly) (acc : α) : α :=
abbrev Poly.denote'.go [Ring α] (ctx : Context α) (p : Poly) (acc : α) : α :=
match p with
| .num 0 => acc
| .num k => acc + Int.cast k
| .add k m p => go p (acc + denoteTerm k m)
| .add k m p => go ctx p (acc + denoteTerm ctx k m)
abbrev Poly.denote' [Ring α] (ctx : Context α) (p : Poly) : α :=
match p with
| .num k => Int.cast k
| .add k m p => denote'.go ctx p (denoteTerm ctx k m)
attribute [semireducible] Poly.denote Poly.denote' Poly.denote'.go denoteTerm
def Poly.ofMon (m : Mon) : Poly :=
.add 1 m (.num 0)
@ -995,8 +1005,8 @@ theorem Mon.eq_of_revlex {m₁ m₂ : Mon} : revlex m₁ m₂ = .eq → m₁ = m
theorem Mon.eq_of_grevlex {m₁ m₂ : Mon} : grevlex m₁ m₂ = .eq → m₁ = m₂ := by
simp [grevlex]; intro; apply eq_of_revlex
theorem Poly.denoteTerm_eq {α} [Ring α] (ctx : Context α) (k : Int) (m : Mon) : denote'.denoteTerm ctx k m = k * m.denote ctx := by
simp [denote'.denoteTerm, Mon.denote'_eq_denote, cond_eq_ite, zsmul_eq_intCast_mul]; intro; subst k; rw [Ring.intCast_one, Semiring.one_mul]
theorem Poly.denoteTerm_eq {α} [Ring α] (ctx : Context α) (k : Int) (m : Mon) : denoteTerm ctx k m = k * m.denote ctx := by
simp [denoteTerm, Mon.denote'_eq_denote, cond_eq_ite, zsmul_eq_intCast_mul]; intro; subst k; rw [Ring.intCast_one, Semiring.one_mul]
theorem Poly.denote'_eq_denote {α} [Ring α] (ctx : Context α) (p : Poly) : p.denote' ctx = p.denote ctx := by
cases p <;> simp [denote', denote, denoteTerm_eq, zsmul_eq_intCast_mul]

View file

@ -396,41 +396,38 @@ private def norm (vars : PArray Expr) (lhs rhs lhs' rhs' : RingExpr) : NormResul
def mkLeIffProof (leInst ltInst isPreorderInst orderedRingInst : Expr) (lhs rhs lhs' rhs' : RingExpr) : RingM Expr := do
let ring ← getCommRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp6 (mkConst ``Grind.CommRing.le_norm_expr [ring.u]) ring.type ring.commRingInst leInst ltInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let leFn := mkApp2 (mkConst ``LE.le [ring.u]) ring.type leInst
let le := mkApp2 leFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let le' := mkApp2 leFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq le le'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp6 (mkConst ``Grind.CommRing.le_norm_expr [ring.u]) ring.type ring.commRingInst leInst ltInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let leFn := mkApp2 (mkConst ``LE.le [ring.u]) ring.type leInst
let le := mkApp2 leFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let le' := mkApp2 leFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq le le'
return mkExpectedPropHint h expected
def mkLtIffProof (leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst : Expr) (lhs rhs lhs' rhs' : RingExpr) : RingM Expr := do
let ring ← getCommRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp7 (mkConst ``Grind.CommRing.lt_norm_expr [ring.u]) ring.type ring.commRingInst leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let ltFn := mkApp2 (mkConst ``LT.lt [ring.u]) ring.type ltInst
let lt := mkApp2 ltFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let lt' := mkApp2 ltFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq lt lt'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp7 (mkConst ``Grind.CommRing.lt_norm_expr [ring.u]) ring.type ring.commRingInst leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let ltFn := mkApp2 (mkConst ``LT.lt [ring.u]) ring.type ltInst
let lt := mkApp2 ltFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let lt' := mkApp2 ltFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq lt lt'
return mkExpectedPropHint h expected
def mkEqIffProof (lhs rhs lhs' rhs' : RingExpr) : RingM Expr := do
let ring ← getCommRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.eq_norm_expr [ring.u]) ring.type ring.commRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let eq := mkApp2 eqFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let eq' := mkApp2 eqFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq eq eq'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.eq_norm_expr [ring.u]) ring.type ring.commRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let eq := mkApp2 eqFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let eq' := mkApp2 eqFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq eq eq'
return mkExpectedPropHint h expected
/--
Given `e` and `e'` s.t. `e.toPoly == e'.toPoly`, returns a proof that `e.denote ctx = e'.denote ctx`
@ -438,52 +435,48 @@ Given `e` and `e'` s.t. `e.toPoly == e'.toPoly`, returns a proof that `e.denote
def mkTermEqProof (e e' : RingExpr) : RingM Expr := do
let ring ← getCommRing
let { lhs, lhs', vars, .. } := norm ring.vars e (.num 0) e' (.num 0)
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.Expr.eq_of_toPoly_eq [ring.u]) ring.type ring.commRingInst
let h := mkApp4 h ctx (toExpr lhs) (toExpr lhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let expected := mkApp2 eqFn (← lhs.denoteExpr' vars) (← lhs'.denoteExpr' vars)
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.Expr.eq_of_toPoly_eq [ring.u]) ring.type ring.commRingInst
let h := mkApp4 h ctx (toExpr lhs) (toExpr lhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let expected := mkApp2 eqFn (← lhs.denoteExpr' vars) (← lhs'.denoteExpr' vars)
return mkExpectedPropHint h expected
def mkNonCommLeIffProof (leInst ltInst isPreorderInst orderedRingInst : Expr) (lhs rhs lhs' rhs' : RingExpr) : NonCommRingM Expr := do
let ring ← getRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp6 (mkConst ``Grind.CommRing.le_norm_expr_nc [ring.u]) ring.type ring.ringInst leInst ltInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let leFn := mkApp2 (mkConst ``LE.le [ring.u]) ring.type leInst
let le := mkApp2 leFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let le' := mkApp2 leFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq le le'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp6 (mkConst ``Grind.CommRing.le_norm_expr_nc [ring.u]) ring.type ring.ringInst leInst ltInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let leFn := mkApp2 (mkConst ``LE.le [ring.u]) ring.type leInst
let le := mkApp2 leFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let le' := mkApp2 leFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq le le'
return mkExpectedPropHint h expected
def mkNonCommLtIffProof (leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst : Expr) (lhs rhs lhs' rhs' : RingExpr) : NonCommRingM Expr := do
let ring ← getRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp7 (mkConst ``Grind.CommRing.lt_norm_expr_nc [ring.u]) ring.type ring.ringInst leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let ltFn := mkApp2 (mkConst ``LT.lt [ring.u]) ring.type ltInst
let lt := mkApp2 ltFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let lt' := mkApp2 ltFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq lt lt'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp7 (mkConst ``Grind.CommRing.lt_norm_expr_nc [ring.u]) ring.type ring.ringInst leInst ltInst lawfulOrdLtInst isPreorderInst orderedRingInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let ltFn := mkApp2 (mkConst ``LT.lt [ring.u]) ring.type ltInst
let lt := mkApp2 ltFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let lt' := mkApp2 ltFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq lt lt'
return mkExpectedPropHint h expected
def mkNonCommEqIffProof (lhs rhs lhs' rhs' : RingExpr) : NonCommRingM Expr := do
let ring ← getRing
let { lhs, rhs, lhs', rhs', vars } := norm ring.vars lhs rhs lhs' rhs'
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.eq_norm_expr_nc [ring.u]) ring.type ring.ringInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let eq := mkApp2 eqFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let eq' := mkApp2 eqFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq eq eq'
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.eq_norm_expr_nc [ring.u]) ring.type ring.ringInst
let h := mkApp6 h ctx (toExpr lhs) (toExpr rhs) (toExpr lhs') (toExpr rhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let eq := mkApp2 eqFn (← lhs.denoteExpr' vars) (← rhs.denoteExpr' vars)
let eq' := mkApp2 eqFn (← lhs'.denoteExpr' vars) (← rhs'.denoteExpr' vars)
let expected := mkPropEq eq eq'
return mkExpectedPropHint h expected
/--
Given `e` and `e'` s.t. `e.toPoly_nc == e'.toPoly_nc`, returns a proof that `e.denote ctx = e'.denote ctx`
@ -491,12 +484,11 @@ Given `e` and `e'` s.t. `e.toPoly_nc == e'.toPoly_nc`, returns a proof that `e.d
def mkNonCommTermEqProof (e e' : RingExpr) : NonCommRingM Expr := do
let ring ← getRing
let { lhs, lhs', vars, .. } := norm ring.vars e (.num 0) e' (.num 0)
withAbstractAtoms vars ring.type fun vars => do
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.Expr.eq_of_toPoly_nc_eq [ring.u]) ring.type ring.ringInst
let h := mkApp4 h ctx (toExpr lhs) (toExpr lhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let expected := mkApp2 eqFn (← lhs.denoteExpr' vars) (← lhs'.denoteExpr' vars)
return mkExpectedPropHint h expected
let ctx ← toContextExpr vars
let h := mkApp2 (mkConst ``Grind.CommRing.Expr.eq_of_toPoly_nc_eq [ring.u]) ring.type ring.ringInst
let h := mkApp4 h ctx (toExpr lhs) (toExpr lhs') eagerReflBoolTrue
let eqFn := mkApp (mkConst ``Eq [Level.succ ring.u]) ring.type
let expected := mkApp2 eqFn (← lhs.denoteExpr' vars) (← lhs'.denoteExpr' vars)
return mkExpectedPropHint h expected
end Lean.Meta.Grind.Arith.CommRing

View file

@ -10,27 +10,6 @@ public import Lean.Meta.SynthInstance
public import Init.Data.Rat.Basic
public section
namespace Lean.Meta.Grind.Arith
/--
To prevent the kernel from accidentally reducing the atoms in the equation while typechecking,
we abstract over them.
-/
def withAbstractAtoms [Monad m] [MonadControlT MetaM m] [MonadLiftT CoreM m] [MonadLiftT MetaM m]
(atoms : Array Expr) (type : Expr) (k : Array Expr → m Expr) : m Expr := do
let rec go (i : Nat) (atoms' : Array Expr) (xs : Array Expr) (args : Array Expr) : m Expr := do
if h : i < atoms.size then
let atom := atoms[i]
if atom.isFVar then
go (i+1) (atoms'.push atom) xs args
else
withLocalDeclD (← mkFreshUserName `x) type fun x =>
go (i+1) (atoms'.push x) (xs.push x) (args.push atom)
else
let p ← k atoms'
if xs.isEmpty then
return p
else
return mkAppN (← mkLambdaFVars xs p) args
go 0 #[] #[] #[]
/-- Returns `true` if `e` is a numeral and has type `Nat`. -/
def isNatNum (e : Expr) : Bool := Id.run do

View file

@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Simp.Arith.Int.Basic
public import Lean.Meta.Tactic.Simp.Arith.Int.Simp

View file

@ -4,11 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Simp.Arith.Util
public import Lean.Meta.Tactic.Simp.Arith.Int.Basic
public section
def Int.Linear.Poly.gcdAll : Poly → Nat
@ -35,77 +33,75 @@ namespace Lean.Meta.Simp.Arith.Int
def simpEq? (e : Expr) : MetaM (Option (Expr × Expr)) := do
let some (a, b, atoms) ← eqCnstr? e | return none
withAbstractAtoms atoms ``Int fun atoms => do
let e := mkIntEq (← a.denoteExpr (atoms[·]!)) (← b.denoteExpr (atoms[·]!))
let p := a.sub b |>.norm
if p.isUnsatEq then
let r := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.eq_eq_false) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
let e := mkIntEq (← a.denoteExpr (atoms[·]!)) (← b.denoteExpr (atoms[·]!))
let p := a.sub b |>.norm
if p.isUnsatEq then
let r := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.eq_eq_false) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.isValidEq then
let r := mkConst ``True
let h := mkApp4 (mkConst ``Int.Linear.eq_eq_true) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.toExpr == a && b == .num 0 then
return none
else match p with
| .add 1 x (.add (-1) y (.num 0)) =>
let r := mkIntEq atoms[x]! atoms[y]!
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_var) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr x) (toExpr y) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.isValidEq then
let r := mkConst ``True
let h := mkApp4 (mkConst ``Int.Linear.eq_eq_true) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
| .add 1 x (.num k) =>
let r := mkIntEq atoms[x]! (toExpr (-k))
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_var_const) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr x) (toExpr (-k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.toExpr == a && b == .num 0 then
return none
else match p with
| .add 1 x (.add (-1) y (.num 0)) =>
let r := mkIntEq atoms[x]! atoms[y]!
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_var) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr x) (toExpr y) eagerReflBoolTrue
| _ =>
let k := p.gcdCoeffs'
if k == 1 then
let r := mkIntEq (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp5 (mkConst ``Int.Linear.norm_eq) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
| .add 1 x (.num k) =>
let r := mkIntEq atoms[x]! (toExpr (-k))
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_var_const) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr x) (toExpr (-k)) eagerReflBoolTrue
else if p.getConst % k == 0 then
let p := p.div k
let r := mkIntEq (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_coeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else
let r := mkConst ``False
let h := mkApp5 (mkConst ``Int.Linear.eq_eq_false_of_divCoeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
| _ =>
let k := p.gcdCoeffs'
if k == 1 then
let r := mkIntEq (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp5 (mkConst ``Int.Linear.norm_eq) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.getConst % k == 0 then
let p := p.div k
let r := mkIntEq (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp6 (mkConst ``Int.Linear.norm_eq_coeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else
let r := mkConst ``False
let h := mkApp5 (mkConst ``Int.Linear.eq_eq_false_of_divCoeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
def simpLe? (e : Expr) (checkIfModified : Bool) : MetaM (Option (Expr × Expr)) := do
-- If `e` is not already a `≤`, then we should not check whether it has changed.
let checkIfModified := e.isAppOf ``LE.le && checkIfModified
let some (a, b, atoms) ← leCnstr? e | return none
withAbstractAtoms atoms ``Int fun atoms => do
let e := mkIntLE (← a.denoteExpr (atoms[·]!)) (← b.denoteExpr (atoms[·]!))
let p := a.sub b |>.norm
if p.isUnsatLe then
let r := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.le_eq_false) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
let e := mkIntLE (← a.denoteExpr (atoms[·]!)) (← b.denoteExpr (atoms[·]!))
let p := a.sub b |>.norm
if p.isUnsatLe then
let r := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.le_eq_false) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.isValidLe then
let r := mkConst ``True
let h := mkApp4 (mkConst ``Int.Linear.le_eq_true) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if checkIfModified && p.toExpr == a && b == .num 0 then
return none
else
let k := p.gcdCoeffs'
if k == 1 then
let r := mkIntLE (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp5 (mkConst ``Int.Linear.norm_le) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if p.isValidLe then
let r := mkConst ``True
let h := mkApp4 (mkConst ``Int.Linear.le_eq_true) (← toContextExpr atoms) (toExpr a) (toExpr b) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
else if checkIfModified && p.toExpr == a && b == .num 0 then
return none
else
let k := p.gcdCoeffs'
if k == 1 then
let r := mkIntLE (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h := mkApp5 (mkConst ``Int.Linear.norm_le) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
let tight := p.getConst % k != 0
let p := p.div k
let r := mkIntLE (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h ← if tight then
pure <| mkApp6 (mkConst ``Int.Linear.norm_le_coeff_tight) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
else
let tight := p.getConst % k != 0
let p := p.div k
let r := mkIntLE (← p.denoteExpr (atoms[·]!)) (mkIntLit 0)
let h ← if tight then
pure <| mkApp6 (mkConst ``Int.Linear.norm_le_coeff_tight) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
else
pure <| mkApp6 (mkConst ``Int.Linear.norm_le_coeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
pure <| mkApp6 (mkConst ``Int.Linear.norm_le_coeff) (← toContextExpr atoms) (toExpr a) (toExpr b) (toExpr p) (toExpr (Int.ofNat k)) eagerReflBoolTrue
return some (r, mkExpectedPropHint h (mkPropEq e r))
def simpRel? (e : Expr) : MetaM (Option (Expr × Expr)) := do
if let some arg := e.not? then
@ -139,37 +135,35 @@ def simpRel? (e : Expr) : MetaM (Option (Expr × Expr)) := do
def simpDvd? (e : Expr) : MetaM (Option (Expr × Expr)) := do
let some (d, e, atoms) ← dvdCnstr? e | return none
if d == 0 then return none
withAbstractAtoms atoms ``Int fun atoms => do
let lhs := mkIntDvd (toExpr d) (← e.denoteExpr (atoms[·]!))
let p := e.norm
let g := p.gcdCoeffs d
if p.getConst % g == 0 then
let p := p.div g
let d' := d / g
if e == p.toExpr then
return none
let rhs := mkIntDvd (toExpr d') (← p.denoteExpr (atoms[·]!))
let h ← if g == 1 then
pure <| mkApp5 (mkConst ``Int.Linear.norm_dvd) (← toContextExpr atoms) (toExpr d) (toExpr e) (toExpr p) eagerReflBoolTrue
else
pure <| mkApp7 (mkConst ``Int.Linear.norm_dvd_gcd) (← toContextExpr atoms) (toExpr d) (toExpr e) (toExpr d') (toExpr p) (toExpr g) eagerReflBoolTrue
return some (rhs, mkExpectedPropHint h (mkPropEq lhs rhs))
let lhs := mkIntDvd (toExpr d) (← e.denoteExpr (atoms[·]!))
let p := e.norm
let g := p.gcdCoeffs d
if p.getConst % g == 0 then
let p := p.div g
let d' := d / g
if e == p.toExpr then
return none
let rhs := mkIntDvd (toExpr d') (← p.denoteExpr (atoms[·]!))
let h ← if g == 1 then
pure <| mkApp5 (mkConst ``Int.Linear.norm_dvd) (← toContextExpr atoms) (toExpr d) (toExpr e) (toExpr p) eagerReflBoolTrue
else
let rhs := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.dvd_eq_false) (← toContextExpr atoms) (toExpr d) (toExpr e) eagerReflBoolTrue
return some (rhs, mkExpectedPropHint h (mkPropEq lhs rhs))
pure <| mkApp7 (mkConst ``Int.Linear.norm_dvd_gcd) (← toContextExpr atoms) (toExpr d) (toExpr e) (toExpr d') (toExpr p) (toExpr g) eagerReflBoolTrue
return some (rhs, mkExpectedPropHint h (mkPropEq lhs rhs))
else
let rhs := mkConst ``False
let h := mkApp4 (mkConst ``Int.Linear.dvd_eq_false) (← toContextExpr atoms) (toExpr d) (toExpr e) eagerReflBoolTrue
return some (rhs, mkExpectedPropHint h (mkPropEq lhs rhs))
def simpExpr? (lhs : Expr) : MetaM (Option (Expr × Expr)) := do
let (e, ctx) ← toLinearExpr lhs
withAbstractAtoms ctx ``Int fun ctx => do
let p := e.norm
let e' := p.toExpr
if e != e' then
let h := mkApp4 (mkConst ``Int.Linear.Expr.eq_of_norm_eq) (← toContextExpr ctx) (toExpr e) (toExpr p) eagerReflBoolTrue
let lhs ← e.denoteExpr (ctx[·]!)
let rhs ← p.denoteExpr (ctx[·]!)
return some (rhs, mkExpectedPropHint h (mkIntEq lhs rhs))
else
return none
let p := e.norm
let e' := p.toExpr
if e != e' then
let h := mkApp4 (mkConst ``Int.Linear.Expr.eq_of_norm_eq) (← toContextExpr ctx) (toExpr e) (toExpr p) eagerReflBoolTrue
let lhs ← e.denoteExpr (ctx[·]!)
let rhs ← p.denoteExpr (ctx[·]!)
return some (rhs, mkExpectedPropHint h (mkIntEq lhs rhs))
else
return none
end Lean.Meta.Simp.Arith.Int

View file

@ -14,26 +14,28 @@ namespace Lean.Meta.Simp.Arith.Nat
def simpCnstrPos? (e : Expr) : MetaM (Option (Expr × Expr)) := do
let some (c, atoms) ← toLinearCnstr? e
| return none
withAbstractAtoms atoms ``Nat fun atoms => do
let lhs ← c.toArith atoms
let c₁ := c.toPoly
let c₂ := c₁.norm
if c₂.isUnsat then
let r := mkConst ``False
let p := mkApp3 (mkConst ``Nat.Linear.ExprCnstr.eq_false_of_isUnsat) (← toContextExpr atoms) (toExpr c) eagerReflBoolTrue
return some (r, mkExpectedPropHint p (mkPropEq lhs r))
else if c₂.isValid then
let r := mkConst ``True
let p := mkApp3 (mkConst ``Nat.Linear.ExprCnstr.eq_true_of_isValid) (← toContextExpr atoms) (toExpr c) eagerReflBoolTrue
return some (r, mkExpectedPropHint p (mkPropEq lhs r))
let lhs ← c.toArith atoms
let c₁ := c.toPoly
let c₂ := c₁.norm
if c₂.isUnsat then
let r := mkConst ``False
let p := mkApp3 (mkConst ``Nat.Linear.ExprCnstr.eq_false_of_isUnsat) (← toContextExpr atoms) (toExpr c) eagerReflBoolTrue
let h := mkExpectedPropHint p (mkPropEq lhs r)
return some (r, h)
else if c₂.isValid then
let r := mkConst ``True
let p := mkApp3 (mkConst ``Nat.Linear.ExprCnstr.eq_true_of_isValid) (← toContextExpr atoms) (toExpr c) eagerReflBoolTrue
let h := mkExpectedPropHint p (mkPropEq lhs r)
return some (r, h)
else
let c₂ : LinearCnstr := c₂.toExpr
let r ← c₂.toArith atoms
if r != lhs then
let p := mkApp4 (mkConst ``Nat.Linear.ExprCnstr.eq_of_toNormPoly_eq) (← toContextExpr atoms) (toExpr c) (toExpr c₂) eagerReflBoolTrue
let h := mkExpectedPropHint p (mkPropEq lhs r)
return some (r, h)
else
let c₂ : LinearCnstr := c₂.toExpr
let r ← c₂.toArith atoms
if r != lhs then
let p := mkApp4 (mkConst ``Nat.Linear.ExprCnstr.eq_of_toNormPoly_eq) (← toContextExpr atoms) (toExpr c) (toExpr c₂) eagerReflBoolTrue
return some (r, mkExpectedPropHint p (mkPropEq lhs r))
else
return none
return none
def simpCnstr? (e : Expr) : MetaM (Option (Expr × Expr)) := do
if let some arg := e.not? then
@ -66,15 +68,15 @@ def simpCnstr? (e : Expr) : MetaM (Option (Expr × Expr)) := do
def simpExpr? (input : Expr) : MetaM (Option (Expr × Expr)) := do
let (e, ctx) ← toLinearExpr input
withAbstractAtoms ctx ``Nat fun ctx => do
let p := e.toPoly
let p' := p.norm
let e' : LinearExpr := p'.toExpr
if e' == e then
return none
let p := mkApp4 (mkConst ``Nat.Linear.Expr.eq_of_toNormPoly_eq) (← toContextExpr ctx) (toExpr e) (toExpr e') eagerReflBoolTrue
let l ← e.toArith ctx
let r ← e'.toArith ctx
return some (r, mkExpectedPropHint p (mkNatEq l r))
let p := e.toPoly
let p' := p.norm
let e' : LinearExpr := p'.toExpr
if e' == e then
return none
let p := mkApp4 (mkConst ``Nat.Linear.Expr.eq_of_toNormPoly_eq) (← toContextExpr ctx) (toExpr e) (toExpr e') eagerReflBoolTrue
let l ← e.toArith ctx
let r ← e'.toArith ctx
let h := mkExpectedPropHint p (mkNatEq l r)
return some (r, h)
end Lean.Meta.Simp.Arith.Nat

View file

@ -4,37 +4,10 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Basic
public section
namespace Lean.Meta.Simp.Arith
/-
To prevent the kernel from accidentally reducing the atoms in the equation while typechecking,
we abstract over them.
-/
def withAbstractAtoms (atoms : Array Expr) (type : Name) (k : Array Expr → MetaM (Option (Expr × Expr))) :
MetaM (Option (Expr × Expr)) := do
let type := mkConst type
let rec go (i : Nat) (atoms' : Array Expr) (xs : Array Expr) (args : Array Expr) : MetaM (Option (Expr × Expr)) := do
if h : i < atoms.size then
let atom := atoms[i]
if atom.isFVar then
go (i+1) (atoms'.push atom) xs args
else
withLocalDeclD (← mkFreshUserName `x) type fun x =>
go (i+1) (atoms'.push x) (xs.push x) (args.push atom)
else
if xs.isEmpty then
k atoms'
else
let some (r, p) ← k atoms' | return none
let r := (← mkLambdaFVars xs r).beta args
let p := mkAppN (← mkLambdaFVars xs p) args
return some (r, p)
go 0 #[] #[] #[]
private def isSupportedType (type : Expr) : Bool :=
match_expr type with

View file

@ -161,17 +161,14 @@ theorem ex₂ (x y z : Int) (f : Int → Int) : x + f y + 2 + f y + z + z ≤ f
info: theorem ex₂ : ∀ (x y z : Int) (f : Int → Int), x + f y + 2 + f y + z + z ≤ f y + 3 * z + 1 + 1 + x + f y - z :=
fun x y z f =>
of_eq_true
((fun x_1 =>
id
(le_eq_true
(Lean.RArray.branch 1 (Lean.RArray.leaf x_1)
(Lean.RArray.branch 2 (Lean.RArray.leaf z) (Lean.RArray.leaf x)))
((((((Expr.var 2).add (Expr.var 0)).add (Expr.num 2)).add (Expr.var 0)).add (Expr.var 1)).add (Expr.var 1))
(((((((Expr.var 0).add (Expr.mulL 3 (Expr.var 1))).add (Expr.num 1)).add (Expr.num 1)).add (Expr.var 2)).add
(Expr.var 0)).sub
(Expr.var 1))
(eagerReduce (Eq.refl true))))
(f y))
(id
(le_eq_true
(Lean.RArray.branch 1 (Lean.RArray.leaf (f y)) (Lean.RArray.branch 2 (Lean.RArray.leaf z) (Lean.RArray.leaf x)))
((((((Expr.var 2).add (Expr.var 0)).add (Expr.num 2)).add (Expr.var 0)).add (Expr.var 1)).add (Expr.var 1))
(((((((Expr.var 0).add (Expr.mulL 3 (Expr.var 1))).add (Expr.num 1)).add (Expr.num 1)).add (Expr.var 2)).add
(Expr.var 0)).sub
(Expr.var 1))
(eagerReduce (Eq.refl true))))
-/
#guard_msgs (info) in
open Int.Linear in