"""Adapol/TRIQS: Adaptive Pole Approximation of Frequency Data
User facing TRIQS based API for approximating frequency data
with a sum of simple poles, using the AAA algorithm.
Author: Hugo U. R. Strand (2026)"""
import numpy as np
from .adapol import _frequency_data_driver
from .adapol import _sum_of_simple_poles_driver
[docs]
def approx_gf_imfreq_aaa(G_w, max_n_poles=None, aaa_tol=None, verbose=False):
"""Approximate a Green's function :math:`G` given on an imaginary-frequency
mesh with a sum of simple poles, by running the AAA algorithm.
This is the TRIQS front-end to `adapol.approx_freq_aaa`, taking the
frequency data :math:`F` and the sample points :math:`Z` from the Green's
function and its mesh.
Parameters
----------
G_w : triqs.gf.Gf
Green's function to approximate, defined on an imaginary-frequency
mesh (`MeshImFreq` or `MeshDLRImFreq`).
max_n_poles : int, optional
Maximum number of poles to use in the approximation.
aaa_tol : float, optional
Tolerance on the AAA residual, i.e. on the pole step. It does not bound
the final error, which also depends on the subsequent residue fit.
verbose : bool, optional
If True, print verbose output during the approximation process.
Returns
-------
poles : (M,) ndarray
Poles of the approximating sum of simple poles (`M <= max_n_poles`).
residues : (M, ...) ndarray
Residues of the approximating sum of simple poles.
error : float
Maximum absolute error of the approximation at the mesh points.
Raises
------
ValueError
If neither `max_n_poles` nor `aaa_tol` is given.
Notes
-----
- If only `max_n_poles` is set, AAA runs until at most `max_n_poles` poles
are used.
- If only `aaa_tol` is set, AAA runs until the AAA residual tolerance
`aaa_tol` is reached.
- If both are set, AAA stops as soon as either the AAA residual tolerance
`aaa_tol` is reached or `max_n_poles` poles are used, whichever happens
first.
See `adapol.approx_freq_aaa` for details on the algorithm, on why the
number of poles produced may be smaller than `max_n_poles`, and on why
`aaa_tol` does not bound the returned `error`.
See Also
--------
adapol.approx_freq_aaa : Underlying array-based routine.
"""
if max_n_poles is None and aaa_tol is None:
raise ValueError("At least one of `max_n_poles` or `aaa_tol` must be provided.")
Z, F = _gf_imfreq_to_data(G_w)
return _frequency_data_driver(
F, Z, max_n_poles=max_n_poles, tol=aaa_tol, verbose=verbose)
[docs]
def approx_gf_dlr_fast(
G_dlr, max_n_poles=None, aaa_tol=None,
nonlinear_optimization=False, verbose=False):
"""Approximate a Green's function :math:`G` given in the discrete Lehmann
representation (DLR) with a sum of a (possibly) smaller number of simple
poles, by running the AAA algorithm and, optionally, a non-linear
optimization step.
This is the TRIQS front-end to `adapol.approx_sop_fast`, taking the
original poles and residues from the DLR frequencies and coefficients of
the Green's function.
Parameters
----------
G_dlr : triqs.gf.Gf
Green's function to approximate, defined on a DLR mesh (`MeshDLR`,
`MeshDLRImFreq` or `MeshDLRImTime`).
max_n_poles : int, optional
Maximum number of poles to use in the approximation.
aaa_tol : float, optional
Tolerance on the AAA residual. This is the maximum absolute deviation
from the imaginary-frequency-domain data used by the AAA algorithm (the
DLR expansion evaluated on the grid described below), over the sample
points not yet used as AAA support points.
nonlinear_optimization : bool, optional
If True, run a non-linear optimization step after the AAA approximation,
using the AAA poles only as an initial guess.
verbose : bool, optional
If True, print verbose output during the approximation process.
Returns
-------
poles : (M,) ndarray
Poles of the approximating sum of simple poles.
residues : (M, ...) ndarray
Residues of the approximating sum of simple poles.
error : float
Normalized imaginary-time :math:`L^2(\\tau)` norm of the difference
between the original DLR expansion and the approximating sum of simple
poles (see Notes).
Raises
------
ValueError
If neither `max_n_poles` nor `aaa_tol` is given, or if `G_dlr` is not
defined on a DLR mesh.
Notes
-----
The DLR expansion of :math:`G` is a sum of simple poles
.. math::
G(Z) = \\sum_{k=1}^K \\frac{R_k}{Z - P_k}
with poles :math:`P_k = \\omega_k / \\beta` given by the DLR frequencies
:math:`\\omega_k` of the mesh, and residues :math:`R_k` given by the DLR
coefficients of `G_dlr`. The approximation is then built in two steps:
1. **Pole step:** the DLR expansion is evaluated on the DLR
imaginary-frequency nodes and AAA is run on this data to determine the
pole locations. Note that this is the DLR Matsubara node set of the
mesh of `G_dlr`, and not the equispaced fermionic Matsubara grid that
`adapol.approx_sop_fast` uses by default.
2. **Residue step:** the residues are determined by minimizing the
imaginary-time :math:`L^2(\\tau)` norm of the difference from the DLR
expansion. By default this is a linear least-squares fit of the
residues for the AAA poles; if `nonlinear_optimization` is True, the
pole locations and residues are instead jointly optimized.
The imaginary-time :math:`L^2(\\tau)` norm is normalized by the inverse
temperature :math:`\\beta`,
.. math::
\\lVert f \\rVert_{L^2(\\tau)} =
\\left( \\frac{1}{\\beta} \\int_0^\\beta |f(\\tau)|^2 \\, d\\tau \\right)^{1/2}.
- If only `max_n_poles` is set, AAA runs until at most `max_n_poles` poles
are used.
- If only `aaa_tol` is set, AAA runs until the AAA residual tolerance
`aaa_tol` is reached.
- If both are set, AAA stops as soon as either the AAA residual tolerance
`aaa_tol` is reached or `max_n_poles` poles are used, whichever happens
first.
Note
----
Setting `aaa_tol` does **not** guarantee that the returned imaginary-time
L2 norm `error` is below `aaa_tol`. Use `approx_gf_dlr_tol` to impose a
tolerance on the final error instead.
See Also
--------
adapol.approx_sop_fast : Underlying array-based routine.
approx_gf_dlr_tol : Smallest approximation meeting a final error tolerance.
"""
if max_n_poles is None and aaa_tol is None:
raise ValueError("At least one of `max_n_poles` or `aaa_tol` must be provided.")
poles, residues, beta, Z = _gf_dlr_to_data(G_dlr)
return _sum_of_simple_poles_driver(
poles, residues, max_n_poles=max_n_poles, tol=aaa_tol, beta=beta,
nonlinear_optimization=nonlinear_optimization, Z=Z, verbose=verbose)
[docs]
def approx_gf_dlr_tol(G_dlr, tol, nonlinear_optimization=False, verbose=False):
"""Approximate a Green's function :math:`G` given in the discrete Lehmann
representation (DLR) with the smallest sum of simple poles whose
imaginary-time :math:`L^2(\\tau)` error is below the tolerance `tol`.
This is the TRIQS front-end to `adapol.approx_sop_tol`, taking the
original poles and residues from the DLR frequencies and coefficients of
the Green's function.
Parameters
----------
G_dlr : triqs.gf.Gf
Green's function to approximate, defined on a DLR mesh (`MeshDLR`,
`MeshDLRImFreq` or `MeshDLRImTime`).
tol : float
Target tolerance on the final imaginary-time :math:`L^2(\\tau)` norm of
the difference between the original DLR expansion and the approximating
sum of simple poles.
nonlinear_optimization : bool, optional
If True, the residue step jointly optimizes the pole locations and
residues (rather than fitting residues only) to minimize the
imaginary-time :math:`L^2(\\tau)` error.
verbose : int or bool, optional
Amount of printed output. 0 (or False) is silent, 1 (or True) prints one
line per pass through the AAA and residue fit pipeline, showing the pole
count and error of each candidate fit, and 2 additionally prints the
indented per step output of the AAA algorithm itself.
Returns
-------
poles : (M,) ndarray
Poles of the approximating sum of simple poles.
residues : (M, ...) ndarray
Residues of the approximating sum of simple poles.
error : float
Normalized imaginary-time :math:`L^2(\\tau)` norm of the difference
between the original DLR expansion and the approximating sum of simple
poles (with the :math:`1/\\beta` normalization defined in
`approx_gf_dlr_fast`).
Raises
------
ValueError
If the target tolerance `tol` cannot be achieved within the internal
maximum number of search steps, or if `G_dlr` is not defined on a DLR
mesh.
Notes
-----
Unlike `approx_gf_dlr_fast`, where the tolerance only controls the AAA
pole step, here `tol` is imposed on the **final** imaginary-time error,
i.e. after the residues have been fit. Since the residues (and hence the
final error) are only known after the residue step, the AAA + residue
pipeline is run repeatedly to search for the minimal number of poles that
achieves `tol`: the number of AAA poles is first increased until the
imaginary-time error drops below `tol`, then a bisection on the number of
AAA steps locates the smallest pole count that still meets `tol`.
As in `approx_gf_dlr_fast`, the AAA data is the DLR expansion evaluated on
the DLR imaginary-frequency nodes of the mesh of `G_dlr`.
See Also
--------
adapol.approx_sop_tol : Underlying array-based routine.
approx_gf_dlr_fast : Single-pass compression with an AAA stopping criterion.
"""
comp = TriqsDLRCompression(
G_dlr, tol=tol, nonlinear_optimize=nonlinear_optimization,
nonlinear_post_optimize=nonlinear_optimization, verbose=verbose)
return comp.poles, comp.residues, comp.error
def _gf_imfreq_to_data(G_w):
Z = np.array([complex(w) for w in G_w.mesh])
F = G_w.data.copy()
return Z, F
def _gf_dlr_to_data(G_dlr):
from triqs.gfs import MeshDLR
from triqs.gfs import MeshDLRImFreq
from triqs.gfs import MeshDLRImTime
from triqs.gfs import make_gf_dlr
from triqs.gfs import make_gf_dlr_imfreq
if type(G_dlr.mesh) not in [MeshDLR, MeshDLRImFreq, MeshDLRImTime]:
raise ValueError('G_dlr must be defined on a DLR mesh')
G_c = G_dlr if type(G_dlr.mesh) is MeshDLR else make_gf_dlr(G_dlr)
beta = G_c.mesh.beta
poles = np.array([float(w) for w in G_c.mesh]) / beta
residues = G_c.data.copy()
G_w = make_gf_dlr_imfreq(G_c)
Z = np.array([complex(w) for w in G_w.mesh])
return poles, residues, beta, Z
class TriqsDLRCompression:
def __init__(self, G, tol=1e-14,
nonlinear_optimize=False, nonlinear_post_optimize=False,
max_upwind_steps=4, verbose=True):
self.G = G
self.tol = tol
self.nonlinear_optimize = nonlinear_optimize
self.nonlinear_post_optimize = nonlinear_post_optimize
self.verbose = verbose
from triqs.gfs import MeshDLR
from triqs.gfs import make_gf_dlr
self.G_dlr = G if type(G.mesh) is MeshDLR else make_gf_dlr(G)
self.dlr_freq = np.array([float(w) for w in self.G_dlr.mesh])
self.G_dlr_coeff = self.G_dlr.data.copy()
self.beta = self.G_dlr.mesh.beta
poles = self.dlr_freq / self.beta
residues = self.G_dlr_coeff.copy()
from triqs.gfs import MeshDLRImFreq
from triqs.gfs import make_gf_dlr_imfreq
self.G_w = G if type(G.mesh) is MeshDLRImFreq else make_gf_dlr_imfreq(G)
self.Z = np.array([complex(w) for w in self.G_w.mesh])
from .sop_compr import SumOfPolesCompression
self.sop_comp = SumOfPolesCompression(
poles=poles, residues=residues,
Z=self.Z,
beta=self.beta, tol=tol,
nonlinear_optimize=nonlinear_optimize,
nonlinear_post_optimize=nonlinear_post_optimize,
max_upwind_steps=max_upwind_steps, verbose=verbose)
sc = self.sop_comp
self.poles, self.residues, self.aaa_steps, self.error = sc.poles, sc.residues, sc.aaa_steps, sc.error