Compare commits

...

4 Commits

Author SHA1 Message Date
Leonardo de Moura
e9e777f44e chore: mark TODO 2025-06-13 15:34:18 -07:00
Leonardo de Moura
5fc1226f8d test: 2025-06-13 15:33:29 -07:00
Leonardo de Moura
050d20e026 feat: basic field support in grind ring 2025-06-13 15:33:06 -07:00
Leonardo de Moura
ae4850bb32 feat: check whether ring is a field in CommRing module 2025-06-13 11:39:54 -07:00
6 changed files with 172 additions and 43 deletions

View File

@@ -11,14 +11,13 @@ import Init.Data.Hashable
import all Init.Data.Ord
import Init.Data.RArray
import Init.Grind.CommRing.Basic
import Init.Grind.CommRing.Field
import Init.Grind.Ordered.Ring
namespace Lean.Grind
namespace CommRing
-- These are no longer global instances, so we need to turn them on here.
attribute [local instance] Semiring.natCast Ring.intCast
namespace CommRing
abbrev Var := Nat
inductive Expr where
@@ -1225,5 +1224,29 @@ theorem not_lt_norm' {α} [CommRing α] [Preorder α] [Ring.IsOrdered α] (ctx :
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem div_int_eq {α} [Field α] [IsCharP α 0] (a : α) (b : Int) : b != 0 a = denoteInt b * (a / denoteInt b) := by
simp [Field.div_eq_mul_inv]; intro h
have : (denoteInt b : α) 0 := by
simp [denoteInt_eq]; intro h
have := IsCharP.intCast_eq_zero_iff (α := α) 0 b; simp [*] at this
rw [CommRing.mul_comm, Semiring.mul_assoc, CommRing.mul_comm _ (denoteInt b), Field.mul_inv_cancel this, Semiring.mul_one]
theorem div_int_eqC {α c} [Field α] [IsCharP α c] (a : α) (b : Int) : b % c != 0 a = (denoteInt b) * (a / denoteInt b) := by
simp [Field.div_eq_mul_inv]; intro h
have : (denoteInt b : α) 0 := by
simp [denoteInt_eq]; intro h
have := IsCharP.intCast_eq_zero_iff (α := α) c b; simp [*] at this
rw [CommRing.mul_comm, Semiring.mul_assoc, CommRing.mul_comm _ (denoteInt b), Field.mul_inv_cancel this, Semiring.mul_one]
theorem div_zero_eq {α} [Field α] (a : α) : a / 0 = 0 := by
simp [Field.div_eq_mul_inv, Field.inv_zero, Semiring.mul_zero]
theorem div_zero_eqC {α c} [Field α] [IsCharP α c] (a : α) (b : Int) : b % c == 0 (a / denoteInt b) = 0 := by
simp [Field.div_eq_mul_inv, denoteInt_eq]; intro h
have : (b : α) = 0 := by
have := IsCharP.intCast_eq_zero_iff (α := α) c b
simp [*]
simp [this, Field.div_eq_mul_inv, Field.inv_zero, Semiring.mul_zero]
end CommRing
end Lean.Grind

View File

@@ -14,7 +14,7 @@ Helper functions for converting reified terms back into their denotations.
variable [Monad M] [MonadGetRing M]
private def denoteNum (k : Int) : M Expr := do
def denoteNum (k : Int) : M Expr := do
let ring getRing
let n := mkRawNatLit k.natAbs
let ofNatInst := mkApp3 (mkConst ``Grind.Semiring.ofNat [ring.u]) ring.type ring.semiringInst n

View File

