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