v2DM-DOCI  1.0
Matrix.cpp
Go to the documentation of this file.
1 /*
2  * @BEGIN LICENSE
3  *
4  * Copyright (C) 2014-2015 Ward Poelmans
5  *
6  * This file is part of v2DM-DOCI.
7  *
8  * v2DM-DOCI is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Foobar is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Foobar. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * @END LICENSE
22  */
23 
24 #include <iostream>
25 #include <iomanip>
26 #include <time.h>
27 #include <cmath>
28 #include <cstring>
29 #include <algorithm>
30 #include <assert.h>
31 #include <hdf5.h>
32 
33 using std::endl;
34 using std::ostream;
35 
36 #include "Matrix.h"
37 #include "lapack.h"
38 #include "Vector.h"
39 
40 #define HDF5_STATUS_CHECK(status) { \
41  if(status < 0) \
42  std::cerr << __FILE__ << ":" << __LINE__ << \
43  ": Problem with writing to file. Status code=" \
44  << status << std::endl; \
45 }
46 
47 
48 using namespace doci2DM;
49 
55 {
56  this->n = n;
57 
58  // we store column major (for Fortran compatiblity)
59  matrix.reset(new double[n*n]);
60 }
61 
66 Matrix::Matrix(const Matrix &orig){
67 
68  this->n = orig.n;
69 
70  matrix.reset(new double[n*n]);
71 
72  std::memcpy(matrix.get(), orig.matrix.get(), n*n*sizeof(double));
73 }
74 
76 {
77  n = orig.n;
78  matrix = std::move(orig.matrix);
79 }
80 
85 {
86 }
87 
92 Matrix &Matrix::operator=(const Matrix &matrix_copy)
93 {
94  assert(n == matrix_copy.n);
95 
96  std::memcpy(matrix.get(), matrix_copy.matrix.get(), n*n*sizeof(double));
97 
98  return *this;
99 }
100 
102 {
103  assert(n == matrix_copy.n);
104 
105  matrix = std::move(matrix_copy.matrix);
106 
107  return *this;
108 }
109 
115 
116  for(int i = 0;i < n*n;++i)
117  matrix[i] = a;
118 
119  return *this;
120 }
121 
126 Matrix &Matrix::operator+=(const Matrix &matrix_pl)
127 {
128  int dim = n*n;
129  int inc = 1;
130  double alpha = 1.0;
131 
132  daxpy_(&dim,&alpha,matrix_pl.matrix.get(),&inc,matrix.get(),&inc);
133 
134  return *this;
135 }
136 
141 Matrix &Matrix::operator-=(const Matrix &matrix_pl)
142 {
143  int dim = n*n;
144  int inc = 1;
145  double alpha = -1.0;
146 
147  daxpy_(&dim,&alpha,matrix_pl.matrix.get(),&inc,matrix.get(),&inc);
148 
149  return *this;
150 }
151 
157 Matrix &Matrix::daxpy(double alpha,const Matrix &matrix_pl)
158 {
159  int dim = n*n;
160  int inc = 1;
161 
162  daxpy_(&dim,&alpha,matrix_pl.matrix.get(),&inc,matrix.get(),&inc);
163 
164  return *this;
165 }
166 
172 {
173  int dim = n*n;
174  int inc = 1;
175 
176  dscal_(&dim,&c,matrix.get(),&inc);
177 
178  return *this;
179 }
180 
186 {
187  operator*=(1.0/c);
188 
189  return *this;
190 }
191 
199 double &Matrix::operator()(int i,int j)
200 {
201  assert(i<n && j<n);
202 
203  return matrix[i+j*n];
204 }
205 
213 double Matrix::operator()(int i,int j) const
214 {
215  assert(i<n && j<n);
216 
217  return matrix[i+j*n];
218 }
219 
224 {
225  return matrix.get();
226 }
227 
228 const double *Matrix::gMatrix() const
229 {
230  return matrix.get();
231 }
232 
236 int Matrix::gn() const
237 {
238  return n;
239 }
240 
244 double Matrix::trace() const
245 {
246  double ward = 0;
247 
248  for(int i = 0;i < n;++i)
249  ward += matrix[i+i*n];
250 
251  return ward;
252 }
253 
260 {
261  char jobz = 'V';
262  char uplo = 'U';
263 
264  int lwork = 3*n - 1;
265 
266  Vector eigenvalues(n);
267 
268  std::unique_ptr<double []> work (new double [lwork]);
269 
270  int info = 0;
271 
272  dsyev_(&jobz,&uplo,&n,matrix.get(),&n,eigenvalues.gVector(),work.get(),&lwork,&info);
273 
274  if(info)
275  std::cerr << "dsyev failed. info = " << info << std::endl;
276 
277  return eigenvalues;
278 }
279 
286 {
287  assert(n==2 && "Only works for 2x2 matrices");
288 
289  // matrix has this form:
290  // a c
291  // c d
292  auto a = matrix[0];
293  auto c = matrix[1];
294  auto &d = matrix[3];
295 
296  // double discr = (a+d)*(a+d) - 4*(a*d-c*c);
297  double discr = a*a + d*d + 4*c*c - 2*a*d;
298  // sometimes, we get -1e15. Deal with it.
299  if(discr<0)
300  discr = 0;
301 // assert(!(discr < 0) && "Impossible!?!");
302 
303  Vector eig(2);
304  eig[0] = ((a+d) - std::sqrt(discr))/2;
305  eig[1] = ((a+d) + std::sqrt(discr))/2;
306 
307  double norm = 1.0/std::sqrt(1 + (eig[0]-a)*(eig[0]-a)/(c*c));
308 
309  matrix[0] = norm;
310  matrix[1] = norm*(eig[0]-a)/c;
311 
312  norm = 1.0/std::sqrt(1 + (eig[1]-a)*(eig[1]-a)/(c*c));
313 
314  matrix[2] = norm;
315  matrix[3] = norm*(eig[1]-a)/c;
316 
317  return eig;
318 }
319 
324 double Matrix::ddot(const Matrix &matrix_i) const
325 {
326  int dim = n*n;
327  int inc = 1;
328 
329  return ddot_(&dim,matrix.get(),&inc,matrix_i.matrix.get(),&inc);
330 }
331 
336 {
337  char uplo = 'U';
338 
339  int info = 0;
340 
341  dpotrf_(&uplo,&n,matrix.get(),&n,&info);//cholesky decompositie
342 
343  if(info)
344  std::cerr << "dpotrf failed. info = " << info << std::endl;
345 
346  dpotri_(&uplo,&n,matrix.get(),&n,&info);//inverse berekenen
347 
348  if(info)
349  std::cerr << "dpotri failed. info = " << info << std::endl;
350 
351  //terug symmetrisch maken:
352  this->symmetrize();
353 }
354 
359 {
360  assert(n==2 && "Only works for 2x2 matrices");
361 
362  // matrix has this form:
363  // a c
364  // c d
365  auto a = matrix[0];
366  auto &c = matrix[1];
367  auto &d = matrix[3];
368 
369  double fac = 1.0/(a*d-c*c);
370 
371  matrix[0] = fac * d;
372  matrix[3] = fac * a;
373  matrix[1] *= -fac;
374  matrix[2] *= -fac;
375 }
376 
381 void Matrix::dscal(double alpha)
382 {
383  int dim = n*n;
384  int inc = 1;
385 
386  dscal_(&dim,&alpha,matrix.get(),&inc);
387 }
388 
393 {
394  fill_Random(time(NULL));
395 }
396 
401 void Matrix::fill_Random(int seed)
402 {
403  srand(seed);
404 
405  for(int i = 0;i < n;++i)
406  for(int j = i;j < n;++j)
407  matrix[i*n+j] = matrix[j*n+i] = (double) rand()/RAND_MAX;
408 }
409 
414 void Matrix::sqrt(int option)
415 {
416  Matrix hulp(*this);
417 
418  auto eigen = hulp.diagonalize();
419 
420  if(option == 1)
421  for(int i = 0;i < n;++i)
422  eigen[i] = std::sqrt(eigen[i]);
423  else
424  for(int i = 0;i < n;++i)
425  eigen[i] = 1.0/std::sqrt(eigen[i]);
426 
427  //hulp opslaan
428  Matrix hulp_c = hulp;
429 
430  //vermenigvuldigen met diagonaalmatrix
431  hulp_c.mdiag(eigen);
432 
433  //en tenslotte de laatste matrixvermenigvuldiging
434  char transA = 'N';
435  char transB = 'T';
436 
437  double alpha = 1.0;
438  double beta = 0.0;
439 
440  dgemm_(&transA,&transB,&n,&n,&n,&alpha,hulp_c.matrix.get(),&n,hulp.matrix.get(),&n,&beta,matrix.get(),&n);
441 }
442 
448 void Matrix::sqrt_2x2(int option)
449 {
450  assert(n==2 && "Only works for 2x2 matrices");
451 
452  // matrix has this form:
453  // a c
454  // c d
455  auto a = matrix[0];
456  auto c = matrix[1];
457  auto &d = matrix[3];
458 
459  // double discr = (a+d)*(a+d) - 4*(a*d-c*c);
460  double discr = a*a + d*d + 4*c*c - 2*a*d;
461  // sometimes, we get -1e15. Deal with it.
462  if(discr<0)
463  discr = 0;
464 // assert(!(discr < 0) && "Impossible!?!");
465 
466  // the eigenvalues
467  double x1 = ((a+d) - std::sqrt(discr))/2;
468  double x2 = ((a+d) + std::sqrt(discr))/2;
469 
470  double norm1 = 1.0/(1 + (x1-a)*(x1-a)/(c*c));
471  double norm2 = 1.0/(1 + (x2-a)*(x2-a)/(c*c));
472 
473  // calculate the sqrt from both eigenvalues
474  double x1_s, x2_s;
475 
476  if(option)
477  {
478  x1_s = std::sqrt(x1);
479  x2_s = std::sqrt(x2);
480  } else {
481  x1_s = 1.0/std::sqrt(x1);
482  x2_s = 1.0/std::sqrt(x2);
483  }
484 
485  matrix[0] = norm1*x1_s + norm2*x2_s;
486  matrix[1] = matrix[2] = norm1*x1_s*(x1-a)/c + norm2*x2_s*(x2-a)/c;
487  matrix[3] = norm1*x1_s*(x1-a)/c*(x1-a)/c + norm2*x2_s*(x2-a)/c*(x2-a)/c;
488 }
489 
494 void Matrix::mdiag(const Vector &diag)
495 {
496  int inc = 1;
497 
498  for(int i = 0;i < n;++i)
499  dscal_(&n, &diag.gVector()[i], &matrix[i*n], &inc);
500 }
501 
508 void Matrix::L_map(const Matrix &map,const Matrix &object)
509 {
510  char side = 'L';
511  char uplo = 'U';
512 
513  double alpha = 1.0;
514  double beta = 0.0;
515 
516  Matrix hulp(n);
517 
518  dsymm_(&side,&uplo,&n,&n,&alpha,map.matrix.get(),&n,object.matrix.get(),&n,&beta,hulp.matrix.get(),&n);
519 
520  side = 'R';
521 
522  dsymm_(&side,&uplo,&n,&n,&alpha,map.matrix.get(),&n,hulp.matrix.get(),&n,&beta,matrix.get(),&n);
523 
524  //expliciet symmetriseren van de uit matrix
525  this->symmetrize();
526 }
527 
533 void Matrix::L_map_2x2(const Matrix &map,const Matrix &object)
534 {
535  assert(n==2 && map.n==2 && object.n==2 && "Only works for 2x2 matrices");
536 
537  // matrix has this form:
538  // a c
539  // c d
540  auto &a1 = object.matrix[0];
541  auto &c1 = object.matrix[1];
542  auto &d1 = object.matrix[3];
543 
544  auto &a2 = map.matrix[0];
545  auto &c2 = map.matrix[1];
546  auto &d2 = map.matrix[3];
547 
548  matrix[0] = a2*(a1*a2+c1*c2)+c2*(a2*c1+c2*d1);
549  matrix[1] = matrix[2] = a2*(a1*c2+c1*d2)+c2*(c1*c2+d1*d2);
550  matrix[3] = c2*(a1*c2+c1*d2)+d2*(c1*c2+d1*d2);
551 }
552 
558 Matrix &Matrix::mprod(const Matrix &A,const Matrix &B)
559 {
560  char trans = 'N';
561 
562  double alpha = 1.0;
563  double beta = 0.0;
564 
565  dgemm_(&trans,&trans,&n,&n,&n,&alpha,A.matrix.get(),&n,B.matrix.get(),&n,&beta,matrix.get(),&n);
566 
567  return *this;
568 }
569 
574 {
575  for(int i = 0;i < n;++i)
576  for(int j = i + 1;j < n;++j)
577  matrix[j+i*n] = matrix[i+n*j];
578 }
579 
580 namespace doci2DM {
581  std::ostream &operator<<(std::ostream &output,const doci2DM::Matrix &matrix_p)
582  {
583  // output << std::setprecision(2) << std::fixed;
584  //
585  // for(int i = 0;i < matrix_p.gn();i++)
586  // {
587  // for(int j = 0;j < matrix_p.gn();j++)
588  // output << std::setfill('0') << std::setw(6) << matrix_p(i,j) << " ";
589  //
590  // output << endl;
591  // }
592  //
593  // output << endl;
594  // output << std::setprecision(10) << std::scientific;
595 
596  for(int i = 0;i < matrix_p.gn();++i)
597  for(int j = 0;j < matrix_p.gn();++j)
598  output << i << "\t" << j << "\t" << matrix_p(i,j) << endl;
599 
600  // output.unsetf(std::ios_base::floatfield);
601 
602  return output;
603  }
604 }
605 
606 void Matrix::SaveRawToFile(const std::string filename) const
607 {
608  hid_t file_id, dataset_id, dataspace_id, attribute_id;
609  herr_t status;
610 
611  file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
612  HDF5_STATUS_CHECK(file_id);
613 
614  hsize_t dimarr = n*n;
615 
616  dataspace_id = H5Screate_simple(1, &dimarr, NULL);
617 
618  dataset_id = H5Dcreate(file_id, "matrix", H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
619 
620  status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, matrix.get() );
621  HDF5_STATUS_CHECK(status);
622 
623  status = H5Sclose(dataspace_id);
624  HDF5_STATUS_CHECK(status);
625 
626  dataspace_id = H5Screate(H5S_SCALAR);
627 
628  attribute_id = H5Acreate (dataset_id, "n", H5T_STD_I32LE, dataspace_id, H5P_DEFAULT, H5P_DEFAULT);
629  status = H5Awrite (attribute_id, H5T_NATIVE_INT, &n );
630  HDF5_STATUS_CHECK(status);
631  status = H5Aclose(attribute_id);
632  HDF5_STATUS_CHECK(status);
633 
634  status = H5Sclose(dataspace_id);
635  HDF5_STATUS_CHECK(status);
636 
637  status = H5Dclose(dataset_id);
638  HDF5_STATUS_CHECK(status);
639 
640  status = H5Fclose(file_id);
641  HDF5_STATUS_CHECK(status);
642 }
643 
650 {
651  //init:
652 #ifdef __INTEL_COMPILER
653  p = 0;
654  m = *this;
655 #else
656  p = *this;
657  m = 0;
658 #endif
659 
660  std::unique_ptr<double []> eigenvalues(new double [n]);
661 
662  //diagonalize orignal matrix:
663  char jobz = 'V';
664  char uplo = 'U';
665 
666  // to avoid valgrind errors
667  int info = 0;
668 
669 #ifdef SYEVD
670  int lwork = 2*n*n+6*n+1;
671  int liwork = 5*n+3;
672 
673  std::unique_ptr<double []> work(new double [lwork]);
674  std::unique_ptr<int []> iwork(new int [liwork]);
675 
676  dsyevd_(&jobz,&uplo,&n,matrix.get(),&n,eigenvalues.get(),work.get(),&lwork,iwork.get(),&liwork,&info);
677 
678  if(info)
679  std::cerr << "dsyevd in sep_pm failed..." << std::endl;
680 #else
681  int lwork = 3*n - 1;
682 
683  std::unique_ptr<double []> work(new double [lwork]);
684 
685  dsyev_(&jobz,&uplo,&n,matrix.get(),&n,eigenvalues.get(),work.get(),&lwork,&info);
686 
687  if(info)
688  std::cerr << "dsyev in sep_pm failed..." << std::endl;
689 #endif
690 
691 #ifdef __INTEL_COMPILER
692  if( eigenvalues[0] >= 0 )
693  {
694  p = m;
695  m = 0;
696  return;
697  }
698 
699  if( eigenvalues[n-1] < 0)
700  return;
701 
702  int i = 0;
703 
704  while(i < n && eigenvalues[i] < 0.0)
705  eigenvalues[i++] = 0;
706 
707  Matrix copy(*this);
708 
709  int inc = 1;
710 
711  // multiply each column with an eigenvalue
712  for(i = 0;i < n;++i)
713  dscal_(&n,&eigenvalues[i],&matrix[i*n],&inc);
714 
715  char transA = 'N';
716  char transB = 'T';
717 
718  double alpha = 1.0;
719  double beta = 0.0;
720 
721  dgemm_(&transA,&transB,&n,&n,&n,&alpha,matrix.get(),&n,copy.matrix.get(),&n,&beta,p.matrix.get(),&n);
722 
723  m -= p;
724 #else
725  if( eigenvalues[0] >= 0 )
726  return;
727 
728  if( eigenvalues[n-1] < 0)
729  {
730  m = p;
731  p = 0;
732  return;
733  }
734 
735  //fill the plus and minus matrix
736  int i = 0;
737 
738  while(i < n && eigenvalues[i] < 0.0)
739  {
740  for(int j = 0;j < n;++j)
741  for(int k = j;k < n;++k)
742  m(j,k) += eigenvalues[i] * (*this)(j,i) * (*this)(k,i);
743 
744  ++i;
745  }
746 
747  m.symmetrize();
748 
749  p -= m;
750 #endif
751 }
752 
759 {
760  assert(n==2 && "Only works for 2x2 matrices");
761 
762  // matrix has this form:
763  // a c
764  // c d
765  auto &a = matrix[0];
766  auto &c = matrix[1];
767  auto &d = matrix[3];
768 
769  // there are 3 cases: both positive, both negative and one of each
770 
771  // double discr = (a+d)*(a+d) - 4*(a*d-c*c);
772  double discr = a*a + d*d + 4*c*c - 2*a*d;
773  // sometimes, we get -1e15. Deal with it.
774  if(discr<0)
775  discr = 0;
776 // assert(!(discr < 0) && "Impossible!?!");
777 
778  double x1 = (a+d) - std::sqrt(discr);
779  double x2 = (a+d) + std::sqrt(discr);
780 
781  // both positive
782  if(x1>0)
783  {
784  pos = *this;
785  neg = 0;
786  return;
787  }
788 
789  // both negative
790  if(x2<0)
791  {
792  pos = 0;
793  neg = *this;
794  return;
795  }
796 
797  // one negative eigenvalue
798  x1 /= 2; // the actual eigenvalue still have to be divided by 2
799  double norm = 1.0/(1 + (x1-a)*(x1-a)/(c*c));
800 
801  neg(0,0) = x1*norm;
802  neg(1,0) = neg(0,1) = x1*norm*(x1-a)/c;
803  neg(1,1) = x1*norm*(x1-a)*(x1-a)/(c*c);
804 
805  pos = *this;
806  pos -= neg;
807 }
808 
813 {
814  (*this) = 0;
815 
816  for(int i=0;i<n;i++)
817  matrix[i*n+i] = 1.0;
818 }
819 
820 /* vim: set ts=3 sw=3 expandtab :*/
double ddot_(int *n, double *x, int *incx, double *y, int *incy)
double trace() const
Definition: Matrix.cpp:244
void SaveRawToFile(const std::string) const
Definition: Matrix.cpp:606
void L_map(const Matrix &, const Matrix &)
Definition: Matrix.cpp:508
#define HDF5_STATUS_CHECK(status)
Definition: Matrix.cpp:40
void invert()
Definition: Matrix.cpp:335
Matrix & operator=(const Matrix &)
Definition: Matrix.cpp:92
Matrix & mprod(const Matrix &, const Matrix &)
Definition: Matrix.cpp:558
void dsymm_(char *side, char *uplo, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc)
Vector diagonalize_2x2()
Definition: Matrix.cpp:285
void symmetrize()
Definition: Matrix.cpp:573
double * gMatrix()
Definition: Matrix.cpp:223
std::ostream & operator<<(std::ostream &output, const doci2DM::BlockStructure< MyBlockType > &blocks_p)
void dgemm_(char *transA, char *transB, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc)
void mdiag(const Vector &)
Definition: Matrix.cpp:494
void dsyev_(char *jobz, char *uplo, int *n, double *A, int *lda, double *W, double *work, int *lwork, int *info)
void invert_2x2()
Definition: Matrix.cpp:358
double * gVector()
Definition: Vector.cpp:221
double & operator()(int i, int j)
Definition: Matrix.cpp:199
void dsyevd_(char *jobz, char *uplo, int *n, double *a, int *lda, double *w, double *work, int *lwork, int *iwork, int *liwork, int *info)
void daxpy_(int *n, double *alpha, double *x, int *incx, double *y, int *incy)
void sep_pm_2x2(Matrix &, Matrix &)
Definition: Matrix.cpp:758
Matrix & operator/=(double)
Definition: Matrix.cpp:185
Matrix & operator*=(double)
Definition: Matrix.cpp:171
void dpotrf_(char *uplo, int *n, double *A, int *lda, int *INFO)
virtual ~Matrix()
Definition: Matrix.cpp:84
double ddot(const Matrix &) const
Definition: Matrix.cpp:324
int n
dimension of the matrix
Definition: Matrix.h:148
Vector diagonalize()
Definition: Matrix.cpp:259
Matrix & operator-=(const Matrix &)
Definition: Matrix.cpp:141
void dscal(double alpha)
Definition: Matrix.cpp:381
int gn() const
Definition: Matrix.cpp:236
void L_map_2x2(const Matrix &, const Matrix &)
Definition: Matrix.cpp:533
void sqrt(int option)
Definition: Matrix.cpp:414
void sqrt_2x2(int option)
Definition: Matrix.cpp:448
Matrix & operator+=(const Matrix &)
Definition: Matrix.cpp:126
void fill_Random()
Definition: Matrix.cpp:392
std::unique_ptr< double[]> matrix
pointer of doubles, contains the numbers, the matrix
Definition: Matrix.h:145
void dscal_(int *n, double *alpha, double *x, int *incx)
void sep_pm(Matrix &, Matrix &)
Definition: Matrix.cpp:649
Matrix(int n)
Definition: Matrix.cpp:54
void dpotri_(char *uplo, int *n, double *A, int *lda, int *INFO)
Matrix & daxpy(double alpha, const Matrix &)
Definition: Matrix.cpp:157