Zulip Chat Archive

Stream: new members

Topic: simplify a match pattern nested within another function


ZhiKai Pong (Apr 18 2025 at 20:04):

import Mathlib

local notation "ℝ³" => EuclideanSpace  (Fin 3)
open InnerProductSpace
noncomputable def curl (f : ³  ³) :
    ³  ³ := fun x =>
  -- get i-th component of `f`
   let fi i x := f x, EuclideanSpace.single i (1:)⟫_
  -- derivative of i-th component in j-th coordinate
  -- ∂fᵢ/∂xⱼ
  let df i j x := fderiv  (fi i) x (EuclideanSpace.single j (1:))
  fun i =>
    match i with
    | 0 => df 1 2 x - df 2 1 x
    | 1 => df 2 0 x - df 0 2 x
    | 2 => df 0 1 x - df 1 0 x

variable {ft :   ³  ³}

example :  curl (fderiv  (fun t => (ft t)) t 1) = fderiv  (fun t => curl (ft t)) t 1 := by
  unfold curl
  ext x i
  simp
  fin_cases i
  simp -- how to simplify the match pattern nested within fderiv and fun?
  sorry

I have a match pattern nested within fderiv and fun=>. I want to extract the 0th component which is at the end of the expression, but I don't know how to substitute it into the expression. is there a way to do that or any suggestion on how to avoid this?

ZhiKai Pong (Apr 18 2025 at 20:05):

