Skip to content

Commit e1d1de3

Browse files
JovanGerbjoneugster
andcommitted
feat(Convert): less aggressive congruence (#38071)
This PR tries to make `convert` behave more predictably, disabling some of the aggressive congruence steps that `congr!` does. In particular, when the two sides have a different head constants, then we should not use congruence on these applications. Co-authored-by: Jon Eugster <eugster.jon@gmail.com>
1 parent 9d11f4f commit e1d1de3

5 files changed

Lines changed: 19 additions & 56 deletions

File tree

Mathlib/Analysis/Complex/Exponential.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ variable (x y : ℂ)
9595
theorem exp_zero : exp 0 = 1 := by
9696
rw [exp]
9797
refine lim_eq_of_equiv_const fun ε ε0 => ⟨1, fun j hj => ?_⟩
98-
convert! (config := .unfoldSameFun) ε0 -- ε0 : ε > 0 but goal is _ < ε
98+
convert ε0.lt
9999
rcases j with - | j
100100
· exact absurd hj (not_le_of_gt zero_lt_one)
101101
· dsimp [exp']

Mathlib/Data/Fin/Tuple/Basic.lean

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,8 @@ theorem append_castAdd_natAdd {f : Fin (m + n) → α} :
400400

401401
/-- Splitting a dependent finite sequence v into an initial part and a final part,
402402
and then concatenating these components, produces an identical sequence. -/
403-
theorem addCases_castAdd_natAdd {γ : Fin (m + n) → Sort*} (v : ∀ i, γ i) :
404-
addCases (fun i ↦ v (castAdd n i)) (fun j ↦ v (natAdd m j)) = v := by
405-
ext i
403+
theorem addCases_castAdd_natAdd {γ : Fin (m + n) → Sort*} (v : ∀ i, γ i) (i : Fin (m + n)) :
404+
addCases (fun i ↦ v (castAdd n i)) (fun j ↦ v (natAdd m j)) i = v i := by
406405
cases i using addCases <;> simp
407406

408407
theorem append_comp_sumElim {xs : Fin m → α} {ys : Fin n → α} :
@@ -802,8 +801,8 @@ theorem forall_fin_add_pi {γ : Fin (m + n) → Sort*} {P : (∀ i, γ i) → Pr
802801
(∀ (vₘ : ∀ i, γ (castAdd n i)) (vₙ : ∀ j, γ (natAdd m j)), P (addCases vₘ vₙ)) where
803802
mp hv vm vn := hv (addCases vm vn)
804803
mpr h v := by
805-
convert! h (fun i => v (castAdd n i)) (fun j => v (natAdd m j))
806-
exact (addCases_castAdd_natAdd v).symm
804+
convert h (fun i => v (castAdd n i)) (fun j => v (natAdd m j))
805+
exact (addCases_castAdd_natAdd v _).symm
807806

808807
lemma exists_iff_castSucc {P : Fin (n + 1) → Prop} :
809808
(∃ i, P i) ↔ P (last n) ∨ ∃ i : Fin n, P i.castSucc where

Mathlib/Data/List/Perm/Basic.lean

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ end Rel
164164
lemma count_eq_count_filter_add [DecidableEq α] (P : α → Prop) [DecidablePred P]
165165
(l : List α) (a : α) :
166166
count a l = count a (l.filter P) + count a (l.filter (¬ P ·)) := by
167-
convert! countP_eq_countP_filter_add l _ P
167+
unfold count
168+
convert countP_eq_countP_filter_add l _ P
168169
simp only [decide_not]
169170

170171
theorem Perm.foldl_eq {f : β → α → β} {l₁ l₂ : List α} [rcomm : RightCommutative f] (p : l₁ ~ l₂) :

Mathlib/Tactic/Convert.lean

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,30 +24,22 @@ e : Prime (2 * n + 1)
2424
⊢ Prime (n + n + 1)
2525
```
2626
27-
the tactic `convert e using 2` will change the goal to
27+
the tactic `convert e` will change the goal to
2828
2929
```lean
3030
⊢ n + n = 2 * n
3131
```
3232
3333
In this example, the new goal can be solved using `ring`.
3434
35-
The `using 2` indicates it should iterate the congruence algorithm up to two times,
36-
where `convert e` would use an unrestricted number of iterations and lead to two
37-
impossible goals: `⊢ HAdd.hAdd = HMul.hMul` and `⊢ n = 2`.
38-
39-
A variant configuration is `convert (config := .unfoldSameFun) e`, which only equates function
40-
applications for the same function (while doing so at the higher `default` transparency).
41-
This gives the same goal of `⊢ n + n = 2 * n` without needing `using 2`.
42-
4335
The `convert` tactic applies congruence lemmas eagerly before reducing,
4436
therefore it can fail in cases where `exact` succeeds:
4537
```lean
4638
def p (n : ℕ) := True
4739
example (h : p 0) : p 1 := by exact h -- succeeds
4840
example (h : p 0) : p 1 := by convert h -- fails, with leftover goal `1 = 0`
4941
```
50-
Limiting the depth of recursion can help with this. For example, `convert h using 1` will work
42+
Limiting the depth of recursion can help with this. For example, `convert h using 0` will work
5143
in this case.
5244
5345
The syntax `convert ← e` will reverse the direction of the new goals
@@ -85,21 +77,12 @@ between `Convert.CheapConfig` and `Convert.ExpensiveConfig` based on other flags
8577
-/
8678
structure Convert.CheapConfig extends Congr!.Config where
8779
postTransparency := .reducible
80+
partialApp := false
81+
sameFun := true
8882

8983
/-- Internal elaborator for `Convert.CheapConfig`: use `Convert.elabConfig` instead. -/
9084
declare_config_elab Convert.elabCheapConfig Convert.CheapConfig
9185

92-
/-- A configuration option that makes `convert` do the sorts of aggressive unfoldings that `congr`
93-
does while also similarly preventing `convert` from considering partial applications or congruences
94-
between different functions being applied.
95-
96-
Note that `convert (config := .unfoldSameFun)` and `convert! (config := .unfoldSameFun)`
97-
currently do the same thing since `.unfoldSameFun` runs at default transparency always.
98-
This may change in the future, if `convert!` affects other options too.
99-
-/
100-
abbrev Convert.CheapConfig.unfoldSameFun : Convert.CheapConfig :=
101-
{ Congr!.Config.unfoldSameFun with }
102-
10386
/-- Configuration for the `convert!` family of tactics.
10487
This is `Convert.CheapConfig` (used by `convert` without exclamation mark) with different,
10588
more aggressive, defaults.
@@ -113,26 +96,14 @@ example the following call runs at `.instances` transparency.
11396
convert! (postTransparency := .instances)
11497
```
11598
-/
116-
structure Convert.ExpensiveConfig extends Convert.CheapConfig where
99+
structure Convert.ExpensiveConfig extends Congr!.Config where
117100
-- TODO: also enable this in the future?
118101
-- preTransparency := .default
119102
-- transparency := .default
120-
postTransparency := .default
121103

122104
/-- Internal elaborator for `Convert.ExpensiveConfig`: use `Convert.elabConfig` instead. -/
123105
declare_config_elab Convert.elabExpensiveConfig Convert.ExpensiveConfig
124106

125-
/-- A configuration option that makes `convert!` do the sorts of aggressive unfoldings that `congr`
126-
does while also similarly preventing `convert!` from considering partial applications or congruences
127-
between different functions being applied.
128-
129-
Note that `convert (config := .unfoldSameFun)` and `convert! (config := .unfoldSameFun)`
130-
currently do the same thing since `.unfoldSameFun` runs at default transparency always.
131-
This may change in the future, if `convert!` affects other options too.
132-
-/
133-
abbrev Convert.ExpensiveConfig.unfoldSameFun : Convert.ExpensiveConfig :=
134-
{ Congr!.Config.unfoldSameFun with }
135-
136107
/-- Configuration elaborator for the `convert`/`convert!` family of tactics.
137108
138109
If `expensive` is true, we're elaborating for `convert!`, and will configure to run at default
@@ -201,9 +172,10 @@ pattern-matched, like `rintro` would, using the `with` keyword.
201172
See also `convert_to t`, where `t` specifies the expected type, instead of a proof term of type `t`.
202173
In other words, `convert_to t` works like `convert (?_ : t)`. Both tactics use the same options.
203174
204-
* `convert! e` uses default transparency, rather than reducible, when solving side goals.
175+
* `convert! e` uses default transparency, rather than reducible, when solving side goals, and
176+
it tries to apply congruence even if the two expressions do not have the same head constant.
205177
* `convert ← e` creates equality goals in the opposite direction (with the goal type on the right).
206-
* `convert e using n`, where `n` is a positive numeral, controls the depth with which congruence is
178+
* `convert e using n`, where `n` is a numeral, controls the depth with which congruence is
207179
applied. For example, if the main goal is `⊢ Prime (n + n + 1)` and `e : Prime (2 * n + 1)`, then
208180
`convert e using 2` results in one goal, `⊢ n + n = 2 * n`, and `convert e using 3` (or more)
209181
results in two (impossible) goals `⊢ HAdd.hAdd = HMul.hMul` and `⊢ n = 2`.
@@ -216,10 +188,9 @@ In other words, `convert_to t` works like `convert (?_ : t)`. Both tactics use t
216188
Examples:
217189
218190
```lean
219-
-- `convert using` controls the depth of congruence.
220191
example {n : ℕ} (e : Prime (2 * n + 1)) :
221192
Prime (n + n + 1) := by
222-
convert e using 2
193+
convert e
223194
-- One goal: ⊢ n + n = 2 * n
224195
ring
225196
@@ -234,7 +205,7 @@ example (h : p 0) : p 1 := by
234205
-- `convert with` names introduced variables.
235206
example (p q : Nat → Prop) (h : ∀ ε > 0, p ε) :
236207
∀ ε > 0, q ε := by
237-
convert h using 2 with ε hε
208+
convert h with ε hε
238209
-- Goal now looks like:
239210
-- hε : ε > 0
240211
-- ⊢ q ε ↔ p ε

MathlibTest/Tactic/Convert/Basic.lean

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,15 @@ end convert_to
6161

6262
example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
6363
prime (n + n + 1) := by
64-
convert h
64+
convert! h
6565
· guard_target = (HAdd.hAdd : Nat → Nat → Nat) = HMul.hMul
6666
exact test_sorry
6767
· guard_target = n = 2
6868
exact test_sorry
6969

7070
example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
7171
prime (n + n + 1) := by
72-
convert (config := .unfoldSameFun) h
73-
guard_target = n + n = 2 * n
74-
exact test_sorry
75-
76-
-- `convert! (config := .unfoldSameFun)` does the same thing as `convert (config := .unfoldSameFun)`
77-
-- (at least for now)
78-
example (prime : Nat → Prop) (n : Nat) (h : prime (2 * n + 1)) :
79-
prime (n + n + 1) := by
80-
convert! (config := .unfoldSameFun) h
72+
convert h
8173
guard_target = n + n = 2 * n
8274
exact test_sorry
8375

0 commit comments

Comments
 (0)