Skip to content

Commit c30cf20

Browse files
committed
[test] analytic diagrams test order 1, 2, and 3
1 parent f19e187 commit c30cf20

2 files changed

Lines changed: 348 additions & 0 deletions

File tree

test/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ set(all_tests
1515
h5_io
1616
triqs_solver
1717
one_spinful_fermion
18+
analytic_diagrams
1819
)
1920

2021
foreach(test ${all_tests})

test/python/analytic_diagrams.py

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
################################################################################
2+
#
3+
# triqs_soehyb: Sum-Of-Exponentials bold HYBridization expansion impurity solver
4+
#
5+
# Copyright (C) 2025 by H. U.R. Strand
6+
#
7+
# triqs_soehyb is free software: you can redistribute it and/or modify it under the
8+
# terms of the GNU General Public License as published by the Free Software
9+
# Foundation, either version 3 of the License, or (at your option) any later
10+
# version.
11+
#
12+
# triqs_soehyb is distributed in the hope that it will be useful, but WITHOUT ANY
13+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15+
# details.
16+
#
17+
# You should have received a copy of the GNU General Public License along with
18+
# triqs_soehyb. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
################################################################################
21+
22+
23+
""" Test diagram evaluation for simple case that can be treated analytically.
24+
25+
Letting the local Hamiltonian being H_loc = 0 gives a degenerate atomic
26+
pseudo-particle propagator
27+
28+
G(\tau) = - exp(-alpha * tau)
29+
30+
with alpha = log(N) / beta where N is the size of the local Hilbert space.
31+
32+
Since G is a simple exponential it factorizes out of all self-energy and
33+
single particle diagrams.
34+
35+
The hybridization is set to Delta(\tau) = -0.5
36+
37+
(corresponding to a bath level at zero energy)
38+
39+
In this case the diagram integrations become simple polynomials in tau
40+
41+
e.g. the OCA diagram has the form
42+
43+
\Sigma_{OCA} = \sum_{abcd} \int_{\tau}^\beta d\tau_1 \int_0^{\tau_1} d\tau_2
44+
\Delta_{ab}(\tau - \tau_2) \Delta_{cd}(\tau_1 - 0) *
45+
[ O_a G(\tau - \tau_1) O_c G(\tau_1 - \tau_2) O_b G(\tau_2 - 0) O_d ]
46+
47+
which simplifies to
48+
49+
\Sigma_{OCA} = (-1/2)^2 G(\tau) ( \sum_{abcd} [O_a O_c O_b O_d] )
50+
\int_{\tau}^\beta d\tau_1 \int_0^{\tau_1} d\tau_2
51+
52+
where the integrals produce the polynomial
53+
54+
\int_{\tau}^\beta d\tau_1 \int_0^{\tau_1} d\tau_2 = \tau^2 / 2
55+
56+
The sum over operators give a combinatorical factor
57+
58+
A = \sum_{abcd} [O_a O_c O_b O_d] = (n - 1) n
59+
60+
that depends on the number of orbitals n.
61+
62+
So in total the OCA self-energy has the form
63+
64+
\Sigma_{OCA} = (-1/2)^2 G(\tau) n (n - 1) \tau^2 / 2
65+
66+
Below this analysis is performed also for the single particle Green's function
67+
diagrams up to third order.
68+
69+
"""
70+
71+
72+
import numpy as np
73+
74+
from mpi4py import MPI
75+
76+
from triqs.gf import make_gf_dlr_imtime, make_gf_dlr_imfreq, inverse, iOmega_n
77+
from triqs.operators import c, c_dag, Operator
78+
79+
from triqs_soehyb.solver import Solver, is_root
80+
from triqs_soehyb.triqs_solver import TriqsSolver
81+
82+
83+
def test_n_fermions(n, verbose):
84+
85+
gf_struct = [('0', n)]
86+
h_int = 0.0 * Operator()
87+
88+
analytic_diagram_cf(h_int, gf_struct, verbose)
89+
90+
91+
def analytic_diagram_cf(h_int, gf_struct, verbose):
92+
93+
S = TriqsSolver(beta=2.0, gf_struct=gf_struct, eps=1e-12, w_max=10.0)
94+
95+
for bidx, delta_tau in S.Delta_tau:
96+
delta_w = make_gf_dlr_imfreq(delta_tau)
97+
delta_w << inverse(iOmega_n)
98+
delta_tau[:] = make_gf_dlr_imtime(delta_w)
99+
100+
S.solve(h_int=h_int, order=1, tol=1e-9, maxiter=0)
101+
102+
g_iaa_nca = S.S.calc_spgf(max_order=1, verbose=True)
103+
g_iaa_oca = S.S.calc_spgf(max_order=2, verbose=True)
104+
g_iaa_tca = S.S.calc_spgf(max_order=3, verbose=True)
105+
106+
dg_iaa_oca = g_iaa_oca - g_iaa_nca
107+
dg_iaa_tca = g_iaa_tca - g_iaa_oca
108+
109+
Sigma_iaa_nca = S.S.calc_Sigma(max_order=1, verbose=True)
110+
Sigma_iaa_oca = S.S.calc_Sigma(max_order=2, verbose=True)
111+
Sigma_iaa_tca = S.S.calc_Sigma(max_order=3, verbose=True)
112+
113+
dSigma_iaa_oca = Sigma_iaa_oca - Sigma_iaa_nca
114+
dSigma_iaa_tca = Sigma_iaa_tca - Sigma_iaa_oca
115+
116+
def test_equal_diagonal(arr):
117+
assert(len(arr.shape) == 3)
118+
assert(arr.shape[1] == arr.shape[2])
119+
m = arr.shape[-1]
120+
121+
off_diag = arr.copy()
122+
for i in range(m):
123+
np.testing.assert_array_almost_equal(arr[:, i, i], arr[:, 0, 0])
124+
off_diag[:, i, i] -= arr[:, i, i]
125+
126+
np.testing.assert_array_almost_equal(off_diag, np.zeros_like(off_diag))
127+
128+
129+
for arr in [g_iaa_nca, g_iaa_oca, Sigma_iaa_nca, Sigma_iaa_oca]:
130+
test_equal_diagonal(arr)
131+
132+
# -- Analytic solution
133+
134+
n = len(S.S.fundamental_operators)
135+
N = S.S.H_mat.shape[0]
136+
137+
print(f'n = {n}, N = {N}')
138+
139+
I_n = np.eye(n)[None, :, :]
140+
I_N = np.eye(N)[None, :, :]
141+
142+
beta = S.beta
143+
tau = S.S.tau_i[:, None, None]
144+
145+
alpha = np.log(N) / beta
146+
G0_iaa_ref = -np.exp(-alpha * tau) * I_N
147+
148+
Sigma_iaa_nca_ref = -1 * (-0.5) * G0_iaa_ref * n
149+
150+
dSigma_iaa_oca_ref = (-0.5)**2 * (tau**2/2) * G0_iaa_ref * n * (n - 1)
151+
dSigma_iaa_tca_ref = (-0.5)**3 * tau**4/24 * G0_iaa_ref * (-func_K(n))
152+
153+
Sigma_iaa_oca_ref = Sigma_iaa_nca_ref + dSigma_iaa_oca_ref
154+
Sigma_iaa_tca_ref = Sigma_iaa_oca_ref + dSigma_iaa_tca_ref
155+
156+
g_iaa_nca_ref = -0.5 * I_n + 0 * tau
157+
158+
dg_iaa_oca_ref = - 0.5 * (beta - tau) * (tau - 0) * (n - 1) / 2 * I_n
159+
160+
C_1 = 0.5 * func_C_1(n)
161+
C_2 = 0.5 * func_C_2(n)
162+
C_3 = C_2
163+
164+
g_tca_contrib_1 = (beta**2 - 2*beta*tau + tau**2) * tau**2/4 * C_1
165+
g_tca_contrib_2 = (beta**3 - 3*beta**2*tau + 3*beta*tau**2 - tau**3) * tau / 6 * C_2
166+
g_tca_contrib_3 = (beta - tau) * tau**3 / 6 * C_3
167+
168+
dg_iaa_tca_ref = - (0.5)**2 * ( g_tca_contrib_1 + g_tca_contrib_2 + g_tca_contrib_3) * I_n
169+
170+
g_iaa_oca_ref = g_iaa_nca_ref + dg_iaa_oca_ref
171+
g_iaa_tca_ref = g_iaa_oca_ref + dg_iaa_tca_ref
172+
173+
174+
if verbose and is_root():
175+
176+
import matplotlib.pyplot as plt
177+
178+
plt.figure(figsize=(10, 8))
179+
180+
subp = [3, 3, 1]
181+
182+
plt.subplot(*subp); subp[-1] += 1
183+
184+
for i in range(n):
185+
plt.plot(S.S.tau_i, S.Delta_tau['0'].data[:, i, i].flatten().real, 'x-')
186+
plt.ylim([-.75, 0])
187+
plt.ylabel(r'$\Delta(\tau)$')
188+
189+
plt.subplot(*subp); subp[-1] += 1
190+
191+
#for i in range(N):
192+
for i in [0]:
193+
plt.plot(S.S.tau_i, S.S.G0_iaa[:, i, i].real, 'x-', label='G0')
194+
plt.plot(S.S.tau_i, G0_iaa_ref[:, i, i].real, '+-', label='G0 ref')
195+
196+
plt.legend(loc='best')
197+
plt.ylabel(r'$\hat{G}(\tau)$')
198+
199+
plt.subplot(*subp); subp[-1] += 1
200+
201+
plt.subplot(*subp); subp[-1] += 1
202+
203+
#for i in range(n):
204+
for i in [0]:
205+
plt.plot(S.S.tau_i, g_iaa_nca[:, i, i].flatten().real, '-', label='nca')
206+
plt.plot(S.S.tau_i, g_iaa_nca_ref[:, i, i].flatten().real, 'x', label='nca (ref)')
207+
208+
plt.legend(loc='best')
209+
#plt.ylim([-.75, 0])
210+
plt.ylabel(r'$g(\tau)$')
211+
212+
plt.subplot(*subp); subp[-1] += 1
213+
214+
#for i in range(n):
215+
for i in [0]:
216+
plt.plot(S.S.tau_i, dg_iaa_oca[:, i, i].flatten().real, '-', label='oca')
217+
plt.plot(S.S.tau_i, dg_iaa_oca_ref[:, i, i].flatten().real, '+', label='oca (ref)')
218+
219+
plt.legend(loc='best')
220+
#plt.ylim([-.75, 0])
221+
plt.ylabel(r'$g(\tau)$')
222+
223+
plt.subplot(*subp); subp[-1] += 1
224+
225+
#for i in range(n):
226+
for i in [0]:
227+
plt.plot(S.S.tau_i, dg_iaa_tca[:, i, i].flatten().real, '-', label='tca')
228+
plt.plot(S.S.tau_i, dg_iaa_tca_ref[:, i, i].flatten().real, '+', label='tca (ref)')
229+
230+
plt.legend(loc='best')
231+
#plt.ylim([-.75, 0])
232+
plt.ylabel(r'$g(\tau)$')
233+
234+
plt.subplot(*subp); subp[-1] += 1
235+
236+
#for i in range(N):
237+
for i in [0]:
238+
plt.plot(S.S.tau_i, Sigma_iaa_nca[:, i, i].real, 'x-', label='nca')
239+
plt.plot(S.S.tau_i, Sigma_iaa_nca_ref[:, i, i].real, '+-', label='nca ref')
240+
#plt.plot(TS.S.tau_i, TS.G_tau[0].data.flatten().real, 'x-', label='triqs')
241+
plt.legend(loc='best')
242+
plt.ylabel(r'$\hat{\Sigma}(\tau)$')
243+
244+
plt.subplot(*subp); subp[-1] += 1
245+
246+
#for i in range(N):
247+
for i in [0]:
248+
plt.plot(S.S.tau_i, dSigma_iaa_oca[:, i, i].real, 'x-', label='oca')
249+
plt.plot(S.S.tau_i, dSigma_iaa_oca_ref[:, i, i].real, '+-', label='oca ref')
250+
251+
plt.legend(loc='best')
252+
plt.ylabel(r'$\hat{\Sigma}_{OCA}(\tau)$')
253+
254+
plt.subplot(*subp); subp[-1] += 1
255+
256+
#for i in range(N):
257+
for i in [0]:
258+
plt.plot(S.S.tau_i, dSigma_iaa_tca[:, i, i].real, 'x-', label='tca')
259+
plt.plot(S.S.tau_i, dSigma_iaa_tca_ref[:, i, i].real, '+-', label='tca ref')
260+
261+
plt.legend(loc='best')
262+
plt.ylabel(r'$\hat{\Sigma}_{TCA}(\tau)$')
263+
264+
plt.tight_layout()
265+
plt.show()
266+
267+
268+
if is_root():
269+
np.testing.assert_array_almost_equal(S.S.G0_iaa, S.S.G_iaa)
270+
np.testing.assert_array_almost_equal(G0_iaa_ref, S.S.G0_iaa)
271+
272+
np.testing.assert_array_almost_equal(Sigma_iaa_nca, Sigma_iaa_nca_ref)
273+
np.testing.assert_array_almost_equal(Sigma_iaa_oca, Sigma_iaa_oca_ref)
274+
np.testing.assert_array_almost_equal(Sigma_iaa_tca, Sigma_iaa_tca_ref)
275+
276+
np.testing.assert_array_almost_equal(g_iaa_nca, g_iaa_nca_ref)
277+
np.testing.assert_array_almost_equal(g_iaa_oca, g_iaa_oca_ref)
278+
np.testing.assert_array_almost_equal(g_iaa_tca, g_iaa_tca_ref)
279+
280+
281+
def func_K(n):
282+
return n * (4*n**2 - 9*n + 4)
283+
284+
285+
def func_C_1(n):
286+
return (n - 2) * (n + 0) + (n - 1) * (n - 2)
287+
288+
289+
def func_C_2(n):
290+
return (n - 1) * (n - 1)
291+
292+
293+
def test_prefactors():
294+
295+
nK = [1, 2, 3, 4, 5, 6]
296+
K = [-1, 4, 39, 128, 295, 564]
297+
298+
pK = np.polyfit(nK, K, 3)
299+
print(f'pK = {pK}')
300+
301+
K_err = np.max(np.abs(np.polyval(pK, nK) - K))
302+
print(f'K_err = {K_err}')
303+
304+
nC = [1, 2, 3, 4, 5]
305+
C1 = [-1, 0, 5, 14, 27]
306+
C2 = [0, 1, 4, 9, 16]
307+
308+
p1 = np.polyfit(nC, C1, 2)
309+
p2 = np.polyfit(nC, C2, 2)
310+
311+
print(f'p1 = {p1}')
312+
print(f'p2 = {p2}')
313+
314+
315+
for n in [6]:
316+
print(f'n = {n}')
317+
print(f'C1 = {np.polyval(p1, n)}')
318+
print(f'C2 = {np.polyval(p2, n)}')
319+
320+
321+
import matplotlib.pyplot as plt
322+
323+
plt.plot(nK, K, 's', label='K')
324+
plt.plot(nC, C1, 'o', label='C1')
325+
plt.plot(nC, C2, '>', label='C2')
326+
327+
x = np.linspace(0, 5.5, num=400)
328+
329+
plt.plot(x, np.polyval(pK, x), ':', label='K fit')
330+
plt.plot(x, np.polyval(p1, x), ':', label='C1 fit')
331+
plt.plot(x, np.polyval(p2, x), ':', label='C2 fit')
332+
333+
plt.plot(x, func_K(x), '-', label='K func')
334+
plt.plot(x, func_C_1(x), '-', label='C1 func')
335+
plt.plot(x, func_C_2(x), '-', label='C2 func')
336+
337+
plt.legend(loc='best')
338+
plt.show()
339+
340+
341+
if __name__ == '__main__':
342+
343+
#test_prefactors(); exit()
344+
345+
for n in range(1, 4):
346+
test_n_fermions(n=n, verbose=False)
347+

0 commit comments

Comments
 (0)