Skip to content

Commit fafe840

Browse files
kim-emjcommelin
andcommitted
chore: use Std.TreeMap in linarith (#24367)
This PR avoids use of `Batteries.RBMap` in the implementation of `linarith`, where the new `Std.TreeMap` suffices. Co-authored-by: Johan Commelin <johan@commelin.net>
1 parent 78ec36c commit fafe840

File tree

2 files changed

+76
-31
lines changed

2 files changed

+76
-31
lines changed

Mathlib/Tactic/Linarith/Oracle/FourierMotzkin.lean

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,29 @@ we conclude that the original system was unsatisfiable.
3232
-/
3333

3434
open Batteries
35-
open Std (format ToFormat)
35+
open Std (format ToFormat TreeSet)
36+
37+
namespace Std.TreeSet
38+
39+
variable {α : Type*} {cmp}
40+
41+
/--
42+
`O(n₂ * log (n₁ + n₂))`. Merges the maps `t₁` and `t₂`.
43+
If equal keys exist in both, the key from `t₂` is preferred.
44+
-/
45+
def union (t₁ t₂ : TreeSet α cmp) : TreeSet α cmp :=
46+
t₂.foldl .insert t₁
47+
48+
instance : Union (TreeSet α cmp) := ⟨TreeSet.union⟩
49+
50+
/--
51+
`O(n₁ * (log n₁ + log n₂))`. Constructs the set of all elements of `t₁` that are not in `t₂`.
52+
-/
53+
def sdiff (t₁ t₂ : TreeSet α cmp) : TreeSet α cmp := t₁.filter (!t₂.contains ·)
54+
55+
instance : SDiff (TreeSet α cmp) := ⟨TreeSet.sdiff⟩
56+
57+
end Std.TreeSet
3658

3759
namespace Linarith
3860

@@ -108,17 +130,17 @@ structure PComp : Type where
108130
back to the original assumptions. -/
109131
src : CompSource
110132
/-- The set of original assumptions which have been used in constructing this comparison. -/
111-
history : RBSet ℕ Ord.compare
133+
history : TreeSet ℕ Ord.compare
112134
/-- The variables which have been *effectively eliminated*,
113135
i.e. by running the elimination algorithm on that variable. -/
114-
effective : RBSet ℕ Ord.compare
136+
effective : TreeSet ℕ Ord.compare
115137
/-- The variables which have been *implicitly eliminated*.
116138
These are variables that appear in the historical set,
117139
do not appear in `c` itself, and are not in `effective. -/
118-
implicit : RBSet ℕ Ord.compare
140+
implicit : TreeSet ℕ Ord.compare
119141
/-- The union of all variables appearing in those original assumptions
120142
which appear in the `history` set. -/
121-
vars : RBSet ℕ Ord.compare
143+
vars : TreeSet ℕ Ord.compare
122144

123145
/--
124146
Any comparison whose history is not minimal is redundant,
@@ -191,7 +213,7 @@ No variables have been eliminated (effectively or implicitly).
191213
def PComp.assump (c : Comp) (n : ℕ) : PComp where
192214
c := c
193215
src := CompSource.assump n
194-
history := RBSet.empty.insert n
216+
history := {n}
195217
effective := .empty
196218
implicit := .empty
197219
vars := .ofList c.vars _

Mathlib/Tactic/Linarith/Parsing.lean

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Robert Y. Lewis
55
-/
66
import Mathlib.Tactic.Linarith.Datatypes
7-
import Batteries.Data.RBMap.WF
87

98
/-!
109
# Parsing input expressions into linear form
@@ -16,19 +15,43 @@ It identifies atoms up to ring-equivalence: that is, `(y*3)*x` will be identifie
1615
where the monomial `x*y` is the linear atom.
1716
1817
* Variables are represented by natural numbers.
19-
* Monomials are represented by `Monom := RBMap ℕ ℕ`.
18+
* Monomials are represented by `Monom := TreeMap ℕ ℕ`.
2019
The monomial `1` is represented by the empty map.
21-
* Linear combinations of monomials are represented by `Sum := RBMap Monom ℤ`.
20+
* Linear combinations of monomials are represented by `Sum := TreeMap Monom ℤ`.
2221
2322
All input expressions are converted to `Sum`s, preserving the map from expressions to variables.
2423
We then discard the monomial information, mapping each distinct monomial to a natural number.
25-
The resulting `RBMap ℕ ℤ` represents the ring-normalized linear form of the expression.
24+
The resulting `TreeMap ℕ ℤ` represents the ring-normalized linear form of the expression.
2625
This is ultimately converted into a `Linexp` in the obvious way.
2726
2827
`linearFormsAndMaxVar` is the main entry point into this file. Everything else is contained.
2928
-/
3029

3130
open Mathlib.Ineq Batteries
31+
open Std (TreeMap)
32+
33+
namespace Std.TreeMap
34+
35+
-- This will be replaced by a `BEq` instance implemented in the standard library,
36+
-- likely in Q4 2025.
37+
38+
/-- Returns true if the two maps have the same size and the same keys and values
39+
(with keys compared using the ordering, and values compared using `BEq`). -/
40+
def beq {α β : Type*} [BEq β] {c : α → α → Ordering} (m₁ m₂ : TreeMap α β c) : Bool :=
41+
m₁.size == m₂.size && Id.run do
42+
-- This could be made more efficient by simultaneously traversing both maps.
43+
for (k, v) in m₁ do
44+
if let some v' := m₂[k]? then
45+
if v != v' then
46+
return false
47+
else
48+
return false
49+
return true
50+
51+
instance {α β : Type*} [BEq β] {c : α → α → Ordering} : BEq (TreeMap α β c) := ⟨beq⟩
52+
53+
end Std.TreeMap
54+
3255

3356
section
3457
open Lean Elab Tactic Meta
@@ -47,26 +70,26 @@ def List.findDefeq {v : Type} (red : TransparencyMode) (m : List (Expr × v)) (e
4770
end
4871

4972
/--
50-
We introduce a local instance allowing addition of `RBMap`s,
73+
We introduce a local instance allowing addition of `TreeMap`s,
5174
removing any keys with value zero.
5275
We don't need to prove anything about this addition, as it is only used in meta code.
5376
-/
5477
local instance {α β : Type*} {c : α → α → Ordering} [Add β] [Zero β] [DecidableEq β] :
55-
Add (RBMap α β c) where
78+
Add (TreeMap α β c) where
5679
add := fun f g => (f.mergeWith (fun _ b b' => b + b') g).filter (fun _ b => b ≠ 0)
5780

5881
namespace Linarith
5982

60-
/-- A local abbreviation for `RBMap` so we don't need to write `Ord.compare` each time. -/
61-
abbrev Map (α β) [Ord α] := RBMap α β Ord.compare
83+
/-- A local abbreviation for `TreeMap` so we don't need to write `Ord.compare` each time. -/
84+
abbrev Map (α β) [Ord α] := TreeMap α β Ord.compare
6285

6386
/-! ### Parsing datatypes -/
6487

6588
/-- Variables (represented by natural numbers) map to their power. -/
6689
abbrev Monom : Type := Map ℕ ℕ
6790

6891
/-- `1` is represented by the empty monomial, the product of no variables. -/
69-
def Monom.one : Monom := RBMap.empty
92+
def Monom.one : Monom := TreeMap.empty
7093

7194
/-- Compare monomials by first comparing their keys and then their powers. -/
7295
def Monom.lt : Monom → Monom → Bool :=
@@ -81,16 +104,16 @@ instance : Ord Monom where
81104
abbrev Sum : Type := Map Monom ℤ
82105

83106
/-- `1` is represented as the singleton sum of the monomial `Monom.one` with coefficient 1. -/
84-
def Sum.one : Sum := RBMap.empty.insert Monom.one 1
107+
def Sum.one : Sum := TreeMap.empty.insert Monom.one 1
85108

86109
/-- `Sum.scaleByMonom s m` multiplies every monomial in `s` by `m`. -/
87110
def Sum.scaleByMonom (s : Sum) (m : Monom) : Sum :=
88-
s.foldr (fun m' coeff sm => sm.insert (m + m') coeff) RBMap.empty
111+
s.foldr (fun m' coeff sm => sm.insert (m + m') coeff) TreeMap.empty
89112

90113
/-- `sum.mul s1 s2` distributes the multiplication of two sums. -/
91114
def Sum.mul (s1 s2 : Sum) : Sum :=
92-
s1.foldr (fun mn coeff sm => sm + ((s2.scaleByMonom mn).mapVal (fun _ v => v * coeff)))
93-
RBMap.empty
115+
s1.foldr (fun mn coeff sm => sm + ((s2.scaleByMonom mn).map (fun _ v => v * coeff)))
116+
TreeMap.empty
94117

95118
/-- The `n`th power of `s : Sum` is the `n`-fold product of `s`, with `s.pow 0 = Sum.one`. -/
96119
partial def Sum.pow (s : Sum) : ℕ → Sum
@@ -106,18 +129,18 @@ partial def Sum.pow (s : Sum) : ℕ → Sum
106129

107130
/-- `SumOfMonom m` lifts `m` to a sum with coefficient `1`. -/
108131
def SumOfMonom (m : Monom) : Sum :=
109-
RBMap.empty.insert m 1
132+
TreeMap.empty.insert m 1
110133

111-
/-- The unit monomial `one` is represented by the empty RBMap. -/
112-
def one : Monom := RBMap.empty
134+
/-- The unit monomial `one` is represented by the empty TreeMap. -/
135+
def one : Monom := TreeMap.empty
113136

114137
/-- A scalar `z` is represented by a `Sum` with coefficient `z` and monomial `one` -/
115138
def scalar (z : ℤ) : Sum :=
116-
RBMap.empty.insert one z
139+
TreeMap.empty.insert one z
117140

118141
/-- A single variable `n` is represented by a sum with coefficient `1` and monomial `n`. -/
119142
def var (n : ℕ) : Sum :=
120-
RBMap.empty.insert (RBMap.empty.insert n 1) 1
143+
TreeMap.empty.insert (TreeMap.empty.insert n 1) 1
121144

122145

123146
/-! ### Parsing algorithms -/
@@ -159,7 +182,7 @@ and forces some functions that call it into `MetaM` as well.
159182
partial def linearFormOfExpr (red : TransparencyMode) (m : ExprMap) (e : Expr) :
160183
MetaM (ExprMap × Sum) :=
161184
match e.numeral? with
162-
| some 0 => return ⟨m, RBMap.empty⟩
185+
| some 0 => return ⟨m, TreeMap.empty⟩
163186
| some (n+1) => return ⟨m, scalar (n+1)⟩
164187
| none =>
165188
match e.getAppFnArgs with
@@ -174,10 +197,10 @@ partial def linearFormOfExpr (red : TransparencyMode) (m : ExprMap) (e : Expr) :
174197
| (``HSub.hSub, #[_, _, _, _, e1, e2]) => do
175198
let (m1, comp1) ← linearFormOfExpr red m e1
176199
let (m2, comp2) ← linearFormOfExpr red m1 e2
177-
return (m2, comp1 + comp2.mapVal (fun _ v => -v))
200+
return (m2, comp1 + comp2.map (fun _ v => -v))
178201
| (``Neg.neg, #[_, _, e]) => do
179202
let (m1, comp) ← linearFormOfExpr red m e
180-
return (m1, comp.mapVal (fun _ v => -v))
203+
return (m1, comp.map (fun _ v => -v))
181204
| (``HPow.hPow, #[_, _, _, _, a, n]) => do
182205
match n.numeral? with
183206
| some n => do
@@ -190,18 +213,18 @@ partial def linearFormOfExpr (red : TransparencyMode) (m : ExprMap) (e : Expr) :
190213
`elimMonom s map` eliminates the monomial level of the `Sum` `s`.
191214
192215
`map` is a lookup map from monomials to variable numbers.
193-
The output `RBMap ℕ ℤ` has the same structure as `s : Sum`,
216+
The output `TreeMap ℕ ℤ` has the same structure as `s : Sum`,
194217
but each monomial key is replaced with its index according to `map`.
195218
If any new monomials are encountered, they are assigned variable numbers and `map` is updated.
196219
-/
197220
def elimMonom (s : Sum) (m : Map Monom ℕ) : Map Monom ℕ × Map ℕ ℤ :=
198221
s.foldr (fun mn coeff ⟨map, out⟩ ↦
199-
match map.find? mn with
222+
match map[mn]? with
200223
| some n => ⟨map, out.insert n coeff⟩
201224
| none =>
202225
let n := map.size
203226
⟨map.insert mn n, out.insert n coeff⟩)
204-
(m, RBMap.empty)
227+
(m, TreeMap.empty)
205228

206229
/--
207230
`toComp red e e_map monom_map` converts an expression of the form `t < 0`, `t ≤ 0`, or `t = 0`
@@ -240,7 +263,7 @@ It also returns the largest variable index that appears in comparisons in `c`.
240263
def linearFormsAndMaxVar (red : TransparencyMode) (pfs : List Expr) :
241264
MetaM (List Comp × ℕ) := do
242265
let pftps ← (pfs.mapM inferType)
243-
let (l, _, map) ← toCompFold red [] pftps RBMap.empty
266+
let (l, _, map) ← toCompFold red [] pftps TreeMap.empty
244267
trace[linarith.detail] "monomial map: {map.toList.map fun ⟨k,v⟩ => (k.toList, v)}"
245268
return (l, map.size - 1)
246269

0 commit comments

Comments
 (0)