Skip to content

Commit ef47980

Browse files
grunwegpre-commit-ci-lite[bot]
authored andcommitted
feat: support coercions in the differential geometry elaborators (leanprover-community#30307)
Generalise the `CMDiff`, `MDiff` and `mfderiv` elaborators (and its friends) to apply to any object which coerces to a function. We can such speak about the smoothness of a `PartialEquiv`, `OpenPartialHomeomorph` (or the future `ClosedPartialHomeomorph` and `PartialHomeomorph`), smooth section or bundled smooth map without the need for a coercion. As a by-product, add new functions `Lean.Meta.ensure{HasType,IsFunction,IsSort}` which apply the respective coercion if necessary. While at it, add more sections (and section headers) to the test file for the elaborators. Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 462e91f commit ef47980

3 files changed

Lines changed: 167 additions & 30 deletions

File tree

Mathlib/Geometry/Manifold/Notation.lean

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ variable {s : E → E'} in
6767
These elaborators can be combined: `CMDiffAt[u] n (T% s) x`
6868
6969
**Warning.** These elaborators are a proof of concept; the implementation should be considered a
70-
prototype. Don't rewrite all of mathlib to use it just yet. Notable bugs and limitations include
70+
prototype. Don't rewrite all of mathlib to use it just yet. Notable limitations include
7171
the following.
7272
7373
## TODO
@@ -76,10 +76,8 @@ the following.
7676
is correct 90% of the time).
7777
For products of vector spaces `E × F`, this could print a warning about making a choice between
7878
the model in `E × F` and the product of the models on `E` and `F`.
79-
- extend the elaborators to support `OpenPartialHomeomorph`s and `PartialEquiv`s
80-
- better error messages (as needed)
81-
- further testing and fixing of edge cases
82-
- add tests for all of the above
79+
- better error messages (as needed), with tests
80+
- further testing and fixing of edge cases (with tests)
8381
- add delaborators for these elaborators
8482
8583
-/
@@ -102,7 +100,7 @@ private def findSomeLocalInstanceOf? (c : Name) {α} (p : Expr → Expr → Meta
102100
MetaM (Option α) := do
103101
(← getLocalInstances).findSomeM? fun inst ↦ do
104102
if inst.className == c then
105-
let type ← whnfR <|← instantiateMVars <|← inferType inst.fvar
103+
let type ← whnfR <| ← instantiateMVars <| ← inferType inst.fvar
106104
p inst.fvar type
107105
else return none
108106

@@ -112,7 +110,7 @@ to `p`. -/
112110
private def findSomeLocalHyp? {α} (p : Expr → Expr → MetaM (Option α)) : MetaM (Option α) := do
113111
(← getLCtx).findDeclRevM? fun decl ↦ do
114112
if decl.isImplementationDetail then return none
115-
let type ← whnfR <|← instantiateMVars decl.type
113+
let type ← whnfR <| ← instantiateMVars decl.type
116114
p decl.toExpr type
117115

118116
end Elab
@@ -134,7 +132,7 @@ run.
134132
-- TODO: factor out `MetaM` component for reuse
135133
scoped elab:max "T% " t:term:arg : term => do
136134
let e ← Term.elabTerm t none
137-
let etype ← whnf <|← instantiateMVars <|← inferType e
135+
let etype ← whnf <| ← instantiateMVars <| ← inferType e
138136
match etype with
139137
| .forallE x base tgt _ => withLocalDeclD x base fun x ↦ do
140138
let tgtHasLooseBVars := tgt.hasLooseBVars
@@ -319,7 +317,7 @@ We pass `e` instead of just its type for better diagnostics.
319317
320318
If `es` is `some`, we verify that `src` and the type of `es` are definitionally equal. -/
321319
def findModels (e : Expr) (es : Option Expr) : TermElabM (Expr × Expr) := do
322-
let etype ← whnf <|← instantiateMVars <|← inferType e
320+
let etype ← whnf <| ← instantiateMVars <| ← inferType e
323321
match etype with
324322
| .forallE _ src tgt _ =>
325323
if tgt.hasLooseBVars then
@@ -332,7 +330,7 @@ def findModels (e : Expr) (es : Option Expr) : TermElabM (Expr × Expr) := do
332330
/- Note: we use `isDefEq` here since persistent metavariable assignments in `src` and
333331
`estype` are acceptable.
334332
TODO: consider attempting to coerce `es` to a `Set`. -/
335-
if !(← isDefEq estype <|← mkAppM ``Set #[src]) then
333+
if !(← isDefEq estype <| ← mkAppM ``Set #[src]) then
336334
throwError "The domain {src} of {e} is not definitionally equal to the carrier type of \
337335
the set {es} : {estype}"
338336
let tgtI ← findModel tgt (src, srcI)
@@ -348,15 +346,15 @@ trying to determine `I` and `J` from the local context.
348346
The argument `x` can be omitted. -/
349347
scoped elab:max "MDiffAt[" s:term "]" ppSpace f:term:arg : term => do
350348
let es ← Term.elabTerm s none
351-
let ef ← Term.elabTerm f none
349+
let ef ← ensureIsFunction <| ← Term.elabTerm f none
352350
let (srcI, tgtI) ← findModels ef es
353351
mkAppM ``MDifferentiableWithinAt #[srcI, tgtI, ef, es]
354352

355353
/-- `MDiffAt f x` elaborates to `MDifferentiableAt I J f x`,
356354
trying to determine `I` and `J` from the local context.
357355
The argument `x` can be omitted. -/
358356
scoped elab:max "MDiffAt" ppSpace t:term:arg : term => do
359-
let e ← Term.elabTerm t none
357+
let e ← ensureIsFunction <| ← Term.elabTerm t none
360358
let (srcI, tgtI) ← findModels e none
361359
mkAppM ``MDifferentiableAt #[srcI, tgtI, e]
362360

@@ -366,7 +364,7 @@ scoped elab:max "MDiffAt" ppSpace t:term:arg : term => do
366364
-- The argument `x` can be omitted. -/
367365
-- scoped elab:max "MDiffAt2" ppSpace t:term:arg : term => do
368366
-- let e ← Term.elabTerm t none
369-
-- let etype ← whnfR <|← instantiateMVars <|← inferType e
367+
-- let etype ← whnfR <| ← instantiateMVars <| ← inferType e
370368
-- forallBoundedTelescope etype (some 1) fun src tgt ↦ do
371369
-- if let some src := src[0]? then
372370
-- let srcI ← findModel (← inferType src)
@@ -383,25 +381,30 @@ scoped elab:max "MDiffAt" ppSpace t:term:arg : term => do
383381
trying to determine `I` and `J` from the local context. -/
384382
scoped elab:max "MDiff[" s:term "]" ppSpace t:term:arg : term => do
385383
let es ← Term.elabTerm s none
386-
let et ← Term.elabTerm t none
384+
let et ← ensureIsFunction <| ← Term.elabTerm t none
387385
let (srcI, tgtI) ← findModels et es
388386
mkAppM ``MDifferentiableOn #[srcI, tgtI, et, es]
389387

390388
/-- `MDiff f` elaborates to `MDifferentiable I J f`,
391389
trying to determine `I` and `J` from the local context. -/
392390
scoped elab:max "MDiff" ppSpace t:term:arg : term => do
393-
let e ← Term.elabTerm t none
391+
let e ← ensureIsFunction <| ← Term.elabTerm t none
394392
let (srcI, tgtI) ← findModels e none
395393
mkAppM ``MDifferentiable #[srcI, tgtI, e]
396394

395+
-- We ensure the type of `n` before checking `f` is a function to provide better error messages
396+
-- in case e.g. `f` and `n` are swapped.
397+
-- TODO: provide better error messages if just `n` is forgotten (say, by making `n` optional in
398+
-- the parser and erroring later in the elaborator); currently, this yields just a parser error.
399+
397400
/-- `CMDiffAt[s] n f x` elaborates to `ContMDiffWithinAt I J n f s x`,
398401
trying to determine `I` and `J` from the local context.
399402
`n` is coerced to `WithTop ℕ∞` if necessary (so passing a `ℕ`, `∞` or `ω` are all supported).
400403
The argument `x` can be omitted. -/
401404
scoped elab:max "CMDiffAt[" s:term "]" ppSpace nt:term:arg ppSpace f:term:arg : term => do
402405
let es ← Term.elabTerm s none
403-
let ef ← Term.elabTerm f none
404406
let ne ← Term.elabTermEnsuringType nt q(WithTop ℕ∞)
407+
let ef ← ensureIsFunction <| ← Term.elabTerm f none
405408
let (srcI, tgtI) ← findModels ef es
406409
mkAppM ``ContMDiffWithinAt #[srcI, tgtI, ne, ef, es]
407410

@@ -410,7 +413,7 @@ trying to determine `I` and `J` from the local context.
410413
`n` is coerced to `WithTop ℕ∞` if necessary (so passing a `ℕ`, `∞` or `ω` are all supported).
411414
The argument `x` can be omitted. -/
412415
scoped elab:max "CMDiffAt" ppSpace nt:term:arg ppSpace t:term:arg : term => do
413-
let e ← Term.elabTerm t none
416+
let e ← ensureIsFunction <| ← Term.elabTerm t none
414417
let ne ← Term.elabTermEnsuringType nt q(WithTop ℕ∞)
415418
let (srcI, tgtI) ← findModels e none
416419
mkAppM ``ContMDiffAt #[srcI, tgtI, ne, e]
@@ -420,32 +423,32 @@ trying to determine `I` and `J` from the local context.
420423
`n` is coerced to `WithTop ℕ∞` if necessary (so passing a `ℕ`, `∞` or `ω` are all supported). -/
421424
scoped elab:max "CMDiff[" s:term "]" ppSpace nt:term:arg ppSpace f:term:arg : term => do
422425
let es ← Term.elabTerm s none
423-
let ef ← Term.elabTerm f none
424426
let ne ← Term.elabTermEnsuringType nt q(WithTop ℕ∞)
427+
let ef ← ensureIsFunction <| ← Term.elabTerm f none
425428
let (srcI, tgtI) ← findModels ef es
426429
mkAppM ``ContMDiffOn #[srcI, tgtI, ne, ef, es]
427430

428431
/-- `CMDiff n f` elaborates to `ContMDiff I J n f`,
429432
trying to determine `I` and `J` from the local context.
430433
`n` is coerced to `WithTop ℕ∞` if necessary (so passing a `ℕ`, `∞` or `ω` are all supported). -/
431434
scoped elab:max "CMDiff" ppSpace nt:term:arg ppSpace f:term:arg : term => do
432-
let e ← Term.elabTerm f none
433435
let ne ← Term.elabTermEnsuringType nt q(WithTop ℕ∞)
436+
let e ← ensureIsFunction <| ← Term.elabTerm f none
434437
let (srcI, tgtI) ← findModels e none
435438
mkAppM ``ContMDiff #[srcI, tgtI, ne, e]
436439

437440
/-- `mfderiv[u] f x` elaborates to `mfderivWithin I J f u x`,
438441
trying to determine `I` and `J` from the local context. -/
439442
scoped elab:max "mfderiv[" s:term "]" ppSpace t:term:arg : term => do
440443
let es ← Term.elabTerm s none
441-
let e ← Term.elabTerm t none
444+
let e ← ensureIsFunction <| ← Term.elabTerm t none
442445
let (srcI, tgtI) ← findModels e es
443446
mkAppM ``mfderivWithin #[srcI, tgtI, e, es]
444447

445448
/-- `mfderiv% f x` elaborates to `mfderiv I J f x`,
446449
trying to determine `I` and `J` from the local context. -/
447450
scoped elab:max "mfderiv%" ppSpace t:term:arg : term => do
448-
let e ← Term.elabTerm t none
451+
let e ← ensureIsFunction <| ← Term.elabTerm t none
449452
let (srcI, tgtI) ← findModels e none
450453
mkAppM ``mfderiv #[srcI, tgtI, e]
451454

Mathlib/Lean/Meta/Basic.lean

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,28 @@ def Lean.Meta.withEnsuringLocalInstance {α : Type} (inst : MVarId) (k : MetaM (
9494
let (e, v) ← k
9595
let e' := (← e.abstractM #[inst']).instantiate1 instE
9696
return (e', v)
97+
98+
/-- Checks that `e` has type `expectedType` (i.e. that its type is defeq to `expectedType`
99+
at the current transparency). If not, coerces `e` to this type
100+
or fails with a descriptive error. -/
101+
def Lean.Meta.ensureHasType (e expectedType : Expr) : MetaM Expr := do
102+
let ty ← inferType e
103+
if ← withNewMCtxDepth (isDefEq ty expectedType) then return e else
104+
(← coerceSimple? e expectedType).toOption.getDM <|
105+
throwError "Expected{indentD e}\nto have type{indentD ty}\n or to be coercible to it"
106+
107+
/-- Checks that `e` is a function (i.e. that its type is a `.forallE` after `instantiateMVars` and
108+
`whnf`). If not, coerces `e` to a function or fails with a descriptive error. -/
109+
def Lean.Meta.ensureIsFunction (e : Expr) : MetaM Expr := do
110+
let ty ← whnf <| ← instantiateMVars <| ← inferType e
111+
if ty.isForall then return e else (← coerceToFunction? e).getDM <|
112+
throwError "Expected{indentD e}\nof type{indentD ty}\nto be a function, or to be coercible to \
113+
a function"
114+
115+
/-- Checks that `e` is a type (i.e. that its type is a `Sort` after `instantiateMVars` and
116+
`whnf`). If not, coerces `e` to a Sort or fails with a descriptive error. -/
117+
def Lean.Meta.ensureIsSort (e : Expr) : MetaM Expr := do
118+
let ty ← whnf <| ← instantiateMVars <| ← inferType e
119+
if ty.isSort then return e else (← coerceToSort? e).getDM <|
120+
throwError "Expected{indentD e}\nof type{indentD ty}\nto be a Sort, or to be coercible to \
121+
a Sort"

MathlibTest/DifferentialGeometry/Notation.lean

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,13 @@ end TotalSpace
9292
/-! Tests for the elaborators for `MDifferentiable{WithinAt,At,On}`. -/
9393
section differentiability
9494

95-
-- Start with some basic tests: a simple function, both in applied and unapplied form.
9695
variable {EM' : Type*} [NormedAddCommGroup EM']
9796
[NormedSpace 𝕜 EM'] {H' : Type*} [TopologicalSpace H'] (I' : ModelWithCorners 𝕜 EM' H')
9897
{M' : Type*} [TopologicalSpace M'] [ChartedSpace H' M']
9998

99+
/-! Some basic tests: a simple function, both in applied and unapplied form -/
100+
section basic
101+
100102
-- General case: a function between two manifolds.
101103
variable {f : M → M'} {s : Set M} {m : M}
102104

@@ -306,12 +308,98 @@ variable {f : 𝕜 → 𝕜} {u : Set 𝕜} {a : 𝕜}
306308
#guard_msgs in
307309
#check MDiff[u] f
308310

309-
-- This elaborator can be combined with the total space elaborator.
310-
-- XXX: these tests might be incomplete; extend as needed!
311+
end basic
312+
313+
/-! A partial homeomorphism or partial equivalence. More generally, this works for any type
314+
with a coercion to (possibly dependent) functions. -/
315+
section coercion
316+
317+
variable {s : Set M} {m : M}
318+
319+
variable {φ : OpenPartialHomeomorph M E} {ψ : PartialEquiv M E}
320+
321+
/-- info: MDifferentiableWithinAt I 𝓘(𝕜, E) (↑φ) s : M → Prop -/
322+
#guard_msgs in
323+
#check MDiffAt[s] φ
324+
325+
/-- info: MDifferentiableWithinAt I 𝓘(𝕜, E) (↑ψ) s : M → Prop -/
326+
#guard_msgs in
327+
#check MDiffAt[s] ψ
328+
329+
/-- info: MDifferentiableAt I 𝓘(𝕜, E) ↑φ : M → Prop -/
330+
#guard_msgs in
331+
#check MDiffAt φ
332+
333+
/-- info: MDifferentiableAt I 𝓘(𝕜, E) ↑ψ : M → Prop -/
334+
#guard_msgs in
335+
#check MDiffAt ψ
336+
337+
/-- info: MDifferentiableOn I 𝓘(𝕜, E) (↑φ) s : Prop -/
338+
#guard_msgs in
339+
#check MDiff[s] φ
340+
341+
/-- info: MDifferentiableOn I 𝓘(𝕜, E) (↑ψ) s : Prop -/
342+
#guard_msgs in
343+
#check MDiff[s] ψ
344+
345+
/-- info: MDifferentiable I 𝓘(𝕜, E) ↑φ : Prop -/
346+
#guard_msgs in
347+
#check MDiff φ
348+
349+
/-- info: ContMDiffWithinAt I 𝓘(𝕜, E) 2 (↑ψ) s : M → Prop -/
350+
#guard_msgs in
351+
#check CMDiffAt[s] 2 ψ
352+
353+
/-- info: ContMDiffOn I 𝓘(𝕜, E) 2 (↑φ) s : Prop -/
354+
#guard_msgs in
355+
#check CMDiff[s] 2 φ
356+
357+
/-- info: ContMDiffAt I 𝓘(𝕜, E) 2 ↑φ : M → Prop -/
358+
#guard_msgs in
359+
#check CMDiffAt 2 φ
360+
361+
/-- info: ContMDiff I 𝓘(𝕜, E) 2 ↑ψ : Prop -/
362+
#guard_msgs in
363+
#check CMDiff 2 ψ
364+
365+
/-- info: mfderiv I 𝓘(𝕜, E) ↑φ : (x : M) → TangentSpace I x →L[𝕜] TangentSpace 𝓘(𝕜, E) (↑φ x) -/
366+
#guard_msgs in
367+
#check mfderiv% φ
368+
369+
/--
370+
info: mfderivWithin I 𝓘(𝕜, E) (↑ψ) s : (x : M) → TangentSpace I x →L[𝕜] TangentSpace 𝓘(𝕜, E) (↑ψ x)
371+
-/
372+
#guard_msgs in
373+
#check mfderiv[s] ψ
374+
375+
/--
376+
info: mfderivWithin I 𝓘(𝕜, E) (↑ψ) s : (x : M) → TangentSpace I x →L[𝕜] TangentSpace 𝓘(𝕜, E) (↑ψ x)
377+
-/
378+
#guard_msgs in
379+
variable {f : ContMDiffSection I F n V} in
380+
#check mfderiv[s] ψ
381+
382+
/-- info: mfderiv I I' ⇑g : (x : M) → TangentSpace I x →L[𝕜] TangentSpace I' (g x) -/
383+
#guard_msgs in
384+
variable {g : ContMDiffMap I I' M M' n} in
385+
#check mfderiv% g
386+
387+
-- An example of "any type" which coerces to functions.
388+
/-- info: mfderiv I I' ⇑g : (x : M) → TangentSpace I x →L[𝕜] TangentSpace I' (g x) -/
389+
#guard_msgs in
390+
variable {g : Equiv M M'} in
391+
#check mfderiv% g
392+
393+
end coercion
311394

312395
variable {σ : Π x : M, V x} {σ' : (x : E) → Trivial E E' x} {s : E → E'}
313396
variable (X : (m : M) → TangentSpace I m) [IsManifold I 1 M]
314397

398+
/-! These elaborators can be combined with the total space elaborator. -/
399+
section interaction
400+
401+
-- Note: these tests might be incomplete; extend as needed!
402+
315403
/-- info: MDifferentiableAt I (I.prod 𝓘(𝕜, E)) fun m ↦ TotalSpace.mk' E m (X m) : M → Prop -/
316404
#guard_msgs in
317405
#check MDiffAt (T% X)
@@ -326,6 +414,8 @@ info: MDifferentiableAt 𝓘(𝕜, E) (𝓘(𝕜, E).prod 𝓘(𝕜, E')) fun x
326414
#guard_msgs in
327415
#check MDiffAt (T% σ')
328416

417+
end interaction
418+
329419
section
330420

331421
variable [IsManifold I 2 M]
@@ -479,10 +569,11 @@ variable {f : M → M'} {s : Set M} {m : M}
479569

480570
variable [IsManifold I 1 M] [IsManifold I' 1 M']
481571

482-
-- TODO: can there be better error messages when forgetting the smoothness exponent?
572+
-- Testing error messages when forgetting the smoothness exponent or swapping arguments.
483573
section error
484574

485-
-- yields a parse error, "unexpected toekn '/--'; expected term"
575+
-- yields a parse error, "unexpected token '/--'; expected term"
576+
-- TODO: make this parse, but error in the elaborator
486577
-- #check CMDiffAt[s] f
487578

488579
/--
@@ -498,7 +589,7 @@ error: Expected
498589
m
499590
of type
500591
M
501-
to be a function
592+
to be a function, or to be coercible to a function
502593
-/
503594
#guard_msgs in
504595
#check CMDiffAt[s] f m
@@ -516,14 +607,32 @@ error: Expected
516607
m
517608
of type
518609
M
519-
to be a function
610+
to be a function, or to be coercible to a function
520611
-/
521612
#guard_msgs in
522-
#check CMDiffAt[s] f m
613+
#check CMDiff[s] f m
523614

524-
-- yields a parse error, "unexpected toekn '/--'; expected term"
615+
-- yields a parse error, "unexpected token '/--'; expected term"
525616
-- #check CMDiffAt f
526617

618+
/--
619+
error: Type mismatch
620+
f
621+
has type
622+
M → M'
623+
of sort `Type (max u_10 u_4)` but is expected to have type
624+
WithTop ℕ∞
625+
of sort `Type`
626+
---
627+
error: Expected
628+
n
629+
of type
630+
Option ℕ∞
631+
to be a function, or to be coercible to a function
632+
-/
633+
#guard_msgs in
634+
#check CMDiff f n
635+
527636
end error
528637

529638
/-- info: ContMDiffWithinAt I I' 1 f s : M → Prop -/

0 commit comments

Comments
 (0)