Zulip Chat Archive

Stream: PhysLean

Topic: QM reflectionless potential


Afiq Hatta (Jul 31 2025 at 11:47):

Hey folks I'm starting to work on the reflectionless potential just to get myself acquianted with lean

Afiq Hatta (Jul 31 2025 at 11:47):

@Joseph Tooby-Smith

Joseph Tooby-Smith (Jul 31 2025 at 11:52):

Just for some context Afiq and I have being discussing this 'offline', so this is a place to discuss things related to the QM reflectionless potential in the open.

Afiq Hatta (Aug 01 2025 at 09:44):

hey @Joseph Tooby-Smith was just thinking of an easy next step - i'm guessing defining the creation and annihilation operators in the paper?

Joseph Tooby-Smith (Aug 01 2025 at 10:00):

Yes, this seems like a good next step to me, that or writing down the full Hamiltonian as a operator on ordinary functions

Afiq Hatta (Aug 04 2025 at 07:58):

@Joseph Tooby-Smith we can discuss this on the call later but I need to use the position and momentum operators, for the creation / annihilation stuff - I guess those are good to import from the Operators module?

Joseph Tooby-Smith (Aug 04 2025 at 08:03):

Yep, it is best to import them from the operators module, if you need any properties of them which aren't yet proven, these should go into the operators module.

Afiq Hatta (Aug 04 2025 at 08:03):

awesomesauce

Afiq Hatta (Aug 04 2025 at 08:42):

pull request made

Afiq Hatta (Aug 04 2025 at 08:49):

i guess the next dumb thing to do would be to prove creation operator is conjugate to the annihilation one!

Joseph Tooby-Smith (Aug 04 2025 at 09:00):

This might be tricky, but it is certainly doable. It is tricky because by conjugate here you really mean hermitian conjugate (if you don't then it's not true anyway).
The way your operators are currently defined, they are defined on the normal functions from ℝ β†’ β„‚, but to define their hermitian conjugate you need them defined on the Hilbert space (at least part of the Hilbert space).
An example of this type of thing is done in the momentumOperator file, see e.g. the lemma

momentumOperatorUnbounded_isSelfAdjoint

In particular, in addition to creationOperator you should also define creationOperatorSchwartz and creationOperatorUnbounded copying what is in the momentum operator module.

Afiq Hatta (Aug 04 2025 at 09:30):

agreed to all - will get cracking

Afiq Hatta (Aug 04 2025 at 10:34):

To do:

  • do the Schwartz Space operator versions

Afiq Hatta (Aug 04 2025 at 20:16):

so @Joseph Tooby-Smith to even create these Schwartz versions of creation and annihilation operators, we need to prove that they're linear right?

Afiq Hatta (Aug 04 2025 at 20:17):

ie what you did here:

/-- The parity operator on the Schwartz maps is defined as the linear map from
  `𝓒(ℝ, β„‚)` to itself, such that `ψ` is taken to `fun x => - I ℏ * ψ' x`. -/
def momentumOperatorSchwartz : 𝓒(ℝ, β„‚) β†’L[β„‚] 𝓒(ℝ, β„‚) where
  toFun ψ := (- Complex.I * ℏ) β€’ SchwartzMap.derivCLM β„‚ ψ
  map_add' ψ1 ψ2 := by
    simp only [neg_mul, map_add, smul_add, neg_smul]
  map_smul' a ψ := by
    simp only [neg_mul, map_smul, neg_smul, RingHom.id_apply]
    rw [smul_comm]
    simp
  cont := by fun_prop

Afiq Hatta (Aug 04 2025 at 20:20):

in the case of the creation and annihilation operators, would the cleanest way be to prove that the tanh operator is Schwartz, and then since their linear combinations of the tanh operator and the momentum operator => creation and annihilation operators are aalso schwartz

Joseph Tooby-Smith (Aug 04 2025 at 20:22):

Afiq Hatta said:

so Joseph Tooby-Smith to even create these Schwartz versions of creation and annihilation operators, we need to prove that they're linear right?

Yes, exactly, the β†’L[β„‚] is the notation for a continuous linear map.

Joseph Tooby-Smith (Aug 04 2025 at 20:22):

Afiq Hatta said:

in the case of the creation and annihilation operators, would the cleanest way be to prove that the tanh operator is Schwartz, and then since their linear combinations of the tanh operator and the momentum operator => creation and annihilation operators are aalso schwartz

Yep that sounds like the best way to me.

Afiq Hatta (Aug 04 2025 at 20:45):

really dumb question

I know what is required for a continuous linear map, but how in general can i find the 'requirements' needed to define a particular structure?

