From 137ab5323ed4fdf28ff07dbccd52e9d27372442e Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Thu, 27 Oct 2011 15:25:33 -0400 Subject: CarpetLib: Improve bboxlib algorithms Modify the algorithm used to insert a new bbox into an existing bboxset. This reduces the computational complexity of this operation from O(n^2) to O(n), where n is the number of elements in the bboxset. This has the potential to speed up regridding significantly. --- Carpet/CarpetLib/src/bboxset.cc | 343 +++++++++++++++++++++++++++------------- Carpet/CarpetLib/src/bboxset.hh | 80 +++++++--- 2 files changed, 291 insertions(+), 132 deletions(-) (limited to 'Carpet/CarpetLib/src') diff --git a/Carpet/CarpetLib/src/bboxset.cc b/Carpet/CarpetLib/src/bboxset.cc index 8a22960b8..b2ab93d5e 100644 --- a/Carpet/CarpetLib/src/bboxset.cc +++ b/Carpet/CarpetLib/src/bboxset.cc @@ -13,26 +13,40 @@ using namespace std; +#define SKIP_NORMALIZE(s) skip_normalize_t skip_normalize_v(s) +// #define SKIP_NORMALIZE(s) do { } while(0) + + + // Constructors template -bboxset::bboxset () { +bboxset::bboxset () + : skip_normalize(false) +{ assert (invariant()); } template -bboxset::bboxset (const box& b) { - //S if (!b.empty()) bs.insert(b); - if (!b.empty()) bs.push_back(b); +bboxset::bboxset (const box& b) + : skip_normalize(false) +{ + //S if (not b.empty()) bs.insert(b); + if (not b.empty()) bs.push_back(b); assert (invariant()); } template -bboxset::bboxset (const bboxset& s): bs(s.bs) { +bboxset::bboxset (const bboxset& s) + : bs(s.bs), skip_normalize(false) +{ assert (invariant()); } template -bboxset::bboxset (const list& lb) { +bboxset::bboxset (const list& lb) + : skip_normalize(false) +{ + SKIP_NORMALIZE(*this); for (typename list::const_iterator li = lb.begin(); li != lb.end(); ++ li) { @@ -41,7 +55,22 @@ bboxset::bboxset (const list& lb) { } template -bboxset::bboxset (const vector >& vlb) { +bboxset::bboxset (const vector& lb) + : skip_normalize(false) +{ + SKIP_NORMALIZE(*this); + for (typename vector::const_iterator + vi = lb.begin(); vi != lb.end(); ++ vi) + { + *this |= *vi; + } +} + +template +bboxset::bboxset (const vector >& vlb) + : skip_normalize(false) +{ + SKIP_NORMALIZE(*this); for (typename vector >::const_iterator vli = vlb.begin(); vli != vlb.end(); ++ vli) { @@ -51,7 +80,10 @@ bboxset::bboxset (const vector >& vlb) { template template -bboxset::bboxset (const vector& vb, const bbox U::* const v) { +bboxset::bboxset (const vector& vb, const bbox U::* const v) + : skip_normalize(false) +{ + SKIP_NORMALIZE(*this); for (typename vector::const_iterator vi = vb.begin(); vi != vb.end(); ++ vi) { @@ -61,7 +93,10 @@ bboxset::bboxset (const vector& vb, const bbox U::* const v) { template template -bboxset::bboxset (const vector& vb, const bboxset U::* const v) { +bboxset::bboxset (const vector& vb, const bboxset U::* const v) + : skip_normalize(false) +{ + SKIP_NORMALIZE(*this); for (typename vector::const_iterator vi = vb.begin(); vi != vb.end(); ++ vi) { @@ -79,7 +114,7 @@ bboxset bboxset::poison () { // Invariant template bool bboxset::invariant () const { -// This is very slow when there are many bboxes + // This is very slow when there are many bboxes #if 0 && defined(CARPET_DEBUG) for (const_iterator bi=begin(); bi!=end(); ++bi) { if ((*bi).empty()) return false; @@ -99,6 +134,7 @@ bool bboxset::invariant () const { template void bboxset::normalize () { + if (skip_normalize) return; assert (invariant()); bboxset const oldbs = * this; @@ -147,14 +183,15 @@ void bboxset::normalize () int const nblo = * curr; int const nbhi = * next; assert (nbhi > nblo); // ensure that the set remains sorted - box nb (b.lower().replace(d, nblo), - b.upper().replace(d, nbhi - bstr), - b.stride()); + box const nb (b.lower().replace(d, nblo), + b.upper().replace(d, nbhi - bstr), + b.stride()); //S nbs.insert (nb); nbs.push_back (nb); } } // Replace old set + bs.clear (); bs.swap (nbs); assert (invariant()); } @@ -226,9 +263,11 @@ void bboxset::normalize () // Accessors +// cost: O(n) template -typename bboxset::size_type bboxset::size () const { - size_type s=0; +typename bboxset::size_type bboxset::size () const +{ + size_type s = 0; for (const_iterator bi=begin(); bi!=end(); ++bi) { const size_type bsz = (*bi).size(); assert (numeric_limits::max() - bsz >= s); @@ -243,8 +282,10 @@ typename bboxset::size_type bboxset::size () const { // Queries // Intersection +// cost: O(n) template -bool bboxset::intersects (const box& b) const { +bool bboxset::intersects (const box& b) const +{ for (const_iterator bi=begin(); bi!=end(); ++bi) { if ((*bi).intersects(b)) return true; } @@ -254,8 +295,10 @@ bool bboxset::intersects (const box& b) const { // Add (bboxes that don't overlap) +// cost: O(1) template -bboxset& bboxset::operator+= (const box& b) { +bboxset& bboxset::operator+= (const box& b) +{ if (b.empty()) return *this; // This is very slow when there are many bboxes #if 0 && defined(CARPET_DEBUG) @@ -271,8 +314,11 @@ bboxset& bboxset::operator+= (const box& b) { return *this; } +// cost: O(n) template -bboxset& bboxset::operator+= (const bboxset& s) { +bboxset& bboxset::operator+= (const bboxset& s) +{ + SKIP_NORMALIZE(*this); for (const_iterator bi=s.begin(); bi!=s.end(); ++bi) { *this += *bi; } @@ -280,24 +326,30 @@ bboxset& bboxset::operator+= (const bboxset& s) { return *this; } +// cost: O(1) template -bboxset& bboxset::add_transfer (bboxset& s) { +bboxset& bboxset::add_transfer (bboxset& s) +{ bs.splice (bs.end(), s.bs); assert (invariant()); normalize(); return *this; } +// cost: O(n) template -bboxset bboxset::operator+ (const box& b) const { +bboxset bboxset::operator+ (const box& b) const +{ bboxset r(*this); r += b; assert (r.invariant()); return r; } +// cost: O(n) template -bboxset bboxset::operator+ (const bboxset& s) const { +bboxset bboxset::operator+ (const bboxset& s) const +{ bboxset r(*this); r += s; assert (r.invariant()); @@ -307,32 +359,53 @@ bboxset bboxset::operator+ (const bboxset& s) const { // Union +// cost: O(n) template -bboxset& bboxset::operator|= (const box& b) { +bboxset& bboxset::operator|= (const box& b) +{ +#if 0 + // this has a cost of O(n^2) bboxset tmp = b - *this; add_transfer (tmp); +#else + // this has a cost of O(n) + bset oldbs; + oldbs.swap(bs); + bs.push_back(b); + SKIP_NORMALIZE(*this); + for (const_iterator bi=oldbs.begin(); bi!=oldbs.end(); ++bi) { + bboxset tmp = *bi - b; + add_transfer(tmp); + } +#endif assert (invariant()); return *this; } +// cost: O(n^2) template -bboxset& bboxset::operator|= (const bboxset& s) { +bboxset& bboxset::operator|= (const bboxset& s) +{ bboxset tmp = s - *this; add_transfer (tmp); assert (invariant()); return *this; } +// cost: O(n) template -bboxset bboxset::operator| (const box& b) const { +bboxset bboxset::operator| (const box& b) const +{ bboxset r(*this); r |= b; assert (r.invariant()); return r; } +// cost: O(n^2) template -bboxset bboxset::operator| (const bboxset& s) const { +bboxset bboxset::operator| (const bboxset& s) const +{ bboxset r(*this); r |= s; assert (r.invariant()); @@ -342,42 +415,56 @@ bboxset bboxset::operator| (const bboxset& s) const { // Intersection +// cost: O(n) template -bboxset bboxset::operator& (const box& b) const { +bboxset bboxset::operator& (const box& b) const +{ // start with an empty set bboxset r; - // walk all my elements - for (const_iterator bi=begin(); bi!=end(); ++bi) { - // insert the intersection with the bbox - r += *bi & b; + { + SKIP_NORMALIZE(r); + // walk all my elements + for (const_iterator bi=begin(); bi!=end(); ++bi) { + // insert the intersection with the bbox + r += *bi & b; + } } assert (r.invariant()); return r; } +// cost: O(n) template -bboxset bboxset::operator& (const bboxset& s) const { +bboxset bboxset::operator& (const bboxset& s) const +{ // start with an empty set bboxset r; - // walk all the bboxes - for (const_iterator bi=s.begin(); bi!=s.end(); ++bi) { - // insert the intersection with this bbox - bboxset tmp = *this & *bi; - r.add_transfer (tmp); + { + SKIP_NORMALIZE(r); + // walk all the bboxes + for (const_iterator bi=s.begin(); bi!=s.end(); ++bi) { + // insert the intersection with this bbox + bboxset tmp = *this & *bi; + r.add_transfer (tmp); + } } assert (r.invariant()); return r; } +// cost: O(n) template -bboxset& bboxset::operator&= (const box& b) { +bboxset& bboxset::operator&= (const box& b) +{ *this = *this & b; assert (invariant()); return *this; } +// cost: O(n) template -bboxset& bboxset::operator&= (const bboxset& s) { +bboxset& bboxset::operator&= (const bboxset& s) +{ *this = *this & s; assert (invariant()); return *this; @@ -386,8 +473,10 @@ bboxset& bboxset::operator&= (const bboxset& s) { // Difference +// cost: O(1) template -bboxset bboxset::minus (const bbox& b1, const bbox& b2) { +bboxset bboxset::minus (const bbox& b1, const bbox& b2) +{ assert (b1.is_aligned_with(b2)); if (b1.empty()) return bboxset(); if (not b1.intersects(b2)) return bboxset(b1); @@ -397,49 +486,60 @@ bboxset bboxset::minus (const bbox& b1, const bbox& b2) { vect const & b1up = b1.upper(); vect const & str = b1.stride(); bboxset r; - for (int d=0; d lb, ub; - bbox b; - for (int dd=0; ddd) { - lb[dd] = b1lo[dd]; - ub[dd] = b1up[dd]; + SKIP_NORMALIZE(r); + { + for (int d=0; d lb, ub; + bbox b; + for (int dd=0; ddd) { + lb[dd] = b1lo[dd]; + ub[dd] = b1up[dd]; + } } + lb[d] = b1lo[d]; + ub[d] = b2lo[d] - str[d]; + b = bbox(lb,ub,str); + r += b; + lb[d] = b2up[d] + str[d]; + ub[d] = b1up[d]; + b = bbox(lb,ub,str); + r += b; } - lb[d] = b1lo[d]; - ub[d] = b2lo[d] - str[d]; - b = bbox(lb,ub,str); - r += b; - lb[d] = b2up[d] + str[d]; - ub[d] = b1up[d]; - b = bbox(lb,ub,str); - r += b; } assert (r.invariant()); return r; } +// cost: O(n) template -bboxset bboxset::operator- (const box& b) const { +bboxset bboxset::operator- (const box& b) const +{ // start with an empty set bboxset r; - // walk all my elements - for (const_iterator bi=begin(); bi!=end(); ++bi) { - // insert the difference with the bbox - bboxset tmp = *bi - b; - r.add_transfer (tmp); + { + SKIP_NORMALIZE(r); + // walk all my elements + for (const_iterator bi=begin(); bi!=end(); ++bi) { + // insert the difference with the bbox + bboxset tmp = *bi - b; + r.add_transfer (tmp); + } } assert (r.invariant()); return r; } +// cost: O(n^2) template -bboxset& bboxset::operator-= (const bboxset& s) { +bboxset& bboxset::operator-= (const bboxset& s) +{ + SKIP_NORMALIZE(*this); for (const_iterator bi=s.begin(); bi!=s.end(); ++bi) { *this -= *bi; } @@ -447,16 +547,20 @@ bboxset& bboxset::operator-= (const bboxset& s) { return *this; } +// cost: O(n^2) template -bboxset bboxset::operator- (const bboxset& s) const { +bboxset bboxset::operator- (const bboxset& s) const +{ bboxset r(*this); r -= s; assert (r.invariant()); return r; } +// cost: O(n^2) template -bboxset bboxset::minus (const bbox& b, const bboxset& s) { +bboxset bboxset::minus (const bbox& b, const bboxset& s) +{ bboxset r = bboxset(b) - s; assert (r.invariant()); return r; @@ -464,8 +568,10 @@ bboxset bboxset::minus (const bbox& b, const bboxset& s) { +// cost: O(n) template -typename bboxset::box bboxset::container () const { +typename bboxset::box bboxset::container () const +{ box b; for (const_iterator bi=begin(); bi!=end(); ++bi) { b = b.expanded_containing(*bi); @@ -474,58 +580,71 @@ typename bboxset::box bboxset::container () const { } template -bboxset bboxset::pseudo_inverse (const int n) const { +bboxset bboxset::pseudo_inverse (const int n) const +{ assert (not empty()); box const c = container().expand(n,n); return c - *this; } +// cost: O(n^2) in general, but only O(n) for shifting template bboxset bboxset::expand (const vect& lo, const vect& hi) const { bboxset res; - if (all (lo == -hi)) { - // Special case for shifting, since this is faster - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res += (*bi).expand(lo,hi); - } - } else { - // We don't know (yet?) how to shrink a set - assert (all (lo>=0 and hi>=0)); - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res |= (*bi).expand(lo,hi); + { + SKIP_NORMALIZE(res); + if (all (lo == -hi)) { + // Special case for shifting, since this is faster + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res += (*bi).expand(lo,hi); + } + } else { + // We don't know (yet?) how to shrink a set + assert (all (lo>=0 and hi>=0)); + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res |= (*bi).expand(lo,hi); + } } } return res; } +// cost: O(n^2) in general, but only O(n) for shifting template bboxset bboxset::expand (const vect& lo, const vect& hi, const vect& denom) const { assert (all(denom > vect(0))); bboxset res; - if (all (lo == -hi)) { - // Special case for shifting, since this is faster - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res += (*bi).expand(lo,hi,denom); - } - } else { - // We don't know (yet?) how to shrink a set - assert (all ((lo>=0 and hi>=0) or (lo == hi))); - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res |= (*bi).expand(lo,hi,denom); + { + SKIP_NORMALIZE(res); + if (all (lo == -hi)) { + // Special case for shifting, since this is faster + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res += (*bi).expand(lo,hi,denom); + } + } else { + // We don't know (yet?) how to shrink a set + assert (all ((lo>=0 and hi>=0) or (lo == hi))); + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res |= (*bi).expand(lo,hi,denom); + } } } return res; } template -bboxset bboxset::expanded_for (const box& b) const { +bboxset bboxset::expanded_for (const box& b) const +{ bboxset res; - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res |= (*bi).expanded_for(b); + { + SKIP_NORMALIZE(res); + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res |= (*bi).expanded_for(b); + } } return res; } @@ -533,10 +652,14 @@ bboxset bboxset::expanded_for (const box& b) const { #warning "TODO: this is incorrect" #if 1 template -bboxset bboxset::contracted_for (const box& b) const { +bboxset bboxset::contracted_for (const box& b) const +{ bboxset res; - for (const_iterator bi=begin(); bi!=end(); ++bi) { - res |= (*bi).contracted_for(b); + { + SKIP_NORMALIZE(res); + for (const_iterator bi=begin(); bi!=end(); ++bi) { + res |= (*bi).contracted_for(b); + } } return res; } @@ -546,40 +669,47 @@ bboxset bboxset::contracted_for (const box& b) const { // Equality template -bool bboxset::operator<= (const bboxset& s) const { +bool bboxset::operator<= (const bboxset& s) const +{ return (*this - s).empty(); } template -bool bboxset::operator< (const bboxset& s) const { - return (*this - s).empty() && ! (s - *this).empty(); +bool bboxset::operator< (const bboxset& s) const +{ + return (*this - s).empty() && not (s - *this).empty(); } template -bool bboxset::operator>= (const bboxset& s) const { +bool bboxset::operator>= (const bboxset& s) const +{ return s <= *this; } template -bool bboxset::operator> (const bboxset& s) const { +bool bboxset::operator> (const bboxset& s) const +{ return s < *this; } template -bool bboxset::operator== (const bboxset& s) const { +bool bboxset::operator== (const bboxset& s) const +{ return (*this <= s) && (*this >= s); } template -bool bboxset::operator!= (const bboxset& s) const { - return ! (*this == s); +bool bboxset::operator!= (const bboxset& s) const +{ + return not (*this == s); } // Input template -istream& bboxset::input (istream& is) { +istream& bboxset::input (istream& is) +{ T Tdummy; try { skipws (is); @@ -616,7 +746,8 @@ istream& bboxset::input (istream& is) { // Output template -ostream& bboxset::output (ostream& os) const { +ostream& bboxset::output (ostream& os) const +{ T Tdummy; os << "bboxset<" << typestring(Tdummy) << "," << D << ">:{" << "size=" << size() << "," diff --git a/Carpet/CarpetLib/src/bboxset.hh b/Carpet/CarpetLib/src/bboxset.hh index 617a60efb..281202edb 100644 --- a/Carpet/CarpetLib/src/bboxset.hh +++ b/Carpet/CarpetLib/src/bboxset.hh @@ -42,6 +42,25 @@ ostream& operator<< (ostream& os, const bboxset& s); template class bboxset { + // Cost annotations depend on the number of bset elements n, and + // assume that normalization is skipped. + + struct skip_normalize_t { + bboxset& s; + bool const saved_skip_normalize; + skip_normalize_t(bboxset& s_) + : s(s_), + saved_skip_normalize(s.skip_normalize) + { + s.skip_normalize = true; + } + ~skip_normalize_t() + { + s.skip_normalize = saved_skip_normalize; + s.normalize(); + } + }; + // Types typedef bbox box; //S typedef set bset; @@ -54,14 +73,17 @@ class bboxset { // No bbox is empty. // The bboxes don't overlap. + bool skip_normalize; + public: // Constructors - bboxset (); - bboxset (const box& b); - bboxset (const bboxset& s); + bboxset (); // cost: O(1) + bboxset (const box& b); // cost: O(1) + bboxset (const bboxset& s); // cost: O(n) bboxset (const list& lb); + bboxset (const vector& lb); bboxset (const vector >& vlb); template bboxset (const vector& vb, const bbox U::* const v); @@ -79,51 +101,52 @@ private: public: // Accessors - bool empty () const { return bs.empty(); } + bool empty () const { return bs.empty(); } // cost: O(1) // T size () const; typedef typename box::size_type size_type; - size_type size () const; - int setsize () const { return bs.size(); } + size_type size () const; // cost: O(n) + int setsize () const { return bs.size(); } // cost: O(1) // Find out whether this bboxset intersects the bbox b - bool intersects (const box& b) const; + bool intersects (const box& b) const; // cost: O(n) // Add (bboxes that don't overlap) - bboxset& operator+= (const box& b); - bboxset& operator+= (const bboxset& s); - bboxset& add_transfer (bboxset& s); - bboxset operator+ (const box& b) const; - bboxset operator+ (const bboxset& s) const; + bboxset& operator+= (const box& b); // cost: O(1) + bboxset& operator+= (const bboxset& s); // cost: O(n) + bboxset& add_transfer (bboxset& s); // cost: O(1) + bboxset operator+ (const box& b) const; // cost: O(n) + bboxset operator+ (const bboxset& s) const; // cost: O(n) // Union - bboxset& operator|= (const box& b); - bboxset& operator|= (const bboxset& s); - bboxset operator| (const box& b) const; - bboxset operator| (const bboxset& s) const; + bboxset& operator|= (const box& b); // cost: O(n) + bboxset& operator|= (const bboxset& s); // cost: O(n^2) + bboxset operator| (const box& b) const; // cost: O(n) + bboxset operator| (const bboxset& s) const; // cost: O(n^2) // Intersection - bboxset operator& (const box& b) const; - bboxset operator& (const bboxset& s) const; - bboxset& operator&= (const box& b); - bboxset& operator&= (const bboxset& s); + bboxset operator& (const box& b) const; // cost: O(n) + bboxset operator& (const bboxset& s) const; // cost: O(n) + bboxset& operator&= (const box& b); // cost: O(n) + bboxset& operator&= (const bboxset& s); // cost: O(n) // Difference // friend bboxset operator- (const box& b1, const box& b2); - static bboxset minus (const box& b1, const box& b2); - bboxset operator- (const box& b) const; + static bboxset minus (const box& b1, const box& b2); // cost: O(1) + bboxset operator- (const box& b) const; // cost: O(n) + // cost: O(n) bboxset& operator-= (const box& b) { *this = *this - b; assert (invariant()); return *this; } - bboxset& operator-= (const bboxset& s); - bboxset operator- (const bboxset& s) const; + bboxset& operator-= (const bboxset& s); // cost: O(n^2) + bboxset operator- (const bboxset& s) const; // cost: O(n^2) // friend bboxset operator- (const box& b, const bboxset& s); - static bboxset minus (const box& b, const bboxset& s); + static bboxset minus (const box& b, const bboxset& s); // cost: O(n^2) /** Find a bbox containing the whole set. */ - box container () const; + box container () const; // cost: O(n) /** Find the pseudo-inverse. */ bboxset pseudo_inverse (const int n) const; @@ -138,12 +161,15 @@ public: /** Expand (enlarge) the bboxset by multiples of a fraction of the stride. */ + // cost: O(n^2) in general, but only O(n) for shifting bboxset expand (const vect& lo, const vect& hi, const vect& denom) const; + // cost: O(n^2) in general, but only O(n) for shifting bboxset expand (const vect,2>& lohi, const vect& denom) const { return expand (lohi[0], lohi[1], denom); } /** Shift the bboxset by multiples of a fraction of the stride. */ +// cost: O(n) bboxset shift (const vect& v, const vect& denom) const { return expand (-v, v, denom); } @@ -196,11 +222,13 @@ inline bboxset operator+ (const bbox& b, const bboxset& s) { return bboxset(b) + s; } +// cost: O(1) template inline bboxset operator- (const bbox& b1, const bbox& b2) { return bboxset::minus(b1,b2); } +// cost: O(n^2) template inline bboxset operator- (const bbox& b, const bboxset& s) { return bboxset::minus(b,s); -- cgit v1.2.3