Skip to content

Commit 5cdffe1

Browse files
edegeltjeblizzard_inc
andcommitted
feat(Tactic/Algebraize): Allow the algebraize tactic to use lemmas which don't (directly) mention RingHom (#24661)
This PR allows the `algebraize` tactic to be applied in more cases. Specifically, in cases where the assumption is not (directly) about RingHom, it should still be able to apply lemmas. Written at the suggestion of @chrisflav , with the following example usecase: ```lean-4 import Mathlib open AlgebraicGeometry attribute [algebraize flat_of_flat] Flat -- pretend this is added where `AlgebraicGeometry.Flat` is defined lemma flat_of_flat {R S : Type u} [CommRing R] [CommRing S] (f : R →+* S) (h : Flat (Spec.map (CommRingCat.ofHom f))) : @module.Flat R S _ _ f.toAlgebra.toModule := by have : f.Flat := sorry -- supposedly, this is true. ask christian. algebraize [f] assumption set_option tactic.hygienic false -- allow the tactic to produce accessible names for testing example {R S : Type u} [CommRing R] [CommRing S] (f : R →+* S) (h : Flat (Spec.map (CommRingCat.ofHom f))) : True := by algebraize [f] guard_hyp algebraizeInst : Module.Flat R S -- used to fail, succeeds now trivial ``` the key changes that makes this possible are the following: - a different way of making lemma/type applications, creating a term which can be added to the context - a different way of checking that the previously mentioned term actually relevant w/r/t the arguments provided to the tactic The previous check was ad-hoc and flimsy, making requirements of the precise order and the types of the arguments of lemmas or definitions, which resulted in the lemma being applicable in limited cases only. Now we still have some requirements, but these are less stringent. Co-authored-by: blizzard_inc <[email protected]> Co-authored-by: Edward van de Meent <[email protected]> Co-authored-by: blizzard_inc <[email protected]>
1 parent a978d17 commit 5cdffe1

File tree

2 files changed

+146
-62
lines changed

2 files changed

+146
-62
lines changed

