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 check_reductions() {
44
45
46 // test reductions
47
49 f[ALL] = expi(2 * M_PI * X.coordinate(e_x) / lattice.size(e_x));
50
51 Complex<double> sum = 0;
52 onsites(ALL) sum += f[X];
53
54 sum /= lattice.volume();
55 report_pass("Complex reduction value " + hila::prettyprint(sum), abs(sum), 1e-4);
56
57 ReductionVector<Complex<double>> rv(lattice.size(e_x));
58
59 onsites(ALL) {
60 rv[X.coordinate(e_x)] += f[X];
61 }
62
63 sum = 0;
64 for (int i = 0; i < lattice.size(e_x); i++) {
65 sum +=
66 expi(2 * M_PI * i / lattice.size(e_x)) - rv[i] / (lattice.volume() / lattice.size(e_x));
67 }
68 report_pass("Vector reduction, sum " + hila::prettyprint(sum), abs(sum), 1e-4);
69}
70
71
72// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
73// write something to a location
74
75void test_site_access() {
76
79
81 for (int i = 0; i < 3; i++) {
82 foralldir(d) c[d] = hila::random() * lattice.size(d);
83 hila::broadcast(c); // need to broadcast!
84
85 f[c] = 4; // write to location c
86 sum = f[c]; // read back - does mpi comm
87 report_pass("Setting and reading a value at " + hila::prettyprint(c.transpose()),
88 abs(sum - 4), 1e-10);
89 }
90}
91
92
93// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
94// test maxloc and minloc operation
95
96void test_minmax() {
97
98 CoordinateVector c, loc;
100 n[ALL] = hila::random();
101 foralldir(d) c[d] = hila::random() * lattice.size(d);
103 n[c] = 2;
104
105 auto v = n.max(loc);
106 report_pass("Maxloc is " + hila::prettyprint(loc.transpose()), (c - loc).norm(), 1e-8);
107 report_pass("Max value " + hila::prettyprint(v), v - 2, 1e-9);
108
109
110 foralldir(d) c[d] = hila::random() * lattice.size(d);
112 n[c] = -1;
113 v = n.min(loc);
114 report_pass("Minloc is " + hila::prettyprint(loc.transpose()), (c - loc).norm(), 1e-8);
115 report_pass("Min value " + hila::prettyprint(v), v + 1, 1e-9);
116}
117
118// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
119// test random number properties
120// rough test, testing spectrum of gaussians
121
122void test_random() {
123
124 constexpr int n_loops = 100;
125
127
128 double fsum = 0, fsqr = 0;
129 for (int i=0; i<n_loops; i++) {
130 f.gaussian_random();
131 double s = 0, s2 = 0;
132 onsites(ALL) {
133 f[X] = hila::gaussrand();
134 s += f[X];
135 s2 += sqr(f[X]);
136 }
137 fsum += s/lattice.volume();
138 fsqr += s2/lattice.volume();
139 }
140
141 fsum /= n_loops;
142 fsqr /= n_loops;
143
144 report_pass("Gaussian random average (6 sigma limit) " + hila::prettyprint(fsum),
145 abs(fsum), 6/sqrt(((double)n_loops)*lattice.volume()));
146
147 report_pass("Gaussian random width^2 " + hila::prettyprint(fsqr),
148 fsqr - 1, 6/sqrt(((double)n_loops)*lattice.volume()));
149
150}
151
152
153
154// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
155// test access to a list of sites
156
157void test_set_elements_and_select() {
158
161
162 std::vector<CoordinateVector> cvec;
163 std::vector<Complex<double>> vals;
164
165
166 if (hila::myrank() == 0) {
167 int k = 1;
168 for (int i = 0; i <= 50; i++) {
169 foralldir(d) c[d] = hila::random() * lattice.size(d);
170 bool found = false;
171
172 // don't overwrite previous loc
173 for (auto &r : cvec)
174 if (r == c)
175 found = true;
176 if (!found) {
177 cvec.push_back(c);
178 vals.push_back(Complex<double>(k, k));
179 k++;
180 }
181 }
182 }
183
184 hila::broadcast(vals);
185 hila::broadcast(cvec);
186
187 f.set_elements(vals, cvec);
188
189 vals = f.get_elements(cvec, true);
190
191 Complex<double> sum = 0;
192 for (int i = 0; i < vals.size(); i++) {
193 sum += vals[i] - Complex<double>(i + 1, i + 1);
194 }
195
196 report_pass("Field set_elements and get_elements with " + hila::prettyprint(vals.size()) +
197 " coordinates",
198 abs(sum), 1e-8);
199
200 // use the same vector for siteselect
201
202 SiteSelect s;
203 SiteValueSelect<Complex<double>> sv;
204
205 onsites(ALL) {
206 if (squarenorm(f[X]) >= 1) {
207 s.select(X);
208 sv.select(X, f[X]);
209 }
210 }
211
212 report_pass("SiteSelect size " + hila::prettyprint(s.size()), s.size() - cvec.size(), 1e-3);
213 report_pass("SiteValueSelect size " + hila::prettyprint(sv.size()), sv.size() - cvec.size(),
214 1e-3);
215
216 int ok = 1;
217
218 for (auto &c : cvec) {
219 bool found = false;
220 for (int i = 0; !found && i < s.size(); i++) {
221 if (s.coordinates(i) == c)
222 found = true;
223 }
224 if (!found)
225 ok = 0;
226 }
227
228 report_pass("SiteSelect content", 1 - ok, 1e-6);
229
230 ok = 1;
231 for (int k = 0; k < cvec.size(); k++) {
232
233 bool found = false;
234 for (int i = 0; !found && i < sv.size(); i++) {
235 if (sv.coordinates(i) == cvec[k] && sv.value(i) == vals[k])
236 found = true;
237 }
238 if (!found)
239 ok = 0;
240 }
241
242 report_pass("SiteValueSelect content", 1 - ok, 1e-6);
243
244
245 onsites(ALL) {}
246}
247
248/////////////////////////////////////////////////////////////////////////////////////
249
250void test_subvolumes() {
251
252 bool ok = true;
253 for (int i = 0; i < 20; i++) {
255 foralldir(d) c[d] = hila::random() * lattice.size(d);
256 auto si = SiteIndex(c);
257 if (si.coordinates() != c)
258 ok = false;
259 }
260
261 report_pass("SiteIndex", ok == false, 1e-2);
262
263
265 f[ALL] = SiteIndex(X.coordinates());
266
268 c.fill(-1);
269 size_t vol = lattice.volume();
270 foralldir(d) if (d < NDIM - 1) {
271 c[d] = hila::random() * lattice.size(d);
273
274 auto slice = f.get_slice(c);
275
276 vol /= lattice.size(d);
277 if (hila::myrank() == 0) {
278
279 report_pass(hila::prettyprint(NDIM - (int)d - 1) + "-dimensional slice size " +
280 hila::prettyprint(vol),
281 slice.size() - vol, 1e-3);
282
283
284 bool pass = true;
285
286 CoordinateVector mycoord = 0;
287 foralldir(d2) {
288 if (c[d2] >= 0)
289 mycoord[d2] = c[d2];
290 }
291 foralldir(d2) {
292 if (c[d2] < 0) {
293 mycoord[d2] = -1;
294 break;
295 }
296 }
297
298 for (auto s : slice) {
299
300 // get the coordinate which should be here
301 bool add = true;
302 foralldir(d2) {
303 if (add && c[d2] < 0) {
304 mycoord[d2]++;
305 if (mycoord[d2] < lattice.size(d2)) {
306 add = false;
307 } else {
308 mycoord[d2] = 0;
309 }
310 }
311 }
312
313 if (pass && mycoord != s.coordinates()) {
314 hila::out0 << "Slice coord error, should be "
315 << hila::prettyprint(mycoord.transpose()) << " is "
316 << hila::prettyprint(s.coordinates().transpose()) << " slice is "
317 << hila::prettyprint(c.transpose()) << '\n';
318 pass = false;
319 break;
320 }
321 }
322
323 report_pass("slice content", pass == false, 1e-2);
324 }
325 }
326}
327
328/////////////////////////////////////////////////////////////////////////////////////
329
330
331void fft_test() {
332
333
334 using T = Complex<double>;
335
336 Field<T> f, p, p2;
337
338 // Start with unit field
339 f = 1;
340
341 // After one FFT the field is 0 except at coord 0
342 p2 = 0;
343
344 p2[{0, 0, 0}] = lattice.volume();
345
346 FFT_field(f, p);
347
348 double eps = squarenorm_relative(p, p2);
349
350 report_pass("FFT constant field", eps, 1e-13 * sqrt(lattice.volume()));
351
352 //-----------------------------------------------------------------
353 // After two applications the field should be back to a constant * volume
354
355 FFT_field(p, f, fft_direction::back);
356
357 double sum = 0;
358 double tnorm = 0;
359 onsites(ALL) {
360 sum += (f[X] - lattice.volume()).squarenorm();
361 tnorm += f[X].squarenorm();
362 }
363
364 eps = fabs(sum / tnorm);
365
366 report_pass("FFT inverse transform", eps, 1e-10);
367
368 //-----------------------------------------------------------------
369 for (int iter = 0; iter < 5; iter++) {
372 foralldir(d) {
373 kx[d] = hila::broadcast(hila::random()) * lattice.size(d);
374 }
375
376 kv = convert_to_k(kx);
377
378 onsites(ALL) {
379 double d = kv.dot(X.coordinates());
380 f[X] = expi(d);
381 }
382
383 FFT_field(f, p);
384
385 p2 = 0;
386 p2[kx] = lattice.volume();
387
388 eps = squarenorm_relative(p, p2);
389
390 report_pass("FFT of wave vector " + hila::prettyprint(kx.transpose()), eps,
391 1e-13 * sqrt(lattice.volume()));
392 }
393
394 //-----------------------------------------------------------------
395
396 {
398 onsites(ALL) r[X] = hila::gaussrand();
399
400 f = r.FFT_real_to_complex();
401 p = f.FFT(fft_direction::back) / lattice.volume();
402 eps = squarenorm_relative(r, p);
403
404 report_pass("FFT real to complex", eps, 1e-13 * sqrt(lattice.volume()));
405
406 auto r2 = f.FFT_complex_to_real(fft_direction::back) / lattice.volume();
407 eps = squarenorm_relative(r, r2);
408
409 report_pass("FFT complex to real", eps, 1e-13 * sqrt(lattice.volume()));
410 }
411
412 //-----------------------------------------------------------------
413 // Check fft norm
414
415 onsites(ALL) {
416 p[X] = hila::random() * exp(-convert_to_k(X.coordinates()).squarenorm());
417 }
418 f = p.FFT(fft_direction::back) / sqrt(lattice.volume());
419
420 double nf = f.squarenorm();
421 double np = p.squarenorm();
422 report_pass("Norm of field = " + hila::prettyprint(nf) + " and FFT = " + hila::prettyprint(np),
423 (nf - np) / nf, 1e-10);
424
426 b.k_max(M_PI * sqrt(3.0));
427
428 auto bf = b.bin_k_field(p.conj() * p);
429
430 double s = 0;
431 for (auto b : bf) {
432 s += abs(b);
433 }
435
436 report_pass("Norm of binned FFT = " + hila::prettyprint(s), (s - np) / np, 1e-10);
437}
438
439//---------------------------------------------------------------------------
440
441void spectraldensity_test() {
442
443
444 // test spectral density for single waves
445
447
449 b.k_max(M_PI * sqrt(3.0));
450
451 // test std binning first
452
453 for (int iter = 0; iter < 3; iter++) {
456 foralldir(d) {
457 kx[d] = hila::broadcast(hila::random()) * lattice.size(d);
458 }
459 kv = convert_to_k(kx);
460 auto absk = kv.norm();
461
462 // test first std. binning (normally )
463 f = 0;
464 f[kx] = 1;
465
466 auto fb = b.bin_k_field(f);
467
468 double sum = 0;
469 for (int i = 0; i < b.bins(); i++) {
470 if (b.bin_min(i) <= absk && b.bin_max(i) > absk) {
471 // should be one hit here
472 sum += squarenorm(fb[i] - 1);
473 } else {
474 sum += squarenorm(fb[i]);
475 }
476 }
477
478 report_pass("Binning test at vector " + hila::prettyprint(kx.transpose()), sum, 1e-10);
479
480 // then test the spectral density extraction
481 // set single wave
482 onsites(ALL) {
483 double d = kv.dot(X.coordinates());
484 f[X] = expi(d);
485 }
486
487 auto fsd = b.spectraldensity(f);
488
489 sum = 0;
490 for (int i = 0; i < b.bins(); i++) {
491 if (b.bin_min(i) <= absk && b.bin_max(i) > absk) {
492 // should be one hit here
493 sum += fabs(fsd[i] / pow(lattice.volume(), 2) - 1);
494 } else {
495 sum += fabs(fsd[i]);
496 }
497 }
498
499 report_pass("Spectral density test with above vector ", sum, 1e-10);
500 }
501}
502
503//--------------------------------------------------------------------------------
504
505void test_field_slices() {
506
508 onsites(ALL) {
509 s[X] = SiteIndex(X.coordinates());
510 }
511}
512
513
514//--------------------------------------------------------------------------------
515
516void test_matrix_algebra() {
517
518 using myMatrix = SquareMatrix<4, Complex<double>>;
519
521 Field<double> delta;
522
523 M.gaussian_random(2.0);
524
525 // eigenvalue test - show that M = U D U^*, where D is diagonal eigenvalue matrix and U matrix
526 // of eigenvectors
527
528 onsites(ALL) {
529 auto H = M[X] * M[X].dagger(); // make hermitean
530
531 auto r = H.eigen_hermitean();
532 delta[X] = (H - r.eigenvectors * r.eigenvalues * r.eigenvectors.dagger()).norm();
533 }
534
535 auto max_delta = delta.max();
536
537 report_pass("Eigenvalue analysis with " + hila::prettyprint(myMatrix::rows()) + "x" +
538 hila::prettyprint(myMatrix::columns()) + " Hermitean matrix",
539 max_delta, 1e-10);
540
541 // Singular value test - non-pivoted
542
543 onsites(ALL) {
544 auto r = M[X].svd();
545 delta[X] = (M[X] - r.U * r.singularvalues * r.V.dagger()).norm();
546 }
547
548 max_delta = delta.max();
549
550 report_pass("SVD with " + hila::prettyprint(myMatrix::rows()) + "x" +
551 hila::prettyprint(myMatrix::columns()) + " Complex matrix",
552 max_delta, 1e-10);
553
554 // pivoted singular values
555
556 M.gaussian_random();
557
558 onsites(ALL) {
559 auto r = M[X].svd_pivot(hila::sort::ascending);
560 delta[X] = (M[X] - r.U * r.singularvalues * r.V.dagger()).norm();
561 }
562
563 max_delta = delta.max();
564
565 report_pass("Fully pivoted SVD with " + hila::prettyprint(myMatrix::rows()) + "x" +
566 hila::prettyprint(myMatrix::columns()) + " Complex matrix",
567 max_delta, 1e-10);
568
569
570}
571
572
573//////////////////////////////////////////////////////////////////////////////////////////
574
575int main(int argc, char **argv) {
576
577 hila::initialize(argc, argv);
578
579 hila::input par("parameters");
580
581 CoordinateVector lsize = par.get("lattice size"); // reads NDIM numbers
582 long seed = par.get("random seed");
583
584 par.close();
585
586 // setting up the lattice is convenient to do after reading
587 // the parameters
588 lattice.setup(lsize);
589
590 // We need random number here
591 hila::seed_random(seed);
592
593
594 ///////////////////////////////////////////////////////////////
595 // start tests
596
597 check_reductions();
598
599 test_site_access();
600
601 test_minmax();
602
603 test_random();
604
605 test_set_elements_and_select();
606
607 test_subvolumes();
608
609 fft_test();
610
611 spectraldensity_test();
612
613 test_matrix_algebra();
614
616}
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:957
Complex definition.
Definition cmplx.h:50
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:1160
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:1147
double squarenorm() const
Squarenorm.
Definition field.h:1120
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
std::vector< T > get_slice(const CoordinateVector &c, bool broadcast=false) const
Get a slice (subvolume)
Definition field_comm.h:701
hila::arithmetic_type< T > norm() const
Calculate vector norm - sqrt of squarenorm.
Definition matrix.h:1183
const Mtype & fill(const S rhs)
Matrix fill.
Definition matrix.h:980
Rtype transpose() const
Transpose of matrix.
Definition matrix.h:997
R dot(const Matrix< p, q, S > &rhs) const
Dot product.
Definition matrix.h:1251
Matrix class which defines matrix operations.
Definition matrix.h:1679
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
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1322
#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:149
Vector< 4, double > convert_to_k(const CoordinateVector &cv)
Convert momentum space CoordinateVector to wave number k, where -pi/2 < k_i <= pi/2.
Definition fft.h:32
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:120
double gaussrand()
Gaussian random generation routine.
Definition random.cpp:212
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...