HubbardGPU
Hubbard diagonalisation on the GPU (and CPU)
 All Classes Files Functions Variables Typedefs Friends Macros
ham-spin.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 <algorithm>
21 #include <cmath>
22 #include <sstream>
23 #include <cstring>
24 #include <iomanip>
25 #include <hdf5.h>
26 #include "ham-spin.h"
27 
28 // macro to help check return status of HDF5 functions
29 #define HDF5_STATUS_CHECK(status) { \
30  if(status < 0) \
31  std::cerr << __FILE__ << ":" << __LINE__ << \
32  ": Problem with writing to file. Status code=" \
33  << status << std::endl; \
34 }
35 
44 SpinHamiltonian::SpinHamiltonian(int L, int Nu, int Nd, double J, double U): BareHamiltonian(L,Nu,Nd,J,U)
45 {
46  if( (Nu+Nd) & 0x1 )
47  std::cerr << "There will be trouble: make sure the number of particles is even!" << std::endl;
48 
49  Sz = Nu-Nd;
50 
51  if(Sz<0)
52  {
53  Sz *= -1;
54  auto tmp = Nu;
55  Nu = Nd;
56  Nd = tmp;
57  std::cout << "Swapping Nu and Nd to have positive Sz!" << std::endl;
58  }
59 }
60 
65 {
66 }
67 
73 {
74  BuildSpinBase();
75 }
76 
81 {
82  BuildHamWithS(-1);
83 }
84 
90 {
91  blockmat.resize(spinbasis->getnumblocks());
92 
93 #pragma omp parallel for
94  for(int i=0;i<spinbasis->getnumblocks();i++)
95  {
96  int K = spinbasis->getKS(i).first;
97  int S = spinbasis->getKS(i).second;
98 
99  if(myS != -1 && S != myS)
100  continue;
101 
102  std::cout << "Building K=" << K << " S=" << S << std::endl;
103 
104  int cur_dim = spinbasis->getBlock(i).getspacedim();
105 
106  // calculate all elements for this block first
107  std::unique_ptr<double []> hop(new double[cur_dim]);
108  std::unique_ptr<int []> interact(new int[cur_dim*cur_dim]);
109 
110  for(int a=0;a<cur_dim;a++)
111  {
112  auto bra = spinbasis->getBlock(i).Get(a);
113  hop[a] = hopping(bra.first) + hopping(bra.second);
114 
115  for(int b=a;b<cur_dim;b++)
116  {
117  auto ket = spinbasis->getBlock(i).Get(b);
118 
119  interact[b+a*cur_dim] = interaction(bra.first, bra.second, ket.first, ket.second);
120  interact[a+b*cur_dim] = interact[b+a*cur_dim];
121  }
122  }
123 
124  std::cout << "Filling hamiltonian" << std::endl;
125 
126  int mydim = spinbasis->getBlock(i).getdim();
127  std::unique_ptr<matrix> tmp (new matrix (mydim,mydim));
128 
129  auto &sparse = spinbasis->getBlock(i).getSparse();
130 
131 #pragma omp parallel for schedule(guided)
132  for(int a=0;a<mydim;a++)
133  {
134  for(int b=a;b<mydim;b++)
135  {
136  (*tmp)(a,b) = 0;
137 
138  for(int k=0;k<sparse.NumOfElInCol(a);k++)
139  for(int l=0;l<sparse.NumOfElInCol(b);l++)
140  {
141  if(sparse.GetElementRowIndexInCol(a,k) == sparse.GetElementRowIndexInCol(b,l) )
142  (*tmp)(a,b) += sparse.GetElementInCol(a,k) * sparse.GetElementInCol(b,l) * \
143  J * hop[sparse.GetElementRowIndexInCol(a,k)];
144 
145  (*tmp)(a,b) += sparse.GetElementInCol(a,k) * sparse.GetElementInCol(b,l) * U/L * \
146  interact[sparse.GetElementRowIndexInCol(a,k) + cur_dim * sparse.GetElementRowIndexInCol(b,l)];
147  }
148 
149  (*tmp)(b,a) = (*tmp)(a,b);
150  }
151  }
152 
153  blockmat[i] = std::move(tmp);
154  }
155 }
156 
162 {
163  blockmat.resize(0);
164 
165 #pragma omp parallel for
166  for(int i=0;i<spinbasis->getnumblocks();i++)
167  {
168  int K = spinbasis->getKS(i).first;
169  int S = spinbasis->getKS(i).second;
170 
171 #pragma omp critical
172  {
173  std::cout << "Building K=" << K << " S=" << S << std::endl;
174  }
175 
176  int cur_dim = spinbasis->getBlock(i).getspacedim();
177 
178  // calculate all elements for this block first
179  std::unique_ptr<double []> hop(new double[cur_dim]);
180  std::unique_ptr<int []> interact(new int[cur_dim*cur_dim]);
181 
182  for(int a=0;a<cur_dim;a++)
183  {
184  auto bra = spinbasis->getBlock(i).Get(a);
185  hop[a] = hopping(bra.first) + hopping(bra.second);
186 
187  for(int b=a;b<cur_dim;b++)
188  {
189  auto ket = spinbasis->getBlock(i).Get(b);
190 
191  interact[b+a*cur_dim] = interaction(bra.first, bra.second, ket.first, ket.second);
192  interact[a+b*cur_dim] = interact[b+a*cur_dim];
193  }
194  }
195 
196  int mydim = spinbasis->getBlock(i).getdim();
197  std::unique_ptr<matrix> Jterm (new matrix (mydim,mydim));
198  std::unique_ptr<matrix> Uterm (new matrix (mydim,mydim));
199 
200  auto &sparse = spinbasis->getBlock(i).getSparse();
201 
202 #pragma omp parallel for schedule(guided)
203  for(int a=0;a<mydim;a++)
204  {
205  for(int b=a;b<mydim;b++)
206  {
207  (*Jterm)(a,b) = 0;
208  (*Uterm)(a,b) = 0;
209 
210  for(int k=0;k<sparse.NumOfElInCol(a);k++)
211  for(int l=0;l<sparse.NumOfElInCol(b);l++)
212  {
213  if(sparse.GetElementRowIndexInCol(a,k) == sparse.GetElementRowIndexInCol(b,l) )
214  (*Jterm)(a,b) += sparse.GetElementInCol(a,k) * sparse.GetElementInCol(b,l) * \
215  1 * hop[sparse.GetElementRowIndexInCol(a,k)];
216 
217  (*Uterm)(a,b) += sparse.GetElementInCol(a,k) * sparse.GetElementInCol(b,l) * 1.0/L * \
218  interact[sparse.GetElementRowIndexInCol(a,k) + cur_dim * sparse.GetElementRowIndexInCol(b,l)];
219  }
220 
221  (*Jterm)(b,a) = (*Jterm)(a,b);
222  (*Uterm)(b,a) = (*Uterm)(a,b);
223  }
224  }
225 
226 #pragma omp critical
227  {
228  std::stringstream nameJ;
229  nameJ << "Jterm-" << K << "-" << S << ".h5";
230  SaveToFile(nameJ.str(),Jterm->getpointer(), mydim*mydim);
231 
232  std::stringstream nameU;
233  nameU << "Uterm-" << K << "-" << S << ".h5";
234  SaveToFile(nameU.str(),Uterm->getpointer(), mydim*mydim);
235  }
236  }
237 }
238 
249 {
250  int result = 0;
251 
252  myint downket = d;
253  myint upket, upbra;
254 
255  while(downket)
256  {
257  // select rightmost down state in the ket
258  myint k2sp = downket & (~downket + 1);
259  // set it to zero
260  downket ^= k2sp;
261 
262  int k2 = CountBits(k2sp-1);
263 
264  int signk2 = 1;
265 
266  // when uneven, extra minus sign
267  if(Nu & 0x1)
268  signk2 *= -1;
269 
270  signk2 *= CalcSign(k2,Hbc,d);
271 
272  upket = c;
273 
274  while(upket)
275  {
276  // select rightmost up state in the ket
277  myint k1sp = upket & (~upket + 1);
278  // set it to zero
279  upket ^= k1sp;
280 
281  int k1 = CountBits(k1sp-1);
282 
283  int signk1 = CalcSign(k1,Hbc,c);
284 
285  myint K = (k1+k2) % L;
286 
287  upbra = a;
288 
289  while(upbra)
290  {
291  // select rightmost up state in the ket
292  myint k3sp = upbra & (~upbra + 1);
293  // set it to zero
294  upbra ^= k3sp;
295 
296  // k3: spin up in bra
297  int k3 = CountBits(k3sp-1);
298 
299  if(! ((c ^ k1sp) == (a ^ k3sp)) )
300  continue;
301 
302  // k4: spin down in bra
303  int k4 = (K-k3+L)%L;
304 
305  if(! ((1<<k4 & b) && ((d ^ k2sp) == (b ^ 1<<k4)) ))
306  continue;
307 
308  int signk3 = CalcSign(k3,Hbc,a);
309 
310  int signk4 = 1;
311 
312  // when uneven, extra minus sign
313  if(Nu & 0x1)
314  signk4 *= -1;
315 
316  signk4 *= CalcSign(k4,Hbc,b);
317 
318  result += signk1*signk2*signk3*signk4;
319  }
320  }
321  }
322 
323  return result;
324 }
325 
334 {
335  double result = 0;
336 
337  while(ket)
338  {
339  // select rightmost one
340  myint hop = ket & (~ket + 1);
341  // set it to zero in ket
342  ket ^= hop;
343 
344  result += -2* cos(2.0*M_PI/L * CountBits(hop-1));
345  }
346 
347  return result;
348 }
349 
356 void SpinHamiltonian::mvprod(double *x, double *y, double alpha) const
357 {
358  double beta = 1;
359  int incx = 1;
360  char uplo = 'U';
361 
362  int offset = 0;
363 
364  for(int B=0;B<blockmat.size();B++)
365  {
366  int dim = blockmat[B]->getn();
367 
368  dsymv_(&uplo,&dim,&beta,blockmat[B]->getpointer(),&dim,&x[offset],&incx,&alpha,&y[offset],&incx);
369 
370  offset += dim;
371  }
372 }
373 
381 std::vector<double> SpinHamiltonian::ExactDiagonalizeFull(bool calc_eigenvectors)
382 {
383  int mydim = 0;
384 
385  for(int B=0;B<blockmat.size();B++)
386  if(blockmat[B])
387  mydim += blockmat[B]->getn();
388 
389  std::vector<double> eigenvalues(mydim);
390 
391  int offset = 0;
392 
393  for(int B=0;B<blockmat.size();B++)
394  {
395  if(!blockmat[B])
396  continue;
397 
398  int dim = blockmat[B]->getn();
399 
400  Diagonalize(dim, blockmat[B]->getpointer(), &eigenvalues[offset], calc_eigenvectors);
401 
402  offset += dim;
403  }
404 
405  std::sort(eigenvalues.begin(), eigenvalues.end());
406 
407  return eigenvalues;
408 }
409 
418 std::vector< std::tuple<int,int,double> > SpinHamiltonian::ExactSpinDiagonalizeFull(bool calc_eigenvectors)
419 {
420  std::vector< std::tuple<int, int, double> > energy;
421 
422  std::vector< std::tuple<int,int,class matrix> > eigs (blockmat.size());
423 
424 #pragma omp parallel for
425  for(int B=0;B<blockmat.size();B++)
426  {
427  if(!blockmat[B])
428  continue;
429 
430  int K = spinbasis->getKS(B).first;
431  int S = spinbasis->getKS(B).second;
432 
433  int mydim = blockmat[B]->getn();
434 
435  matrix cur_eigs(mydim,1);
436 
437  Diagonalize(mydim, blockmat[B]->getpointer(), cur_eigs.getpointer(), calc_eigenvectors);
438 
439  eigs[B] = std::make_tuple(K, S, std::move(cur_eigs));
440  }
441 
442  for(int i=0;i<eigs.size();i++)
443  for(int j=0;j<std::get<2>(eigs[i]).getn();j++)
444  energy.push_back(std::make_tuple(std::get<0>(eigs[i]), std::get<1>(eigs[i]), std::get<2>(eigs[i])[j]));
445 
446  // sort to energy
447  std::sort(energy.begin(), energy.end(),
448  [](const std::tuple<int,int,double> & a, const std::tuple<int,int,double> & b) -> bool
449  {
450  return std::get<2>(a) < std::get<2>(b);
451  });
452 
453  return energy;
454 }
455 
457 {
458  for(int i=0;i<spinbasis->getnumblocks();i++)
459  {
460  int K = spinbasis->getKS(i).first;
461  int S = spinbasis->getKS(i).second;
462 
463  std::cout << "Block K=" << K << " S=" << S << std::endl;
464  spinbasis->getBlock(i).Print();
465  }
466 }
467 
473 {
474  int Smax = (Nu+Nd)/2;
475 
476  BasisList basis(L, Nu, Nd);
477 
478  // number of up
479  int Su = Nu+Nd;
480  // number of down
481  int Sd = 0;
482  int cur_Sz = (Su - Sd)/2;
483 
484  // first do Sz=max (all up)
485  std::unique_ptr<MomBasis> tmp_basis(new MomBasis(L,Su,Sd));
486  // the K of the only possible state
487  int tmp_k = ((L-1)*L)/2 % L;
488  basis.Create(tmp_k,cur_Sz,cur_Sz,*tmp_basis,tmp_basis->getdimK(tmp_k));
489 
490  // go one level lower
491  Su--;
492  Sd++;
493  cur_Sz = (Su - Sd)/2;
494  tmp_basis.reset(new MomBasis(L,Su,Sd));
495  basis.Create(tmp_k,cur_Sz+1,cur_Sz,*tmp_basis, basis.Get(tmp_k,cur_Sz+1,cur_Sz+1).getdim());
496  basis.Get(tmp_k,cur_Sz+1,cur_Sz).Slad_min(basis.Get(tmp_k,cur_Sz+1,cur_Sz+1));
497  // projection
498  basis.Create(tmp_k,cur_Sz,cur_Sz,*tmp_basis,tmp_basis->getdimK(tmp_k) - basis.Get(tmp_k,cur_Sz+1,cur_Sz).getdim() );
499  basis.DoProjection(tmp_k,cur_Sz,cur_Sz,*tmp_basis);
500 
501  for(int k=0;k<L;k++)
502  if(k != tmp_k)
503  basis.Create(k,cur_Sz,cur_Sz,*tmp_basis,tmp_basis->getdimK(k));
504 
505  basis.Clean(cur_Sz+1);
506 
507  // go one level lower
508  Su--;
509  Sd++;
510  cur_Sz = (Su - Sd)/2;
511 
512  while( cur_Sz >= Sz )
513  {
514  tmp_basis.reset(new MomBasis(L,Su,Sd));
515 
516 #pragma omp parallel for
517  for(int K=0;K<L;K++)
518  {
519  int cur_dim = tmp_basis->getdimK(K);
520 
521  for(int cur_S=Smax;cur_S>cur_Sz;cur_S--)
522  {
523 #pragma omp critical
524  {
525  std::cout << "At: Sz=" << cur_Sz << " K=" << K << " (" << cur_dim << ") => S=" << cur_S << std::endl;
526  }
527 
528  if(basis.Exists(K,cur_S,cur_Sz+1))
529  {
530  basis.Create(K,cur_S,cur_Sz,*tmp_basis,basis.Get(K,cur_S,cur_Sz+1).getdim());
531 
532  cur_dim -= basis.Get(K,cur_S,cur_Sz+1).getdim();
533  basis.Get(K,cur_S,cur_Sz).Slad_min(basis.Get(K,cur_S,cur_Sz+1));
534  }
535  }
536 
537 #pragma omp critical
538  {
539  std::cout << "Projection to K=" << K << " S=" << cur_Sz << " Sz=" << cur_Sz << std::endl;
540  }
541  basis.Create(K,cur_Sz,cur_Sz,*tmp_basis,cur_dim);
542  basis.DoProjection(K, cur_Sz, cur_Sz, *tmp_basis);
543  }
544 
545  // drop what we don't need
546  basis.Clean(cur_Sz+1);
547 
548  Su--;
549  Sd++;
550  cur_Sz = (Su - Sd)/2;
551  }
552 
553  spinbasis.reset(new SpinBasis(L,Nu,Nd,basis));
554 }
555 
556 void SpinHamiltonian::SaveBasis(const char *filename) const
557 {
558  if(spinbasis)
559  spinbasis->SaveBasis(filename);
560 }
561 
562 void SpinHamiltonian::ReadBasis(const char *filename)
563 {
564  if(spinbasis)
565  spinbasis->ReadBasis(filename);
566  else
567  spinbasis.reset(new SpinBasis(filename));
568 }
569 
578 void SpinHamiltonian::GenerateData(double Ubegin, double Uend, double step, std::string filename)
579 {
580  hid_t file_id, group_id, dataset_id, dataspace_id, attribute_id;
581  herr_t status;
582 
583  file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
584  HDF5_STATUS_CHECK(file_id);
585 
586  group_id = H5Gcreate(file_id, "run", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
587  HDF5_STATUS_CHECK(group_id);
588 
589  dataspace_id = H5Screate(H5S_SCALAR);
590 
591  attribute_id = H5Acreate (group_id, "L", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
592  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &L );
593  HDF5_STATUS_CHECK(status);
594  status = H5Aclose(attribute_id);
595  HDF5_STATUS_CHECK(status);
596 
597  attribute_id = H5Acreate (group_id, "Nu", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
598  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &Nu );
599  HDF5_STATUS_CHECK(status);
600  status = H5Aclose(attribute_id);
601  HDF5_STATUS_CHECK(status);
602 
603  attribute_id = H5Acreate (group_id, "Nd", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
604  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &Nd );
605  HDF5_STATUS_CHECK(status);
606  status = H5Aclose(attribute_id);
607  HDF5_STATUS_CHECK(status);
608 
609  attribute_id = H5Acreate (group_id, "J", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
610  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &J );
611  HDF5_STATUS_CHECK(status);
612  status = H5Aclose(attribute_id);
613  HDF5_STATUS_CHECK(status);
614 
615  attribute_id = H5Acreate (group_id, "Ubegin", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
616  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &Ubegin );
617  HDF5_STATUS_CHECK(status);
618  status = H5Aclose(attribute_id);
619  HDF5_STATUS_CHECK(status);
620 
621  attribute_id = H5Acreate (group_id, "Uend", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
622  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &Uend );
623  HDF5_STATUS_CHECK(status);
624  status = H5Aclose(attribute_id);
625  HDF5_STATUS_CHECK(status);
626 
627  attribute_id = H5Acreate (group_id, "Ustep", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
628  status = H5Awrite (attribute_id, H5T_NATIVE_DOUBLE, &step );
629  HDF5_STATUS_CHECK(status);
630  status = H5Aclose(attribute_id);
631  HDF5_STATUS_CHECK(status);
632 
633  status = H5Sclose(dataspace_id);
634  HDF5_STATUS_CHECK(status);
635 
636  status = H5Gclose(group_id);
637  HDF5_STATUS_CHECK(status);
638 
639  status = H5Fclose(file_id);
640  HDF5_STATUS_CHECK(status);
641 
642  std::vector< std::unique_ptr<double []> > all_eigs;
643  all_eigs.resize(blockmat.size());
644 
645  double Ucur = Ubegin;
646 
647  while(Ucur <= Uend)
648  {
649  std::cout << "U = " << Ucur << std::endl;
650  setU(Ucur);
651 
652  BuildFullHam();
653 
654 #pragma omp parallel for
655  for(int B=0;B<blockmat.size();B++)
656  {
657  int mydim = blockmat[B]->getn();
658 
659  std::unique_ptr<double []> eigs(new double [mydim]);
660 
661  Diagonalize(mydim, blockmat[B]->getpointer(), eigs.get(), false);
662 
663  all_eigs[B] = std::move(eigs);
664  }
665 
666  hid_t U_id;
667  std::stringstream name;
668  name << std::setprecision(5) << std::fixed << "/run/" << Ucur;
669 
670  file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
671  HDF5_STATUS_CHECK(file_id);
672 
673  U_id = H5Gcreate(file_id, name.str().c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
674  HDF5_STATUS_CHECK(U_id);
675 
676  for(int B=0;B<all_eigs.size();B++)
677  {
678  int K = spinbasis->getKS(B).first;
679  int S = spinbasis->getKS(B).second;
680 
681  int mydim = blockmat[B]->getn();
682 
683  hsize_t dimarr = mydim;
684 
685  dataspace_id = H5Screate_simple(1, &dimarr, NULL);
686 
687  std::stringstream cur_block;
688  cur_block << B;
689  dataset_id = H5Dcreate(U_id, cur_block.str().c_str(), H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
690 
691  status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, all_eigs[B].get() );
692  HDF5_STATUS_CHECK(status);
693 
694  status = H5Sclose(dataspace_id);
695  HDF5_STATUS_CHECK(status);
696 
697  dataspace_id = H5Screate(H5S_SCALAR);
698 
699  attribute_id = H5Acreate (dataset_id, "K", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
700  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &K );
701  HDF5_STATUS_CHECK(status);
702  status = H5Aclose(attribute_id);
703  HDF5_STATUS_CHECK(status);
704 
705  attribute_id = H5Acreate (dataset_id, "S", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
706  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &S );
707  HDF5_STATUS_CHECK(status);
708  status = H5Aclose(attribute_id);
709  HDF5_STATUS_CHECK(status);
710 
711  status = H5Dclose(dataset_id);
712  HDF5_STATUS_CHECK(status);
713  }
714 
715  status = H5Gclose(U_id);
716  HDF5_STATUS_CHECK(status);
717 
718  status = H5Fclose(file_id);
719  HDF5_STATUS_CHECK(status);
720 
721  Ucur += step;
722  }
723 }
724 
725 /* vim: set ts=8 sw=4 tw=0 expandtab :*/