diff --git a/test/triqs/gfs/test_fit_tail.cpp b/test/triqs/gfs/test_fit_tail.cpp new file mode 100644 index 00000000..d7bf9a80 --- /dev/null +++ b/test/triqs/gfs/test_fit_tail.cpp @@ -0,0 +1,77 @@ +#//define TRIQS_ARRAYS_ENFORCE_BOUNDCHECK + +#include +#include + +using triqs::arrays::make_shape; +using namespace triqs::gfs; +using triqs::gfs::local::tail; +#define TEST(X) std::cout << BOOST_PP_STRINGIZE((X)) << " ---> "<< (X) < iom_; + double beta =10; + int N=100; + + auto gw = gf{{beta, Fermion, N}, {1, 1}}; + + triqs::arrays::array c(3); + triqs::clef::placeholder<1> i_; + + c(i_) << (2*i_+1); + + int size=0; //means we don't know any moments + int order_min=1; //means that the first moment in the final tail will be the first moment + auto known_moments = tail(make_shape(1,1), size, order_min); //length is 0, first moment to fit is order_min + + gw(iom_) << c(0)/iom_ + c(1)/iom_/iom_ + c(2)/iom_/iom_/iom_; + + //show tail +// std::cout<< "before fitting:" < precision) TRIQS_RUNTIME_ERROR<<" fit_tail error : diff="< precision) TRIQS_RUNTIME_ERROR<<" fit_tail error : diff="<. + * + ******************************************************************************/ +#ifndef TRIQS_GF_LOCAL_FIT_TAIL_H +#define TRIQS_GF_LOCAL_FIT_TAIL_H +#include +#include +#include +#include +#include + +namespace triqs { +namespace gfs { + namespace local { + + using triqs::gfs::imfreq; + using triqs::gfs::block_index; + using triqs::gfs::block_index; + + namespace tgl = triqs::gfs::local; + + // routine for fitting the tail (singularity) of a Matsubara Green's function + // this is a *linear* least squares problem (with non-linear basis functions) + // which is solved by singular value decomposition of the design matrix + // the routine fits the real part (even moments) and the imaginary part + //(odd moments) separately, since this is more stable + + // input: + // the input gf Green's function: gf + // the known moments in the form of a tail(_view): known_moments + // the TOTAL number of desired moments (including the known ones): n_moments + // the index of the first and last frequency to fit (the last one is included): wn_min, wn_max + + // output: returns the tail obtained by fitting + + tail fit_tail_impl(gf &gf, const tail_view known_moments, int n_moments, int wn_min, int wn_max) { + + tail res(get_target_shape(gf), n_moments, known_moments.order_min()); + if (known_moments.size()) + for (int i = known_moments.order_min(); i <= known_moments.order_max(); i++) res(i) = known_moments(i); + + // if known_moments.size()==0, the lowest order to be obtained from the fit is determined by order_min in known_moments + // if known_moments.size()==0, the lowest order is the one following order_max in known_moments + + const double beta = gf.mesh().domain().beta; + + int n_unknown_moments = n_moments - known_moments.size(); + if (n_unknown_moments < 1) return known_moments; + + // get the number of even unknown moments: it is n_unknown_moments/2+1 if the first + // moment is even and n_moments is odd; n_unknown_moments/2 otherwise + int omin = known_moments.size() == 0 ? known_moments.order_min() : known_moments.order_max() + 1; // smallest unknown moment + int omin_even = omin % 2 == 0 ? omin : omin + 1; + int omin_odd = omin % 2 != 0 ? omin : omin + 1; + int size_even = n_unknown_moments / 2; + if (n_unknown_moments % 2 != 0 && omin % 2 == 0) size_even += 1; + int size_odd = n_unknown_moments - size_even; + + int size1 = wn_max - wn_min + 1; + // size2 is the number of moments + + arrays::matrix A(size1, std::max(size_even, size_odd), FORTRAN_LAYOUT); + arrays::matrix B(size1, 1, FORTRAN_LAYOUT); + arrays::vector S(std::max(size_even, size_odd)); + const double rcond = 0.0; + int rank; + + for (size_t i = 0; i < get_target_shape(gf)[0]; i++) { + for (size_t j = 0; j < get_target_shape(gf)[1]; j++) { + + // fit the odd moments + // S.resize(size_odd); + // A.resize(size1,size_odd); //when resizing, gelss segfaults + for (int k = 0; k < size1; k++) { + auto n = wn_min + k; + auto iw = std::complex(0.0, (2 * n + 1) * M_PI / beta); + + B(k, 0) = imag(gf.data()(n, i, j)); + // subtract known tail if present + if (known_moments.size() > 0) + B(k, 0) -= imag(slice_target(known_moments, arrays::range(i, i + 1), arrays::range(j, j + 1)).evaluate(iw)(0, 0)); + + for (int l = 0; l < size_odd; l++) { + int order = omin_odd + 2 * l; + A(k, l) = imag(pow(iw, -1.0 * order)); // set design matrix for odd moments + } + } + + arrays::lapack::gelss(A, B, S, rcond, rank); + for (int m = 0; m < size_odd; m++) { + res(omin_odd + 2 * m)(i, j) = B(m, 0); + } + + // fit the even moments + // S.resize(size_even); + // A.resize(size1,size_even); //when resizing, gelss segfaults + for (int k = 0; k < size1; k++) { + auto n = wn_min + k; + auto iw = std::complex(0.0, (2 * n + 1) * M_PI / beta); + + B(k, 0) = real(gf.data()(n, i, j)); + // subtract known tail if present + if (known_moments.size() > 0) + B(k, 0) -= real(slice_target(known_moments, arrays::range(i, i + 1), arrays::range(j, j + 1)).evaluate(iw)(0, 0)); + + for (int l = 0; l < size_even; l++) { + int order = omin_even + 2 * l; + A(k, l) = real(pow(iw, -1.0 * order)); // set design matrix for odd moments + } + } + + arrays::lapack::gelss(A, B, S, rcond, rank); + for (int m = 0; m < size_even; m++) { + res(omin_even + 2 * m)(i, j) = B(m, 0); + } + } + } + + return res; // return tail + } + + + void set_tail_from_fit(gf &gf, tail_view known_moments, int n_moments, size_t wn_min, size_t wn_max, + bool replace_by_fit = false) { + if (get_target_shape(gf) != known_moments.shape()) TRIQS_RUNTIME_ERROR << "shape of tail does not match shape of gf"; + gf.singularity() = fit_tail_impl(gf, known_moments, n_moments, wn_min, wn_max); + + if (replace_by_fit) { // replace data in the fitting range by the values from the fitted tail + size_t i = 0; + for (auto iw : gf.mesh()) { // (arrays::range(wn_min,wn_max+1)) { + if ((i >= wn_min) && (i <= wn_max)) gf[iw] = gf.singularity().evaluate(iw); + i++; + } + } + } + + + void set_tail_from_fit(gf> &block_gf, tail_view known_moments, int n_moments, size_t wn_min, + size_t wn_max, bool replace_by_fit = false) { + // for(auto &gf : block_gf) set_tail_from_fit(gf, known_moments, n_moments, wn_min, wn_max, replace_by_fit); + for (size_t i = 0; i < block_gf.mesh().size(); i++) + set_tail_from_fit(block_gf[i], known_moments, n_moments, wn_min, wn_max, replace_by_fit); + } + } +} +} // namespace +#endif