1 /*
2   HMat-OSS (HMatrix library, open source software)
3 
4   Copyright (C) 2014-2015 Airbus Group SAS
5 
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License
8   as published by the Free Software Foundation; either version 2
9   of the License, or (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 
20   http://github.com/jeromerobert/hmat-oss
21 */
22 
23 /*! \file
24   \ingroup HMatrix
25   \brief Spatial cluster tree for the Dofs.
26 */
27 #ifndef _ADMISSIBLITY_HPP
28 #define _ADMISSIBLITY_HPP
29 
30 #include <cstddef>
31 #include <string>
32 
33 namespace hmat {
34 
35 // Forward declarations
36 class ClusterTree;
37 class AxisAlignedBoundingBox;
38 
39 class AdmissibilityCondition
40 {
41 public:
AdmissibilityCondition()42   AdmissibilityCondition() : maxWidth_((size_t)-1L) {}
43   /*! \brief Virtual copy constructor */
44   virtual AdmissibilityCondition * clone() const = 0;
45 
~AdmissibilityCondition()46   virtual ~AdmissibilityCondition() {}
47 
48   /*! \brief Precompute ClusterTree::cache_ */
prepare(const ClusterTree & rows,const ClusterTree & cols) const49   virtual void prepare(const ClusterTree& rows, const ClusterTree& cols) const {}
50   /*! \brief Clean up data which may be allocated by prepare  */
clean(const ClusterTree & rows,const ClusterTree & cols) const51   virtual void clean(const ClusterTree& rows, const ClusterTree& cols) const {}
52 
53   /*! \brief Returns true if the block of interaction between 2 nodes has a
54       low-rank representation.
55 
56     \return true  if the block should be Rk.
57    */
58   virtual bool isLowRank(const ClusterTree& rows, const ClusterTree& cols) const = 0;
59   /*! \brief Returns a boolean telling if the block of interaction between 2 nodes
60       is too small to recurse.
61       Note: stopRecursion and forceRecursion must not both return true.
62 
63     \return true  if the block is too small to recurse
64    */
stopRecursion(const ClusterTree & rows,const ClusterTree & cols) const65   virtual bool stopRecursion(const ClusterTree& rows, const ClusterTree& cols) const {
66       (void)rows, (void)cols; // unused
67       return false;
68   }
69   /*! \brief Returns a boolean telling if the block of interaction between 2 nodes
70       is too large to perform compression, if it is low rank; in this case, recursion
71       is performed.
72       Note: stopRecursion and forceRecursion must not both return true.
73       \param elemSize the size of the element of the matrix (i.e. sizeof(T))
74     \return true  if the block is too large to perform compression
75    */
76   virtual bool forceRecursion(const ClusterTree& rows, const ClusterTree& cols, size_t elemSize) const;
77   /*! \brief Returns a boolean telling if the block of interaction between 2 nodes
78       must not be compressed, even if admissible.
79       Note: forceFull and forceRk must not both return true.
80 
81     \return true  if the admissible block must not be compressed
82    */
forceFull(const ClusterTree & rows,const ClusterTree & cols) const83   virtual bool forceFull(const ClusterTree& rows, const ClusterTree& cols) const {
84       (void)rows, (void)cols; // unused
85       return false;
86   }
87   /*! \brief Returns a boolean telling if the block of interaction between 2 nodes
88       must be compressed, even if not admissible.
89       Note: forceFull and forceRk must not both return true.
90 
91     \return true  if the block must be compressed
92    */
forceRk(const ClusterTree & rows,const ClusterTree & cols) const93   virtual bool forceRk(const ClusterTree& rows, const ClusterTree& cols) const {
94       (void)rows, (void)cols; // unused
95       return false;
96   }
97   /*! \brief Tell whether a block must be splitted along rows, cols or both.
98       Note: This method must not return {false, false}
99     \return a pair of boolean.
100 
101    */
102   virtual std::pair<bool, bool> splitRowsCols(const ClusterTree& rows, const ClusterTree& cols) const;
103 
104   /**
105    * Return true if the block is always null,
106    * (i.e. we know that is will not even be filled during the factorization).
107    * @param rows the rows cluster tree
108    * @param cols the cols cluster tree
109    */
isInert(const ClusterTree & rows,const ClusterTree & cols) const110   virtual bool isInert(const ClusterTree& rows, const ClusterTree& cols) const {
111       (void)rows, (void)cols; // unused
112       return false;
113   }
114 
115   /*! \brief Get approximate rank of a block cluster */
getApproximateRank(const ClusterTree & rows,const ClusterTree & cols) const116   virtual int getApproximateRank(const ClusterTree& rows, const ClusterTree& cols) const {
117       (void)rows, (void)cols; // unused
118       return 25;
119   }
120 
121   /**
122    * @brief Get axis aligned bounding box of a cluster tree
123    * @param current cluster tree
124    * @param is_rows current is a rows (resp. cols) cluster when is_rows
125         is true (resp. false)
126    */
127   virtual const AxisAlignedBoundingBox* getAxisAlignedBoundingBox(const ClusterTree& current, bool is_rows) const;
128 
129   virtual std::string str() const = 0;
130 
131   /**
132    * @brief Set ratio to cut tall and skinny matrices
133    * @param ratio  allows to cut tall and skinny matrices along only one direction:
134       if size(rows) < ratio*size(cols), rows is not subdivided.
135       if size(cols) < ratio*size(rows), cols is not subdivided.
136  */
setRatio(double ratio)137   void setRatio(double ratio) { ratio_ = ratio; }
138 
139   /**
140    * @brief Set maximum width (default is unlimited)
141    * @param maxWidth  force recursion if size(rows) or size(cols) is larger than this threshold.
142  */
setMaxWidth(size_t maxWidth)143   void setMaxWidth(size_t maxWidth) { maxWidth_ = maxWidth; }
144 
145 protected:
146   double ratio_;
147   size_t maxWidth_;
148 };
149 
150 /**
151  * @brief Combine Hackbusch admissibility with block size
152  * @param eta    a parameter used in the evaluation of the admissibility.
153  * @param ratio  allows to cut tall and skinny matrices along only one direction:
154       if size(rows) < ratio*size(cols), rows is not subdivided.
155       if size(cols) < ratio*size(rows), cols is not subdivided.
156  */
157 class StandardAdmissibilityCondition : public AdmissibilityCondition
158 {
159 public:
160   StandardAdmissibilityCondition(double eta, double ratio = 0);
clone() const161   StandardAdmissibilityCondition * clone() const { return new StandardAdmissibilityCondition(*this); }
162   // Precompute axis aligned bounding blocks
163   void prepare(const ClusterTree& rows, const ClusterTree& cols) const;
164   void clean(const ClusterTree& rows, const ClusterTree& cols) const;
165   // Returns true if block is admissible (Hackbusch condition)
166   bool isLowRank(const ClusterTree& rows, const ClusterTree& cols) const;
167   // Returns true when there is less than 2 rows or cols
168   bool stopRecursion(const ClusterTree& rows, const ClusterTree& cols) const;
169   // Returns true when there is less than 2 rows or cols
170   bool forceFull(const ClusterTree& rows, const ClusterTree& cols) const;
171   std::string str() const;
172   void setEta(double eta);
173   double getEta() const;
174 protected:
175   double eta_;
176 };
177 
178 class AlwaysAdmissibilityCondition : public AdmissibilityCondition {
179 public:
180     struct BlockSizeDetector {
181       virtual void compute(size_t & max_block_size, unsigned int & min_nr_block,
182                            bool never)=0;
~BlockSizeDetectorhmat::AlwaysAdmissibilityCondition::BlockSizeDetector183       virtual ~BlockSizeDetector(){};
184     };
185 private:
186     size_t max_block_size_;
187     unsigned int min_nr_block_;
188     std::pair<bool, bool> split_rows_cols_;
189     mutable size_t max_block_size_impl_;
190     bool never_;
191     static BlockSizeDetector * blockSizeDetector_;
192 public:
193     /**
194      * @brief AlwaysAdmissibilityCondition
195      * Create an admissibility condiction which set all blocks as admissible
196      * @param max_block_size The maximum acceptable block size in number of values (rows * cols)
197      * @param min_nr_block The minimum acceptable number of blocks created with this condition
198      * @param split_rows Tel whether or not to split rows (see splitRowsCols)
199      * @param split_cols Tel whether or not to split cols (see splitRowsCols)
200      */
201     AlwaysAdmissibilityCondition(size_t max_block_size, unsigned int min_nr_block,
202                                  bool split_rows = true, bool split_cols = false);
clone() const203     AlwaysAdmissibilityCondition * clone() const { return new AlwaysAdmissibilityCondition(*this); }
204     std::string str() const;
205     bool isLowRank(const ClusterTree&, const ClusterTree&) const;
206     std::pair<bool, bool> splitRowsCols(const ClusterTree& rows, const ClusterTree&) const;
207     bool forceRecursion(const ClusterTree& rows, const ClusterTree& cols, size_t elemSize) const;
208     bool stopRecursion(const ClusterTree& rows, const ClusterTree& cols) const;
209     bool forceFull(const ClusterTree& rows, const ClusterTree& cols) const;
210     /** @Brief Let this admissibility condition always create full blocks */
211     void never(bool n);
setBlockSizeDetector(BlockSizeDetector * b)212     static void setBlockSizeDetector(BlockSizeDetector * b) { blockSizeDetector_ = b; }
213 };
214 
215 /**
216  * @brief Class which can be used as a base class to override only some methods.
217  * @param admissibility All methods which are not redefined are delegated to this instance.
218  */
219 class ProxyAdmissibilityCondition : public AdmissibilityCondition
220 {
221 public:
ProxyAdmissibilityCondition(AdmissibilityCondition * admissibility)222   explicit ProxyAdmissibilityCondition(AdmissibilityCondition * admissibility) : proxy_(admissibility ? admissibility->clone() : NULL) {}
clone() const223   ProxyAdmissibilityCondition * clone() const { return new ProxyAdmissibilityCondition(*this); }
~ProxyAdmissibilityCondition()224   ~ProxyAdmissibilityCondition() { delete proxy_; }
getProxy() const225   AdmissibilityCondition * getProxy() const { return proxy_; }
setProxy(AdmissibilityCondition * admissibility)226   void setProxy(AdmissibilityCondition * admissibility) { proxy_ = admissibility; }
227 
prepare(const ClusterTree & rows,const ClusterTree & cols) const228   void prepare(const ClusterTree& rows, const ClusterTree& cols) const {
229     proxy_->prepare(rows, cols);
230   }
clean(const ClusterTree & rows,const ClusterTree & cols) const231   void clean(const ClusterTree& rows, const ClusterTree& cols) const {
232     return proxy_->clean(rows, cols);
233   }
isLowRank(const ClusterTree & rows,const ClusterTree & cols) const234   bool isLowRank(const ClusterTree& rows, const ClusterTree& cols) const {
235     return proxy_->isLowRank(rows, cols);
236   }
stopRecursion(const ClusterTree & rows,const ClusterTree & cols) const237   bool stopRecursion(const ClusterTree& rows, const ClusterTree& cols) const {
238     return proxy_->stopRecursion(rows, cols);
239   }
forceRecursion(const ClusterTree & rows,const ClusterTree & cols,size_t elemSize) const240   bool forceRecursion(const ClusterTree& rows, const ClusterTree& cols, size_t elemSize) const {
241     return proxy_->forceRecursion(rows, cols, elemSize);
242   }
forceFull(const ClusterTree & rows,const ClusterTree & cols) const243   bool forceFull(const ClusterTree& rows, const ClusterTree& cols) const {
244     return proxy_->forceFull(rows, cols);
245   }
forceRk(const ClusterTree & rows,const ClusterTree & cols) const246   bool forceRk(const ClusterTree& rows, const ClusterTree& cols) const {
247     return proxy_->forceRk(rows, cols);
248   }
splitRowsCols(const ClusterTree & rows,const ClusterTree & cols) const249   std::pair<bool, bool> splitRowsCols(const ClusterTree& rows, const ClusterTree& cols) const {
250     return proxy_->splitRowsCols(rows, cols);
251   }
isInert(const ClusterTree & rows,const ClusterTree & cols) const252   bool isInert(const ClusterTree& rows, const ClusterTree& cols) const {
253     return proxy_->isInert(rows, cols);
254   }
getApproximateRank(const ClusterTree & rows,const ClusterTree & cols) const255   int getApproximateRank(const ClusterTree& rows, const ClusterTree& cols) const {
256     return proxy_->getApproximateRank(rows, cols);
257   }
getAxisAlignedBoundingBox(const ClusterTree & current,bool is_rows) const258   const AxisAlignedBoundingBox* getAxisAlignedBoundingBox(const ClusterTree& current, bool is_rows) const {
259     return proxy_->getAxisAlignedBoundingBox(current, is_rows);
260   }
str() const261   std::string str() const {
262     return proxy_->str();
263   }
264 
265 private:
266   AdmissibilityCondition * proxy_;
267 };
268 
269 } //  end namespace hmat
270 #endif
271