qflux 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qflux might be problematic. Click here for more details.

Files changed (38) hide show
  1. qflux/GQME/__init__.py +7 -0
  2. qflux/GQME/dynamics_GQME.py +438 -0
  3. qflux/GQME/params.py +62 -0
  4. qflux/GQME/readwrite.py +119 -0
  5. qflux/GQME/tdvp.py +233 -0
  6. qflux/GQME/tt_tfd.py +448 -0
  7. qflux/__init__.py +5 -0
  8. qflux/closed_systems/__init__.py +17 -0
  9. qflux/closed_systems/classical_methods.py +427 -0
  10. qflux/closed_systems/custom_execute.py +22 -0
  11. qflux/closed_systems/hamiltonians.py +88 -0
  12. qflux/closed_systems/qubit_methods.py +266 -0
  13. qflux/closed_systems/spin_dynamics_oo.py +371 -0
  14. qflux/closed_systems/spin_propagators.py +300 -0
  15. qflux/closed_systems/utils.py +205 -0
  16. qflux/open_systems/__init__.py +2 -0
  17. qflux/open_systems/dilation_circuit.py +183 -0
  18. qflux/open_systems/numerical_methods.py +303 -0
  19. qflux/open_systems/params.py +29 -0
  20. qflux/open_systems/quantum_simulation.py +360 -0
  21. qflux/open_systems/trans_basis.py +121 -0
  22. qflux/open_systems/walsh_gray_optimization.py +311 -0
  23. qflux/typing/__init__.py +0 -0
  24. qflux/typing/examples.py +24 -0
  25. qflux/utils/__init__.py +0 -0
  26. qflux/utils/io.py +16 -0
  27. qflux/utils/logging_config.py +61 -0
  28. qflux/variational_methods/__init__.py +1 -0
  29. qflux/variational_methods/qmad/__init__.py +0 -0
  30. qflux/variational_methods/qmad/ansatz.py +64 -0
  31. qflux/variational_methods/qmad/ansatzVect.py +61 -0
  32. qflux/variational_methods/qmad/effh.py +75 -0
  33. qflux/variational_methods/qmad/solver.py +356 -0
  34. qflux-0.0.1.dist-info/METADATA +144 -0
  35. qflux-0.0.1.dist-info/RECORD +38 -0
  36. qflux-0.0.1.dist-info/WHEEL +5 -0
  37. qflux-0.0.1.dist-info/licenses/LICENSE +674 -0
  38. qflux-0.0.1.dist-info/top_level.txt +1 -0
