adapol 0.1.0__tar.gz

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.
@@ -0,0 +1,8 @@
1
+ prune .git
2
+ prune .github
3
+ prune build
4
+ prune doc
5
+ prune example
6
+ prune test
7
+
8
+ global-exclude .gitignore pytest.ini
adapol-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.1
2
+ Name: adapol
3
+ Version: 0.1.0
4
+ Summary: Adaptive Pole Fitting for Quantum Many-Body Physics
5
+ Author-email: Zhen Huang <hertz@berkeley.edu>, Chia-Nan Yeh <cyeh@flatironinstitute.org>, Nils Wentzell <nwentzell@flatironinstitute.org>, Jason Kaye <jkaye@flatironinstitute.org>, Lin Lin <linlin@berkeley.edu>
6
+ Project-URL: Homepage, https://flatironinstitute.github.io/adapol
7
+ Project-URL: Bug Tracker, https://github.com/flatironinstitute/adapol/issues
8
+ Keywords: Bath,Fitting,Hybridization,DMFT,Matsubara
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: numpy
15
+ Requires-Dist: scipy
16
+ Requires-Dist: cvxpy
17
+
18
+ # adapol: Adaptive Pole Fitting for Quantum Many-Body Physics
19
+ [`adapol`](https://github.com/Hertz4/Adapol) (pronounced "add a pole") is a python package for fitting Matsubara functions with the following form:
20
+ ```math
21
+ G(\mathrm i \omega_k) = \sum_l \frac{V_lV_l^{\dagger}}{\mathrm i\omega_k - E_l}.
22
+ ```
23
+
24
+ Current applications include
25
+ (1) hybridization fitting, (2) analytic continuation.
26
+
27
+ We also provide a [TRIQS](https://triqs.github.io/) interface if the Matsubara functions are stored in `triqs` Green's function container.
28
+
29
+ # Installation
30
+ `adapol` has `numpy` and `scipy` as its prerequisites. [`cvxpy`](https://www.cvxpy.org/) is also required for hybridization fitting of matrix-valued (instead of scalar-valued) Matsubara functions.
31
+
32
+ To install `adapol`, run
33
+ ```terminal
34
+ pip install adapol
35
+ ```
36
+
37
+
38
+
39
+ # Documentation
40
+
41
+ See the detailed [documentation](https://flatironinstitute.github.io/adapol/) for physical background, algorithms and user manual.
42
+
43
+ `Adapol` is a stand-alone package. For TRIQS users, we also provide a TRIQS interface. See [user manual](https://flatironinstitute.github.io/adapol/latest/python.html#triqs-interface) for details.
44
+
45
+ # Examples
46
+ In the `tutorial` page, we provide two examples [`discrete.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/discrete.html) and [`semicircle.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/semicircle.html), showcasing how to use `adapol` for both discrete spectrum and continuous spectrum.
47
+
48
+ In these notebooks, we also demonstrate how to use our code through the triqs interface.
49
+
50
+ # References
51
+ To cite this work, please include a reference to this GitHub repository, and
52
+ cite the following references:
53
+
54
+ 1. Huang, Zhen, Emanuel Gull, and Lin Lin. "Robust analytic continuation of Green's functions via projection, pole estimation, and semidefinite relaxation." Physical Review B 107.7 (2023): 075151.
55
+ 2. Mejuto-Zaera, Carlos, et al. "Efficient hybridization fitting for dynamical mean-field theory via semi-definite relaxation." Physical Review B 101.3 (2020): 035143.
56
+ 3. Nakatsukasa, Yuji, Olivier Sète, and Lloyd N. Trefethen. "The AAA algorithm for rational approximation." SIAM Journal on Scientific Computing 40.3 (2018): A1494-A1522.
adapol-0.1.0/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # adapol: Adaptive Pole Fitting for Quantum Many-Body Physics
2
+ [`adapol`](https://github.com/Hertz4/Adapol) (pronounced "add a pole") is a python package for fitting Matsubara functions with the following form:
3
+ ```math
4
+ G(\mathrm i \omega_k) = \sum_l \frac{V_lV_l^{\dagger}}{\mathrm i\omega_k - E_l}.
5
+ ```
6
+
7
+ Current applications include
8
+ (1) hybridization fitting, (2) analytic continuation.
9
+
10
+ We also provide a [TRIQS](https://triqs.github.io/) interface if the Matsubara functions are stored in `triqs` Green's function container.
11
+
12
+ # Installation
13
+ `adapol` has `numpy` and `scipy` as its prerequisites. [`cvxpy`](https://www.cvxpy.org/) is also required for hybridization fitting of matrix-valued (instead of scalar-valued) Matsubara functions.
14
+
15
+ To install `adapol`, run
16
+ ```terminal
17
+ pip install adapol
18
+ ```
19
+
20
+
21
+
22
+ # Documentation
23
+
24
+ See the detailed [documentation](https://flatironinstitute.github.io/adapol/) for physical background, algorithms and user manual.
25
+
26
+ `Adapol` is a stand-alone package. For TRIQS users, we also provide a TRIQS interface. See [user manual](https://flatironinstitute.github.io/adapol/latest/python.html#triqs-interface) for details.
27
+
28
+ # Examples
29
+ In the `tutorial` page, we provide two examples [`discrete.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/discrete.html) and [`semicircle.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/semicircle.html), showcasing how to use `adapol` for both discrete spectrum and continuous spectrum.
30
+
31
+ In these notebooks, we also demonstrate how to use our code through the triqs interface.
32
+
33
+ # References
34
+ To cite this work, please include a reference to this GitHub repository, and
35
+ cite the following references:
36
+
37
+ 1. Huang, Zhen, Emanuel Gull, and Lin Lin. "Robust analytic continuation of Green's functions via projection, pole estimation, and semidefinite relaxation." Physical Review B 107.7 (2023): 075151.
38
+ 2. Mejuto-Zaera, Carlos, et al. "Efficient hybridization fitting for dynamical mean-field theory via semi-definite relaxation." Physical Review B 101.3 (2020): 035143.
39
+ 3. Nakatsukasa, Yuji, Olivier Sète, and Lloyd N. Trefethen. "The AAA algorithm for rational approximation." SIAM Journal on Scientific Computing 40.3 (2018): A1494-A1522.
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools.packages.find]
6
+ where = ["src"]
7
+
8
+ [project]
9
+ name = "adapol"
10
+ version = "0.1.0"
11
+ authors = [
12
+ { name="Zhen Huang", email="hertz@berkeley.edu" },
13
+ { name="Chia-Nan Yeh", email="cyeh@flatironinstitute.org"},
14
+ { name="Nils Wentzell", email="nwentzell@flatironinstitute.org"},
15
+ { name="Jason Kaye", email="jkaye@flatironinstitute.org"},
16
+ { name="Lin Lin", email="linlin@berkeley.edu"}
17
+ ]
18
+ description = "Adaptive Pole Fitting for Quantum Many-Body Physics "
19
+ readme = "README.md"
20
+ keywords = ["Bath", "Fitting", "Hybridization", "DMFT", "Matsubara"]
21
+ requires-python = ">=3.7"
22
+ classifiers = [
23
+ "Programming Language :: Python :: 3",
24
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
25
+ "Operating System :: OS Independent",
26
+ ]
27
+ dependencies = [
28
+ "numpy",
29
+ "scipy",
30
+ "cvxpy"
31
+ ]
32
+
33
+ [project.urls]
34
+ "Homepage" = "https://flatironinstitute.github.io/adapol"
35
+ "Bug Tracker" = "https://github.com/flatironinstitute/adapol/issues"
adapol-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .hybfit import hybfit, hybfit_triqs
2
+ from .anacont import anacont
3
+ from .fit_utils import check_psd
4
+ __all__ = ["hybfit", "hybfit_triqs", "check_psd", "anacont"]
@@ -0,0 +1,123 @@
1
+ """
2
+ This code implements a specific variant of the AAA algorithm.
3
+
4
+ """
5
+
6
+ import numpy as np
7
+ import scipy.linalg
8
+
9
+
10
+ def aaa_matrix_real(F, Z, tol=1e-13, mmax=100):
11
+ """ """
12
+ Z = np.asanyarray(Z).ravel()
13
+
14
+ # only use input z that are on iR_+. Will map them to iR_- by taking conjugate of function value.
15
+ half_index = np.imag(Z) > 0
16
+ Z_half = Z[half_index]
17
+ F_half = F[half_index, :, :]
18
+ M_half = len(Z_half)
19
+
20
+ Z = np.append(Z_half, np.conjugate(Z_half))
21
+
22
+ Norb = F.shape[1]
23
+ F_other_half = np.zeros_like(F_half)
24
+ for i in range(M_half):
25
+ F_other_half[i, :, :] = np.conjugate(np.transpose(F_half[i, :, :]))
26
+ F = np.concatenate((F_half, F_other_half), axis=0)
27
+
28
+ M = M_half * 2
29
+
30
+ F_mat = np.reshape(F, (M, Norb * Norb))
31
+
32
+ J = list(range(M))
33
+ zj = np.empty(0, dtype=Z.dtype)
34
+ fj = np.empty((0, Norb * Norb), dtype=F.dtype)
35
+ C = np.empty([M, 0], dtype=F.dtype)
36
+ errors = []
37
+
38
+ reltol = tol * np.linalg.norm(F_mat, np.inf)
39
+
40
+ R = np.mean(F_mat) * np.ones_like(F_mat)
41
+
42
+ mlist = range(2, mmax + 1, 2)
43
+
44
+ for m in mlist:
45
+ # find largest residual
46
+ jj = np.argmax(np.sum(abs(F_mat - R) ** 2, 1))
47
+ zj = np.append(zj, (Z[jj],))
48
+ fj = np.concatenate((fj, F_mat[jj : jj + 1, :]), axis=0)
49
+ J.remove(jj)
50
+
51
+ # Cauchy matrix containing the basis functions as columns
52
+
53
+ jj2 = (jj + M_half) % M
54
+ zj = np.append(zj, (Z[jj2],))
55
+ fj = np.concatenate((fj, F_mat[jj2 : jj2 + 1, :]), axis=0)
56
+
57
+ J.remove(jj2)
58
+
59
+ C = 1.0 / (Z[J, None] - zj[None, :])
60
+
61
+ # Loewner matrix
62
+ Apart = np.zeros(((M - m) * Norb * Norb, m), dtype=F.dtype)
63
+ for i in range(Norb * Norb):
64
+ Fhere = F_mat[:, i]
65
+ fjhere = fj[:, i]
66
+
67
+ Apart[range(0 + i, i + (M - m) * Norb * Norb, Norb * Norb), :] = (
68
+ Fhere[J, None] - fjhere[None, :]
69
+ ) * C
70
+
71
+ Awidth = np.size(Apart, 1)
72
+ Apart_l = Apart[:, range(0, Awidth, 2)]
73
+ Apart_r = Apart[:, range(1, Awidth, 2)]
74
+ Anew = np.concatenate((Apart_l + Apart_r, (Apart_l - Apart_r) * 1j), axis=1)
75
+ Anew = np.concatenate((np.real(Anew), np.imag(Anew)), axis=0)
76
+
77
+ # compute weights as right singular vector for smallest singular value
78
+ _, _, Vh = np.linalg.svd(Anew, full_matrices=False)
79
+
80
+ wj_r = Vh[-1, :]
81
+
82
+ wj_r = np.reshape(wj_r, (2, int(m / 2)))
83
+ wj_c = np.zeros((2, int(m / 2)), dtype=np.complex128)
84
+ wj_c[0, :] = wj_r[0, :] + 1j * wj_r[1, :]
85
+ wj_c[1, :] = wj_r[0, :] - 1j * wj_r[1, :]
86
+
87
+ wj = np.asanyarray(wj_c.T).ravel()
88
+
89
+ # approximation: numerator / denominator
90
+
91
+ D = C.dot(wj)
92
+
93
+ # update residual
94
+ R = F_mat.copy()
95
+
96
+ for i in range(Norb * Norb):
97
+ fjhere = fj[:, i]
98
+ N = C.dot(wj * fjhere) # needs to change N and R
99
+ R[J, i] = N / D
100
+
101
+ # check for convergence
102
+ errors.append(np.linalg.norm(F_mat - R, np.inf))
103
+ if errors[-1] <= reltol:
104
+ break
105
+
106
+ fj = fj.reshape(m, Norb, Norb)
107
+ pol = aaa_pol(zj, wj)
108
+ return pol, zj, fj, wj
109
+
110
+
111
+ def aaa_pol(zj, wj):
112
+ """Return the poles and residues of the rational function."""
113
+
114
+ m = len(wj)
115
+
116
+ # compute poles
117
+ B = np.eye(m + 1)
118
+ B[0, 0] = 0
119
+ E = np.block([[0, wj], [np.ones((m, 1)), np.diag(zj)]])
120
+ evals = scipy.linalg.eigvals(E, B)
121
+ pol = np.real_if_close(evals[np.isfinite(evals)])
122
+
123
+ return pol
@@ -0,0 +1,210 @@
1
+ import numpy as np
2
+ from .fit_utils import pole_fitting, eval_with_pole
3
+
4
+ def anacont(
5
+ Delta,
6
+ iwn_vec,
7
+ tol=None,
8
+ Np=None,
9
+ solver="lstsq",
10
+ maxiter=500,
11
+ mmin=4,
12
+ mmax=50,
13
+ verbose=False,
14
+ ):
15
+ """
16
+ The function for analytical continuation.
17
+
18
+ Examples:
19
+ ----------
20
+
21
+ - Analytic continuation with :math:`N_p` poles:
22
+ :code:`func = anacont(Np = Np)`
23
+
24
+ - Fitting with fixed error tolerance tol:
25
+ :code:`func = anacont(tol = tol)`
26
+
27
+ - Analytic continuation with improved accuracy:
28
+ :code:`fitting(Np = Np, flag = flag, solver = "sdp")`
29
+
30
+ Parameters:
31
+ ------------
32
+ :code:`Delta`: np.array, :math:`(N_w, N_\mathrm{orb}, N_\mathrm{orb})`
33
+ The input hybridization function in Matsubara frequency.
34
+
35
+ :code:`iwn_vec`: np.array, :math:`(N_w,)`
36
+ The Matsubara frequency vector, complex-valued
37
+
38
+
39
+ :code:`tol`: Fitting error tolreance, float
40
+ If tol is specified, the fitting will be conducted with fixed error tolerance tol.
41
+ default: None
42
+
43
+ :code:`Np`: number of poles, integer
44
+ If Np is specified, the fitting will be conducted with fixed number of poles Np.
45
+ default: None
46
+ Np needs to be an odd integer, and number of supoort points is Np + 1.
47
+
48
+
49
+ :code:`solver`: string
50
+ The solver that is used for optimization.
51
+ choices: "lstsq", "sdp"
52
+ default: "lstsq"
53
+
54
+ :code:`maxiter`: int
55
+ maximum number of iterations
56
+ default: 500
57
+
58
+ :code:`mmin`, :code:`mmax`: number of minimum or maximum poles, integer
59
+ default: mmin = 4, mmax = 50
60
+ if tol is specified, mmin and mmax will be used as the minimum and maximum number of poles.
61
+ if Np is specified, mmin and mmax will not be used.
62
+
63
+ :code:`verbose`: bool
64
+ whether to display optimization details
65
+ default: False
66
+
67
+
68
+
69
+
70
+ Returns:
71
+ ---------
72
+ :code:`func`: function
73
+ Analytic continuation function:
74
+ :math:`f(z) = \sum_n \mathrm{Weight}[n]/(z-\mathrm{pol}[n]).`
75
+
76
+ :code:`fitting_error`: float
77
+ fitting error
78
+
79
+ :code:`pol`: np.array, :math:`(N_p,)`
80
+ poles obtained from fitting
81
+
82
+ :code:`weight`: np.array, :math:`(N_p, N_\mathrm{orb}, N_\mathrm{orb})`
83
+ weights obtained from fitting
84
+
85
+ """
86
+
87
+ # Check dimensions
88
+ assert len(iwn_vec.shape) == 1 or len(iwn_vec.shape) == 2
89
+ if len(iwn_vec.shape) == 2:
90
+ assert iwn_vec.shape[1] == 1
91
+ iwn_vec = iwn_vec.flatten()
92
+ assert len(Delta.shape) == 3 or len(Delta.shape) == 1
93
+ if len(Delta.shape) == 1:
94
+ assert Delta.shape[0] == iwn_vec.shape[0]
95
+ Delta = Delta[:, None, None]
96
+ if len(Delta.shape) == 3:
97
+ assert Delta.shape[0] == iwn_vec.shape[0]
98
+ assert Delta.shape[1] == Delta.shape[2]
99
+
100
+ solver = solver.lower()
101
+ assert solver == "lstsq" or solver == "sdp"
102
+
103
+ # Check input tol or Np
104
+ if tol is None and Np is None:
105
+ raise ValueError("Please specify either tol or Np")
106
+ if tol is not None and Np is not None:
107
+ raise ValueError(
108
+ "Please specify either tol or Np. One can not specify both of them."
109
+ )
110
+
111
+ wn_vec = np.imag(iwn_vec)
112
+
113
+ if Np is not None:
114
+ pol, weight, fitting_error = pole_fitting(
115
+ Delta, wn_vec, Ns=Np+1, maxiter=maxiter, solver=solver, disp=verbose
116
+ )
117
+ elif tol is not None:
118
+ pol, weight, fitting_error = pole_fitting(
119
+ Delta,
120
+ wn_vec,
121
+ tol=tol,
122
+ mmin=mmin,
123
+ mmax=mmax,
124
+ maxiter=maxiter,
125
+ solver=solver,
126
+ disp=verbose,
127
+ )
128
+ def func(Z):
129
+ return eval_with_pole(pol, Z, weight)
130
+ return func, fitting_error, pol, weight
131
+
132
+
133
+ def anacont_triqs(
134
+ Delta_triqs,
135
+ tol=None,
136
+ Np=None,
137
+ solver="lstsq",
138
+ maxiter=500,
139
+ mmin=4,
140
+ mmax=50,
141
+ verbose=False,
142
+ debug=False
143
+ ):
144
+ """
145
+ The triqs interface for analytical continuation.
146
+ The function requires triqs package in python.
147
+
148
+ Parameters:
149
+ ------------
150
+ :code:`Delta_triqs`: triqs Green's function container
151
+ The input hybridization function in Matsubara frequency
152
+
153
+ :code:`debug`: bool
154
+ return additional outputs for debugging.
155
+ Default: False
156
+
157
+ :code:`tol`, :code:`Np`, :code:`solver`, :code:`maxiter`, :code:`mmin`, :code:`mmax`, :code:`verbose`:
158
+ same as in anacont
159
+
160
+
161
+ Returns:
162
+ ---------
163
+ :code:`func`: function
164
+ Analytic continuation function:
165
+ :math:`f(z) = \sum_n \mathrm{Weight}[n]/(z-\mathrm{pol}[n]).`
166
+
167
+ if debug == True:
168
+ :code:`fitting_error`: float
169
+ fitting error
170
+
171
+ :code:`pol`: np.array, :math:`(N_p,)`
172
+ poles obtained from fitting
173
+
174
+ :code:`weight`: np.array, :math:`(N_p, N_\mathrm{orb}, N_\mathrm{orb})`
175
+ weights obtained from fitting
176
+
177
+ """
178
+ try:
179
+ from triqs.gf import Gf, BlockGf, MeshImFreq, MeshDLRImFreq
180
+ except ImportError:
181
+ raise ImportError("Failed to import the triqs package (https://triqs.github.io/triqs/latest/). "
182
+ "Please ensure it is installed.")
183
+
184
+ if isinstance(Delta_triqs, Gf) and isinstance(Delta_triqs.mesh, (MeshImFreq, MeshDLRImFreq)):
185
+ iwn_vec = np.array([iw.value for iw in Delta_triqs.mesh.values()])
186
+ func, fit_error, pol, weight = anacont(Delta_triqs.data, iwn_vec, tol, Np, solver, maxiter,
187
+ mmin, mmax, verbose)
188
+ print('optimization finished with fitting error {:.3e}'.format(fit_error))
189
+
190
+ if debug:
191
+ return func, fit_error, pol, weight
192
+ else:
193
+ return func
194
+ elif isinstance(Delta_triqs, BlockGf) and isinstance(Delta_triqs.mesh, (MeshImFreq, MeshDLRImFreq)):
195
+ func_list, error_list, pol_list, weight_list = [], [], [], []
196
+ for j, (block, delta_blk) in enumerate(Delta_triqs):
197
+ func, fit_error, pol, weight = anacont_triqs(delta_blk, tol, Np, solver, maxiter, mmin,
198
+ mmax, verbose)
199
+ func_list.append(func)
200
+ if debug:
201
+ error_list.append(fit_error)
202
+ pol_list.append(pol)
203
+ weight_list.append(weight)
204
+
205
+ if debug:
206
+ return func_list, error_list, pol_list, weight_list
207
+ else:
208
+ return func_list
209
+ else:
210
+ raise RuntimeError("Error: Delta_triqs.mesh must be an instance of MeshImFreq or MeshDLRImFreq.")
@@ -0,0 +1,278 @@
1
+ # This is a python implementation for analytic continuation of Fermionic Green's functions/self energy
2
+ # using PES (ES) method
3
+ # Reference: PhysRevB.107.075151
4
+ import numpy as np
5
+ import scipy
6
+ import scipy.optimize
7
+ import cvxpy as cp
8
+ from .aaa import aaa_matrix_real
9
+
10
+
11
+ # import mosek
12
+ def eval_with_pole(pol, Z, weight):
13
+ pol_t = np.reshape(pol, [pol.size, 1])
14
+ M = 1 / (Z - pol_t)
15
+ M = M.transpose()
16
+ if len(weight.shape) == 1:
17
+ return M @ weight
18
+ else:
19
+ G = M @ np.reshape(weight, (weight.shape[0], weight.shape[1] * weight.shape[2]))
20
+ return np.reshape(G, (G.shape[0], weight.shape[1], weight.shape[2]))
21
+
22
+
23
+ def get_weight(
24
+ pol, Z, G, cleanflag=True, maxiter=1000, complex=True, fast=False, eps=1e-8
25
+ ):
26
+ pol_t = np.reshape(pol, [pol.size, 1])
27
+ M = 1 / (Z - pol_t)
28
+ M = M.transpose()
29
+ MM = np.concatenate([M.real, M.imag])
30
+ if len(G.shape) == 1:
31
+ GG = np.concatenate([G.real, G.imag])
32
+ if cleanflag:
33
+ R = np.linalg.lstsq(MM, GG, rcond=0)[0]
34
+ else:
35
+ [R, rnorm] = scipy.optimize.nnls(MM, GG, maxiter=maxiter)
36
+ residue = G - M @ R
37
+ else:
38
+ Np = len(pol)
39
+ Norb = G.shape[1]
40
+ R = np.zeros((Np, Norb, Norb), dtype=np.complex128)
41
+ if cleanflag:
42
+ for i in range(Norb):
43
+ GG = np.concatenate([G[:, i, i].real, G[:, i, i].imag])
44
+ R[:, i, i] = np.linalg.lstsq(MM, GG, rcond=0)[0]
45
+ for j in range(i + 1, Norb):
46
+ g1 = (G[:, j, i] + G[:, i, j]) / 2.0
47
+ g2 = (G[:, i, j] - G[:, j, i]) / 2.0
48
+ GG1 = np.concatenate([g1.real, g1.imag])
49
+ GG2 = np.concatenate([g2.imag, -g2.real])
50
+ R1 = np.linalg.lstsq(MM, GG1, rcond=0)[0]
51
+ R2 = np.linalg.lstsq(MM, GG2, rcond=0)[0]
52
+ R[:, i, j] = R1 + 1j * R2
53
+ R[:, j, i] = R1 - 1j * R2
54
+ else:
55
+ if not fast:
56
+ Nw = len(Z)
57
+
58
+ if complex:
59
+ X = [cp.Variable((Norb, Norb), hermitian=True) for i in range(Np)]
60
+ constraints = [X[i] >> 0 for i in range(Np)]
61
+ else:
62
+ X = [cp.Variable((Norb, Norb), PSD=True) for i in range(Np)]
63
+
64
+
65
+ Gfit = []
66
+ for w in range(Nw):
67
+ Gfit.append(cp.sum_squares(sum([ M[w,i]*X[i] for i in range(Np)]) - G[w,:,:]))
68
+
69
+ if complex:
70
+ prob = cp.Problem(cp.Minimize(sum(Gfit)), constraints)
71
+ else:
72
+ prob = cp.Problem(cp.Minimize(sum(Gfit)))
73
+ prob.solve(solver="SCS", verbose=False, eps=eps)
74
+ # MOSEK parameters
75
+ # mosek_params_dict = {"MSK_DPAR_INTPNT_CO_TOL_PFEAS": 1.e-8,\
76
+ # "MSK_DPAR_INTPNT_CO_TOL_DFEAS": 1.e-8,
77
+ # "MSK_DPAR_INTPNT_CO_TOL_REL_GAP": 1.e-8,
78
+ # "MSK_DPAR_INTPNT_CO_TOL_NEAR_REL": 1000}
79
+ # result = prob.solve(solver = "MOSEK", verbose=False,\
80
+ # mosek_params = mosek_params_dict)
81
+
82
+ for i in range(Np):
83
+ R[i] = X[i].value
84
+ else:
85
+ for i in range(Norb):
86
+ GG = np.concatenate([G[:, i, i].real, G[:, i, i].imag])
87
+ Rii = scipy.optimize.nnls(MM, GG, maxiter=maxiter)[0]
88
+ R[:, i, i] = Rii
89
+ MM2 = np.concatenate([M, np.conj(M)])
90
+ for i in range(Norb):
91
+ for j in range(i + 1, Norb):
92
+ GG = np.concatenate([G[:, i, j], np.conj(G[:, j, i])])
93
+ bound = np.sqrt(np.abs(R[:, i, i] * R[:, j, j]))
94
+ x = cp.Variable(MM.shape[1], complex=True)
95
+ constraints = [
96
+ cp.abs(x[k]) <= bound[k] for k in range(MM.shape[1])
97
+ ]
98
+ objective = cp.Minimize(cp.sum_squares(MM2 @ x - GG))
99
+ prob = cp.Problem(objective, constraints)
100
+ prob.solve(solver="SCS", verbose=False, eps=eps)
101
+ # result = prob.solve(solver = cp.MOSEK,verbose = False,mosek_params = mosek_params_dict)
102
+ R[:, i, j] = x.value
103
+ R[:, j, i] = np.conj(x.value)
104
+
105
+ residue = 1.0 * G
106
+ for i in range(Np):
107
+ residue = residue - M[:, i, None, None] * R[i]
108
+ return R, M, residue
109
+
110
+
111
+ def aaa_reduce(pol, R, eps=1e-6):
112
+ Np = R.shape[0]
113
+ Rnorm = np.zeros(Np)
114
+ for i in range(Np):
115
+ Rnorm[i] = np.linalg.norm(R[i])
116
+ nonz_index = Rnorm > eps
117
+ return pol[nonz_index], R[nonz_index]
118
+
119
+
120
+ def erroreval(pol, Z, G, cleanflag=True, maxiter=1000, fast=False, complex=True):
121
+ R, M, residue = get_weight(
122
+ pol, Z, G, cleanflag=cleanflag, maxiter=maxiter, complex=complex, fast=fast
123
+ )
124
+ if len(G.shape) == 1:
125
+ y = np.linalg.norm(residue)
126
+ grad = np.real(np.dot(np.conj(residue), (R * (M**2))))
127
+ else:
128
+ y = np.linalg.norm(residue.flatten())
129
+
130
+ Np = len(pol)
131
+ grad = np.zeros(Np)
132
+ Nw = len(Z)
133
+ for k in range(Np):
134
+ for w in range(Nw):
135
+ grad[k] = grad[k] + np.real(
136
+ np.sum((M[w, k] ** 2) * (np.conj(residue[w, :, :]) * R[k]))
137
+ )
138
+
139
+ grad = -grad / y
140
+ return y, grad
141
+
142
+
143
+ def pole_fitting(
144
+ Delta,
145
+ Z,
146
+ tol=None,
147
+ Ns=None,
148
+ mmin=None,
149
+ mmax=50,
150
+ maxiter=50,
151
+ solver="lstsq",
152
+ fast=False,
153
+ disp=False,
154
+ complex=True,
155
+ ):
156
+ # set cleanflag
157
+ if solver == "lstsq":
158
+ cleanflag = True
159
+ if solver == "sdp":
160
+ cleanflag = False
161
+
162
+ # pole estimation
163
+ # tol needs to be fixed
164
+ if Ns is None and tol is None:
165
+ raise Exception(
166
+ "One needs to specify either the number of poles or the fitting error tolerance."
167
+ )
168
+ if Ns is not None and tol is not None:
169
+ raise Exception(
170
+ "One can not specify both the number of poles and the fitting error tolerance. Only specify one of them."
171
+ )
172
+ if Ns is None:
173
+ if mmin is None or mmin < 4:
174
+ mmin = 4
175
+ if mmin % 2 == 1:
176
+ mmin = mmin + 1
177
+ if mmax > 2 * (Z.shape[0] // 2):
178
+ mmax = 2 * (Z.shape[0] // 2)
179
+ else:
180
+ if Ns % 2 == 1:
181
+ Ns = Ns + 1
182
+ mmin, mmax = Ns, Ns
183
+ if len(Delta.shape) == 1:
184
+ Delta = Delta.reshape(Delta.shape[0], 1, 1)
185
+
186
+ for m in range(mmin, mmax + 1, 2):
187
+ pol, _, _, _ = aaa_matrix_real(Delta, 1j * Z, mmax=m)
188
+ pol = np.real(pol)
189
+ weight, _, residue = get_weight(
190
+ pol, 1.0j * Z, Delta, cleanflag=cleanflag, complex=complex, fast=fast
191
+ )
192
+ # print(np.max(np.abs(residue)))
193
+ if tol is not None:
194
+ if np.max(np.abs(residue)) > tol * 10:
195
+ continue
196
+ if Ns is None:
197
+ pol, weight = aaa_reduce(pol, weight, 1e-5)
198
+ # print("Number of poles is ", len(pol))
199
+ if cleanflag:
200
+ if maxiter > 0:
201
+
202
+ def fhere(pole):
203
+ return erroreval(
204
+ pole, 1j * Z, Delta, cleanflag=cleanflag, complex=complex
205
+ )
206
+
207
+ # fhere = lambda pole: erroreval(pole,1j*Z,Delta,cleanflag=cleanflag,complex=complex)
208
+ res = scipy.optimize.minimize(
209
+ fhere,
210
+ pol,
211
+ method="L-BFGS-B",
212
+ jac=True,
213
+ options={
214
+ "disp": disp,
215
+ "maxiter": maxiter,
216
+ "gtol": 1e-10,
217
+ "ftol": 1e-10,
218
+ },
219
+ )
220
+ else:
221
+
222
+ def fhere1(pole):
223
+ return erroreval(pole, 1j * Z, Delta, cleanflag=True, complex=complex)
224
+
225
+ # fhere = lambda pole: erroreval(pole,1j*Z,Delta,cleanflag=True,complex=complex)
226
+ res = scipy.optimize.minimize(
227
+ fhere1,
228
+ pol,
229
+ method="L-BFGS-B",
230
+ jac=True,
231
+ options={"disp": False, "gtol": 1e-10, "ftol": 1e-10},
232
+ )
233
+ if maxiter > 0:
234
+
235
+ def fhere2(pole):
236
+ return erroreval(
237
+ pole, 1j * Z, Delta, cleanflag=False, complex=complex, fast=fast
238
+ )
239
+
240
+ # fhere = lambda pole: erroreval(pole,1j*Z,Delta,cleanflag=False,complex= complex,fast = fast)
241
+ res = scipy.optimize.minimize(
242
+ fhere2,
243
+ res.x,
244
+ method="L-BFGS-B",
245
+ jac=True,
246
+ options={
247
+ "disp": disp,
248
+ "maxiter": maxiter,
249
+ "gtol": 1e-10,
250
+ "ftol": 1e-10,
251
+ },
252
+ )
253
+
254
+ weight, _, residuenew = get_weight(
255
+ res.x, 1j * Z, Delta, cleanflag=cleanflag, fast=fast, complex=complex
256
+ )
257
+ if not check_psd(weight):
258
+ weight, _, residuenew = get_weight(
259
+ res.x, 1j * Z, Delta, cleanflag=False, complex=complex
260
+ )
261
+ err = np.max(np.abs(residuenew))
262
+ if tol is not None:
263
+ if err < tol:
264
+ return res.x, weight, err
265
+ else:
266
+ return res.x, weight, err
267
+
268
+ if tol is not None:
269
+ print("Fail to reach desired fitting error!")
270
+ return res.x, weight, np.max(np.abs(residuenew))
271
+
272
+
273
+ def check_psd(weight, atol=1e-6):
274
+ check_psd = True
275
+ for i in range(weight.shape[0]):
276
+ val, _ = np.linalg.eig(weight[i])
277
+ check_psd = check_psd and np.min(val.real) > -atol
278
+ return check_psd
@@ -0,0 +1,229 @@
1
+ import numpy as np
2
+ from .fit_utils import pole_fitting, eval_with_pole
3
+
4
+ def hybfit(
5
+ Delta,
6
+ iwn_vec,
7
+ tol=None,
8
+ Np=None,
9
+ svdtol=1e-7,
10
+ solver="lstsq",
11
+ maxiter=500,
12
+ mmin=4,
13
+ mmax=50,
14
+ verbose=False,
15
+ ):
16
+ """
17
+ The function for hybridization fitting.
18
+
19
+ Examples:
20
+ ----------
21
+
22
+ - Fitting with :math:`N_p` poles:
23
+ :code:`hybfit(Delta, iwn_vec, Np = Np)`
24
+
25
+ - Fitting with fixed error tolerance tol :
26
+ :code:`hybfit(Delta, iwn_vec, tol = tol)`
27
+
28
+ Parameters:
29
+ ------------
30
+ :code:`Delta`: np.array, :math:`(N_w, N_\mathrm{orb}, N_\mathrm{orb})`
31
+ The input hybridization function in Matsubara frequency.
32
+
33
+ :code:`iwn_vec`: np.array, :math:`(N_w)`
34
+ The Matsubara frequency vector, complex-valued
35
+
36
+ :code:`svdtol`: float, optional
37
+ Truncation threshold for bath orbitals while doing SVD of weight matrices in hybridization fitting
38
+ default:1e-7
39
+
40
+ :code:`tol`, :code:`Np`, :code:`solver`, :code:`maxiter`, :code:`mmin`, :code:`mmax`, :code:`verbose`:
41
+ see below in anacont
42
+
43
+
44
+ Returns:
45
+ ---------
46
+
47
+
48
+ :code:`bathenergy` :math:`E`: np.array, :math:`(N_b)`
49
+ Bath energy
50
+
51
+ :code:`bathhyb` :math:`V`: np.array, :math:`(N_b,N_{\mathrm{orb}})`
52
+ Bath hybridization
53
+
54
+ :code:`final_error`: float
55
+ final fitting error
56
+
57
+ :code:`func`: function
58
+ Hybridization function evaluator
59
+ :math:`f(z) = \sum_n V_{ni}V_{nj}^*/(z-E_n).`
60
+
61
+ """
62
+
63
+
64
+ # Check dimensions
65
+ assert len(iwn_vec.shape) == 1 or len(iwn_vec.shape) == 2
66
+ if len(iwn_vec.shape) == 2:
67
+ assert iwn_vec.shape[1] == 1
68
+ iwn_vec = iwn_vec.flatten()
69
+ assert len(Delta.shape) == 3 or len(Delta.shape) == 1
70
+ if len(Delta.shape) == 1:
71
+ assert Delta.shape[0] == iwn_vec.shape[0]
72
+ Delta = Delta[:, None, None]
73
+ if len(Delta.shape) == 3:
74
+ assert Delta.shape[0] == iwn_vec.shape[0]
75
+ assert Delta.shape[1] == Delta.shape[2]
76
+
77
+ solver = solver.lower()
78
+ assert solver == "lstsq" or solver == "sdp"
79
+
80
+ # Check input tol or Np
81
+ if tol is None and Np is None:
82
+ raise ValueError("Please specify either tol or Np")
83
+ if tol is not None and Np is not None:
84
+ raise ValueError(
85
+ "Please specify either tol or Np. One can not specify both of them."
86
+ )
87
+
88
+ wn_vec = np.imag(iwn_vec)
89
+
90
+ if Np is not None:
91
+ pol, weight, fitting_error = pole_fitting(
92
+ Delta, wn_vec, Ns=Np + 1, maxiter=maxiter, solver=solver, disp=verbose
93
+ )
94
+ elif tol is not None:
95
+ pol, weight, fitting_error = pole_fitting(
96
+ Delta,
97
+ wn_vec,
98
+ tol=tol,
99
+ mmin=mmin,
100
+ mmax=mmax,
101
+ maxiter=maxiter,
102
+ solver=solver,
103
+ disp=verbose,
104
+ )
105
+
106
+ bathenergy, bathhyb, bath_mat = obtain_orbitals(pol, weight, svdtol=svdtol)
107
+ def func(Z):
108
+ return eval_with_pole(bathenergy, Z, bath_mat)
109
+ Delta_reconstruct = func(iwn_vec)
110
+ final_error = np.max(np.abs(Delta - Delta_reconstruct))
111
+ return bathenergy, bathhyb, final_error, func
112
+
113
+
114
+ def hybfit_triqs(
115
+ Delta_triqs,
116
+ tol=None,
117
+ Np=None,
118
+ svdtol=1e-7,
119
+ solver="lstsq",
120
+ maxiter=500,
121
+ mmin=4,
122
+ mmax=50,
123
+ verbose=False,
124
+ debug=False
125
+ ):
126
+ """
127
+ The triqs interface for hybridization fitting.
128
+ The function requires triqs package in python.
129
+
130
+ Examples:
131
+ ----------
132
+
133
+ - Fitting with :math:`N_p` poles:
134
+ :code:`hybfit_triqs(delta_triqs, Np = Np)`
135
+
136
+ - Fitting with fixed error tolerance tol :
137
+ :code:`hybfit_triqs(delta_triqs, tol = tol)`
138
+
139
+
140
+ Parameters:
141
+ ------------
142
+ :code:`Delta_triqs`: triqs Green's function container
143
+ The input hybridization function in Matsubara frequency
144
+
145
+ :code:`debug`: bool
146
+ return additional outputs for debugging.
147
+ Default: False
148
+
149
+ :code:`svdtol`: float, optional
150
+ Truncation threshold for bath orbitals while doing SVD of weight matrices in hybridization fitting
151
+ default: 1e-7
152
+
153
+ :code:`tol`, :code:`Np`, :code:`solver`, :code:`maxiter`, :code:`mmin`, :code:`mmax`, :code:`verbose`:
154
+ same as in hybfit
155
+
156
+ Returns:
157
+ ---------
158
+
159
+ :code:`bathhyb`: np.array :math:`(N_b, N_\mathrm{orb})`
160
+ Bath hybridization
161
+
162
+ :code:`bathenergy`: np.array :math:`(N_b,)`
163
+ Bath energy
164
+
165
+ :code:`Delta_fit`: triqs Gf or BlockGf
166
+ Discretized hybridization function
167
+ The input hybridization function in Matsubara frequency
168
+
169
+ if debug is True:
170
+ :code:`final_error`: float
171
+ final fitting error
172
+
173
+ :code:`weight`: np.array :math:`(N_p, N_\mathrm{orb}, N_\mathrm{orb})`
174
+ weights obtained from fitting
175
+ """
176
+ try:
177
+ from triqs.gf import Gf, BlockGf, MeshImFreq, MeshDLRImFreq
178
+ except ImportError:
179
+ raise ImportError("Failed to import the triqs package (https://triqs.github.io/triqs/latest/). "
180
+ "Please ensure it is installed.")
181
+
182
+ if isinstance(Delta_triqs, Gf) and isinstance(Delta_triqs.mesh, (MeshImFreq, MeshDLRImFreq)):
183
+ iwn_vec = np.array([iw.value for iw in Delta_triqs.mesh.values()])
184
+ eps_opt, V_opt, final_error, func = hybfit(Delta_triqs.data, iwn_vec, tol, Np,
185
+ svdtol, solver, maxiter, mmin, mmax, verbose)
186
+ print('optimization finished with fitting error {:.3e}'.format(final_error))
187
+
188
+ delta_fit = Gf(mesh=Delta_triqs.mesh, target_shape=Delta_triqs.target_shape)
189
+ delta_fit.data[:] = func(iwn_vec)
190
+
191
+ if debug:
192
+ return V_opt.T.conj(), eps_opt, delta_fit, final_error
193
+ else:
194
+ return V_opt.T.conj(), eps_opt, delta_fit
195
+ elif isinstance(Delta_triqs, BlockGf) and isinstance(Delta_triqs.mesh, (MeshImFreq, MeshDLRImFreq)):
196
+ V_list, eps_list, delta_list, error_list = [], [], [], []
197
+ for j, (block, delta_blk) in enumerate(Delta_triqs):
198
+ res = hybfit_triqs(delta_blk, tol, Np, svdtol, solver, maxiter, mmin, mmax, verbose, debug)
199
+ V_list.append(res[0])
200
+ eps_list.append(res[1])
201
+ delta_list.append(res[2])
202
+ if debug:
203
+ error_list.append(res[3])
204
+
205
+ if debug:
206
+ return V_list, eps_list, BlockGf(name_list=list(Delta_triqs.indices), block_list=delta_list), error_list
207
+ else:
208
+ return V_list, eps_list, BlockGf(name_list=list(Delta_triqs.indices), block_list=delta_list)
209
+ else:
210
+ raise RuntimeError("Error: Delta_triqs.mesh must be an instance of MeshImFreq or MeshDLRImFreq.")
211
+
212
+ def obtain_orbitals(pol, weight, svdtol=1e-7):
213
+ """
214
+ obtaining bath orbitals through svd
215
+ """
216
+ polelist = []
217
+ veclist = []
218
+ matlist = []
219
+ for i in range(weight.shape[0]):
220
+ eigval, eigvec = np.linalg.eig(weight[i])
221
+ for j in range(eigval.shape[0]):
222
+ if eigval[j] > svdtol:
223
+ polelist.append(pol[i])
224
+ veclist.append(eigvec[:, j] * np.sqrt(eigval[j]))
225
+ matlist.append(
226
+ (eigvec[:, j, None] * np.conjugate(eigvec[:, j].T)) * (eigval[j])
227
+ )
228
+
229
+ return np.array(polelist), np.array(veclist), np.array(matlist)
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.1
2
+ Name: adapol
3
+ Version: 0.1.0
4
+ Summary: Adaptive Pole Fitting for Quantum Many-Body Physics
5
+ Author-email: Zhen Huang <hertz@berkeley.edu>, Chia-Nan Yeh <cyeh@flatironinstitute.org>, Nils Wentzell <nwentzell@flatironinstitute.org>, Jason Kaye <jkaye@flatironinstitute.org>, Lin Lin <linlin@berkeley.edu>
6
+ Project-URL: Homepage, https://flatironinstitute.github.io/adapol
7
+ Project-URL: Bug Tracker, https://github.com/flatironinstitute/adapol/issues
8
+ Keywords: Bath,Fitting,Hybridization,DMFT,Matsubara
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: numpy
15
+ Requires-Dist: scipy
16
+ Requires-Dist: cvxpy
17
+
18
+ # adapol: Adaptive Pole Fitting for Quantum Many-Body Physics
19
+ [`adapol`](https://github.com/Hertz4/Adapol) (pronounced "add a pole") is a python package for fitting Matsubara functions with the following form:
20
+ ```math
21
+ G(\mathrm i \omega_k) = \sum_l \frac{V_lV_l^{\dagger}}{\mathrm i\omega_k - E_l}.
22
+ ```
23
+
24
+ Current applications include
25
+ (1) hybridization fitting, (2) analytic continuation.
26
+
27
+ We also provide a [TRIQS](https://triqs.github.io/) interface if the Matsubara functions are stored in `triqs` Green's function container.
28
+
29
+ # Installation
30
+ `adapol` has `numpy` and `scipy` as its prerequisites. [`cvxpy`](https://www.cvxpy.org/) is also required for hybridization fitting of matrix-valued (instead of scalar-valued) Matsubara functions.
31
+
32
+ To install `adapol`, run
33
+ ```terminal
34
+ pip install adapol
35
+ ```
36
+
37
+
38
+
39
+ # Documentation
40
+
41
+ See the detailed [documentation](https://flatironinstitute.github.io/adapol/) for physical background, algorithms and user manual.
42
+
43
+ `Adapol` is a stand-alone package. For TRIQS users, we also provide a TRIQS interface. See [user manual](https://flatironinstitute.github.io/adapol/latest/python.html#triqs-interface) for details.
44
+
45
+ # Examples
46
+ In the `tutorial` page, we provide two examples [`discrete.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/discrete.html) and [`semicircle.ipynb`](https://flatironinstitute.github.io/adapol/latest/tutorials/semicircle.html), showcasing how to use `adapol` for both discrete spectrum and continuous spectrum.
47
+
48
+ In these notebooks, we also demonstrate how to use our code through the triqs interface.
49
+
50
+ # References
51
+ To cite this work, please include a reference to this GitHub repository, and
52
+ cite the following references:
53
+
54
+ 1. Huang, Zhen, Emanuel Gull, and Lin Lin. "Robust analytic continuation of Green's functions via projection, pole estimation, and semidefinite relaxation." Physical Review B 107.7 (2023): 075151.
55
+ 2. Mejuto-Zaera, Carlos, et al. "Efficient hybridization fitting for dynamical mean-field theory via semi-definite relaxation." Physical Review B 101.3 (2020): 035143.
56
+ 3. Nakatsukasa, Yuji, Olivier Sète, and Lloyd N. Trefethen. "The AAA algorithm for rational approximation." SIAM Journal on Scientific Computing 40.3 (2018): A1494-A1522.
@@ -0,0 +1,13 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ src/adapol/__init__.py
5
+ src/adapol/aaa.py
6
+ src/adapol/anacont.py
7
+ src/adapol/fit_utils.py
8
+ src/adapol/hybfit.py
9
+ src/adapol.egg-info/PKG-INFO
10
+ src/adapol.egg-info/SOURCES.txt
11
+ src/adapol.egg-info/dependency_links.txt
12
+ src/adapol.egg-info/requires.txt
13
+ src/adapol.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ numpy
2
+ scipy
3
+ cvxpy
@@ -0,0 +1 @@
1
+ adapol