It's very nice to introduce myself to the Zulip Chat. I'm Ryland, an undergraduate math student currently taking a class on Lean4. I'm experimenting on the side trying to do some work on Partial Derivatives since I saw they still had not been added to Mathlib.
I've been learning about Frechet Derivatives through Wikipedia and Mathlib and I think I have successfully implemented some of them in a more exact case dealing with R^n, but I also think I can implement them for the more general addcommgroup, k module, topological space version too.
Do you all have any advice on how I should best proceed to keep working on this, even if it is just for my own edification // personal project with lean? I'm under the impression that we are stylistically trying to make everything as general as possible, is the way I described enough?
The topic of partial derivatives came up multiple times and always fizzles out as people experienced with Lean don't really need them. Some time ago I wrote a guide to partial derivatives, it might clarify a thing or two.
@Anne Baanen the guide to partial derivatives that Tomas Skrivan wrote and linked above was supposed to eventually make it onto the community website, but that never happened. Can you add that as a TODO item for the website overhaul? To be clear, I'm not asking you to do the work to add it to the website, only to add it as a tracked item on that document.
-- Index in brackets → EuclideanSpace.single i 1 direction, field ℝ#check∂[i](f·y)x-- (∂[i] (f · y)) x = fderiv ℝ (f · y) x (single i 1)#check∂[i]x'',(fx''y)-- fun x'' => fderiv ℝ ... x'' (single i 1)#check∂[i](x'':=x),(fx''y)-- fderiv ℝ ... x (single i 1)-- Numeric literal as index (isFin short-circuits on nat literals)variable[NeZeron]#check∂[0](f·y)x-- fderiv ℝ (f · y) x (single (0 : Fin n) 1)-- Field + index#check∂[ℂ,i](g·y')x'-- fderiv ℂ (g · y') x' (single i 1)-- Field only → curried CLM-map; normal application gives the CLM at a point#check∂[ℝ](f·y)-- fderiv ℝ (f · y) : _ → _ →L[ℝ] _#check∂[ℝ](f·y)x-- (∂[ℝ] (f · y)) x : _ →L[ℝ] _#check∂[ℝ]x'',(fx''y)-- fun x'' => fderiv ℝ (fun x'' => f x'' y) x''#check∂[ℝ](x'':=x),(fx''y)-- fderiv ℝ (fun x'' => f x'' y) x#check∂[ℝ](x'':=x;dx),(fx''y)-- fderiv ℝ (fun x'' => f x'' y) x dx-- No brackets → defaults to ℝ#check∂(f·y)-- fderiv ℝ (f · y)#check∂(f·y)x-- (∂ (f · y)) x-- second derivative#check∂∂(f·y)#check∂(x':=x),∂(x'':=x'),(fx''y)
code
importMathlibopenLeanLean.ElabLean.Elab.TermLean.Metadeclare_syntax_catpartialHeaddeclare_syntax_catpartialBody-- Head formssyntaxterm", "term:partialHead-- `𝕜, i`syntaxterm:partialHead-- `𝕜` or `i` (elaborator decides)-- Body forms: only the non-application cases.-- `∂ f x` is handled by parsing `∂ f` as a term and letting Lean apply `x`.syntaxident", "term:partialBodysyntax"("ident" := "term")"", "term:partialBodysyntax"("ident" := "term" ; "term")"", "term:partialBody-- partialBody forms (tried first due to definition order)syntax(name:=«∂Plain»)"∂"partialBody:termsyntax(name:=«∂Bracketed»)"∂["partialHead"]"partialBody:term-- function forms (fallback when partialBody doesn't match)-- `syntax:max` makes the result atom-level, so `∂ f x` parses as `(∂ f) x`syntax:max(name:=«∂PlainFun»)"∂"term:arg:termsyntax:max(name:=«∂BracketedFun»)"∂["partialHead"]"term:arg:term-- ────────────────────────────────────────────────────────────-- Helpers-- ────────────────────────────────────────────────────────────/-- `true` iff `t` should be treated as a `Fin n` index. Numeric literals (e.g. `0`, `1`) default-elaborate as `Nat` but are unambiguously intended as direction indices, so we short-circuit on them. For other expressions we check whether the inferred type is `Fin n`. -/privatedefisFin(t:Syntax):TermElabMBool:=-- Numeric literal → always an indexift.isNatLit?.isSomethenreturntrueelsewithoutModifyingStatedotrylete←elabTermtnoneletty←whnf(←inferTypee)returnty.isAppOf``Fin&&ty.getAppNumArgs==1catch_=>returnfalse/-- Parse the head bracket into `(field, optional index)` as term syntax. -/privatedefparseHead(head:Syntax):TermElabM(TSyntax`term×Option(TSyntax`term)):=domatchheadwith|`(partialHead|$k:term,$i:term)=>return(k,somei)|`(partialHead|$t:term)=>if←isFintthenreturn(←`(ℝ),somet)-- index only → default field ℝelsereturn(t,none)-- type only → field 𝕜, CLM result|_=>throwError"∂[]: unexpected head syntax"/-- Builds `EuclideanSpace.single i (1 : 𝕜)`. -/privatedefmkDir(𝕜i:TSyntax`term):TermElabM(TSyntax`term):=`(EuclideanSpace.single$i(1:$𝕜))/-- Assemble `∂[·] body` for the lambda / at-point / directional forms. -/privatedefassemble(𝕜:TSyntax`term)(idx:Option(TSyntax`term))(body:Syntax):TermElabM(TSyntax`term):=doletdir?←idx.mapM(mkDir𝕜)matchbodywith-- x', e → fun x' => fderiv 𝕜 (fun x' => e) x' [dir]|`(partialBody|$x:ident,$e)=>matchdir?with|none=>`(fun$x=>fderiv$𝕜(fun$x=>$e)$x)|somed=>`(fun$x=>fderiv$𝕜(fun$x=>$e)$x$d)-- (x := v), e → fderiv 𝕜 (fun x => e) v [dir]|`(partialBody|($x:ident:=$v),$e)=>matchdir?with|none=>`(fderiv$𝕜(fun$x=>$e)$v)|somed=>`(fderiv$𝕜(fun$x=>$e)$v$d)-- (x := v; dv), e → fderiv 𝕜 (fun x => e) v dv [no index allowed]|`(partialBody|($x:ident:=$v;$dv),$e)=>ifidx.isSomethenthrowError"∂: cannot combine a basis index [i] with an explicit direction (x := v; dv)"`(fderiv$𝕜(fun$x=>$e)$v$dv)|_=>throwError"∂: unrecognized body syntax"/-- Assemble `∂[·] f` for the curried function form. -/privatedefassembleFun(𝕜:TSyntax`term)(idx:Option(TSyntax`term))(f:TSyntax`term):TermElabM(TSyntax`term):=domatchidxwith|none=>`(fderiv$𝕜$f)|somei=>letd←mkDir𝕜iwithFreshMacroScope`(funx=>fderiv$𝕜$fx$d)-- ────────────────────────────────────────────────────────────-- Elaborators-- ────────────────────────────────────────────────────────────@[term_elab«∂Plain»]defelabPartialPlain:TermElab:=funstxexp?=>dolet`(∂$body:partialBody):=stx|throwError"∂: unexpected syntax"elabTerm(←assemble(←`(ℝ))nonebody)exp?@[term_elab«∂Bracketed»]defelabPartialBracketed:TermElab:=funstxexp?=>dolet`(∂[$head:partialHead]$body:partialBody):=stx|throwError"∂: unexpected syntax"let(𝕜,idx)←parseHeadheadelabTerm(←assemble𝕜idxbody)exp?@[term_elab«∂PlainFun»]defelabPartialPlainFun:TermElab:=funstxexp?=>dolet`(∂$f:term):=stx|throwError"∂: unexpected syntax"elabTerm(←assembleFun(←`(ℝ))nonef)exp?@[term_elab«∂BracketedFun»]defelabPartialBracketedFun:TermElab:=funstxexp?=>dolet`(∂[$head:partialHead]$f:term):=stx|throwError"∂: unexpected syntax"let(𝕜,idx)←parseHeadheadelabTerm(←assembleFun𝕜idxf)exp?-- ────────────────────────────────────────────────────────────-- Tests-- ────────────────────────────────────────────────────────────variable{mnk:ℕ}(f:EuclideanSpaceℝ(Finn)→EuclideanSpaceℝ(Finm)→EuclideanSpaceℝ(Fink))(g:EuclideanSpaceℂ(Finn)→EuclideanSpaceℂ(Finm)→EuclideanSpaceℂ(Fink))(x:EuclideanSpaceℝ(Finn))(y:EuclideanSpaceℝ(Finm))(x':EuclideanSpaceℂ(Finn))(y':EuclideanSpaceℂ(Finm))(dx:EuclideanSpaceℝ(Finn))(i:Finn)(j:Finm)-- Index in brackets → EuclideanSpace.single i 1 direction, field ℝ#check∂[i](f·y)x-- (∂[i] (f · y)) x = fderiv ℝ (f · y) x (single i 1)#check∂[i]x'',(fx''y)-- fun x'' => fderiv ℝ ... x'' (single i 1)#check∂[i](x'':=x),(fx''y)-- fderiv ℝ ... x (single i 1)-- Numeric literal as index (isFin short-circuits on nat literals)variable[NeZeron]#check∂[0](f·y)x-- fderiv ℝ (f · y) x (single (0 : Fin n) 1)-- Field + index#check∂[ℂ,i](g·y')x'-- fderiv ℂ (g · y') x' (single i 1)-- Field only → curried CLM-map; normal application gives the CLM at a point#check∂[ℝ](f·y)-- fderiv ℝ (f · y) : _ → _ →L[ℝ] _#check∂[ℝ](f·y)x-- (∂[ℝ] (f · y)) x : _ →L[ℝ] _#check∂[ℝ]x'',(fx''y)-- fun x'' => fderiv ℝ (fun x'' => f x'' y) x''#check∂[ℝ](x'':=x),(fx''y)-- fderiv ℝ (fun x'' => f x'' y) x#check∂[ℝ](x'':=x;dx),(fx''y)-- fderiv ℝ (fun x'' => f x'' y) x dx-- No brackets → defaults to ℝ#check∂(f·y)-- fderiv ℝ (f · y)#check∂(f·y)x-- (∂ (f · y)) x-- second derivative#check∂∂(f·y)#check∂(x':=x),∂(x'':=x'),(fx''y)