qflux/GQME/tdvp.py ADDED
@@ -0,0 +1,233 @@
1
+ import numpy as np
2
+ import functools
3
+ import sys
4
+
5
+ from mpsqd.utils import MPS
6
+ from mpsqd.utils.split import split_rq,split_qr
7
+
8
+ from mpsqd.tdvp import ttfunc as ttf
9
+
10
+
11
+ """
12
+ Modified from Mpsqd (https://github.com/qiangshi-group/MPSQD)
13
+
14
+ The MIT License (MIT)
15
+ Copyright (c) 2020 mpsqd team, Qiang Shi group
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining a copy
18
+ of this software and associated documentation files (the "Software"), to deal
19
+ in the Software without restriction, including without limitation the rights
20
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21
+ copies of the Software, and to permit persons to whom the Software is
22
+ furnished to do so, subject to the following conditions:
23
+
24
+ The above copyright notice and this permission notice shall be included in all
25
+ copies or substantial portions of the Software.
26
+
27
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
+ SOFTWARE.
34
+
35
+ By Weizhong Guan; Peng Bao; Jiawei Peng; Zhenggang Lan and Qiang Shi
36
+ J. Chem. Phys. 161, 122501 (2024)
37
+ Changes:
38
+ add rk4slices as argument (for update_type='rk4')
39
+ """
40
+ def tdvp1site(rin,pall,dt,update_type='krylov',mmax=30,nsteps=1,rk4slices = 10):
41
+ argdict = {'dt':dt,'mmax':mmax,'nsteps':nsteps,'update_type':update_type, 'rk4slices':rk4slices}
42
+ try:
43
+ for key in ['nsteps','mmax']:
44
+ argdict[key] = int(argdict[key])
45
+ for key in ['dt']:
46
+ argdict[key] = float(argdict[key])
47
+ for key in ['dt','nsteps','mmax']:
48
+ if (argdict[key] <= 0):
49
+ print("Wrong value in tdvp: ",key," = ",argdict[key])
50
+ sys.exit()
51
+ key = 'update_type'
52
+ argdict[key] = argdict[key].lower()
53
+ if (not argdict[key] in ['krylov','rk4']):
54
+ print("Wrong value in tdvp: update_type = ",update_type)
55
+ sys.exit()
56
+ if(rin.__class__.__name__ != "MPS"):
57
+ print("Wrong value in tdvp: improper MPS")
58
+ sys.exit()
59
+ if(pall.__class__.__name__ != "MPO"):
60
+ print("Wrong value in tdvp: improper MPO")
61
+ sys.exit()
62
+ except (ValueError):
63
+ print("Wrong value in tdvp: ",key," = ",argdict[key])
64
+ sys.exit()
65
+ for istep in range(nsteps):
66
+ rin = _tdvp1(rin,pall,argdict['dt'],argdict['update_type'],argdict['mmax'],argdict['rk4slices'])
67
+ return rin
68
+
69
+
70
+ """
71
+ Modified from Mpsqd (https://github.com/qiangshi-group/MPSQD)
72
+
73
+ The MIT License (MIT)
74
+ Copyright (c) 2020 mpsqd team, Qiang Shi group
75
+
76
+ Permission is hereby granted, free of charge, to any person obtaining a copy
77
+ of this software and associated documentation files (the "Software"), to deal
78
+ in the Software without restriction, including without limitation the rights
79
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
80
+ copies of the Software, and to permit persons to whom the Software is
81
+ furnished to do so, subject to the following conditions:
82
+
83
+ The above copyright notice and this permission notice shall be included in all
84
+ copies or substantial portions of the Software.
85
+
86
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
87
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
88
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
89
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
90
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
91
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
92
+ SOFTWARE.
93
+
94
+ By Weizhong Guan; Peng Bao; Jiawei Peng; Zhenggang Lan and Qiang Shi
95
+ J. Chem. Phys. 161, 122501 (2024)
96
+ Changes:
97
+ add rk4slices as argument (for update_type='rk4')
98
+ """
99
+ def _tdvp1(rin,pall,dt,update_type='krylov',mmax=30, rk4slices = 10):
100
+
101
+ dt2 = 0.5*dt
102
+ if(update_type=='krylov'):
103
+ update_v = functools.partial(ttf.expmv,mmax=mmax,dt=dt2)
104
+ else:
105
+ update_v = functools.partial(ttf.update_rk4,dt=dt2,rk4slices=rk4slices)
106
+
107
+ nlen = len(rin.nodes)
108
+ r1 = MPS(nlen,rin.nb)
109
+ r2 = MPS(nlen,rin.nb)
110
+ r3 = MPS(nlen,rin.nb)
111
+
112
+ phia = []
113
+
114
+ # phia[nlevel+2]
115
+ vtmp = np.ones((1,1,1),dtype=np.complex128)
116
+ phia.append(vtmp)
117
+
118
+ #======================================================
119
+ # ortho from right
120
+ r, q = split_rq(rin.nodes[nlen-1])
121
+
122
+ r1.nodes.append(q)
123
+ u1 = r
124
+ # phia[nlevel+1]
125
+ phia.append(ttf.phia_next(phia[0],pall.nodes[nlen-1],q,q,1))
126
+ #-------------------------------------------------------
127
+ # intermediate terms
128
+ for i in range(nlen-2,0,-1):
129
+
130
+ rtmp = np.tensordot(rin.nodes[i], u1, axes=((2),(0)))
131
+
132
+ r, q = split_rq(rtmp)
133
+ r1.nodes.append(q)
134
+ u1 = r
135
+
136
+ phia.append(ttf.phia_next(phia[nlen-1-i],pall.nodes[i],q,q,1))
137
+
138
+ # the left matrix
139
+
140
+ rtmp = np.tensordot(rin.nodes[0], u1, axes=((2),(0)))
141
+ r1.nodes.append(rtmp)
142
+
143
+ r1.nodes.reverse()
144
+
145
+ # add phia[0] and reverse to normal
146
+ vtmp = np.ones((1,1,1),dtype=np.complex128)
147
+ phia.append(vtmp)
148
+ phia.reverse()
149
+
150
+ #===============================================
151
+ ###### the first part of KSL, from left to right
152
+ for i in range(nlen-1):
153
+ phi1 = phia[i]
154
+ phi2 = phia[i+1]
155
+
156
+ if (i == 0):
157
+ ksol = r1.nodes[i].copy()
158
+ else:
159
+ ksol = r2.nodes[i].copy()
160
+
161
+
162
+ #for islice in range(pa.rk4slice):
163
+ ksol = update_v(yy=ksol, phi1=phi1, phi2=phi2, mat1=pall.nodes[i])
164
+ q, r = split_qr(ksol)
165
+
166
+
167
+ if (i == 0):
168
+ r2.nodes.append(q)
169
+ else:
170
+ r2.nodes[i] = q
171
+
172
+
173
+ phi1 = ttf.phia_next(phi1,pall.nodes[i],q,q,0)
174
+ phia[i+1] = phi1
175
+
176
+ # ?? need to copy
177
+ ssol = r
178
+ ssol = update_v(yy=ssol, phi1=phi1, phi2=phi2)
179
+
180
+ rtmp1 = np.tensordot(ssol,r1.nodes[i+1],axes=((1),(0)))
181
+ r2.nodes.append(rtmp1)
182
+
183
+ #--------------------------------------------------------
184
+ ### right most part
185
+
186
+ phi1 = phia[nlen-1]
187
+ phi2 = phia[nlen]
188
+ ksol = r2.nodes[nlen-1].copy()
189
+ ksol = update_v(yy=ksol, phi1=phi1, phi2=phi2, mat1=pall.nodes[nlen-1])
190
+
191
+ r2.nodes[nlen-1] = ksol
192
+ #===================================================================
193
+ ###### the second part of KSL, from right to left
194
+ for i in range(nlen-1,0,-1):
195
+
196
+ phi1 = phia[i]
197
+ phi2 = phia[i+1]
198
+
199
+ if (i==nlen-1):
200
+ ksol = r2.nodes[i].copy()
201
+ else:
202
+ ksol = r3.nodes[nlen-1-i].copy()
203
+
204
+ #for islice in range(pa.rk4slice):
205
+ ksol = update_v(yy=ksol, phi1=phi1, phi2=phi2, mat1=pall.nodes[i])
206
+ r, q = split_rq(ksol)
207
+
208
+ if (i==nlen-1):
209
+ r3.nodes.append(q)
210
+ else:
211
+ r3.nodes[nlen-1-i] = q
212
+
213
+ phi2 = ttf.phia_next(phi2,pall.nodes[i],q,q,1)
214
+ phia[i] = phi2
215
+
216
+ ssol = r
217
+
218
+ #for islice in range(pa.rk4slice):
219
+ ssol = update_v(yy=ssol, phi1=phi1, phi2=phi2)
220
+
221
+ rtmp1 = np.tensordot(r2.nodes[i-1],ssol,axes=((2),(0)))
222
+ r3.nodes.append(rtmp1)
223
+
224
+ r3.nodes.reverse()
225
+ ### the left most matrix
226
+ phi1 = phia[0]
227
+ phi2 = phia[1]
228
+ ksol = r3.nodes[0].copy()
229
+ ksol = update_v(yy=ksol, phi1=phi1, phi2=phi2, mat1=pall.nodes[0])
230
+
231
+ r3.nodes[0] = ksol
232
+
233
+ return r3
qflux/GQME/tt_tfd.py ADDED
@@ -0,0 +1,448 @@
1
+ """
2
+ This section uses some MPSQD functions to perform TT-TFD calculations for the Spin-Boson model dynamics.
3
+ Created by Xiaohan Dan
4
+ """
5
+
6
+ import numpy as np
7
+ from . import params as pa
8
+ import sys
9
+
10
+ from mpsqd.utils import MPS,add_tensor, MPS2MPO, calc_overlap
11
+ from .tdvp import tdvp1site
12
+ import time
13
+
14
+
15
+ def initial(istate: int) -> MPS:
16
+ """
17
+ Initialize the state in the tensor train format for TT-TFD calculation.
18
+
19
+ Args:
20
+ istate (int): The initial state type to be used:
21
+ - 0: Spin-Up state.
22
+ - 1: Equal superposition of spin-Up and spin-Down.
23
+ - 2: Superposition of spin-Up and 1j * spin-Down.
24
+ - 3: Spin-Down state.
25
+
26
+ Returns:
27
+ MPS: The initialized MPS object with the specified initial state.
28
+ """
29
+
30
+ #initial state
31
+ #Build initial ground state at spin-Up state
32
+ su = np.zeros((1,pa.DOF_E,pa.MAX_TT_RANK),dtype=np.complex128)
33
+ sd = np.zeros((1,pa.DOF_E,pa.MAX_TT_RANK),dtype=np.complex128)
34
+
35
+ su[0,:,0] = pa.spin_up
36
+ sd[0,:,0] = pa.spin_down
37
+
38
+ e1 = np.sqrt(0.5) * (su + sd)
39
+ e2 = np.sqrt(0.5) * (su + 1j * sd)
40
+
41
+ # initial MPS wavepacket
42
+ nbarr = np.ones((1+2*pa.DOF_N))*pa.occ
43
+ nbarr[0] = pa.DOF_E
44
+ y0 = MPS(1+2*pa.DOF_N,nb=nbarr)
45
+
46
+ if(istate==0):
47
+ y0.nodes.append(su)
48
+ elif(istate==1):
49
+ y0.nodes.append(e1)
50
+ elif(istate==2):
51
+ y0.nodes.append(e2)
52
+ elif(istate==3):
53
+ y0.nodes.append(sd)
54
+
55
+ gs = np.zeros((pa.MAX_TT_RANK,pa.occ,pa.MAX_TT_RANK),dtype=np.complex128)
56
+ gs[0,0,0] = 1.
57
+ for k in range(2 * pa.DOF_N-1): # double space formation
58
+ y0.nodes.append(gs)
59
+ gs = np.zeros((pa.MAX_TT_RANK,pa.occ,1),dtype=np.complex128)
60
+ gs[0,0,0] = 1.
61
+ y0.nodes.append(gs)
62
+
63
+ return y0
64
+
65
+
66
+ def construct_Hamil(eps: float = 1E-14) -> MPS:
67
+ """
68
+ Construct the effective Hamiltonian -iH for TT-TFD calculation in the Spin-Boson model with an Ohmic spectral density.
69
+
70
+ Args:
71
+ eps (float, optional): The truncation precision for the effective Hamiltonian. Default is 1E-14.
72
+
73
+ Returns:
74
+ MPS: The MPO object representing the effective Hamiltonian.
75
+ """
76
+
77
+ om = pa.OMEGA_C / pa.DOF_N * (1 - np.exp(-pa.OMEGA_MAX/pa.OMEGA_C))
78
+
79
+ # initialize arrays for parameters
80
+ freq = np.zeros((pa.DOF_N)) # frequency
81
+ ck = np.zeros((pa.DOF_N)) # linear electron-phonon coupling constant
82
+ gk = np.zeros((pa.DOF_N)) # ck in occupation number representation
83
+ thetak = np.zeros((pa.DOF_N)) # temperature-dependent mixing parameter in TFD
84
+ sinhthetak = np.zeros((pa.DOF_N)) # sinh(theta)
85
+ coshthetak = np.zeros((pa.DOF_N)) # cosh(theta)
86
+ for i in range(pa.DOF_N):
87
+ freq[i] = -pa.OMEGA_C * np.log(1-(i+1) * om/(pa.OMEGA_C)) # Ohmic frequency
88
+ ck[i] = np.sqrt(pa.XI * om) * freq[i] #Ohmic coupling constant
89
+ gk[i] = -ck[i] / np.sqrt(2 * freq[i]) #Transfer ck to occ. num. representation
90
+
91
+ thetak[i] = np.arctanh(np.exp(-pa.BETA * freq[i]/2)) #theta, defined for harmonic models
92
+ sinhthetak[i] = np.sinh(thetak[i]) #sinh(theta)
93
+ coshthetak[i] = np.cosh(thetak[i]) #cosh(theta)
94
+
95
+ #MPO of identity for all nuclear DOFs
96
+ ident_nuclear = tt_eye(pa.DOF_N*2, pa.occ)
97
+
98
+ # constructing Pauli operators
99
+ px = np.array([[0.0,1],[1,0]],dtype=np.complex128)
100
+ pz = np.array([[1.0,0],[0,-1]],dtype=np.complex128)
101
+ # Build electronic site energy matrix
102
+ He = pa.EPSILON * pz + pa.GAMMA_DA * px
103
+ # TT-ize that energy matrix
104
+ tt_He = tt_matrix(He)
105
+ tt_He = tt_kron(tt_He, ident_nuclear)
106
+
107
+
108
+
109
+ # Build number operator, corresponds to harmonic oscillator Hamiltonian
110
+ numoc = np.diag(np.arange(0, pa.occ, 1,dtype=np.complex128))
111
+ # Initiate the TT-ized number operator as a zero TT array with shape of occ^N
112
+ tt_numoc = tt_eye(pa.DOF_N,pa.occ)
113
+ for i in range(pa.DOF_N): tt_numoc.nodes[i] *= 0.0
114
+
115
+ # Construct number operator as TT
116
+ for k in range(pa.DOF_N):
117
+ tmp0 = tt_matrix(numoc)
118
+ tmp0.nodes[0] *= freq[k]
119
+ if k == 0:
120
+ tmp = tt_kron(tmp0, tt_eye(pa.DOF_N-1, pa.occ))
121
+ elif 0 < k < pa.DOF_N-1:
122
+ tmp = tt_kron(tt_eye(k-1,pa.occ), tmp0)
123
+ tmp = tt_kron(tmp,tt_eye(pa.DOF_N - k,pa.occ))
124
+ else:
125
+ tmp = tt_kron(tt_eye(k, pa.occ),tmp0)
126
+ tt_numoc = add_tensor(tt_numoc, tmp,small=eps)
127
+
128
+
129
+ # Ensure correct dimensionality
130
+ tt_Ie = tt_matrix(np.eye(2,dtype=np.complex128))
131
+ tt_systemnumoc = tt_kron(tt_Ie, tt_numoc)
132
+ tt_systemnumoc = tt_kron(tt_systemnumoc, tt_eye(pa.DOF_N,pa.occ))
133
+
134
+ # create a duplicate of number operator for the ficticious system
135
+ tt_tildenumoc = tt_kron(tt_Ie, tt_eye(pa.DOF_N,pa.occ))
136
+ tt_tildenumoc = tt_kron(tt_tildenumoc, tt_numoc)
137
+
138
+ thetak = np.zeros((pa.DOF_N)) #temperature-dependent mixing parameter in TFD
139
+ sinhthetak = np.zeros((pa.DOF_N)) #sinh(theta)
140
+ coshthetak = np.zeros((pa.DOF_N)) #cosh(theta)
141
+ for i in range(pa.DOF_N):
142
+ thetak[i] = np.arctanh(np.exp(-pa.BETA * freq[i]/2)) #theta, defined for harmonic models
143
+ sinhthetak[i] = np.sinh(thetak[i]) #sinh(theta)
144
+ coshthetak[i] = np.cosh(thetak[i]) #cosh(theta)
145
+
146
+ #Build displacement operator, corresponds to x operator in real space
147
+ eneroc = np.zeros((pa.occ, pa.occ),dtype=np.complex128)
148
+ for i in range(pa.occ - 1):
149
+ eneroc[i,i+1] = np.sqrt(i+1)
150
+ eneroc[i+1,i] = eneroc[i,i+1]
151
+
152
+ # initialize displacement operator
153
+ tt_energy = tt_eye(pa.DOF_N,pa.occ)
154
+ for i in range(pa.DOF_N): tt_energy.nodes[i] *= 0.0
155
+
156
+ for k in range(pa.DOF_N):
157
+ tmp0 = tt_matrix(eneroc)
158
+ tmp0.nodes[0] *= gk[k] * coshthetak[k]
159
+ if k == 0:
160
+ # coshtheta takes account for energy flow from real to ficticious system
161
+ # thus takes account for temperature effect
162
+ tmp = tt_kron(tmp0, tt_eye(pa.DOF_N-1, pa.occ))
163
+ elif 0 < k < pa.DOF_N - 1:
164
+ tmp = tt_kron(tt_eye(k-1,pa.occ), tmp0)
165
+ tmp = tt_kron(tmp,tt_eye(pa.DOF_N - k,pa.occ))
166
+ else:
167
+ tmp = tt_kron(tt_eye(k, pa.occ), tmp0)
168
+
169
+ tt_energy = add_tensor(tt_energy, tmp, small=eps)
170
+
171
+ tt_systemenergy = tt_kron(tt_matrix(pz), tt_energy)
172
+ tt_systemenergy = tt_kron(tt_systemenergy, tt_eye(pa.DOF_N, pa.occ))
173
+
174
+
175
+ # initialize displacement operator
176
+ tt_tilenergy = tt_eye(pa.DOF_N,pa.occ)
177
+ for i in range(pa.DOF_N): tt_tilenergy.nodes[i] *= 0.0
178
+
179
+ for k in range(pa.DOF_N):
180
+ tmp0 = tt_matrix(eneroc)
181
+ tmp0.nodes[0] *= gk[k] * sinhthetak[k]
182
+ if k == 0:
183
+ tmp = tt_kron(tmp0 , tt_eye(pa.DOF_N-1, pa.occ))
184
+ elif 0 < k < pa.DOF_N - 1:
185
+ tmp = tt_kron(tt_eye(k-1,pa.occ), tmp0)
186
+ tmp = tt_kron(tmp, tt_eye(pa.DOF_N - k,pa.occ))
187
+ else:
188
+ tmp = tt_kron(tt_eye(k, pa.occ), tmp0)
189
+ tt_tilenergy = add_tensor(tt_tilenergy, tmp,small=eps)
190
+
191
+ tt_tildeenergy = tt_kron(tt_matrix(pz), tt_eye(pa.DOF_N, pa.occ))
192
+ tt_tildeenergy = tt_kron(tt_tildeenergy, tt_tilenergy)
193
+
194
+ #The total propogation Hamiltonian
195
+ # Note that ficticious Harmonic oscillators carry negative sign
196
+ H = add_tensor(tt_He, tt_systemnumoc, small=eps)
197
+ H = add_tensor(H, tt_tildenumoc, coeff = -1.0, small=eps)
198
+ H = add_tensor(H, tt_systemenergy, coeff = 1.0, small=eps)
199
+ H = add_tensor(H, tt_tildeenergy, coeff = 1.0, small=eps)
200
+
201
+ # Construct propagation operator, d/dt psi(t0)=-1j H psi(t0)
202
+ H.nodes[0] *= -1j
203
+
204
+ # convert to MPO
205
+ A = MPS2MPO(H).truncation(small=eps)
206
+ return A
207
+
208
+
209
+ def tt_eye(length: int, mode_dim: int) -> MPS:
210
+ """
211
+ Generate an identity MPO (in the form of MPS) with rank-1.
212
+
213
+ Args:
214
+ length (int): The number of MPS nodes.
215
+ mode_dim (int): The size of the physical index.
216
+
217
+ Returns:
218
+ MPS: The resulting MPS representation of the identity matrix.
219
+ """
220
+
221
+ nb_arr = np.ones(length,dtype=int)*mode_dim
222
+ y0 = MPS(length,nb_arr**2)
223
+
224
+ for i in range(length):
225
+ identy = np.eye(nb_arr[i],dtype=np.complex128)
226
+ y0.nodes.append(identy.reshape((1,nb_arr[i]**2,1),order='F'))
227
+
228
+ return y0
229
+
230
+
231
+ def tt_matrix(array: np.ndarray) -> MPS:
232
+ """
233
+ Generate a tensor train (in the form of MPS) from the input array.
234
+ Now the input array should be a square matrix.
235
+
236
+ Args:
237
+ array (np.ndarray): A 2D square matrix to be converted into a tensor train.
238
+
239
+ Returns:
240
+ MPS: The resulting tensor train (MPS) representation of the input array.
241
+
242
+ Raises:
243
+ SystemExit: If the input array is not square.
244
+ """
245
+ M, N = array.shape
246
+ if(M!=N):
247
+ print("array shape not matched")
248
+ sys.exit()
249
+
250
+ y0 = MPS(1,np.array([M**2],dtype=int))
251
+ y0.nodes.append(array.reshape((1,M**2,1),order='F'))
252
+
253
+ return y0
254
+
255
+ def tt_kron(mps1: MPS, mps2: MPS) -> MPS:
256
+ """
257
+ Compute the Kronecker product of two MPS (Matrix Product States).
258
+
259
+ This function combines two MPS objects into a new MPS which represents the Kronecker
260
+ product of the two input MPS objects.
261
+
262
+ Args:
263
+ mps1 (MPS): The first Matrix Product State.
264
+ mps2 (MPS): The second Matrix Product State.
265
+
266
+ Returns:
267
+ MPS: A new MPS object which is the Kronecker product of mps1 and mps2.
268
+ """
269
+ new_length = mps1.length + mps2.length
270
+
271
+ new_nb = np.empty(new_length,dtype=int)
272
+
273
+ new_mps = MPS(new_length,new_nb)
274
+
275
+ for i in range(mps1.length):
276
+ new_mps.nb[i] = mps1.nb[i]
277
+ new_mps.nodes.append(mps1.nodes[i])
278
+ for i in range(mps2.length):
279
+ new_mps.nb[i+mps1.length] = mps2.nb[i]
280
+ new_mps.nodes.append(mps2.nodes[i])
281
+
282
+ return new_mps
283
+
284
+ def tt_ones(length: int, mode_dim: int) -> MPS:
285
+ """
286
+ Generate an MPS with rank-1, where all the elements are set to one.
287
+
288
+ Args:
289
+ length (int): The number of MPS nodes (the length of the tensor chain).
290
+ mode_dim (int): The size of the physical index for each node in the MPS.
291
+
292
+ Returns:
293
+ MPS: The MPS object with the specified number of nodes, each having rank-1 and elements initialized to one.
294
+ """
295
+
296
+ nb_arr = np.ones(length,dtype=int)*mode_dim
297
+ y0 = MPS(length,nb=nb_arr)
298
+
299
+ for i in range(length):
300
+ tmp = np.ones((1,nb_arr[i],1),dtype=np.complex128)
301
+ y0.nodes.append(tmp)
302
+
303
+ return y0
304
+
305
+ def cal_property(mps0: MPS) -> np.ndarray:
306
+ """
307
+ Calculate the population and coherence for the spin-boson model.
308
+
309
+ Args:
310
+ mps0 (MPS): The wavefunction MPS representing the state.
311
+
312
+ Returns:
313
+ np.ndarray: An array containing the population and coherence values.
314
+ """
315
+
316
+ # Initialize the sigma array to hold the results
317
+ sigma_arr = np.zeros(pa.DOF_E_SQ,dtype=np.complex128)
318
+
319
+ # Define the spin-up and spin-down states
320
+ su = pa.spin_up
321
+ sd = pa.spin_down
322
+
323
+ # Create copies of the original MPS for the calculations
324
+ mps_up = mps0.copy()
325
+ mps_down = mps0.copy()
326
+
327
+ #ul = np.array([[1,0],[0,0]])
328
+ ur = np.array([[0,1],[0,0]])
329
+ mps_ur = mps0.copy()
330
+
331
+ # Calculate the overlaps for population and coherence
332
+ mps_up.nodes[0] = np.multiply(mps_up.nodes[0], su.reshape((1,-1,1)))
333
+ mps_down.nodes[0] = np.multiply(mps_down.nodes[0], sd.reshape(1,-1,1))
334
+ mps_ur.nodes[0] = (np.tensordot(ur, mps_ur.nodes[0],axes=((1),(1)))).transpose(1,0,2)
335
+ sigma_arr[0] = calc_overlap(mps_up, mps_up)
336
+ sigma_arr[3] = calc_overlap(mps_down, mps_down)
337
+ sigma_arr[2] = calc_overlap(mps_up,mps_ur)
338
+ sigma_arr[1] = calc_overlap(mps_ur,mps_up)
339
+ return sigma_arr
340
+
341
+
342
+ def multiply_mps(mps1: MPS, mps2: MPS) -> MPS:
343
+ """
344
+ Perform element-wise multiplication of two MPS.
345
+
346
+ Args:
347
+ mps1 (MPS): The first MPS object.
348
+ mps2 (MPS): The second MPS object.
349
+
350
+ Returns:
351
+ MPS: A new MPS object resulting from the element-wise multiplication of the two MPS.
352
+ """
353
+
354
+ nlen1 = len(mps1.nodes)
355
+ nlen2 = len(mps2.nodes)
356
+ if(nlen1!=nlen2):
357
+ raise ValueError('MPS lengths do not match! Unable to perform element-wise multiplication.')
358
+ nb1 = mps1.nb
359
+ nb2 = mps2.nb
360
+
361
+ new_mps = MPS(nlen1,nb1)
362
+
363
+ for i in range(nlen1):
364
+ if(nb1[i]!=nb2[i]):
365
+ raise ValueError(f'Physical indices do not match for node {i}!')
366
+ ra1,ra2,ra3 = mps1.nodes[i].shape
367
+ rb1,rb2,rb3 = mps2.nodes[i].shape
368
+
369
+ array_tmp = np.einsum('ijk,ljm->iljkm',mps1.nodes[i],mps2.nodes[i]).reshape((ra1*rb1,nb1[i],ra3*rb3))
370
+
371
+ new_mps.nodes.append(array_tmp)
372
+ return new_mps.truncation(small=pa.eps)
373
+
374
+
375
+
376
+ def tt_tfd(
377
+ initial_state: int,
378
+ update_type: str = 'rk4',
379
+ rk4slices: int = 1,
380
+ mmax: int = 4,
381
+ RDO_arr_bench: np.ndarray = None,
382
+ show_steptime: bool = False
383
+ ) -> tuple[np.ndarray, np.ndarray]:
384
+ """
385
+ Perform the TT-TFD calculation.
386
+
387
+ Args:
388
+ initial_state (int): The initial state of the system. This determines the starting state for the calculation.
389
+ update_type (str, optional): The method used for updating each core of the MPS. Can be either 'rk4' (Runge-Kutta 4th order) or 'krylov'. Default is 'rk4'.
390
+ rk4slices (int, optional): The number of time slices for 'rk4' method when performing the update. Default is 1.
391
+ mmax (int, optional): The size of the Krylov subspace when using the 'krylov' method. Default is 4.
392
+ RDO_arr_bench (np.ndarray, optional): If provided, the function will compare the calculated reduced density matrix (RDO) at each step with this benchmark array. Default is None.
393
+ show_steptime (bool, optional): If True, the function will print the time taken for each propagation step. Default is False.
394
+
395
+ Returns:
396
+ tuple[np.ndarray, np.ndarray]:
397
+ A tuple containing:
398
+ - A time array (`t`) representing the simulation times.
399
+ - The reduced density matrix (`RDO_arr`) over time.
400
+ """
401
+
402
+ y0 = initial(initial_state)
403
+
404
+
405
+ A = construct_Hamil(eps=pa.eps)
406
+
407
+ RDO_arr = np.zeros((pa.TIME_STEPS, pa.DOF_E_SQ), dtype=np.complex128)
408
+ t = np.arange(0, pa.TIME_STEPS * pa.DT, pa.DT)
409
+
410
+ # Propagation loop
411
+ START_TIME = time.time()
412
+ print('Start doing propagation')
413
+
414
+ for ii in range(pa.TIME_STEPS):
415
+
416
+ print(ii,t[ii])
417
+ STEP_TIME = time.time()
418
+
419
+ #Doing TDVP
420
+ y0 = tdvp1site(y0, A, pa.DT, update_type=update_type,mmax=mmax,rk4slices=rk4slices)
421
+ #Calculating the Reduced density matrix
422
+ RDO_arr[ii] = cal_property(y0)
423
+
424
+ STEP_TIME2 = time.time()
425
+ if(RDO_arr_bench is not None):
426
+ compare_diff(RDO_arr[ii], RDO_arr_bench[ii])
427
+
428
+ if(show_steptime):
429
+ print('timefor tdvp:',STEP_TIME2-STEP_TIME)
430
+
431
+ print("\tPropagation time:", time.time() - START_TIME)
432
+
433
+ return t,RDO_arr
434
+
435
+
436
+
437
+ def compare_diff(vec1,vec2):
438
+ """
439
+ testing function for comparing the two vectors
440
+ """
441
+
442
+ sr = 0.0; si = 0.0
443
+ sr = np.max(np.abs(vec1.real-vec2.real))
444
+ si = np.max(np.abs(vec1.imag-vec2.imag))
445
+ print('error, real', sr, 'imag', si)
446
+ #print('vec1',vec1)
447
+ #print('vec2',vec2)
448
+
qflux/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from . import closed_systems
2
+ from . import open_systems
3
+ from . import variational_methods
4
+ from . import GQME
5
+ from . import utils
@@ -0,0 +1,17 @@
1
+ """
2
+
3
+ qflux v.0.1.0
4
+
5
+ __version__ = 0.1.0
6
+
7
+ Description:
8
+
9
+ classical_methods.py --> DynamicsCS : Closed-System dynamics
10
+ qubit_methods.py --> QubitDynamicsCS : Qubit-based dynamics
11
+ spin_dynamics_oo.py --> SpinDynamicsS, SpinDynamicsH : Statevector and hadamard-test implementations (respectively) for spin-chain systems
12
+
13
+ """
14
+
15
+ from .classical_methods import DynamicsCS
16
+ from .qubit_methods import QubitDynamicsCS
17
+ from .spin_dynamics_oo import SpinDynamicsS, SpinDynamicsH