1 /////////////////////////////////////////////////////////////////////////////////////////////////
2 /// \file  /utils/lmyengine/block_mat.cpp
3 ///
4 /// \brief  Implementation file for block recursive matrix class
5 ///
6 /////////////////////////////////////////////////////////////////////////////////////////////////
7 
8 #include <vector>
9 #include <list>
10 #include <string>
11 #include <numeric>
12 #include <cassert>
13 #include <algorithm>
14 #include <cmath>
15 #include <sstream>
16 #include "formic/utils/openmp.h"
17 
18 #include <boost/format.hpp>
19 #include <boost/shared_ptr.hpp>
20 
21 #include "formic/utils/matrix.h"
22 #include "formic/utils/mpi_interface.h"
23 #include "block_mat.h"
24 #include "formic/utils/lmyengine/block_detail.h"
25 
26 //////////////////////////////////////////////////////////////////////////////////////////////////
27 /// \brief  Function that contracts each block's previous update components with derivative ratios
28 ///
29 /// \param[in]   dr        derivative ratio(bare or energy) for this sample
30 /// \param[in]   ou_mat    matrix holds old updates, size num_old_update * num_variables
31 /// \param[out]  cont_vec  contracted results
32 ///
33 //////////////////////////////////////////////////////////////////////////////////////////////////
prep_block_ou_contractions(const std::vector<double> & dr,const formic::Matrix<double> & ou_mat,std::vector<formic::ColVec<double>> & cont_vec)34 void cqmc::engine::LMBlockerMatData::prep_block_ou_contractions(const std::vector<double> & dr,
35                                                                 const formic::Matrix<double> & ou_mat,
36                                                                 std::vector<formic::ColVec<double> > & cont_vec) {
37 
38   // loop over blocks
39   for (int b = 0; b < this->nb(); b++) {
40 
41     const int ibeg = 1 + m_block_beg.at(b);
42     const int len = m_block_len.at(b);
43 
44     formic::ColVec<double> & cont = cont_vec.at(b);
45     for (int i = 0; i < cont.size(); i++)
46       cont.at(i) = 0.0;
47 
48     for (int k = 0; k < m_nou; k++) {
49       for (int j = ibeg; j < ibeg+len; j++) {
50         cont.at(k) += dr.at(j) * ou_mat.at(j-1,k);
51       }
52     }
53   }
54 }
55 
56 ////////////////////////////////////////////////////////////////////////////////////////////////
57 /// \brief  Function that create a matrix in the basis of a block of variables and older updates
58 ///         from a different block
59 ///
60 /// \param[in]   b     block index
61 /// \param[in]   x     block index
62 /// \param[out]  mat   output matrix
63 ///
64 ////////////////////////////////////////////////////////////////////////////////////////////////
prep_lm_block_plus_other_ou_matrix(const int b,const int x,formic::Matrix<double> & mat)65 void cqmc::engine::LMBlockerMatData::prep_lm_block_plus_other_ou_matrix(const int b, const int x, formic::Matrix<double> & mat) {
66 
67   // first check that if x and b is the same block
68   if ( x == b )
69     throw formic::Exception("b and x must be different in prep_lm_block_plus_other_ou_matrix");
70 
71   // get the length of this block
72   const int len = this->bl(b);
73 
74   // get the dimension of this matrix
75   const int dim = 1 + len + m_nou;
76 
77   const int y = x - ( x > b ? 1 : 0 );
78 
79   // size the output matrix correctly
80   mat.reset(dim, dim);
81 
82   // <wfn|wfn> element
83   mat.at(0,0) = m_ww[0];
84   //std::cout << "in prep_lm_block_plus_other_ou_matrix mat(0,0) is " << m_ww << std::endl;
85 
86   // <wfn|var>
87   for (int i = 0; i < len; i++)
88     mat.at(0, 1+i) = m_wv[0].at(b).at(i);
89 
90   // <var|wfn>
91   for (int i = 0; i < len; i++)
92     mat.at(1+i, 0) = m_vw[0].at(b).at(i);
93 
94   // <var|var>
95   for (int i = 0; i < len; i++) {
96     for (int j = 0; j < len; j++) {
97       mat.at(1+i,1+j) = m_vv[0].at(b).at(i,j);
98     }
99   }
100 
101   // <wfn|old_update>
102   for (int k = 0; k < m_nou; k++)
103     mat.at(0, 1+len+k) = m_wo[0].at(x).at(k);
104 
105   // <old_update_wfn>
106   for (int k = 0; k < m_nou; k++)
107     mat.at(1+len+k, 0) = m_ow[0].at(x).at(k);
108 
109   // <var|old_update>
110   for (int i = 0; i < len; i++) {
111     for (int k = 0; k < m_nou; k++) {
112       mat.at(1+i, 1+len+k) = m_vo[0].at(b).at(i, y*m_nou+k);
113     }
114   }
115 
116   // <old_update|var>
117   for (int k = 0; k < m_nou; k++) {
118     for (int i = 0; i < len; i++) {
119       mat.at(1+len+k, 1+i) = m_ov[0].at(b).at(y*m_nou+k, i);
120     }
121   }
122 
123   // <old_update|old_update>
124   for (int k = 0; k < m_nou; k++) {
125     for (int l = 0; l < m_nou; l++) {
126       mat.at(1+len+k, 1+len+l) = m_oo[0].at(x).at(k,l);
127     }
128   }
129 }
130 
131 
132 ////////////////////////////////////////////////////////////////////////////////////////////////
133 /// \brief  Function that accumulates contribution to block matrices for this sample
134 ///
135 /// \param[in]   d        weight for this sample
136 /// \param[in]   lr       left vector for outer product
137 /// \param[in]   rr       right vector for outer product
138 /// \param[in]   ou_mat   matrix storing the old update coefficients
139 ///
140 ////////////////////////////////////////////////////////////////////////////////////////////////
acc(const double d,const std::vector<double> & lr,const std::vector<double> & rr,const formic::Matrix<double> & ou_mat)141 void cqmc::engine::LMBlockerMatData::acc(const double d, const std::vector<double> & lr, const std::vector<double> &rr, const formic::Matrix<double> & ou_mat) {
142 
143   // get the thread number
144   int myThread = omp_get_thread_num();
145 
146   // check to see if the input vector is of the correct size
147   if ( lr.size() != 1 + this->tot_rows(m_vv[myThread]) )
148     throw formic::Exception("lr vector length was %i but should be %i LMBlockerMatData::acc") % lr.size() % ( 1 + this->tot_rows(m_vv[myThread]) );
149   if ( rr.size() != 1 + this->tot_rows(m_vv[myThread]) )
150     throw formic::Exception("rr vector length was %i but should be %i LMBlockerMatData::acc") % rr.size() % ( 1 + this->tot_rows(m_vv[myThread]) );
151 
152   // prep intermediates for old updates
153   this->prep_block_ou_contractions(lr, ou_mat, m_boulr[myThread]);
154   this->prep_block_ou_contractions(rr, ou_mat, m_bourr[myThread]);
155 
156   // <wfn|wfn>
157   m_ww[myThread] += d * lr.at(0) * rr.at(0);
158 
159   // loop over blocks
160   for (int b = 0; b < this->nb(); b++) {
161 
162     // beginning index
163     const int ibeg = 1 + m_block_beg.at(b);
164 
165     // block length
166     const int len = m_block_len.at(b);
167 
168     for (int i = 0; i < len; i++) {
169 
170       // <wfn|var>
171       m_wv[myThread].at(b).at(i) += d * lr.at(0) * rr.at(ibeg + i);
172 
173       // <var|wfn>
174       m_vw[myThread].at(b).at(i) += d * lr.at(ibeg + i) * rr.at(0);
175 
176     }
177 
178     for (int k = 0; k < m_nou; k++) {
179 
180       // <wfn|old_updates>
181       m_wo[myThread].at(b).at(k) += d * lr.at(0) * m_bourr[myThread].at(b).at(k);
182 
183       // <old_updates|wfn>
184       m_ow[myThread].at(b).at(k) += d * m_boulr[myThread].at(b).at(k) * rr.at(0);
185     }
186 
187     // <var|var>
188     { formic::Matrix<double> & vv_mat = m_vv[myThread].at(b);
189       for (int i = 0; i < len; i++) {
190         for (int j = 0; j < len; j++) {
191           vv_mat.at(i,j) += d * lr.at(ibeg+i) * rr.at(ibeg+j);
192         }
193       }
194     }
195 
196     // <var|old_update> note this is for var in the current block and old-update in all other blocks
197     { formic::Matrix<double> & vo_mat = m_vo[myThread].at(b);
198       for (int x = 0, y = 0; x < this->nb(); x++) {
199         if ( x == b )
200           continue;
201         const formic::ColVec<double> & rr_vec = m_bourr[myThread].at(x);
202         for (int o = 0; o < m_nou; o++) {
203           for (int i = 0; i < len; i++) {
204             vo_mat.at(i,y*m_nou+o) += d * lr.at(ibeg+i) * rr_vec.at(o);
205            }
206          }
207          y++;
208        }
209      }
210 
211      // <old_update|var> note this is for var in the current block and old-update in all other blocks
212      { formic::Matrix<double> & ov_mat = m_ov[myThread].at(b);
213        for (int i = 0; i < len; i++) {
214          for (int x = 0, y = 0; x < this->nb(); x++) {
215            if ( x == b )
216              continue;
217            const formic::ColVec<double> & lr_vec = m_boulr[myThread].at(x);
218            for (int k = 0; k < m_nou; k++) {
219              ov_mat.at(m_nou*y+k, i) += d * lr_vec.at(k) * rr.at(ibeg+i);
220            }
221            y++;
222          }
223        }
224      }
225 
226      // <old_update|old_update>
227      { formic::Matrix<double> & oo_mat = m_oo[myThread].at(b);
228        for (int k = 0; k < m_nou; k++) {
229          for (int l = 0; l < m_nou; l++) {
230            oo_mat.at(k,l) += d * m_boulr[myThread].at(b).at(k) * m_bourr[myThread].at(b).at(l);
231          }
232        }
233      }
234    }
235 }
236 
237 ////////////////////////////////////////////////////////////////////////////////////////////////
238 /// \brief  Function that finalizes the accumulation by dividing matrices total weight
239 ///
240 ////////////////////////////////////////////////////////////////////////////////////////////////
finalize(const double total_weight)241 void cqmc::engine::LMBlockerMatData::finalize(const double total_weight) {
242 
243   // get the number of threads
244   int NumThreads = omp_get_max_threads();
245 
246   // sum over threads
247   for (int ip = 1; ip < NumThreads; ip++) {
248     m_ww[0] += m_ww[ip];
249     for (int b = 0; b < this->nb(); b++) {
250       m_wv[0].at(b) += m_wv[ip].at(b);
251       m_vw[0].at(b) += m_vw[ip].at(b);
252       m_wo[0].at(b) += m_wo[ip].at(b);
253       m_ow[0].at(b) += m_ow[ip].at(b);
254       m_ov[0].at(b) += m_ov[ip].at(b);
255       m_vo[0].at(b) += m_vo[ip].at(b);
256       m_oo[0].at(b) += m_oo[ip].at(b);
257     }
258   }
259 
260   // <wfn|wfn>
261   m_ww[0] /= total_weight;
262 
263   // loop over blocks
264   for (int b = 0; b < this->nb(); b++) {
265     m_wv[0].at(b) /= total_weight;
266     m_vw[0].at(b) /= total_weight;
267     m_vv[0].at(b) /= total_weight;
268     m_wo[0].at(b) /= total_weight;
269     m_ow[0].at(b) /= total_weight;
270     m_ov[0].at(b) /= total_weight;
271     m_vo[0].at(b) /= total_weight;
272     m_oo[0].at(b) /= total_weight;
273   }
274 }
275 
276 ////////////////////////////////////////////////////////////////////////////////////////////////
277 /// \brief  Function that finalizes the accumulation by dividing matrices total weight
278 ///         across the whole processes
279 ///
280 ////////////////////////////////////////////////////////////////////////////////////////////////
mpi_finalize(const double total_weight)281 void cqmc::engine::LMBlockerMatData::mpi_finalize(const double total_weight) {
282 
283   // get rank number and number of ranks
284   int my_rank = formic::mpi::rank();
285 
286   // get the number of threads
287   int NumThreads = omp_get_max_threads();
288 
289   // loop over blocks
290   //for (int b = 0; b < this->nb(); b++) {
291   //  // print out to debug
292   //  for (int i = 0; i < m_ow.at(b).size(); i++) {
293   //    std::cout << m_ow.at(b).at(i) << "   ";
294   //  }
295   //  std::cout << std::endl;
296   //}
297   //std::cout << std::endl;
298 
299   // sum over threads
300   for (int ip = 1; ip < NumThreads; ip++) {
301     m_ww[0] += m_ww[ip];
302     for (int b = 0; b < this->nb(); b++) {
303       m_wv[0].at(b) += m_wv[ip].at(b);
304       m_vw[0].at(b) += m_vw[ip].at(b);
305       m_vv[0].at(b) += m_vv[ip].at(b);
306       m_wo[0].at(b) += m_wo[ip].at(b);
307       m_ow[0].at(b) += m_ow[ip].at(b);
308       m_ov[0].at(b) += m_ov[ip].at(b);
309       m_vo[0].at(b) += m_vo[ip].at(b);
310       m_oo[0].at(b) += m_oo[ip].at(b);
311     }
312   }
313 
314   // <wfn|wfn>
315   double m_ww_tot = 0.0;
316   formic::mpi::reduce(&m_ww[0], &m_ww_tot, 1, MPI_SUM);
317   //MPI_Reduce(&m_ww, &m_ww_tot, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
318   m_ww[0] = m_ww_tot / total_weight;
319 
320   // get space for MPI reduce
321   std::vector<formic::ColVec<double> > m_wv_tot, m_vw_tot, m_wo_tot, m_ow_tot;
322   std::vector<formic::Matrix<double> > m_vv_tot, m_vo_tot, m_ov_tot, m_oo_tot;
323   for (int i = 0; i < this->nb(); i++) {
324     m_wv_tot.push_back(formic::ColVec<double>(this->bl(i), 0.0));
325     m_vw_tot.push_back(formic::ColVec<double>(this->bl(i), 0.0));
326     m_vv_tot.push_back(formic::Matrix<double>(this->bl(i), this->bl(i), 0.0));
327     m_wo_tot.push_back(formic::ColVec<double>(m_nou, 0.0));
328     m_ow_tot.push_back(formic::ColVec<double>(m_nou, 0.0));
329     m_ov_tot.push_back(formic::Matrix<double>((this->nb()-1)*m_nou, this->bl(i), 0.0));
330     m_vo_tot.push_back(formic::Matrix<double>(this->bl(i), (this->nb()-1)*m_nou, 0.0));
331     m_oo_tot.push_back(formic::Matrix<double>(m_nou, m_nou, 0.0));
332   }
333 
334   // do MPI reduce
335   for (int i = 0; i < this->nb(); i++) {
336     formic::mpi::reduce(&m_wv[0].at(i).at(0), &m_wv_tot.at(i).at(0), this->bl(i), MPI_SUM);
337     formic::mpi::reduce(&m_vw[0].at(i).at(0), &m_vw_tot.at(i).at(0), this->bl(i), MPI_SUM);
338     formic::mpi::reduce(&m_vv[0].at(i).at(0,0), &m_vv_tot.at(i).at(0,0), m_vv[0].at(i).size(), MPI_SUM);
339     formic::mpi::reduce(&m_wo[0].at(i).at(0), &m_wo_tot.at(i).at(0), m_nou, MPI_SUM);
340     formic::mpi::reduce(&m_ow[0].at(i).at(0), &m_ow_tot.at(i).at(0), m_nou, MPI_SUM);
341     formic::mpi::reduce(&m_ov[0].at(i).at(0,0), &m_ov_tot.at(i).at(0,0), m_ov[0].at(i).size(), MPI_SUM);
342     formic::mpi::reduce(&m_vo[0].at(i).at(0,0), &m_vo_tot.at(i).at(0,0), m_vo[0].at(i).size(), MPI_SUM);
343     formic::mpi::reduce(&m_oo[0].at(i).at(0,0), &m_oo_tot.at(i).at(0,0), m_oo[0].at(i).size(), MPI_SUM);
344   }
345 
346   // evaluate the average across all processors
347   if ( my_rank == 0 ) {
348 
349     // loop over blocks
350     for (int b = 0; b < this->nb(); b++) {
351       m_vw[0].at(b) = m_vw_tot.at(b) / total_weight;
352       m_wv[0].at(b) = m_wv_tot.at(b) / total_weight;
353       m_vv[0].at(b) = m_vv_tot.at(b) / total_weight;
354       m_ow[0].at(b) = m_ow_tot.at(b) / total_weight;
355       m_wo[0].at(b) = m_wo_tot.at(b) / total_weight;
356       m_vo[0].at(b) = m_vo_tot.at(b) / total_weight;
357       m_ov[0].at(b) = m_ov_tot.at(b) / total_weight;
358       m_oo[0].at(b) = m_oo_tot.at(b) / total_weight;
359       // print out to debug
360       //for (int i = 0; i < m_ow.at(b).size(); i++) {
361       //  std::cout << m_ow.at(b).at(i) << "   ";
362       //}
363       //std::cout << std::endl;
364     }
365   }
366 }
367 
368 /////////////////////////////////////////////////////////////////////////////////////////////////
369 /// \brief  Function that resets the matrices
370 ///
371 /////////////////////////////////////////////////////////////////////////////////////////////////
reset(const int nv,const int nblock,const int nou)372 void cqmc::engine::LMBlockerMatData::reset(const int nv, const int nblock, const int nou) {
373 
374   m_nou = nou;
375 
376   // get block information
377   cqmc::engine::brlm_get_block_info(nv, nblock, m_block_beg, m_block_end, m_block_len);
378 
379   // get the maximum number of threads
380   int NumThreads = omp_get_max_threads();
381 
382   m_ww.assign(NumThreads, 0.0);
383 
384   m_wv.clear();
385   m_wv.resize(NumThreads);
386   for (int ip = 0; ip < NumThreads; ip++) {
387     for (int i = 0; i < this->nb(); i++) {
388       m_wv[ip].push_back(formic::ColVec<double>(this->bl(i), 0.0));
389     }
390   }
391 
392   m_vw.clear();
393   m_vw.resize(NumThreads);
394   for (int ip = 0; ip < NumThreads; ip++) {
395     for (int i = 0; i < this->nb(); i++) {
396       m_vw[ip].push_back(formic::ColVec<double>(this->bl(i), 0.0));
397     }
398   }
399 
400   m_vv.clear();
401   m_vv.resize(NumThreads);
402   for (int ip = 0; ip < NumThreads; ip++) {
403     for (int i = 0; i < this->nb(); i++) {
404       m_vv[ip].push_back(formic::Matrix<double>(this->bl(i), this->bl(i), 0.0));
405     }
406   }
407 
408   m_wo.clear();
409   m_wo.resize(NumThreads);
410   for (int ip = 0; ip < NumThreads; ip++) {
411     for (int i = 0; i < this->nb(); i++) {
412       m_wo[ip].push_back(formic::ColVec<double>(m_nou, 0.0));
413     }
414   }
415 
416   m_ow.clear();
417   m_ow.resize(NumThreads);
418   for (int ip = 0; ip < NumThreads; ip++) {
419     for (int i = 0; i < this->nb(); i++) {
420       m_ow[ip].push_back(formic::ColVec<double>(m_nou, 0.0));
421     }
422   }
423 
424   m_vo.clear();
425   m_vo.resize(NumThreads);
426   for (int ip = 0; ip < NumThreads; ip++) {
427     for (int i = 0; i < this->nb(); i++) {
428       m_vo[ip].push_back(formic::Matrix<double>(this->bl(i), (this->nb()-1)*m_nou, 0.0));
429     }
430   }
431 
432   m_ov.clear();
433   m_ov.resize(NumThreads);
434   for (int ip = 0; ip < NumThreads; ip++) {
435     for (int i = 0; i < this->nb(); i++) {
436       m_ov[ip].push_back(formic::Matrix<double>((this->nb()-1)*m_nou, this->bl(i), 0.0));
437     }
438   }
439 
440   m_oo.clear();
441   m_oo.resize(NumThreads);
442   for (int ip = 0; ip < NumThreads; ip++) {
443     for (int i = 0; i < this->nb(); i++) {
444       m_oo[ip].push_back(formic::Matrix<double>(m_nou, m_nou, 0.0));
445     }
446   }
447 
448   m_boulr.clear();
449   m_bourr.clear();
450   m_boulr.resize(NumThreads);
451   m_bourr.resize(NumThreads);
452   for (int ip = 0; ip < NumThreads; ip++) {
453     for (int i = 0; i < this->nb(); i++) {
454       m_boulr[ip].push_back(formic::ColVec<double>(m_nou, 0.0));
455       m_bourr[ip].push_back(formic::ColVec<double>(m_nou, 0.0));
456     }
457   }
458 }
459 
460