// Copyright (C) 2004, 2007 International Business Machines and others. // All Rights Reserved. // This code is published under the Common Public License. // // $Id: IpCachedResults.hpp 949 2007-03-27 00:41:26Z andreasw $ // // Authors: Carl Laird, Andreas Waechter IBM 2004-08-13 #ifndef __IPCACHEDRESULTS_HPP__ #define __IPCACHEDRESULTS_HPP__ #include "IpTaggedObject.hpp" #include "IpObserver.hpp" #include #include #include namespace Ipopt //#define IP_DEBUG_CACHE #if COIN_IPOPT_CHECKLEVEL > 2 # define IP_DEBUG_CACHE #endif #ifdef IP_DEBUG_CACHE # include "IpDebug.hpp" #endif { // Forward Declarations template class DependentResult; // AW: I'm taking this out, since this is by far the most used // class. We should keep it as simple as possible. // /** Cache Priority Enum */ // enum CachePriority // { // CP_Lowest, // CP_Standard, // CP_Trial, // CP_Iterate // }; /** Templated class for Chached Results. This class stores up to a * given number of "results", entities that are stored here * together with identifies, that can be used to later retrieve the * information again. * * Typically, T is a SmartPtr for some calculated quantity that * should be stored (such as a Vector). The identifiers (or * dependencies) are a (possibly varying) number of Tags from * TaggedObjects, and a number of Numbers. Results are added to * the cache using the AddCachedResults methods, and the can be * retrieved with the GetCachedResults methods. The second set of * methods checks whether a result has been cached for the given * identifiers. If a corresponding results is found, a copy of it * is returned and the method evaluates to true, otherwise it * evaluates to false. * * Note that cached results can become "stale", namely when a * TaggedObject that is used to identify this CachedResult is * changed. When this happens, the cached result can never be * asked for again, so that there is no point in storing it any * longer. For this purpose, a cached result, which is stored as a * DependentResult, inherits off an Observer. This Observer * retrieves notification whenever a TaggedObject dependency has * changed. Stale results are later removed from the cache. */ template class CachedResults { public: #ifdef IP_DEBUG_CACHE /** (Only if compiled in DEBUG mode): debug verbosity level */ static const Index dbg_verbosity; #endif /** @name Constructors and Destructors. */ //@{ /** Constructor, where max_cache_size is the maximal number of * results that should be cached. If max_cache_size is negative, * we allow an infinite abount of cache. */ CachedResults(Int max_cache_size); /** Destructor */ virtual ~CachedResults(); //@} /** @name Generic methods for adding and retrieving cached results. */ //@{ /** Generic method for adding a result to the cache, given a * std::vector of TaggesObjects and a std::vector of Numbers. */ void AddCachedResult(const T& result, const std::vector& dependents, const std::vector& scalar_dependents); /** Generic method for retrieving a cached results, given the * dependencies as a std::vector of TaggesObjects and a * std::vector of Numbers. */ bool GetCachedResult(T& retResult, const std::vector& dependents, const std::vector& scalar_dependents) const; /** Method for adding a result, providing only a std::vector of * TaggedObjects. */ void AddCachedResult(const T& result, const std::vector& dependents); /** Method for retrieving a cached result, providing only a * std::vector of TaggedObjects. */ bool GetCachedResult(T& retResult, const std::vector& dependents) const; //@} /** @name Pointer-based methods for adding and retrieving cached * results, providing dependencies explicitly. */ //@{ /** Method for adding a result to the cache, proving one * dependency as a TaggedObject explicitly. */ void AddCachedResult1Dep(const T& result, const TaggedObject* dependent1); /** Method for retrieving a cached result, proving one dependency * as a TaggedObject explicitly. */ bool GetCachedResult1Dep(T& retResult, const TaggedObject* dependent1); /** Method for adding a result to the cache, proving two * dependencies as a TaggedObject explicitly. */ void AddCachedResult2Dep(const T& result, const TaggedObject* dependent1, const TaggedObject* dependent2); /** Method for retrieving a cached result, proving two * dependencies as a TaggedObject explicitly. */ bool GetCachedResult2Dep(T& retResult, const TaggedObject* dependent1, const TaggedObject* dependent2); /** Method for adding a result to the cache, proving three * dependencies as a TaggedObject explicitly. */ void AddCachedResult3Dep(const T& result, const TaggedObject* dependent1, const TaggedObject* dependent2, const TaggedObject* dependent3); /** Method for retrieving a cached result, proving three * dependencies as a TaggedObject explicitly. */ bool GetCachedResult3Dep(T& retResult, const TaggedObject* dependent1, const TaggedObject* dependent2, const TaggedObject* dependent3); /** @name Pointer-free version of the Add and Get methods */ //@{ bool GetCachedResult1Dep(T& retResult, const TaggedObject& dependent1) { return GetCachedResult1Dep(retResult, &dependent1); } bool GetCachedResult2Dep(T& retResult, const TaggedObject& dependent1, const TaggedObject& dependent2) { return GetCachedResult2Dep(retResult, &dependent1, &dependent2); } bool GetCachedResult3Dep(T& retResult, const TaggedObject& dependent1, const TaggedObject& dependent2, const TaggedObject& dependent3) { return GetCachedResult3Dep(retResult, &dependent1, &dependent2, &dependent3); } void AddCachedResult1Dep(const T& result, const TaggedObject& dependent1) { AddCachedResult1Dep(result, &dependent1); } void AddCachedResult2Dep(const T& result, const TaggedObject& dependent1, const TaggedObject& dependent2) { AddCachedResult2Dep(result, &dependent1, &dependent2); } void AddCachedResult3Dep(const T& result, const TaggedObject& dependent1, const TaggedObject& dependent2, const TaggedObject& dependent3) { AddCachedResult3Dep(result, &dependent1, &dependent2, &dependent3); } //@} /** Invalidates the result for given dependecies. Sets the stale * flag for the corresponding cached result to true if it is * found. Returns true, if the result was found. */ bool InvalidateResult(const std::vector& dependents, const std::vector& scalar_dependents); /** Invalidates all cached results */ void Clear(); /** Invalidate all cached results and changes max_cache_size */ void Clear(Int max_cache_size); private: /**@name Default Compiler Generated Methods * (Hidden to avoid implicit creation/calling). * These methods are not implemented and * we do not want the compiler to implement * them for us, so we declare them private * and do not define them. This ensures that * they will not be implicitly created/called. */ //@{ /** Default Constructor */ CachedResults(); /** Copy Constructor */ CachedResults(const CachedResults&); /** Overloaded Equals Operator */ void operator=(const CachedResults&); //@} /** maximum number of cached results */ Int max_cache_size_; /** list of currently cached results. */ mutable std::list*>* cached_results_; /** internal method for removing stale DependentResults from the * list. It is called at the beginning of every * GetDependentResult method. */ void CleanupInvalidatedResults() const; /** Print list of currently cached results */ void DebugPrintCachedResults() const; }; /** Templated class which stores one entry for the CachedResult * class. It stores the result (of type T), together with its * dependencies (vector of TaggedObjects and vector of Numbers). * It also stores a priority. */ template class DependentResult : public Observer { public: #ifdef IP_DEBUG_CACHE static const Index dbg_verbosity; #endif /** @name Constructor, Destructors */ //@{ /** Constructor, given all information about the result. */ DependentResult(const T& result, const std::vector& dependents, const std::vector& scalar_dependents); /** Destructor. */ ~DependentResult(); //@} /** @name Accessor method. */ //@{ /** This returns true, if the DependentResult is no longer valid. */ bool IsStale() const; /** Invalidates the cached result. */ void Invalidate(); /** Returns the cached result. */ const T& GetResult() const; //@} /** This method returns true if the dependencies provided to this * function are identical to the ones stored with the * DependentResult. */ bool DependentsIdentical(const std::vector& dependents, const std::vector& scalar_dependents) const; /** Print information about this DependentResults. */ void DebugPrint() const; protected: /** This method is overloading the pure virtual method from the * Observer base class. This method is called when a Subject * registered for this Observer sends a notification. In this * particular case, if this method is called with * notify_type==NT_Changed or NT_BeingDeleted, then this results * is marked as stale. */ virtual void RecieveNotification(NotifyType notify_type, const Subject* subject); private: /**@name Default Compiler Generated Methods * (Hidden to avoid implicit creation/calling). * These methods are not implemented and * we do not want the compiler to implement * them for us, so we declare them private * and do not define them. This ensures that * they will not be implicitly created/called. */ //@{ /** Default Constructor */ DependentResult(); /** Copy Constructor */ DependentResult(const DependentResult&); /** Overloaded Equals Operator */ void operator=(const DependentResult&); //@} /** Flag indicating, if the cached result is still valid. A result becomes invalid, if the RecieveNotification method is called with NT_Changed */ bool stale_; /** The value of the dependent results */ const T result_; /** Dependencies in form of TaggedObjects */ std::vector dependent_tags_; /** Dependencies in form a Numbers */ std::vector scalar_dependents_; }; #ifdef IP_DEBUG_CACHE template const Index CachedResults::dbg_verbosity = 0; template const Index DependentResult::dbg_verbosity = 0; #endif template DependentResult::DependentResult( const T& result, const std::vector& dependents, const std::vector& scalar_dependents) : stale_(false), result_(result), dependent_tags_(dependents.size()), scalar_dependents_(scalar_dependents) { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::DependentResult()", dbg_verbosity); #endif for (Index i=0; i<(Index)dependents.size(); i++) { if (dependents[i]) { // Call the RequestAttach method of the Observer base class. // This will add this dependent result in the Observer list // for the Subject dependents[i]. As a consequence, the // RecieveNotification method of this DependentResult will be // called with notify_type=NT_Changed, whenever the // TaggedResult dependents[i] is changed (i.e. its HasChanged // method is called). RequestAttach(NT_Changed, dependents[i]); dependent_tags_[i] = dependents[i]->GetTag(); } else { dependent_tags_[i] = 0; } } } template DependentResult::~DependentResult() { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::~DependentResult()", dbg_verbosity); //DBG_ASSERT(stale_ == true); #endif // Nothing to be done here, destructor // of T should sufficiently remove // any memory, etc. } template bool DependentResult::IsStale() const { return stale_; } template void DependentResult::Invalidate() { stale_ = true; } template void DependentResult::RecieveNotification(NotifyType notify_type, const Subject* subject) { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::RecieveNotification", dbg_verbosity); #endif if (notify_type == NT_Changed || notify_type==NT_BeingDestroyed) { stale_ = true; // technically, I could unregister the notifications here, but they // aren't really hurting anything } } template bool DependentResult::DependentsIdentical(const std::vector& dependents, const std::vector& scalar_dependents) const { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::DependentsIdentical", dbg_verbosity); DBG_ASSERT(stale_ == false); DBG_ASSERT(dependents.size() == dependent_tags_.size()); #endif bool retVal = true; if (dependents.size() != dependent_tags_.size() || scalar_dependents.size() != scalar_dependents_.size()) { retVal = false; } else { for (Index i=0; i<(Index)dependents.size(); i++) { if ( (dependents[i] && dependents[i]->GetTag() != dependent_tags_[i]) || (!dependents[i] && dependent_tags_[i] != 0) ) { retVal = false; break; } } if (retVal) { for (Index i=0; i<(Index)scalar_dependents.size(); i++) { if (scalar_dependents[i] != scalar_dependents_[i]) { retVal = false; break; } } } } return retVal; } template const T& DependentResult::GetResult() const { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::GetResult()", dbg_verbosity); DBG_ASSERT(stale_ == false); #endif return result_; } template void DependentResult::DebugPrint() const { #ifdef IP_DEBUG_CACHE DBG_START_METH("DependentResult::DebugPrint", dbg_verbosity); #endif } template CachedResults::CachedResults(Int max_cache_size) : max_cache_size_(max_cache_size), cached_results_(NULL) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::CachedResults", dbg_verbosity); #endif } template CachedResults::~CachedResults() { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::!CachedResults()", dbg_verbosity); #endif if (cached_results_) { for (typename std::list< DependentResult* >::iterator iter = cached_results_-> begin(); iter != cached_results_->end(); iter++) { delete *iter; } delete cached_results_; } /* while (!cached_results_.empty()) { DependentResult* result = cached_results_.back(); cached_results_.pop_back(); delete result; } */ } template void CachedResults::AddCachedResult(const T& result, const std::vector& dependents, const std::vector& scalar_dependents) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::AddCachedResult", dbg_verbosity); #endif CleanupInvalidatedResults(); // insert the new one here DependentResult* newResult = new DependentResult(result, dependents, scalar_dependents); if (!cached_results_) { cached_results_ = new std::list*>; } cached_results_->push_front(newResult); // keep the list small enough if (max_cache_size_ >= 0) { // if negative, allow infinite cache // non-negative - limit size of list to max_cache_size DBG_ASSERT((Int)cached_results_->size()<=max_cache_size_+1); if ((Int)cached_results_->size() > max_cache_size_) { delete cached_results_->back(); cached_results_->pop_back(); } } #ifdef IP_DEBUG_CACHE DBG_EXEC(2, DebugPrintCachedResults()); #endif } template void CachedResults::AddCachedResult(const T& result, const std::vector& dependents) { std::vector scalar_dependents; AddCachedResult(result, dependents, scalar_dependents); } template bool CachedResults::GetCachedResult(T& retResult, const std::vector& dependents, const std::vector& scalar_dependents) const { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::GetCachedResult", dbg_verbosity); #endif if (!cached_results_) return false; CleanupInvalidatedResults(); bool retValue = false; typename std::list< DependentResult* >::const_iterator iter; for (iter = cached_results_->begin(); iter != cached_results_->end(); iter++) { if ((*iter)->DependentsIdentical(dependents, scalar_dependents)) { retResult = (*iter)->GetResult(); retValue = true; break; } } #ifdef IP_DEBUG_CACHE DBG_EXEC(2, DebugPrintCachedResults()); #endif return retValue; } template bool CachedResults::GetCachedResult( T& retResult, const std::vector& dependents) const { std::vector scalar_dependents; return GetCachedResult(retResult, dependents, scalar_dependents); } template void CachedResults::AddCachedResult1Dep(const T& result, const TaggedObject* dependent1) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::AddCachedResult1Dep", dbg_verbosity); #endif std::vector dependents(1); dependents[0] = dependent1; AddCachedResult(result, dependents); } template bool CachedResults::GetCachedResult1Dep(T& retResult, const TaggedObject* dependent1) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::GetCachedResult1Dep", dbg_verbosity); #endif std::vector dependents(1); dependents[0] = dependent1; return GetCachedResult(retResult, dependents); } template void CachedResults::AddCachedResult2Dep(const T& result, const TaggedObject* dependent1, const TaggedObject* dependent2) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::AddCachedResult2dDep", dbg_verbosity); #endif std::vector dependents(2); dependents[0] = dependent1; dependents[1] = dependent2; AddCachedResult(result, dependents); } template bool CachedResults::GetCachedResult2Dep(T& retResult, const TaggedObject* dependent1, const TaggedObject* dependent2) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::GetCachedResult2Dep", dbg_verbosity); #endif std::vector dependents(2); dependents[0] = dependent1; dependents[1] = dependent2; return GetCachedResult(retResult, dependents); } template void CachedResults::AddCachedResult3Dep(const T& result, const TaggedObject* dependent1, const TaggedObject* dependent2, const TaggedObject* dependent3) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::AddCachedResult2dDep", dbg_verbosity); #endif std::vector dependents(3); dependents[0] = dependent1; dependents[1] = dependent2; dependents[2] = dependent3; AddCachedResult(result, dependents); } template bool CachedResults::GetCachedResult3Dep(T& retResult, const TaggedObject* dependent1, const TaggedObject* dependent2, const TaggedObject* dependent3) { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::GetCachedResult2Dep", dbg_verbosity); #endif std::vector dependents(3); dependents[0] = dependent1; dependents[1] = dependent2; dependents[2] = dependent3; return GetCachedResult(retResult, dependents); } template bool CachedResults::InvalidateResult(const std::vector& dependents, const std::vector& scalar_dependents) { if (!cached_results_) return false; CleanupInvalidatedResults(); bool retValue = false; typename std::list< DependentResult* >::const_iterator iter; for (iter = cached_results_->begin(); iter != cached_results_->end(); iter++) { if ((*iter)->DependentsIdentical(dependents, scalar_dependents)) { (*iter)->Invalidate(); retValue = true; break; } } return retValue; } template void CachedResults::Clear() { if (!cached_results_) return; typename std::list< DependentResult* >::const_iterator iter; for (iter = cached_results_->begin(); iter != cached_results_->end(); iter++) { (*iter)->Invalidate(); } CleanupInvalidatedResults(); } template void CachedResults::Clear(Int max_cache_size) { Clear(); max_cache_size_ = max_cache_size; } template void CachedResults::CleanupInvalidatedResults() const { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::CleanupInvalidatedResults", dbg_verbosity); #endif if (!cached_results_) return; typename std::list< DependentResult* >::iterator iter; iter = cached_results_->begin(); while (iter != cached_results_->end()) { if ((*iter)->IsStale()) { typename std::list< DependentResult* >::iterator iter_to_remove = iter; iter++; DependentResult* result_to_delete = (*iter_to_remove); cached_results_->erase(iter_to_remove); delete result_to_delete; } else { iter++; } } } template void CachedResults::DebugPrintCachedResults() const { #ifdef IP_DEBUG_CACHE DBG_START_METH("CachedResults::DebugPrintCachedResults", dbg_verbosity); if (DBG_VERBOSITY()>=2 ) { if (!chached_results_) { DBG_PRINT((2," DependentResult:0x%x\n", (*iter))); } else { typename std::list< DependentResult* >::const_iterator iter; DBG_PRINT((2,"Current set of cached results:\n")); for (iter = cached_results_->begin(); iter != cached_results_->end(); iter++) { DBG_PRINT((2," DependentResult:0x%x\n", (*iter))); } } } #endif } } // namespace Ipopt #endif