1 #ifndef SimTK_SimTKCOMMON_MATRIX_HELPER_REP_H_
2 #define SimTK_SimTKCOMMON_MATRIX_HELPER_REP_H_
3 
4 /* -------------------------------------------------------------------------- *
5  *                       Simbody(tm): SimTKcommon                             *
6  * -------------------------------------------------------------------------- *
7  * This is part of the SimTK biosimulation toolkit originating from           *
8  * Simbios, the NIH National Center for Physics-Based Simulation of           *
9  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
10  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
11  *                                                                            *
12  * Portions copyright (c) 2005-12 Stanford University and the Authors.        *
13  * Authors: Michael Sherman                                                   *
14  * Contributors:                                                              *
15  *                                                                            *
16  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
17  * not use this file except in compliance with the License. You may obtain a  *
18  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
19  *                                                                            *
20  * Unless required by applicable law or agreed to in writing, software        *
21  * distributed under the License is distributed on an "AS IS" BASIS,          *
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
23  * See the License for the specific language governing permissions and        *
24  * limitations under the License.                                             *
25  * -------------------------------------------------------------------------- */
26 
27 #include "SimTKcommon/Scalar.h"
28 #include "SimTKcommon/SmallMatrix.h"
29 
30 #include "ElementFilter.h"
31 
32 #include <cstddef>
33 
34 namespace SimTK {
35 template <class S> class MatrixHelper;      // the helper handle class
36 template <class S> class MatrixHelperRep;   // implementation base class
37 
38 template <class S> class FullHelper;        // all elements stored
39 template <class S> class TriHelper;         // half the elements are stored
40 template <class S> class VectorHelper;      // 1-d array of elements
41 
42 /* --------------------------- MatrixHelperRep ---------------------------------
43  *
44  * This is the private implementation of the MatrixHelper<S> class. Note that
45  * this must be explicitly instantiated in library-side source code for all
46  * possible values of the template parameter S, which is just the set of all
47  * SimTK scalar types.
48  *
49  * Every MatrixHelperRep has the following members:
50  *  - handle properties
51  *  - matrix character commitment
52  *  - actual matrix character
53  *  - pointer to memory
54  *  - pointer to memory's reference counter
55  *
56  * Concrete derivatives of MatrixHelperRep contain additional information
57  * used to map logical element indices to physical memory.
58  *
59  * There are some extremely performance-sensitive aspects to this object,
60  * especially with regard to selecting individual elements of a matrix. In order
61  * to get here we have already had to follow the pointer in the Matrix handle.
62  * We would like to be able to return an element in the fewest additional
63  * instructions possible. This is made difficult by the variety of run time
64  * features we want to support here:
65  *  - writable vs. read-only access to data
66  *  - row- and column-ordered data
67  *  - scalar elements vs. composite ones
68  *  - 1d Vector and RowVector objects vs. 2d Matrix objects
69  *  - various ways of selecting meaningful elements from memory based
70  *    on regular and irregular spacing
71  *
72  * If we had to check flags for each of these possibilities before finally
73  * indexing the element of interest, access would be substantially slowed. We
74  * are particularly concerned about performance for the most common case
75  * of regularly-spaced scalar elements. Instead of checking flags at run time,
76  * we want to make use of the virtual function table so that the all of the
77  * above tests can be avoided with a single indirect call through the virtual
78  * function branch table. At run time the compiler will then translate a call
79  * like "rep->getElt(i,j)" to "call rep->vft[getElt](i,j)" where the particular
80  * function called is the right one given this rep's particular combination of
81  * all the possibilities mentioned above, which is encapsulated in the concrete
82  * type of the MatrixHelperRep object. Attempts to call update methods of
83  * non-writable reps will call directly to methods which throw an appropriate
84  * error condition.
85  *
86  * Sorting out all these issues at compile time requires a large number of
87  * small classes. All classes are templatized by Scalar type <S> and explicitly
88  * instantiated for all the SimTK scalar types.
89  *
90  * At handle construction, a suitable default helper is assigned with a minimal
91  * handle commitment derived from the handle type -- element size, column or
92  * row outline, and whether the handle must be a view. Everything else will be
93  * uncommitted. If a later assignment requires a different concrete helper, the
94  * original one will be replaced but the commitments will remain unchanged.
95  * Note that MatrixHelpers cannot be shared; each is paired with just one
96  * handle.
97  * ---------------------------------------------------------------------------*/
98 template <class S>
99 class MatrixHelperRep {
100     typedef MatrixHelperRep<S>                      This;
101     typedef MatrixHelperRep<typename CNT<S>::TNeg>  ThisNeg;
102     typedef MatrixHelperRep<typename CNT<S>::THerm> ThisHerm;
103 public:
104     typedef typename CNT<S>::Number     Number;     // strips off negator from S
105     typedef typename CNT<S>::StdNumber  StdNumber;  // turns conjugate to complex
106     typedef typename CNT<S>::Precision  Precision;  // strips off complex from StdNumber
107     typedef typename CNT<S>::TNeg       SNeg;       // the negated version of S
108     typedef typename CNT<S>::THerm      SHerm;      // the conjugate version of S
109 
110     // Constructors are protected; for use by derived classes only.
111 
112     // This is the MatrixHelper factory method for owner matrices. Given a
113     // particular matrix character, this method will deliver the best
114     // MatrixHelperRep subclass with those characteristics.
115     static This* createOwnerMatrixHelperRep(int esz, int cppEsz,
116                                             const MatrixCharacter&);
117 
118     // This is the MatrixHelper factory method for matrices providing a view
119     // into externally-allocated data. The supplied MatrixCharacter describes
120     // the actual properties of the external data as we want it to appear
121     // through this view. Const and non-const methods are provided. There is
122     // provision for a "spacing" argument for the external data; this may
123     // mean different things. Typically it is the leading dimensions for
124     // full-storage matrices, and the stride for vectors. We'll return the
125     // best MatrixHelperRep we can come up with for these characteristics; it
126     // will not be an owner rep.
127     static This* createExternalMatrixHelperRep
128        (int esz, int cppEsz, const MatrixCharacter& actual,
129         int spacing, S* data, bool canWrite=true);
130 
131     // This is the factory for const access to externally-allocated storage.
createExternalMatrixHelperRep(int esz,int cppEsz,const MatrixCharacter & actual,int spacing,const S * data)132     static This* createExternalMatrixHelperRep
133        (int esz, int cppEsz, const MatrixCharacter& actual, int spacing,
134         const S* data)
135     {   return createExternalMatrixHelperRep(esz, cppEsz, actual, spacing,
136                                              const_cast<S*>(data), false); }
137 
138     // Here we have determined that this source is acceptable for the data
139     // already allocated in this handle. In particular both dimensions match.
copyInFromCompatibleSource(const MatrixHelperRep<S> & source)140     void copyInFromCompatibleSource(const MatrixHelperRep<S>& source) {
141         if (!m_writable)
142             SimTK_THROW1(Exception::OperationNotAllowedOnNonconstReadOnlyView,
143                          "assignment");
144         copyInFromCompatibleSource_(source);
145     }
146 
147     // Same as above except the source is negated as it is copied in.
negatedCopyInFromCompatibleSource(const ThisNeg & source)148     void negatedCopyInFromCompatibleSource(const ThisNeg& source) {
149         copyInFromCompatibleSource
150            (reinterpret_cast<const MatrixHelperRep<S>&>(source));
151         scaleBy(StdNumber(-1));
152     }
153 
154     // Fill every element with repeated copies of a single scalar value.
155     // The default implementation is very slow.
fillWithScalar(const StdNumber & scalar)156     void fillWithScalar(const StdNumber& scalar) {
157         if (!m_writable)
158             SimTK_THROW1(Exception::OperationNotAllowedOnNonconstReadOnlyView,
159                          "fillWithScalar()");
160         fillWithScalar_(scalar);
161     }
162 
163     // These are used in copy constructors. Hence the resulting value must
164     // be the same as the source value, even if we're removing a negator<> from
165     // the elements. So the createNegatedDeepCopy actually has to negate every
166     // element with (yuck) floating point operations. Note that the returned
167     // Matrix is always an owner, writable, and has whatever is the most
168     // efficient storage type for the source data.
createDeepCopy()169     This* createDeepCopy() const {return createDeepCopy_();}
createNegatedDeepCopy()170     ThisNeg* createNegatedDeepCopy() const {
171         ThisNeg* p = reinterpret_cast<const ThisNeg*>(this)->createDeepCopy_();
172         p->scaleBy(StdNumber(-1));
173         return p;
174     }
175 
createWholeView(bool wantToWrite)176     This* createWholeView(bool wantToWrite) const {
177         This* p       = cloneHelper_();
178         p->m_owner    = false;
179         p->m_writable = m_writable && wantToWrite;
180         p->m_data     = m_data;
181         p->m_actual   = m_actual;
182         return p;
183     }
184 
185 
createHermitianView(bool wantToWrite)186     ThisHerm* createHermitianView(bool wantToWrite) const {
187         const ThisHerm* thisHerm = reinterpret_cast<const ThisHerm*>(this);
188         ThisHerm* p = const_cast<ThisHerm*>(thisHerm)->createTransposeView_();
189         p->m_writable = m_writable && wantToWrite;
190         p->m_actual.setActualSize(ncol(),nrow());
191         return p;
192     }
193 
194 
createBlockView(const EltBlock & block,bool wantToWrite)195     This* createBlockView(const EltBlock& block, bool wantToWrite) const {
196         SimTK_SIZECHECK(block.row0(), nrow()-block.nrow(),
197                         "MatrixHelperRep::createBlockView()");
198         SimTK_SIZECHECK(block.col0(), ncol()-block.ncol(),
199                         "MatrixHelperRep::createBlockView()");
200         This* p = const_cast<This*>(this)->createBlockView_(block);
201         p->m_writable = m_writable && wantToWrite;
202         p->m_actual.setActualSize(block.nrow(), block.ncol());
203         return p;
204     }
createRegularView(const EltBlock & block,const EltIndexer & ix,bool wantToWrite)205     This* createRegularView(const EltBlock& block, const EltIndexer& ix,
206                             bool wantToWrite) const {
207         SimTK_SIZECHECK(block.row0(), nrow()-ix.row(block.nrow(), block.ncol()),
208                         "MatrixHelperRep::createRegularView()");
209         SimTK_SIZECHECK(block.col0(), ncol()-ix.col(block.nrow(), block.ncol()),
210                         "MatrixHelperRep::createRegularView()");
211         This* p = const_cast<This*>(this)->createRegularView_(block, ix);
212         p->m_writable = m_writable && wantToWrite;
213         p->m_actual.setActualSize(block.nrow(), block.ncol());
214         return p;
215     }
216 
217         // 1-d views //
218 
createDiagonalView(bool wantToWrite)219     VectorHelper<S>* createDiagonalView(bool wantToWrite) const {
220         VectorHelper<S>* p = const_cast<This*>(this)->createDiagonalView_();
221         p->m_writable = m_writable && wantToWrite;
222         p->m_actual.setActualSize(std::min(nrow(),ncol()), 1); // a column
223         return p;
224     }
225     // Select column j or part of column j.
createColumnView(int j,int i,int m,bool wantToWrite)226     VectorHelper<S>* createColumnView(int j, int i, int m, bool wantToWrite) const {
227         SimTK_INDEXCHECK(j, ncol(), "MatrixHelperRep::createColumnView()");
228         SimTK_SIZECHECK(i, nrow()-m, "MatrixHelperRep::createColumnView()");
229         VectorHelper<S>* p = const_cast<This*>(this)->createColumnView_(j,i,m);
230         p->m_writable = m_writable && wantToWrite;
231         p->m_actual.setActualSize(m, 1); // a column
232         return p;
233     }
234     // Select row i or part of row i.
createRowView(int i,int j,int n,bool wantToWrite)235     VectorHelper<S>* createRowView(int i, int j, int n, bool wantToWrite) const {
236         SimTK_INDEXCHECK(i, nrow(), "MatrixHelperRep::createRowView()");
237         SimTK_SIZECHECK(j, ncol()-n, "MatrixHelperRep::createRowView()");
238         VectorHelper<S>* p = const_cast<This*>(this)->createRowView_(i,j,n);
239         p->m_writable = m_writable && wantToWrite;
240         p->m_actual.setActualSize(1, n); // a row
241         return p;
242     }
243 
244     // Is it more efficient to access this Matrix in row order rather than
245     // the default column order? If so, you should try to operate on
246     // all columns of each row before moving on to the next row. Otherwise,
247     // try to operate on all rows of each column before moving on
248     // to the next column.
preferRowOrder()249     bool preferRowOrder() const {return preferRowOrder_();}
250 
251     // Is the memory that we ultimately reference organized contiguously?
hasContiguousData()252     bool hasContiguousData() const {return hasContiguousData_();}
253 
254     // Using *element* indices, obtain a pointer to the beginning of a
255     // particular element. This is always a slow operation compared to raw
256     // array access; use sparingly.
257     //
258     // These inline base class interface routines provide Debug-mode range
259     // checking but evaporate completely in Release mode so that the underlying
260     // virtual method is called directly.
getElt(int i,int j)261     const S* getElt(int i, int j) const {
262         SimTK_INDEXCHECK(i, nrow(), "MatrixHelperRep::getElt(i,j)");
263         SimTK_INDEXCHECK(j, ncol(), "MatrixHelperRep::getElt(i,j)");
264         return getElt_(i,j);
265     }
updElt(int i,int j)266     S* updElt(int i, int j) {
267         SimTK_INDEXCHECK(i, nrow(), "MatrixHelperRep::updElt(i,j)");
268         SimTK_INDEXCHECK(j, ncol(), "MatrixHelperRep::updElt(i,j)");
269         SimTK_ERRCHK(m_writable, "MatrixHelperRep::updElt()",
270                      "Matrix not writable.");
271         return updElt_(i,j);
272     }
273 
getAnyElt(int i,int j,S * value)274     void getAnyElt(int i, int j, S* value) const {
275         SimTK_INDEXCHECK(i, nrow(), "MatrixHelperRep::getAnyElt(i,j)");
276         SimTK_INDEXCHECK(j, ncol(), "MatrixHelperRep::getAnyElt(i,j)");
277         SimTK_ERRCHK(value, "MatrixHelperRep::getAnyElt()",
278             "The value return pointer must not be null.");
279         return getAnyElt_(i,j,value);
280     }
281 
getElt(int i)282     const S* getElt(int i) const {
283         SimTK_INDEXCHECK(i, length(), "MatrixHelperRep::getElt(i)");
284         return getElt_(i);
285     }
updElt(int i)286     S* updElt(int i) {
287         SimTK_INDEXCHECK(i, length(), "MatrixHelperRep::updElt(i)");
288         SimTK_ERRCHK(m_writable, "MatrixHelperRep::updElt(i)",
289                      "Matrix not writable.");
290         return updElt_(i);
291     }
292 
getAnyElt(int i,S * value)293     void getAnyElt(int i, S* value) const {
294         SimTK_INDEXCHECK(i, length(), "MatrixHelperRep::getAnyElt(i)");
295         SimTK_ERRCHK(value, "MatrixHelperRep::getAnyElt()",
296             "The value return pointer must not be null.");
297         return getAnyElt_(i,value);
298     }
299 
300     // Many matrix storage formats assume values for some of their elements
301     // and thus those elements are not stored. Generic algorithms must avoid
302     // writing on such elements, so we expect to be able to query the concrete
303     // class to find out if particular elements actually have a memory
304     // representation.
305     //
306     // Note: for symmetric matrices only one of (i,j) and (j,i) should return
307     // true here, and for unit diagonals (i,i) will return false.
eltIsStored(int i,int j)308     bool eltIsStored(int i, int j) const {
309         SimTK_INDEXCHECK(i, nrow(), "MatrixHelperRep::eltIsStored(i,j)");
310         SimTK_INDEXCHECK(j, ncol(), "MatrixHelperRep::eltIsStored(i,j)");
311         return eltIsStored_(i,j);
312     }
eltIsStored(int i)313     bool eltIsStored(int i) const {
314         SimTK_INDEXCHECK(i, length(), "MatrixHelperRep::eltIsStored(i)");
315         return eltIsStored_(i);
316     }
317 
318     // Add up all the *elements*, returning the answer in the element given
319     // by pointer to its first scalar.
sum(S * eltp)320     void sum(S* eltp) const {sum_(eltp);}
colSum(int j,S * eltp)321     void colSum(int j, S* eltp) const {colSum_(j,eltp);}
rowSum(int i,S * eltp)322     void rowSum(int i, S* eltp) const {rowSum_(i,eltp);}
323 
324 
325 
326     // Resizing is permitted only when one of these two conditions is met:
327     //     1. The matrix already has the indicated dimensions,
328     //  OR 2.  (a) this handle is the owner of the data,
329     //     AND (b) handle commitment permits resizing of at least one dimension,
330     //     AND (c) data has not been locked.
resize(int m,int n,bool keep)331     void resize(int m, int n, bool keep) {
332         SimTK_SIZECHECK_NONNEG(m, "MatrixHelperRep::resize()");
333         SimTK_SIZECHECK_NONNEG(n, "MatrixHelperRep::resize()");
334         if (m==nrow() && n==ncol()) return;
335         if (!m_owner)
336             SimTK_THROW1(Exception::OperationNotAllowedOnView, "resize()");
337         // owner
338         if (m_handleIsLocked)
339             SimTK_THROW1(Exception::Cant,
340                 "resize(), because owner handle is locked.");
341         if (!m_commitment.isSizeOK(m,n))
342             SimTK_THROW1(Exception::Cant,
343                 "resize(), because handle commitment doesn't allow this size.");
344         if (keep) resizeKeep_(m,n);
345         else      resize_(m,n);
346         m_actual.setActualSize(m,n);
347     }
348 
349 
350     // addition and subtraction (+= and -=)
351     void addIn(const MatrixHelper<S>&);
352     void addIn(const MatrixHelper<typename CNT<S>::TNeg>&);
353     void subIn(const MatrixHelper<S>&);
354     void subIn(const MatrixHelper<typename CNT<S>::TNeg>&);
355 
356     // Fill all our stored data with copies of the same supplied element.
357     void fillWith(const S* eltp);
358 
359     // We're copying data from a C++ row-oriented matrix into our general
360     // Matrix. In addition to the row ordering, C++ may use different spacing
361     // for elements than Simmatrix does. Lucky we know that value!
362     void copyInByRowsFromCpp(const S* elts);
363 
364     // Scalar multiply (and divide). This is useful no matter what the
365     // element structure and will produce the correct result.
366     void scaleBy(const StdNumber&);
367 
368     // If this is a full or symmetric square matrix with scalar elements,
369     // it can be inverted in place (although that's not the best way to
370     // solve linear equations!).
invertInPlace()371     void invertInPlace() {
372         if (!m_writable)
373             SimTK_THROW1(Exception::OperationNotAllowedOnNonconstReadOnlyView,
374                          "invertInPlace()");
375         if (nrow() != ncol())
376             SimTK_THROW1(Exception::Cant,
377                          "invertInPlace(): matrix must be square");
378         invertInPlace_();
379     }
380 
381     void dump(const char* msg) const;
382 
383         // Bookkeeping //
384 
385 
386     // Dimensions come from the "actual" Matrix character. These are the *logical*
387     // dimensions, in elements (not scalars), and have nothing to do with how much
388     // memory is being used to store these elements.
nrow()389     int       nrow()     const {return m_actual.nrow();}
ncol()390     int       ncol()     const {return m_actual.ncol();}
391 
392     // This is only for 1D vectors; their lengths must fit in an "int". For
393     // a 2D matrix use nelt() to get the total number of elements.
length()394     int length() const {assert(nrow()==1||ncol()==1); return nrow()*ncol();}
395 
nelt()396     ptrdiff_t nelt()     const {return ptrdiff_t(nrow()) * ptrdiff_t(ncol());}
nScalars()397     ptrdiff_t nScalars() const {return nelt()*m_eltSize;}
398 
399     // Does this handle own the data descriptor it points to?
getEltSize()400     int     getEltSize()        const {return m_eltSize;}
getCppEltSize()401     int     getCppEltSize()     const {return m_cppEltSize;}
isWritable()402     bool    isWritable()        const {return m_writable;}
isOwner()403     bool    isOwner()           const {return m_owner;}
isClear()404     bool    isClear()           const {return m_data==0;}
405 
lockHandle()406     void lockHandle()           {m_handleIsLocked=true;}
unlockHandle()407     void unlockHandle()         {m_handleIsLocked=false;}
isHandleLocked()408     bool isHandleLocked() const {return m_handleIsLocked;}
409 
410 
getCharacterCommitment()411     const MatrixCommitment& getCharacterCommitment() const {return m_commitment;}
getMatrixCharacter()412     const MatrixCharacter&  getMatrixCharacter()  const {return m_actual;}
413 
414     // Get a pointer to the raw data viewed various ways.
getData()415     const S*     getData()     const {assert(m_data); return m_data;}
updData()416     S*           updData()           {assert(m_data); return m_data;}
417 
getDataNeg()418     const SNeg*  getDataNeg()  const
419     {   return reinterpret_cast<const SNeg*>(getData()); }
updDataNeg()420     SNeg*        updDataNeg()
421     {   return reinterpret_cast<SNeg*>(updData()); }
getDataHerm()422     const SHerm* getDataHerm() const
423     {   return reinterpret_cast<const SHerm*>(getData()); }
updDataHerm()424     SHerm*       updDataHerm()
425     {   return reinterpret_cast<SHerm*>(updData()); }
426 
427     // Delete the data if necessary, and leave the data pointer null. If this
428     // is not an owner handle, we just clear the pointer. If it is an owner
429     // handle, we check that it isn't locked and if it isn't we'll return the
430     // allocated data to the heap. It is an error to attempt to clear the
431     // data while the handle is locked.
clearData()432     void clearData() {
433         if (m_handleIsLocked) {
434             SimTK_THROW1(Exception::Cant,
435                 "MatrixHelperRep::clearData(): handle is locked.");
436             return;
437         }
438         if (m_owner)
439             deleteAllocatedMemory(m_data);
440         m_data = 0;
441     }
442 
443     // Given a number of elements (not scalars), allocate
444     // just enough memory to hold that many elements in packed storage.
allocateData(ptrdiff_t nelt)445     void allocateData(ptrdiff_t nelt) {
446         assert(nelt >= 0);
447         assert(m_owner && m_data==0);
448         if (m_handleIsLocked) {
449             SimTK_THROW1(Exception::Cant,
450                 "MatrixHelperRep::allocateData(): handle is locked.");
451             return;
452         }
453         m_data = allocateMemory(nelt);
454     }
455 
456     // Given dimensions in number of elements (not scalars), allocate
457     // just enough memory to hold m*n elements in packed storage.
allocateData(int m,int n)458     void allocateData(int m, int n)
459     {   assert(m>=0 && n>=0);
460         allocateData(ptrdiff_t(m) * ptrdiff_t(n)); }
461 
462     // Allocate new heap space to hold nelt densely-packed elements each
463     // composed of m_eltSize scalars of type S. We actually allocate an array of
464     // the underlying Precision type (float or double) to avoid any default
465     // construction of more complicated elements like complex. If we're in Debug
466     // mode, we'll initialize the resulting data to NaN, otherwise we won't
467     // touch it. If nelt is zero we return a null pointer.
allocateMemory(ptrdiff_t nElt)468     S* allocateMemory(ptrdiff_t nElt) const {
469         assert(nElt >= 0);
470         if (nElt==0)
471             return 0;
472         assert(sizeof(S) % sizeof(Precision) == 0);
473         const ptrdiff_t nPrecPerElt = (sizeof(S)/sizeof(Precision))*m_eltSize;
474         const ptrdiff_t nPrec       = nElt * nPrecPerElt;
475         Precision* p = new Precision[nPrec];
476         #ifndef NDEBUG
477             const Precision nan = CNT<Precision>::getNaN();
478             for (ptrdiff_t i=0; i < nPrec; ++i)
479                 p[i] = nan;
480         #endif
481         return reinterpret_cast<S*>(p);
482     }
483     // Allocate enough memory to hold m*n elements. m and n are ints, but their
484     // product may not fit in an int, so we use ptrdiff_t which will be a 64-bit
485     // signed integer on a 64 bit machine.
allocateMemory(int m,int n)486     S* allocateMemory(int m, int n) const
487     {   assert(m>=0 && n>=0);
488         return allocateMemory(ptrdiff_t(m) * ptrdiff_t(n)); }
489 
490     // Use this method to delete help space that you allocated using
491     // allocateNewData() above. We recast it back to the form in which it was
492     // allocated before deleting which will keep the heap system happy and also
493     // prevent the calling of any element destructor that might be present for
494     // the fancier scalar types.
deleteAllocatedMemory(S * mem)495     static void deleteAllocatedMemory(S* mem) {
496         Precision* p = reinterpret_cast<Precision*>(mem);
497         delete[] p;
498     }
499 
500     // Use setData only when there isn't already data in this handle. If this
501     // is an owner handle we're taking over responsibility for the heap space.
setData(S * datap)502     void setData(S* datap) {assert(!m_data); m_data = datap;}
setOwner(bool isOwner)503     void setOwner(bool isOwner) {m_owner=isOwner;}
504 
505     // Single element manipulation: VERY SLOW, use sparingly.
copyElt(S * dest,const S * src)506     void copyElt(S* dest, const S* src) const
507     {   for (int k=0; k<m_eltSize; ++k) dest[k] = src[k]; }
copyAndScaleElt(S * dest,const StdNumber & s,const S * src)508     void copyAndScaleElt(S* dest, const StdNumber& s, const S* src) const
509     {   for (int k=0; k<m_eltSize; ++k) dest[k] = s*src[k]; }
copyAndNegateElt(S * dest,const S * src)510     void copyAndNegateElt(S* dest, const S* src) const
511     {   for (int k=0; k<m_eltSize; ++k) dest[k] = CNT<S>::negate(src[k]); }
copyAndConjugateElt(S * dest,const S * src)512     void copyAndConjugateElt(S* dest, const S* src) const
513     {   for (int k=0; k<m_eltSize; ++k) dest[k] = CNT<S>::transpose(src[k]); }
copyAndNegConjugateElt(S * dest,const S * src)514     void copyAndNegConjugateElt(S* dest, const S* src) const
515     {   for (int k=0; k<m_eltSize; ++k) dest[k] = -CNT<S>::transpose(src[k]); }
516 
fillElt(S * dest,const StdNumber & src)517     void fillElt(S* dest, const StdNumber& src) const
518     {   for (int k=0; k<m_eltSize; ++k) dest[k] = src; }
addToElt(S * dest,const S * src)519     void addToElt(S* dest, const S* src) const
520     {   for (int k=0; k<m_eltSize; ++k) dest[k] += src[k]; }
subFromElt(S * dest,const S * src)521     void subFromElt(S* dest, const S* src) const
522     {   for (int k=0; k<m_eltSize; ++k) dest[k] -= src[k]; }
scaleElt(S * dest,const StdNumber & s)523     void scaleElt(S* dest, const StdNumber& s) const
524     {   for (int k=0; k<m_eltSize; ++k) dest[k] *= s; }
525 
526 protected:
527     //--------------------------------------------------------------------------
528     //                        ABSTRACT INTERFACE
529     // This is the complete set of virtual methods which can be overridden
530     // by classes derived from MatrixHelperRep. All the virtual methods have
531     // names ending in an underscore "_". The base class provides an inline
532     // interface method of the same name but without the underscore. That
533     // method performs operations common to all the implementations, such
534     // as checking arguments and verifying that the handle permits the
535     // operation. It then transfers control to the virtual method, the
536     // various implementations of which do not have to repeat the common
537     // work.
538     //--------------------------------------------------------------------------
539 
540         // DESTRUCTOR
541         // Any concrete class that uses additional heap space must provide
542         // a destructor.
543 
544     virtual ~MatrixHelperRep();
545 
546         // PURE VIRTUALS
547         // All concrete classes must provide implementations.
548 
549     virtual MatrixHelperRep* createDeepCopy_() const = 0;
550 
551     // Make a clone of the current helper, except that the data and
552     // myHandle pointer are left null.
553     virtual This* cloneHelper_() const = 0;
554 
555 
556     virtual This*   createBlockView_(const EltBlock&) = 0;
557     virtual This*   createTransposeView_() = 0;
558     virtual This*   createRegularView_(const EltBlock&, const EltIndexer&) = 0;
559 
560     virtual VectorHelper<S>* createDiagonalView_() = 0;
561     virtual VectorHelper<S>* createColumnView_(int j, int i, int m) = 0;
562     virtual VectorHelper<S>* createRowView_(int i, int j, int n) = 0;
563 
564     virtual bool preferRowOrder_() const = 0;
565     virtual bool hasContiguousData_() const = 0;
566     virtual bool hasRegularData_() const = 0;
567     virtual bool eltIsStored_(int i, int j) const = 0;
568     virtual const S* getElt_(int i, int j) const = 0;
569     virtual S*       updElt_(int i, int j)       = 0;
570     virtual void getAnyElt_(int i, int j, S* value) const = 0;
571 
572         // OPTIONAL FUNCTIONALITY
573         // Optional methods. No default implementation. A derived class can
574         // supply these, but if it doesn't then this functionality will not
575         // be available for matrices which use that class as a helper.
576 
resize_(int m,int n)577     virtual void resize_(int m, int n) {
578         SimTK_THROW1(Exception::Cant,
579             "resize_() not implemented for this kind of matrix");
580     }
resizeKeep_(int m,int n)581     virtual void resizeKeep_(int m, int n) {
582         SimTK_THROW1(Exception::Cant,
583             "resizeKeep_() not implemented for this kind of matrix");
584     }
585 
586     // If this gets called we know the matrix is writable and square.
invertInPlace_()587     virtual void invertInPlace_()  {
588         SimTK_THROW1(Exception::Cant,
589             "invertInPlace_() not implemented for this kind of matrix");
590     }
591 
592         // One-index versions of above two-index methods for use in Vector
593         // helpers.
eltIsStored_(int i)594     virtual bool eltIsStored_(int i) const {
595         SimTK_THROW1(Exception::Cant,
596             "One-index eltIsStored_() not available for 2D matrices");
597         return false;
598     }
getElt_(int i)599     virtual const S* getElt_(int i) const {
600         SimTK_THROW1(Exception::Cant,
601             "One-index getElt_() not available for 2D matrices");
602         return 0;
603     }
604 
updElt_(int i)605     virtual S* updElt_(int i) {
606         SimTK_THROW1(Exception::Cant,
607             "One-index updElt_() not available for 2D matrices");
608         return 0;
609     }
610 
getAnyElt_(int i,S * value)611     virtual void getAnyElt_(int i, S* value) const {
612         SimTK_THROW1(Exception::Cant,
613             "One-index getAnyElt_() not available for 2D matrices");
614     }
615 
resize_(int n)616     virtual void resize_(int n) {
617         SimTK_THROW1(Exception::Cant,
618             "One-index resize_() not available for 2D matrices");
619     }
620 
resizeKeep_(int n)621     virtual void resizeKeep_(int n) {
622         SimTK_THROW1(Exception::Cant,
623             "One-index resizeKeep_() not available for 2D matrices");
624     }
625 
626 
627 
628         // VIRTUALS WITH DEFAULT IMPLEMENTATIONS
629         // This functionality is required of all MatrixHelpers, but there is
630         // a base class default implementation here, slow but functional.
631         // In many cases a concrete class can do much better because of its
632         // intimate knowledge of the data layout; override if you can.
633 
634 
635     // Overridable method to implement copyInFromCompatibleSource().
636     // The default implementation works but is very slow.
copyInFromCompatibleSource_(const MatrixHelperRep<S> & source)637     virtual void copyInFromCompatibleSource_(const MatrixHelperRep<S>& source) {
638         if (preferRowOrder_())
639             for (int i=0; i<nrow(); ++i)
640                 for (int j=0; j<ncol(); ++j) {
641                     if (eltIsStored_(i,j))
642                         copyElt(updElt_(i,j), source.getElt_(i,j));
643                 }
644         else // column order (rows vary fastest)
645             for (int j=0; j<ncol(); ++j)
646                 for (int i=0; i<nrow(); ++i) {
647                     if (eltIsStored_(i,j))
648                         copyElt(updElt_(i,j), source.getElt_(i,j));
649                 }
650     }
651 
652     // Overridable method to implement fillWithScalar().
653     // The default implementation works but is very slow.
fillWithScalar_(const StdNumber & scalar)654     virtual void fillWithScalar_(const StdNumber& scalar) {
655         if (preferRowOrder_())
656             for (int i=0; i<nrow(); ++i)
657                 for (int j=0; j<ncol(); ++j) {
658                     if (eltIsStored_(i,j))
659                         fillElt(updElt_(i,j), scalar);
660                 }
661         else // column order (rows vary fastest)
662             for (int j=0; j<ncol(); ++j)
663                 for (int i=0; i<nrow(); ++i) {
664                     if (eltIsStored_(i,j))
665                         fillElt(updElt_(i,j), scalar);
666                 }
667     }
668 
colSum_(int j,S * csum)669     virtual void colSum_(int j, S* csum) const {
670         fillElt(csum, 0);
671         for (int i=0; i < nrow(); ++i)
672             addToElt(csum, getElt_(i,j));
673     }
674 
rowSum_(int i,S * rsum)675     virtual void rowSum_(int i, S* rsum) const {
676         fillElt(rsum, 0);
677         for (int j=0; j < ncol(); ++j)
678             addToElt(rsum, getElt_(i,j));
679     }
sum_(S * esum)680     virtual void sum_(S* esum) const {
681         fillElt(esum, 0);
682         S* tsum = new S[m_eltSize]; // temporary variable for row or col sums
683         if (preferRowOrder_()) // i.e., row sums are cheaper
684             for (int i=0; i < nrow(); ++i)
685             {   rowSum_(i, tsum); addToElt(esum, tsum); }
686         else // col sums are cheaper
687             for (int j=0; j < ncol(); ++j)
688             {   colSum_(j, tsum); addToElt(esum, tsum); }
689         delete[] tsum;
690     }
691 
692 
693 
694 protected:
MatrixHelperRep(int esz,int cppesz)695     MatrixHelperRep(int esz, int cppesz)
696     :   m_data(0), m_actual(), m_writable(false),
697         m_eltSize(esz), m_cppEltSize(cppesz),
698         m_canBeOwner(true), m_owner(false),
699         m_handleIsLocked(false), m_commitment(), m_handle(0) {}
700 
MatrixHelperRep(int esz,int cppesz,const MatrixCommitment & commitment)701     MatrixHelperRep(int esz, int cppesz, const MatrixCommitment& commitment)
702     :   m_data(0), m_actual(), m_writable(false),
703         m_eltSize(esz), m_cppEltSize(cppesz),
704         m_canBeOwner(true), m_owner(false),
705         m_handleIsLocked(false), m_commitment(commitment), m_handle(0) {}
706 
707     // Copy constructor copies just the base class members, and *not* the data.
708     // We assume we don't have write access and that we aren't going to be
709     // the data owner. The handle character commitment and actual character are
710     // copied from the source, but may need to be changed by the caller.
MatrixHelperRep(const MatrixHelperRep & src)711     MatrixHelperRep(const MatrixHelperRep& src)
712     :   m_data(0), m_actual(src.m_actual), m_writable(false),
713         m_eltSize(src.m_eltSize), m_cppEltSize(src.m_cppEltSize),
714         m_canBeOwner(true), m_owner(false),
715         m_handleIsLocked(false), m_commitment(src.m_commitment), m_handle(0) {}
716 
717         // Properties of the actual matrix //
718 
719     // Raw memory holding the elements of this matrix, as an array of Scalars.
720     S*                  m_data;
721     // The actual characteristics of the matrix as seen through this handle.
722     MatrixCharacter     m_actual;
723     // Whether we have write access to the data through the pointer above.
724     // If not, the data is treated as though it were "const".
725     bool                m_writable;
726 
727         // Properties of the handle //
728 
729     const int           m_eltSize;
730     const int           m_cppEltSize;
731 
732     bool                m_canBeOwner;
733     bool                m_owner;
734     bool                m_handleIsLocked; // temporarily prevent resize of owner
735 
736     /// All commitments are by default "Uncommitted", meaning we're happy to
737     /// take on matrices in any format, provided that the element types match.
738     /// Otherwise the settings here limit what we'll find acceptable as actual data.
739     /// Note: don't look at these to find out anything about the current
740     /// matrix. These are used only when the matrix is being created or
741     /// changed structurally.
742     MatrixCommitment    m_commitment;
743 
744 
745 private:
746     // Point back to the owner handle of this rep.
747     MatrixHelper<S>*    m_handle;
748 
749     // suppress assignment
750     MatrixHelperRep& operator=(const MatrixHelperRep&);
751 
752     // For use by our friend, MatrixHelper<S>.
setMyHandle(MatrixHelper<S> & h)753     void                   setMyHandle(MatrixHelper<S>& h) {m_handle = &h;}
getMyHandle()754     const MatrixHelper<S>& getMyHandle() const {assert(m_handle); return *m_handle;}
clearMyHandle()755     void                   clearMyHandle() {m_handle=0;}
756 
757     // ============================= Unimplemented =============================
758     // See comment in MatrixBase::matmul for an explanation.
759     template <class SA, class SB>
760     void matmul(const StdNumber& beta,   // applied to 'this'
761                 const StdNumber& alpha, const MatrixHelper<SA>& A, const MatrixHelper<SB>& B);
762 
763 
764 friend class MatrixHelperRep<typename CNT<S>::TNeg>;
765 friend class MatrixHelperRep<typename CNT<S>::THerm>;
766 friend class MatrixHelper<S>;
767 };
768 
769 
770 } // namespace SimTK
771 
772 
773 #endif // SimTK_SimTKCOMMON_MATRIX_HELPER_REP_H_
774