User-defined ODEs: the Gross-Pitaevskii Equation and Nonlinear Time Evolution
This example shows how to code up the Gross-Pitaevskii equation for a system in a one-dimensional lattice subject to a harmonic trapping potential:
\[\begin{split}i\partial_t\psi_j(t) &=& -J\left( \psi_{j-1}(t) + \psi_{j+1}(t)\right) + \frac{1}{2}\kappa_\mathrm{trap}(t)(j-j_0)^2\psi_j(t) + U|\psi_j(t)|^2\psi_j(t), \nonumber \\
\kappa_\mathrm{trap}(t)&=&(\kappa_f-\kappa_i)t/t_\mathrm{ramp}+ \kappa_i.\end{split}\]
Details about the code below can be found in SciPost Phys. 7, 020 (2019).
Script
1#####################################################################
2# example 8 #
3# In this script we demonstrate how to use QuSpin to define #
4# and solve nonlinear ordinary differential equations. #
5# In particular, we show real and imaginary time evolution #
6# in the Gross-Pitaevskii equation of a Bose condensate in a #
7# harmonic trap. #
8#####################################################################
9from quspin.operators import hamiltonian # Hamiltonians and operators
10from quspin.basis import boson_basis_1d # Hilbert space boson basis
11from quspin.tools.evolution import evolve # nonlinear evolution
12import numpy as np # generic math functions
13import matplotlib.pyplot as plt # plotting library
14from six import iteritems # loop over elements of dictionary
15
16#
17##### define model parameters #####
18L = 300 # system size
19# calculate centre of chain
20if L % 2 == 0:
21 j0 = L // 2 - 0.5 # centre of chain
22else:
23 j0 = L // 2 # centre of chain
24sites = np.arange(L) - j0
25# static parameters
26J = 1.0 # hopping
27U = 1.0 # Bose-Hubbard interaction strength
28# dynamic parameters
29kappa_trap_i = 0.001 # initial chemical potential
30kappa_trap_f = 0.0001 # final chemical potential
31t_ramp = 40.0 / J # set total ramp time
32
33
34# ramp protocol
35def ramp(t, kappa_trap_i, kappa_trap_f, t_ramp):
36 return (kappa_trap_f - kappa_trap_i) * t / t_ramp + kappa_trap_i
37
38
39# ramp protocol parameters
40ramp_args = [kappa_trap_i, kappa_trap_f, t_ramp]
41#
42##### construct single-particle Hamiltonian #####
43# define site-coupling lists
44hopping = [[-J, i, (i + 1) % L] for i in range(L - 1)]
45trap = [[0.5 * (i - j0) ** 2, i] for i in range(L)]
46# define static and dynamic lists
47static = [["+-", hopping], ["-+", hopping]]
48dynamic = [["n", trap, ramp, ramp_args]]
49# define basis
50basis = boson_basis_1d(L, Nb=1, sps=2)
51# build Hamiltonian
52Hsp = hamiltonian(static, dynamic, basis=basis, dtype=np.float64)
53E, V = Hsp.eigsh(time=0.0, k=1, which="SA")
54
55
56#
57##### imaginary-time evolution to compute GS of GPE #####
58def GPE_imag_time(tau, phi, Hsp, U):
59 """
60 This function solves the real-valued GPE in imaginary time:
61 $$ -\dot\phi(\tau) = Hsp(t=0)\phi(\tau) + U |\phi(\tau)|^2 \phi(\tau) $$
62 """
63 return -(Hsp.dot(phi, time=0) + U * np.abs(phi) ** 2 * phi)
64
65
66# define ODE parameters
67GPE_params = (Hsp, U)
68# define initial state to flow to GS from
69phi0 = V[:, 0] * np.sqrt(L) # initial state normalised to 1 particle per site
70# define imaginary time vector
71tau = np.linspace(0.0, 35.0, 71)
72# evolve state in imaginary time
73psi_tau = evolve(
74 phi0,
75 tau[0],
76 tau,
77 GPE_imag_time,
78 f_params=GPE_params,
79 imag_time=True,
80 real=True,
81 iterate=True,
82)
83#
84# display state evolution
85for i, psi0 in enumerate(psi_tau):
86 # compute energy
87 E_GS = (
88 Hsp.matrix_ele(psi0, psi0, time=0) + 0.5 * U * np.sum(np.abs(psi0) ** 4)
89 ).real
90 # plot wave function
91 plt.plot(
92 sites,
93 abs(phi0) ** 2,
94 color="r",
95 marker="s",
96 alpha=0.2,
97 label="$|\\phi_j(0)|^2$",
98 )
99 plt.plot(sites, abs(psi0) ** 2, color="b", marker="o", label="$|\\phi_j(\\tau)|^2$")
100 plt.xlabel("$\\mathrm{lattice\\ sites}$", fontsize=14)
101 plt.title(
102 "$J\\tau=%0.2f,\\ E_\\mathrm{GS}(\\tau)=%0.4fJ$" % (tau[i], E_GS), fontsize=14
103 )
104 plt.ylim([-0.01, max(abs(phi0) ** 2) + 0.01])
105 plt.legend(fontsize=14)
106 plt.draw() # draw frame
107 plt.pause(0.005) # pause frame
108 plt.clf() # clear figure
109plt.close()
110
111
112#
113##### real-time evolution of GPE #####
114def GPE(time, psi):
115 """
116 This function solves the complex-valued time-dependent GPE:
117 $$ i\dot\psi(t) = Hsp(t)\psi(t) + U |\psi(t)|^2 \psi(t) $$
118 """
119 # solve static part of GPE
120 psi_dot = Hsp.static.dot(psi) + U * np.abs(psi) ** 2 * psi
121 # solve dynamic part of GPE
122 for f, Hd in iteritems(Hsp.dynamic):
123 psi_dot += f(time) * Hd.dot(psi)
124 return -1j * psi_dot
125
126
127# define real time vector
128t = np.linspace(0.0, t_ramp, 101)
129# time-evolve state according to GPE
130psi_t = evolve(psi0, t[0], t, GPE, iterate=True, atol=1e-12, rtol=1e-12)
131#
132# display state evolution
133for i, psi in enumerate(psi_t):
134 # compute energy
135 E = (Hsp.matrix_ele(psi, psi, time=t[i]) + 0.5 * U * np.sum(np.abs(psi) ** 4)).real
136 # compute trap
137 kappa_trap = ramp(t[i], kappa_trap_i, kappa_trap_f, t_ramp) * (sites) ** 2
138 # plot wave function
139 plt.plot(
140 sites,
141 abs(psi0) ** 2,
142 color="r",
143 marker="s",
144 alpha=0.2,
145 label="$|\\psi_{\\mathrm{GS},j}|^2$",
146 )
147 plt.plot(sites, abs(psi) ** 2, color="b", marker="o", label="$|\\psi_j(t)|^2$")
148 plt.plot(sites, kappa_trap, "--", color="g", label="$\\mathrm{trap}$")
149 plt.ylim([-0.01, max(abs(psi0) ** 2) + 0.01])
150 plt.xlabel("$\\mathrm{lattice\\ sites}$", fontsize=14)
151 plt.title(
152 "$Jt=%0.2f,\\ E(t)-E_\\mathrm{GS}=%0.4fJ$" % (t[i], E - E_GS), fontsize=14
153 )
154 plt.legend(loc="upper right", fontsize=14)
155 plt.tight_layout()
156 plt.draw() # draw frame
157 plt.pause(0.00005) # pause frame
158 plt.clf() # clear figure
159plt.close()