matrix_m.h Source File

LibRPA: matrix_m.h Source File
LibRPA
matrix_m.h
1 /*
2  * @file matrix_m.h
3  * @brief implementing a matrix template class with support of column-major or row-major style
4  */
5 #pragma once
6 #include <array>
7 #include <cassert>
8 #include <iomanip>
9 #include <vector>
10 #include <cmath>
11 #include <ctime>
12 #include <random>
13 #include <valarray>
14 #include <memory>
15 #include <functional>
16 #include <utility>
17 #include <ostream>
18 
19 #include "base_utility.h"
20 #include "lapack_connector.h"
21 #include "vec.h"
22 #include "utils_io.h"
23 
25 typedef std::function<int(const int& nr, const int& ir, const int& nc, const int& ic)> Indx_picker_2d;
26 typedef std::function<void(const int& indx, const int& nr, int& ir, const int& nc, int& ic)> Indx_folder_2d;
27 // major dependent flatted index of 2D matrix
29 inline int flatid_rm(const int &nr, const int &ir, const int &nc, const int &ic) { return ir*nc+ic; }
31 inline int flatid_cm(const int &nr, const int &ir, const int &nc, const int &ic) { return ic*nr+ir; }
32 
33 // major dependent folding of 1D index to 2D index of matrix
35 inline void foldid_rm(const int& indx, const int &nr, int &ir, const int &nc, int &ic) { ir = indx / nc; ic = indx % nc; }
37 inline void foldid_cm(const int& indx, const int &nr, int &ir, const int &nc, int &ic) { ir = indx % nr; ic = indx / nr; }
38 
39 
40 enum MAJOR
41 {
42  ROW = 0,
43  COL = 1
44 };
45 const Indx_picker_2d Indx_pickers_2d[]
46 {
47  flatid_rm,
48  flatid_cm
49 };
50 const Indx_folder_2d Indx_folders_2d[]
51 {
52  foldid_rm,
53  foldid_cm
54 };
55 
56 
57 template <typename T>
58 void get_nr_nc_from_nested_vector(const std::vector<std::vector<T>> &nested_vec, int &nr, int &nc)
59 {
60  nr = nested_vec.size();
61  int nc_ = 0;
62  for (const auto& v: nested_vec)
63  nc_ = v.size() > nc_? v.size() : nc_;
64  nc = nc_;
65 }
66 
67 template <typename T>
68 void expand_nested_vector_to_pointer(const std::vector<std::vector<T>> &nested_vector, const int &nr, const int &nc, T* c, bool row_major = true)
69 {
70  int nc_, nr_;
71  get_nr_nc_from_nested_vector(nested_vector, nr_, nc_);
72  if (nr < nr_) nr_ = nr;
73  if (nc < nc_) nc_ = nc;
74 
75  const Indx_picker_2d picker = Indx_pickers_2d[int(!row_major)];
76 
77  if (nr&&nc)
78  {
79  for (int ir = 0; ir < nr_; ir++)
80  for (int ic = 0; ic < std::min(size_t(nc_), nested_vector[ir].size()); ic++)
81  c[picker(nr, ir, nc, ic)] = nested_vector[ir][ic];
82  }
83 }
84 
86 template <typename T>
88 {
89 public:
90  std::shared_ptr<std::array<int, 2>> dim = nullptr;
91  std::shared_ptr<std::valarray<T>> data = nullptr;
92 
93 private:
94  int size_;
95  int mrank_;
96  void set_size_mrank()
97  {
98  mrank_ = std::min((*dim)[0], (*dim)[1]);
99  size_ = (*dim)[0] * (*dim)[1];
100  }
101 
102 public:
103  matrix_data()
104  {
105  dim = std::make_shared<std::array<int, 2>>();
106  mrank_ = size_ = 0;
107  }
108  matrix_data(const std::array<int, 2> &dim_in)
109  {
110  dim = std::make_shared<std::array<int, 2>>(dim_in);
111  set_size_mrank();
112  if (size())
113  data = std::make_shared<std::valarray<T>>(size());
114  }
115  matrix_data(const std::array<int, 2> &dim_in, const std::valarray<T> &data_in)
116  {
117  dim = std::make_shared<std::array<int, 2>>(dim_in);
118  data = std::make_shared<std::valarray<T>>(data_in);
119  set_size_mrank();
120  }
121  matrix_data(const std::array<int, 2> &dim_in, const T* const pdata)
122  {
123  dim = std::make_shared<std::array<int, 2>>(dim_in);
124  set_size_mrank();
125  if (size() > 0)
126  data = std::make_shared<std::valarray<T>>(pdata, size());
127  }
128  matrix_data(const std::array<int, 2> &dim_in,
129  const std::shared_ptr<std::valarray<T>> &data_in)
130  : data(data_in)
131  {
132  dim = std::make_shared<std::array<int, 2>>(dim_in);
133  set_size_mrank();
134  }
135  matrix_data(const std::shared_ptr<std::array<int, 2>> &dim_in,
136  const std::shared_ptr<std::valarray<T>> &data_in)
137  : dim(dim_in), data(data_in)
138  {
139  set_size_mrank();
140  }
141 
142  inline int nr() const { return (*dim)[0]; }
143  inline int nc() const { return (*dim)[1]; }
144  inline int size() const { return size_; }
145  inline int mrank() const { return mrank_; }
146 
147  // data access
148  inline T &operator[](const int i) { return (*data)[i]; }
149  inline const T &operator[](const int i) const { return (*data)[i]; }
150 
151  inline T* ptr() { return &(*this->data)[0]; }
152  inline const T* ptr() const { return &(*this->data)[0]; }
153  inline std::shared_ptr<std::valarray<T>> sptr() { return data; }
154  inline const std::shared_ptr<std::valarray<T>> sptr() const { return data; }
155 
156  void resize(const std::array<int, 2>& dim_new)
157  {
158  *dim = dim_new;
159  set_size_mrank();
160  if (data == nullptr)
161  {
162  if (size() > 0)
163  data = std::make_shared<std::valarray<T>>(0, size());
164  }
165  else
166  {
167  if (size() > 0)
168  data->resize(size());
169  else
170  data->resize(0);
171  }
172  }
173 
174  void reshape(const std::array<int, 2>& dim_new)
175  {
176  assert(dim_new[0] * dim_new[1] == size());
177  *dim = dim_new;
178  set_size_mrank();
179  }
180 
181  void operator+=(const matrix_data<T> &mdata)
182  {
183  if (data != nullptr && mdata.data != nullptr)
184  *data += *(mdata.data);
185  }
186 
187  void operator-=(const matrix_data<T> &mdata)
188  {
189  if (data != nullptr && mdata.data != nullptr)
190  *data -= *(mdata.data);
191  }
192 };
193 
194 template <typename T>
195 bool operator==(const matrix_data<T> &mdata1, const matrix_data<T> &mdata2)
196 {
197  if (mdata1.nr() != mdata2.nr() || mdata1.nc() != mdata2.nc())
198  return false;
199  if (mdata1.sptr() == nullptr || mdata2.sptr() == nullptr)
200  return false;
201  using real_t = typename to_real<T>::type;
202  const bool is_double = std::is_same<T, double>::value;
203  const real_t thres = is_double ? 1e-14 : 1e-6;
204  auto diff = std::abs(*(mdata1.data) - *(mdata2.data));
205  for (int i = 0; i < mdata1.size(); i++)
206  if (::get_real(diff[i]) > thres)
207  return false;
208  return true;
209 }
210 
211 template <typename T>
212 class matrix_m
213 {
214 public:
216  static const bool is_complex = is_complex_t<T>::value;
217  static const bool is_double = std::is_same<T, double>::value;
218 private:
220  MAJOR major_;
222  int ld_;
224  Indx_picker_2d indx_picker_2d_;
226  Indx_folder_2d indx_folder_2d_;
227 
228 public:
229  matrix_data<T> dataobj;
230 
231 private:
232  void assign_value(const T &v)
233  {
234  if (size() > 0)
235  *(dataobj.data) = v;
236  }
237  void assign_value(const T* const pv, MAJOR major_pv)
238  {
239  if (major_ == major_pv)
240  {
241  for (int i = 0; i < size(); i++) dataobj[i] = pv[i];
242  }
243  else
244  {
245  const Indx_picker_2d pv_picker = Indx_pickers_2d[major_pv];
246  for (int ir = 0; ir < nr(); ir++)
247  for (int ic = 0; ic < nc(); ic++)
248  this->at(ir, ic) = pv[pv_picker(nr(), ir, nc(), ic)];
249  }
250  }
251 
252  void set_ld()
253  {
254  ld_ = (major_ == MAJOR::ROW ? nc() : nr());
255  }
256 
257  void set_indx_picker_folder()
258  {
259  indx_picker_2d_ = Indx_pickers_2d[major_];
260  indx_folder_2d_ = Indx_folders_2d[major_];
261  }
262 
263 public:
264  using type = T;
265  using real_t = typename to_real<T>::type;
266  using cplx_t = typename to_cplx<T>::type;
267 
268  // constructors
269  matrix_m() : major_(MAJOR::ROW), ld_(0), dataobj()
270  {
271  set_ld();
272  set_indx_picker_folder();
273  }
274  matrix_m(const int &nrows, const int &ncols, MAJOR major = MAJOR::ROW): major_(major), ld_(0), dataobj({nrows, ncols})
275  {
276  set_ld();
277  set_indx_picker_folder();
278  zero_out();
279  }
280  matrix_m(const int &nrows, const int &ncols, const T * const parr, MAJOR major = MAJOR::ROW): major_(major), ld_(0), dataobj({nrows, ncols}, parr)
281  {
282  set_ld();
283  set_indx_picker_folder();
284  }
285  matrix_m(const int &nrows, const int &ncols, const T * const parr, MAJOR major_arr, MAJOR major): major_(major), ld_(0), dataobj({nrows, ncols})
286  {
287  set_ld();
288  set_indx_picker_folder();
289  zero_out();
290  assign_value(parr, major_arr);
291  }
292  matrix_m(const int &nrows, const int &ncols, const std::shared_ptr<std::valarray<T>> &valarr, MAJOR major_valarr): major_(major_valarr), ld_(0), dataobj({nrows, ncols}, valarr)
293  {
294  set_ld();
295  set_indx_picker_folder();
296  }
297  // constructor from a nested vector
298  matrix_m(const std::vector<std::vector<T>> &nested_vector, MAJOR major = MAJOR::ROW): major_(major), ld_(0), dataobj()
299  {
300  int nr_, nc_;
301  get_nr_nc_from_nested_vector(nested_vector, nr_, nc_);
302  dataobj.resize({nr_, nc_});
303  set_ld();
304  set_indx_picker_folder();
305  zero_out();
306  expand_nested_vector_to_pointer(nested_vector, nr_, nc_, dataobj.ptr(), is_row_major());
307  }
308 
309  // copy constructor
310  matrix_m(const matrix_m<T> &m) = default;
311 
312  matrix_m(matrix_m &&m) = default;
313 
314  // destructor
315  ~matrix_m() {}
316 
317  void zero_out()
318  {
319  assign_value(T(0));
320  }
321 
322  void clear()
323  {
324  this->resize(0, 0);
325  }
326 
327  void set_diag(const T& v)
328  {
329  for (int i = 0; i < mrank(); i++)
330  this->at(i, i) = v;
331  }
332 
333  void scale_row(int irow, const T &scale)
334  {
335  for (int ic = 0; ic < nc(); ic++)
336  this->at(irow, ic) *= scale;
337  }
338 
339  void scale_col(int icol, const T &scale)
340  {
341  for (int ir = 0; ir < nr(); ir++)
342  this->at(ir, icol) *= scale;
343  }
344 
346  void randomize(const T &lb = 0, const T &ub = 1, bool symmetrize = false, bool hermitian = false)
347  {
348  static std::default_random_engine e(time(0));
349  std::uniform_real_distribution<real_t> dr(::get_real(lb), ::get_real(ub));
351  {
352  std::uniform_real_distribution<real_t> di(::get_imag(lb), ::get_imag(ub));
353  if (symmetrize)
354  {
355  for (int i = 0; i != mrank(); i++)
356  {
357  join_re_im((*this)(i, i), dr(e), di(e));
358  for (int j = i + 1; j < mrank(); j++)
359  {
360  join_re_im((*this)(i, j), dr(e), di(e));
361  (*this)(j, i) = (*this)(i, j);
362  }
363  }
364  }
365  else if (hermitian)
366  {
367  for (int i = 0; i != mrank(); i++)
368  {
369  join_re_im((*this)(i, i), dr(e), real_t(0.0));
370  for (int j = i + 1; j < mrank(); j++)
371  {
372  join_re_im((*this)(i, j), dr(e), di(e));
373  (*this)(j, i) = ::get_conj((*this)(i, j));
374  }
375  }
376  }
377  else
378  for (int i = 0; i != size(); i++)
379  join_re_im(this->dataobj[i], dr(e), di(e));
380  }
381  else
382  {
383  if (symmetrize)
384  for (int i = 0; i != mrank(); i++)
385  {
386  this->at(i, i) = dr(e);
387  for (int j = i + 1; j < mrank(); j++)
388  {
389  this->at(i, j) = dr(e);
390  this->at(j, i) = this->at(i, j);
391  }
392  }
393  else
394  {
395  for (int i = 0; i != size(); i++)
396  this->dataobj[i] = dr(e);
397  }
398  }
399  }
400 
401  // access to private variables
402  int nr() const { return dataobj.nr(); }
403  int nc() const { return dataobj.nc(); }
404  // leading dimension
405  int ld() const { return ld_; }
406  MAJOR major() const { return major_; }
407  int size() const { return dataobj.size(); }
408  int mrank() const { return dataobj.mrank(); }
409 
410  bool is_row_major() const { return major_ == MAJOR::ROW; }
411  bool is_col_major() const { return major_ == MAJOR::COL; }
412 
413  // indexing
414  T &operator()(const int ir, const int ic) { return this->at(ir, ic); }
415  const T &operator()(const int ir, const int ic) const { return this->at(ir, ic); }
416  T &at(const int ir, const int ic) { return dataobj[indx_picker_2d_(nr(), ir, nc(), ic)]; }
417  const T &at(const int ir, const int ic) const { return dataobj[indx_picker_2d_(nr(), ir, nc(), ic)]; }
418 
419  T* ptr() { return dataobj.ptr(); }
420  const T* ptr() const { return dataobj.ptr(); }
421  std::shared_ptr<std::valarray<T>> sptr() { return dataobj.sptr(); }
422  const std::shared_ptr<std::valarray<T>> sptr() const { return dataobj.sptr(); }
423 
424  matrix_m<T> copy() const
425  {
426  matrix_m<T> m_copy(nr(), nc(), ptr(), major_);
427  return m_copy;
428  }
429 
430  // swap major
431  void swap_to_row_major()
432  {
433  if (is_col_major())
434  {
435  int nr_old = nr(), nc_old = nc();
436  this->transpose();
437  reshape(nr_old, nc_old);
438  major_ = MAJOR::ROW;
439  set_ld();
440  set_indx_picker_folder();
441  }
442  }
443  void swap_to_col_major()
444  {
445  if (is_row_major())
446  {
447  int nr_old = nr(), nc_old = nc();
448  this->transpose();
449  reshape(nr_old, nc_old);
450  major_ = MAJOR::COL;
451  set_ld();
452  set_indx_picker_folder();
453  }
454  }
455 
456  // assignment copy
457  matrix_m<T> & operator=(const matrix_m<T> &m) = default;
458  matrix_m<T> & operator=(matrix_m<T> &&m) = default;
459 
460  // operator overload
461  matrix_m<T> & operator=(const T &cnum)
462  {
463  (dataobj.data)->fill(cnum);
464  return *this;
465  }
466 
467  template <typename T1>
468  void operator+=(const matrix_m<T1> &m)
469  {
470  assert(size() == m.size() && major() == m.major());
471  for (int i = 0; i < size(); i++)
472  dataobj[i] += m.dataobj[i];
473  }
474  template <typename T1>
475  void operator+=(const std::vector<T1> &v)
476  {
477  assert(nc() == v.size());
478  for (int i = 0; i < nr(); i++)
479  for (int ic = 0; ic < nc(); ic++)
480  this->at(i, ic) += v[ic];
481  }
482  template <typename T1>
483  void operator+=(const T1 &cnum)
484  {
485  for (int i = 0; i < size(); i++)
486  dataobj[i] += cnum;
487  }
488 
489  template <typename T1>
490  void operator-=(const matrix_m<T1> &m)
491  {
492  assert(size() == m.size() && major() == m.major());
493  for (int i = 0; i < size(); i++)
494  dataobj[i] -= m.dataobj[i];
495  }
496  template <typename T1>
497  void operator-=(const std::vector<T1> &v)
498  {
499  assert(nc() == v.size());
500  for (int i = 0; i < nr(); i++)
501  for (int ic = 1; ic < nc(); ic++)
502  this->at(i, ic) += v[ic];
503  }
504  template <typename T1>
505  void operator-=(const T1 &cnum)
506  {
507  for (int i = 0; i < size(); i++)
508  dataobj[i] -= cnum;
509  }
510  template <typename T1>
511  void operator*=(const T1 &cnum)
512  {
513  for (int i = 0; i < size(); i++)
514  dataobj[i] *= cnum;
515  }
516  template <typename T1>
517  void operator/=(const T1 &cnum)
518  {
519  for (int i = 0; i < size(); i++)
520  dataobj[i] /= cnum;
521  }
522 
523  void reshape(int nrows_new, int ncols_new)
524  {
525  assert(size() == nrows_new * ncols_new);
526  dataobj.reshape({nrows_new, ncols_new});
527  }
528  matrix_m<T>& resize(int nrows_new, int ncols_new)
529  {
530  dataobj.resize({nrows_new, ncols_new});
531  set_ld();
532  zero_out();
533  return *this;
534  }
535 
536  matrix_m<T>& resize(int nrows_new, int ncols_new, MAJOR major)
537  {
538  major_ = major;
539  set_indx_picker_folder();
540  return this->resize(nrows_new, ncols_new);
541  }
542 
543  matrix_m<T>& conj()
544  {
545  if (!matrix_m<T>::is_complex) return *this;
546  for (int i = 0; i < this->size(); i++)
547  dataobj[i] = get_conj(dataobj[i]);
548  return *this;
549  };
550 
551  matrix_m<T> get_transpose(bool conjugate = false) const
552  {
553  matrix_m<T> m_trans(nc(), nr(), major_);
554  for (int i = 0; i < nr(); i++)
555  for (int j = 0; j < nc(); j++)
556  m_trans(j, i) = this->at(i, j);
557  if (conjugate) m_trans.conj();
558  return m_trans;
559  }
560 
561  matrix_m<T>& transpose(bool conjugate = false)
562  {
563  if (nr() == nc())
564  {
565  // handling within the memory of c pointer for square matrix
566  int n = nr();
567  for (int i = 0; i != n; i++)
568  for (int j = i + 1; j < n; j++)
569  {
570  T temp = dataobj[i * n + j];
571  dataobj[i * n + j] = dataobj[j * n + i];
572  dataobj[j * n + i] = temp;
573  }
574  }
575  else
576  {
577  // NOTE: may have memory issue for large matrix as extra memory is requested
578  if (size() > 0)
579  {
580  std::valarray<T> a_new(size());
581  for (int i = 0; i < nr(); i++)
582  for (int j = 0; j < nc(); j++)
583  a_new[indx_picker_2d_(nc(), j, nr(), i)] = this->at(i, j);
584  *(dataobj.data) = a_new;
585  }
586  }
587  if (conjugate) conj();
588  return *this;
589  }
590 
591  matrix_m<cplx_t> to_complex() const
592  {
593  matrix_m<cplx_t> m(nr(), nc(), major_);
594  for (int i = 0; i < size(); i++)
595  m.dataobj[i] = dataobj[i];
596  return m;
597  }
598 
599  matrix_m<real_t> get_real() const
600  {
601  matrix_m<real_t> m(nr(), nc(), major_);
602  for (int i = 0; i < size(); i++)
603  m.dataobj[i] = ::get_real(dataobj[i]);
604  return m;
605  }
606 
607  matrix_m<real_t> get_imag() const
608  {
609  matrix_m<real_t> m(nr(), nc(), major_);
610  for (int i = 0; i < size(); i++)
611  m.dataobj[i] = ::get_imag(dataobj[i]);
612  return m;
613  }
614 
615  int count_absle(real_t thres) const
616  {
617  int count = 0;
618  for (int i = 0; i < size(); i++)
619  if (std::abs(dataobj[i]) <= thres) count++;
620  return count;
621  }
622 
623  int count_absge(real_t thres) const
624  {
625  int count = 0;
626  for (int i = 0; i < size(); i++)
627  if (std::abs(dataobj[i]) >= thres) count++;
628  return count;
629  }
630 
631  void argmin(int &ir, int &ic) const
632  {
633  // for complex type, compare the absolute value
634  const std::function<real_t(T)> filter =
635  matrix_m<T>::is_complex ? [](T a) { return std::abs(a); } : [](T a) { return ::get_real(a); };
636  real_t value = std::numeric_limits<real_t>::max();
637  int indx = 0;
638  for (int i = 0; i < size(); i++)
639  if (filter(dataobj[i]) < value)
640  {
641  indx = i;
642  value = filter(dataobj[i]);
643  }
644  indx_folder_2d_(indx, nr(), ir, nc(), ic);
645  }
646 
647  void argmax(int &ir, int &ic) const
648  {
649  // for complex type, compare the absolute value
650  const std::function<real_t(T)> filter =
651  matrix_m<T>::is_complex ? [](T a) { return std::abs(a); } : [](T a) { return ::get_real(a); };
652  real_t value = std::numeric_limits<real_t>::min();
653  int indx = 0;
654  for (int i = 0; i < size(); i++)
655  if (filter(dataobj[i]) > value)
656  {
657  indx = i;
658  value = filter(dataobj[i]);
659  }
660  indx_folder_2d_(indx, nr(), ir, nc(), ic);
661  }
662 
663  real_t min() const
664  {
665  // for complex type, compare the absolute value
666  const std::function<real_t(T)> filter =
667  matrix_m<T>::is_complex ? [](T a) { return std::abs(a); } : [](T a) { return ::get_real(a); };
668  real_t value = std::numeric_limits<real_t>::max();
669  for (int i = 0; i < size(); i++)
670  if (filter(dataobj[i]) < value)
671  {
672  value = filter(dataobj[i]);
673  }
674  return value;
675  }
676 
677  real_t max() const
678  {
679  // for complex type, compare the absolute value
680  const std::function<real_t(T)> filter =
681  matrix_m<T>::is_complex ? [](T a) { return std::abs(a); } : [](T a) { return ::get_real(a); };
682  real_t value = std::numeric_limits<real_t>::min();
683  for (int i = 0; i < size(); i++)
684  if (filter(dataobj[i]) > value)
685  {
686  value = filter(dataobj[i]);
687  }
688  return value;
689  }
690 
691  real_t absmin() const
692  {
693  real_t value = 0;
694  for (int i = 0; i < size(); i++)
695  value = std::min(value, std::abs(dataobj[i]));
696  return value;
697  }
698 
699  real_t absmax() const
700  {
701  real_t value = 0;
702  for (int i = 0; i < size(); i++)
703  value = std::max(value, std::abs(dataobj[i]));
704  return value;
705  }
706 };
707 
709 typedef matrix_m<double> Matd;
712 
713 template <typename T1, typename T2>
714 inline bool same_major(const matrix_m<T1> &m1, const matrix_m<T2> &m2)
715 {
716  return m1.major() == m2.major();
717 }
718 
719 template <typename T1, typename T2>
720 inline bool same_type(const matrix_m<T1> &m1, matrix_m<T2> &m2)
721 {
722  return std::is_same<T1, T2>::value;
723 }
724 
725 // copy matrix elements of type T1 src to matrix of a different type
726 template <typename T1, typename T2>
727 void copy(const matrix_m<T1> &src, matrix_m<T2> &dest)
728 {
729  assert(src.size() == dest.size() && same_major(src, dest));
730  for (int i = 0; i < src.size(); i++)
731  dest.dataobj[i] = src.dataobj[i];
732 }
733 
734 // copy matrix elements of src to matrix of the same type
735 template <typename T>
736 void copy(const matrix_m<T> &src, matrix_m<T> &dest)
737 {
738  assert(src.size() == dest.size() && same_major(src, dest));
739  memcpy(dest.ptr(), src.ptr(), src.size() * sizeof(T));
740 }
741 
742 template <typename T1, typename T2>
743 matrix_m<T1> operator+(const matrix_m<T1> &m1, const matrix_m<T2> &m2)
744 {
745  assert(m1.nc() == m2.nc() && m1.nr() == m2.nr() && same_major(m1, m2));
746  auto sum = m1.copy();
747  sum += m2;
748  return sum;
749 }
750 
751 template <typename T1, typename T2>
752 inline matrix_m<T1> operator+(const matrix_m<T1> &m, const std::vector<T2> &v)
753 {
754  assert(m.nc() == v.size());
755  auto mnew = m.copy();
756  mnew += v;
757  return mnew;
758 }
759 
760 template <typename T, typename T2>
761 inline matrix_m<T> operator+(const matrix_m<T> &m, const T2 &cnum)
762 {
763  auto sum = m.copy();
764  sum += cnum;
765  return sum;
766 }
767 
768 template <typename T1, typename T2>
769 inline matrix_m<T2> operator+(const T1 &cnum, const matrix_m<T2> &m)
770 {
771  return m + cnum;
772 }
773 
774 template <typename T1, typename T2>
775 inline matrix_m<T1> operator-(const matrix_m<T1> &m1, const matrix_m<T2> &m2)
776 {
777  assert(m1.nc() == m2.nc() && m1.nr() == m2.nr());
778  auto mnew = m1.copy();
779  mnew -= m2;
780  return mnew;
781 }
782 
783 template <typename T1, typename T2>
784 inline matrix_m<T1> operator-(const matrix_m<T1> &m, const std::vector<T2> &v)
785 {
786  assert(m.nc() == v.size());
787  auto mnew = m.copy();
788  mnew -= v;
789  return mnew;
790 }
791 
793 template <typename T>
794 void matmul(const matrix_m<T> &m1, const matrix_m<T> &m2, matrix_m<T> &prod)
795 {
796  assert(m1.nc() == m2.nr());
797  assert(m1.nr() == prod.nr());
798  assert(m2.nc() == prod.nc());
799  assert(m1.major() == m2.major() && m1.major() == prod.major());
800 
801  if (m1.is_row_major())
802  {
803  LapackConnector::gemm('N', 'N', m1.nr(), m2.nc(), m1.nc(),
804  T(1.0), m1.ptr(), m1.nc(), m2.ptr(), m2.nc(), T(0.0), prod.ptr(), prod.nc());
805  }
806  else
807  {
808  LapackConnector::gemm_f('N', 'N', m1.nr(), m2.nc(), m1.nc(),
809  T(1.0), m1.ptr(), m1.nr(), m2.ptr(), m2.nr(), T(0.0), prod.ptr(), prod.nr());
810  }
811 }
812 
813 template <typename T>
814 inline matrix_m<T> operator*(const matrix_m<T> &m1, const matrix_m<T> &m2)
815 {
816  assert(m1.nc() == m2.nr());
817  assert(m1.major() == m2.major());
818 
819  auto major = m1.major();
820 
821  matrix_m<T> prod(m1.nr(), m2.nc(), major);
822  matmul<T>(m1, m2, prod);
823  return prod;
824 }
825 
826 // a naive implementation for integer matrix, e.g. Spglib rotation matrices
827 template <>
828 inline matrix_m<int> operator*(const matrix_m<int> &m1, const matrix_m<int> &m2)
829 {
830  assert(m1.nc() == m2.nr());
831  matrix_m<int> prod(m1.nr(), m2.nc(), m1.major());
832  for (int ir = 0; ir < m1.nr(); ir++)
833  for (int ik = 0; ik < m1.nc(); ik++)
834  for (int ic = 0; ic < m2.nc(); ic++)
835  prod(ir, ic) += m1(ir, ik) * m2(ik, ic);
836  return prod;
837 }
838 
839 template <typename T1, typename T2>
840 inline matrix_m<T1> operator*(const matrix_m<T1> &m, const T2 &cnum)
841 {
842  auto sum = m.copy();
843  sum *= cnum;
844  return sum;
845 }
846 
847 template <typename T1, typename T2>
848 inline matrix_m<T2> operator*(const T1 &cnum, const matrix_m<T2> &m)
849 {
850  return m * cnum;
851 }
852 
853 template <typename T>
854 std::ostream& operator<<(std::ostream& os, const matrix_m<T> &m)
855 {
856  os << str(m);
857  return os;
858 }
859 
861 template <typename T>
862 void inverse(const matrix_m<T> &m, matrix_m<T> &m_inv)
863 {
864  assert(m.major() == m_inv.major());
865  int lwork = m.nr();
866  int info = 0;
867  T work[lwork];
868  int ipiv[std::min(m.nr(), m.nc())];
869  m_inv.resize(m.nr(), m.nc());
870  copy(m, m_inv);
871  // debug
872  // std::cout << m_inv.size() << " " << m_inv(0, 0) << " " << m_inv(m.nr-1, m.nc-1) << std::endl;
873  if (m.is_row_major())
874  {
875  LapackConnector::getrf(m_inv.nr(), m_inv.nc(), m_inv.ptr(), m_inv.nr(), ipiv, info);
876  LapackConnector::getri(m_inv.nr(), m_inv.ptr(), m_inv.nr(), ipiv, work, lwork, info);
877  }
878  else
879  {
880  LapackConnector::getrf_f(m_inv.nr(), m_inv.nc(), m_inv.ptr(), m_inv.nr(), ipiv, info);
881  LapackConnector::getri_f(m_inv.nr(), m_inv.ptr(), m_inv.nr(), ipiv, work, lwork, info);
882  }
883  m_inv.reshape(m_inv.nc(), m_inv.nr());
884 }
885 
887 template <typename T>
888 inline matrix_m<T> transpose(const matrix_m<T> &m, bool conjugate = false)
889 {
890  return m.get_transpose(conjugate);
891 }
892 
894 template <typename T>
895 inline matrix_m<T> conj(const matrix_m<T> &m)
896 {
897  return m.copy().conj();
898 }
899 
901 template <typename T>
902 T get_determinant(const matrix_m<T> &m)
903 {
904  matrix_m<T> m_copy = m.copy();
905  int lwork = m_copy.nr();
906  int info = 0;
907  int mrank = std::min(m_copy.nr(), m_copy.nc());
908  T work[lwork];
909  int ipiv[mrank];
910  // debug
911  if (m_copy.is_row_major())
912  LapackConnector::getrf(m_copy.nr(), m_copy.nc(), m_copy.ptr(), m_copy.nr(), ipiv, info);
913  else
914  LapackConnector::getrf_f(m_copy.nr(), m_copy.nc(), m_copy.ptr(), m_copy.nr(), ipiv, info);
915  T det = 1;
916  for (int i = 0; i < mrank; i++)
917  {
918  /* std::cout << i << " " << ipiv[i] << " " << m.c[i*m.nc+i] << " "; */
919  det *= static_cast<T>(2*int(ipiv[i] == (i+1))-1) * m_copy(i, i);
920  }
921  return det;
922 }
923 
924 template <typename T>
925 matrix_m<std::complex<T>> power_hemat(matrix_m<std::complex<T>> &mat,
926  T power, bool filter_original,
927  const T &threshold = -1.e5)
928 {
930 
931  assert (mat.nr() == mat.nc());
932  const char jobz = 'V';
933  const char uplo = 'U';
934  const int n = mat.nr();
935  int nb;
937  nb = LapackConnector::ilaenv(1, "zheev", "VU", n, -1, -1, -1);
938  else
939  nb = LapackConnector::ilaenv(1, "cheev", "VU", n, -1, -1, -1);
940 
941  int lwork = mat.nc() * (nb+1);
942  int info = 0;
943  T w[n], wpow[n];
944  T rwork[3*n-2];
945  std::complex<T> work[lwork];
946  if (mat.is_row_major())
947  LapackConnector::heev(jobz, uplo, n, mat.ptr(), n, w, work, lwork, rwork, info);
948  else
949  LapackConnector::heev_f(jobz, uplo, n, mat.ptr(), n, w, work, lwork, rwork, info);
950  bool is_int_power = fabs(power - int(power)) < 1e-8;
951  // debug
952  // for ( int i = 0; i != n; i++ )
953  // {
954  // cout << w[i] << " ";
955  // }
956  // cout << endl;
957  // end debug
958  for ( int i = 0; i != n; i++ )
959  {
960  if (w[i] < 0 && w[i] > threshold && !is_int_power)
961  lib_printf("Warning! kept negative eigenvalue with non-integer power: # %d ev = %f , pow = %f\n", i, w[i], power);
962  if (fabs(w[i]) < 1e-10 && power < 0)
963  lib_printf("Warning! nearly-zero eigenvalue with negative power: # %d ev = %f , pow = %f\n", i, w[i], power);
964  if (w[i] < threshold)
965  {
966  wpow[i] = 0;
967  if (filter_original)
968  w[i] = 0;
969  }
970  else
971  wpow[i] = w[i];
972  wpow[i] = pow(wpow[i], power);
973  }
974  auto evconj = transpose(mat, true);
975  auto temp = evconj.copy();
976  for (int i = 0; i != n; i++)
977  temp.scale_row(i, wpow[i]);
978  auto pmat = mat * temp;
979  // recover the original matrix here
980  temp = mat.copy();
981  for (int i = 0; i != n; i++)
982  evconj.scale_row(i, w[i]);
983  matmul(temp, evconj, mat);
984  return pmat;
985 }
986 
987 template <typename T>
988 bool operator==(const matrix_m<T> &m1, const matrix_m<T> &m2)
989 {
990  return m1.dataobj == m2.dataobj;
991 }
992 
994 template <typename T>
995 std::string str(const matrix_m<T> &m)
996 {
997  std::string s = "";
998  for (int i = 0; i < m.nr(); i++)
999  {
1000  if (m.nc() > 0) s = s + std::to_string(m(i, 0));
1001  for (int j = 1; j < m.nc(); j++)
1002  s = s + " " + std::to_string(m(i, j));
1003  s = s + "\n";
1004  }
1005  return s;
1006 }
1007 
1009 template <typename T>
1010 std::string str(const matrix_m<std::complex<T>> &m)
1011 {
1012  std::string s = "";
1013  for (int i = 0; i != m.nr(); i++)
1014  {
1015  if (m.nc() > 0)
1016  s = s + "(" + std::to_string(m(i, 0).real()) + "," + std::to_string(m(i, 0).imag()) + ")";
1017  for (int j = 1; j != m.nc(); j++)
1018  s = s + " (" + std::to_string(m(i, j).real()) + "," + std::to_string(m(i, j).imag()) + ")";
1019  s = s + "\n";
1020  }
1021  return s;
1022 }
1023 
1025 template <typename T>
1026 inline matrix_m<T> random(int nr, int nc, const T &lb, const T &ub, MAJOR major = MAJOR::ROW)
1027 {
1028  matrix_m<T> m(nr, nc, major);
1029  m.randomize(lb, ub);
1030  return m;
1031 }
1032 
1034 template <typename T>
1035 inline matrix_m<T> random_sy(int n, const T &lb, const T &ub, MAJOR major = MAJOR::ROW)
1036 {
1037  matrix_m<T> m(n, n, major);
1038  m.randomize(lb, ub, true, false);
1039  return m;
1040 }
1041 
1043 template <typename T>
1044 inline matrix_m<std::complex<T>> random_he(int n, const std::complex<T> &lb, const std::complex<T> &ub, MAJOR major = MAJOR::ROW)
1045 {
1046  matrix_m<std::complex<T>> m(n, n, major);
1047  m.randomize(lb, ub, false, true);
1048  return m;
1049 }
1050 
1052 template <typename T>
1053 inline matrix_m<std::complex<T>> random_unitary(int n, MAJOR major = MAJOR::ROW)
1054 {
1055  int info;
1056  // always use column major here to ensure that the eigvec construction is correct
1057  auto mat = random_he<T>(n, {-1.0, -1.0}, {1.0, 1.0}, MAJOR::COL);
1058  const char *name = mat.is_double? "ZHETRD" : "CHETRD";
1059  // printf("%s\n", name);
1060  int nb = LapackConnector::ilaenv(1, name, "VU", n, -1, -1, -1);
1061  if (nb <= 1) nb = std::max(1, n);
1062  const int lwork = n * (nb + 1);
1063 
1064  T *w = new T[n];
1065  std::complex<T> *work = new std::complex<T>[lwork];
1066  T *rwork = new T[std::max(1, 3 * n - 2)];
1067 
1068  LapackConnector::heev_f('V', 'U', n, mat.ptr(), mat.nr(), w, work, lwork, rwork, info);
1069  delete [] rwork, w, work;
1070  return matrix_m<std::complex<T>>(n, n, mat.ptr(), MAJOR::COL, major);
1071 }
1072 
1076 template <typename T>
1077 inline matrix_m<std::complex<T>> random_he_selected_ev(int n, const vector<T> &evs, MAJOR major = MAJOR::ROW, const T& padding_zero = 1e-14)
1078 {
1079  const int nvec = evs.size();
1080  if (nvec == 0) return matrix_m<std::complex<T>>{0, 0, major};
1081 
1082  auto eigvec = random_unitary<T>(n, major);
1083  auto eigvec_H = eigvec.get_transpose(true);
1084  for (int ie = 0; ie != n; ie++)
1085  eigvec_H.scale_row(ie, ie < nvec? evs[ie]: padding_zero);
1086  return eigvec * eigvec_H;
1087 }
1088 
1090 template <typename T, typename Treal = typename to_real<T>::type>
1091 void print_matrix_mm(const matrix_m<T> &mat, ostream &os, Treal threshold = 1e-15, bool row_first = true)
1092 {
1093  const int nr = mat.nr(), nc = mat.nc();
1094  size_t nnz = 0;
1095  for (int i = 0; i != mat.size(); i++)
1096  {
1097  if (fabs(mat.dataobj[i]) > threshold)
1098  nnz++;
1099  }
1100  os << "%%MatrixMarket matrix coordinate "
1101  << (is_complex<T>()? "complex" : "real") << " general" << endl
1102  << "%" << endl;
1103  os << nr << " " << nc << " " << nnz << endl;
1104  // NOTE: better to remove duplicate code?
1105  if (row_first)
1106  {
1107  for (int i = 0; i != nr; i++)
1108  for (int j = 0; j != nc; j++)
1109  {
1110  auto &v = mat(i, j);
1111  if (fabs(v) > threshold)
1112  {
1113  os << i + 1 << " " << j + 1 << " " << showpoint << scientific << setprecision(15) << get_real(v);
1114  if (is_complex<T>())
1115  os << " " << showpoint << scientific << setprecision(15) << get_imag(v);
1116  os << endl;
1117  }
1118  }
1119  }
1120  else
1121  {
1122  for (int j = 0; j != nc; j++)
1123  for (int i = 0; i != nr; i++)
1124  {
1125  auto &v = mat(i, j);
1126  if (fabs(v) > threshold)
1127  {
1128  os << i + 1 << " " << j + 1 << " " << showpoint << scientific << setprecision(15) << get_real(v);
1129  if (is_complex<T>())
1130  os << " " << showpoint << scientific << setprecision(15) << get_imag(v);
1131  os << endl;
1132  }
1133  }
1134  }
1135 }
1136 
1137 template <typename T, typename Treal = typename to_real<T>::type>
1138 void print_whole_matrix(const char *desc, const matrix_m<T> &mat)
1139 {
1141 
1142  int nr = mat.nr();
1143  int nc = mat.nc();
1144  lib_printf("\n %s\n", desc);
1145  lib_printf("nr = %d, nc = %d\n", nr, nc);
1146  if(is_complex<T>())
1147  {
1148  for (int i = 0; i < nr; i++)
1149  {
1150  for (int j = 0; j < nc; j++)
1151  lib_printf("%10.6f,%9.6f ", mat.c[i * nc + j].real(), mat.c[i * nc + j].imag());
1152  lib_printf("\n");
1153  }
1154  }
1155  else
1156  {
1157  for (int i = 0; i < nr; i++)
1158  {
1159  for (int j = 0; j < nc; j++)
1160  lib_printf("%10.6f", mat.c[i * nc + j].real());
1161  lib_printf("\n");
1162  }
1163  }
1164 }
1165 
1166 template <typename T, typename Treal = typename to_real<T>::type>
1167 void print_matrix_mm_file(const matrix_m<T> &mat, const std::string &fn, Treal threshold = 1e-15, bool row_first = true)
1168 {
1169  ofstream fs;
1170  fs.open(fn);
1171  print_matrix_mm(mat, fs, threshold, row_first);
1172  fs.close();
1173 }
Definition: matrix_m.h:213
void randomize(const T &lb=0, const T &ub=1, bool symmetrize=false, bool hermitian=false)
Randomize the matrix elements with lower and upper bound and symmetry constraint.
Definition: matrix_m.h:346
static const bool is_complex
Flag of whether the instantialized matrix class is complex-typed.
Definition: matrix_m.h:216
void lib_printf(const char *format, Args &&... args)
printf that handles the stdout redirect
Definition: utils_io.h:13
Definition: base_utility.h:11
struct to store the shared data and dimension
Definition: matrix_m.h:88