Skip to content

Commit 0efe4d8

Browse files
committed
feat: MBTC for linarith
1 parent d4b17b9 commit 0efe4d8

File tree

5 files changed

+80
-2
lines changed

5 files changed

+80
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import Lean.Meta.Tactic.Grind.Arith.Linear.PropagateEq
1919
import Lean.Meta.Tactic.Grind.Arith.Linear.Internalize
2020
import Lean.Meta.Tactic.Grind.Arith.Linear.Model
2121
import Lean.Meta.Tactic.Grind.Arith.Linear.PP
22+
import Lean.Meta.Tactic.Grind.Arith.Linear.MBTC
2223

2324
namespace Lean
2425

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
prelude
7+
import Lean.Meta.Tactic.Grind.Canon
8+
import Lean.Meta.Tactic.Grind.MBTC
9+
import Lean.Meta.Tactic.Grind.Arith.Linear.Model
10+
import Lean.Meta.Tactic.Grind.Arith.Linear.PropagateEq
11+
12+
namespace Lean.Meta.Grind.Arith.Linear
13+
14+
private partial def toRatValue? (a : Expr) : Option Rat :=
15+
if a.isAppOfArity ``Zero.zero 2 then
16+
some 0
17+
else if a.isAppOfArity ``One.one 2 then
18+
some 1
19+
else if a.isAppOfArity ``OfNat.ofNat 3 then
20+
toRatValue? (a.getArg! 1)
21+
else if a.isAppOfArity ``Neg.neg 3 then
22+
(- ·) <$> toRatValue? a.appArg!
23+
else if a.isAppOfArity ``HDiv.hDiv 6 then
24+
(· / ·) <$> toRatValue? a.appFn!.appArg! <*> toRatValue? a.appArg!
25+
else if let .lit (.natVal n) := a then
26+
some (Std.Internal.mkRat n 1)
27+
else
28+
none
29+
30+
private def getAssignmentExt? (s : Struct) (a : Expr) : Option Rat := do
31+
if let some v := getAssignment? s a then
32+
some v
33+
else
34+
toRatValue? a
35+
36+
private def hasTheoryVar (e : Expr) : GoalM Bool := do
37+
return (← getRootENode e).linarith?.isSome || (toRatValue? e).isSome
38+
39+
private def isInterpreted (e : Expr) : GoalM Bool := do
40+
if isInterpretedTerm e then return true
41+
let f := e.getAppFn
42+
return f.isConstOf ``LE.le || f.isConstOf ``LT.lt || f.isConstOf ``Dvd.dvd
43+
44+
private def eqAssignment (a b : Expr) : GoalM Bool := do
45+
let structId₁? := (← get).arith.linear.exprToStructId.find? { expr := a }
46+
let structId₂? := (← get).arith.linear.exprToStructId.find? { expr := b }
47+
let some structId := structId₁? <|> structId₂? | return false
48+
let s := (← get).arith.linear.structs[structId]!
49+
-- It is pointless to generate case-splits unless we have support for disequality.
50+
unless s.linearInst?.isSome do return false
51+
let some v₁ := getAssignmentExt? s a | return false
52+
let some v₂ := getAssignmentExt? s b | return false
53+
return v₁ == v₂
54+
55+
def mbtc : GoalM Bool := do
56+
Grind.mbtc {
57+
hasTheoryVar := hasTheoryVar
58+
isInterpreted := isInterpreted
59+
eqAssignment := eqAssignment
60+
}
61+
62+
end Lean.Meta.Grind.Arith.Linear

src/Lean/Meta/Tactic/Grind/Arith/Linear/PropagateEq.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Lean.Meta.Tactic.Grind.Arith.Linear.Proof
1616

1717
namespace Lean.Meta.Grind.Arith.Linear
1818
/-- Returns `some structId` if `a` and `b` are elements of the same structure. -/
19-
private def inSameStruct? (a b : Expr) : GoalM (Option Nat) := do
19+
def inSameStruct? (a b : Expr) : GoalM (Option Nat) := do
2020
let some structId ← getTermStructId? a | return none
2121
let some structId' ← getTermStructId? b | return none
2222
unless structId == structId' do return none -- This can happen when we have heterogeneous equalities

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ where
4747
intros gen
4848
else
4949
break
50-
if (← assertAll <||> Arith.check <||> ematch <||> lookahead <||> splitNext <||> Arith.Cutsat.mbtc <||> tryFallback) then
50+
if (← assertAll <||> Arith.check <||> ematch <||> lookahead <||> splitNext <||> Arith.Cutsat.mbtc
51+
<||> Arith.Linear.mbtc <||> tryFallback) then
5152
continue
5253
return some (← getGoal) -- failed
5354
return none -- solved

tests/lean/run/grind_linarith_2.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,17 @@ set_option trace.grind.linarith.model true in
8888
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (a b c d : α)
8989
: a ≤ b → a - c ≥ 0 + d → d ≤ 0 → b = c → a ≠ b → False := by
9090
grind
91+
92+
/-- trace: [grind.split] x = 0, generation: 0 -/
93+
#guard_msgs (trace) in
94+
set_option trace.grind.split true in
95+
example [IntModule α] [LinearOrder α] [IntModule.IsOrdered α] (f : α → α) (x : α)
96+
: Zero.zero ≤ x → x ≤ 0 → f x = a → f 0 = a := by
97+
grind
98+
99+
-- should not split on `x = 0` since `α` is not a linear order
100+
#guard_msgs (drop error, trace) in
101+
set_option trace.grind.split true in
102+
example [IntModule α] [Preorder α] [IntModule.IsOrdered α] (f : α → α) (x : α)
103+
: Zero.zero ≤ x → x ≤ 0 → f x = a → f 0 = a := by
104+
grind

0 commit comments

Comments
 (0)