/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2014 by T. Ayral, O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * TRIQS. If not, see . * ******************************************************************************/ #pragma once #include #include #include #include #include #include #include namespace triqs { namespace statistics { // trait to find out if T models the concept TimeSeries template struct is_time_series : std::false_type {}; template struct is_time_series : is_time_series {}; template struct is_time_series : is_time_series {}; template struct is_time_series : is_time_series {}; template struct is_time_series> : std::true_type {}; /* ********************************************************* * * Binning * * ********************************************************/ template class binned_series { int bin_size; std::vector binned; public: using value_type = ValueType; template binned_series(TimeSeries const& t, int bin_size_) : bin_size(bin_size_), binned(t.size() / bin_size_, value_type{}) { if (bin_size_ > t.size()) TRIQS_RUNTIME_ERROR << "bin size (" << bin_size_ << ") cannot be larger than size (" << t.size() << ") of time series"; for (int i = 0; i < size(); i++) { for (int j = 0; j < bin_size; j++) binned[i] += t[i * bin_size + j]; binned[i] /= bin_size; } } value_type operator[](int i) const { return binned[i]; } int size() const { return binned.size(); } std::vector const & data() const & { return binned;} std::vector const & data() & { return binned;} std::vector data() && { return std::move(binned);} using const_iterator = typename std::vector::const_iterator; const_iterator begin() const { return binned.begin(); } const_iterator end() const { return binned.end(); } friend std::ostream& operator<<(std::ostream& out, binned_series const& s_) { for (auto const& x : s_.binned) out << x << " "; return out; } }; template struct is_time_series> : std::true_type {}; /// Factory template binned_series make_binned_series(TimeSeries const& t, int bin_size) { return {t, bin_size}; } /* ********************************************************* * * TS_observer: an implementation class * Contains a ref or a value to a TS, and the implementation of the const_iterator * * ********************************************************/ template class ts_observer { // TimeSeries can be a T or a T & protected: TimeSeries ts; public: using value_type = typename std::remove_reference::type::value_type; template ts_observer(TS&& t_) : ts(std::forward(t_)) {} ts_observer(ts_observer const&) = default; ts_observer(ts_observer&&) = default; ts_observer& operator=(ts_observer const&) = delete; ts_observer& operator=(ts_observer&&) = default; int size() const { return ts.size(); } // const_iterator class const_iterator : public boost::iterator_facade { friend class boost::iterator_core_access; Derived const* t; int u; void increment() { ++u; } value_type dereference() const { return (*t)[u]; } bool equal(const_iterator const& other) const { return ((t == other.t) && (other.u == u)); } public: const_iterator(Derived const* m, bool at_end) : t(m) { u = (at_end ? m->size() : 0); } }; const_iterator begin() const { return {static_cast(this), false}; } const_iterator end() const { return {static_cast(this), true}; } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } // printing friend std::ostream& operator<<(std::ostream& out, ts_observer const& s_) { for (auto const& x : s_) out << x << " "; return out; } }; /* ********************************************************* * * Jackknife * * ********************************************************/ template class jackknife : public ts_observer, TimeSeries> { using B = ts_observer, TimeSeries>; typename B::value_type sum; public: template jackknife(TS&& t_) : B(std::forward(t_)) { sum = typename B::value_type{0 * this->ts[0]}; auto si = this->ts.size(); for (int i = 0; i < si; i++) sum += this->ts[i]; } typename B::value_type operator[](int i) const { return (sum - this->ts[i]) / (this->size() - 1); } }; /// template jackknife make_jackknife(TimeSeries&& t) { return {std::forward(t)}; } template struct is_time_series> : std::true_type {}; /* ********************************************************* * * Observable * * ********************************************************/ template class observable { std::vector _series; public: observable() { _series.reserve(1000); } observable(binned_series && s):_series(std::move(s).data()){} observable& operator<<(T x) { // copy and move : check speed ... or overload const &, && _series.push_back(std::move(x)); return *this; } template observable& operator<<(A&& a) { _series.emplace_back(std::forward(a)); return *this; } // TimeSeries concept using value_type = T; int size() const { return _series.size(); } T operator[](int i) const { return _series[i]; } }; template struct is_time_series> : std::true_type {}; /* ********************************************************* * * Expressions * * ********************************************************/ // -------------- clef leaf evaluation ---------------------- struct repl_by_jack {}; struct bin_and_repl_by_jack { int bin_size; }; template auto eval(observable const& obs, repl_by_jack) DECL_AND_RETURN(make_jackknife(obs)); template auto eval(observable const& obs, bin_and_repl_by_jack info) DECL_AND_RETURN(make_jackknife(make_binned_series(obs), info.bin_size)); template auto eval(TS const& obs, int i) -> std::c14::enable_if_t::value, decltype(obs[i])> { return obs[i]; } // --------- Operations -------------------------- // The principle is : // All operations between a time_series and anything results in a clef lazy expression // This implements the case of binary/unary operators when there is no clef expression (which is already handled by clef operators // hence this avoid ambiguity). #define TRIQS_TS_OPERATION(TAG, OP) \ template \ std::c14::enable_if_t<(is_time_series::value || is_time_series::value) && (!clef::is_any_lazy::value), \ clef::expr_node_t> operator OP(L&& l, R&& r) { \ return {clef::tags::TAG(), std::forward(l), std::forward(r)}; \ } TRIQS_TS_OPERATION(plus, +); TRIQS_TS_OPERATION(minus, -); TRIQS_TS_OPERATION(multiplies, *); TRIQS_TS_OPERATION(divides, / ); #undef TRIQS_TS_OPERATION // Any function overloaded for clef should also accept object modelling is_time_series // Here : define all math function defined for clef ... #define AUX(r, data, elem) TRIQS_CLEF_EXTEND_FNT_LAZY(elem, is_time_series) BOOST_PP_SEQ_FOR_EACH(AUX, nil, TRIQS_CLEF_STD_MATH_FNT_TO_MAKE_LAZY); #undef AUX #undef TRIQS_TS_FUNCTION // ------------- Dress an expression as a time serie -------------------------- template struct _immutable_time_series { Expr expr; using value_type = typename std::remove_reference::type; value_type operator[](int i) const { return eval(expr, i); } int size() const { return get_size(expr); } }; template struct is_time_series<_immutable_time_series> : std::true_type {}; // make_immutable_time_series (x) returns : // x if it is already a time_series // _immutable_time_series(x) if it is an expression template std::c14::enable_if_t::value, _immutable_time_series> make_immutable_time_series(T&& x) { return {std::forward(x)}; } template std::c14::enable_if_t::value, T> make_immutable_time_series(T&& x) { return std::forward(x); } // ------------- Computation of the size -------------------------- // a function object that when called on x, returns nothing but : // if x models TimeSeries : check if its sizes is equal to previously encountered and stores it // otherwise do nothing struct _get_size_visitor { int res; template std::c14::enable_if_t::value> operator()(T const&) {} template std::c14::enable_if_t::value> operator()(T const& obs) { int i = obs.size(); if ((res * i != 0) && (res != i)) TRIQS_RUNTIME_ERROR << "Expression of time series with time mismatch"; res = i; // keep the result } }; template int get_size(T const& x) { auto l = _get_size_visitor{0}; clef::apply_on_each_leaf(l, x); return l.res; } /* ********************************************************* * * Average and error * * ********************************************************/ // ------------- A value and its error -------------------------- template struct value_and_error_bar { T value, error_bar; // error is variance : a T???? complex ?? friend std::ostream& operator<<(std::ostream& out, value_and_error_bar const& ve) { return out << ve.value << " +/- " << ve.error_bar; } }; // ------------- empirical average and variance -------------------------- template typename TimeSeries::value_type empirical_average(TimeSeries const& t) { auto si = t.size(); if (si == 0) return typename TimeSeries::value_type{}; auto sum = t[0]; for (int i = 1; i < si; ++i) sum += t[i]; return sum / t.size(); } /// template typename TimeSeries::value_type empirical_variance(TimeSeries const& t) { auto si = t.size(); if (si == 0) return typename TimeSeries::value_type{}; auto avg = t[0]; decltype(avg) sum = t[0] * t[0]; // also valid if t[0] is an array e.g., i.e. no trivial contructor... for (int i = 1; i < si; ++i) { sum += t[i] * t[i]; avg += t[i]; } avg /= t.size(); sum /= t.size(); return sum - avg * avg; } // ------------- Overload average for observables and expressions of observables -------------------------- template T average(observable const& obs) { return empirical_average(obs); } template std::c14::enable_if_t::value, double> average(ObservableExpr const& obs) { return empirical_average(_immutable_time_series{obs}); } // ------------- Overload average and error for observables and expressions of observables -------------------------- template value_and_error_bar empirical_average_and_error(T const& ts) { using std::sqrt; return {empirical_average(ts), sqrt((ts.size() - 1.0) * (empirical_variance(ts)))}; } template value_and_error_bar average_and_error(observable const& obs) { auto ts = make_jackknife(obs); return empirical_average_and_error(ts); } template value_and_error_bar average_and_error(observable const& obs, int bin_size) { auto ts = make_jackknife(make_binned_series(obs, bin_size)); return empirical_average_and_error(ts); } template std::c14::enable_if_t::value, value_and_error_bar> average_and_error(ObservableExpr const& obs) { auto expr_jack = eval(obs, repl_by_jack{}); // replace every TS leaf by a jacknifed version return empirical_average_and_error(make_immutable_time_series(expr_jack)); } template std::c14::enable_if_t::value, value_and_error_bar> average_and_error(ObservableExpr const& obs, int bin_size) { auto expr_bin_jack = eval(obs, bin_and_repl_by_jack{bin_size}); return empirical_average_and_error(make_immutable_time_series(expr_bin_jack)); } /* ********************************************************* * * Auto-correlations * * ********************************************************/ // ------ k-> ( - ^2 )/ ( - ^2 ) -------------------- template class normalized_autocorrelation : public ts_observer, TimeSeries> { using B = ts_observer, TimeSeries>; typename B::value_type var, avg2; public: template normalized_autocorrelation(TS&& t_) : B(std::forward(t_)) { var = empirical_variance(this->ts); auto avg = empirical_average(this->ts); avg2 = avg * avg; } typename B::value_type operator[](int k) const { const int N = this->size(); auto r = typename B::value_type{0 * this->ts[0]}; for (int i = 0; i < N - k; i++) r += this->ts[i + k] * this->ts[i]; r = (r / (N - k) - avg2) / var; return r; } }; /// template normalized_autocorrelation make_normalized_autocorrelation(TimeSeries&& t) { return {std::forward(t)}; } // ------ Auto-correlation time from the computation of the autocorrelation -------------------- template int autocorrelation_time(TimeSeries const& a) { auto normalized_autocorr = make_normalized_autocorrelation(make_immutable_time_series(a)); // is a N*dim_f matrix... double t_int = normalized_autocorr[0]; // in principle, a vector dim_f double coeff_tau = 6; // if exponential decay -> 0.25 % precision for (int l_max = 1; l_max < coeff_tau * t_int; l_max++) t_int += normalized_autocorr[l_max]; return int(t_int); } // ------ Auto-correlation time from binning -------------------- template double autocorrelation_time_from_binning(TimeSeries const& A, double intrinsic_variance, double precision = 0.05) { auto size = make_immutable_time_series(A).size(); auto t_cor = [&](int b) { auto A_binned = make_binned_series(make_immutable_time_series(A), b); double var = empirical_variance(A_binned); return 0.5 * (b * var / intrinsic_variance - 1.); }; double coeff = -6 * std::log(precision); // heuristic -> 0.25 % precision int B = 2; while (B < coeff * std::abs(t_cor(B))) ++B; // now, B is large enough: average over a few estimates int Navg = std::min(100, int(size - B)); double autocorr_time = 0.; for (int b = 0; b < Navg; b++) autocorr_time += t_cor(B + b); return autocorr_time / Navg; } // ------ Auto-correlation time from binning -------------------- template double autocorrelation_time_from_binning2(TimeSeries const& A) { auto size = make_immutable_time_series(A).size(); double var1 = empirical_variance(make_immutable_time_series(A)); auto t_cor = [&](int b) { auto A_binned = make_binned_series(make_immutable_time_series(A), b); double var = empirical_variance(A_binned); return .5 * var / var1 * b; }; int B = 2; double autocorr_time = t_cor(B); double slope = 1.; int small_slope_count = 0; std::vector t; while (small_slope_count < 100 && B < size / 10) { double t_cor_new = t_cor(++B); slope = (std::abs(t_cor_new - autocorr_time) / autocorr_time); if (slope < 1e-5) small_slope_count++; if (small_slope_count > 0) t.push_back(t_cor_new); autocorr_time = t_cor_new; } return empirical_average(t); } template double t_cor(TimeSeries const & A, int bin_size, double var1){ double var = empirical_variance(A); return .5 * var / var1 * bin_size; } template double autocorrelation_time_from_binning(TimeSeries const& A) { auto size = make_immutable_time_series(A).size(); double var1 = empirical_variance(make_immutable_time_series(A)); int B = 2; auto Ab=make_binned_series(A,2); double autocorr_time = t_cor(Ab, B, var1); double slope = 1.; int small_slope_count = 0; std::vector t; while (small_slope_count < 5 && B < size / 10) { B*=2; Ab=make_binned_series(Ab,2); double t_cor_new = t_cor(Ab, B, var1); slope = (std::abs(t_cor_new - autocorr_time) / autocorr_time); if (slope < .5*1e-1) small_slope_count++; if (small_slope_count > 0) t.push_back(t_cor_new); autocorr_time = t_cor_new; //std::cout << B << "\t" << t_cor_new << "\t" << slope << std::endl; } if (t.size()>0) {return empirical_average(t);} else{ std::cout << "autocorrelation time not converged!!" << std::endl; return autocorr_time;} } } // namespace statistics } // triqs