Mathlib/Tactic/Algebraize.lean

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -176,38 +176,44 @@ def addProperties (t : Array Expr) : TacticM Unit := withMainContext do
176176
-- If it has, `p` will either be the name of the corresponding `Algebra` property, or a
177177
-- lemma/constructor.
178178
| some p =>
179-
-- The last argument of the `RingHom` property is assumed to be `f`
180-
let f := args[args.size - 1]!
181-
-- Check that `f` appears in the list of functions given to `algebraize`
182-
if ¬ (← t.anyM (Meta.isDefEq · f)) then return
183-
184-
let cinfo ← getConstInfo p
185-
let n ← getExpectedNumArgs cinfo.type
186-
let pargs := Array.replicate n (none : Option Expr)
187-
/- If the attribute points to the corresponding `Algebra` property itself, we assume that it
188-
is definitionally the same as the `RingHom` property. Then, we just need to construct its type
189-
and the local declaration will already give a valid term. -/
190-
if cinfo.isInductive then
191-
let pargs := pargs.set! 0 args[0]!
192-
let pargs := pargs.set! 1 args[1]!
193-
let tp ← mkAppOptM p pargs -- This should be the type `Algebra.Property A B`
194-
unless (← synthInstance? tp).isSome do
195-
liftMetaTactic fun mvarid => do
196-
let nm ← mkFreshBinderNameForTactic `algebraizeInst
197-
let (_, mvar) ← mvarid.note nm decl.toExpr tp
198-
return [mvar]
199-
/- Otherwise, the attribute points to a lemma or a constructor for the `Algebra` property.
200-
In this case, we assume that the `RingHom` property is the last argument of the lemma or
201-
constructor (and that this is all we need to supply explicitly). -/
202-
else
203-
let pargs := pargs.set! (n - 1) decl.toExpr
204-
let val ← mkAppOptM p pargs
205-
let tp ← inferType val
206-
unless (← synthInstance? tp).isSome do
207-
liftMetaTactic fun mvarid => do
208-
let nm ← mkFreshBinderNameForTactic `algebraizeInst
209-
let (_, mvar) ← mvarid.note nm val
210-
return [mvar]
179+
let cinfo ← try getConstInfo p catch _ => return
180+
let p' ← mkConstWithFreshMVarLevels p
181+
let (pargs,_,_) ← forallMetaTelescope (← inferType p')
182+
let tp' := mkAppN p' pargs
183+
184+
let getValType : MetaM (Option (Expr × Expr)) := do
185+
/- If the attribute points to the corresponding `Algebra` property itself, we assume that it
186+
is definitionally the same as the `RingHom` property. Then, we just need to construct its
187+
type and the local declaration will already give a valid term. -/
188+
if cinfo.isInductive then
189+
pargs[0]!.mvarId!.assignIfDefEq args[0]!
190+
pargs[1]!.mvarId!.assignIfDefEq args[1]!
191+
-- This should be the type `Algebra.Property A B`
192+
let tp ← instantiateMVars tp'
193+
if ← isDefEqGuarded decl.type tp then return (decl.toExpr, tp)
194+
else return .none
195+
/- Otherwise, the attribute points to a lemma or a constructor for the `Algebra` property.
196+
In this case, we assume that the `RingHom` property is the last argument of the lemma or
197+
constructor (and that this is all we need to supply explicitly). -/
198+
else
199+
try pargs.back!.mvarId!.assignIfDefEq decl.toExpr catch _ => return .none
200+
let val ← instantiateMVars tp'
201+
let tp ← inferType val -- This should be the type `Algebra.Property A B`.
202+
return (val, tp)
203+
let .some (val,tp) ← getValType | return
204+
/- Find all arguments to `Algebra.Property A B` which are of the form
205+
`RingHom.toAlgebra x` or `Algebra.toModule (RingHom.toAlgebra x)`. -/
206+
let ringHom_args ← tp.getAppArgs.filterMapM <| fun x => liftMetaM do
207+
let y := (← whnfUntil x ``Algebra.toModule) >>= (·.getAppArgs.back?)
208+
return (← whnfUntil (y.getD x) ``RingHom.toAlgebra) >>= (·.getAppArgs.back?)
209+
/- Check that we're not reproving a local hypothesis, and that all involved `RingHom`s are
210+
indeed arguments to the tactic. -/
211+
unless (← synthInstance? tp).isSome || !(← ringHom_args.allM (fun z => t.anyM
212+
(withoutModifyingMCtx <| isDefEq z ·))) do
213+
liftMetaTactic fun mvarid => do
214+
let nm ← mkFreshBinderNameForTactic `algebraizeInst
215+
let (_, mvar) ← mvarid.note nm val tp
216+
return [mvar]
211217
| none => return
212218

213219
/-- Configuration for `algebraize`. -/

MathlibTest/algebraize.lean

Lines changed: 108 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,57 +6,57 @@ section example_definitions
66

77
/-- Test property for when `RingHom` and `Algebra` properties are definitionally the same,
88
see e.g. `RingHom.FiniteType` for a concrete example of this. -/
9-
class Algebra.testProperty1 (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
9+
class Algebra.TestProperty1 (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
1010
out : ∀ x : A, algebraMap A B x = 0
1111

1212
/-- Test property for when `RingHom` and `Algebra` properties are definitionally the same,
1313
see e.g. `RingHom.FiniteType` for a concrete example of this. -/
1414
@[algebraize]
15-
def RingHom.testProperty1 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
16-
@Algebra.testProperty1 A B _ _ f.toAlgebra
15+
def RingHom.TestProperty1 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
16+
@Algebra.TestProperty1 A B _ _ f.toAlgebra
1717

1818
/-- Test property for when the `RingHom` property corresponds to a `Module` property (that is
1919
definitionally the same). See e.g. `Module.Finite` for a concrete example of this. -/
20-
class Module.testProperty2 (A M : Type*) [Semiring A] [AddCommMonoid M] [Module A M] : Prop where
20+
class Module.TestProperty2 (A M : Type*) [Semiring A] [AddCommMonoid M] [Module A M] : Prop where
2121
out : ∀ x : A, ∀ M : M, x • M = 0
2222

2323
/-- Test property for when the `RingHom` property corresponds to a `Module` property (that is
2424
definitionally the same). See e.g. `Module.Finite` for a concrete example of this. -/
25-
@[algebraize Module.testProperty2]
26-
def RingHom.testProperty2 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
25+
@[algebraize Module.TestProperty2]
26+
def RingHom.TestProperty2 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
2727
letI : Algebra A B := f.toAlgebra
28-
Module.testProperty2 A B
28+
Module.TestProperty2 A B
2929

3030
/-- Test property for when the `RingHom` property corresponds to a `Algebra` property that is not
3131
definitionally the same, and needs to be created through a lemma. See e.g. `Algebra.IsIntegral` for
3232
an example. -/
33-
class Algebra.testProperty3 (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
34-
out : Algebra.testProperty1 A B
33+
class Algebra.TestProperty3 (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
34+
out : Algebra.TestProperty1 A B
3535

3636
/- Test property for when the `RingHom` property corresponds to a `Algebra` property that is not
3737
definitionally the same, and needs to be created through a lemma. See e.g. `Algebra.IsIntegral` for
3838
an example. -/
39-
@[algebraize Algebra.testProperty3.mk]
40-
def RingHom.testProperty3 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
41-
f.testProperty1
39+
@[algebraize Algebra.TestProperty3.mk]
40+
def RingHom.TestProperty3 {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) : Prop :=
41+
f.TestProperty1
4242

4343
/- Test property for when the `RingHom` (and `Algebra`) property have an extra explicit argument,
4444
and hence needs to be created through a lemma. See e.g.
4545
`Algebra.IsStandardSmoothOfRelativeDimension` for an example. -/
46-
class Algebra.testProperty4 (n : ℕ) (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
46+
class Algebra.TestProperty4 (n : ℕ) (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] : Prop where
4747
out : ∀ m, n = m
4848

4949
/- Test property for when the `RingHom` (and `Algebra`) property have an extra explicit argument,
5050
and hence needs to be created through a lemma. See e.g.
5151
`Algebra.IsStandardSmoothOfRelativeDimension` for an example. -/
52-
@[algebraize testProperty4.toAlgebra]
53-
def RingHom.testProperty4 (n : ℕ) {A B : Type*} [CommRing A] [CommRing B] (_ : A →+* B) : Prop :=
52+
@[algebraize RingHom.TestProperty4.toAlgebra]
53+
def RingHom.TestProperty4 (n : ℕ) {A B : Type*} [CommRing A] [CommRing B] (_ : A →+* B) : Prop :=
5454
∀ m, n = m
5555

56-
lemma testProperty4.toAlgebra (n : ℕ) {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B)
57-
(hf : f.testProperty4 n) :
56+
lemma RingHom.TestProperty4.toAlgebra (n : ℕ) {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B)
57+
(hf : f.TestProperty4 n) :
5858
letI : Algebra A B := f.toAlgebra
59-
Algebra.testProperty4 n A B :=
59+
Algebra.TestProperty4 n A B :=
6060
letI : Algebra A B := f.toAlgebra
6161
{ out := hf }
6262

@@ -78,7 +78,7 @@ example (A B : Type*) [CommRing A] [CommRing B] (f : A →+* B) : True := by
7878
fail_if_success -- Check that this instance is not available by default
7979
have h : Algebra A B := inferInstance
8080
algebraize [f']
81-
guard_hyp algInst := f'.toAlgebra
81+
guard_hyp algInst := f'.toAlgebra
8282
trivial
8383

8484
/-- Synthesize algebra instance from a composition -/
@@ -99,35 +99,113 @@ example (A B C : Type*) [CommRing A] [CommRing B] [CommRing C] (f : A →+* B) (
9999
guard_hyp scalarTowerInst := IsScalarTower.of_algebraMap_eq' rfl
100100
trivial
101101

102-
example (A B : Type*) [CommRing A] [CommRing B] (f : A →+* B) (hf : f.testProperty1) : True := by
102+
example (A B : Type*) [CommRing A] [CommRing B] (f g : A →+* B) (hf : f.TestProperty1)
103+
(hg : g.TestProperty1) : True := by
103104
algebraize [f]
104-
guard_hyp algebraizeInst : Algebra.testProperty1 A B
105+
guard_hyp algebraizeInst : @Algebra.TestProperty1 A B _ _ f.toAlgebra
106+
fail_if_success
107+
guard_hyp algebraizeInst_1
105108
trivial
106109

107-
example (A B : Type*) [CommRing A] [CommRing B] (f : A →+* B) (hf : f.testProperty2) : True := by
110+
example (A B : Type*) [CommRing A] [CommRing B] (f g : A →+* B) (hf : f.TestProperty2)
111+
(hg : g.TestProperty2) : True := by
108112
algebraize [f]
109-
guard_hyp algebraizeInst : Module.testProperty2 A B
113+
guard_hyp algebraizeInst : @Module.TestProperty2 A B _ _ f.toAlgebra.toModule
114+
fail_if_success
115+
guard_hyp algebraizeInst_1
110116
trivial
111117

112-
example (A B : Type*) [CommRing A] [CommRing B] (f : A →+* B) (hf : f.testProperty3) : True := by
118+
example (A B : Type*) [CommRing A] [CommRing B] (f g : A →+* B) (hf : f.TestProperty3)
119+
(hg : g.TestProperty3) : True := by
113120
algebraize [f]
114-
guard_hyp algebraizeInst : Algebra.testProperty3 A B
121+
guard_hyp algebraizeInst : @Algebra.TestProperty3 A B _ _ f.toAlgebra
122+
fail_if_success
123+
guard_hyp algebraizeInst_1
115124
trivial
116125

117-
example (n : ℕ) (A B : Type*) [CommRing A] [CommRing B] (f : A →+* B) (hf : f.testProperty4 n) :
118-
True := by
126+
example (n m : ℕ) (A B : Type*) [CommRing A] [CommRing B] (f g : A →+* B) (hf : f.TestProperty4 n)
127+
(hg : g.TestProperty4 m) : True := by
119128
algebraize [f]
120-
guard_hyp algebraizeInst : Algebra.testProperty4 n A B
129+
guard_hyp algebraizeInst : Algebra.TestProperty4 n A B
130+
fail_if_success
131+
guard_hyp algebraizeInst_1
121132
trivial
122133

123134
/-- Synthesize from morphism property of a composition (and check that tower is also synthesized). -/
124135
example (A B C : Type*) [CommRing A] [CommRing B] [CommRing C] (f : A →+* B) (g : B →+* C)
125-
(hfg : (g.comp f).testProperty1) : True := by
136+
(hfg : (g.comp f).TestProperty1) : True := by
126137
fail_if_success -- Check that this instance is not available by default
127138
have h : Algebra.Flat A C := inferInstance
128139
fail_if_success
129140
have h : IsScalarTower A B C := inferInstance
130141
algebraize [f, g, g.comp f]
131-
guard_hyp algebraizeInst : Algebra.testProperty1 A C
142+
guard_hyp algebraizeInst : Algebra.TestProperty1 A C
132143
guard_hyp scalarTowerInst := IsScalarTower.of_algebraMap_eq' rfl
133144
trivial
145+
146+
section
147+
/- Test that the algebraize tactic also works on non-RingHom types -/
148+
149+
structure Bar (A B : Type*) [CommRing A] [CommRing B] where
150+
f : A →+* B
151+
152+
@[algebraize testProperty1_of_bar]
153+
def Bar.TestProperty1 {A B : Type*} [CommRing A] [CommRing B] (b : Bar A B) : Prop :=
154+
∀ z, b.f z = 0
155+
156+
lemma testProperty1_of_bar {A B : Type*} [CommRing A] [CommRing B] (b : Bar A B) (h : b.TestProperty1) :
157+
@Algebra.TestProperty1 A B _ _ b.f.toAlgebra := @Algebra.TestProperty1.mk A B _ _ b.f.toAlgebra h
158+
159+
@[algebraize testProperty2_of_bar]
160+
def Bar.testProperty2 {A B : Type*} [CommRing A] [CommRing B] (b : Bar A B) : Prop :=
161+
letI : Algebra A B := b.f.toAlgebra;
162+
∀ (r : A) (M : B), r • M = 0
163+
164+
lemma testProperty2_of_bar {A B : Type*} [CommRing A] [CommRing B] (b : Bar A B) (h : b.testProperty2) :
165+
@Module.TestProperty2 A B _ _ b.f.toAlgebra.toModule :=
166+
@Module.TestProperty2.mk A B _ _ b.f.toAlgebra.toModule h
167+
168+
example {A B : Type*} [CommRing A] [CommRing B] (b c : Bar A B) (hb : b.TestProperty1)
169+
(hc : c.TestProperty1) : True := by
170+
algebraize [b.f]
171+
guard_hyp algebraizeInst : @Algebra.TestProperty1 A B _ _ b.f.toAlgebra
172+
fail_if_success -- make sure that only arguments are used
173+
guard_hyp algebraizeInst_1 : @Algebra.testProperty1 A B _ _ c.f.toAlgebra
174+
trivial
175+
176+
example {A B : Type*} [CommRing A] [CommRing B] (b c : Bar A B) (hb : b.testProperty2)
177+
(hc : c.testProperty2) : True := by
178+
algebraize [b.f]
179+
guard_hyp algebraizeInst : @Module.TestProperty2 A B _ _ b.f.toAlgebra.toModule
180+
fail_if_success
181+
guard_hyp algebraizeInst_1 --: @Module.testProperty2 A B _ _ c.f.toAlgebra.toModule
182+
trivial
183+
184+
structure Buz (A B : Type*) [CommRing A] [CommRing B] where
185+
x : (A →+* B) ⊕ (A →+* B)
186+
187+
@[algebraize testProperty1_of_buz_inl]
188+
def Buz.TestProperty1 {A B : Type*} [CommRing A] [CommRing B] (b : Buz A B) :=
189+
b.x.elim (@Algebra.TestProperty1 A B _ _ ·.toAlgebra) (fun _ => False)
190+
191+
lemma testProperty1_of_buz_inl {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B) :
192+
Buz.TestProperty1 ⟨.inl f⟩ → @Algebra.TestProperty1 A B _ _ f.toAlgebra := id
193+
194+
-- check that this also works when the argument *contains* a ringhom
195+
example {A B : Type*} [CommRing A] [CommRing B] (f g : A →+* B)
196+
(hf : Buz.TestProperty1 ⟨.inl f⟩) (hg : Buz.TestProperty1 ⟨.inl g⟩) : True := by
197+
algebraize [f]
198+
guard_hyp algebraizeInst : @Algebra.TestProperty1 A B _ _ f.toAlgebra
199+
fail_if_success
200+
guard_hyp algebraizeInst_1 --: @Algebra.testProperty1 A B _ _ g.toAlgebra
201+
trivial
202+
203+
-- check that there is no issue with trying the lemma on a mismatching argument.
204+
example {A B : Type*} [CommRing A] [CommRing B] (f : A →+* B)
205+
(hf : Buz.TestProperty1 ⟨.inr f⟩) : True := by
206+
algebraize [f] -- this could error if it tried applying `testProperty1_ofBuz_inl` to `hf`
207+
fail_if_success
208+
guard_hyp algebraizeInst
209+
trivial
210+
211+
end

0 commit comments

Comments
 (0)