1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by: Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
8 //                    Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
9 //                    Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
10 //                    Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
11 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
12 //
13 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
14 //////////////////////////////////////////////////////////////////////////////////////
15 
16 
17 /** @file
18  * @brief Define determinant operators
19  */
20 #ifndef OHMMS_NUMERIC_DETERMINANT_H
21 #define OHMMS_NUMERIC_DETERMINANT_H
22 
23 #include <algorithm>
24 #include "OhmmsPETE/TinyVector.h"
25 #include "OhmmsPETE/OhmmsVector.h"
26 #include "OhmmsPETE/OhmmsMatrix.h"
27 #include "CPU/BLAS.hpp"
28 #include "config/stdlib/math.hpp"
29 #include "CPU/SIMD/inner_product.hpp"
30 #include "Numerics/determinant_operators.h"
31 
32 namespace qmcplusplus
33 {
34 /** LU factorization of double */
LUFactorization(int n,int m,double * restrict a,int n0,int * restrict piv)35 inline void LUFactorization(int n, int m, double* restrict a, int n0, int* restrict piv)
36 {
37   int status;
38   dgetrf(n, m, a, n0, piv, status);
39 }
40 
41 /** LU factorization of float */
LUFactorization(int n,int m,float * restrict a,const int & n0,int * restrict piv)42 inline void LUFactorization(int n, int m, float* restrict a, const int& n0, int* restrict piv)
43 {
44   int status;
45   sgetrf(n, m, a, n0, piv, status);
46 }
47 
48 /** LU factorization of std::complex<double> */
LUFactorization(int n,int m,std::complex<double> * restrict a,int n0,int * restrict piv)49 inline void LUFactorization(int n, int m, std::complex<double>* restrict a, int n0, int* restrict piv)
50 {
51   int status;
52   zgetrf(n, m, a, n0, piv, status);
53 }
54 
55 /** LU factorization of complex<float> */
LUFactorization(int n,int m,std::complex<float> * restrict a,int n0,int * restrict piv)56 inline void LUFactorization(int n, int m, std::complex<float>* restrict a, int n0, int* restrict piv)
57 {
58   int status;
59   cgetrf(n, m, a, n0, piv, status);
60 }
61 
62 /** Inversion of a double matrix after LU factorization*/
InvertLU(int n,double * restrict a,int n0,int * restrict piv,double * restrict work,int n1)63 inline void InvertLU(int n, double* restrict a, int n0, int* restrict piv, double* restrict work, int n1)
64 {
65   int status;
66   dgetri(n, a, n0, piv, work, n1, status);
67 }
68 
69 /** Inversion of a float matrix after LU factorization*/
InvertLU(const int & n,float * restrict a,const int & n0,int * restrict piv,float * restrict work,const int & n1)70 inline void InvertLU(const int& n,
71                      float* restrict a,
72                      const int& n0,
73                      int* restrict piv,
74                      float* restrict work,
75                      const int& n1)
76 {
77   int status;
78   sgetri(n, a, n0, piv, work, n1, status);
79 }
80 
81 /** Inversion of a std::complex<double> matrix after LU factorization*/
InvertLU(int n,std::complex<double> * restrict a,int n0,int * restrict piv,std::complex<double> * restrict work,int n1)82 inline void InvertLU(int n,
83                      std::complex<double>* restrict a,
84                      int n0,
85                      int* restrict piv,
86                      std::complex<double>* restrict work,
87                      int n1)
88 {
89   int status;
90   zgetri(n, a, n0, piv, work, n1, status);
91 }
92 
93 /** Inversion of a complex<float> matrix after LU factorization*/
InvertLU(int n,std::complex<float> * restrict a,int n0,int * restrict piv,std::complex<float> * restrict work,int n1)94 inline void InvertLU(int n,
95                      std::complex<float>* restrict a,
96                      int n0,
97                      int* restrict piv,
98                      std::complex<float>* restrict work,
99                      int n1)
100 {
101   int status;
102   cgetri(n, a, n0, piv, work, n1, status);
103 }
104 
105 /** @}*/
106 
107 /** inverse a matrix
108  * @param x starting address of an n-by-m matrix
109  * @param n rows
110  * @param m cols
111  * @param work workspace array
112  * @param pivot integer pivot array
113  * @return determinant
114  */
115 template<class T>
Invert(T * restrict x,int n,int m,T * restrict work,int * restrict pivot)116 inline T Invert(T* restrict x, int n, int m, T* restrict work, int* restrict pivot)
117 {
118   T detvalue(1.0);
119   LUFactorization(n, m, x, n, pivot);
120   for (int i = 0, ip = 1; i < m; i++, ip++)
121   {
122     if (pivot[i] == ip)
123       detvalue *= x[i * m + i];
124     else
125       detvalue *= -x[i * m + i];
126   }
127   InvertLU(n, x, n, pivot, work, n);
128   return detvalue;
129 }
130 
131 /** determinant of a matrix
132  * @param x starting address of an n-by-m matrix
133  * @param n rows
134  * @param m cols
135  * @param pivot integer pivot array
136  * @return determinant
137  */
138 template<class T>
Determinant(T * restrict x,int n,int m,int * restrict pivot)139 inline T Determinant(T* restrict x, int n, int m, int* restrict pivot)
140 {
141   T detvalue(1.0);
142   LUFactorization(n, m, x, n, pivot);
143   for (int i = 0, ip = 1; i < m; i++, ip++)
144   {
145     if (pivot[i] == ip)
146       detvalue *= x[i * m + i];
147     else
148       detvalue *= -x[i * m + i];
149   }
150   return detvalue;
151 }
152 
153 /** inverse a matrix
154  * @param x starting address of an n-by-m matrix
155  * @param n rows
156  * @param m cols
157  * @return determinant
158  *
159  * Workspaces are handled internally.
160  */
161 template<class T>
Invert(T * restrict x,int n,int m)162 inline T Invert(T* restrict x, int n, int m)
163 {
164   std::vector<int> pivot(n);
165   std::vector<T> work(n);
166   return Invert(x, n, m, work.data(), pivot.data());
167 }
168 
169 template<class T, class T1>
InvertWithLog(T * restrict x,int n,int m,T * restrict work,int * restrict pivot,std::complex<T1> & logdet)170 inline void InvertWithLog(T* restrict x, int n, int m, T* restrict work, int* restrict pivot, std::complex<T1>& logdet)
171 {
172   LUFactorization(n, m, x, n, pivot);
173   logdet = std::complex<T1>();
174   for (int i = 0; i < n; i++)
175     logdet += std::log(std::complex<T1>((pivot[i] == i + 1) ? x[i * m + i] : -x[i * m + i]));
176   InvertLU(n, x, n, pivot, work, n);
177 }
178 
179 /** invert a matrix
180  * \param M a matrix to be inverted
181  * \param getdet bool, if true, calculate the determinant
182  * \return the determinant
183  */
184 template<class MatrixA>
185 inline typename MatrixA::value_type invert_matrix(MatrixA& M, bool getdet = true)
186 {
187   typedef typename MatrixA::value_type value_type;
188   const int n = M.rows();
189   std::vector<int> pivot(n);
190   std::vector<value_type> work(n);
191   LUFactorization(n, n, M.data(), n, pivot.data());
192   value_type det0 = 1.0;
193   if (getdet)
194   // calculate determinant
195   {
196     int sign = 1;
197     for (int i = 0; i < n; ++i)
198     {
199       if (pivot[i] != i + 1)
200         sign *= -1;
201       det0 *= M(i, i);
202     }
203     det0 *= static_cast<value_type>(sign);
204   }
205   InvertLU(n, M.data(), n, pivot.data(), work.data(), n);
206   return det0;
207 }
208 
209 /** determinant ratio with a row substitution
210  * @param Minv inverse matrix
211  * @param newv row vector
212  * @param rowchanged row index to be replaced
213  * @return \f$ M^{new}/M\f$
214  */
215 template<typename MatA, typename VecB>
DetRatioByRow(const MatA & Minv,const VecB & newv,int rowchanged)216 inline typename MatA::value_type DetRatioByRow(const MatA& Minv, const VecB& newv, int rowchanged)
217 {
218   return simd::dot(Minv[rowchanged], newv.data(), Minv.cols());
219   //return BLAS::dot(Minv.cols(),Minv[rowchanged],newv.data());
220 }
221 
222 /** determinant ratio with a column substitution
223  * @param Minv inverse matrix
224  * @param newv column vector
225  * @param colchanged column index to be replaced
226  * @return \f$ M^{new}/M\f$
227  */
228 template<typename MatA, typename VecB>
DetRatioByColumn(const MatA & Minv,const VecB & newv,int colchanged)229 inline typename MatA::value_type DetRatioByColumn(const MatA& Minv, const VecB& newv, int colchanged)
230 {
231   //use BLAS dot since the stride is not uniform
232   return simd::dot(Minv.cols(), Minv.data() + colchanged, Minv.cols(), newv.data(), 1);
233 }
234 
235 /** update a inverse matrix by a row substitution
236  * @param Minv in/out inverse matrix
237  * @param newrow row vector
238  * @param rvec workspace
239  * @param rvecinv workspace
240  * @param rowchanged row index to be replaced
241  * @param c_ratio determinant-ratio with the row replacement
242  */
243 template<class MatA, class VecT>
InverseUpdateByRow(MatA & Minv,VecT & newrow,VecT & rvec,VecT & rvecinv,int rowchanged,typename MatA::value_type c_ratio)244 inline void InverseUpdateByRow(MatA& Minv,
245                                VecT& newrow,
246                                VecT& rvec,
247                                VecT& rvecinv,
248                                int rowchanged,
249                                typename MatA::value_type c_ratio)
250 {
251   //using gemv+ger
252   det_row_update(Minv.data(), newrow.data(), Minv.cols(), rowchanged, c_ratio, rvec.data(), rvecinv.data());
253   //int ncols=Minv.cols();
254   //typename MatA::value_type ratio_inv=1.0/c_ratio;
255   //for(int j=0; j<ncols; j++) {
256   //  if(j == rowchanged) continue;
257   //  typename MatA::value_type temp = 0.0;
258   //  for(int k=0; k<ncols; k++) temp += newrow[k]*Minv(j,k);
259   //  temp *= -ratio_inv;
260   //  for(int k=0; k<ncols; k++) Minv(j,k) += temp*Minv(rowchanged,k);
261   //}
262   //for(int k=0; k<ncols; k++) Minv(rowchanged,k) *= ratio_inv;
263 }
264 
265 template<typename MatA, typename VecT>
InverseUpdateByColumn(MatA & Minv,VecT & newcol,VecT & rvec,VecT & rvecinv,int colchanged,typename MatA::value_type c_ratio)266 inline void InverseUpdateByColumn(MatA& Minv,
267                                   VecT& newcol,
268                                   VecT& rvec,
269                                   VecT& rvecinv,
270                                   int colchanged,
271                                   typename MatA::value_type c_ratio)
272 {
273   det_col_update(Minv.data(), newcol.data(), Minv.rows(), colchanged, c_ratio, rvec.data(), rvecinv.data());
274   //int nrows=Minv.rows();
275   //typename MatA::value_type ratio_inv=1.0/c_ratio;
276   //for(int i=0; i<nrows; i++) {
277   //  if(i == colchanged) continue;
278   //  typename MatA::value_type temp = 0.0;
279   //  for(int k=0; k<nrows; k++) temp += newcol[k]*Minv(k,i);
280   //  temp *= -ratio_inv;
281   //  for(int k=0; k<nrows; k++) Minv(k,i) += temp*Minv(k,colchanged);
282   //}
283   //for(int k=0; k<nrows; k++) Minv(k,colchanged) *= ratio_inv;
284 }
285 
286 } // namespace qmcplusplus
287 #endif
288