1 // @HEADER
2 // ************************************************************************
3 //
4 //               Rapid Optimization Library (ROL) Package
5 //                 Copyright (2014) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact lead developers:
38 //              Drew Kouri   (dpkouri@sandia.gov) and
39 //              Denis Ridzal (dridzal@sandia.gov)
40 //
41 // ************************************************************************
42 // @HEADER
43 
44 #pragma once
45 #ifndef ROL_VECTORWORKSPACE_HPP
46 #define ROL_VECTORWORKSPACE_HPP
47 
48 #include "ROL_Vector.hpp"
49 #include <iostream>
50 #include <map>
51 #include <utility>
52 
53 /** @ingroup la_group
54     \class ROL::VectorWorkspace
55     \brief Provides a "smart" cloning manager to be used a member variable in
56            a class and called in the member function of the same class.
57 
58            VectorWorkspace::clone( const Vector& x )   and
59            VectorWorkspace::clone( const Ptr<Vector>& x )
60 
61            Will allocate new memory of a clone of x *if needed* and return
62            a pointer to the clone. A new clone is considered to be needed
63            only if these is not a previously allocated compatible vector
64            stored in the VectorWorkspace.
65 
66            Compatibility is determined by derived type (typeid::hash_code)
67            and vector dimension. Together these form a VectorKey.
68            When cloning a vector inside a member function, VectorWorkspace
69            will identify it's VectorKey type. If such a type exists in the
70            database, with will then refer to the associated VectorStack that
71            is specific to the VectorKey type.
72 
73            The VectorStack will be searched for the first available dynamically
74            allocated vector which has no external references to it and return
75            a pointer to it. If no such vector exists, a new one will be
76            cloned and added to the stack. When the local pointers to the
77            VectorStack elements go out of scope at the end of the member
78            function, the reference counts are decremented and the vectors
79            become available for use again.
80 
81            NOTE: Stored clones will have a reference count of 2 when there
82            are no external pointers to the same object.
83 
84            It should also be possible use a single VectorWorkspace in
85            multiple levels of nested objects.
86 
87            The concept could probably also generalize to the MPI setting
88            where node locality is an additional distinguishing qualifier
89            for cloning.
90 
91 */
92 
93 namespace ROL {
94 
95 namespace details {
96 
97 using namespace std;
98 
99 
100 template<typename Real>
101 class VectorWorkspace {
102 
103   using V = ROL::Vector<Real>;
104   using size_type = typename vector<Real>::size_type;
105 
106 private:
107 
108   struct VectorKey {
109     friend class VectorWorkspace<Real>;
110     size_t hash_code;
111     int    dimension;
112 
VectorKeyROL::details::VectorWorkspace::VectorKey113     VectorKey( const V& x ) :
114       hash_code(typeid(x).hash_code()),
115       dimension( x.dimension() ) {}
116 
VectorKeyROL::details::VectorWorkspace::VectorKey117     VectorKey( const Ptr<V>& x ) :
118       VectorKey( *x ) {}
119 
to_stringROL::details::VectorWorkspace::VectorKey120     static string to_string( const VectorKey& key ) {
121       stringstream ss;
122       ss << "VectorKey(" << hex << key.hash_code << ","
123                          << dec << key.dimension << ")";
124       return ss.str();
125     }
126 
operator <ROL::details::VectorWorkspace::VectorKey127     bool operator < ( const VectorKey& x ) const {
128       return ( hash_code < x.hash_code ) && ( dimension < x.dimension );
129     }
130 
operator ==ROL::details::VectorWorkspace::VectorKey131     bool operator == ( const VectorKey& x ) const {
132       return ( hash_code == x.hash_code ) && ( dimension == x.dimension );
133     }
134 
135 //    bool operator != ( const VectorKey& x ) const {
136 //      return ( hash_code != x.hash_code ) || ( dimension != x.dimension );
137 //    }
138 
139   }; // class VectorKey
140 
141   struct VectorStack {
142 
143     friend class VectorWorkspace<Real>;
144     vector<Ptr<V>> vectors_;
145     VectorKey key_;
146 
VectorStackROL::details::VectorWorkspace::VectorStack147     VectorStack( const V& x ) : vectors_( 1, x.clone() ),
148       key_(VectorKey(x)) {}
149 
getKeyROL::details::VectorWorkspace::VectorStack150     const VectorKey& getKey() const { return key_; }
151 
sizeROL::details::VectorWorkspace::VectorStack152     size_type size() const { return vectors_.size(); }
153 
number_assignedROL::details::VectorWorkspace::VectorStack154     size_type number_assigned() const {
155       size_type count = 0;
156       for( auto v : vectors_ ) count += ( getCount(v) > 3 );
157       return count;
158     }
159 
160     /** If no next element exists, clone it, increment the index, and
161         return a the clone by pointer
162     */
cloneROL::details::VectorWorkspace::VectorStack163     Ptr<V> clone( const V& x ) {
164       VectorKey x_key(x);
165 
166       ROL_TEST_FOR_EXCEPTION( key_.hash_code != x_key.hash_code, logic_error,
167         "VectorWorkspace::VectorStack tried to clone a vector of type "     <<
168         hex << key_.hash_code << ", but it can only clone vectors of type " <<
169         hex << x_key.hash_code );
170 
171       ROL_TEST_FOR_EXCEPTION( key_.dimension != x_key.dimension, logic_error,
172         "VectorWorkspace::VectorStack tried to clone a vector of dimension "     <<
173         hex << key_.dimension << ", but it can only clone vectors of dimension " <<
174         hex << x_key.dimension );
175 
176       for( auto e : vectors_ ) { // Return first unreferenced vector
177         if( getCount(e) <= 2 ) { // Storing pointers in vector increments count
178           return e;
179         }
180       }
181       // If no unreferenced vectors exist, add a new one
182       auto v = x.clone();
183       vectors_.push_back( v );
184       return v;
185     }
186 
187     // For testing purposes
getRefCountsROL::details::VectorWorkspace::VectorStack188     vector<size_type> getRefCounts( void ) const {
189       vector<size_type> counts;
190       for( auto e: vectors_ ) counts.push_back( getCount(e) );
191       return counts;
192     }
193 
194   }; // VectorStack
195 
196   map<VectorKey,Ptr<VectorStack>> workspace_;
197 
198 public:
199 
clone(const V & x)200   Ptr<V> clone( const V& x ) {
201 
202     VectorKey        key(x);
203     size_type        key_count{0};
204     Ptr<VectorStack> vstack{nullPtr};
205 
206     for( auto e : workspace_ ) key_count += (key == e.first);
207 
208     if( key_count == 0 ) { // New key
209       vstack = makePtr<VectorStack>(x);
210       workspace_.insert( make_pair(key,vstack) );
211     }
212     else vstack = workspace_[key];
213 
214     return vstack->clone(x);
215   }
216 
clone(const Ptr<const V> & x)217   Ptr<V> clone( const Ptr<const V>& x ) { return clone(*x); }
218 
219   // Deep copy
copy(const V & x)220   Ptr<V> copy( const V& x ) {
221     auto xc = clone(x);
222     xc->set(x);
223     return xc;
224   }
225 
copy(const Ptr<const V> & x)226   Ptr<V> copy( const Ptr<const V>& x ) { return copy(*x); }
227 
status(ostream & os) const228   void status( ostream& os ) const {
229     os << "\n\n" << string(80,'-') << std::endl;
230     os << "VectorWorkspace contains the following VectorStack(hash_code,dim) entries:\n\n";
231     for( auto entry : workspace_ ) {
232       os << "  VectorStack(" << hex << entry.first.hash_code << ","
233                                << dec << entry.first.dimension << ")";
234       os << "\n  Reference Counts per element" << std::endl;
235       for( auto e : entry.second->vectors_ ) {
236         os << "        " << getCount( e ) << std::endl;
237       }
238     }
239     os << string(80,'-') << std::endl;
240   }
241 
242 
243 }; // VectorWorkspace
244 
245 } // namespace details
246 
247 using details::VectorWorkspace;
248 
249 } // namespace ROL
250 
251 
252 #endif
253