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