1 #ifndef SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_H_
2 #define SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_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) 2008-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 "MatrixHelperRep.h"
31
32 #include <cstddef>
33
34 namespace SimTK {
35
36
37 /*-------------------------------- VectorHelper --------------------------------
38
39 This abstract class represents a 1d matrix, a.k.a. a Vector or a RowVector
40 (covector). However, its use is not limited to the SimTK::Vector and and
41 SimTK::RowVector classes; any skinny Matrix or skinny slice of a fatter
42 Matrix might use a VectorHelper since it provides faster access to memory
43 than a generic 2d helper would.
44
45 Most operations don't care whether this is a column or a row,
46 however we have to know so that we can support matrix operations on the
47 vector when necessary. For example, getElt(i,j) still has to work (as long
48 as the appropriate one of i or j is 0), even though getElt(i) is more
49 efficient and direction-agnostic.
50
51 The most common layout is that all elements are stored, either consecutively
52 in memory or with a regular stride. Vectors constructed from a larger pool
53 of stored data via indexing are also important and need to be implemented
54 efficiently.
55
56 TODO: However, there are several other important
57 layouts that arise most commonly from row and column selections performed
58 on non-full matrices, like triangular or symmetric matrices. Supporting such
59 selections allows simple (if inefficient) implementations of operations on
60 mixed types of matrices by breaking them into row and column operations.
61 Vectors selected in this way can be repetitions of the same element (often
62 zero), negations or conjugations of stored elements, and may sometimes have
63 a single "distinguished" element whose value is known (this occurs for example
64 when crossing a non-stored unit diagonal).
65
66 TODO: Finally, we allow a Vector to be formed of a composition of smaller
67 Vectors. Then the whole vector can be accessed by element or more efficiently
68 by segments.
69 ------------------------------------------------------------------------------*/
70 template <class S>
71 class VectorHelper : public MatrixHelperRep<S> {
72 typedef VectorHelper<S> This;
73 typedef MatrixHelperRep<S> Base;
74 typedef typename CNT<S>::TNeg SNeg;
75 typedef typename CNT<S>::THerm SHerm;
76 typedef VectorHelper<SNeg> ThisNeg;
77 typedef VectorHelper<SHerm> ThisHerm;
78 public:
VectorHelper(int esz,int cppesz,bool isRow)79 VectorHelper(int esz, int cppesz, bool isRow)
80 : Base(esz,cppesz),m_row(isRow)
81 {
82 }
83
84
85 // A deep copy of a Vector will always return another Vector, so we'll
86 // change the return type here.
87 virtual This* createDeepCopy_() const = 0;
88
89 // Just changing the return type here.
90 virtual This* cloneHelper_() const = 0;
91
preferRowOrder_()92 bool preferRowOrder_() const {return m_row;}
93
94
95 // Forward the two-index operations to the appropriate one-index operation.
96
97 // One of the indices must be zero.
eltIsStored_(int i,int j)98 bool eltIsStored_(int i, int j) const {return eltIsStored_(i+j);}
getElt_(int i,int j)99 const S* getElt_ (int i, int j) const {return getElt_(i+j);}
updElt_(int i,int j)100 S* updElt_ (int i, int j) {return updElt_(i+j);}
getAnyElt_(int i,int j,S * value)101 void getAnyElt_ (int i, int j, S* value) const {getAnyElt_(i+j, value);}
102
103
104 // These are mandatory for vectors.
105 virtual bool eltIsStored_(int i) const = 0;
106 virtual const S* getElt_ (int i) const = 0;
107 virtual S* updElt_ (int i) = 0;
108 virtual void getAnyElt_ (int i, S* value) const = 0;
109
110 // (Positional) transpose view is identical to this one except that we'll
111 // call it a row rather than a column or vice versa.
createTransposeView_()112 This* createTransposeView_() {
113 This* p = cloneHelper_();
114 p->m_data = this->m_data;
115 p->m_row = !m_row;
116 p->m_actual.updStorage().setOrder
117 (p->m_row ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder);
118 return p;
119 }
120
121 // Not sure if this should ever be supported.
createRegularView_(const EltBlock &,const EltIndexer &)122 MatrixHelperRep<S>* createRegularView_(const EltBlock&, const EltIndexer&) {
123 SimTK_ERRCHK_ALWAYS(!"not implemented", "VectorHelper::createRegularView_()", "not implemented");
124 return 0;
125 }
126
127
128 protected:
129 bool m_row; // true if this should be a row when treated as a matrix
130 };
131
132 //----------------------------- FullVectorHelper -------------------------------
133 /// All elements of the Vector are stored. The simplest form of this has the
134 /// data pointing to the Vector's 0th element with all the rest following
135 /// consecutively in memory. Derived classes add stride or indexing and
136 /// optimize for scalar elements.
137 //------------------------------------------------------------------------------
138 template <class S>
139 class FullVectorHelper : public VectorHelper<S> {
140 typedef FullVectorHelper<S> This;
141 typedef VectorHelper<S> Base;
142 public:
FullVectorHelper(int esz,int cppesz,bool isRow)143 FullVectorHelper(int esz, int cppesz, bool isRow)
144 : Base(esz,cppesz,isRow) {}
145
146 // This will always produce a 1-element "contiguous" column vector.
147 VectorHelper<S>* createDiagonalView_();
148
149 // Source matches size and shape of this row or column.
copyInFromCompatibleSource_(const MatrixHelperRep<S> & source)150 void copyInFromCompatibleSource_(const MatrixHelperRep<S>& source) {
151 if (this->getEltSize() == 1) {
152 // The elements are scalars, so we can copy them directly.
153 if (this->nrow() == 1) // a row vector
154 for (int j=0; j<this->ncol(); ++j)
155 *this->updElt_(j) = *source.getElt(0,j);
156 else // a column vector
157 for (int i=0; i<this->nrow(); ++i)
158 *this->updElt_(i) = *source.getElt(i,0);
159 }
160 else { // Here the elements are not scalars.
161 if (this->nrow() == 1) // a row vector
162 for (int j=0; j<this->ncol(); ++j)
163 this->copyElt(this->updElt_(j), source.getElt(0,j));
164 else // a column vector
165 for (int i=0; i<this->nrow(); ++i)
166 this->copyElt(this->updElt_(i), source.getElt(i,0));
167 }
168 }
169 };
170
171 //-------------------------- ContiguousVectorHelper ----------------------------
172 /// All elements of the Vector are stored, and they are contiguous in memory.
173 /// This class handles general elements; a derived class handles the special
174 /// case of scalar elements by overriding some of the methods for speed.
175 //------------------------------------------------------------------------------
176 template <class S>
177 class ContiguousVectorHelper : public FullVectorHelper<S> {
178 typedef ContiguousVectorHelper<S> This;
179 typedef FullVectorHelper<S> Base;
180 public:
181 // Allocate our own space.
ContiguousVectorHelper(int esz,int cppesz,int n,bool isRow)182 ContiguousVectorHelper(int esz, int cppesz, int n, bool isRow)
183 : Base(esz,cppesz,isRow)
184 {
185 this->m_owner = true;
186 this->m_writable = true;
187 this->allocateData(n);
188 this->m_actual.setStructure(MatrixStructure::Matrix1d);
189 this->m_actual.setStorage(
190 MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
191 isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
192 MatrixStorage::NoDiag));
193 this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
194 }
195
196 // Use someone else's memory, which we assume to be the right size.
197 // We take care of stride elsewhere.
ContiguousVectorHelper(int esz,int cppesz,int n,bool isRow,const S * shared,bool canWrite)198 ContiguousVectorHelper(int esz, int cppesz, int n, bool isRow,
199 const S* shared, bool canWrite)
200 : Base(esz,cppesz,isRow)
201 {
202 this->m_owner = false;
203 this->m_writable = canWrite;
204 this->setData(const_cast<S*>(shared));
205 this->m_actual.setStructure(MatrixStructure::Matrix1d);
206 this->m_actual.setStorage(
207 MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
208 isRow ? MatrixStorage::RowOrder
209 : MatrixStorage::ColumnOrder,
210 MatrixStorage::NoDiag));
211 // apparent size; sets Outline
212 this->m_actual.setActualSize(isRow?1:n, isRow?n:1);
213 }
214
cloneHelper_()215 virtual This* cloneHelper_() const {return new This(*this);}
216
217 // A block view of a full, contiguous row/column is either
218 // (1) a smaller full, contiguous row/column, or (2) a zero-width
219 // slice. In the latter case it may no longer be a row or column so we
220 // have to switch helper types to a Full mX0 or 0Xn matrix.
createBlockView_(const EltBlock & block)221 MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
222 const int m=block.nrow(), n=block.ncol();
223 if (m && n) { // normal case; same orientation but smaller
224 This* p = cloneHelper_();
225 p->m_data = updElt_(block.row0() + block.col0());
226 return p;
227 }
228 // Here we know at least one of m or n is zero.
229 if (m==1 || n==1) { // i.e., (1,0) or (0,1)
230 This* p = cloneHelper_(); // still 1d "contiguous"
231 p->m_data = 0;
232 p->m_row = (m==1); // regardless of what it was
233 return p;
234 }
235
236 // Here one of m,n is zero and the other is > 1; not 1d any more.
237 RegularFullHelper<S>* p = 0;
238 if (this->getEltSize()==1)
239 p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
240 else p = new FullColOrderEltHelper<S>(this->getEltSize(),
241 this->getCppEltSize(),
242 m,n,m,(S*)0,this->m_writable);
243 // Called shared-data constructor so p is non-owner.
244 return p;
245 }
246
247 // This creates an mx1 column vector.
createColumnView_(int j,int i,int m)248 This* createColumnView_(int j, int i, int m) {
249 This* p = cloneHelper_();
250 p->m_data = m > 0 ? updElt_(i+j) : 0;
251 p->m_row = false; // even if this was a row
252 p->m_actual.updStorage().setOrder(MatrixStorage::ColumnOrder);
253 return p;
254 }
255
256 // This creates a 1xn row vector.
createRowView_(int i,int j,int n)257 This* createRowView_(int i, int j, int n) {
258 This* p = cloneHelper_();
259 p->m_data = n > 0 ? updElt_(i+j) : 0;
260 p->m_row = true; // even if this was a column
261 p->m_actual.updStorage().setOrder(MatrixStorage::RowOrder);
262 return p;
263 }
264
265 // Override for strided or indexed data.
hasContiguousData_()266 virtual bool hasContiguousData_() const {return true;}
267 // Override for indexed data.
hasRegularData_()268 virtual bool hasRegularData_() const {return true;}
269
getElt_(int i)270 const S* getElt_ (int i) const {return this->m_data + i*this->m_eltSize;}
updElt_(int i)271 S* updElt_ (int i) {return this->m_data + i*this->m_eltSize;}
eltIsStored_(int i)272 bool eltIsStored_(int i) const {return true;}
273
274 // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)275 void getAnyElt_(int i, S* value) const
276 { this->copyElt(value, getElt_(i)); }
277
278 // OK for any size elements that are packed contiguously.
createDeepCopy_()279 This* createDeepCopy_() const {
280 This* p = cloneHelper_();
281 p->m_writable = true;
282 p->m_owner = true;
283 p->allocateData(this->nelt());
284 std::copy(this->m_data, this->m_data +
285 this->length()*this->m_eltSize, p->m_data);
286 return p;
287 }
288
289 // One of the lengths must be 1.
resize_(int m,int n)290 void resize_ (int m, int n) {resize_(m*n);}
resizeKeep_(int m,int n)291 void resizeKeep_ (int m, int n) {resizeKeep_(m*n);}
292
293 // OK for any size elements.
resize_(int n)294 void resize_(int n) {
295 this->clearData();
296 this->allocateData(n);
297 }
298
299 // OK for any size elements that are packed contiguously.
resizeKeep_(int n)300 void resizeKeep_(int n) {
301 S* const newData = this->allocateMemory(n);
302 const int nToCopy = std::min(n, this->length());
303 std::copy(this->m_data, this->m_data +
304 nToCopy*this->m_eltSize, newData);
305 this->clearData();
306 this->setData(newData);
307 }
308
copyInFromCompatibleSource_(const MatrixHelperRep<S> & source)309 void copyInFromCompatibleSource_(const MatrixHelperRep<S>& source) {
310 if (source.hasContiguousData() && this->nScalars())
311 std::copy(source.getElt(0,0), source.getElt(0,0) +
312 this->nScalars(), this->m_data);
313 else
314 FullVectorHelper<S>::copyInFromCompatibleSource_(source);
315 }
316 };
317
318
319 //----------------------- ContiguousVectorScalarHelper -------------------------
320 /// All elements of the Vector are stored, and they are contiguous in memory,
321 /// and the elements are scalars. This inherits most functionality from the
322 /// contiguous general-element case.
323 //------------------------------------------------------------------------------
324 template <class S>
325 class ContiguousVectorScalarHelper : public ContiguousVectorHelper<S> {
326 typedef ContiguousVectorScalarHelper<S> This;
327 typedef ContiguousVectorHelper<S> Base;
328 public:
329 // Allocate our own space.
ContiguousVectorScalarHelper(int n,bool isRow)330 ContiguousVectorScalarHelper(int n, bool isRow) : Base(1,1,n,isRow) {}
331 // Use someone else's memory.
ContiguousVectorScalarHelper(int n,bool isRow,const S * shared,bool canWrite)332 ContiguousVectorScalarHelper(int n, bool isRow, const S* shared, bool canWrite)
333 : Base(1,1,n,isRow,shared,canWrite) {}
334
cloneHelper_()335 This* cloneHelper_() const {return new This(*this);}
336
getElt_(int i)337 const S* getElt_ (int i) const {return this->m_data + i;}
updElt_(int i)338 S* updElt_ (int i) {return this->m_data + i;}
339
340 // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)341 void getAnyElt_(int i, S* value) const {*value = *getElt_(i);}
342 };
343
344
345 template <class S> inline VectorHelper<S>*
createDiagonalView_()346 FullVectorHelper<S>::createDiagonalView_() {
347 VectorHelper<S>* p = 0;
348 const int nDiags = std::min(this->length(), 1); // 0 or 1
349 S* data = nDiags ? this->updElt_(0) : 0;
350
351 p = (this->m_eltSize==1)
352 ? new ContiguousVectorScalarHelper<S>(nDiags, false, data, false)
353 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, nDiags,
354 false, data, false);
355 return p;
356 }
357
358
359 //---------------------------- StridedVectorHelper -----------------------------
360 /// This is a vector with regularly-spaced but non-contiguous elements. This
361 /// is only for views so does not include an implementation for resizing.
362 //------------------------------------------------------------------------------
363 template <class S>
364 class StridedVectorHelper : public FullVectorHelper<S> {
365 typedef StridedVectorHelper<S> This;
366 typedef FullVectorHelper<S> Base;
367 public:
368 /// Use someone else's memory, which we assume to be the right size. Note
369 /// that stride is given in elements, with stride==1 meaning the elements
370 /// are packed contiguously, in which case this is the wrong helper class to use.
StridedVectorHelper(int esz,int cppesz,int n,bool isRow,int strideInElements,const S * shared,bool canWrite)371 StridedVectorHelper(int esz, int cppesz, int n, bool isRow,
372 int strideInElements, const S* shared, bool canWrite)
373 : Base(esz,cppesz,isRow), m_spacing((ptrdiff_t)strideInElements * esz)
374 {
375 SimTK_ASSERT1(strideInElements >= 2,
376 "StridedVectorHelper::ctor(): illegal stride %d", strideInElements);
377 this->m_owner = false;
378 this->m_writable = canWrite;
379 this->setData(const_cast<S*>(shared));
380 this->m_actual.setStructure(MatrixStructure::Matrix1d);
381 this->m_actual.setStorage(
382 MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
383 isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
384 MatrixStorage::NoDiag));
385 this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
386 }
387
cloneHelper_()388 virtual This* cloneHelper_() const {return new This(*this);}
389
390 // A block view of a strided vector is usually a smaller vector with
391 // identical stride. No stride needed if there are fewer than two
392 // elements, though. Also, this could be an mX0 or 0Xn slice which is
393 // no longer a Vector unless m==1 or n==1.
createBlockView_(const EltBlock & block)394 MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
395 const int m=block.nrow(), n=block.ncol();
396 if ((m==0 && n!=1) || (n==0 && m!=1)) {
397 // One or both dimensions is 0 and the other is not 1, so this
398 // is no longer a 1d object.
399 RegularFullHelper<S>* p = 0;
400 if (this->getEltSize()==1)
401 p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
402 else p = new FullColOrderEltHelper<S>(this->getEltSize(),
403 this->getCppEltSize(),
404 m,n,m,(S*)0,this->m_writable);
405 // Called shared-data constructor so p is non-owner.
406 return p;
407 }
408
409 // At least one of m,n is a 1. Could still be 1x0 or 0x1.
410
411 VectorHelper<S>* p = 0;
412 const int start = block.row0() + block.col0(); // one of those is zero
413 const int length = m*n; // one of those is one
414 S* data = length ? updElt_(start) : 0;
415
416 if (length <= 1) {
417 p = (this->m_eltSize==1)
418 ? new ContiguousVectorScalarHelper<S>(length, false, data, false)
419 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, length,
420 false, data, false);
421 // called a shared-data constructor, so p is non-owner
422 return p;
423 }
424
425 p = cloneHelper_();
426 p->setData(data);
427 return p;
428 }
429
430 // Row and column view are like block view but without the possibility
431 // of a non-Vector result.
createColumnView_(int j,int i,int m)432 VectorHelper<S>* createColumnView_(int j, int i, int m) {
433 VectorHelper<S>* p = 0;
434 const int start = i+j; // one of those is zero
435 S* data = m ? updElt_(start) : 0;
436
437 if (m <= 1) {
438 p = (this->m_eltSize==1)
439 ? new ContiguousVectorScalarHelper<S>(m, false, data, false)
440 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, m,
441 false, data, false);
442 return p;
443 }
444
445 // length is > 1 so this must already be a column
446 assert(j==0);
447 p = cloneHelper_();
448 p->setData(data);
449 return p;
450 }
451
createRowView_(int i,int j,int n)452 VectorHelper<S>* createRowView_(int i, int j, int n) {
453 VectorHelper<S>* p = 0;
454 const int start = i+j; // one of those is zero
455 S* data = n ? updElt_(start) : 0;
456
457 if (n <= 1) {
458 p = (this->m_eltSize==1)
459 ? new ContiguousVectorScalarHelper<S>(n, true, data, false)
460 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, n,
461 true, data, false);
462 return p;
463 }
464
465 // length is > 1 so this must already be a row
466 assert(i==0);
467 p = cloneHelper_();
468 p->setData(data);
469 return p;
470 }
471
472
hasContiguousData_()473 bool hasContiguousData_() const {return false;}
hasRegularData_()474 bool hasRegularData_() const {return true;}
475
eltIsStored_(int i)476 bool eltIsStored_(int i) const {return true;}
getElt_(int i)477 const S* getElt_ (int i) const {return this->m_data + i*m_spacing;}
updElt_(int i)478 S* updElt_ (int i) {return this->m_data + i*m_spacing;}
479
480 // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)481 void getAnyElt_(int i, S* value) const
482 { this->copyElt(value, getElt_(i)); }
483
484 /// A deep copy of a strided vector produces a contiguous (stride==1)
485 /// vector containing the same number of elements.
createDeepCopy_()486 FullVectorHelper<S>* createDeepCopy_() const {
487 ContiguousVectorHelper<S>* p =
488 new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
489 this->length(), this->m_row);
490 for (int i=0; i < this->length(); ++i)
491 this->copyElt(p->updData() + i*this->m_eltSize, this->getData() + i*m_spacing);
492 return p;
493 }
494
495 protected:
496 ptrdiff_t m_spacing; // m_eltsize*stride (spacing in scalars)
497 };
498
499
500 //------------------------- StridedVectorScalarHelper --------------------------
501 /// This is a vector with regularly-spaced but non-contiguous elements, where
502 /// the elements are scalars. Most functionality is inherited from the general-
503 /// element case but we specialize a few methods here for speed. This
504 /// is only for views so does not include an implementation for resizing.
505 //------------------------------------------------------------------------------
506 template <class S>
507 class StridedVectorScalarHelper : public StridedVectorHelper<S> {
508 typedef StridedVectorScalarHelper<S> This;
509 typedef StridedVectorHelper<S> Base;
510 public:
511 /// Use someone else's memory, which we assume to be the right size. Note
512 /// that stride is given in elements, with stride==1 meaning the elements
513 /// are packed contiguously, in which case this is the wrong helper class to use.
StridedVectorScalarHelper(int n,bool isRow,int stride,const S * shared,bool canWrite)514 StridedVectorScalarHelper(int n, bool isRow, int stride, const S* shared, bool canWrite)
515 : Base(1,1,n,isRow,stride,shared,canWrite) {}
516
cloneHelper_()517 This* cloneHelper_() const {return new This(*this);}
518
519 // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)520 void getAnyElt_(int i, S* value) const {*value = *this->getElt_(i);}
521
522 /// A deep copy of a strided vector produces a contiguous (stride==1)
523 /// vector containing the same number of elements.
createDeepCopy_()524 FullVectorHelper<S>* createDeepCopy_() const {
525 ContiguousVectorScalarHelper<S>* p =
526 new ContiguousVectorScalarHelper<S>(this->length(), this->m_row);
527 const int nToCopy = this->length();
528 for (int i=0; i < nToCopy; ++i)
529 p->updData()[i] = this->getData()[i*this->m_spacing];
530 return p;
531 }
532 };
533
534 //--------------------------- IndexedVectorHelper ------------------------------
535 /// All elements of the Vector are stored, but they are selected irregularly
536 /// from the available data. This is only for views so does not include an
537 /// implementation for resizing, and there is no constructor for data allocation.
538 /// This class handles general elements; a derived class handles the special
539 /// case of scalar elements by overriding some of the methods for speed.
540 /// TODO: we only allow 32 bit indices, although 64 bit indices
541 /// are possible (just need another helper class for "long long" indices).
542 //------------------------------------------------------------------------------
543 template <class S>
544 class IndexedVectorHelper : public FullVectorHelper<S> {
545 typedef IndexedVectorHelper<S> This;
546 typedef FullVectorHelper<S> Base;
547 public:
548 // Use someone else's memory, which we assume to be the right size. Here the
549 // indices must be in terms of elements. We'll store them internally in terms
550 // of scalars instead. We insist here that the indices are all nonnegative and
551 // in monotonically increasing order.
IndexedVectorHelper(int esz,int cppesz,int n,bool isRow,const int * eltIndices,const S * shared,bool canWrite)552 IndexedVectorHelper(int esz, int cppesz, int n, bool isRow,
553 const int* eltIndices, const S* shared, bool canWrite)
554 : Base(esz,cppesz,isRow), m_scalarIndices(0)
555 {
556 this->m_owner = false;
557 this->m_writable = canWrite;
558 this->setData(const_cast<S*>(shared));
559 this->m_actual.setStructure(MatrixStructure::Matrix1d);
560 this->m_actual.setStorage(
561 MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
562 isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
563 MatrixStorage::NoDiag));
564 this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
565
566 if (n) {
567 m_scalarIndices = new int[n];
568 for (int i=0; i<n; ++i) {
569 SimTK_ERRCHK(i==0 || eltIndices[i]>eltIndices[i-1], "IndexedVectorHelper::ctor()",
570 "Indices must be in monotonically ascending order.");
571 m_scalarIndices[i] = this->m_eltSize*eltIndices[i];
572 }
573 }
574 }
575
576 // Copy constructor must copy indices.
IndexedVectorHelper(const This & src)577 IndexedVectorHelper(const This& src) : Base(src), m_scalarIndices(0) {
578 if (src.length()) {
579 m_scalarIndices = new int[src.length()];
580 std::copy(src.m_scalarIndices, src.m_scalarIndices +
581 src.length(), m_scalarIndices);
582 }
583 }
584
585
586 // (Virtual) destructor must delete indices.
~IndexedVectorHelper()587 ~IndexedVectorHelper() {delete[] m_scalarIndices;}
588
589 // Note that cloning the helper also copies the indices.
cloneHelper_()590 virtual This* cloneHelper_() const {return new This(*this);}
591
592 // A block view of an indexed vector is a shorter indexed vector.
593 // Also, this could be an mX0 or 0Xn slice which is
594 // no longer a Vector unless m==1 or n==1.
createBlockView_(const EltBlock & block)595 MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
596 const int m=block.nrow(), n=block.ncol();
597 if ((m==0 && n!=1) || (n==0 && m!=1)) {
598 // One or both dimensions is 0 and the other is not 1, so this
599 // is no longer a 1d object.
600 RegularFullHelper<S>* p = 0;
601 if (this->getEltSize()==1)
602 p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
603 else p = new FullColOrderEltHelper<S>(this->getEltSize(),
604 this->getCppEltSize(),
605 m,n,m,(S*)0,this->m_writable);
606 // Called a shared-data constructor so p is non-owner.
607 return p;
608 }
609
610 // At least one of the dimensions is a 1. Could still be 1x0 or 0x1.
611
612 const int start = block.row0() + block.col0(); // one of these is zero
613 const int length = block.nrow()*block.ncol(); // one of these is 1
614
615 if (length <= 1) {
616 // No need for indices; this is contiguous now.
617 S* data = length ? updElt_(start) : 0;
618 ContiguousVectorHelper<S>* p = (this->m_eltSize==1)
619 ? new ContiguousVectorScalarHelper<S>
620 (length, false, data, false)
621 : new ContiguousVectorHelper<S>
622 (this->m_eltSize, this->m_cppEltSize, length,
623 false, data, false);
624 // Called a shared-data constructor so p is non-owner.
625 return p;
626 }
627
628 // Still an indexed vector.
629
630 This* p = new This(*this, true); // don't copy the indices
631 p->m_data = this->m_data;
632 p->m_scalarIndices = new int[length];
633 std::copy(m_scalarIndices+start, m_scalarIndices+start +
634 length, p->m_scalarIndices);
635 return p;
636 }
637
createColumnView_(int j,int i,int m)638 VectorHelper<S>* createColumnView_(int j, int i, int m) {
639 if (m <= 1) {
640 S* data = m ? updElt_(i+j) : 0; // one of i or j is 0
641 VectorHelper<S>* p = (this->m_eltSize==1)
642 ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(m, false, data, false)
643 : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
644 m, false, data, false);
645 return p;
646 }
647
648 // This must already be a column or we couldn't make a >1 element column here.
649 assert(j==0);
650 This* p = new This(*this, true); // don't copy the indices
651 p->setData(this->m_data); // leaving the indices the same, so data starts at 0
652 p->m_scalarIndices = new int[m];
653 std::copy(m_scalarIndices+i, m_scalarIndices+i+m, p->m_scalarIndices);
654 return p;
655 }
656
657
createRowView_(int i,int j,int n)658 VectorHelper<S>* createRowView_(int i, int j, int n) {
659 if (n<= 1) {
660 S* data = n ? updElt_(i+j) : 0; // one of i or j is 0
661 VectorHelper<S>* p = (this->m_eltSize==1)
662 ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(n, true, data, false)
663 : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
664 n, true, data, false);
665 return p;
666 }
667
668 // This must already be a row or we couldn't make a >1 element row here.
669 assert(i==0);
670 This* p = new This(*this, true); // don't copy the indices
671 p->setData(this->m_data); // leaving the indices the same, so data starts at 0
672 p->m_scalarIndices = new int[n];
673 std::copy(m_scalarIndices+j, m_scalarIndices+j+n, p->m_scalarIndices);
674 return p;
675 }
676
hasContiguousData_()677 bool hasContiguousData_() const {return false;}
hasRegularData_()678 bool hasRegularData_() const {return false;}
679
getElt_(int i)680 const S* getElt_ (int i) const {return this->m_data + m_scalarIndices[i];}
updElt_(int i)681 S* updElt_ (int i) {return this->m_data + m_scalarIndices[i];}
eltIsStored_(int i)682 bool eltIsStored_(int i) const {return true;}
683
684 // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)685 void getAnyElt_(int i, S* value) const
686 { this->copyElt(value, getElt_(i)); }
687
688 // A deep copy of an indexed vector produces a contiguous, non-indexed vector.
createDeepCopy_()689 ContiguousVectorHelper<S>* createDeepCopy_() const {
690 ContiguousVectorHelper<S>* p =
691 new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
692 this->length(), this->m_row);
693 for (int i=0; i<this->length(); ++i)
694 this->copyElt(p->updElt_(i), getElt_(i));
695 return p;
696 }
697
698 protected:
699 int* m_scalarIndices;
700
701 private:
702 // This is like a copy constructor, but it does not copy the indices. The second
703 // parameter is a dummy.
IndexedVectorHelper(const This & src,bool)704 IndexedVectorHelper(const This& src, bool) : Base(src), m_scalarIndices(0) {}
705 };
706
707
708
709 } // namespace SimTK
710
711
712 #endif // SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_H_
713