(I know I'll need differentiability conditions etc. to finish the proof)

ZhiKai Pong (Apr 19 2025 at 20:32):

sorry for bumping this, still stuck :(

if this is not a simple thing to resolve, I'd like to know what's needed (specific auxiliary lemmas etc.) or are there any clever ways around this

ZhiKai Pong (Apr 20 2025 at 14:43):

Essentially I have something of the form:

(fderiv  (fun t x i => match i with ... ) t) 1 x 0

I think docs#fderiv_pi is closest to what I have but won't work in its current form, so I had a try at modifying it:

import Mathlib

variable
  {𝕜} [NontriviallyNormedField 𝕜]
  {W} [NormedAddCommGroup W] [NormedSpace 𝕜 W]
  {X} [NormedAddCommGroup X] [NormedSpace 𝕜 X]
  {Y} [NormedAddCommGroup Y] [NormedSpace 𝕜 Y]
  {ι : Type*} [Fintype ι] {Y' : ι  Type*} [ i, NormedAddCommGroup (Y' i)]
  [ i, NormedSpace 𝕜 (Y' i)] {Φ : X   i, Y' i} {φ :  i, X  Y' i} {w : W} {x : X}

lemma fderiv_coord_apply (h : DifferentiableAt 𝕜 Φ x) :
    (fderiv 𝕜 (fun w x i => Φ x i) w) w x i = (fderiv 𝕜 (fun w x => Φ x i) w) w x := by
  sorry

lemma fderiv_pi_nested (h :  i, DifferentiableAt 𝕜 (φ i) x) :
    (fderiv 𝕜 (fun w x i => φ i x) w) w x i = pi fun i => fderiv 𝕜 => (φ i) x :=
  sorry -- don't know how to state RHS

I'm not sure which form is the correct one for moving the x and the 0 inside the fderiv, or whether I'm on the wrong track. Any pointers appreciated

Kyle Miller (Apr 20 2025 at 17:04):

I tried looking at this a little last night, but I'm not too familiar with working with fderiv.

I got the sense that what you want to do is work with HasFDerivAt to do computations (see Mathematics in Lean, chapter 11). The general idea is that fderiv computes the correct derivative if the provided function is differentiable. Using HasFDerivAt keeps track of differentiability.

Joseph Tooby-Smith (Apr 21 2025 at 07:00):

I think for fderiv_pi_nested you want something like this no:

lemma fderiv_pi_nested (h :  i, DifferentiableAt 𝕜 (φ i) x) :
    (fderiv 𝕜 (fun w x i => φ i x) w) w x i =
    (fderiv 𝕜 (fun w i => φ i x) w) w i :=
  sorry -- don't know how to state RHS

ZhiKai Pong (Apr 21 2025 at 11:33):

Joseph Tooby-Smith said:

I think for fderiv_pi_nested you want something like this no:

lemma fderiv_pi_nested (h :  i, DifferentiableAt 𝕜 (φ i) x) :
    (fderiv 𝕜 (fun w x i => φ i x) w) w x i =
    (fderiv 𝕜 (fun w i => φ i x) w) w i :=
  sorry -- don't know how to state RHS

I've tried various forms of this but none of them seem to be able to see through the match pattern, or at least I don't know how to apply it. something like simp [fderiv_pi_nested] doesn't work nor does rw/apply

Tomas Skrivan (Apr 21 2025 at 18:00):

Ha of all things you are hitting the most obscure part of fderiv :)

In your original question, after the last simp you have on right hand side

  (fderiv 
        (fun (t : ) (x : ³) (i : Fin 3) => ...))

It does not even matter that ... contains match statement there is a different problem lurking.

You are differentiating function of type ℝ → ℝ³ → ℝ³ i.e. domain is and codomain is ℝ³ → ℝ³. The problem is that ℝ³ → ℝ³ is not a normed space (i.e. you can't really equip it with a sensible norm) and unfortunately most of the theorems about fderiv, like fderiv_pi, are formulated for normed spaces. There is an ongoing push to generalize fderiv to topological vector spaces and that is the reason you are even allowed to write fderiv ℝ (fun (t : ℝ) (x : ℝ³) (i : Fin 3) => ...) and Lean does not complain that it can't synthesize NormedAddCommGroup (ℝ³ → ℝ³).

If you are not familiar with functional analysis, this probably did not make much sense. I would say that the take away is that you should double check that you are differentiating expressions that are values not functions. For examples, in fderiv ℝ (fun t => curl (ft t)) t 1 you are differentiating the expression curl (ft t) w.r.t. t but the type of curl (ft t) is ℝ³ → ℝ³. (Yes ℝ³ is a function as well, but the domain is finite type so it make sense to think about it as a value as well)

Tomas Skrivan (Apr 21 2025 at 18:05):

So you should reformulate the original problem as

example (x : ³) : curl (fderiv  (fun t => (ft t)) t 1) x = fderiv  (fun t => curl (ft t) x) t 1 := ...

and the proof should be effectivelly

by
  unfold curl
  ext i
  simp [-fderiv_eq_smul_deriv]
  rw[fderiv_pi (ι:=Fin 3) (h:=by sorry)]
  fin_cases i  <;> /- simp and swap x and t derivativevs -/

For some reason, I'm having really hard time applying ContinuousLinearMap.pi_apply

Tomas Skrivan (Apr 21 2025 at 18:08):

Ughhh I think this is again the problem with the fact that Fin 3 → ℝ and EuclideanSpace ℝ (Fin 3) have different topology ... hmm in that case maybe using fderiv_pi is not the right move and some PiLp equivalent is necessary. Ahh this is so annoying an beginner unfriendly :(

Tomas Skrivan (Apr 21 2025 at 18:11):

Or can anyone tell me why the last simp call does not simplify (ContinuousLinearMap.pi f) 1 0 to f 0 1?

code

Tomas Skrivan (Apr 21 2025 at 18:31):

Right, so I think the mathlib idiomatic way we should define curl as

noncomputable def curl (f : ³  ³) :
    ³  ³ := fun x =>
  -- get i-th component of `f`
  let fi i x := f x, EuclideanSpace.single i (1:)⟫_
  -- derivative of i-th component in j-th coordinate
  -- ∂fᵢ/∂xⱼ
  let df i j x := fderiv  (fi i) x (EuclideanSpace.single j (1:))
  (WithLp.equiv 2 (Fin 3  )).symm fun i =>
    match i with
    | 0 => df 1 2 x - df 2 1 x
    | 1 => df 2 0 x - df 0 2 x
    | 2 => df 0 1 x - df 1 0 x

Notice the (WithLp.equiv 2 (Fin 3 → ℝ)).symm. Then at some point we will be faced with

(fderiv 
        (fun t =>
          (WithLp.equiv 2 (Fin 3  )).symm fun i => ...

which can still be rewritten fderiv_pi but you are committing defeq abuse so some specialized version of fderiv_pi should exists. This is very similar situation we encountered in this thread and had to use PiLp.proj.

ZhiKai Pong (Apr 21 2025 at 18:36):

Tomas Skrivan said:

Or can anyone tell me why the last simp call does not simplify (ContinuousLinearMap.pi f) 1 0 to f 0 1?

code


Joseph gave me some suggestions earlier, with some adaptations I can get the following:

code

ZhiKai Pong (Apr 21 2025 at 18:45):

I guess that bypasses the match issue somehow (I'm still figuring out the difference). but then I think IsSymmSndFDerivAt.eq requires both t and x to be of the same type? does this mean what you said above is still a problem (t and x are in different domains )?

Tomas Skrivan (Apr 21 2025 at 18:49):

You want to use IsSymmSndFDerivAt.eq with

f := fun (tx :  × ³) => ft tx.1 tx.2
v := (1,0)
w := (0,EuclideanSpace.single i (1:))

Tomas Skrivan (Apr 21 2025 at 18:53):

But then you still need to turn it into the form you need which should be mathematically trivial but not practically. I think fderiv_uncurry will be useful at some point.

ZhiKai Pong (Apr 21 2025 at 18:58):

ok, more notations to learn... I'll try to go through these and see what I can come up with, thanks!

Tomas Skrivan (Apr 21 2025 at 19:32):

Yeah I'm sorry that it is so complicated. I don't have much time these days to browse Zulip so feel free to ping me next time.

Aaron Liu (Apr 21 2025 at 19:34):

btw there is a !₂[x, y, z] notation that you can use

Aaron Liu (Apr 21 2025 at 19:34):

Then you won't have to use a match maybe?

Tomas Skrivan (Apr 21 2025 at 19:51):

Correct, you can define curl as

import Mathlib

local notation "ℝ³" => EuclideanSpace  (Fin 3)
open InnerProductSpace
noncomputable def curl (f : ³  ³) :
    ³  ³ := fun x =>
  let fi i x := f x, EuclideanSpace.single i (1:)⟫_
  let df i j x := fderiv  (fi i) x (EuclideanSpace.single j (1:))
  !₂[df 1 2 x - df 2 1 x,
     df 2 0 x - df 0 2 x,
     df 0 1 x - df 1 0 x]

but I think that to make any progress in proofs you will have to use fin_cases i anyway.

Tomas Skrivan (Apr 21 2025 at 19:55):

The match is not really the problem. The main issues are

  • you can't use fderiv_pi with (ι:=ℝ³)
  • you should not be using fderiv_pi in the first place but some PiLp alternative
  • how to actually apply IsSymmSndFDerivAt.eq in this scenarios i.e. how to get and apply curried version of IsSymmSndFDerivAt.eq

Last updated: May 02 2025 at 03:31 UTC