TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
chebyshev.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 Simons Foundation
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You may obtain a copy of the License at
14// https://www.gnu.org/licenses/gpl-3.0.txt
15//
16// Authors: Nils Wentzell
17
22
23#pragma once
24
25#include "./mesh_iterator.hpp"
26#include "./utils.hpp"
28#include "../utility/macros.hpp"
29
30#include <fmt/format.h>
31#include <h5/h5.hpp>
32#include <nda/nda.hpp>
33
34#include <cstdint>
35#include <iostream>
36#include <memory>
37#include <string>
38
39namespace triqs::mesh {
40
45
71 class C2PY_RENAME(MeshChebyshev) chebyshev {
72 public:
74 using value_t = double;
75
77 using index_t = long;
78
80 using data_index_t = long;
81
87 class C2PY_IGNORE mesh_point_t {
88 public:
91
93 mesh_point_t() = default;
94
104 mesh_point_t(long n, long d, uint64_t mhash, double tau) : index_(n), data_index_(d), mesh_hash_(mhash), value_(tau) {}
105
107 [[nodiscard]] long index() const { return index_; }
108
110 [[nodiscard]] long data_index() const { return data_index_; }
111
113 [[nodiscard]] double value() const { return value_; }
114
116 [[nodiscard]] uint64_t mesh_hash() const noexcept { return mesh_hash_; }
117
119 operator double() const { return value_; } // NOLINT (implicit conversion intended)
120
121 // Arithmetic operations with scalars.
122#define IMPL_OP(OP) \
123 template <typename U> friend auto operator OP(mesh_point_t const &mp, U &&y) { return mp.value() OP std::forward<U>(y); } \
124 template <typename U> \
125 requires(not std::is_same_v<std::decay_t<U>, mesh_point_t>) \
126 friend auto operator OP(U &&x, mesh_point_t const &mp) { \
127 return std::forward<U>(x) OP mp.value(); \
128 }
129 IMPL_OP(+)
130 IMPL_OP(-)
131 IMPL_OP(*)
132 IMPL_OP(/)
133#undef IMPL_OP
134
135 private:
136 long index_ = 0;
137 long data_index_ = 0;
138 uint64_t mesh_hash_ = 0;
139 double value_ = 0.0;
140 };
141
143 chebyshev() = default;
144
152 chebyshev(double beta, statistic_enum stat, long N) : beta_(beta), stat_(stat), N_(N), mesh_hash_(hash(beta, stat, N)) {
153 EXPECTS(beta_ > 0);
154 EXPECTS(N_ > 0);
155 init_arrays();
156 }
157
159 bool operator==(chebyshev const &) const = default;
160
167 [[nodiscard]] bool is_index_valid(index_t n) const noexcept { return 0 <= n and n < N_; }
168
175 [[nodiscard]] data_index_t to_data_index(index_t n) const noexcept {
176 EXPECTS(is_index_valid(n));
177 return n;
178 }
179
186 [[nodiscard]] index_t to_index(data_index_t d) const noexcept {
187 EXPECTS(is_index_valid(d));
188 return d;
189 }
190
197 [[nodiscard]] value_t to_value(index_t n) const noexcept {
198 EXPECTS(is_index_valid(n));
199 return points_scaled_[n];
200 }
201
208 [[nodiscard]] mesh_point_t operator[](long d) const { return {to_index(d), d, mesh_hash_, to_value(d)}; }
209
216 [[nodiscard]] mesh_point_t operator()(long n) const { return {n, to_data_index(n), mesh_hash_, to_value(n)}; }
217
219 [[nodiscard]] C2PY_PROPERTY_GET(beta) double beta() const noexcept { return beta_; }
220
222 [[nodiscard]] double inv_beta_x2() const noexcept { return inv_beta_x2_; }
223
225 [[nodiscard]] C2PY_PROPERTY_GET(statistic) statistic_enum statistic() const noexcept { return stat_; }
226
228 [[nodiscard]] long size() const { return N_; }
229
231 [[nodiscard]] C2PY_PROPERTY_GET(mesh_hash) uint64_t mesh_hash() const { return mesh_hash_; }
232
234 [[nodiscard]] nda::vector_const_view<double> points_standard() const { return points_standard_; }
235
237 [[nodiscard]] C2PY_PROPERTY_GET(points) nda::vector_const_view<double> points() const { return points_scaled_; }
238
240 [[nodiscard]] C2PY_PROPERTY_GET(weights) nda::vector_const_view<double> weights() const { return weights_; }
241
243 [[nodiscard]] auto begin() const { return mesh_iterator<chebyshev>{.mesh_ptr = this, .data_index = 0}; }
244
246 [[nodiscard]] auto cbegin() const { return begin(); }
247
249 [[nodiscard]] auto end() const { return mesh_iterator<chebyshev>{.mesh_ptr = this, .data_index = size()}; }
250
252 [[nodiscard]] auto cend() const { return end(); }
253
261 friend std::ostream &operator<<(std::ostream &sout, chebyshev const &m) {
262 auto stat_cstr = (m.stat_ == Boson ? "Boson" : "Fermion");
263 return sout << fmt::format("Chebyshev mesh with beta = {}, statistics = {}, N = {}", m.beta_, stat_cstr, m.N_);
264 }
265
270 void serialize(auto &ar) const { ar & beta_ & stat_ & N_ & mesh_hash_; }
271
276 void deserialize(auto &ar) {
277 ar & beta_ & stat_ & N_ & mesh_hash_;
278 if (N_ > 0) { init_arrays(); }
279 }
280
282 [[nodiscard]] static std::string hdf5_format() { return "MeshChebyshev"; }
283
291 friend void h5_write(h5::group g, std::string const &name, chebyshev const &m) {
292 h5::group gr = g.create_group(name);
293 h5::write_hdf5_format(gr, m); // NOLINT (downcasting to base class)
294 h5::write(gr, "beta", m.beta_);
295 h5::write(gr, "statistic", (m.stat_ == Fermion ? "F" : "B"));
296 h5::write(gr, "n_chebyshev", m.N_);
297 }
298
306 friend void h5_read(h5::group g, std::string const &name, chebyshev &m) {
307 h5::group gr = g.open_group(name);
308 h5::assert_hdf5_format(gr, m, true); // NOLINT (downcasting to base class)
309
310 auto beta = h5::read<double>(gr, "beta");
311 auto statistic = (h5::read<std::string>(gr, "statistic") == "F" ? Fermion : Boson);
312 auto N = h5::read<long>(gr, "n_chebyshev");
313
314 m = chebyshev(beta, statistic, N);
315 }
316
317 private:
318 // Initialize precomputed arrays from beta_ and N_
319 void init_arrays() {
320 inv_beta_x2_ = 2.0 / beta_;
321 points_standard_ = utility::chebyshev_points(N_);
323 points_scaled_ = nda::vector<double>(N_);
324 for (long i = 0; i < N_; ++i) { points_scaled_[i] = utility::from_standard_interval(points_standard_[i], 0.0, beta_); }
325 }
326
327 double beta_ = 1.0;
328 double inv_beta_x2_ = 2.0; // precomputed 2.0 / beta_ for fast tau -> [-1, 1] mapping
329 statistic_enum stat_ = Fermion;
330 long N_ = 0;
331 uint64_t mesh_hash_ = 0;
332
333 // Precomputed arrays.
334 nda::vector<double> points_standard_{}; // Chebyshev points on [-1, 1]
335 nda::vector<double> points_scaled_{}; // Chebyshev points on [0, beta]
336 nda::vector<double> weights_{}; // Barycentric weights
337 };
338
339 namespace detail {
340
341 // Type-correct return for exact mesh point match
342 template <typename F> auto make_exact_result(F const &f, long i) {
343 using R = std::decay_t<decltype(f(0))>;
344 if constexpr (nda::is_scalar_v<R>) {
345 return f(i);
346 } else if constexpr (requires { f(0).mesh(); }) {
347 return typename R::regular_type{f(i)};
348 } else {
349 return nda::make_regular(f(i));
350 }
351 }
352
353 // Scalar barycentric interpolation
354 template <typename R, typename F, typename Points, typename Weights>
355 R barycentric_scalar(double x, Points const &points, Weights const &weights, F const &f, long N) {
356 // For complex callables (e.g. product mesh curry), precompute values to avoid repeated lambda overhead
357 constexpr bool f_is_complex = sizeof(f) > 2 * sizeof(void *);
358
359 if constexpr (f_is_complex) {
360 for (long i = 0; i < N; ++i) {
361 if (x == points[i]) { return f(i); }
362 }
363
364 constexpr long stack_threshold = 128;
365 R fval_stack[stack_threshold];
366 std::unique_ptr<R[]> fval_heap;
367 R *fval = (N <= stack_threshold) ? fval_stack : (fval_heap = std::make_unique<R[]>(N)).get();
368 for (long i = 0; i < N; ++i) { fval[i] = f(i); }
369
370 R num{0};
371 double den = 0.0;
372 for (long i = 0; i < N; ++i) {
373 double q = weights[i] / (x - points[i]);
374 num += q * fval[i];
375 den += q;
376 }
377 return num / den;
378 } else {
379 // Simple accessor: single-pass barycentric formula
380 R num{0};
381 double den = 0.0;
382 for (long i = 0; i < N; ++i) {
383 double diff = x - points[i];
384 if (diff == 0.0) { return f(i); }
385 double q = weights[i] / diff;
386 num += q * f(i);
387 den += q;
388 }
389 return num / den;
390 }
391 }
392
393 // Array/gf barycentric interpolation
394 template <typename F, typename Points, typename Weights>
395 auto barycentric_array(double x, Points const &points, Weights const &weights, F const &f, long N) {
396 using f_ret_t = std::decay_t<decltype(f(0))>;
397
398 // Compute normalized barycentric weights
399 constexpr long stack_threshold = 512;
400 double q_stack[stack_threshold];
401 std::unique_ptr<double[]> q_heap;
402 double *q = (N <= stack_threshold) ? q_stack : (q_heap = std::make_unique<double[]>(N)).get();
403
404 double denominator = 0.0;
405 for (long i = 0; i < N; ++i) {
406 double diff = x - points[i];
407 if (diff == 0.0) { return make_exact_result(f, i); }
408 q[i] = weights[i] / diff;
409 denominator += q[i];
410 }
411 double inv_denom = 1.0 / denominator;
412 for (long i = 0; i < N; ++i) { q[i] *= inv_denom; }
413
414 // Accumulate weighted sum
415 if constexpr (requires { f(0).mesh(); }) {
416 using gf_t = typename f_ret_t::regular_type;
417 gf_t result{q[0] * f(0)};
418 for (long i = 1; i < N; ++i) { result() += q[i] * f(i); }
419 return result;
420 } else {
421 constexpr bool f_returns_expression = !requires { f(0).data(); };
422
423 auto result = nda::make_regular(q[0] * f(0));
424 using scalar_t = typename decltype(result)::value_type;
425 scalar_t *r = result.data();
426 long const sz = result.size();
427
428 for (long i = 1; i < N; ++i) {
429 auto const fi = [&] {
430 if constexpr (f_returns_expression)
431 return nda::make_regular(f(i));
432 else
433 return f(i);
434 }();
435 scalar_t const *fi_data = fi.data();
436 for (long j = 0; j < sz; ++j) { r[j] += q[i] * fi_data[j]; }
437 }
438 return result;
439 }
440 }
441
442 } // namespace detail
443
459 inline auto evaluate(chebyshev const &m, auto const &f, double tau) {
460 EXPECTS(m.size() > 0 and tau >= 0 and tau <= m.beta());
461
462 double x = tau * m.inv_beta_x2() - 1.0;
463 auto const &points = m.points_standard();
464 auto const &weights = m.weights();
465 long N = m.size();
466
467 using f_ret_t = std::decay_t<decltype(f(0))>;
468
469 if constexpr (nda::is_scalar_v<f_ret_t>)
470 return detail::barycentric_scalar<f_ret_t>(x, points, weights, f, N);
471 else
472 return detail::barycentric_array(x, points, weights, f, N);
473 }
474
476
477} // namespace triqs::mesh
iterator end()
Get an iterator past the last block.
iterator begin()
Get an iterator to the first block.
int size() const
Get the total number of blocks.
mesh_point_t()=default
Default constructor leaves the mesh point uninitialized.
chebyshev()=default
Default constructor constructs an empty mesh.
Mesh point of a triqs::mesh::chebyshev mesh.
Definition chebyshev.hpp:87
double value() const
Get the value of the mesh point.
mesh_point_t()=default
Default constructor leaves the mesh point uninitialized.
uint64_t mesh_hash() const noexcept
Get the hash value of the parent mesh.
long index() const
Get the index of the mesh point.
chebyshev mesh_t
Parent mesh type.
Definition chebyshev.hpp:90
long data_index() const
Get the data index of the mesh point.
mesh_point_t(long n, long d, uint64_t mhash, double tau)
Construct a mesh point with a given index , data index , hash value of the parent mesh and value .
Chebyshev imaginary time mesh type.
Definition chebyshev.hpp:71
bool operator==(chebyshev const &) const =default
Equal-to comparison operator compares , and the particle statistics.
double inv_beta_x2() const noexcept
Get the precomputed for fast tau -> [-1, 1] mapping.
auto begin() const
Get an iterator to the beginning of the mesh.
value_t to_value(index_t n) const noexcept
Map an index to its corresponding value .
auto cbegin() const
Get a const iterator to the beginning of the mesh.
statistic_enum statistic() const noexcept
Get the particle statistics.
auto end() const
Get an iterator to the end of the mesh.
long size() const
Get the size of the mesh, i.e. the number of mesh points.
nda::vector_const_view< double > points_standard() const
Access to Chebyshev points on [-1, 1].
uint64_t mesh_hash() const
Get the hash value of the mesh.
long data_index_t
Data index type.
Definition chebyshev.hpp:80
static std::string hdf5_format()
Get the HDF5 format tag.
mesh_point_t operator[](long d) const
Subscript operator to access a mesh point by its data index .
chebyshev(double beta, statistic_enum stat, long N)
Construct a Chebyshev mesh on with collocation points.
mesh_point_t operator()(long n) const
Function call operator to access a mesh point by its index .
nda::vector_const_view< double > weights() const
Access to barycentric weights.
friend void h5_read(h5::group g, std::string const &name, chebyshev &m)
Read a triqs::mesh::chebyshev mesh from HDF5.
auto cend() const
Get a const iterator to the end of the mesh.
index_t to_index(data_index_t d) const noexcept
Map a data index to the corresponding index .
data_index_t to_data_index(index_t n) const noexcept
Map an index to its corresponding data index .
double beta() const noexcept
Get the inverse temperature .
nda::vector_const_view< double > points() const
Access to Chebyshev points scaled to [0, beta].
void serialize(auto &ar) const
Serialize the mesh to a generic archive.
friend void h5_write(h5::group g, std::string const &name, chebyshev const &m)
Write a triqs::mesh::chebyshev mesh to HDF5.
double value_t
Value type (imaginary time tau).
Definition chebyshev.hpp:74
bool is_index_valid(index_t n) const noexcept
Check if an index is valid.
friend std::ostream & operator<<(std::ostream &sout, chebyshev const &m)
Write a triqs::mesh::chebyshev mesh to a std::ostream.
chebyshev()=default
Default constructor constructs an empty mesh.
long index_t
Index type.
Definition chebyshev.hpp:77
void deserialize(auto &ar)
Deserialize the mesh from a generic archive.
statistic_enum
Enum to specify particle statistics.
Definition utils.hpp:163
uint64_t hash(Ts &&...ts)
Generic hash function for multiple arguments.
Definition utils.hpp:70
nda::vector< double > chebyshev_barycentric_weights(long N)
Compute barycentric weights for Chebyshev points of the first kind.
Definition chebyshev.hpp:73
nda::vector< double > chebyshev_points(long N)
Compute Chebyshev points of the first kind on the interval .
Definition chebyshev.hpp:50
double from_standard_interval(double x, double a, double b)
Scale a value from the standard interval to interval .
Common macros used in TRIQS.
Provides various utilities used with Meshes.
Provides a generic random access iterator for 1D meshes.
A generic random access iterator for 1D meshes.
Provides utilities for Chebyshev polynomial computations.