@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
66module
77prelude
88public import Lean.Meta.Tactic.Grind.Arith.Linear.Types
9+ import Lean.Meta.Tactic.Grind.Arith.Linear.Reify
910import Lean.Meta.Tactic.Grind.Arith.ModelUtil
1011import Init.Grind.Module.Envelope
1112public 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/--
3285Construct a model that satisfies all constraints in the linarith model for the structure with id `structId`.
3386It 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
0 commit comments