Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions c++/cppdlr/dlr_build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,48 +42,61 @@ namespace cppdlr {
if (p <= 0) throw std::runtime_error("Choose p > 0.");
}

nda::vector<double> build_rf_fine(fineparams &fine) {
nda::vector<double> build_rf_fine(fineparams const &fine, bool symmetrize) {

int p = fine.p;
int npom = fine.npom;

auto bc = barycheb(p); // Get barycheb object for Chebyshev nodes
auto xc = (bc.getnodes() + 1) / 2; // Cheb nodes on [0,1]

// Real frequency grid points
// Real frequency grid points, mirror-symmetric about zero: om(j) = -om(n-1-j).
// The symmetric case additionally includes omega=0, which is its own mirror.

auto om = nda::vector<double>(fine.nom);
int nhalf = npom * p; // # points on (0,lambda)
int upper = symmetrize ? nhalf + 1 : nhalf; // first index of the positive half
int ntot = upper + nhalf; // Total # points
auto om = nda::vector<double>(ntot);

double a = 0, b = 0;

// Points on (0,lambda)

a = 0.0;
for (int i = 0; i < npom; ++i) {
b = fine.lambda / pow(2.0, npom - i - 1);
om(range((npom + i) * p, (npom + i + 1) * p)) = a + (b - a) * xc;
om(range(upper + i * p, upper + (i + 1) * p)) = a + (b - a) * xc;
a = b;
}

// Points on (-lambda,0)
// Central point omega=0, its own mirror

om(range(0, npom * p)) = -om(range(2 * npom * p - 1, npom * p - 1, -1));
if (symmetrize) om(nhalf) = 0.0;

// Points on (-lambda,0): mirror of the positive half

om(range(0, nhalf)) = -om(range(ntot - 1, upper - 1, -1));

return om;
}

std::tuple<nda::vector<double>, nda::vector<double>> build_it_fine(fineparams &fine) {
std::tuple<nda::vector<double>, nda::vector<double>> build_it_fine(fineparams const &fine, bool symmetrize) {

int p = fine.p;
int npt = fine.npt;

auto [xgl, wgl] = gaussquad(p); // Gauss-Legendre nodes and weights on [-1,1]
xgl = (xgl + 1) / 2; // Transform to [0,1]

// Imaginary time grid points
// Imaginary time grid points, mirror-symmetric about beta/2: t(i) = -t(n-1-i) in
// relative format. The symmetric case additionally includes tau=beta/2, its own
// mirror since k_it(0.5,om) = k_it(-0.5,om), with zero quadrature weight so the
// quadrature rule is unchanged.

auto t = nda::vector<double>(fine.nt);
auto w = nda::vector<double>(fine.nt);
int nhalf = npt * p; // # points on (0,1/2)
int upper = symmetrize ? nhalf + 1 : nhalf; // first index of the (1/2,1) half
int ntot = upper + nhalf; // Total # points
auto t = nda::vector<double>(ntot);
auto w = nda::vector<double>(ntot);

double a = 0, b = 0;

Expand All @@ -96,10 +109,17 @@ namespace cppdlr {
a = b;
}

// Points on (1/2,1) in relative format
// Central point tau=beta/2, carrying no quadrature weight

if (symmetrize) {
t(nhalf) = 0.5;
w(nhalf) = 0.0;
}

// Points on (1/2,1) in relative format: mirror of the first half

t(range(npt * p, 2 * npt * p)) = -t(range(npt * p - 1, -1, -1));
w(range(npt * p, 2 * npt * p)) = w(range(npt * p - 1, -1, -1));
t(range(upper, ntot)) = -t(range(nhalf - 1, -1, -1));
w(range(upper, ntot)) = w(range(nhalf - 1, -1, -1));

return {t, w};
}
Expand Down Expand Up @@ -162,7 +182,7 @@ namespace cppdlr {
return kvec;
}

std::tuple<double, double> geterr_k_it(fineparams &fine, nda::vector_const_view<double> t, nda::vector_const_view<double> om,
std::tuple<double, double> geterr_k_it(fineparams const &fine, nda::vector_const_view<double> t, nda::vector_const_view<double> om,
nda::matrix_const_view<double> kmat) {

int nt = fine.nt;
Expand Down Expand Up @@ -263,8 +283,8 @@ namespace cppdlr {
auto fine = fineparams(lambda);

// Get fine grids in frequency and imaginary time
auto [t, w] = build_it_fine(fine);
auto om = build_rf_fine(fine);
auto [t, w] = build_it_fine(fine, symmetrize);
auto om = build_rf_fine(fine, symmetrize);

// Get discretization of analytic continuation kernel on fine grids (the K
// matrix), weighted in the time variable by the square root of the
Expand Down
12 changes: 9 additions & 3 deletions c++/cppdlr/dlr_build.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,27 @@ namespace cppdlr {
* @brief Build fine composite Chebyshev grid in real frequency
*
* @param[in] fine Fine grid parameters
* @param[in] symmetrize If true, include omega=0, making the grid odd-sized and
* mirror-symmetric about it.
*
* @return Fine real frequency grid
*/
nda::vector<double> build_rf_fine(fineparams &fine);
nda::vector<double> build_rf_fine(fineparams const &fine, bool symmetrize = false);

/**
* @brief Get fine composite Legendre grid in imaginary time and corresponding
* square root quadrature weights
*
* @param[in] fine Fine grid parameters
* @param[in] symmetrize If true, include tau=beta/2 with zero
* quadrature weight, making the grid odd-sized and mirror-symmetric about it.
*
* @return Tuple containing fine imaginary time grid and corresponding square
* root quadrature weights
*
* \note Fine imaginary time grid is given in relative format
*/
std::tuple<nda::vector<double>, nda::vector<double>> build_it_fine(fineparams &fine);
std::tuple<nda::vector<double>, nda::vector<double>> build_it_fine(fineparams const &fine, bool symmetrize = false);

/**
* @brief Get imaginary time discretization of analytic continuation kernel
Expand Down Expand Up @@ -134,7 +138,7 @@ namespace cppdlr {
* \note \p kmat should be computed using the function get_kfine with composite
* Chebyshev grids produced by get_omfine and get_tfine
*/
std::tuple<double, double> geterr_k_it(fineparams &fine, nda::vector_const_view<double> t, nda::vector_const_view<double> om,
std::tuple<double, double> geterr_k_it(fineparams const &fine, nda::vector_const_view<double> t, nda::vector_const_view<double> om,
nda::matrix_const_view<double> kmat);

/**
Expand All @@ -152,6 +156,8 @@ namespace cppdlr {
*
* @return DLR frequencies
*
* @note With SYM the DLR frequencies are mirror-symmetric about omega=0, which is
* itself included as a self-paired node, so the rank is odd.
*/
nda::vector<double> build_dlr_rf(double lambda, double eps, bool symmetrize);

Expand Down
14 changes: 8 additions & 6 deletions c++/cppdlr/dlr_imfreq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ namespace cppdlr {
imfreq_ops::imfreq_ops(double lambda, nda::vector_const_view<double> dlr_rf, statistic_t statistic, bool symmetrize)
: lambda_(lambda), statistic(statistic), r(dlr_rf.size()), dlr_rf(dlr_rf) {

// Get # DLR imaginary frequency nodes; for symmetrized bosonic case, this
// is DLR rank + 1, otherwise it is DLR rank
niom = (statistic == Boson && symmetrize) ? r + 1 : r;
// # DLR imaginary frequency nodes. The symmetrized grid is mirror-symmetric
// about i*nu=0. The bosonic grid contains the self-paired node n=0, so niom = r,
// which is odd. The fermionic grid has no self-paired node, so it consists of
// mirror pairs only and needs the even niom = r + 1.
niom = (symmetrize && statistic == Fermion) ? r + 1 : r;
dlr_if = nda::vector<int>(niom);
cf2if = nda::matrix<dcomplex>(niom, r);

Expand Down Expand Up @@ -55,9 +57,9 @@ namespace cppdlr {
for (int j = 0; j < r; ++j) { cf2if(i, j) = kmat(piv(i), j); }
}

if (!(symmetrize && statistic == Boson)) {
// Prepare imaginary time values to coefficients transformation by computing
// LU factors of coefficient to imaginary time matrix
if (niom == r) {
// Square case: LU factors invert cf2if. The over-determined case stores no
// factors and uses a least-squares solve instead (see vals2coefs).
if2cf.lu = nda::matrix<dcomplex>(cf2if);
if2cf.piv = nda::vector<int>(r);
lapack::getrf(if2cf.lu, if2cf.piv);
Expand Down
7 changes: 4 additions & 3 deletions c++/cppdlr/dlr_imfreq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ namespace cppdlr {
* @param[in] symmetrize NONSYM or false for non-symmetrized DLR frequencies,
* SYM or true for symmetrized
*
* @note In case Boson and SYM options are selected, we enforce that i*nu_n=0 is
* chosen as a DLR imaginary frequency node.
* @note With SYM the DLR rank r is odd. Boson includes the self-symmetric node
* i*nu_n=0, giving a square niom = r. Fermion has no such node, so it uses
* niom = r + 1 mirror pairs and an over-determined map, inverted by least squares.
*/
imfreq_ops(double lambda, nda::vector_const_view<double> dlr_rf, statistic_t statistic, bool symmetrize = false);

Expand Down Expand Up @@ -245,7 +246,7 @@ namespace cppdlr {
double lambda_; ///< Energy cutoff divided by temperature
statistic_t statistic; ///< Particle statistic: Fermion or Boson
int r; ///< DLR rank
int niom; ///< # DLR imaginary freq nodes (different from r in symmetrized bosonic case)
int niom; ///< # DLR imaginary freq nodes (= r + 1 in the symmetrized fermionic case, else = r)
nda::vector<double> dlr_rf; ///< DLR frequencies
nda::vector<int> dlr_if; ///< DLR imaginary frequency nodes
nda::matrix<nda::dcomplex> cf2if; /// Transformation matrix from DLR coefficients to values at DLR imaginary frequency nodes
Expand Down
6 changes: 5 additions & 1 deletion c++/cppdlr/dlr_imtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ namespace cppdlr {

imtime_ops::imtime_ops(double lambda, nda::vector_const_view<double> dlr_rf, bool symmetrize) : lambda_(lambda), r(dlr_rf.size()), dlr_rf(dlr_rf) {

// The symmetrized selection always takes the self-paired tau=beta/2 node plus
// mirror pairs, so it can only produce an odd number of nodes.
if (symmetrize && r % 2 == 0) throw std::runtime_error("Symmetrized DLR frequency grid must have odd rank.");

dlr_it = nda::vector<double>(r);
cf2it = nda::matrix<double>(r, r);
it2cf.lu = nda::matrix<double>(r, r);
Expand All @@ -35,7 +39,7 @@ namespace cppdlr {
// Get discretization of analytic continuation kernel on fine grid in
// imaginary time, at DLR frequencies
auto fine = fineparams(lambda);
auto [t, w] = build_it_fine(fine);
auto [t, w] = build_it_fine(fine, symmetrize);
auto kmat = build_k_it(t, dlr_rf);

// Pivoted Gram-Schmidt to obtain DLR imaginary time nodes
Expand Down
4 changes: 4 additions & 0 deletions c++/cppdlr/dlr_imtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ namespace cppdlr {
* @param[in] dlr_rf DLR frequencies
* @param[in] symmetrize NONSYM or false for non-symmetrized DLR frequencies,
* SYM or true for symmetrized
*
* @note With SYM the nodes are mirror-symmetric about tau=beta/2, which is
* itself included as a self-paired node. SYM therefore requires @p dlr_rf of odd
* rank, as built by build_dlr_rf with SYM, and throws otherwise.
*/
imtime_ops(double lambda, nda::vector_const_view<double> dlr_rf, bool symmetrize);

Expand Down
Loading
Loading