25#include <boost/archive/binary_iarchive.hpp>
26#include <boost/archive/binary_oarchive.hpp>
27#include <boost/iostreams/device/array.hpp>
28#include <boost/iostreams/stream.hpp>
29#include <boost/serialization/string.hpp>
30#include <boost/serialization/vector.hpp>
45int min(
int i,
unsigned int j) {
return std::min(i,
static_cast<int>(j)); }
51 std::vector<double>
const &A2) {
52 assert(A1.size() == A2.size());
53 std::vector<double> A_compressed(A1.size());
55 std::ranges::transform(A1, A2, A_compressed.begin(),
56 [](
double a,
double b) { return 0.5 * (a + b); });
64 [[maybe_unused]] std::vector<double>
const &A2) {
65 assert(A1.size() == A2.size());
66 std::vector<double> A_compressed(A2);
73 [[maybe_unused]] std::vector<double>
const &A2) {
74 assert(A1.size() == A2.size());
75 std::vector<double> A_compressed(A1);
80 std::vector<double>
const &B,
82 if (A.size() != B.size()) {
83 throw std::runtime_error(
84 "Error in scalar product: The vector sizes do not match");
87 auto const result = std::inner_product(A.begin(), A.end(), B.begin(), 0.0);
92 std::vector<double>
const &B,
94 std::vector<double> C(A.size());
95 if (A.size() != B.size()) {
96 throw std::runtime_error(
97 "Error in componentwise product: The vector sizes do not match");
100 std::ranges::transform(A, B, C.begin(), std::multiplies<>());
106 std::vector<double>
const &B,
108 std::vector<double> C(A.size() * B.size());
109 auto C_it = C.begin();
121 std::vector<double>
const &B,
123 if (A.size() != B.size()) {
124 throw std::runtime_error(
125 "Error in square distance componentwise: The vector sizes do not "
129 std::vector<double> C(A.size());
131 std::ranges::transform(A, B, C.begin(), [](
double a,
double b) ->
double {
132 return Utils::sqr(a - b);
140std::vector<double>
fcs_acf(std::vector<double>
const &A,
141 std::vector<double>
const &B,
143 if (A.size() != B.size()) {
144 throw std::runtime_error(
145 "Error in fcs_acf: The vector sizes do not match.");
148 auto const C_size = A.size() / 3u;
149 assert(3u * C_size == A.size());
151 std::vector<double> C{};
154 for (std::size_t i = 0u; i < C_size; i++) {
156 for (std::size_t j = 0u; j < 3u; j++) {
157 auto const a = A[3u * i + j];
158 auto const b = B[3u * i + j];
161 C.emplace_back(std::exp(acc));
167void Correlator::initialize_operations() {
170 if (m_tau_lin == 1) {
171 m_tau_lin =
static_cast<int>(std::ceil(m_tau_max / m_dt));
172 m_tau_lin += m_tau_lin % 2;
176 throw std::runtime_error(
"tau_lin must be >= 2");
180 throw std::runtime_error(
"tau_lin must be divisible by 2");
183 if (m_tau_max <= m_dt) {
184 throw std::runtime_error(
"tau_max must be >= delta_t (delta_N too large)");
187 if ((m_tau_max / m_dt) < m_tau_lin) {
188 m_hierarchy_depth = 1;
190 auto const operand = (m_tau_max / m_dt) /
double(m_tau_lin - 1);
191 assert(operand > 0.);
192 m_hierarchy_depth =
static_cast<int>(std::ceil(1. + std::log2(operand)));
197 dim_A = A_obs->n_values();
198 dim_B = B_obs->n_values();
201 throw std::runtime_error(
"dimension of first observable has to be >= 1");
204 throw std::runtime_error(
"dimension of second observable has to be >= 1");
208 if (corr_operation_name ==
"componentwise_product") {
210 m_shape = A_obs->shape();
213 }
else if (corr_operation_name ==
"tensor_product") {
214 m_dim_corr = dim_A * dim_B;
216 m_shape.emplace_back(dim_A);
217 m_shape.emplace_back(dim_B);
220 }
else if (corr_operation_name ==
"square_distance_componentwise") {
222 m_shape = A_obs->shape();
225 }
else if (corr_operation_name ==
"fcs_acf") {
229 throw std::runtime_error(
"missing parameter for fcs_acf: w_x w_y w_z");
232 m_correlation_args_input);
234 throw std::runtime_error(
"dimA must be divisible by 3 for fcs_acf");
235 m_dim_corr = dim_A / 3u;
236 m_shape = A_obs->shape();
237 if (m_shape.back() != 3u)
238 throw std::runtime_error(
239 "the last dimension of dimA must be 3 for fcs_acf");
242 }
else if (corr_operation_name ==
"scalar_product") {
245 m_shape.emplace_back(1u);
249 throw std::invalid_argument(
"correlation operation '" +
250 corr_operation_name +
"' not implemented");
254 if (compressA_name ==
"discard2") {
256 }
else if (compressA_name ==
"discard1") {
258 }
else if (compressA_name ==
"linear") {
261 throw std::invalid_argument(
"unknown compression method '" +
262 compressA_name +
"' for first observable");
265 if (compressB_name ==
"discard2") {
267 }
else if (compressB_name ==
"discard1") {
269 }
else if (compressB_name ==
"linear") {
272 throw std::invalid_argument(
"unknown compression method '" +
273 compressB_name +
"' for second observable");
277void Correlator::initialize_buffers() {
278 using index_type =
decltype(result)::index;
280 A.resize(std::array<int, 2>{{m_hierarchy_depth, m_tau_lin + 1}});
281 std::fill_n(A.data(), A.num_elements(), std::vector<double>(dim_A, 0));
282 B.resize(std::array<int, 2>{{m_hierarchy_depth, m_tau_lin + 1}});
283 std::fill_n(B.data(), B.num_elements(), std::vector<double>(dim_B, 0));
286 A_accumulated_average = std::vector<double>(dim_A, 0);
287 B_accumulated_average = std::vector<double>(dim_B, 0);
290 n_sweeps = std::vector<std::size_t>(n_result, 0);
291 n_vals = std::vector<long>(m_hierarchy_depth, 0);
293 result.resize(std::array<std::size_t, 2>{{n_result, m_dim_corr}});
294 for (index_type i = 0; i < static_cast<index_type>(n_result); i++) {
295 for (index_type j = 0; j < static_cast<index_type>(m_dim_corr); j++) {
300 newest = std::vector<long>(m_hierarchy_depth, m_tau_lin);
302 tau.resize(n_result);
303 for (
int i = 0; i < m_tau_lin + 1; i++) {
307 for (
int j = 1; j < m_hierarchy_depth; j++) {
308 for (
int k = 0; k < m_tau_lin / 2; k++) {
309 tau[m_tau_lin + 1 + (j - 1) * m_tau_lin / 2 + k] =
310 (k + (m_tau_lin / 2) + 1) * (1 << j);
317 throw std::runtime_error(
318 "No data can be added after finalize() was called.");
321 if (comm.rank() != 0) {
323 A_obs->operator()(comm);
324 if (A_obs != B_obs) {
325 B_obs->operator()(comm);
334 int highest_level_to_compress = -1;
341 auto const max_depth = m_hierarchy_depth - 1;
344 if (i >= max_depth or n_vals[i] <= m_tau_lin) {
347 auto const modulo = 1 << (i + 1);
348 auto const remainder = (t - (m_tau_lin + 1) * (modulo - 1) - 1) % modulo;
349 if (remainder != 0) {
352 highest_level_to_compress += 1;
360 for (
int i = highest_level_to_compress; i >= 0; i--) {
363 newest[i + 1] = (newest[i + 1] + 1) % (m_tau_lin + 1);
365 A[i + 1][newest[i + 1]] =
366 (*compressA)(A[i][(newest[i] + 1) % (m_tau_lin + 1)],
367 A[i][(newest[i] + 2) % (m_tau_lin + 1)]);
368 B[i + 1][newest[i + 1]] =
369 (*compressB)(B[i][(newest[i] + 1) % (m_tau_lin + 1)],
370 B[i][(newest[i] + 2) % (m_tau_lin + 1)]);
373 newest[0] = (newest[0] + 1) % (m_tau_lin + 1);
376 A[0][newest[0]] = A_obs->operator()(comm);
377 if (A_obs != B_obs) {
378 B[0][newest[0]] = B_obs->operator()(comm);
380 B[0][newest[0]] = A[0][newest[0]];
385 for (std::size_t k = 0; k < dim_A; k++) {
386 A_accumulated_average[k] += A[0][newest[0]][k];
389 for (std::size_t k = 0; k < dim_B; k++) {
390 B_accumulated_average[k] += B[0][newest[0]][k];
393 using index_type =
decltype(result)::index;
395 for (
long j = 0; j < min(m_tau_lin + 1, n_vals[0]); j++) {
396 auto const index_new = newest[0];
397 auto const index_old = (newest[0] - j + m_tau_lin + 1) % (m_tau_lin + 1);
399 (corr_operation)(A[0][index_old], B[0][index_new], m_correlation_args);
400 assert(temp.size() == m_dim_corr);
403 for (index_type k = 0; k < static_cast<index_type>(m_dim_corr); k++) {
404 result[j][k] += temp[k];
408 for (
int i = 1; i < highest_level_to_compress + 2; i++) {
409 for (
long j = (m_tau_lin + 1) / 2 + 1; j < min(m_tau_lin + 1, n_vals[i]);
411 auto const index_new = newest[i];
412 auto const index_old = (newest[i] - j + m_tau_lin + 1) % (m_tau_lin + 1);
413 auto const index_res =
414 m_tau_lin + (i - 1) * m_tau_lin / 2 + (j - m_tau_lin / 2 + 1) - 1;
415 auto const temp = (corr_operation)(A[i][index_old], B[i][index_new],
417 assert(temp.size() == m_dim_corr);
419 n_sweeps[index_res]++;
420 for (index_type k = 0; k < static_cast<index_type>(m_dim_corr); k++) {
421 result[index_res][k] += temp[k];
428 using index_type =
decltype(result)::index;
430 throw std::runtime_error(
"Correlator::finalize() can only be called once.");
440 if (comm.rank() != 0) {
444 for (
int ll = 0; ll < m_hierarchy_depth - 1; ll++) {
446 if (n_vals[ll] > m_tau_lin + 1)
447 vals_ll = m_tau_lin + n_vals[ll] % 2;
449 vals_ll = n_vals[ll];
453 auto highest_level_to_compress = (vals_ll % 2) ? ll : -1;
458 auto const max_depth = m_hierarchy_depth - 1;
460 while (highest_level_to_compress > -1) {
461 if (i >= max_depth or n_vals[i] % 2 == 0 or n_vals[i] <= m_tau_lin) {
464 highest_level_to_compress += 1;
474 for (
int i = highest_level_to_compress; i >= ll; i--) {
477 newest[i + 1] = (newest[i + 1] + 1) % (m_tau_lin + 1);
480 (*compressA)(A[i][(newest[i] + 1) % (m_tau_lin + 1)],
481 A[i][(newest[i] + 2) % (m_tau_lin + 1)]);
482 (*compressB)(B[i][(newest[i] + 1) % (m_tau_lin + 1)],
483 B[i][(newest[i] + 2) % (m_tau_lin + 1)]);
485 newest[ll] = (newest[ll] + 1) % (m_tau_lin + 1);
488 for (
int i = ll + 1; i < highest_level_to_compress + 2; i++) {
489 for (
long j = (m_tau_lin + 1) / 2 + 1;
490 j < min(m_tau_lin + 1, n_vals[i]); j++) {
491 auto const index_new = newest[i];
492 auto const index_old =
493 (newest[i] - j + m_tau_lin + 1) % (m_tau_lin + 1);
494 auto const index_res =
495 m_tau_lin + (i - 1) * m_tau_lin / 2 + (j - m_tau_lin / 2 + 1) - 1;
497 auto const temp = (corr_operation)(A[i][index_old], B[i][index_new],
499 assert(temp.size() == m_dim_corr);
501 n_sweeps[index_res]++;
502 for (index_type k = 0; k < static_cast<index_type>(m_dim_corr); k++) {
503 result[index_res][k] += temp[k];
513 using index_type =
decltype(result)::index;
515 std::vector<double> res(n_result * m_dim_corr);
517 for (std::size_t i = 0; i < n_result; i++) {
518 auto const index =
static_cast<index_type
>(m_dim_corr * i);
519 for (index_type k = 0; k < static_cast<index_type>(m_dim_corr); k++) {
521 res[index + k] = result[
static_cast<index_type
>(i)][k] /
522 static_cast<double>(n_sweeps[i]);
530 std::vector<double> res(
n_values());
531 std::ranges::transform(tau, res.begin(),
532 [
dt = m_dt](
auto const &a) { return a * dt; });
537 std::stringstream ss;
538 boost::archive::binary_oarchive oa(ss);
543 oa << m_correlation_args_input;
550 oa << A_accumulated_average;
551 oa << B_accumulated_average;
558 namespace iostreams = boost::iostreams;
559 iostreams::array_source src(state.data(), state.size());
560 iostreams::stream<iostreams::array_source> ss(src);
561 boost::archive::binary_iarchive ia(ss);
566 ia >> m_correlation_args_input;
573 ia >> A_accumulated_average;
574 ia >> B_accumulated_average;
576 initialize_operations();
Vector implementation and trait types for boost qvm interoperability.
void const * m_system
for bookkeeping purposes
std::size_t n_values() const
std::string get_internal_state() const final
void set_internal_state(std::string const &) final
std::vector< double > get_lag_times() const
int finalize(boost::mpi::communicator const &comm)
At the end of data collection, go through the whole hierarchy and correlate data left there.
std::vector< double > get_correlation()
Return correlation result.
void update(boost::mpi::communicator const &comm) override
The function to process a new datapoint of A and B.
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
std::vector< double > componentwise_product(std::vector< double > const &A, std::vector< double > const &B, Utils::Vector3d const &)
std::vector< double > tensor_product(std::vector< double > const &A, std::vector< double > const &B, Utils::Vector3d const &)
std::vector< double > compress_linear(std::vector< double > const &A1, std::vector< double > const &A2)
Compress computing arithmetic mean: A_compressed=(A1+A2)/2.
std::vector< double > scalar_product(std::vector< double > const &A, std::vector< double > const &B, Utils::Vector3d const &)
std::vector< double > compress_discard1(std::vector< double > const &A1, std::vector< double > const &A2)
Compress discarding the 1st argument and return the 2nd.
std::vector< double > compress_discard2(std::vector< double > const &A1, std::vector< double > const &A2)
Compress discarding the 2nd argument and return the 1st.
std::vector< double > fcs_acf(std::vector< double > const &A, std::vector< double > const &B, Utils::Vector3d const &wsquare)
std::vector< double > square_distance_componentwise(std::vector< double > const &A, std::vector< double > const &B, Utils::Vector3d const &)
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
int min(int i, unsigned int j)