Zulip Chat Archive

Stream: mathlib4

Topic: Stating Schrodinger's equation


Joseph Tooby-Smith (Feb 18 2025 at 13:08):

In the project PhysLean, I am interested in making steps in the direction of quantum mechanics. One thing that would need to be done in this direction is state the Schrodinger's equation in Euclidean space:

For a twice-differentiable function ψ:R3 C\psi : \mathbb{R}^3 \to  \mathbb{C}, and a  smooth function V: R3 RV :  \mathbb{R}^3 \to  \mathbb{R} (possibly defined everywhere except a finite set of points e.g. 1/x1/||x||) we say that ψ\psi satisfies the Schrodinger equation with potential VV if there exists an ERE \in \mathbb{R} such that 

(22m2+V(x))ψ=Eψ(- \frac{\hbar^2}{2m} \nabla^2 + V (x)) \psi= E \psi
Here \hbar and mm are constants in R\mathbb{R}, and 2\nabla^2 is the Laplacian operator on Euclidean space, such that in the standard coordinates (x1,x2,x3)(x_1, x_2, x_3) of R3\mathbb{R}^3

2=2x12 +2x22 +2x32\nabla^2 = \frac{\partial^2}{\partial x_1^2}  + \frac{\partial^2}{\partial x_2^2}  + \frac{\partial^2}{\partial x_3^2}

As a first goal I am interested in just writing down this problem in a way that can be later used to e.g. write down or find solutions to the Schrodinger equation.