@@ -7,6 +7,7 @@ prelude
import Lean.Meta.Tactic.Grind.Simp
import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
import Lean.Meta.Tactic.Grind.Arith.CommRing.Reify
import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
namespace Lean.Meta.Grind.Arith.CommRing
@@ -41,8 +42,79 @@ private def isForbiddenParent (parent? : Option Expr) : Bool :=
else
true
private partial def toInt? (e : Expr) : RingM (Option Int) := do
match_expr e with
| Neg.neg _ i a =>
if isNegInst ( getRing) i then return (- .) <$> ( toInt? a) else return none
| IntCast.intCast _ i a =>
if isIntCastInst ( getRing) i then getIntValue? a else return none
| NatCast.natCast _ i a =>
if isNatCastInst ( getRing) i then
let some v getNatValue? a | return none
return some (Int.ofNat v)
else
return none
| OfNat.ofNat _ n _ =>
let some v getNatValue? n | return none
return some (Int.ofNat v)
| _ => return none
private def isDivInst (inst : Expr) : RingM Bool := do
let some fn := ( getRing).divFn? | return false
return isSameExpr fn.appArg! inst
/--
Given `e` of the form `@HDiv.hDiv _ _ _ inst a b`,
asserts `a = b * e` if `b` is a numeral.
Otherwise, asserts `b = 0 a = b * e`
-/
private def processDiv (e inst a b : Expr) : RingM Unit := do
unless ( isDivInst inst) do return ()
if ( getRing).divSet.contains (a, b) then return ()
modifyRing fun s => { s with divSet := s.divSet.insert (a, b) }
if let some k toInt? b then
let ring getRing
let some fieldInst := ring.fieldInst? | return ()
if k == 0 then
pushNewFact <| mkApp3 (mkConst ``Grind.CommRing.div_zero_eq [ring.u]) ring.type fieldInst a
else if ( hasChar) then
let (charInst, c) getCharInst
if c == 0 then
let expected mkEq a (mkApp2 ring.mulFn b e)
pushNewFact <| mkExpectedPropHint
(mkApp6 (mkConst ``Grind.CommRing.div_int_eq [ring.u]) ring.type fieldInst charInst a (mkIntLit k) reflBoolTrue)
expected
else if k % c == 0 then
let expected mkEq e ( denoteNum 0)
pushNewFact <| mkExpectedPropHint
(mkApp7 (mkConst ``Grind.CommRing.div_zero_eqC [ring.u]) ring.type (mkNatLit c) fieldInst charInst a (mkIntLit k) reflBoolTrue)
expected
else
let expected mkEq a (mkApp2 ring.mulFn b e)
pushNewFact <| mkExpectedPropHint
(mkApp7 (mkConst ``Grind.CommRing.div_int_eqC [ring.u]) ring.type (mkNatLit c) fieldInst charInst a (mkIntLit k) reflBoolTrue)
expected
else
-- TODO
return ()
/--
Returns `true` if `e` is a term `a/b` or `a⁻¹`.
-/
private def internalizeDivInv (e : Expr) : GoalM Bool := do
match_expr e with
| HDiv.hDiv α _ _ inst a b =>
let some ringId getRingId? α | return true
RingM.run ringId do processDiv e inst a b
return true
| Inv.inv _α _inst _a =>
-- TODO
return true
| _ => return false
def internalize (e : Expr) (parent? : Option Expr) : GoalM Unit := do
if !( getConfig).ring && !( getConfig).ringNull then return ()
if ( internalizeDivInv e) then return ()
let some type := getType? e | return ()
if isForbiddenParent parent? then return ()
let some ringId getRingId? type | return ()

View File

