MPQC 3.0.0-alpha
Loading...
Searching...
No Matches
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
46namespace sc { namespace cadf {
47
48template<typename NormContainer, typename Index, typename NormValue>
49class 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
129template<typename BlockType>
130class TreeMatrix
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
230template<
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>
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
300namespace 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
312template<typename LeftTreeType, typename RightTreeType, typename LeftIndex, typename RightIndex>
313inline std::vector<std::pair<
314 typename LeftTreeType::index_t,
315 typename RightTreeType::index_t
316>>
317relevant_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 */
Definition treemat.h:235
Definition cadf_attic.h:406
Definition cadf_attic.h:330
SpinCase1 other(SpinCase1 S)
given 1-spin return the other 1-spin
Contains all MPQC code up to version 3.
Definition mpqcin.h:14
Definition treemat.h:304

Generated at Wed Sep 25 2024 02:45:30 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.12.0.