HubbardGPU
Hubbard diagonalisation on the GPU (and CPU)
 All Classes Files Functions Variables Typedefs Friends Macros
bare-ham.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2014 Ward Poelmans
2 
3 This file is part of Hubbard-GPU.
4 
5 Hubbard-GPU is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 Hubbard-GPU is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with Hubbard-GPU. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <iostream>
20 #include <cstdlib>
21 #include <cmath>
22 #include <sstream>
23 #include <assert.h>
24 #include <hdf5.h>
25 #include "bare-ham.h"
26 
27 // macro to help check return status of HDF5 functions
28 #define HDF5_STATUS_CHECK(status) { \
29  if(status < 0) \
30  std::cerr << __FILE__ << ":" << __LINE__ << \
31  ": Problem with writing to file. Status code=" \
32  << status << std::endl; \
33 }
34 
43 BareHamiltonian::BareHamiltonian(int L, int Nu, int Nd, double J, double U)
44 {
45  this->L = L;
46  this->Nu = Nu;
47  this->Nd = Nd;
48  this->J = J;
49  this->U = U;
50 
51  if(L > 30)
52  std::cerr << "We cannot do more then 30 sites" << std::endl;
53 
54  if(Nu > L || Nd > L)
55  std::cerr << "Too many electrons for this lattice" << std::endl;
56 
57  dim = CalcDim(L,Nu) * CalcDim(L,Nd);
58 
59  ham = 0;
60 
61  Hb = 1<<L; // highest power of 2 used
62  Hbc = CountBits(Hb-1);
63 }
64 
69 {
70  if(ham)
71  delete [] ham;
72 }
73 
83 int BareHamiltonian::CalcDim(int L, int N)
84 {
85  int result = 1;
86 
87  for(int i=1;i<=N;i++)
88  {
89  result *= L--;
90  result /= i;
91  }
92 
93  return result;
94 }
95 
107 {
108  return __builtin_popcount(bits);
109 }
110 
118 std::string BareHamiltonian::print_bin(myint num,int bitcount)
119 {
120  std::string output = "";
121  output.reserve(bitcount);
122 
123  for(int i=bitcount-1;i>=0;i--)
124  if( (num>>i) & 0x1 )
125  output += "1";
126  else
127  output += "0";
128 
129  return output;
130 }
131 
138 std::string BareHamiltonian::print_bin(myint num) const
139 {
140  return print_bin(num, Hbc);
141 }
142 
147 {
148  baseUp.reserve(CalcDim(L,Nu));
149  baseDown.reserve(CalcDim(L,Nd));
150 
151  for(myint i=0;i<Hb;i++)
152  {
153  if(CountBits(i) == Nd)
154  baseDown.push_back(i);
155 
156  if(CountBits(i) == Nu)
157  baseUp.push_back(i);
158  }
159 }
160 
166 {
167  BuildFullHam();
168 }
169 
178 int BareHamiltonian::CalcSign(int i,int j,myint a) const
179 {
180  int sign;
181 
182  // count the number of set bits between i and j in ket a
183  sign = CountBits(( ((1<<j) - 1) ^ ((1<<(i+1)) - 1) ) & a);
184 
185  // when uneven, we get a minus sign
186  if( sign & 0x1 )
187  return -1;
188  else
189  return +1;
190 }
191 
197 {
198  return L;
199 }
200 
206 {
207  return Nu;
208 }
209 
215 {
216  return Nd;
217 }
218 
224 {
225  return dim;
226 }
227 
232 double BareHamiltonian::getJ() const
233 {
234  return J;
235 }
236 
241 double BareHamiltonian::getU() const
242 {
243  return U;
244 }
245 
250 void BareHamiltonian::setU(double myU)
251 {
252  U = myU;
253 }
254 
260 myint BareHamiltonian::getBaseUp(unsigned int i) const
261 {
262  return baseUp[i];
263 }
264 
270 myint BareHamiltonian::getBaseDown(unsigned int i) const
271 {
272  return baseDown[i];
273 }
274 
280 std::vector<double> BareHamiltonian::ExactDiagonalizeFull(bool calc_eigenvectors)
281 {
282  assert(ham);
283 
284  std::vector<double> eigenvalues(dim);
285 
286  Diagonalize(dim, ham, eigenvalues.data(), calc_eigenvectors);
287 
288  return eigenvalues;
289 }
290 
296 {
297  int NumDown = CalcDim(L,Nd);
298 
299  for(int i=0;i<dim;i++)
300  {
301  int b = i % NumDown;
302  int a = (i - b)/NumDown;
303  std::cout << ham[i] << "\t |" << print_bin(getBaseUp(a)) << " " << print_bin(getBaseDown(b)) << ">" << std::endl;
304  }
305 }
306 
314 {
315  if(!m)
316  m = 10;
317 
318  std::vector<double> a(m,0);
319  std::vector<double> b(m,0);
320 
321  double *qa = new double [dim];
322  double *qb = new double [dim];
323 
324  double E = 1;
325 
326  int i;
327 
328  srand(time(0));
329 
330  for(i=0;i<dim;i++)
331  {
332  qa[i] = 0;
333  qb[i] = rand()*1.0/RAND_MAX;
334  }
335 
336  int incx = 1;
337 
338  double norm = 1.0/sqrt(ddot_(&dim,qb,&incx,qb,&incx));
339 
340  dscal_(&dim,&norm,qb,&incx);
341 
342  norm = 1;
343 
344  double *f1 = qa;
345  double *f2 = qb;
346  double *tmp;
347 
348  double alpha;
349 
350  std::vector<double> acopy(a);
351  std::vector<double> bcopy(b);
352 
353  i = 1;
354 
355  while(fabs(E-acopy[0]) > 1e-4)
356  {
357  E = acopy[0];
358 
359  for(;i<m;i++)
360  {
361  alpha = -b[i-1];
362  dscal_(&dim,&alpha,f1,&incx);
363 
364  mvprod(f2,f1,norm);
365 
366  a[i-1] = ddot_(&dim,f1,&incx,f2,&incx);
367 
368  alpha = -a[i-1];
369  daxpy_(&dim,&alpha,f2,&incx,f1,&incx);
370 
371  b[i] = sqrt(ddot_(&dim,f1,&incx,f1,&incx));
372 
373  if( fabs(b[i]) < 1e-10 )
374  break;
375 
376  alpha = 1.0/b[i];
377 
378  dscal_(&dim,&alpha,f1,&incx);
379 
380  tmp = f2;
381  f2 = f1;
382  f1 = tmp;
383  }
384 
385  acopy = a;
386  bcopy = b;
387 
388  char jobz = 'N';
389  int info;
390 
391  dstev_(&jobz,&m,acopy.data(),&bcopy.data()[1],&alpha,&m,&alpha,&info);
392 
393  if(info != 0)
394  std::cerr << "Error in Lanczos" << std::endl;
395 
396  m += 10;
397  a.resize(m);
398  b.resize(m);
399  }
400 
401  delete [] qa;
402  delete [] qb;
403 
404  return acopy[0];
405 }
406 
410 void BareHamiltonian::Print(bool list) const
411 {
412  for(int i=0;i<dim;i++)
413  {
414  if(list)
415  for(int j=0;j<dim;j++)
416  std::cout << i << "\t" << j << "\t\t" << ham[i+j*dim] << std::endl;
417  else
418  {
419  for(int j=0;j<dim;j++)
420  std::cout << ham[i+j*dim] << "\t";
421  std::cout << std::endl;
422  }
423  }
424 }
425 
430 {
431  for(unsigned int a=0;a<baseUp.size();a++)
432  for(unsigned int b=0;b<baseDown.size();b++)
433  std::cout << a*baseDown.size()+b << "\t" << print_bin(baseUp[a]) << "\t" << print_bin(baseDown[b]) << std::endl;
434 }
435 
437 {
438  // dimension of the matrix
439  int n = dim;
440 
441  // number of eigenvalues to calculate
442  int nev = 1;
443 
444  // reverse communication parameter, must be zero on first iteration
445  int ido = 0;
446  // standard eigenvalue problem A*x=lambda*x
447  char bmat = 'I';
448  // calculate the smallest algebraic eigenvalue
449  char which[] = {'S','A'};
450  // calculate until machine precision
451  double tol = 0;
452 
453  // the residual vector
454  double *resid = new double[n];
455 
456  // the number of columns in v: the number of lanczos vector
457  // generated at each iteration, ncv <= n
458  // We use the answer to life, the universe and everything, if possible
459  int ncv = 42;
460 
461  if( n < ncv )
462  ncv = n;
463 
464  // v containts the lanczos basis vectors
465  int ldv = n;
466  double *v = new double[ldv*ncv];
467 
468  int *iparam = new int[11];
469  iparam[0] = 1; // Specifies the shift strategy (1->exact)
470  iparam[2] = 3*n; // Maximum number of iterations
471  iparam[6] = 1; /* Sets the mode of dsaupd.
472  1 is exact shifting,
473  2 is user-supplied shifts,
474  3 is shift-invert mode,
475  4 is buckling mode,
476  5 is Cayley mode. */
477 
478  int *ipntr = new int[11]; /* Indicates the locations in the work array workd
479  where the input and output vectors in the
480  callback routine are located. */
481 
482  // array used for reverse communication
483  double *workd = new double[3*n];
484 
485  int lworkl = ncv*(ncv+8); /* Length of the workl array */
486  double *workl = new double[lworkl];
487 
488  // info = 0: random start vector is used
489  int info = 0; /* Passes convergence information out of the iteration
490  routine. */
491 
492  // rvec == 0 : calculate only eigenvalue
493  // rvec > 0 : calculate eigenvalue and eigenvector
494  int rvec = 1;
495 
496  // how many eigenvectors to calculate: 'A' => nev eigenvectors
497  char howmny = 'A';
498 
499  int *select;
500  // when howmny == 'A', this is used as workspace to reorder the eigenvectors
501  if( howmny == 'A' )
502  select = new int[ncv];
503 
504  // This vector will return the eigenvalues from the second routine, dseupd.
505  double *d = new double[nev];
506 
507  double *z = 0;
508 
509  if( rvec )
510  z = new double[n*nev];
511 
512  // not used if iparam[6] == 1
513  double sigma;
514 
515  // first iteration
516  dsaupd_(&ido, &bmat, &n, &which[0], &nev, &tol, resid, &ncv, v, &ldv, iparam, ipntr, workd, workl, &lworkl, &info);
517 
518  while( ido != 99 )
519  {
520  // matrix-vector multiplication
521  mvprod(workd+ipntr[0]-1, workd+ipntr[1]-1,0);
522 
523  dsaupd_(&ido, &bmat, &n, &which[0], &nev, &tol, resid, &ncv, v, &ldv, iparam, ipntr, workd, workl, &lworkl, &info);
524  }
525 
526  if( info < 0 )
527  std::cerr << "Error with dsaupd, info = " << info << std::endl;
528  else if ( info == 1 )
529  std::cerr << "Maximum number of Lanczos iterations reached." << std::endl;
530  else if ( info == 3 )
531  std::cerr << "No shifts could be applied during implicit Arnoldi update, try increasing NCV." << std::endl;
532 
533  dseupd_(&rvec, &howmny, select, d, z, &ldv, &sigma, &bmat, &n, which, &nev, &tol, resid, &ncv, v, &ldv, iparam, ipntr, workd, workl, &lworkl, &info);
534 
535  if ( info != 0 )
536  std::cerr << "Error with dseupd, info = " << info << std::endl;
537 
538  // use something to store the result before deleting...
539  sigma = d[0];
540 
541  if( rvec )
542  {
543  std::stringstream name;
544  name << "results-" << L << "-" << Nu << "-" << Nd << "-" << U << ".h5";
545  SaveToFile(name.str(), z, n*nev);
546 
547 /* int NumDown = CalcDim(L,Nd);
548  * double max = 0;
549  *
550  * for(int i=0;i<dim;i++)
551  * {
552  * int b = i % NumDown;
553  * int a = (i - b)/NumDown;
554  * if( ! (getBaseUp(a) & getBaseDown(b)) )
555  * {
556  * // if( fabs(z[i]) > 1e-3 )
557  * std::cout << z[i] << "\t |" << print_bin(getBaseUp(a)) << " " << print_bin(getBaseDown(b)) << ">\t" << (getBaseUp(a) & getBaseDown(b)) << std::endl;
558  *
559  * if(fabs(z[i]) > max)
560  * max = fabs(z[i]);
561  * }
562  * }
563  *
564  * std::cout << "max: " << max << std::endl;
565  */
566  }
567 
568  delete [] resid;
569  delete [] v;
570  delete [] iparam;
571  delete [] ipntr;
572  delete [] workd;
573  delete [] workl;
574  delete [] d;
575 
576  if( rvec )
577  delete [] z;
578 
579  if( howmny == 'A' )
580  delete [] select;
581 
582  return sigma;
583 }
584 
592 {
593  unsigned int dim = CalcDim(L,Nu) * CalcDim(L,Nd);
594  double result;
595 
596  // base kets
597  result = (CalcDim(L,Nu) + CalcDim(L,Nd)) * sizeof(myint);
598 
599  // BareHamiltonian matrix
600  result += dim * dim * sizeof(double);
601 
602  // eigenvalues of ham matrix
603  result += dim * sizeof(double);
604 
605  // work array for dsyev
606  result += (2*dim+1) * sizeof(double);
607 
608  return result;
609 }
610 
618 {
619  unsigned int dim = CalcDim(L,Nu) * CalcDim(L,Nd);
620  double result;
621 
622  // base kets
623  result = (CalcDim(L,Nu) + CalcDim(L,Nd)) * sizeof(myint);
624 
625  // hamup sparse matrix
626  result += CalcDim(L,Nu) * (((L-Nu)>Nu) ? 2*Nu : 2*(L-Nu)) * sizeof(double);
627 
628  // hamdown sparse matrix
629  result += CalcDim(L,Nd) * (((L-Nd)>Nd) ? 2*Nd : 2*(L-Nd)) * sizeof(double);
630 
631  // we store 2 vectors
632  result += 2 * dim * sizeof(double);
633 
634  return result;
635 }
636 
644 {
645  unsigned int dim = CalcDim(L,Nu) * CalcDim(L,Nd);
646  double result;
647 
648  // base kets
649  result = (CalcDim(L,Nu) + CalcDim(L,Nu)) * sizeof(myint);
650 
651  // hamup sparse matrix
652  result += CalcDim(L,Nu) * (((L-Nu)>Nu) ? 2*Nu : 2*(L-Nu)) * sizeof(double);
653 
654  // hamdown sparse matrix
655  result += CalcDim(L,Nd) * (((L-Nd)>Nd) ? 2*Nd : 2*(L-Nd)) * sizeof(double);
656 
657  // arpack stuff:
658  // resid
659  result += 1 * dim * sizeof(double);
660 
661  // v
662  result += 16 * dim * sizeof(double);
663 
664  // workd
665  result += 3 * dim * sizeof(double);
666 
667  // lworkd
668  result += 16 * (16+8) * sizeof(double);
669 
670  return result;
671 }
672 
681 void BareHamiltonian::Diagonalize(int dim, double *mat, double *eigs, bool calc_eigenvectors)
682 {
683  assert(mat && "mat must be allocated");
684  assert(eigs && "eigs must be allocated");
685 
686  char jobz;
687 
688  if(calc_eigenvectors)
689  jobz = 'V';
690  else
691  jobz = 'N';
692 
693  char uplo = 'U';
694 
695  int lwork, liwork;
696 
697  if(calc_eigenvectors)
698  {
699  lwork = 6*dim+1+2*dim*dim;
700  liwork = 3+5*dim;
701  } else
702  {
703  lwork = 2*dim+1;
704  liwork = 1;
705  }
706 
707  double *work = new double[lwork];
708 
709  int *iwork = new int[liwork];
710 
711  int info;
712 
713  dsyevd_(&jobz, &uplo, &dim, mat, &dim, eigs, work, &lwork, iwork, &liwork, &info);
714 
715  if(info != 0)
716  std::cerr << "Calculating eigenvalues failed..." << std::endl;
717 
718  delete [] work;
719  delete [] iwork;
720 }
721 
722 
727 void BareHamiltonian::SaveToFile(const std::string filename) const
728 {
729  if(!ham)
730  return;
731 
732  SaveToFile(filename, ham, dim*dim);
733 }
734 
741 void BareHamiltonian::SaveToFile(const std::string filename, double *data, int dim) const
742 {
743  hid_t file_id, dataset_id, dataspace_id, attribute_id;
744  herr_t status;
745 
746  file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
747  HDF5_STATUS_CHECK(file_id);
748 
749  hsize_t dimarr = dim;
750 
751  dataspace_id = H5Screate_simple(1, &dimarr, NULL);
752 
753  dataset_id = H5Dcreate(file_id, "ham", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
754 
755  status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, data );
756  HDF5_STATUS_CHECK(status);
757 
758  status = H5Sclose(dataspace_id);
759  HDF5_STATUS_CHECK(status);
760 
761  dataspace_id = H5Screate(H5S_SCALAR);
762 
763  attribute_id = H5Acreate (dataset_id, "L", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
764  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &L );
765  HDF5_STATUS_CHECK(status);
766  status = H5Aclose(attribute_id);
767  HDF5_STATUS_CHECK(status);
768 
769  attribute_id = H5Acreate (dataset_id, "Nu", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
770  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &Nu );
771  HDF5_STATUS_CHECK(status);
772  status = H5Aclose(attribute_id);
773  HDF5_STATUS_CHECK(status);
774 
775  attribute_id = H5Acreate (dataset_id, "Nd", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
776  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &Nd );
777  HDF5_STATUS_CHECK(status);
778  status = H5Aclose(attribute_id);
779  HDF5_STATUS_CHECK(status);
780 
781  attribute_id = H5Acreate (dataset_id, "J", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
782  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &J );
783  HDF5_STATUS_CHECK(status);
784  status = H5Aclose(attribute_id);
785  HDF5_STATUS_CHECK(status);
786 
787  attribute_id = H5Acreate (dataset_id, "U", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
788  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &U );
789  HDF5_STATUS_CHECK(status);
790  status = H5Aclose(attribute_id);
791  HDF5_STATUS_CHECK(status);
792 
793  attribute_id = H5Acreate (dataset_id, "dim", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
794  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &dim );
795  HDF5_STATUS_CHECK(status);
796  status = H5Aclose(attribute_id);
797  HDF5_STATUS_CHECK(status);
798 
799  status = H5Sclose(dataspace_id);
800  HDF5_STATUS_CHECK(status);
801 
802  status = H5Dclose(dataset_id);
803  HDF5_STATUS_CHECK(status);
804 
805  status = H5Fclose(file_id);
806  HDF5_STATUS_CHECK(status);
807 }
808 
809 /* vim: set ts=8 sw=4 tw=0 expandtab :*/