is it the 'fields' section of what i get when i type ```
#print ContinuousLinearMap

structure ContinuousLinearMap.{u_1, u_2, u_3, u_4} {R : Type u_1} {S : Type u_2} [Semiring R] [Semiring S] (Οƒ : R β†’+* S)
  (M : Type u_3) [TopologicalSpace M] [AddCommMonoid M] (Mβ‚‚ : Type u_4) [TopologicalSpace Mβ‚‚] [AddCommMonoid Mβ‚‚]
  [Module R M] [Module S Mβ‚‚] : Type (max u_3 u_4)
number of parameters: 13
parents:
  ContinuousLinearMap.toLinearMap : M β†’β‚›β‚—[Οƒ] Mβ‚‚
fields:
  AddHom.toFun : M β†’ Mβ‚‚
  AddHom.map_add' : βˆ€ (x y : M), (↑self).toFun (x + y) = (↑self).toFun x + (↑self).toFun y
  MulActionHom.map_smul' : βˆ€ (m : R) (x : M), (↑self).toFun (m β€’ x) = Οƒ m β€’ (↑self).toFun x
  ContinuousLinearMap.cont : Continuous (↑self).toFun := by
    continuity
constructor:
  ContinuousLinearMap.mk.{u_1, u_2, u_3, u_4} {R : Type u_1} {S : Type u_2} [Semiring R] [Semiring S] {Οƒ : R β†’+* S}
    {M : Type u_3} [TopologicalSpace M] [AddCommMonoid M] {Mβ‚‚ : Type u_4} [TopologicalSpace Mβ‚‚] [AddCommMonoid Mβ‚‚]
    [Module R M] [Module S Mβ‚‚] (toLinearMap : M β†’β‚›β‚—[Οƒ] Mβ‚‚) (cont : Continuous toLinearMap.toFun := by continuity) :
    M β†’SL[Οƒ] Mβ‚‚
field notation resolution order:
  ContinuousLinearMap, LinearMap, AddHom, MulActionHom

Joseph Tooby-Smith (Aug 05 2025 at 05:39):

The way I do it is just start typing the definition, e.g.

def momentumOperatorSchwartz : 𝓒(ℝ, β„‚) β†’L[β„‚] 𝓒(ℝ, β„‚) where

This will then give an error on the where because things are incomplete, and the error will list the fields needed.

Afiq Hatta (Aug 05 2025 at 08:59):

got it - thanks Joe for the flag

Afiq Hatta (Aug 10 2025 at 11:15):

really dumb question @Joseph Tooby-Smith but i can't get this to work - just even the definition part

noncomputable def tanhOperatorSchwartz : 𝓒(ℝ, β„‚) β†’L[β„‚] 𝓒(ℝ, β„‚) where
  toFun ψ := (fun x ↦ (Real.tanh (Q.ΞΊ * x) : β„‚) * ψ x)

I am getting this error
type mismatch
fun x => ↑(tanh (Q.ΞΊ * x)) * ψ x
has type
ℝ β†’ β„‚ : Type
but is expected to have type
𝓒(ℝ, β„‚) : Type

Afiq Hatta (Aug 10 2025 at 11:16):

I don't see anything wrong with the definition itself - where am I falling over?

Afiq Hatta (Aug 10 2025 at 11:18):

am i not 'casting' the function correctly here?

Kenny Lau (Aug 10 2025 at 11:36):

@Afiq Hatta you're misinterpreting what toFun means

Kenny Lau (Aug 10 2025 at 11:36):

toFun requires you to make a function 𝓒(ℝ, β„‚) -> 𝓒(ℝ, β„‚) first

Kenny Lau (Aug 10 2025 at 11:36):

and prove that this function is linear and continuous

Kenny Lau (Aug 10 2025 at 11:38):

it does not mean, first make a function 𝓒(ℝ, β„‚) -> (ℝ -> β„‚), and prove that every result is Schwarz

Kenny Lau (Aug 10 2025 at 11:38):

in other words, you need two layers of toFun here

Afiq Hatta (Aug 10 2025 at 11:42):

ahhh i hear you - i need to first prove that pointwise multiplication by tanh ( x ) is itself - 𝓒(ℝ, β„‚) -> 𝓒(ℝ, β„‚) (by boundedness, continuity) ? @Kenny Lau

Afiq Hatta (Aug 10 2025 at 11:43):

apologies - beginner here!! :p

Afiq Hatta (Aug 10 2025 at 12:03):

if thats the case - then there should be an inductive proof that all derivatives of (tanh (x) psi (x) ) don't blow up - i don't think theres a general result that we can apply here from other places in the library?

Kenny Lau (Aug 10 2025 at 12:11):

what's the maths proof?

Afiq Hatta (Aug 10 2025 at 12:11):

writing it down now...

Kenny Lau (Aug 10 2025 at 12:12):

I feel like it should follow from tanh(x) itself being Schwartz?

Afiq Hatta (Aug 10 2025 at 12:16):

its not though right, since -> +- 1 at +- infty?

Kenny Lau (Aug 10 2025 at 12:16):

oh...

Kenny Lau (Aug 10 2025 at 12:17):

well, it should then follow from tanh(x) being bounded and its derivatives vanishing

Afiq Hatta (Aug 10 2025 at 12:17):

yeah - actually thats the best way - but trying to see if there's already a ready made proof here on that result

Afiq Hatta (Aug 10 2025 at 14:19):

Just breaking this down - need to prove the lemma that tanh (x) has bounded derivatives

Afiq Hatta (Aug 10 2025 at 14:23):

can you guys tell im an absolute noob.. hahaha

Joseph Tooby-Smith (Aug 10 2025 at 17:09):

Sorry, jumping into this conversation half way through: Something like

might be useful here, all though I think it would need generalizing.

Kenny Lau (Aug 10 2025 at 17:10):

wow you've added a lot of lemmas, nice

Joseph Tooby-Smith (Aug 10 2025 at 18:10):

Yeah, they are all probably not as general as they could be - but I did a start :).

Afiq Hatta (Aug 10 2025 at 21:46):

keen to talk about this tomorrow!

Afiq Hatta (Aug 13 2025 at 13:48):

an excuritatingly dumb question which I've spent too much time on

I want to prove | tanh x | < 1
ok, so start with
lemma : tanh x < 1
there's an equality rewrite so that the goal transforms to

sinh x / cosh x < 1

but i can't for the life of me find the right package / technique that transforms this to
sinh x < cosh x

there's a preset theorem already in mathlib for sinh x < cosh x, so I just need to do the above
@Joseph Tooby-Smith

Kenny Lau (Aug 13 2025 at 13:48):

https://loogle.lean-lang.org/?q=_+%2F+_+<+1

Afiq Hatta (Aug 13 2025 at 13:51):

thanks kenny, sorry for being an idiot, still getting used to searching for things

Afiq Hatta (Aug 13 2025 at 13:52):

ah wait - those are for the natural numbers

Kenny Lau (Aug 13 2025 at 13:52):

well, not the first one

Kenny Lau (Aug 13 2025 at 13:53):

the 6th one should be what you need

Afiq Hatta (Aug 13 2025 at 13:58):

this is where im falling over the syntax a bit

Afiq Hatta (Aug 13 2025 at 13:58):

lemma tanh_le_one (x : ℝ) : Real.tanh x < 1 := by
  rw [Real.tanh_eq_sinh_div_cosh]
  apply div_lt_one

Afiq Hatta (Aug 13 2025 at 13:59):

Expected type
xΒ : ℝ
⊒ βˆ€ {Ξ± : Type ?u.1166} [inst : Semifield Ξ±] [inst_1 : LinearOrder Ξ±] [IsStrictOrderedRing Ξ±] {a b : Ξ±}, 0 < b β†’ (a / b < 1 ↔ a < b)

Messages (1)

Basic.lean:68:2

tactic 'apply' failed, could not unify the conclusion of @div_lt_one
?a / ?b < 1 ↔ ?a < ?b
with the goal
sinh x / cosh x < 1
Note: The full type of @div_lt_one is
βˆ€ {Ξ± : Type ?u.1166} [inst : Semifield Ξ±] [inst_1 : LinearOrder Ξ±] [IsStrictOrderedRing Ξ±] {a b : Ξ±},
0 < b β†’ (a / b < 1 ↔ a < b)
xΒ : ℝ
⊒ sinh x / cosh x < 1

All Messages (1)

Kenny Lau (Aug 13 2025 at 13:59):

it's not an apply, it's a rw

Afiq Hatta (Aug 13 2025 at 14:02):

got it - this creates a subgoal where now i need to prove 0 < cosh x as expected, and then sinh x < cosh x

Afiq Hatta (Aug 13 2025 at 14:02):

thanks kenny

Afiq Hatta (Aug 13 2025 at 14:03):

woohoo!!

lemma tanh_le_one (x : ℝ) : Real.tanh x < 1 := by
  rw [Real.tanh_eq_sinh_div_cosh]
  rw [div_lt_one]
  apply Real.sinh_lt_cosh
  apply Real.cosh_pos

Kenny Lau (Aug 13 2025 at 14:03):

@Afiq Hatta rw rewrites an iff, apply is for applying P -> Q to a goal Q to get a goal P

Afiq Hatta (Aug 13 2025 at 14:03):

got it, and rw also works for equalities too right

Kenny Lau (Aug 13 2025 at 14:04):

yes

Afiq Hatta (Aug 13 2025 at 14:18):

one thing im finding hard is that i often want to apply results on the reals,
but most of the theorems that i get from loogle / searching mathlib or defined on like rings / fields / monoids (?!!)

Afiq Hatta (Aug 13 2025 at 14:18):

so i have to do the mental gymnastics to remember what's what

Kenny Lau (Aug 13 2025 at 14:20):

you can try to separate it out into a clean environment (i.e. example) and then use apply?:

import Mathlib

example (x y : ℝ) : x / y < 1 ↔ x < y := by
  apply?

Afiq Hatta (Aug 15 2025 at 15:47):

@Joseph Tooby-Smith in the process of linting:
https://github.com/HEPLean/PhysLean/pull/694/files

Joseph Tooby-Smith (Aug 15 2025 at 16:38):

Going back to the problem of showing that

fun x => tanh (x) * ψ(x)

is Schwartz if ψ is, I think the general version of this result is docs#SchwartzMap.bilinLeftCLM with the bilinear map B : ℝ β†’L[ℝ] ℝ β†’L[ℝ] ℝ taking x, y to x * y, and with g(x) = tanh(x). Thus to prove this result 'all' one would need to do is prove that Function.HasTemperateGrowth Real.tanh, which is essentially what you are trying to show anyway @Afiq Hatta .

Afiq Hatta (Aug 15 2025 at 16:43):

ooooh

Afiq Hatta (Aug 15 2025 at 16:44):

yeah good point - lets use that - thats perfectΒ±

Afiq Hatta (Aug 15 2025 at 16:45):

btw @Joseph Tooby-Smith linters passed on last commit

Joseph Tooby-Smith (Aug 15 2025 at 16:46):

Yeah,

Afiq Hatta said:

yeah good point - lets use that - thats perfectΒ±

Yeah - still need to show that tanh has bounded iterated derivatives for this, but it takes care of the 'SchwartzMap' side of the problem for you, and reduces it to a properties of 'tanh' problem.

Afiq Hatta (Aug 15 2025 at 16:46):

yeah exactly, we don't have to do the leg work of the closure stuff since we cn just use this. but still, need to prove that we have bounded iterated derivs

Afiq Hatta (Aug 22 2025 at 14:52):

@Joseph Tooby-Smith the mission here is to prove temperate growth, as a warm up going to commit these that i just did

/-
Copyright (c) 2025 Afiq Hatta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Afiq Hatta
-/
import Mathlib.Analysis.Calculus.IteratedDeriv.Defs
import Mathlib.Data.Complex.Trigonometric
import PhysLean.Meta.Linters.Sorry

/-!
# Properties of Tanh
We want to prove that the reflectionless potential is a Schwartz map.
This means proving that pointwise multiplication of a Schwartz map with tanh is a Schwartz map.
This means we need to prove that all derivatives of tanh are bounded and continuous, so that
the nth derivative of a function multiplied by tanh decays faster than any polynomial.

## TODO
- Add these to mathlib eventually
- Fill in the proofs for the properties of tanh
-/

open Real
open NNReal
open Field


/-- tanh(x) is less than 1 for all x -/
lemma tanh_lt_one (x : ℝ) : Real.tanh x < 1 := by
  rw [Real.tanh_eq_sinh_div_cosh]
  rw [div_lt_one]
  apply Real.sinh_lt_cosh
  apply Real.cosh_pos

/-- tanh(x) is greater than -1 for all x -/
lemma minus_one_lt_tanh (x : ℝ) : -1 < Real.tanh x := by
  rw [Real.tanh_eq_sinh_div_cosh]
  rw [lt_div_iffβ‚€]
  rw [← sub_pos]
  rw [neg_one_mul]
  simp
  apply Real.exp_pos
  apply Real.cosh_pos

/-- The absolute value of tanh is bounded by 1 -/
lemma abs_tanh_lt_one (x: ℝ) : |Real.tanh x| < 1 := by
  rw [abs_lt]
  constructor
  apply minus_one_lt_tanh
  apply tanh_lt_one

Joseph Tooby-Smith (Aug 22 2025 at 14:57):

Sounds good! I would maybe update the module doc-string with a comment about docs#SchwartzMap.bilinLeftCLM as well. Also, this is not necessary but it maybe good to know, you can combine rewrites e.g.

lemma minus_one_lt_tanh (x : ℝ) : -1 < Real.tanh x := by
  rw [Real.tanh_eq_sinh_div_cosh, lt_div_iffβ‚€, ← sub_pos, neg_one_mul]
  simp
  apply Real.exp_pos
  apply Real.cosh_pos

ZhiKai Pong (Aug 22 2025 at 15:48):

Not the most experienced user here but some minor style comments:

  1. if you opened namespace Real, you don't have to type Real.xxx
  2. The cosh_pos goal arises as a result of using lemmas related to division by cosh, since it's an additional condition associated with the division lemma rather than part of the main proof personally I'd just inline it
  3. I prefer using exact to close goals which indicates more clearly where the proof ends
  4. if the proof itself does require multiple goals use centerdot (\.) to structure the proof for better clarity (e.g. after constructor).

for example, this is how I'd write your code:

import Mathlib

open Real

lemma tanh_lt_one (x : ℝ) : tanh x < 1 := by
  rw [tanh_eq_sinh_div_cosh, div_lt_one (cosh_pos x)]
  exact sinh_lt_cosh x

lemma minus_one_lt_tanh (x : ℝ) : -1 < tanh x := by
  rw [tanh_eq_sinh_div_cosh, lt_div_iffβ‚€ (cosh_pos x), ← sub_pos, neg_one_mul]
  simp [exp_pos x]

lemma abs_tanh_lt_one (x : ℝ) : |tanh x| < 1 := by
  rw [abs_lt]
  constructor
  Β· exact minus_one_lt_tanh x
  Β· exact tanh_lt_one x

Afiq Hatta (Aug 22 2025 at 15:51):

this is great @Joseph Tooby-Smith @ZhiKai Pong thank you for the tips

ZhiKai Pong (Aug 22 2025 at 15:52):

(I guess you can also do this once you're more comfortable)

lemma abs_tanh_lt_one (x : ℝ) : |tanh x| < 1 := by
  rw [abs_lt]
  exact ⟨minus_one_lt_tanh x, tanh_lt_one x⟩

Joseph Tooby-Smith (Aug 22 2025 at 16:20):

Aside concerning the center dots: there is this open issue which I attempted to do complete here, but one thing or another got in the way

Afiq Hatta (Aug 22 2025 at 17:19):

guys i have a mega dumb question

/-- Tanh(x) is n times continuously differentiable for all n -/
lemma contDiff_tanh {n : β„•} : ContDiff ℝ n tanh := by
  have hdiv : ContDiff ℝ n (fun x => Real.sinh x / Real.cosh x) := by
    apply ContDiff.div
    Β· exact contDiff_sinh
    Β· exact contDiff_cosh
    Β· intro x
      exact ne_of_gt (Real.cosh_pos x)

  simpa [tanh_eq_sinh_div_cosh] using hdiv

the last line is throwing error -

type mismatch, term
hdiv
after simplification has type
ContDiff ℝ ↑n fun x => sinh x / cosh x : Prop
but is expected to have type
ContDiff ℝ (↑n) tanh : Prop

what is the syntax to use the tanh = sinh / coosh to finally prove this thing?

Afiq Hatta (Aug 22 2025 at 17:20):

rewriting the goal with rw [ tanh_eq_sinh_div_cosh] doesn't work

ZhiKai Pong (Aug 22 2025 at 17:35):

here's a possible solution (there might be an easier way) (also I'm surprised this isn't in Mathlib)

import Mathlib

open Real

/-- Tanh(x) is n times continuously differentiable for all n -/
lemma contDiff_tanh {n : β„•} : ContDiff ℝ n tanh := by
  have hdiv : ContDiff ℝ n (fun x => sinh x / cosh x) := by
    apply ContDiff.div
    Β· exact contDiff_sinh
    Β· exact contDiff_cosh
    Β· intro x
      exact ne_of_gt (cosh_pos x)
  conv =>
    enter [3, x]
    rw [tanh_eq_sinh_div_cosh]
  exact hdiv

ZhiKai Pong (Aug 22 2025 at 17:37):

there are two layers of issue here:

  1. rw can't see through what's inside ContDiff
  2. tanh_eq_sinh_div_cosh has to be of the form tanh x = sinh x / cosh x, so you have to give it an x. Not sure if you're familiar with ext, the enter is just doing that in a similar fashion

Afiq Hatta (Aug 22 2025 at 17:40):

ah - yeah i wanted to figure out how to do the replacement of tanh within ContDiff

Afiq Hatta (Aug 22 2025 at 17:46):

thank you for this

ZhiKai Pong (Aug 22 2025 at 17:48):

No worries!

Afiq Hatta (Aug 22 2025 at 17:49):

btw i couldn't find this in mathlib - do you agree it's not there? still trying to build my searching skills

ZhiKai Pong (Aug 22 2025 at 17:54):

(sorry first time trying to do this in zulip)

ZhiKai Pong (Aug 22 2025 at 17:55):

@loogle tanh

loogle (Aug 22 2025 at 17:55):

:exclamation: unknown identifier 'tanh'
Did you mean "tanh", Float.tanh, or something else?

ZhiKai Pong (Aug 22 2025 at 17:56):

@loogle Real.tanh

loogle (Aug 22 2025 at 17:56):

:search: Real.tanh, Complex.ofReal_tanh, and 7 more

ZhiKai Pong (Aug 22 2025 at 17:57):

@loogle Real.sinh

loogle (Aug 22 2025 at 17:57):

:search: Real.sinh, Real.sinh_lt_cosh, and 117 more

ZhiKai Pong (Aug 22 2025 at 17:57):

It's quite clear that a lot is missing for tanh :)

ZhiKai Pong (Aug 22 2025 at 17:57):

(this is https://loogle.lean-lang.org/)

Afiq Hatta (Aug 22 2025 at 17:57):

yeah - reflectionless potential sidequests to the rescue!! XD

Afiq Hatta (Aug 22 2025 at 19:51):

@Joseph Tooby-Smith i submitted a pull request but no rush - its fridayyy night!!!

Joseph Tooby-Smith (Aug 22 2025 at 19:54):

Ha - no worries, was literally doing it as I got this message :)

Afiq Hatta (Aug 23 2025 at 14:33):

@Joseph Tooby-Smith to prove the derivative of tanh x - will using the identity below make this a bit easier, then using the chain rule on this - or will there be shenanigans with Real / Complex

theorem tanh_mul_I : tanh (x * I) = tan x * I := by
rw [tanh_eq_sinh_div_cosh, cosh_mul_I, sinh_mul_I, mul_div_right_comm, tan]

Joseph Tooby-Smith (Aug 23 2025 at 16:00):

This might work, it really depends on how good the API is around real to complex numbers - if I recall it is quite good.

Afiq Hatta (Aug 26 2025 at 11:48):

@Joseph Tooby-Smith really DUMB q i cant seem to rewrite the goal here so that i can use my h result

/-- The derivative of tanh(x) is 1 - tanh(x)^2 -/
lemma deriv_tanh (x: ℝ) : deriv Real.tanh = fun x => 1 - Real.tanh x ^ 2 := by
  have h: deriv (sinh / cosh) x = 1 - Real.tanh x ^ 2 := by
    rw [deriv_div, Real.deriv_sinh, Real.deriv_cosh]
    field_simp
    rw [sq, sq, tanh_eq_sinh_div_cosh, mul_sub_right_distrib]
    field_simp
    Β· apply Real.differentiable_sinh
    Β· apply Real.differentiable_cosh
    Β· exact ne_of_gt (Real.cosh_pos x)

tanh_eq_sinh_div_cosh is the result tanh x = sinh x / cosh x

using funext x i think rewrites that goal as (deriv Real.tanh) x , so the pesky symbols aren't in the right place

Afiq Hatta (Aug 26 2025 at 11:49):

i have this

unsolved goals
xΒ : ℝ
hΒ :Β deriv (sinh / cosh) x = 1 - tanh x ^ 2
⊒ deriv tanh = fun x => 1 - tanh x ^ 2

Afiq Hatta (Aug 26 2025 at 11:50):

i could make a functional version of

tanh_eq_sinh_div_cosh such that tanh = sinh / cosh, and then change the form of h to make it "functional" but i m not sure how to do that

Afiq Hatta (Aug 26 2025 at 11:52):

(deleted)

Joseph Tooby-Smith (Aug 26 2025 at 11:55):

try funext x :)

Afiq Hatta (Aug 26 2025 at 11:56):

OH WAIT I GOT IT i think

Afiq Hatta (Aug 26 2025 at 11:56):

i think i answered my own question by asking it hah

Afiq Hatta (Aug 26 2025 at 11:59):

boom

/-- The derivative of tanh(x) is 1 - tanh(x)^2 -/
lemma deriv_tanh : deriv Real.tanh = fun x => 1 - Real.tanh x ^ 2 := by
  have h: deriv (sinh / cosh) = fun x => 1 - Real.tanh x ^ 2 := by
    funext x
    rw [deriv_div, Real.deriv_sinh, Real.deriv_cosh]
    field_simp
    rw [sq, sq, tanh_eq_sinh_div_cosh, mul_sub_right_distrib]
    field_simp
    Β· apply Real.differentiable_sinh
    Β· apply Real.differentiable_cosh
    Β· exact ne_of_gt (Real.cosh_pos x)
  have h': Real.tanh = (sinh / cosh) := by
    funext x
    rw [Pi.div_apply, tanh_eq_sinh_div_cosh]
  nth_rewrite 1 [h']
  apply h

Afiq Hatta (Aug 26 2025 at 11:59):

one big leap for tanh

Afiq Hatta (Aug 26 2025 at 11:59):

okay will commit this

Joseph Tooby-Smith (Aug 26 2025 at 11:59):

Nice, congrats! :grinning:

Joseph Tooby-Smith (Aug 26 2025 at 12:06):

So I don't have an explanation for this, other then 'field_simp' is an awesome tactic, but your proof of h can be golfed to:

import Mathlib
open Real
/-- The derivative of tanh(x) is 1 - tanh(x)^2 -/
lemma deriv_tanh : deriv Real.tanh = fun x => 1 - Real.tanh x ^ 2 := by
  have h: deriv (sinh / cosh) = fun x => 1 - Real.tanh x ^ 2 := by
    funext x
    field_simp [deriv_div, Real.deriv_sinh, Real.deriv_cosh, sq,
       tanh_eq_sinh_div_cosh, mul_sub_right_distrib]
  have h': Real.tanh = (sinh / cosh) := by
    funext x
    rw [Pi.div_apply, tanh_eq_sinh_div_cosh]
  nth_rewrite 1 [h']
  apply h

(I think this is something one would only find given an already complete proof)

Afiq Hatta (Aug 26 2025 at 13:27):

awesome.

i also have another dumb question: if, in an inductive proof, we use the inductive hypothesis, how do we 'obtain' that object from it and use it.

example:

/-- The nth derivative of Tanh(x) is a polynomial of Tanh(x) -/
lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) (x : ℝ) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n
  rw [iteratedDeriv_zero]
  use Polynomial.X
  simp

  rw [iteratedDeriv_succ]

im trying to 'use' the polynomial in the induction hypothesis
case succ
xΒ : ℝ
n✝ :Β β„•
a✝ :Β βˆƒ P, βˆ€ (x : ℝ), iteratedDeriv n✝ tanh x = Polynomial.eval (tanh x) P
⊒ βˆƒ P, βˆ€ (x : ℝ), deriv (iteratedDeriv n✝ tanh) x = Polynomial.eval (tanh x) P

Afiq Hatta (Aug 26 2025 at 13:28):

so here we have in a✝ that there is some polynomial such that nth iterated deriv is a polynomial of tan, and i want to sub that into the goal

Joseph Tooby-Smith (Aug 26 2025 at 13:41):

I think the tactic you want is obtain. I would set this up as follows:

import Mathlib
open Real

/-- The nth derivative of Tanh(x) is a polynomial of Tanh(x) -/
lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) (x : ℝ) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n with
  | zero =>
    rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  | succ n ih =>
    obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    sorry

Afiq Hatta (Aug 26 2025 at 13:41):

dumb question so where does ih come from here ?

Afiq Hatta (Aug 26 2025 at 13:41):

ah no worries, got it

Joseph Tooby-Smith (Aug 26 2025 at 13:43):

More generally you can get rid of the ✝'s with the expose_names tactic.

Afiq Hatta (Aug 26 2025 at 13:44):

got it, thanks joe

Afiq Hatta (Aug 26 2025 at 13:45):

okok so i have to modify P then use it

Joseph Tooby-Smith (Aug 26 2025 at 13:52):

Yeah, the P in your goal is not going to be the same one you obtain from the induction hypothesis.
Namely I think the one in your goal should be (1 - X ^ 2) * Polynomial.derivative P or something along those lines

Afiq Hatta (Aug 26 2025 at 13:53):

yeah

Afiq Hatta (Aug 26 2025 at 13:54):

need to rewrite deriv (iteratedDeriv n tanh) x as deriv (Polynomial.eval (tanh x) P ), then apply chain rule, then use modified ploynomail

Joseph Tooby-Smith (Aug 26 2025 at 13:55):

Yep, this seems right to me.

Afiq Hatta (Aug 26 2025 at 14:37):

hm - im pretty sure i've got this rewrite correct, but still getting a fail

⊒ βˆƒ P_1, βˆ€ (x : ℝ), deriv ((fun t => Polynomial.eval t P) ∘ tanh) x = Polynomial.eval (tanh x) P_1

Tanh.lean:92:8

tactic 'rewrite' failed, did not find instance of the pattern in the target expression
deriv (?hβ‚‚ ∘ ?h) ?x
*

Afiq Hatta (Aug 26 2025 at 14:38):

applying the derivative composition theorem here

Joseph Tooby-Smith (Aug 26 2025 at 14:38):

Could you paste a code snippet with the failing rewrite?

Afiq Hatta (Aug 26 2025 at 14:38):

lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) (x : ℝ) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n with
  | zero =>
    rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  | succ n ih =>
    obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    have h'': iteratedDeriv n tanh = (fun x => Polynomial.eval (tanh x) P) := by
      funext x
      apply h'
    have h_comp : (fun x => Polynomial.eval (tanh x) P) = (fun t => P.eval t) ∘ tanh := by
      funext x
      simp [Function.comp_apply]
    rw [h'']
    rw [h_comp]
    rw [deriv_comp]

Afiq Hatta (Aug 26 2025 at 14:38):

its that last deriv_comp that fails

Afiq Hatta (Aug 26 2025 at 14:41):

is this a teachable moment - the symbols match up, but the match isn't working, is there something else that has to match, like the type on t?

ZhiKai Pong (Aug 26 2025 at 14:50):

get rid of the quantifiers first, i.e. use P and intro x(I'm just guessing from the infoview)

Kevin Buzzard (Aug 26 2025 at 15:03):

rw sometimes doesn't work under binders. You could try simp_rw or simp only instead.

Afiq Hatta (Aug 26 2025 at 18:23):

thank you both @ZhiKai Pong and @Kevin Buzzard and also @Joseph Tooby-Smith

this ended up working. now on the way to prove that tanh has temperate growth and therefore is a schwartz operator!

lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n with
  | zero =>
    rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  | succ n ih =>
    obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    have h'': iteratedDeriv n tanh = (fun x => Polynomial.eval (tanh x) P) := by
      funext x
      apply h'
    have h_comp : (fun x => Polynomial.eval (tanh x) P) = (fun t => P.eval t) ∘ tanh := by
      funext x
      simp [Function.comp_apply]
    rw [h'', h_comp]
    use Polynomial.derivative P * (1 - Polynomial.X^2)
    intro x
    rw [deriv_comp, Polynomial.deriv, deriv_tanh]
    simp
    case h.hh =>
      have h': Real.tanh = (sinh / cosh) := by
        funext x
        rw [Pi.div_apply, tanh_eq_sinh_div_cosh]
      rw [h']
      apply DifferentiableAt.div
      Β· apply Real.differentiable_sinh
      Β· apply Real.differentiable_cosh
      Β· exact ne_of_gt (Real.cosh_pos x)
    case h.hhβ‚‚ =>
      apply Polynomial.differentiableAt

Daniel Morrison (Aug 26 2025 at 20:23):

Joseph Tooby-Smith said:

import Mathlib
open Real

/-- The nth derivative of Tanh(x) is a polynomial of Tanh(x) -/
lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) (x : ℝ) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n with
  | zero =>
    rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  | succ n ih =>
    obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    sorry

I will add that it is useful to use induction' rather than induction. It does essentially the same thing but is a bit more user friendly. You can name the variable in the induction line by saying for example induction' n with n ih in this example. It also separates the goals more nicely so you can rewrite this as

  induction' n with n ih
  . rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  . obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    sorry

which I find a little bit nicer to read. (I haven't checked this code but I think it should be right)

Matteo Cipollina (Aug 26 2025 at 20:50):

Joseph Tooby-Smith ha scritto:

More generally you can get rid of the ✝'s with the expose_names tactic.

While a great heuristic, eventually it would be preferable safer to get rid of expose_names as well

Afiq Hatta (Aug 26 2025 at 20:53):

thanks for the tips team :) will clean the code up tomorrow morning with the alternative induction syntax

Joseph Tooby-Smith (Aug 27 2025 at 04:57):

Matteo Cipollina said:

Joseph Tooby-Smith ha scritto:

More generally you can get rid of the ✝'s with the expose_names tactic.

While a great heuristic, eventually it would be preferable safer to get rid of expose_names as well

It is better to use e.g. rename?

Matteo Cipollina (Aug 27 2025 at 06:28):

Joseph Tooby-Smith ha scritto:

Matteo Cipollina said:

Joseph Tooby-Smith ha scritto:

More generally you can get rid of the ✝'s with the expose_names tactic.

While a great heuristic, eventually it would be preferable safer to get rid of expose_names as well

It is better to use e.g. rename?

Yes, or combination of rw-exact often works for me

Afiq Hatta (Aug 28 2025 at 10:07):

hey @Joseph Tooby-Smith - im falling over a bit on how to formalise 'this function has temperate growth'

Afiq Hatta (Aug 28 2025 at 10:11):

not sure which exact property to use - brain is not braining

Afiq Hatta (Aug 28 2025 at 10:12):

if you have an example or hint i can take it from there

Joseph Tooby-Smith (Aug 28 2025 at 10:12):

I think the next step would be to show that for any polynomial P, Polynomial.eval (tanh x) P is bounded. I'm not actually sure the best way of doing this though.

Afiq Hatta (Aug 28 2025 at 10:12):

cool, i can do that first

Afiq Hatta (Aug 28 2025 at 10:13):

presumably there is some property in mathlib that represents 'bounded for all x in \R'

Afiq Hatta (Aug 28 2025 at 10:23):

ah actually i reckon can just do this

lemma polynomials_tanh_bounded : βˆ€ P : Polynomial ℝ, βˆƒ C : ℝ, βˆ€ x : ℝ, |P.eval (Real.tanh x)| < C :=
  sorry

Joseph Tooby-Smith (Aug 28 2025 at 10:23):

I think if there is it would be then the temperate growth proposition would us it (and I don't think it does). But I think the thing you want is:

import Mathlib

open Real Polynomial

example (P : Polynomial ℝ) : βˆƒ C, βˆ€ x, P.eval (tanh x) ≀ C := by sorry

Afiq Hatta (Aug 28 2025 at 10:23):

omg hahah

Afiq Hatta (Aug 28 2025 at 10:24):

we literally wrote that at the same time

Joseph Tooby-Smith (Aug 28 2025 at 10:24):

haha yep, either of these would work, actually I think yours is better because you have abs :).

Joseph Tooby-Smith (Aug 28 2025 at 10:25):

Btw when putting code snippets into Zulip it helps to put the necessary preamble e.g.

import Mathlib

open Real Polynomial

...

That way people can open the code snippet in https://live.lean-lang.org by pressing the pop-up in the top right corner of the snippet, and everything will compile nicely :).

Afiq Hatta (Aug 28 2025 at 10:26):

ah - didn't realise you could do that - awesome

Afiq Hatta (Aug 28 2025 at 11:49):

this all works - i proved that the iteratived derivative of tanh is bounded

import PhysLean.Meta.Linters.Sorry
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Deriv
import Mathlib.Data.Complex.Trigonometric
import Mathlib.Analysis.Calculus.Deriv.Polynomial
import Mathlib.Topology.Algebra.Polynomial
/-!
# Properties of Tanh
We want to prove that the reflectionless potential is a Schwartz map.
This means proving that pointwise multiplication of a Schwartz map with tanh is a Schwartz map.
This means we need to prove that all derivatives of tanh are bounded and continuous, so that
the nth derivative of a function multiplied by tanh decays faster than any polynomial.

## TODO
- Add these to mathlib eventually
- Fill in the proofs for the properties of tanh
-/

open Real
open NNReal
open Field

/-- tanh(x) is less than 1 for all x -/
lemma tanh_lt_one (x : ℝ) : tanh x < 1 := by
  rw [tanh_eq_sinh_div_cosh, div_lt_one (cosh_pos x)]
  exact sinh_lt_cosh x

/-- tanh(x) is greater than -1 for all x -/
lemma minus_one_lt_tanh (x : ℝ) : -1 < tanh x := by
  rw [tanh_eq_sinh_div_cosh, lt_div_iffβ‚€ (cosh_pos x), ← sub_pos, neg_one_mul]
  simp [exp_pos x]

/-- The absolute value of tanh is bounded by 1 -/
lemma abs_tanh_lt_one (x : ℝ) : |tanh x| < 1 := by
  rw [abs_lt]
  constructor
  Β· exact minus_one_lt_tanh x
  Β· exact tanh_lt_one x

/-- The derivative of tanh(x) is 1 - tanh(x)^2 -/
lemma deriv_tanh : deriv Real.tanh = fun x => 1 - Real.tanh x ^ 2 := by
  have h: deriv (sinh / cosh) = fun x => 1 - Real.tanh x ^ 2 := by
    funext x
    rw [deriv_div, Real.deriv_sinh, Real.deriv_cosh]
    field_simp
    rw [sq, sq, tanh_eq_sinh_div_cosh, mul_sub_right_distrib]
    field_simp
    Β· apply Real.differentiable_sinh
    Β· apply Real.differentiable_cosh
    Β· exact ne_of_gt (Real.cosh_pos x)
  have h': Real.tanh = (sinh / cosh) := by
    funext x
    rw [Pi.div_apply, tanh_eq_sinh_div_cosh]
  nth_rewrite 1 [h']
  apply h

/-- Tanh(x) is n times continuously differentiable for all n -/
lemma contDiff_tanh {n : β„•} : ContDiff ℝ n tanh := by
  have hdiv : ContDiff ℝ n (fun x => Real.sinh x / Real.cosh x) := by
    apply ContDiff.div
    Β· exact contDiff_sinh
    Β· exact contDiff_cosh
    Β· intro x
      exact ne_of_gt (Real.cosh_pos x)
  conv =>
    enter [3, x]
    rw [tanh_eq_sinh_div_cosh]
  exact hdiv

/-- The nth derivative of Tanh(x) is a polynomial of Tanh(x) -/
lemma iteratedDeriv_tanh_is_polynomial_of_tanh (n : β„•) : βˆƒ P : Polynomial ℝ, βˆ€ x,
    iteratedDeriv n Real.tanh x = P.eval (Real.tanh x) := by
  induction n with
  | zero =>
    rw [iteratedDeriv_zero]
    use Polynomial.X
    simp
  | succ n ih =>
    obtain ⟨P, h'⟩ := ih
    rw [iteratedDeriv_succ]
    have h'': iteratedDeriv n tanh = (fun x => Polynomial.eval (tanh x) P) := by
      funext x
      apply h'
    have h_comp : (fun x => Polynomial.eval (tanh x) P) = (fun t => P.eval t) ∘ tanh := by
      funext x
      simp [Function.comp_apply]
    rw [h'', h_comp]
    use Polynomial.derivative P * (1 - Polynomial.X^2)
    intro x
    rw [deriv_comp, Polynomial.deriv, deriv_tanh]
    simp
    case h.hh =>
      have h': Real.tanh = (sinh / cosh) := by
        funext x
        rw [Pi.div_apply, tanh_eq_sinh_div_cosh]
      rw [h']
      apply DifferentiableAt.div
      Β· apply Real.differentiable_sinh
      Β· apply Real.differentiable_cosh
      Β· exact ne_of_gt (Real.cosh_pos x)
    case h.hhβ‚‚ =>
      apply Polynomial.differentiableAt
-- For a polynomial P, show it's bounded on any bounded interval
lemma polynomial_bounded_on_interval (P : Polynomial ℝ) (a b : ℝ):
  βˆƒ M : ℝ, βˆ€ x : ℝ, x ∈ Set.Icc a b β†’ |P.eval x| ≀ M := by
  -- Polynomials are continuous
  have hcont : Continuous (fun x => P.eval x) := P.continuous  -- Closed bounded intervals are compact
  have hcompact : IsCompact (Set.Icc a b) := isCompact_Icc
  -- Continuous functions on compact sets are bounded
  obtain ⟨M, hM⟩ := hcompact.exists_bound_of_continuousOn hcont.continuousOn
  use M
  exact hM
-- For a polynomial P, show that P (tanh x) is bounded on the real line
theorem polynomial_tanh_bounded (P : Polynomial ℝ) :
  βˆƒ C : ℝ, βˆ€ x : ℝ, |P.eval (Real.tanh x)| ≀ C := by
  have h_range : βˆ€ x : ℝ, Real.tanh x ∈ Set.Icc (-1) 1 := by
    intro x
    constructor
    Β· exact le_of_lt (minus_one_lt_tanh x)
    Β· exact le_of_lt (tanh_lt_one x)
  -- Apply polynomial boundedness on [-1, 1]
  obtain ⟨M, hM⟩ := polynomial_bounded_on_interval P (-1) 1
  use M
  intro x
  exact hM (Real.tanh x) (h_range x)
-- The nth derivative of tanh is bounded on the real line
theorem iteratedDeriv_tanh_bounded (n : β„•) :
  βˆƒ C : ℝ, βˆ€ x : ℝ, |iteratedDeriv n Real.tanh x| ≀ C := by
  obtain ⟨P, hP⟩ := iteratedDeriv_tanh_is_polynomial_of_tanh n
  obtain ⟨C, hC⟩ := polynomial_tanh_bounded P
  use C
  intro x
  rw [hP]
  exact hC x

Joseph Tooby-Smith (Aug 28 2025 at 11:54):

Nice! This looks great! Proving that tanh has temperate growth should now follow fairly trivially right?

Afiq Hatta (Aug 28 2025 at 11:54):

dumb question - where is the 'temperate growth' property?

Joseph Tooby-Smith (Aug 28 2025 at 11:56):

(deleted)

Joseph Tooby-Smith (Aug 28 2025 at 11:56):

@loogle Function.HasTemperateGrowth

loogle (Aug 28 2025 at 11:56):

:search: Function.HasTemperateGrowth, Function.HasTemperateGrowth.const, and 9 more

Joseph Tooby-Smith (Aug 28 2025 at 11:57):

So "Mathlib.Analysis.Distribution.SchwartzSpace"

Afiq Hatta (Aug 28 2025 at 11:57):

btw is anyone else getting 404s accessing https://leanprover-community.github.io/?

Afiq Hatta (Aug 28 2025 at 11:57):

got it - will prove that and then commit this soup

Joseph Tooby-Smith (Aug 28 2025 at 11:58):

Afiq Hatta said:

btw is anyone else getting 404s accessing https://leanprover-community.github.io/?

Works for me.

Afiq Hatta (Aug 28 2025 at 13:09):

really dumb question - i'm struggling with going from 'for all n' to 'infinitely differentiable' here - why is there a token error on the \infty part of this lemma?

import PhysLean.Meta.Linters.Sorry
import Mathlib.Analysis.SpecialFunctions.Trigonometric.Deriv
import Mathlib.Data.Complex.Trigonometric
import Mathlib.Analysis.Calculus.Deriv.Polynomial
import Mathlib.Topology.Algebra.Polynomial
import Mathlib.Analysis.Distribution.SchwartzSpace

open Real
open NNReal
open Field

-- Alternative approach for the smoothness part:
/-- tanh is infinitely differentiable -/
lemma contDiff_top_tanh : ContDiff ℝ ∞ Real.tanh x := by
  -- Show that tanh is ContDiff for all finite orders, which implies ContDiff ⊀
  -- apply ContDiff.of_le

  -- Β· exact contDiff_tanh
  -- Β· exact le_top
  sorry

Afiq Hatta (Aug 28 2025 at 13:10):

A bit confused on the type of infty here it expects. the code says n can also be N\infty but that doesn't work either

Afiq Hatta (Aug 28 2025 at 13:16):

The goal i'm aiming for here is
ContDiff ℝ (β†‘βŠ€) tanh

ZhiKai Pong (Aug 28 2025 at 13:19):

from a previous conversation:

Aaron Liu: Read carefully: docs#ContDiff takes a WithTop ENat, which has two tops. The higher top ("omega") is for analytic functions, and the lower top ("infinity") is for smooth (infinitely differentiable) functions.

Aaron Liu: If you open scoped ContDiff it'll print these differently.

Afiq Hatta (Aug 28 2025 at 13:21):

works! thankas @ZhiKai Pong

Afiq Hatta (Sep 03 2025 at 10:29):

hey @Joseph Tooby-Smith I was wondering if there was a theorem on compositions of two temperate growth functions having temperate growth

Afiq Hatta (Sep 03 2025 at 10:29):

i couldn't find it - and needed it to easily show that tanh ( k x ) is temperate for all k real

Afiq Hatta (Sep 03 2025 at 10:34):

if not, i can sketch up a proof?

Joseph Tooby-Smith (Sep 03 2025 at 10:37):

I don't think so, but it might just be easier to show that tanh ( k x ) has temperate growth given tanh x has temperate growth.

Afiq Hatta (Sep 03 2025 at 10:38):

yeah

Afiq Hatta (Sep 03 2025 at 10:39):

i actually don't know if the composition of two temperate growth functions has temperate growth anyway.

Joseph Tooby-Smith (Sep 03 2025 at 10:42):

I think it probably is true - because the iterated derivative of the composition becomes the sum of iterated derivatives of each part via the chain rule, which should be bounded. Proving it formally is a different matter though.

Joseph Tooby-Smith (Sep 03 2025 at 10:48):

It may also be possible/quickest to modify your proofs for tanh x and turn them into proofs of tanh (k * x)

Afiq Hatta (Sep 08 2025 at 15:02):

Hey guys, I have a dumb question. To show that tanh is a Schwartz map: after I apply tanh, there has to be a trivial embedding of Real.tanh into the complex numbers. I then need to show that this composition has temperate growth. Is there any easier way of doing this besides proving that the composition of temperate growth maps is also temperate, or redoing the whole proof of temperateGrowth of tanh in \C ?

-- Scaled tanh embedded into the complex numbers has temperate growth
theorem scaled_tanh_complex_hasTemperateGrowth (ΞΊ : ℝ) :
    Function.HasTemperateGrowth (fun x => (Real.tanh (ΞΊ * x) : β„‚)) := by
  sorry

/-- Define tanh(ΞΊ X) multiplication pointwise as a Schwartz map -/
noncomputable def tanhOperatorSchwartz (Q : ReflectionlessPotential) :
    𝓒(ℝ, β„‚) β†’L[β„‚] 𝓒(ℝ, β„‚) := by
  -- We need to handle the Real β†’ Complex coercion
  let scaled_tanh_real : ℝ β†’ ℝ := fun x => Real.tanh (Q.ΞΊ * x)
  let scaled_tanh_complex : ℝ β†’ β„‚ := fun x => (Real.tanh (Q.ΞΊ * x) : β„‚)
  have h2 : Function.HasTemperateGrowth scaled_tanh_complex
    := scaled_tanh_complex_hasTemperateGrowth Q.ΞΊ
  exact bilinLeftCLM (ContinuousLinearMap.mul β„‚ β„‚) h2

ZhiKai Pong (Sep 08 2025 at 15:08):

The way you phrase this makes me think you should probably have a separate lemma that proves any trivial embedding of real Schwartz maps into the complex also has temperate growth and just apply that, but I'm not sure how feasible as I'm not that familiar with the Schwartz map API at the moment

Afiq Hatta (Sep 08 2025 at 15:09):

yeah that's what i was thinking as well. I am still new to lean so was wondering if there was any quick keyword that does this for me, but I guess not

Afiq Hatta (Sep 08 2025 at 19:44):

@Joseph Tooby-Smith i wrote a pull request for definitions for the creation and operation operators, leaving a sorry statement for the proof of the embedding thing that I need

Joseph Tooby-Smith (Sep 09 2025 at 08:12):

I think this is what you want:

Code

Found it by loogling iteratedFDeriv, "comp" and spotting LinearIsometry.norm_iteratedFDeriv_comp_left.

(Edits: Golf)

Afiq Hatta (Sep 09 2025 at 08:37):

Thanks! I'll take some go through this example because I actually don't know a lot of these keywords. Btw - I see that you're a fan of using 'refine' - what kinds of problems lend well to using that keyword?

Afiq Hatta (Sep 09 2025 at 08:39):

like why use refine And.intro, vs something like constructor?

Joseph Tooby-Smith (Sep 09 2025 at 08:46):

refine is basically exact but you can have holes in it which are the ?_. Your goal then becomes what is needed to fill in the ?_. In some interactive theorem provers like Agda this sort of manipulation is basically all that is used. I like to view refine, exact and apply as sitting in the same collection of tactics.

I personally tend not to use constructor that often because for some reason I only recently learnt about it - and I have a habit of using refine And.intro ?_ ?_ or apply And.intro etc.

Afiq Hatta (Sep 09 2025 at 08:47):

got it - makes sense

Afiq Hatta (Sep 09 2025 at 09:01):

Where do we think is an appropriate place to put the embedding theorem that you wrote?

Joseph Tooby-Smith (Sep 09 2025 at 09:02):

I would probably just put it as a private lemma just before you need it for now.

Joseph Tooby-Smith (Sep 09 2025 at 09:03):

(FYI - I just cleaned it up some more)

Joseph Tooby-Smith (Sep 09 2025 at 09:07):

I should also say - for reference that my proof above is after golfing to make it shorter and nice - I started with something a lot longer and messier.

Afiq Hatta (Sep 09 2025 at 09:09):

got it. that's good to know, because when i read some solutions i'm like 'how...!!'. the way I've been trying to get at solutions is by doing

constructor
apply
apply
apply

till the cows come home, just trying to match goals with theorems in mathlib and then only golfing after.

Joseph Tooby-Smith (Sep 09 2025 at 09:11):

Yeah I think this is common and this is what I do too. Once you have a working proof it is relatively easy to make it shorter - that said there are lots of proofs within PhysLean that have never been golfed :).

Afiq Hatta (Sep 09 2025 at 09:20):

yeah. ok my mission for today is to add this and also maybe just write some words on my blog about the general strategy of just apply apply apply then golfing later.

Afiq Hatta (Sep 09 2025 at 09:54):

@Joseph Tooby-Smith here's a really dumb question - I often get stuck on rewrites not matching the target but it does take a lot of time to figure out what's going on. Is there anything that I can do to make the trouble shooting a bit easier? For example, I'm trying to do the proof on my own and I don't know why this final rewrite is failing?

lemma complex_embedding_of_temperate_growth(f : ℝ β†’ ℝ) (h : Function.HasTemperateGrowth f) :
    Function.HasTemperateGrowth (fun x => (f x : β„‚)) := by
  constructor
  apply ContDiff.fun_comp
  apply ContinuousLinearMap.contDiff Complex.ofRealCLM
  obtain ⟨h1, h2⟩ := h
  apply h1
  obtain ⟨h1, h2⟩ := h
  intro n
  use 0
  use 0
  intro x
  have h : (fun x => ((f x) : β„‚)) = (Complex.ofRealCLM ∘ f) := by
    sorry
  rw [h]
  apply LinearIsometry.norm_iteratedFDeriv_comp_left

Afiq Hatta (Sep 09 2025 at 10:01):

i'm just as curious about the trouble shooting here (ie how to check if I got anything wrong with types etc, and obvious ways to spot errors) as well as what went wrong in this specific example

Joseph Tooby-Smith (Sep 09 2025 at 10:26):

You mean the finial apply? The last rw looks like it's working to me.

Afiq Hatta (Sep 09 2025 at 10:28):

sorry - the final apply!

Afiq Hatta (Sep 09 2025 at 10:28):

i can't seem to get it to work

Joseph Tooby-Smith (Sep 09 2025 at 10:30):

Two reasons:

  1. You need RCLike.ofRealLI not Complex.ofRealCLM in h. The former is a linear isometry whilst the latter is a continuous linear map. As functions they are both equal, but have different types.
  2. To use apply your goal has to be the same as the theorem given, in this case it is not. However, once you have made the change 1 here, then rw [LinearIsometry.norm_iteratedFDeriv_comp_left] should work.

Afiq Hatta (Sep 09 2025 at 10:36):

ah - so does the break come from the actual type being wrong? because usually, what happens is that i apply the theorem, and then a subgoal is created in which i prove that something satisfies that. but i guess that only applies to hypotheses

Joseph Tooby-Smith (Sep 09 2025 at 10:40):

Here is a short description of what apply does:

Let us say you have a proof h : A β†’ B β†’ C and your goal is equal to C, then apply h will great subgoals for A and B. But if your goal is not currently C then you can't use apply h.

I don't really use e.g. apply _ at _ so don't know how that works exactly.

Afiq Hatta (Sep 09 2025 at 10:41):

got it - that's clear - thanks Joe

ZhiKai Pong (Sep 09 2025 at 11:11):

@Joseph Tooby-Smith may I ask how do you inspect this code? since this depends on an open PR, is there a way to directly play around with it or do you just copy the relevant lemmas to your local PhysLean?

Joseph Tooby-Smith (Sep 09 2025 at 12:38):

For this specific example the lemma I ended up proving didn't actually need anything from the PR, so I just used https://live.lean-lang.org.

But more generally, if there is something I want to look at from a given PR or play around with in more detail, I will just check out the corresponding PR. Which on github desktop is done via:

Screenshot 2025-09-09 at 13.38.18.png

Afiq Hatta (Sep 09 2025 at 15:10):

@Joseph Tooby-Smith

my own attempt (learning)

lemma complex_embedding_of_temperate_growth(f : ℝ β†’ ℝ) (h : Function.HasTemperateGrowth f) :
    Function.HasTemperateGrowth (fun x => (f x : β„‚)) := by
  constructor
  apply ContDiff.fun_comp
  apply ContinuousLinearMap.contDiff Complex.ofRealCLM
  obtain ⟨h1, h2⟩ := h
  apply h1
  obtain ⟨h1, h2⟩ := h
  intro n
  obtain ⟨k, C, j⟩ := h2 n
  use k
  use C
  intro x
  have h : (fun x => (Complex.ofReal ∘ f) x) = (RCLike.ofRealLI (K := β„‚) ∘ f) := by
    funext x
    simp
  have h'' : (fun x => (f x : β„‚)) = (fun x => (Complex.ofReal ∘ f) x) := rfl
  rw [h'']
  rw [h]
  rw [LinearIsometry.norm_iteratedFDeriv_comp_left (g := RCLike.ofRealLI (K := β„‚)) (hf := h1.contDiffAt)]
  apply j
  apply ENat.natCast_le_of_coe_top_le_withTop
  simp only [le_refl]

Joseph Tooby-Smith (Sep 09 2025 at 15:12):

Awesome! I would now play the game to see how much you can golf this, maybe using tactics you are less familiar with.

Afiq Hatta (Sep 09 2025 at 15:14):

yeah. i've starting writing some notes on useful go to tactics for beginners which i'll publish tonight

Joseph Tooby-Smith (Sep 09 2025 at 15:15):

Nice! I would be interested to see at some point what are the most common tactics used in e.g. PhysLean compared to Mathlib etc.

I would advocate for conv_lhs, conv_rhs to be part of that list - once I discovered them they were a life-changer.

ZhiKai Pong (Sep 09 2025 at 15:34):

@Afiq Hatta

I did the same earlier but waited for you to finish, just some minor comments:

import Mathlib

lemma complex_embedding_of_temperate_growth(f : ℝ β†’ ℝ) (h : Function.HasTemperateGrowth f) :
    Function.HasTemperateGrowth (fun x => (f x : β„‚)) := by
  -- once you structure the code you'll see that the reason you had two of the same obtains was because they were in different branches so you can just put it before constructor
  -- in general always use dot notation to structure the proof whenever you use constructor and create multiple goals
  obtain ⟨h1, h2⟩ := h
  constructor
  Β· apply ContDiff.fun_comp
    apply ContinuousLinearMap.contDiff Complex.ofRealCLM
    exact h1
  Β· intro n
    obtain ⟨k, C, j⟩ := h2 n
    use k, C
    intro x
    -- actually this is also defeq so you can just combine into one
    -- have h : (fun x => (Complex.ofReal ∘ f) x) = (RCLike.ofRealLI (K := β„‚) ∘ f) := rfl
    /-
    have h'' : (fun x => (f x : β„‚)) = (RCLike.ofRealLI (K := β„‚) ∘ f) := rfl
    rw [h'']
    -/
    -- also for defeq expressions you can simply use `change`:
    change β€–iteratedFDeriv ℝ n (RCLike.ofRealLI ∘ f) xβ€– ≀ C * (1 + β€–xβ€–) ^ k
    rw [LinearIsometry.norm_iteratedFDeriv_comp_left (g := RCLike.ofRealLI (K := β„‚)) (hf := h1.contDiffAt)]
    exact j x
    Β· apply ENat.natCast_le_of_coe_top_le_withTop
      simp only [le_refl]

also please include import Mathlib if it's a standalone lemma so others can just view it in the web playground :)

Afiq Hatta (Sep 09 2025 at 15:44):

Thanks @ZhiKai Pong - this kind of breakdown is really helpful.

Afiq Hatta (Sep 09 2025 at 15:46):

I was wondering about how to use change expressions - thank you for the flag. Need to learn more!!!

Afiq Hatta (Sep 09 2025 at 16:34):

@Joseph Tooby-Smith wrote a reply to one of the things you raised on my pull request btw

Afiq Hatta (Sep 10 2025 at 08:19):

@Joseph Tooby-Smith updated the pull request with the new proofs and also the requested refactors!

Joseph Tooby-Smith (Sep 10 2025 at 08:22):

Just approved it - I merged with master merged master into this PR to clean it up, I think you will have to wait for it to finish the workflows before you can merge it.

Afiq Hatta (Sep 10 2025 at 08:22):

sweet

Afiq Hatta (Sep 14 2025 at 09:05):

I think the next logical thing to prove would be that the Hamiltonian of the reflectionless potential satisfies
H=a†aβˆ’β„2ΞΊ22mI H = a ^ \dagger a - \frac { \hbar ^ 2 \kappa ^ 2 } { 2m } \mathbb I

For this, I reckon we also need

  • the commutuation relations
  • to formalise the Hamiltonian, and also prove that it is also a Schwartz map? In terms of formalising the Hamiltonian - is there a special template that I can follow elsewhere in the code?

Also - what is the convention for what a 'ground state' here? in the paper, it is just a∣0⟩ a \ket 0 = 0. is this a convention that we want to keep in physlean?

Joseph Tooby-Smith (Sep 14 2025 at 17:41):

I think the fact that the Hamiltonian is Schwartz should follow from the proof that the momentum operator is Schwartz and the tanh operator is.

Afiq Hatta (Sep 14 2025 at 19:17):

sweet. and in terms of the algebraic proof of the eigenvalues - i guess i can follow this quantum harmonic oscillator example as a rough guide


Last updated: Dec 20 2025 at 21:32 UTC