MPQC  3.0.0-alpha
treemat.h
1 //
2 // treemat.h
3 //
4 // Copyright (C) 2014 David Hollman
5 //
6 // Author: David Hollman
7 // Maintainer: DSH
8 // Created: May 1, 2014
9 //
10 // This file is part of the SC Toolkit.
11 //
12 // The SC Toolkit is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU Library General Public License as published by
14 // the Free Software Foundation; either version 2, or (at your option)
15 // any later version.
16 //
17 // The SC Toolkit is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Library General Public License for more details.
21 //
22 // You should have received a copy of the GNU Library General Public License
23 // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
24 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 //
26 // The U.S. Government is granted a limited license as per AL 91-7.
27 //
28 
29 #ifndef _chemistry_qc_scf_cadf_treemat_h
30 #define _chemistry_qc_scf_cadf_treemat_h
31 
32 #include <queue>
33 #include <type_traits>
34 #include <iterator>
35 
36 #include <boost/shared_ptr.hpp>
37 
38 #include <Eigen/Dense>
39 
40 #include <util/misc/assert.h>
41 #include <util/misc/scexception.h>
42 
43 #include "treemat_fwd.h"
44 #include "cadfclhf.h"
45 
46 namespace sc { namespace cadf {
47 
48 template<typename NormContainer, typename Index, typename NormValue>
49 class TreeBlock {
50  public:
51 
52  typedef NormContainer norm_container_t;
53  typedef Index index_t;
54  typedef NormValue norm_value_t;
55 
56  private:
57 
58  norm_container_t norms_;
59  index_t begin_index_;
60  index_t end_index_;
61  std::vector<boost::shared_ptr<TreeBlock>> children_;
62  int atom_block_index_ = -1;
63 
64  public:
65 
66  TreeBlock() = default;
67 
68  template<typename Derived>
69  TreeBlock(
70  const Eigen::MatrixBase<Derived>& norms,
71  index_t begin_idx, index_t end_idx
72  ) : norms_(norms),
73  begin_index_(begin_idx),
74  end_index_(end_idx)
75  { }
76 
77  template<typename Iterator>
78  static boost::shared_ptr<TreeBlock>
79  merge_blocks(Iterator begin, Iterator end, Index end_index, int atom_block_index = -1)
80  {
81  boost::shared_ptr<TreeBlock> rv(boost::make_shared<TreeBlock>());
82  const Index norms_size = (*begin)->norms_.size();
83  rv->norms_.resize(norms_size);
84  rv->norms_ = NormContainer::Zero(norms_size);
85  rv->begin_index_ = (*begin)->begin_index();
86  rv->end_index_ = end_index;
87  rv->atom_block_index_ = atom_block_index;
88 
89  // We can only reserve the size of the children vector ahead of time if we can get the
90  // difference between the iterators
91  if(std::is_base_of<std::random_access_iterator_tag,
92  typename std::iterator_traits<Iterator>::iterator_category>::value
93  )
94  {
95  typename std::iterator_traits<Iterator>::difference_type dist = end - begin;
96  rv->children_.reserve(dist);
97  }
98 
99  for(auto it = begin; it != end; ++it) {
100  rv->norms_.array() += (*it)->norms_.array().square();
101  rv->children_.push_back(*it);
102  }
103  rv->norms_ = rv->norms_.cwiseSqrt();
104  return rv;
105  }
106 
107  index_t begin_index() const { return begin_index_; }
108 
109  index_t end_index() const { return end_index_; }
110 
111  bool is_atom_block() const { return atom_block_index_ != -1; }
112 
113  int atom_block_index() const { return atom_block_index_; }
114 
115  template <typename NormIndex>
116  const norm_value_t
117  norm(const NormIndex idx) const
118  {
119  return norms_[idx];
120  }
121 
122  size_t n_children() const { return children_.size(); }
123  bool is_leaf() const { return n_children() == 0; }
124 
125  const std::vector<boost::shared_ptr<TreeBlock>>& children() const { return children_; }
126 
127 };
128 
129 template<typename BlockType>
131 {
132  public:
133 
134  typedef BlockType block_t;
135  typedef typename block_t::index_t index_t;
136  typedef boost::shared_ptr<block_t> block_ptr_t;
137  typedef std::deque<block_ptr_t> block_container_t;
138 
139  private:
140 
141  // Blocks ordered from root to last leaf
142  block_container_t blocks_;
143 
144  public:
145 
146  const block_container_t& blocks() const { return blocks_; }
147 
148  typename block_container_t::const_iterator
149  breadth_first_begin() const {
150  return blocks_.cbegin();
151  }
152 
153  typename block_container_t::const_iterator
154  breadth_first_end() const {
155  return blocks_.cend();
156  }
157 
158  const block_t root() const { return blocks_.front(); }
159 
160  template<typename Derived>
161  TreeMatrix(
162  const Eigen::MatrixBase<Derived>& m,
163  GaussianBasisSet* basis,
164  std::vector<int> block_requirements = { SameAngularMomentum|SameCenter, SameCenter },
165  int max_children = 3
166  )
167  {
168  // These use std::deque objects since we need the iterators, but they act like stacks
169  std::deque<block_ptr_t> curr_blocks;
170  std::deque<block_ptr_t> new_blocks;
171  for(const auto&& ish : shell_range(basis))
172  {
173  auto blk = boost::make_shared<block_t>(m.row(ish), ish.bfoff, ish.bfoff+ish.nbf);
174  new_blocks.push_front(blk);
175  }
176 
177  for(auto req : block_requirements) {
178  curr_blocks.clear();
179  while(!new_blocks.empty()) {
180  curr_blocks.push_front(new_blocks.front());
181  blocks_.push_front(new_blocks.front());
182  new_blocks.pop_front();
183  }
184 
185  auto blk_iter = curr_blocks.cbegin();
186  for(const auto&& iblk : shell_block_range(basis, 0, 0, NoLastIndex, req, NoMaximumBlockSize)) {
187  auto blk_start = blk_iter;
188  index_t end_index = (*blk_start)->end_index();
189  while(blk_iter != curr_blocks.cend()
190  and (*blk_iter)->begin_index() < iblk.bfoff + iblk.nbf
191  ) {
192  end_index = (*blk_iter)->end_index();
193  ++blk_iter;
194  }
195  new_blocks.push_front(block_t::merge_blocks(blk_start, blk_iter, end_index,
196  req==SameCenter ? iblk.center : -1
197  ));
198  }
199  }
200 
201  while(new_blocks.size() > 1) {
202 
203  curr_blocks.clear();
204  while(!new_blocks.empty()) {
205  curr_blocks.push_front(new_blocks.front());
206  blocks_.push_front(new_blocks.front());
207  new_blocks.pop_front();
208  }
209 
210  auto blk_iter = curr_blocks.cbegin();
211  while(blk_iter != curr_blocks.cend()) {
212  auto blk_start = blk_iter;
213  index_t end_index = (*blk_start)->end_index();
214  for(int ichild = 0; ichild < max_children and blk_iter != curr_blocks.end(); ++ichild) {
215  end_index = (*blk_iter)->end_index();
216  ++blk_iter;
217  }
218  new_blocks.push_front(block_t::merge_blocks(blk_start, blk_iter, end_index));
219  }
220 
221  }
222 
223  blocks_.push_front(new_blocks.front());
224 
225  }
226 
227 
228 };
229 
230 template<
231  typename Index=uli,
232  typename LeftBlockPtr=typename TreeMatrix<>::block_ptr_t,
233  typename RightBlockPtr=typename TreeMatrix<>::block_ptr_t
234 >
236 
237  private:
238 
239  double norm_;
240  Index begin_index_;
241  Index end_index_;
242  LeftBlockPtr left_block_;
243  RightBlockPtr right_block_;
244 
245  public:
246 
247  template<typename LeftIndex, typename RightIndex>
248  ProductBlock(
249  const LeftBlockPtr& left, LeftIndex left_index,
250  const RightBlockPtr& right, RightIndex right_index
251  ) : norm_(left->norm(left_index) * right->norm(right_index)),
252  begin_index_(left->begin_index()),
253  end_index_(left->end_index()),
254  left_block_(left), right_block_(right)
255  {
256  MPQC_ASSERT(left->begin_index() == right->begin_index());
257  MPQC_ASSERT(left->end_index() == right->end_index());
258  MPQC_ASSERT(left->n_children() == right->n_children());
259  }
260 
261  const Index size() const { return end_index_ - begin_index_; }
262  const Index begin_index() const { return begin_index_; }
263  const Index end_index() const { return end_index_; }
264  const double norm() const { return norm_; }
265 
266  bool operator<(const ProductBlock& other) const {
267  return norm_ / size() < other.norm_ / other.size();
268  }
269 
270  template<typename LeftIndex, typename RightIndex>
271  void
272  enqueue_children(
273  std::queue<ProductBlock>& queue,
274  const LeftIndex left_index,
275  const RightIndex right_index
276  ) const
277  {
278  auto left_iter = left_block_->children().cbegin();
279  auto right_iter = right_block_->children().cbegin();
280  for(; left_iter != left_block_->children().end(); ++left_iter, ++right_iter) {
281  queue.emplace(*left_iter, left_index, *right_iter, right_index);
282  }
283  }
284 
285  bool is_leaf() const {
286  return left_block_->is_leaf();
287  }
288 
289  bool is_atom_block() const {
290  return left_block_->is_atom_block();
291  }
292 
293  int atom_block_index() const {
294  return left_block_->atom_block_index();
295  }
296 
297 
298 };
299 
300 namespace detail {
301 
302  template<typename T>
304  {
305  bool operator()(const T& a, const T& b) const {
306  return a.begin_index() < b.begin_index();
307  }
308  };
309 
310 }
311 
312 template<typename LeftTreeType, typename RightTreeType, typename LeftIndex, typename RightIndex>
313 inline std::vector<std::pair<
314  typename LeftTreeType::index_t,
315  typename RightTreeType::index_t
316 >>
317 relevant_product_ranges(
318  const LeftTreeType& left, LeftIndex left_index,
319  const RightTreeType& right, RightIndex right_index,
320  double thresh, int exclude_center = -1,
321  // Note: Using this option is possibly more stable
322  // but could result in a *slightly* stronger dependence on
323  // ordering of atoms.
324  bool guarantee_min_error=false
325 )
326 {
327  typedef ProductBlock<
328  typename LeftTreeType::index_t,
329  typename LeftTreeType::block_ptr_t,
330  typename RightTreeType::block_ptr_t
331  > product_block_t;
332  typedef std::vector<std::pair<
333  typename LeftTreeType::index_t,
334  typename RightTreeType::index_t
335  >> return_t;
336 
337  std::priority_queue<product_block_t> discarded_blocks;
338  std::set<product_block_t, detail::begin_index_less<product_block_t>> sig_blocks;
339  std::queue<product_block_t> working_blocks;
340  double curr_error_squared = 0.0;
341 
342  auto left_iter = left.breadth_first_begin();
343  auto right_iter = right.breadth_first_begin();
344 
345  working_blocks.emplace(*left_iter, left_index, *right_iter, right_index);
346 
347  while(!working_blocks.empty()) {
348  // TODO think about how to avoid swapping left and right block lists in and out of cache (perhaps by prefetching?)
349  const auto& curr_block = working_blocks.front();
350 
351  if(exclude_center != -1 and curr_block.is_atom_block() and curr_block.atom_block_index() == exclude_center) {
352  // Discard the block; don't even put it in the discarded blocks
353  }
354  else if(curr_block.norm() < thresh) {
355  if(guarantee_min_error) {
356  discarded_blocks.push(curr_block);
357  }
358  curr_error_squared += curr_block.norm() * curr_block.norm();
359  }
360  else {
361  if(curr_block.is_leaf()) {
362  sig_blocks.insert(curr_block);
363  }
364  else {
365  curr_block.enqueue_children(working_blocks, left_index, right_index);
366  }
367  }
368  working_blocks.pop();
369  }
370 
371  if(guarantee_min_error) {
372  throw FeatureNotImplemented("guarantee_min_error", __FILE__, __LINE__);
373  }
374 
375  // Form the largest contiguous blocks that we can
376  return_t rv;
377  for(const auto& sig_block : sig_blocks) {
378  if(rv.empty() or rv.back().second != sig_block.begin_index()) {
379  rv.emplace_back(sig_block.begin_index(), sig_block.end_index());
380  }
381  else {
382  rv.back().second = sig_block.end_index();
383  }
384  }
385 
386  return rv;
387 }
388 
389 
390 }} // end namespaces
391 
392 #endif /* _chemistry_qc_scf_cadf_treemat_h */
sc::cadf::TreeBlock
Definition: treemat.h:49
shell_block_iterable
Definition: cadf_attic.h:406
sc::other
SpinCase1 other(SpinCase1 S)
given 1-spin return the other 1-spin
shell_iterable
Definition: cadf_attic.h:330
sc::GaussianBasisSet
The GaussianBasisSet class is used describe a basis set composed of atomic gaussian orbitals.
Definition: gaussbas.h:141
sc::cadf::detail::begin_index_less
Definition: treemat.h:303
sc::cadf::ProductBlock
Definition: treemat.h:235
sc
Contains all MPQC code up to version 3.
Definition: mpqcin.h:14
sc::cadf::TreeMatrix
Definition: treemat.h:130

Generated at Sun Jan 26 2020 23:23:59 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.8.16.