@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Grind.CommRing.Field
import Lean.Meta.Tactic.Grind.Simp
import Lean.Meta.Tactic.Grind.Arith.CommRing.Util
@@ -12,41 +13,38 @@ namespace Lean.Meta.Grind.Arith.CommRing
private def internalizeFn (fn : Expr) : GoalM Expr := do
shareCommon ( canon fn)
private def getAddFn (type : Expr) (u : Level) (semiringInst : Expr) : GoalM Expr := do
let instType := mkApp3 (mkConst ``HAdd [u, u, u]) type type type
let .some inst trySynthInstance instType |
throwError "failed to find instance for ring addition{indentExpr instType}"
let inst' := mkApp2 (mkConst ``instHAdd [u]) type <| mkApp2 (mkConst ``Grind.Semiring.toAdd [u]) type semiringInst
unless ( withDefault <| isDefEq inst inst') do
throwError "instance for addition{indentExpr inst}\nis not definitionally equal to the `Grind.Semiring` one{indentExpr inst'}"
internalizeFn <| mkApp4 (mkConst ``HAdd.hAdd [u, u, u]) type type type inst
private def getUnaryFn (type : Expr)(u : Level) (instDeclName : Name) (declName : Name) : GoalM Expr := do
let instType := mkApp (mkConst instDeclName [u]) type
let .some inst trySynthInstance instType
| throwError "`grind ring` failed to find instance{indentExpr instType}"
internalizeFn <| mkApp2 (mkConst declName [u]) type inst
private def getMulFn (type : Expr) (u : Level) (semiringInst : Expr) : GoalM Expr := do
let instType := mkApp3 (mkConst ``HMul [u, u, u]) type type type
let .some inst trySynthInstance instType |
throwError "failed to find instance for ring multiplication{indentExpr instType}"
let inst' := mkApp2 (mkConst ``instHMul [u]) type <| mkApp2 (mkConst ``Grind.Semiring.toMul [u]) type semiringInst
unless ( withDefault <| isDefEq inst inst') do
throwError "instance for multiplication{indentExpr inst}\nis not definitionally equal to the `Grind.Semiring` one{indentExpr inst'}"
internalizeFn <| mkApp4 (mkConst ``HMul.hMul [u, u, u]) type type type inst
private def getBinHomoFn (type : Expr)(u : Level) (instDeclName : Name) (declName : Name) : GoalM Expr := do
let instType := mkApp3 (mkConst instDeclName [u, u, u]) type type type
let .some inst trySynthInstance instType
| throwError "`grind ring` failed to find instance{indentExpr instType}"
internalizeFn <| mkApp4 (mkConst declName [u, u, u]) type type type inst
private def getSubFn (type : Expr) (u : Level) (ringInst : Expr) : GoalM Expr := do
let instType := mkApp3 (mkConst ``HSub [u, u, u]) type type type
let .some inst trySynthInstance instType |
throwError "failed to find instance for ring subtraction{indentExpr instType}"
let inst' := mkApp2 (mkConst ``instHSub [u]) type <| mkApp2 (mkConst ``Grind.Ring.toSub [u]) type ringInst
unless ( withDefault <| isDefEq inst inst') do
throwError "instance for subtraction{indentExpr inst}\nis not definitionally equal to the `Grind.Ring` one{indentExpr inst'}"
internalizeFn <| mkApp4 (mkConst ``HSub.hSub [u, u, u]) type type type inst
-- Remark: we removed consistency checks such as the one that ensures `HAdd` instance matches `Semiring.toAdd`
-- That is, we are assuming the type classes were properly setup.
private def getNegFn (type : Expr) (u : Level) (ringInst : Expr) : GoalM Expr := do
let instType := mkApp (mkConst ``Neg [u]) type
let .some inst trySynthInstance instType |
throwError "failed to find instance for ring negation{indentExpr instType}"
let inst' := mkApp2 (mkConst ``Grind.Ring.toNeg [u]) type ringInst
unless ( withDefault <| isDefEq inst inst') do
throwError "instance for negation{indentExpr inst}\nis not definitionally equal to the `Grind.Ring` one{indentExpr inst'}"
internalizeFn <| mkApp2 (mkConst ``Neg.neg [u]) type inst
private def getAddFn (type : Expr) (u : Level) : GoalM Expr := do
getBinHomoFn type u ``HAdd ``HAdd.hAdd
private def getMulFn (type : Expr) (u : Level) : GoalM Expr := do
getBinHomoFn type u ``HMul ``HMul.hMul
private def getSubFn (type : Expr) (u : Level) : GoalM Expr := do
getBinHomoFn type u ``HSub ``HSub.hSub
private def getDivFn (type : Expr) (u : Level) : GoalM Expr := do
getBinHomoFn type u ``HDiv ``HDiv.hDiv
private def getNegFn (type : Expr) (u : Level) : GoalM Expr := do
getUnaryFn type u ``Neg ``Neg.neg
private def getInvFn (type : Expr) (u : Level) : GoalM Expr := do
getUnaryFn type u ``Inv ``Inv.inv
private def getPowFn (type : Expr) (u : Level) (semiringInst : Expr) : GoalM Expr := do
let instType := mkApp3 (mkConst ``HPow [u, 0, u]) type Nat.mkType type
@@ -135,15 +133,23 @@ where
let noZeroDivType := mkApp3 (mkConst ``Grind.NoNatZeroDivisors [u]) type zeroInst hmulInst
LOption.toOption <$> trySynthInstance noZeroDivType
trace_goal[grind.ring] "NoNatZeroDivisors available: {noZeroDivInst?.isSome}"
let addFn getAddFn type u semiringInst
let mulFn getMulFn type u semiringInst
let subFn getSubFn type u ringInst
let negFn getNegFn type u ringInst
let field := mkApp (mkConst ``Grind.Field [u]) type
let fieldInst? : Option Expr LOption.toOption <$> trySynthInstance field
let addFn getAddFn type u
let mulFn getMulFn type u
let subFn getSubFn type u
let negFn getNegFn type u
let powFn getPowFn type u semiringInst
let intCastFn getIntCastFn type u ringInst
let natCastFn getNatCastFn type u semiringInst
let (invFn?, divFn?) if fieldInst?.isSome then
pure (some ( getInvFn type u), some ( getDivFn type u))
else
pure (none, none)
let id := ( get').rings.size
let ring : Ring := { id, type, u, semiringInst, ringInst, commSemiringInst, commRingInst, charInst?, noZeroDivInst?, addFn, mulFn, subFn, negFn, powFn, intCastFn, natCastFn }
let ring : Ring := {
id, type, u, semiringInst, ringInst, commSemiringInst, commRingInst, charInst?, noZeroDivInst?, fieldInst?,
addFn, mulFn, subFn, negFn, powFn, intCastFn, natCastFn, invFn?, divFn? }
modify' fun s => { s with rings := s.rings.push ring }
return some id

View File

@@ -139,9 +139,11 @@ structure Ring where
/-- `CommRing` instance for `type` -/
commRingInst : Expr
/-- `IsCharP` instance for `type` if available. -/
charInst? : Option (Expr × Nat) := .none
charInst? : Option (Expr × Nat)
/-- `NoNatZeroDivisors` instance for `type` if available. -/
noZeroDivInst? : Option Expr := .none
noZeroDivInst? : Option Expr
/-- `Field` instance for `type` if available. -/
fieldInst? : Option Expr
addFn : Expr
mulFn : Expr
subFn : Expr
@@ -149,6 +151,10 @@ structure Ring where
powFn : Expr
intCastFn : Expr
natCastFn : Expr
/-- Inverse if `fieldInst?` is `some inst` -/
invFn? : Option Expr
/-- Division if `fieldInst?` is `some inst` -/
divFn? : Option Expr
/--
Mapping from variables to their denotations.
Remark each variable can be in only one ring.
@@ -178,6 +184,10 @@ structure Ring where
disequalities and implied equalities.
-/
recheck : Bool := false
/-- Division theorems that have been already asserted. -/
divSet : PHashSet (Expr × Expr) := {}
/-- Inverse theorems that have been already asserted. -/
invSet : PHashSet Expr := {}
deriving Inhabited
/-- State for all `CommRing` types detected by `grind`. -/

View File

@@ -0,0 +1,18 @@
open Lean Grind
example [Field α] [IsCharP α 0] (a b c : α) : a/3 = b c = a/3 a/2 + a/2 = b + 2*c := by
grind
example [Field α] (a b : α) : b = 0 (a + a) / 0 = b := by
grind
example [Field α] [IsCharP α 3] (a b : α) : a/3 = b b = 0 := by
grind
example [Field α] [IsCharP α 7] (a b c : α) : a/3 = b c = a/3 a/2 + a/2 = b + 2*c + 7 := by
grind
example [Field R] [IsCharP R 0] (x : R) (cos : R R) :
(cos x ^ 2 + (2 * cos x ^ 2 - 1) ^ 2 + (4 * cos x ^ 3 - 3 * cos x) ^ 2 - 1) / 4 =
cos x * (cos x ^ 2 - 1 / 2) * (4 * cos x ^ 3 - 3 * cos x) := by
grind