1D GPE#
The GPE in 1D with constant potential is integrable. Here we explain what this means.
We start by assuming we have a solution \(\psi(x, t)\) to the GPE:
Show that the eigenvalues of the following Lax operator do not change in time:
The spectrum allows one to characterize the state \(\psi(x, t)\) in terms of non-linear excitations with discrete eigenvalues correspond to solitons, and a continuum. The eigenvalues \(\lambda\) are the velocities of the corresponding excitations.
One often sees associated with the Lax operator \(\op{L}\), another operator \(\op{A}\) which effects a time-derivative. Together these are referred to as a Lax pair and they have the following property obtained by differentiating the eigenvalue equation:
Note
To simplify the algebra, we choose units where \(2m = \hbar = m\!\abs{g} = 1\), so that the GPE and Lax operator assume the following dimensionless forms:
with \(ℵ \pm 1\). This matches the notation of [Faddeev and Takhtajan, 1987]. Following their approach, we included a constant background density \(\rho^2 = \psi_0^2\) here which allows use to use time-independent twisted boundary conditions if needed.
Repulsive interactions \(g>0\) have \(ℵ = 1\) which is often referred to as de-focusing. Attractive interactions \(g<0\) have \(ℵ = -1\) which is referred to as focusing.
from sympy import *
hbar = 1
m = S(1)/2
g = S(1)/m
t, x, psi0, theta = var('t, x, psi_0, theta', positive=True, real=True)
n = psi0**2
c = sqrt(g*n/m)
v = c*cos(theta)
u = c*sin(theta)
l = hbar/m/u
n = psi0**2
mu = g*n
psi = psi0*exp(mu*t/I/hbar)*(I*v/c + u/c*tanh((x-v*t)/l))
psic = psi.conjugate()
eq = -hbar**2*psi.diff(x, x)/2/m + g*(psi*psic)*psi - I*hbar*psi.diff(t)
eq.simplify()
Solitons
Consider an eigenstate in the de-focusing case \(ℵ = 1\). It is well known that this theory supports soliton solutions
which whose field is asymptotically flat
Grey solitons with velocity \(v = \lambda\) should satisfy this equation:
Solving for \(\phi_1\) and inserting this, we have
Details
We start with some formalism from [Ablowitz and Clarkson, 1991]. The start with the following scattering problem expressed as two linear equations:
Consistency of mixed partials \(v_{,xt} = v_{,tx}\) requires
The GPE comes from a 2×2 system, which they parameterize as
where \(q\), \(r\), \(A\), \(B\), \(C\), and \(D\) are functions of \((x,t;k)\). They also assume that \(k\) is time-independent: \(\dot{k} = 0\).
We can turn \(v_{,x} = \mat{X}v\) into the Lax eigenvalue equation by isolating the eigenvalue:
From \(\mat{X}\) we can find a differentiation operator:
Hence, we can identify \(k \equiv \lambda\), \(v \equiv \phi\) , and
We now need to find matrices \(\mat{X}\) and \(\mat{T}\) whose consistency condition is equivalent to the GPE. They give a solution with a different sign convention, so we instead follow the example in [Faddeev and Takhtajan, 1987], identifying
To simplify the algebra, we choose units where \(2m = \hbar = mg = 1\), so that the GPE and Lax operator assume the following dimensionless forms:
with \(ℵ = \pm 1\). This follows the notation of [Faddeev and Takhtajan, 1987] which contains the best presentation of this material I have yet found. Following their presentation, we have included a constant background density \(\rho^2\) here which allows use to use time-independent twisted boundary conditions if needed.
Working with the Lax operator is not so easy because of the derivatives: i.e. you must be careful with commutators \([\partial_x, f] = f_{,x}\). Instead, [Faddeev and Takhtajan, 1987] replaces this with a zero-curvature representation:
The NS model follows from the compatibility conditions (zero-curvature conditions):
The geometric interpretation is that \(\mat{U}(x, t;\lambda)\) and \(\mat{V}(x, t;\lambda)\) are local connection coefficients in the trivial vector bundle \(\mathbb{R}^2\times \mathbb{C}^2\) where \(F(x, t;\lambda):\quad \mathbb{R}^2 \mapsto \mathbb{C}^2\). The compatibility condition is equivalent to the \((\mat{U}, \mat{V})\)-connection having zero curvature. This system has the gauge transformation
The zero-curvature conditions are related to the Lax operator of the inverse-scattering method (ISM) using the identity \(\mat{σ}_{z}\mat{σ}_{\pm} = \pm \mat{σ}_{\pm}\) and applying \(\I\mat{σ}_{z}\) to \(F_{,x} - \mat{U}F = 0\):
This is self-adjoint for the de-focusing case \(ℵ > 0\) with a corresponding real spectrum \(\lambda/2\).
To Do: This does not quite work. Why? To check that \(\op{L}\) and \(\op{A} = \mat{V}\) form a Lax pair, we compute
Thus
# Here we show that the formulae in Faddeev:1987 work
import IPython.display
import sympy
from sympy import var, Function, Symbol, Matrix, I, Eq, S, sqrt
x, t, lam, g, rho = var('x, t, lambda, aleph, rho')
psi = Function('psi')(x, t)
psic = psi.conjugate()
Sp = Matrix([[0, 1],
[0, 0]])
Sm = Sp.T
Sz = Matrix([[1, 0],
[0, -1]])
# To display things nicely, we introduce some subsitutions
psi_ = Symbol('psi')
psi_x_ = Symbol(r'\psi_{,x}')
psi_xx_ = Symbol(r'\psi_{,xx}')
psic_ = Symbol(r'\bar{\psi}')
psic_x_ = Symbol(r'\bar{\psi}_{,x}')
psic_xx_ = Symbol(r'\bar{\psi}_{,xx}')
ss_ = {
psi.diff(x,x): psi_xx_,
psic.diff(x,x): psic_xx_,
psi.diff(x): psi_x_,
psic.diff(x): psic_x_,
psi: psi_,
psic: psic_,
}
def show_eq(a, b):
display(IPython.display.Latex(f"{a} = {sympy.latex(b.subs(ss_))}"))
# This is the GPE or NLSEQ (1.5)
ss = {psi.diff(t): (-psi.diff(x,x) + 2*g*(psic*psi - rho**2)*psi)/I,
psic.diff(t): -(-psic.diff(x,x) + 2*g*(psic*psi - rho**2)*psic)/I}
# Here are the forumulae from Faddeev:1987
U0 = sqrt(g)*(psic*Sp + psi*Sm) # (2.4)
U1 = Sz/2/I # (2.5)
V0 = (I*g*(psi*psic - rho**2)*Sz # (2.7 + 2.11)
- I*sqrt(g)*(psic.diff(x)*Sp - psi.diff(x)*Sm))
V1, V2 = -U0, -U1 # (2.8)
U = U0 + lam*U1 # (2.3)
V = V0 + lam*V1 + lam**2*V2 # (2.6)
res = U.diff(t).subs(ss) - V.diff(x) + U@V - V@U # (2.10)
res.simplify()
assert res == Matrix([[0,0], [0,0]])
show_eq("U", U)
show_eq("V", V)
res1 = sympy.simplify(U.diff(t))
res2 = sympy.simplify(V.diff(x) - U@V + V@U)
display(Eq(
(sympy.I * res1 @ Sz / sympy.sqrt(g)).subs(ss_),
(sympy.I * res2 @ Sz / sympy.sqrt(g)).subs(ss_)))
# Remainder term from Lax pair... this is not zero... WHY?
display(((Sz@V - V@Sz)@Sz).subs(ss_))
# Testing (1.9.2)
a, x, t = symbols('a, x, t', real=True)
k = symbols('k')
u = Function('u')(x, t)
f1, f2 = Function('f_1')(x, t), Function('f_2')(x, t)
f = Matrix([[f1], [f2]])
uc = u.conjugate()
L0 = Matrix([[0, uc], [u, 0]])
L1 = I*Matrix([[1+k, 0], [0, 1-k]])
M0 = Matrix([[-I*u*uc/(1+k), uc.diff(x)],
[-u.diff(x), I*u*uc/(1-k)]])
M2 = I*k*Matrix([[1, 0], [0, 1]])
def apply(L, f):
L0, L1, L2 = L
return L0 @ f + L1 @ f.diff(x) + L2 @ f.diff(x, x)
L = [L0, L1, 0*M2]
M = [M0, 0*M2, M2]
Lt = [L0.diff(t), L1.diff(t), 0*M2]
res = apply(Lt, f) + apply(L, apply(M, f)) - apply(M, apply(L, f))
res = simplify(res)
def simp(expr):
expr = simplify(expr)
u_ = Symbol('u')
uc_ = u_.conjugate()
ss_ = {
uc.diff(x,x): Symbol(r'\bar{u}_{,xx}'),
uc.diff(x): Symbol(r'\bar{u}_{,x}'),
uc.diff(t): Symbol(r'\bar{u}_{,t}'),
uc: uc_,
u.diff(x,x): Symbol(r'u_{,xx}'),
u.diff(x): Symbol(r'u_{,x}'),
u.diff(t): Symbol(r'u_{,t}'),
u: u_,
}
return expr.subs(ss_)
simp(solve(res[1], u.diff(t))[0])
In the preceding code, we check that the compatibility condition
with the following:
yields the following NLSEQ:
Setting \(r = \pm q^*\) we have
Thus, we can reproduce the GPE by setting \(\I a = 2\):
[Ablowitz and Clarkson, 1991] do something slightly different. They set \(a = 2\I\) to get
Note, however, that the direct formulae have sign errors. The left column is from [Ablowitz and Clarkson, 1991]. The right column is obtained with \(a = 2\I\) and works.
Their notation is equivalent to [Faddeev and Takhtajan, 1987] if we identify
Remarks:
Why do we need opposite signs along the diagonal? It would be nice to have an interpretation that the \(k\) is just the wave-vector, so why do we need the \(\mat{\sigma}_z\)? Probably due to time-reversal or something?
They note that any \(x\)-derivatives in the equation for \(v_{,t}\) can be eliminated by using \(v_{,x} = \mat{X}v\).
If \(r=-1\), we have the time-independent Schrödinger equation:
\[\begin{gather*} v_{1,x} = -\I k v_1 + qv_2, \qquad v_{2,x} = \I k v_2 - v_1, \end{gather*}\]hence, eliminating \(v_1 = \I k v_2 - v_{2,x}\) we have
\[\begin{gather*} \I k v_{2,x} - v_{2,xx} = -\I k (\I k v_2 - v_{2,x}) + qv_2\\ v_{2,xx} + (k^2 + q)v_2 = 0. \end{gather*}\]This is the time-independent Schrödinger equation in units where \(\hbar^2 = 2m =1\) with
\[\begin{gather*} k^2 = E, \qquad q = -V(x, t), \qquad v = \begin{pmatrix} \I k^2 \psi(x, t) - \psi_{,x}(x, t)\\ \psi(x, t) \end{pmatrix}. \end{gather*}\]To relate this to the Lax pair
\[\begin{gather*} \op{L}\phi = \phi \lambda, \qquad \phi_{,t} = \op{A}\phi, \end{gather*}\]we note that
\[\begin{gather*} \mat{X} = -\I \mat{\sigma}_{z} k + \mat{M}, \qquad \mat{M} = \begin{pmatrix} 0 & q\\ r & 0 \end{pmatrix}. \end{gather*}\]Hence:
\[\begin{gather*} v_{,x} = \partial_x v = \mat{X}v = (-\I \mat{\sigma}_{z} k + \mat{M})v\\ \I\mat{\sigma}_z\partial_x v = (k + \I\mat{\sigma}_z\mat{M})v,\\ (\I\mat{\sigma}_z\partial_x - \I\mat{\sigma}_z\mat{M}) v = kv. \end{gather*}\]Hence, we can identify \(k \equiv \lambda\), \(v \equiv \phi\) , and
\[\begin{gather*} \op{L} = \I\mat{\sigma}_z\partial_x - \I\mat{\sigma}_z\mat{M} = \begin{pmatrix} \I\partial_x & -\I q\\ \I r & -\I\partial_x \end{pmatrix},\qquad \op{A} = \mat{T}. \end{gather*}\]Since \(v_{,xt} = v_{,tx}\) is consistent, differentiating the Lax equation and using \(\dot{\lambda} = 0\) gives the consistency condition
\[\begin{gather*} \op{L}_{,t} + [\op{L}, \op{A}] = 0. \end{gather*}\]I find it hard to show this explicitly in general.
Here they have
Note that \(L\) is not hermitian. This is strange.
Proceeding as per [Ablowitz and Clarkson, 1991], we look for a system
If we take \(v = \phi\), then \(\mat{T} = \mat{A}\). To find \(\mat{X}\) note that
Hence
We note that this matches the formulation in [Faddeev and Takhtajan, 1987] for \(\mat{U}\) if we take \(\lambda \rightarrow \lambda /2\) and \(u \rightarrow \psi^*\), and \(ℵ=1\):
This second matrix does not match, however. [Faddeev and Takhtajan, 1987] has
which I have checked above. They do not seem to use \(\op{A}\) in [Osborne, 2010], so perhaps this error was overlooked or perhaps it is gauge equivalent – I have not checked yet.
#:tags: [hide-input]
# Test of Osbourne:2010. It seems there is an error.
x, t, lam, g, rho = var('x, t, lambda, aleph, rho')
g = 1
rho = 0
U0 = sympy.sqrt(g)*(psic*Sp + psi*Sm)
U1 = Sz/2/I
U = U0 + lam*U1
V = Matrix(
[[I*psi*psic - lam**2/2/I, -psic.diff(x) + I*lam*psic],
[-psi.diff(x) + I*lam*psi, -I*psi*psic + lam**2/2/I]])
# From Faddeev
V_ = Matrix(
[[I*psi*psic - lam**2/2/I, -I*psic.diff(x) - lam*psic],
[I*psi.diff(x) - lam*psi, -I*psi*psic + lam**2/2/I]])
ss = {psi.diff(t): (-psi.diff(x,x) + 2*g*(psic*psi - rho**2)*psi)/I,
psic.diff(t): -(-psic.diff(x,x) + 2*g*(psic*psi - rho**2)*psic)/I}
res = U.diff(t).subs(ss) - V.diff(x) + U@V - V@U
res.simplify()
display(V.subs(ss_))
display(res.subs(ss_))
assert res == Matrix([[0,0], [0,0]])
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Cell In[8], line 27
23
24 res.simplify()
25 display(V.subs(ss_))
26 display(res.subs(ss_))
---> 27 assert res == Matrix([[0,0], [0,0]])
AssertionError:
%%time
import atexit
import numpy as np, matplotlib.pyplot as plt
from mmfutils.performance.fft import fftw_wisdom
from pytimeode.evolvers import EvolverABM
from mmf_contexts import FPS
from gpe.bec import StateGPEBase
wisdom = fftw_wisdom(threads=2)
wisdom.__enter__()
atexit.register(wisdom.__exit__)
class State(StateGPEBase):
t_unit = 1
t_unit_name = ""
def __init__(self, seed=2, eps=1, dk=1.0, N0=1, g=1, Nx=64, **kw):
args = dict(
Nxyz=(Nx,),
Lxyz=(10.0,),
m=1/eps**2,
hbar=eps,
g=g)
args.update(kw)
self.seed = seed
self.dk = dk
self.N0 = N0
super().__init__(**args)
def init(self):
super().init()
self.rng = np.random.default_rng(seed=self.seed)
def set_initial_state(self):
Lx = self.basis.Lxyz[0]
k = self.basis.kx
phi = 2*np.pi * self.rng.random(size=self.shape)
nk = np.exp(-(k/self.dk)**2/2)*np.exp(1j*phi)
self.data = self.basis.ifftn(nk)
self *= np.sqrt(self.N0/(self.get_N()/Lx))
s = State(m=0.5)
dT = 0.1
dt = 0.2*s.t_scale
steps = int(np.ceil(dT/dt))
dt = dT/steps
ev = EvolverABM(s, dt=dt)
ss = [s]
axs = s.plot()
for frame in range(5):
ev.evolve(steps)
ev.y.plot(axs=axs, label=f"$t={ev.y.t:.2f}$")
ss.append(ev.get_y())
ax = axs[0]
ax.legend()
ax.set(xlabel="$x$", ylabel="$n$");
plt.close('all')
CPU times: user 1.52 s, sys: 80.3 ms, total: 1.6 s
Wall time: 1.2 s
def get_L(s):
Nx = s.shape[0]
psi = s.get_psi()
k = s.basis.kx.copy()
Q = s.basis.fftn(np.eye(Nx))
iD = Q.T.conj() @ (k[:, None]*Q) / Nx
sqkc = np.sqrt(s.m*s.g/s.hbar**2)
L = s.hbar/2/s.m * np.block(
[[iD, -1j*sqkc*np.diag(psi.conj())],
[1j*sqkc*np.diag(psi), -iD]])
assert np.allclose(L, L.T.conj())
return np.linalg.eigvalsh(L)
from gpe.minimize import MinimizeStateFixedPhase
s = State(Nx=128)
s.set_psi(2)
Nx = s.shape[0]
N = 2
phase = np.sign(np.exp(2j*np.pi * N * np.arange(Nx) / Nx).real)
s = MinimizeStateFixedPhase(s, phase).minimize()
s.plot()
[<Axes: >]
{code-cell} ipython3
%%time
Nx = 256*16*16
s = State(eps=0.1, g=-1. Nxyz=(Nx,), Lxyz=(128*Nx/2**16,))
T = 3.5
Nt = 100
dT = T/Nt
dt = 0.2*s.t_scale
steps = int(np.ceil(dT/dt))
dt = dT/steps
ev = EvolverABM(s, dt=dt)
ss = [s]
fig, ax = plt.subplots()
for n in FPS(Nt, fig=fig, embed=True, display=True):
ev.evolve(steps)
ss.append(ev.get_y())
ax.cla()
ev.y.plot(axs=[ax])
{code-cell} ipython3
k4s = []
ts = []
for s in ss:
n = s.get_density()
k4 = s.basis.Lxyz[0] * s.integrate(n**2) / s.integrate(n)**2
k4s.append(k4)
ts.append(s.t)
fig, ax = plt.subplots()
ax.plot(ts, k4s)
ax.set(xlabel="$t$", ylabel="$k_4(t)$");
{code-cell} ipython3