Skip to content

Commit ef23782

Browse files
authored
feat: ring action (#10834)
This PR implements the `ring` action for `grind`.
1 parent e2b5747 commit ef23782

File tree

6 files changed

+72
-36
lines changed

6 files changed

+72
-36
lines changed

src/Lean/Elab/Tactic/Grind/BuiltinTactic.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def evalCheck (tacticName : Name) (k : GoalM Bool)
114114
evalCheck `linarith Arith.Linear.check Arith.Linear.pp?
115115

116116
@[builtin_grind_tactic ring] def evalRing : GrindTactic := fun _ => do
117-
evalCheck `ring Arith.CommRing.check Arith.CommRing.pp?
117+
evalCheck `ring Arith.CommRing.check' Arith.CommRing.pp?
118118

119119
@[builtin_grind_tactic ac] def evalAC : GrindTactic := fun _ => do
120120
evalCheck `ac AC.check' AC.pp?

src/Lean/Meta/Tactic/Grind/AC/Action.lean

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@ import Lean.Meta.Tactic.Grind.AC.Eq
1010
namespace Lean.Meta.Grind.Action
1111

1212
/-- Associative-Commutative solver action. -/
13-
public def ac : Action := fun goal kna kp => do
14-
let (result, goal') ← GoalM.run goal AC.check
15-
match result with
16-
| .none => kna goal'
17-
| .progress => kp goal'
18-
| .propagated => concatTactic (← kp goal') `(grind| ac)
19-
| .closed => closeWith `(grind| ac)
13+
public def ac : Action :=
14+
solverAction AC.check `(grind| ac)
2015

2116
end Lean.Meta.Grind.Action

src/Lean/Meta/Tactic/Grind/Action.lean

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
66
module
77
prelude
88
public import Lean.Meta.Tactic.Grind.Types
9+
public import Lean.Meta.Tactic.Grind.CheckResult
910
public section
1011
namespace Lean.Meta.Grind
1112
/-!
@@ -257,6 +258,22 @@ def terminalAction (check : GoalM Bool) (mkTac : GrindM (TSyntax `grind)) : Acti
257258
else
258259
kna goal'
259260

261+
/--
262+
Helper action for satellite solvers that use `CheckResult`.
263+
-/
264+
def solverAction (check : GoalM CheckResult) (mkTac : GrindM (TSyntax `grind)) : Action := fun goal kna kp => do
265+
let (result, goal') ← GoalM.run goal check
266+
match result with
267+
| .none => kna goal'
268+
| .progress => kp goal'
269+
| .propagated =>
270+
let goal' ← GoalM.run' goal' processNewFacts
271+
if goal'.inconsistent then
272+
closeWith mkTac
273+
else
274+
concatTactic (← kp goal') mkTac
275+
| .closed => closeWith mkTac
276+
260277
/--
261278
Helper action that checks whether the resulting tactic script produced by its continuation
262279
can close the original goal.

src/Lean/Meta/Tactic/Grind/Arith/CommRing.lean

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public import Lean.Meta.Tactic.Grind.Arith.CommRing.VarRename
2525
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadCanon
2626
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadRing
2727
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadSemiring
28+
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Action
2829
public section
2930
namespace Lean.Meta.Grind.Arith.CommRing
3031
builtin_initialize registerTraceClass `grind.ring
@@ -52,7 +53,7 @@ builtin_initialize
5253
(internalize := CommRing.internalize)
5354
(newEq := CommRing.processNewEq)
5455
(newDiseq := CommRing.processNewDiseq)
55-
(check := CommRing.check)
56+
(check := CommRing.check')
5657
(checkInv := CommRing.checkInvariants)
5758
(mkTactic? := return some (← `(grind| ring)))
5859

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/-
2+
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Leonardo de Moura
5+
-/
6+
module
7+
prelude
8+
public import Lean.Meta.Tactic.Grind.Action
9+
import Lean.Meta.Tactic.Grind.Arith.CommRing.EqCnstr
10+
namespace Lean.Meta.Grind.Action
11+
12+
/-- Ring solver action. -/
13+
public def ring : Action :=
14+
solverAction Arith.CommRing.check `(grind| ring)
15+
16+
end Lean.Meta.Grind.Action

src/Lean/Meta/Tactic/Grind/Arith/CommRing/EqCnstr.lean

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
66
module
77
prelude
88
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
9+
public import Lean.Meta.Tactic.Grind.CheckResult
910
import Lean.Meta.Tactic.Grind.ProveEq
1011
import Lean.Meta.Tactic.Grind.Diseq
1112
import Lean.Meta.Tactic.Grind.Arith.Util
@@ -485,29 +486,31 @@ abbrev PropagateEqMap := Std.HashMap (Int × Poly) (Expr × RingExpr)
485486
/--
486487
Propagates implied equalities.
487488
-/
488-
private def propagateEqs : RingM Unit := do
489-
if (← isInconsistent) then return ()
489+
private def propagateEqs : RingM Bool := do
490+
if (← isInconsistent) then return false
490491
/-
491492
This is a very simple procedure that does not use any indexing data-structure.
492493
We don't even cache the simplified polynomials.
493494
TODO: optimize
494495
TODO: support for semiring
495496
-/
496-
let mut map : PropagateEqMap := {}
497-
for a in (← getRing).vars do
498-
if (← checkMaxSteps) then return ()
499-
let some ra ← toRingExpr? a | unreachable!
500-
map ← process map a ra
501-
for (a, ra) in (← getCommRing).denoteEntries do
502-
if (← checkMaxSteps) then return ()
503-
map ← process map a ra
497+
let go : StateT (Bool × PropagateEqMap) RingM Unit := do
498+
for a in (← getRing).vars do
499+
if (← checkMaxSteps) then return ()
500+
let some ra ← toRingExpr? a | unreachable!
501+
process a ra
502+
for (a, ra) in (← getCommRing).denoteEntries do
503+
if (← checkMaxSteps) then return ()
504+
process a ra
505+
let (_, (propagated, _)) ← go.run (false, {})
506+
return propagated
504507
where
505-
process (map : PropagateEqMap) (a : Expr) (ra : RingExpr) : RingM PropagateEqMap := do
508+
process (a : Expr) (ra : RingExpr) : StateT (Bool × PropagateEqMap) RingM Unit := do
506509
let d : PolyDerivation := .input (← ra.toPolyM)
507510
let d ← d.simplify
508511
let k := d.getMultiplier
509512
trace_goal[grind.debug.ring.impEq] "{a}, {k}, {← d.p.denoteExpr}"
510-
if let some (b, rb) := map[(k, d.p)]? then
513+
if let some (b, rb) := (← get).2[(k, d.p)]? then
511514
-- TODO: use `isEqv` more effectively
512515
unless (← isEqv a b) do
513516
let p ← (ra.sub rb).toPolyM
@@ -518,40 +521,44 @@ where
518521
-- Given the multiplier `k' = d.getMultiplier`, we have that `k*(a - b) = 0`,
519522
-- but we cannot eliminate the `k` because we don't have `noZeroDivisors`.
520523
trace_goal[grind.ring.impEq] "skip: {← mkEq a b}, k: {k}, noZeroDivisors: false"
521-
return map.insert (k, d.p) (a, ra)
524+
modify fun (propagated, map) => (propagated, map.insert (k, d.p) (a, ra))
525+
return ()
522526
trace_goal[grind.ring.impEq] "{← mkEq a b}, {k}, {← p.denoteExpr}"
523527
propagateEq a b ra rb d
524-
return map
528+
modify fun s => (true, s.2)
525529
else
526-
return map.insert (k, d.p) (a, ra)
530+
modify fun (propagated, map) => (propagated, map.insert (k, d.p) (a, ra))
527531

528-
def checkRing : RingM Bool := do
529-
unless (← needCheck) do return false
532+
def checkRing : RingM CheckResult := do
533+
unless (← needCheck) do return .none
530534
trace_goal[grind.debug.ring.check] "{(← getRing).type}"
531535
repeat
532536
checkSystem "ring"
533537
let some c ← getNext? | break
534538
trace_goal[grind.debug.ring.check] "{← c.denoteExpr}"
535539
c.addToBasis
536-
if (← isInconsistent) then return true
537-
if (← checkMaxSteps) then return true
540+
if (← isInconsistent) then return .closed
541+
if (← checkMaxSteps) then return .progress
538542
checkDiseqs
539-
propagateEqs
543+
if (← propagateEqs) then return .propagated
540544
modifyCommRing fun s => { s with recheck := false }
541-
return true
545+
return .progress
542546

543-
def check : GoalM Bool := do profileitM Exception "grind ring" (← getOptions) do
544-
if (← checkMaxSteps) then return false
545-
let mut progress := false
547+
def check : GoalM CheckResult := do profileitM Exception "grind ring" (← getOptions) do
548+
if (← checkMaxSteps) then return .none
549+
let mut result : CheckResult := .none
546550
checkInvariants
547551
try
548552
for ringId in *...(← get').rings.size do
549553
let r ← RingM.run ringId checkRing
550-
progress := progress || r
554+
result := result.join r
551555
if (← isInconsistent) then
552-
return true
553-
return progress
556+
return .closed
557+
return result
554558
finally
555559
checkInvariants
556560

561+
def check' : GoalM Bool :=
562+
return (← check) != .none
563+
557564
end Lean.Meta.Grind.Arith.CommRing

0 commit comments

Comments
 (0)