Are there any suggestions of what should be (and shouldn't be) used from Mathlib to do this?

(This question follows on from this discussion #HepLean > Hydrogen atom)

Joseph Tooby-Smith (Feb 18 2025 at 13:12):

(There is a modification of this problem to the explicitly time-dependent version of Schrodinger's equation which shouldn't be too hard once the above is done)

Tomas Skrivan (Feb 18 2025 at 14:09):

Here is my version of time dependent Schrodinger equation

def SchrodingerEquation (m h : ) (V : ³  ) (ψ :   ³  ) :=
   t x, deriv (ψ · x) t = (-h^2/(2*m))  Δ (ψ t) x + V x  ψ t x

It should generalize to arbitrary vector valued wave function defined over inner product space.

code

In particular, I'm missing continuous version of LinearMap.trace. Also having continuous equivalence in ContinuousLinearMap.adjoint would be useful, it can be done without it but the code looks a bit uglier.

Tomas Skrivan (Feb 18 2025 at 14:28):

I gave two possible definitions of laplace for functions between inner product spaces.

  1. laplace is trace of hessian

    • you compute fderiv twice and then you have to compute component wise trace of bilinear map
    • pros: computing derivatives via fderiv is probably the easiest as most of the API is designed for it right now
    • cons: not sure how easy/difficult is to compute the component wise trace
  2. laplace is divergence of gradient

    • you have to compute vector valued gradient vgrad and vector valued divergence vdiv
    • pros: this will generalize to other equations like heat equation with variable coefficients where you multiply the gradient by some position dependent matrix before you pass it to divergence
    • cons: you have to develop whole new API for vgrad and vdiv

Joseph Tooby-Smith (Feb 18 2025 at 15:03):

Thanks for this @Tomas Skrivan ! I'm going to have a go trying to adapt what you have to a specific case, and see where I can take it. I'll report back if I get to somewhere interesting.

Tomas Skrivan (Feb 18 2025 at 15:15):

I'm curious, what is the specific case?

Tomas Skrivan (Feb 18 2025 at 15:23):

The first stress test would be to show that planar wave is a solution for zero potential.

I would just copy API of LinearMap.trace for ContinuousLinearMap.trace and see if we can show the planar wave is a solution.

It would be nice if some mathlib expert could comment why there is no ContinuousLinearMap.trace or explain why it is not necessary. Or is it the usual explanation, no one needed it before?

Joseph Tooby-Smith (Feb 18 2025 at 15:25):

I was thinking of doing Landau levels i.e. a particle in a plane with a uniform magnetic field perpendicular to that plane. Just because it doesn't have the singularity issues in its potential.

The annoying thing is that you need to modify the Laplacian slightly for this case (the magnetic field gives single derivative terms in the Hamiltonian). The solutions are also quite nice - related to Hermite polynomials.

But yes - your suggestion of the case when V=0V = 0 is probably a better first case! :)

Joseph Tooby-Smith (Feb 18 2025 at 15:30):

(Actually even easier then Landau levels - the harmonic oscillator would probably be the next best thing to try - then wouldn't need to change the Laplacian)

Tomas Skrivan (Feb 18 2025 at 16:07):

Here is my attempt at Schrodinger equation in electro-magnetic field

open InnerProductSpace

/-- Turn vector potential `A` into vector potential operator -/
noncomputable
def vecPotOp (A : ³  ³) : (³  )  (³  ³ L[] ) :=
  fun ψ x => ⟨⟨⟨fun v => A x,v⟫_ * ψ x,sorry,sorry, sorry

open Complex in
/-- `vecPotOp' A` is adjoint of `vecPotOp A` -/
noncomputable
def vecPotOp' (A : ³  ³) : (³  ³ L[] )  (³  ) :=
  fun v x => v x (A x)

/-- vector valued divergence - adjoint of `fderiv` -/
noncomputable
def vdiv' (f : E  E L[𝕜] F) (x : E) : F := hessianTrace (fderiv 𝕜 f x)

open Complex in
def SchroedinterEquationInMagneticField (m h q : ) (φ : ³  ) (A : ³  ³) (ψ :   ³  ) :=
  let D  := (-h*I)  fderiv  - q  vecPotOp A
  let D' := (-h*I)  vdiv' - q  vecPotOp' A
   t x,
     deriv (ψ · x) t = (-h^2/(2*m))  D' (D (ψ t ·)) x + φ x  ψ t x

Tomas Skrivan (Feb 18 2025 at 16:08):

everything together for easy Lean 4 playground access

code

Tomas Skrivan (Feb 18 2025 at 16:11):

(deleted)

Tomas Skrivan (Feb 18 2025 at 16:29):

Also I would like to point out that

fderiv : (E  F)  (E  E L[𝕜] F)
vdiv' : (E  E L[𝕜] F)  (E  F)

are transpose of each other in a particular sense.

We can interpret fderiv as function between test functions and vdiv' as distributional derivative

vgrad : 𝒟(E,F)  𝒟(E, E L[𝕜] F)
vdiv' : 𝒟'(E, E L[𝕜] F)   𝒟'(E,F)

(here I'm using vector valued test functions and distributions, reference)
For function between test functions we can talk about transpose and in this sense -vdiv' is transpose of fderiv

Tomas Skrivan (Feb 18 2025 at 16:47):

In this light D' is really transpose of D and it makes sense why people write (ihqA)2( -ih\nabla - q A)^2 which usually means (ihqA)(ihqA)( -ih\nabla - q A)^\dagger ( -ih\nabla - q A) .

Tomas Skrivan (Feb 18 2025 at 16:55):

(I did bunch of edits because in my first attempt I defined D using vgrad but then realized that I can't multiply it by complex unit I so I redid everything using fderiv instead of vgrad.)

Tomas Skrivan (Feb 18 2025 at 19:30):

I'm trying to show that plane wave is a solution

def planeWave (ω : ) (k : ³) (t : ) (x : ³) :  := Complex.exp (- (k,x⟫_ - ω*t) * I)

and I'm trying to show that

theorem planeWave_is_solution (m k : ) (ω : ) (k : ³) :
    SchroedinterEquation m (fun _ => 0) (planeWave ω k) := ...

(one should pick appropriate ω based on m and k, I just havn't done that yet)

After unfolding all the definitions you get this equality

(fderiv  (fun x_1 => cexp (-(↑⟪k, x⟫_ - ω * x_1) * I)) t) 1 =
    (- ^ 2 / (2 * m))  hessianTrace (fderiv  (fderiv  fun x => cexp (-(↑⟪k, x⟫_ - ω * t) * I)) x) +
      (fun x => 0) x  cexp (-(↑⟪k, x⟫_ - ω * t) * I)

I have managed to eliminate derivative on lhs and inner derivative on rhs.
Now I'm hitting a problem that there is no ContinuousLinearMap for fun x => ⟪y, x⟫_ℝ and that it is differentiable as a function in y + what is its derivative.

Tomas Skrivan (Feb 18 2025 at 19:31):

Here is my progress, there are bunch of missing theorems or they are not in the correct form.

code

Joseph Tooby-Smith (Feb 19 2025 at 10:33):

Hi @Tomas Skrivan ! All this looks great - thanks! I'm going to go through it later today and try and fill in some of the sorries.

I've been working on a simpler case corresponding to the 1d Quantum Harmonic Oscillator. This is easier since you can just use deriv instead of fderiv. I've got as far as stating the Schrodinger equation, the eigenfunctions and eigenvalues and proving that the eigenfunctions and eigenvalues are indeed eigenfunctions and eigenvalues. I haven't proved any completeness relations or orthogonality conditions etc.

It exceeds the character length of Zulip (didn't even know this existed until now :)), so I can't paste it here - but it is in this PR.

Joseph Tooby-Smith (Feb 19 2025 at 10:35):

Naturally any suggestions for improvements either here or on the Github would be much appreciated :).

Tomas Skrivan (Feb 19 2025 at 14:04):

Looks good, few comments:

  1. Not sure if defining hermitePolynomial with three cases has any advantage over two cases. If you do it in two cases then you have to use the fact that 0-1=0 which I guess is that reason for three cases.
  2. You compute lots the derivatives by hand unnecessarily, simp can do most of the work for you, see code bellow.
  3. Formulate the derivative of hermitePolynomial in "compositional" form (do the same for Complex.ofReal
lemma deriv_hermitePolynomial' (x : )
    (f :   ) (hf : DifferentiableAt  f x) (n : )  :
    deriv (fun x => hermitePolynomial n (f x)) x
    =
    (2 * n * hermitePolynomial (n - 1) (f x)) * deriv f x := ...

with this computing derivatives can be done with simp, for example

lemma deriv_hermitePolynomial_succ (m  ω : ) (n : ) :
    deriv (fun x => (hermitePolynomial (n + 1) (Real.sqrt (m * ω / ) * x))) =
    fun x =>
    (Real.sqrt (m * ω / )) * 2 * (n + 1) *
    hermitePolynomial n (Real.sqrt (m * ω / ) * x) := by

  funext x
  simp (disch:=fun_prop)
  rw[deriv_const_mul (hd:=by fun_prop)]
  simp;
  ring

the proof is morally just funext x; simp (disch:=fun_prop); ring but simp fails to apply deriv_const_mul because of indexing issues.


Here is my version of deriv_hermitePolynomial and derived compositional forms of that lemma.

code

Joseph Tooby-Smith (Feb 19 2025 at 14:11):

@Tomas Skrivan Great! I also just found that Mathlib has the probabilists version of Hermite polynomials here and I think inspiration can also be taken from there.

It's still probably worth defining the "physicists" version explicitly though.

Tomas Skrivan (Feb 19 2025 at 14:17):

Few tips for computing derivatives with simp. I usually run simp it with

set_option trace.Meta.Tactic.simp.discharge true in

to see which theorem fails because simp can't discharging differentiability. There seems to be some reducibility issues with id and Function.comp while running fun_prop as discharger. For example this works

example : DifferentiableAt  (fun y => 2 * y * hermitePolynomial (n + 1) y) x := by fun_prop

but the same goal fails when simp tries to discharge it with fun_prop.


It is also very useful to run

set_option trace.Meta.Tactic.simp.unify true in

when you know a certain theorem should be applied but it is not. So turn that option when calling for example simp only [deriv_sub]. Without only you will get lots of noise.


One thing to keep in mind is that simp has issues with eta expansions when indexing theorems. It won't apply deriv_const_mul or deriv_mul to deriv (HMul.hMull 2). The issue is not with unification so it won't come up trace.Meta.Tactic.simp.unify. So either use rw[deriv_const_mul] to simplify deriv (HMul.hMull 2) as in the above code or you can state a new theorem deriv (HMul.hMull c) = (HMul.hMull c) and mark it with simp attribute.

Tomas Skrivan (Feb 19 2025 at 14:21):

Joseph Tooby-Smith said:

Tomas Skrivan
It's still probably worth defining the "physicists" version explicitly though.

I think it is a good idea to have probabilistic and physicist version such that we can state formulas as they are in textbooks. Not sure though if we should pick one canonical version for proofs though.

Kevin Buzzard (Feb 19 2025 at 14:38):

Tomas Skrivan said:

It would be nice if some mathlib expert could comment why there is no ContinuousLinearMap.trace or explain why it is not necessary. Or is it the usual explanation, no one needed it before?

For general vector spaces there is no concept of trace, one needs finite-dimensionality (or some restriction on the class of continuous linear maps you're considering). And in the finite-dimensional case continuity is automatic (all linear maps are continuous).

Tomas Skrivan (Feb 19 2025 at 14:44):

Kevin Buzzard said:

Tomas Skrivan said:

It would be nice if some mathlib expert could comment why there is no ContinuousLinearMap.trace or explain why it is not necessary. Or is it the usual explanation, no one needed it before?

For general vector spaces there is no concept of trace, one needs finite-dimensionality (or some restriction on the class of continuous linear maps you're considering). And in the finite-dimensional case continuity is automatic (all linear maps are continuous).

That was my suspicion but then I do no know how the API is supposed to work. How can I easily turn f : E →ₗ[ℝ] E →ₗ[ℝ] → F into E →L[ℝ] E →L[ℝ] → F for E and F finite dimensional? The linear algebra concepts are done with E →ₗ[ℝ] → F but derivatives are with E →L[ℝ] → F and I just do not know how to make them interact nicely.

(In SciLean I just work with unbundled maps everywhere and use fun_prop to prove any property on the fly when necessary but mathlib is not designed this way so I'm a bit lost)

Kevin Buzzard (Feb 19 2025 at 14:53):

I would imagine the analysts have had to deal with this problem at some point, so hopefully they will chime in. On my part I can mention docs#IsModuleTopology.continuous_of_linearMap , which says that any linear map between vector spaces (or more generally modules) with topologies is automatically continuous if source and target have what I call the "module topology", although this a theorem rather than an upgrading of a linear map to a continuous linear map.

Tomas Skrivan (Feb 19 2025 at 15:01):

Ohh some time ago I used docs#LinearMap.continuous_of_finiteDimensional what is the difference? Looks like that the IsModuleTopology version is lot less restrictive on the ring. Anyway, I'm really interested in the practicalities of actually working with the (continuous) linear maps, mathematically it all make sense.

Kevin Buzzard (Feb 19 2025 at 15:12):

The theorem you quote says that if the ground field is complete then continuity is automatic. My theorem is that if the ground ring is an arbitrary topological ring and the vector space is also arbitrary but but the topology on the vector space satisfies some kind of natural condition then continuity is automatic. If your ground field is complete then you should probably be using the one you suggested as then you won't have to check that the the topology on the module is the natural one. The point is that given any module over any topological ring there is some kind of natural topology one can put on it, but this seems like overkill here.

Joseph Tooby-Smith (Feb 19 2025 at 15:41):

Here is my refactoring of the physicists Hermite polynomials based on @Tomas Skrivan's comments above, and on Mathlibs Hermite polynomials:

code

Tomas Skrivan (Feb 19 2025 at 15:51):

I find it strange that it is defined as polynomial over integers. I would expect to define it as Polynomial ℝ and having coercion CoeFun (Polynomial ℝ) (fun _ => (ℝ → ℝ)). I guess there is a reason for doing it this way. I see that it is more flexible but I do not like that we have physHermite and physHermiteFun

Joseph Tooby-Smith (Feb 19 2025 at 15:56):

I did it this way to match Polynomial.hermite. I agree having both physHermite and physHermiteFun does not seem ideal. Though I don't think writing physHermiteFun as fun x => aeval x (physHermite n) everywhere is also great.

Joseph Tooby-Smith (Feb 21 2025 at 08:42):

@Tomas Skrivan Going to push this just to PhysLean today. I'll add your name to the 'authors...' bit of the PhysicistsHermite file, unless you don't want me too.

I haven't managed to show completeness of the eigenfunctions yet - this seems pretty tricky to do in general (so much so that most resources for the Harmonic oscillator skip any such proof). Ideally it would be down to showing that {xpecx2pN}\{x^p e^{- c * x^2} \mid p \in \mathbb{N}\} for fixed real 0<c0<c form a basis of the 1d Hilbert space of square integrable (equivalence classes of) functions. Or rather that if ff is square integrable such that if

f(x)xpecx2=0\int^\infty_{-\infty} f(x) x^p e^{- c * x^2} = 0

for all pp then ff is almost-everywhere zero.

Tomas Skrivan (Feb 21 2025 at 12:07):

I just skimmed through the code but it looks good to me.

Of course, ideally we would have a general theory of orthogonal polynomials in mathlib and then all proofs about eigenfunctions would be one liners. But don't let the perfect be the enemy of good ;) so I think it will be a great reference for someone else who might want to formalize some physics and don't know where to start.

Joseph Tooby-Smith (Feb 21 2025 at 12:23):

Thanks :).

Agreed. I've been using the 'Mathematics' file of PhysLean for things like this - where we know one day it will be in Mathlib but it would be nice to have certain specific results now to do something with .

Joseph Tooby-Smith (Feb 25 2025 at 15:53):

Ok, I managed to prove completeness of the eigenvectors for the harmonic oscillator - well more properly I've reduced it to a single theorem in analysis called Plancherel's theorem which is not yet in Mathlib.

The PR is here, it is not ready to push yet - as it needs a major tidy & refactor.


Last updated: May 02 2025 at 03:31 UTC