Skip to content

Commit 3655828

Browse files
committed
fix: linariath model construction
1 parent 0a6b177 commit 3655828

File tree

3 files changed

+60
-1
lines changed

3 files changed

+60
-1
lines changed

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

Lines changed: 54 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.Arith.Linear.Types
9+
import Lean.Meta.Tactic.Grind.Arith.Linear.Reify
910
import Lean.Meta.Tactic.Grind.Arith.ModelUtil
1011
import Init.Grind.Module.Envelope
1112
public section
@@ -28,6 +29,58 @@ private def toQ? (e : Expr) : Option Expr :=
2829
| Grind.IntModule.OfNatModule.toQ _ _ a => some a
2930
| _ => none
3031

32+
/--
33+
Helper function for evaluating terms that have been processed by `internalize`, but
34+
we did not added them to constraints. See comment at `assignTerms`.
35+
-/
36+
private partial def evalTermAt? (e : Expr) (s : Struct) (model : Std.HashMap Expr Rat) : MetaM (Option Rat) := do
37+
go e
38+
where
39+
go (e : Expr) : OptionT MetaM Rat := do
40+
if let some val := model.get? e then
41+
return val
42+
match_expr e with
43+
| Neg.neg _ i a => if isNegInst s i then return - (← go a) else failure
44+
| HAdd.hAdd _ _ _ i a b => if isAddInst s i then return (← go a) + (← go b) else failure
45+
| HSub.hSub _ _ _ i a b => if isSubInst s i then return (← go a) - (← go b) else failure
46+
| HMul.hMul _ _ _ i a b => if isHomoMulInst s i then return (← go a) * (← go b) else failure
47+
| HSMul.hSMul _ _ _ i a b =>
48+
if isSMulIntInst s i then
49+
let k ← getIntValue? a
50+
return k * (← go b)
51+
else if isSMulNatInst s i then
52+
let k ← getNatValue? a
53+
return k * (← go b)
54+
else
55+
failure
56+
| Zero.zero _ i => if isZeroInst s i then return 0 else failure
57+
| OfNat.ofNat _ n _ => let k ← getNatValue? n; return k
58+
| _ => failure
59+
60+
/--
61+
Assigns terms that do not have an assignment associated with them because they were used only as markers
62+
for communicating with the `grind` core.
63+
For example, suppose we have `a + b = c`. The `internalize` function marks `a + b`, `a`, and `b`
64+
with theory variables. Let's assume we also have `c = 2*b`. In this case, `internalize` marks `2*b`
65+
with a theory variable, and `b` is already marked. Then, when both equalities are asserted
66+
in the `grind` core, the `linarith` module is notified that `a + b = 2*b` is true, and it is then
67+
normalized as `a + -1*b = 0`. The terms `a` and `b` are assigned rational values by the search
68+
procedure, but `a + b` and `2*b` are not. This procedure assigns them using the model computed by the
69+
search procedure.
70+
71+
**Note**: We should reconsider whether to add the equalities `「a+b」 = a + b` and `「2*b」 = 2*b`
72+
to force the search procedure to assign interpretations to these terms.
73+
-/
74+
private def assignTerms (goal : Goal) (structId : Nat) (model : Std.HashMap Expr Rat) : MetaM (Std.HashMap Expr Rat) := do
75+
let mut model := model
76+
let s ← linearExt.getStateCore goal
77+
let struct := s.structs[structId]!
78+
for (e, structId') in s.exprToStructIdEntries do
79+
if structId == structId' && !model.contains e then
80+
if let some v ← evalTermAt? e struct model then
81+
model := assignEqc goal e v model
82+
return model
83+
3184
/--
3285
Construct a model that satisfies all constraints in the linarith model for the structure with id `structId`.
3386
It also assigns values to (integer) terms that have not been internalized by the linarith model.
@@ -42,6 +95,7 @@ def mkModel (goal : Goal) (structId : Nat) : MetaM (Array (Expr × Rat)) := do
4295
if (← hasType s.type node) then
4396
if let some v := getAssignment? s node.self then
4497
model := assignEqc goal node.self v model
98+
model ← assignTerms goal structId model
4599
-- Assign `toQ a` terms
46100
for e in goal.exprs do
47101
let node ← goal.getENode e

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ structure State where
245245
typeIdOf : PHashMap ExprPtr (Option Nat) := {}
246246
/- Mapping from expressions/terms to their structure ids. -/
247247
exprToStructId : PHashMap ExprPtr Nat := {}
248+
/-- `exprToStructId` content as an array for traversal. -/
249+
exprToStructIdEntries : PArray (Expr × Nat) := {}
248250
/--
249251
Some types are unordered rings, so we do not process them in `linarith`.
250252
When such types are detected in `getStructId?`, we add them to the set

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ def setTermStructId (e : Expr) : LinearM Unit := do
4040
unless structId' == structId do
4141
reportIssue! "expression in two different structure in linarith module{indentExpr e}"
4242
return ()
43-
modify' fun s => { s with exprToStructId := s.exprToStructId.insert { expr := e } structId }
43+
modify' fun s => { s with
44+
exprToStructId := s.exprToStructId.insert { expr := e } structId
45+
exprToStructIdEntries := s.exprToStructIdEntries.push (e, structId)
46+
}
4447

4548
def getNoNatDivInst : LinearM Expr := do
4649
let some inst := (← getStruct).noNatDivInst?

0 commit comments

Comments
 (0)