HILA
Loading...
Searching...
No Matches
hila_healthcheck.cpp
Go to the documentation of this file.
1/**
2 * @file hila_healthcheck.cpp
3 * @author Kari Rummukainen
4 * @brief HILA healthcheck application.
5 * @details Performs a comprehensive health check on HILA libraries.
6 */
7#include "hila.h"
8
9// unistd.h needed for isatty()
10#include <unistd.h>
11
12
13// report the result of the test -- TODO: nicer formatting?
14bool report_pass(std::string message, double eps, double limit) {
15
16 static int is_terminal = -1;
17 static std::string ok_string, fail_string;
18
19 if (is_terminal < 0) {
20 is_terminal = isatty(fileno(stdout));
21
22 if (is_terminal) {
23 ok_string = "\x1B[32m --- \033[0m ";
24 fail_string = "\x1B[31m *** \033[0m ";
25 } else {
26 ok_string = " --- ";
27 fail_string = " *** ";
28 }
29 }
30
31 if (eps < limit) {
32 hila::out0 << ok_string << message << " passed" << std::endl;
33 return true;
34 } else {
35 hila::out0 << fail_string << message << " FAILED: eps " << eps << " limit " << limit
36 << std::endl;
37 return false;
38 }
39}
40
41/////////////////////////////////////////////////////////////////////////////////////
42
43void test_functions() {
44
45 Field<double> df = 0;
46 report_pass("Field functions: real exp", exp(df).sum() - lattice.volume(), 1e-8);
47
49 report_pass("Field functions: complex exp", abs(exp(cf).sum() - lattice.volume()), 1e-8);
50
51 df[ALL] = sin(X.x() * 2 * M_PI / lattice.size(e_x));
52 report_pass("Field functions: sin", df.sum(), 1e-8);
53}
54
55
56/////////////////////////////////////////////////////////////////////////////////////
57
58void check_reductions() {
59
60
61 // test reductions
62
64 f[ALL] = expi(2 * M_PI * X.coordinate(e_x) / lattice.size(e_x));
65
66 Complex<double> sum = 0;
67 onsites(ALL) sum += f[X];
68
69 sum /= lattice.volume();
70 report_pass("Complex reduction value " + hila::prettyprint(sum), abs(sum), 1e-4);
71
72 ReductionVector<Complex<double>> rv(lattice.size(e_x));
73
74 onsites(ALL) {
75 rv[X.coordinate(e_x)] += f[X];
76 }
77
78 sum = 0;
79 for (int i = 0; i < lattice.size(e_x); i++) {
80 sum +=
81 expi(2 * M_PI * i / lattice.size(e_x)) - rv[i] / (lattice.volume() / lattice.size(e_x));
82 }
83 report_pass("Vector reduction, sum " + hila::prettyprint(sum), abs(sum), 1e-4);
84
85 // do a combined reduction too
86 sum = 0;
87 rv = 0;
88 onsites(ALL) {
89 rv[X.x()] += f[X];
90 rv[0] += 1;
91 rv[1] += -0.01;
92
93 sum += f[X];
94 }
95
96 sum /= lattice.volume();
97 Complex<double> sum2 = 0;
98 rv[0] -= lattice.volume();
99 rv[1] += 0.01 * lattice.volume();
100
101 for (int i = 0; i < lattice.size(e_x); i++) {
102 sum2 +=
103 expi(2 * M_PI * i / lattice.size(e_x)) - rv[i] / (lattice.volume() / lattice.size(e_x));
104 }
105 report_pass("Combined reductions, sum " + hila::prettyprint(sum) + ", sum2 " +
106 hila::prettyprint(sum2),
107 abs(sum) + abs(sum2), 1e-4);
108}
109
110
111// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
112// write something to a location
113
114void test_site_access() {
115
117 Complex<double> sum;
118
120 for (int i = 0; i < 3; i++) {
121 foralldir(d) c[d] = hila::random() * lattice.size(d);
122 hila::broadcast(c); // need to broadcast!
123
124 f[c] = 4; // write to location c
125 sum = f[c]; // read back - does mpi comm
126 report_pass("Setting and reading a value at " + hila::prettyprint(c.transpose()),
127 abs(sum - 4), 1e-10);
128 }
129}
130
131
132// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
133// test maxloc and minloc operation
134
135void test_minmax() {
136
137 CoordinateVector c, loc;
139 n[ALL] = hila::random();
140 foralldir(d) c[d] = hila::random() * lattice.size(d);
142 n[c] = 2;
143
144 auto v = n.max(loc);
145 report_pass("Maxloc is " + hila::prettyprint(loc.transpose()), (c - loc).norm(), 1e-8);
146 report_pass("Max value " + hila::prettyprint(v), v - 2, 1e-9);
147
148
149 foralldir(d) c[d] = hila::random() * lattice.size(d);
151 n[c] = -1;
152 v = n.min(loc);
153 report_pass("Minloc is " + hila::prettyprint(loc.transpose()), (c - loc).norm(), 1e-8);
154 report_pass("Min value " + hila::prettyprint(v), v + 1, 1e-9);
155}
156
157// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
158// test random number properties
159// rough test, testing spectrum of gaussians
160
161void test_random() {
162
163 constexpr int n_loops = 100;
164
166
167 double fsum = 0, fsqr = 0;
168 for (int i = 0; i < n_loops; i++) {
169 f.gaussian_random();
170 double s = 0, s2 = 0;
171 onsites(ALL) {
172 s += f[X];
173 s2 += sqr(f[X]);
174 }
175 fsum += s / lattice.volume();
176 fsqr += s2 / lattice.volume();
177 }
178
179 fsum /= n_loops;
180 fsqr /= n_loops;
181
182 report_pass("Gaussian random average (6 sigma limit) " + hila::prettyprint(fsum), abs(fsum),
183 6 / sqrt(((double)n_loops) * lattice.volume()));
184
185 report_pass("Gaussian random width^2 " + hila::prettyprint(fsqr), fsqr - 1,
186 6 / sqrt(((double)n_loops) * lattice.volume()));
187}
188
189
190// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
191// test access to a list of sites
192
193void test_set_elements_and_select() {
194
197
198 std::vector<CoordinateVector> cvec;
199 std::vector<Complex<double>> vals;
200
201
202 if (hila::myrank() == 0) {
203 int k = 1;
204 for (int i = 0; i <= 50; i++) {
205 foralldir(d) c[d] = hila::random() * lattice.size(d);
206 bool found = false;
207
208 // don't overwrite previous loc
209 for (auto &r : cvec)
210 if (r == c)
211 found = true;
212 if (!found) {
213 cvec.push_back(c);
214 vals.push_back(Complex<double>(k, k));
215 k++;
216 }
217 }
218 }
219
220 hila::broadcast(vals);
221 hila::broadcast(cvec);
222
223 f.set_elements(vals, cvec);
224
225 vals = f.get_elements(cvec, true);
226
227 Complex<double> sum = 0;
228 for (int i = 0; i < vals.size(); i++) {
229 sum += vals[i] - Complex<double>(i + 1, i + 1);
230 }
231
232 report_pass("Field set_elements and get_elements with " + hila::prettyprint(vals.size()) +
233 " coordinates",
234 abs(sum), 1e-8);
235
236 // use the same vector for siteselect
237
238 SiteSelect s;
239 SiteValueSelect<Complex<double>> sv;
240
241 onsites(ALL) {
242 if (squarenorm(f[X]) >= 1) {
243 s.select(X);
244 sv.select(X, f[X]);
245 }
246 }
247
248 report_pass("SiteSelect size " + hila::prettyprint(s.size()), s.size() - cvec.size(), 1e-3);
249 report_pass("SiteValueSelect size " + hila::prettyprint(sv.size()), sv.size() - cvec.size(),
250 1e-3);
251
252 int ok = 1;
253
254 for (auto &c : cvec) {
255 bool found = false;
256 for (int i = 0; !found && i < s.size(); i++) {
257 if (s.coordinates(i) == c)
258 found = true;
259 }
260 if (!found)
261 ok = 0;
262 }
263
264 report_pass("SiteSelect content", 1 - ok, 1e-6);
265
266 ok = 1;
267 for (int k = 0; k < cvec.size(); k++) {
268
269 bool found = false;
270 for (int i = 0; !found && i < sv.size(); i++) {
271 if (sv.coordinates(i) == cvec[k] && sv.value(i) == vals[k])
272 found = true;
273 }
274 if (!found)
275 ok = 0;
276 }
277
278 report_pass("SiteValueSelect content", 1 - ok, 1e-6);
279
280
281 onsites(ALL) {}
282}
283
284/////////////////////////////////////////////////////////////////////////////////////
285
286void test_subvolumes() {
287
288 bool ok = true;
289 for (int i = 0; i < 20; i++) {
291 foralldir(d) c[d] = hila::random() * lattice.size(d);
292 auto si = SiteIndex(c);
293 if (si.coordinates() != c)
294 ok = false;
295 }
296
297 report_pass("SiteIndex", ok == false, 1e-2);
298
299
301 f[ALL] = SiteIndex(X.coordinates());
302
304 c.fill(-1);
305 size_t vol = lattice.volume();
306 foralldir(d) if (d < NDIM - 1) {
307 c[d] = hila::random() * lattice.size(d);
309
310 auto slice = f.get_slice(c);
311
312 vol /= lattice.size(d);
313 if (hila::myrank() == 0) {
314
315 report_pass(hila::prettyprint(NDIM - (int)d - 1) + "-dimensional slice size " +
316 hila::prettyprint(vol),
317 slice.size() - vol, 1e-3);
318
319
320 bool pass = true;
321
322 CoordinateVector mycoord = 0;
323 foralldir(d2) {
324 if (c[d2] >= 0)
325 mycoord[d2] = c[d2];
326 }
327 foralldir(d2) {
328 if (c[d2] < 0) {
329 mycoord[d2] = -1;
330 break;
331 }
332 }
333
334 for (auto s : slice) {
335
336 // get the coordinate which should be here
337 bool add = true;
338 foralldir(d2) {
339 if (add && c[d2] < 0) {
340 mycoord[d2]++;
341 if (mycoord[d2] < lattice.size(d2)) {
342 add = false;
343 } else {
344 mycoord[d2] = 0;
345 }
346 }
347 }
348
349 if (pass && mycoord != s.coordinates()) {
350 hila::out0 << "Slice coord error, should be "
351 << hila::prettyprint(mycoord.transpose()) << " is "
352 << hila::prettyprint(s.coordinates().transpose()) << " slice is "
353 << hila::prettyprint(c.transpose()) << '\n';
354 pass = false;
355 break;
356 }
357 }
358
359 report_pass("slice content", pass == false, 1e-2);
360 }
361 }
362}
363
364/////////////////////////////////////////////////////////////////////////////////////
365
366
367void fft_test() {
368
369
370 using T = Complex<double>;
371
372 Field<T> f, p, p2;
373
374 // Start with unit field
375 f = 1;
376
377 // After one FFT the field is 0 except at coord 0
378 p2 = 0;
379
380 p2[{0, 0, 0}] = lattice.volume();
381
382 FFT_field(f, p);
383
384 double eps = squarenorm_relative(p, p2);
385
386 report_pass("FFT constant field", eps, 1e-13 * sqrt(lattice.volume()));
387
388 //-----------------------------------------------------------------
389 // After two applications the field should be back to a constant * volume
390
391 FFT_field(p, f, fft_direction::back);
392
393 double sum = 0;
394 double tnorm = 0;
395 onsites(ALL) {
396 sum += (f[X] - lattice.volume()).squarenorm();
397 tnorm += f[X].squarenorm();
398 }
399
400 eps = fabs(sum / tnorm);
401
402 report_pass("FFT inverse transform", eps, 1e-10);
403
404 //-----------------------------------------------------------------
405 for (int iter = 0; iter < 5; iter++) {
408 foralldir(d) {
409 kx[d] = hila::broadcast(hila::random()) * lattice.size(d);
410 }
411
412 kv = kx.convert_to_k();
413
414
415 onsites(ALL) {
416 double d = kv.dot(X.coordinates());
417 f[X] = expi(d);
418 }
419
420 FFT_field(f, p);
421
422 p2 = 0;
423 p2[kx] = lattice.volume();
424
425 eps = squarenorm_relative(p, p2);
426
427 report_pass("FFT of wave vector " + hila::prettyprint(kx.transpose()), eps,
428 1e-13 * sqrt(lattice.volume()));
429 }
430
431 //-----------------------------------------------------------------
432
433 {
435 onsites(ALL) r[X] = hila::gaussrand();
436
437 f = r.FFT_real_to_complex();
438 p = f.FFT(fft_direction::back) / lattice.volume();
439 eps = squarenorm_relative(r, p);
440
441 report_pass("FFT real to complex", eps, 1e-13 * sqrt(lattice.volume()));
442
443 auto r2 = f.FFT_complex_to_real(fft_direction::back) / lattice.volume();
444 eps = squarenorm_relative(r, r2);
445
446 report_pass("FFT complex to real", eps, 1e-13 * sqrt(lattice.volume()));
447 }
448
449 //-----------------------------------------------------------------
450 // Check fft norm
451
452
453 onsites(ALL) {
454 p[X] = hila::random() * exp(-X.coordinates().convert_to_k().squarenorm());
455 }
456 f = p.FFT(fft_direction::back) / sqrt(lattice.volume());
457
458 double nf = f.squarenorm();
459 double np = p.squarenorm();
460 report_pass("Norm of field = " + hila::prettyprint(nf) + " and FFT = " + hila::prettyprint(np),
461 (nf - np) / nf, 1e-10);
462
464 b.k_max(M_PI * sqrt(3.0));
465
466 auto bf = b.bin_k_field(p.conj() * p);
467
468 double s = 0;
469 for (auto b : bf) {
470 s += abs(b);
471 }
473
474 report_pass("Norm of binned FFT = " + hila::prettyprint(s), (s - np) / np, 1e-10);
475}
476
477//---------------------------------------------------------------------------
478
479void spectraldensity_test() {
480
481
482 // test spectral density for single waves
483
485
487 b.k_max(M_PI * sqrt(3.0));
488
489 // test std binning first
490
491 for (int iter = 0; iter < 3; iter++) {
494 foralldir(d) {
495 kx[d] = hila::broadcast(hila::random()) * lattice.size(d);
496 }
497 kv = kx.convert_to_k();
498 auto absk = kv.norm();
499
500 // test first std. binning (normally )
501 f = 0;
502 f[kx] = 1;
503
504 auto fb = b.bin_k_field(f);
505
506 double sum = 0;
507 for (int i = 0; i < b.bins(); i++) {
508 if (b.bin_min(i) <= absk && b.bin_max(i) > absk) {
509 // should be one hit here
510 sum += squarenorm(fb[i] - 1);
511 } else {
512 sum += squarenorm(fb[i]);
513 }
514 }
515
516 report_pass("Binning test at vector " + hila::prettyprint(kx.transpose()), sum, 1e-10);
517
518 // then test the spectral density extraction
519 // set single wave
520 onsites(ALL) {
521 double d = kv.dot(X.coordinates());
522 f[X] = expi(d);
523 }
524
525 auto fsd = b.spectraldensity(f);
526
527 sum = 0;
528 for (int i = 0; i < b.bins(); i++) {
529 if (b.bin_min(i) <= absk && b.bin_max(i) > absk) {
530 // should be one hit here
531 sum += fabs(fsd[i] / pow(lattice.volume(), 2) - 1);
532 } else {
533 sum += fabs(fsd[i]);
534 }
535 }
536
537 report_pass("Spectral density test with above vector ", sum, 1e-10);
538 }
539}
540
541//--------------------------------------------------------------------------------
542
543void test_field_slices() {
544
546 onsites(ALL) {
547 s[X] = SiteIndex(X.coordinates());
548 }
549}
550
551//--------------------------------------------------------------------------------
552
553void test_matrix_operations() {
554
556
557 onsites(ALL) mf[X].fill(1 + I);
558
560 cm.asArray() = 4;
561 double sum = 0;
562 onsites(ALL) {
563 sum += (mf[X] * mf[X].dagger() - cm).squarenorm();
564 }
565
566 report_pass("matrix multiply and addition", sum, 1e-8);
567
568 auto dm = cm * I - 2*I;
569 dm.asArray() *= I;
570 dm = ((dm - 2).asArray() + 4).asMatrix();
571 report_pass("Array and imaginary unit operations", dm.squarenorm(), 1e-8);
572
573
574
575
576
577
578}
579
580
581//--------------------------------------------------------------------------------
582
583void test_matrix_algebra() {
584
585 using myMatrix = SquareMatrix<4, Complex<double>>;
586
588 Field<double> delta;
589
590 M.gaussian_random(2.0);
591
592 // eigenvalue test - show that M = U D U^*, where D is diagonal eigenvalue matrix and U matrix
593 // of eigenvectors
594
595 onsites(ALL) {
596 auto H = M[X] * M[X].dagger(); // make hermitean
597
598 auto r = H.eigen_hermitean();
599 delta[X] = (H - r.eigenvectors * r.eigenvalues * r.eigenvectors.dagger()).norm();
600 }
601
602 auto max_delta = delta.max();
603
604 report_pass("Eigenvalue analysis with " + hila::prettyprint(myMatrix::rows()) + "x" +
605 hila::prettyprint(myMatrix::columns()) + " Hermitean matrix",
606 max_delta, 1e-10);
607
608 // Singular value test - non-pivoted
609
610 onsites(ALL) {
611 auto r = M[X].svd();
612 delta[X] = (M[X] - r.U * r.singularvalues * r.V.dagger()).norm();
613 }
614
615 max_delta = delta.max();
616
617 report_pass("SVD with " + hila::prettyprint(myMatrix::rows()) + "x" +
618 hila::prettyprint(myMatrix::columns()) + " Complex matrix",
619 max_delta, 1e-10);
620
621 // pivoted singular values
622
623 M.gaussian_random();
624
625 onsites(ALL) {
626 auto r = M[X].svd_pivot(hila::sort::ascending);
627 delta[X] = (M[X] - r.U * r.singularvalues * r.V.dagger()).norm();
628 }
629
630 max_delta = delta.max();
631
632 report_pass("Fully pivoted SVD with " + hila::prettyprint(myMatrix::rows()) + "x" +
633 hila::prettyprint(myMatrix::columns()) + " Complex matrix",
634 max_delta, 1e-10);
635}
636
637
638//////////////////////////////////////////////////////////////////////////////////////////
639
640int main(int argc, char **argv) {
641
642 hila::initialize(argc, argv);
643
644 hila::input par("parameters");
645
646 CoordinateVector lsize = par.get("lattice size"); // reads NDIM numbers
647 long seed = par.get("random seed");
648
649 par.close();
650
651 // setting up the lattice is convenient to do after reading
652 // the parameters
653 lattice.setup(lsize);
654
655 // We need random number here
656 hila::seed_random(seed);
657
658
659 ///////////////////////////////////////////////////////////////
660 // start tests
661
662 check_reductions();
663
664 test_functions();
665
666 test_site_access();
667
668 test_minmax();
669
670 test_random();
671
672 test_set_elements_and_select();
673
674 test_subvolumes();
675
676 test_matrix_operations();
677
678 fft_test();
679
680 spectraldensity_test();
681
682 test_matrix_algebra();
683
685}
Array< n, m, T > exp(Array< n, m, T > a)
Exponential.
Definition array.h:1059
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:1018
Array< n, m, T > sqrt(Array< n, m, T > a)
Square root.
Definition array.h:1039
Array< n, m, T > pow(Array< n, m, T > a, int b)
Power.
Definition array.h:1204
Array< n, m, T > sin(Array< n, m, T > a)
Sine.
Definition array.h:1079
Complex definition.
Definition cmplx.h:56
Vector< 4, double > convert_to_k() const
Convert momentum space CoordinateVector to wave number k, where -pi/2 < k_i <= pi_2 Utility function ...
Definition fft.h:29
The field class implements the standard methods for accessing Fields. Hilapp replaces the parity acce...
Definition field.h:61
Field< A > dagger() const
Returns dagger or Hermitian conjugate of Field depending on how it is defined for Field type T.
Definition field.h:1104
T max(Parity par=ALL) const
Find maximum value from Field.
Definition reduction.h:424
Field< Complex< hila::arithmetic_type< T > > > FFT_real_to_complex(fft_direction fdir=fft_direction::forward) const
Definition fft.h:467
Field< T > conj() const
Returns field with all elements conjugated depending how conjugate is defined for type.
Definition field.h:1091
double squarenorm() const
Squarenorm.
Definition field.h:1064
std::vector< T > get_elements(const std::vector< CoordinateVector > &coord_list, bool broadcast=false) const
Get a list of elements and store them into a vector.
Definition field_comm.h:663
T min(Parity par=ALL) const
Find minimum value from Field.
Definition reduction.h:404
T sum(Parity par=Parity::all, bool allreduce=true) const
Sum reduction of Field.
Definition reduction.h:296
std::vector< T > get_slice(const CoordinateVector &c, bool broadcast=false) const
and get a slice (subvolume)
Definition field_comm.h:701
hila::arithmetic_type< T > norm() const
Calculate vector norm - sqrt of squarenorm.
Definition matrix.h:1133
const auto & fill(const S rhs)
Matrix fill.
Definition matrix.h:937
Rtype transpose() const
Transpose of matrix.
Definition matrix.h:954
R dot(const Matrix< p, q, S > &rhs) const
Dot product.
Definition matrix.h:1220
const Array< n, m, T > & asArray() const
Cast Matrix to Array.
Definition matrix.h:443
Matrix class which defines matrix operations.
Definition matrix.h:1620
Running index for locating sites on the lattice.
Definition site_index.h:17
hila::input - Class for parsing runtime parameter files.
Definition input.h:52
std::vector< double > spectraldensity(const Field< T > &f)
k_binning & k_max(double km)
Max value of k.
double bin_min(int i)
std::vector< T > bin_k_field(const Field< T > &f)
Generic k-space field binner routine - returns field element type vector.
k_binning & bins(int n)
Set number of bins in histogram.
void setup(const CoordinateVector &siz)
General lattice setup.
Definition lattice.cpp:33
constexpr Imaginary_t< double > I(1.0)
Imaginary unit I - global variable.
Complex< T > expi(T a)
Definition cmplx.h:1413
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1187
#define foralldir(d)
Macro to loop over (all) Direction(s)
Definition coordinates.h:78
constexpr Parity ALL
bit pattern: 011
constexpr T sqr(const T &arg)
Define convenience function sqr(), returning square of argument.
Definition defs.h:154
void FFT_field(const Field< T > &input, Field< T > &result, const CoordinateVector &directions, fft_direction fftdir=fft_direction::forward)
Definition fft.h:376
int myrank()
rank of this node
Definition com_mpi.cpp:235
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:118
double gaussrand()
Gaussian random generation routine.
Definition random.cpp:216
std::ostream out0
This writes output only from main process (node 0)
void initialize(int argc, char **argv)
Read in command line arguments. Initialise default stream and MPI communication.
void seed_random(uint64_t seed, bool device_rng=true)
Seed random generators with 64-bit unsigned value. On MPI shuffles the seed so that different MPI ran...
Definition random.cpp:86
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:153
void finishrun()
Normal, controlled exit - all nodes must call this. Prints timing information and information about c...