1 /* -------------------------------------------------------------------------- *
2  *                       Simbody(tm): SimTKcommon                             *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2005-15 Stanford University and the Authors.        *
10  * Authors: Michael Sherman                                                   *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 // Avoid annoying deprecated warnings regarding std::copy during instantiations.
25 #ifdef _MSC_VER
26 #pragma warning(disable:4996)
27 #endif
28 
29 #include "SimTKcommon/Scalar.h"
30 #include "SimTKcommon/SmallMatrix.h"
31 #include "SimTKcommon/TemplatizedLapack.h"
32 
33 #include "SimTKcommon/internal/MatrixHelper.h"
34 #include "SimTKcommon/internal/MatrixCharacteristics.h"
35 
36 #include "MatrixHelperRep.h"
37 #include "MatrixHelperRep_Full.h"
38 #include "MatrixHelperRep_Tri.h"
39 #include "MatrixHelperRep_Vector.h"
40 
41 #include <iostream>
42 #include <cstdio>
43 
44 namespace SimTK {
45 
46 
47 //----------------------------- MATRIX HELPER ----------------------------------
48 //
49 // Implementations of MatrixHelper methods. Most are just pass throughs to
50 // MatrixHelperRep, but some may involve replacing the current MatrixHelperRep
51 // with a different one.
52 //------------------------------------------------------------------------------
53 
54 template <class S> void
deleteRepIfOwner()55 MatrixHelper<S>::deleteRepIfOwner() {
56     if (rep && &rep->getMyHandle() == this)
57         delete rep;
58     rep=0;
59 }
60 
61 // Rep replacement. Delete the existing rep if we're the owner of it.
62 // We are going to be the owner of the new rep regardless.
63 template <class S> void
replaceRep(MatrixHelperRep<S> * hrep)64 MatrixHelper<S>::replaceRep(MatrixHelperRep<S>* hrep) {
65     deleteRepIfOwner();
66     rep=hrep;
67     rep->setMyHandle(*this);
68 }
69 
70 // Space-stealing constructor. We're going to take over ownership
71 // of this rep.
72 template <class S>
MatrixHelper(MatrixHelperRep<S> * hrep)73 MatrixHelper<S>::MatrixHelper(MatrixHelperRep<S>* hrep) : rep(hrep)
74 {   if (rep) rep->setMyHandle(*this); }
75 
76 template <class S> const MatrixCommitment&
getCharacterCommitment() const77 MatrixHelper<S>::getCharacterCommitment() const
78 {   return getRep().getCharacterCommitment(); }
79 
80 template <class S> const MatrixCharacter&
getMatrixCharacter() const81 MatrixHelper<S>::getMatrixCharacter() const
82 {   return getRep().getMatrixCharacter(); }
83 
84 
85 
86 // This is the constructor for a Matrix in which only the handle commitment has been
87 // supplied. The allocated matrix will have the smallest size that satisfies the
88 // commitment, typically 0x0 or 0x1.
89 template <class S>
MatrixHelper(int esz,int cppEsz,const MatrixCommitment & mc)90 MatrixHelper<S>::MatrixHelper(int esz, int cppEsz, const MatrixCommitment& mc) : rep(0) {
91     // Determine the best actual matrix to allocate to satisfy this commitment.
92     const MatrixCharacter actual = mc.calcDefaultCharacter(0,0);
93 
94     rep = MatrixHelperRep<S>::createOwnerMatrixHelperRep(esz, cppEsz, actual);
95     assert(rep);
96     rep->setMyHandle(*this);
97 
98     rep->m_commitment = mc;
99 }
100 
101 // This is effectively the default constructor. It creates a writable, fully resizable 0x0
102 // matrix, with the handle committed only to the given element size.
103 // Just calls the above constructor with a default commitment.
104 template <class S>
MatrixHelper(int esz,int cppEsz)105 MatrixHelper<S>::MatrixHelper(int esz, int cppEsz) : rep(0) {
106     new (this) MatrixHelper(esz, cppEsz, MatrixCommitment());
107 }
108 
109 // This is the constructor for a Matrix in which the handle commitment has been
110 // supplied, along with an initial allocation size. Provided the size satisfies the
111 // commitment, the resulting matrix will have that size.
112 template <class S>
MatrixHelper(int esz,int cppEsz,const MatrixCommitment & commitment,int m,int n)113 MatrixHelper<S>::MatrixHelper
114    (int esz, int cppEsz, const MatrixCommitment& commitment,
115     int m, int n) : rep(0)
116 {
117     SimTK_ERRCHK2(commitment.isSizeOK(m,n),  "MatrixHelper::ctor()",
118         "The initial size allocation %s x %s didn't satisfy the supplied commitment.",
119         m, n);
120 
121     // Determine the best actual matrix to allocate to satisfy this commitment.
122     const MatrixCharacter actual = commitment.calcDefaultCharacter(m,n);
123 
124     rep = MatrixHelperRep<S>::createOwnerMatrixHelperRep(esz, cppEsz, actual);
125     assert(rep);
126     rep->setMyHandle(*this);
127 
128     rep->m_commitment = commitment;
129 }
130 
131 // Create a read-only view into existing data.
132 template <class S>
MatrixHelper(int esz,int cppEsz,const MatrixCommitment & commitment,const MatrixCharacter & actual,int spacing,const S * data)133 MatrixHelper<S>::MatrixHelper
134    (int esz, int cppEsz, const MatrixCommitment& commitment,
135     const MatrixCharacter& actual, int spacing, const S* data) : rep(0)
136 {
137     SimTK_ERRCHK(commitment.isSatisfiedBy(actual), "MatrixHelper::ctor(external data)",
138     "The supplied actual matrix character for the external data did not "
139     "satisfy the specified handle character commitment.");
140 
141     rep = MatrixHelperRep<S>::createExternalMatrixHelperRep
142                 (esz, cppEsz, actual, spacing, const_cast<S*>(data), false);
143     assert(rep);
144     rep->setMyHandle(*this);
145 
146     rep->m_commitment = commitment;
147 }
148 
149 // Create a writable view into existing data.
150 template <class S>
MatrixHelper(int esz,int cppEsz,const MatrixCommitment & commitment,const MatrixCharacter & actual,int spacing,S * data)151 MatrixHelper<S>::MatrixHelper
152    (int esz, int cppEsz, const MatrixCommitment& commitment,
153     const MatrixCharacter& actual, int spacing, S* data) : rep(0)
154 {
155     SimTK_ERRCHK(commitment.isSatisfiedBy(actual), "MatrixHelper::ctor(external data)",
156         "The supplied actual matrix character for the external data did not "
157         "satisfy the specified handle character commitment.");
158 
159     rep = MatrixHelperRep<S>::createExternalMatrixHelperRep
160                 (esz, cppEsz, actual, spacing, data, true);
161     assert(rep);
162     rep->setMyHandle(*this);
163 
164     rep->m_commitment = commitment;
165 }
166 
167 // clear() restores this matrix to the state it would be in if it were constructed
168 // using its current character commitment. We'll replace the current HelperRep with
169 // a fresh one. Note that this is more expensive than resize(0,0) which doesn't
170 // require replacement of the HelperRep.
171 template <class S> void
clear()172 MatrixHelper<S>::clear() {
173     // Determine the best actual matrix to allocate to satisfy this commitment.
174     const MatrixCharacter actual = getCharacterCommitment().calcDefaultCharacter(0,0);
175     const int             esz    = getRep().getEltSize();
176     const int             cppEsz = getRep().getCppEltSize();
177 
178     MatrixHelperRep<S>* newRep =
179         MatrixHelperRep<S>::createOwnerMatrixHelperRep(esz, cppEsz, actual);
180     assert(newRep);
181 
182     newRep->m_commitment = getRep().m_commitment;
183     if (!getRep().m_canBeOwner) {
184         newRep->m_canBeOwner = false;
185         newRep->m_owner      = false;
186     }
187 
188     replaceRep(newRep);
189 }
190 
191 template <class S> bool
isClear() const192 MatrixHelper<S>::isClear() const {return getRep().isClear();}
193 
194 
195 template <class S> void
commitTo(const MatrixCommitment & mc)196 MatrixHelper<S>::commitTo(const MatrixCommitment& mc) {
197         SimTK_ERRCHK_ALWAYS(isClear(), "MatrixHelper::commitTo()",
198             "You can only replace the character commitment in an empty Matrix handle."
199             "  Call clear() first to empty the handle.");
200         updRep().m_commitment = mc;
201         clear(); // reallocate to satisfy the new commitment
202 }
203 
204 // Copy constructor is suppressed; these are closest things but require
205 // specification of deep or shallow copy.
206 
207 // Create a read-only view of existing data.
208 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper & h,const ShallowCopy &)209 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper& h, const ShallowCopy&) : rep(0) {
210     SimTK_ERRCHK(mc.isSatisfiedBy(h.getMatrixCharacter()), "MatrixHelper::ctor(const,shallow)",
211         "The actual matrix character of the source did not "
212         "satisfy the specified handle character commitment.");
213     rep = h.rep->createWholeView(false);
214     rep->setMyHandle(*this);
215     rep->m_commitment = mc;
216 }
217 
218 // Create a (possibly) writable view of existing data.
219 template <class S>
MatrixHelper(const MatrixCommitment & mc,MatrixHelper & h,const ShallowCopy &)220 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, MatrixHelper& h, const ShallowCopy&) : rep(0) {
221     SimTK_ERRCHK(mc.isSatisfiedBy(h.getMatrixCharacter()), "MatrixHelper::ctor(writable,shallow)",
222         "The actual matrix character of the source did not "
223         "satisfy the specified handle character commitment.");
224     rep = h.rep->createWholeView(true);
225     rep->setMyHandle(*this);
226     rep->m_commitment = mc;
227 }
228 
229 // Deep copy. "This" be always writable and in densely packed storage.
230 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper & h,const DeepCopy &)231 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper& h, const DeepCopy&) : rep(0) {
232     SimTK_ERRCHK(mc.isSatisfiedBy(h.getMatrixCharacter()), "MatrixHelper::ctor(deep)",
233         "The actual matrix character of the source did not "
234         "satisfy the specified handle character commitment.");
235     rep = h.rep->createDeepCopy();
236     rep->setMyHandle(*this);
237     rep->m_commitment = mc;
238 }
239 
240 // Negated deep copy. We'll get a brand new, filterless, writable, packed copy with
241 // the same values as the original (duh, that's what "copy" means) -- BUT, the
242 // physical floating point representations will all have been negated since we're
243 // copying a Matrix whose elements are of type negator<S> while ours are type S (or
244 // vice versa).
245 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper<typename CNT<S>::TNeg> & h,const DeepCopy &)246 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper<typename CNT<S>::TNeg>& h, const DeepCopy&) : rep(0) {
247     SimTK_ERRCHK(mc.isSatisfiedBy(h.getMatrixCharacter()), "MatrixHelper::ctor(negated,deep)",
248         "The actual matrix character of the source did not "
249         "satisfy the specified handle character commitment.");
250     rep = h.rep->createNegatedDeepCopy();
251     rep->setMyHandle(*this);
252     rep->m_commitment = mc;
253 }
254 
255 // construct read-only block view
256 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper & h,int i,int j,int m,int n)257 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper& h, int i, int j, int m, int n) : rep(0) {
258     if (n==1)
259         rep = h.rep->createColumnView(j,i,m,false); // column j, from i to i+m-1
260     else if (m==1)
261         rep = h.rep->createRowView(i,j,n,false); // row i, from j to j+n-1
262     else
263         rep = h.rep->createBlockView(EltBlock(i,j,m,n), false);
264 
265     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()), "MatrixHelper::ctor(const,block)",
266         "The actual matrix character of the source block did not "
267         "satisfy the specified handle character commitment.");
268 
269     rep->setMyHandle(*this);
270     rep->m_commitment = mc;
271 }
272 
273 // construct (possibly) writable block view
274 template <class S>
MatrixHelper(const MatrixCommitment & mc,MatrixHelper & h,int i,int j,int m,int n)275 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, MatrixHelper& h, int i, int j, int m, int n) : rep(0) {
276     if (n==1)
277         rep = h.rep->createColumnView(j,i,m,true); // column j, from i to i+m-1
278     else if (m==1)
279         rep = h.rep->createRowView(i,j,n,true); // row i, from j to j+n-1
280     else
281         rep = h.rep->createBlockView(EltBlock(i,j,m,n),true);
282 
283     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()), "MatrixHelper::ctor(writable,block)",
284         "The actual matrix character of the source block did not "
285         "satisfy the specified handle character commitment.");
286 
287     rep->setMyHandle(*this);
288     rep->m_commitment = mc;
289 }
290 
291 // Construct read only transposed view of passed-in helper.
292 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper<typename CNT<S>::THerm> & h,const TransposeView &)293 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper<typename CNT<S>::THerm>& h,
294                               const TransposeView&) : rep(0) {
295     rep = h.rep->createHermitianView(false);
296 
297     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()), "MatrixHelper::ctor(const,transpose)",
298         "The actual matrix character of the transposed source did not "
299         "satisfy the specified handle character commitment.");
300 
301     rep->setMyHandle(*this);
302     rep->m_commitment = mc;
303 }
304 
305 // Construct (possibly) writable transposed view of passed-in helper.
306 template <class S>
MatrixHelper(const MatrixCommitment & mc,MatrixHelper<typename CNT<S>::THerm> & h,const TransposeView &)307 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, MatrixHelper<typename CNT<S>::THerm>& h,
308                               const TransposeView&) : rep(0) {
309     rep = h.rep->createHermitianView(true);
310 
311     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()), "MatrixHelper::ctor(writable,transpose)",
312         "The actual matrix character of the transposed source did not "
313         "satisfy the specified handle character commitment.");
314 
315     rep->setMyHandle(*this);
316     rep->m_commitment = mc;
317 }
318 
319 // Construct read only diagonal view of passed-in helper.
320 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper & h,const DiagonalView &)321 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper& h,
322                               const DiagonalView&) : rep(0) {
323     rep = h.rep->createDiagonalView(false);
324 
325     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()), "MatrixHelper::ctor(const,diagonal)",
326         "The actual matrix character of the diagonal of the source did not "
327         "satisfy the specified handle character commitment.");
328 
329     rep->setMyHandle(*this);
330     rep->m_commitment = mc;
331 }
332 
333 // Construct (possibly) writable diagonal view of passed-in helper.
334 template <class S>
MatrixHelper(const MatrixCommitment & mc,MatrixHelper & h,const DiagonalView &)335 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, MatrixHelper& h,
336                               const DiagonalView&) : rep(0) {
337     rep = h.rep->createDiagonalView(true);
338 
339     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()),
340         "MatrixHelper::ctor(writable,diagonal)",
341         "The actual matrix character of the diagonal of the source did not "
342         "satisfy the specified handle character commitment.");
343 
344     rep->setMyHandle(*this);
345     rep->m_commitment = mc;
346 }
347 
348 // Construct a read-only indexed view of a Vector.
349 template <class S>
MatrixHelper(const MatrixCommitment & mc,const MatrixHelper & h,int n,const int * ix)350 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, const MatrixHelper& h,
351                               int n, const int* ix) : rep(0) {
352     const VectorHelper<S>& vh =
353         SimTK_DYNAMIC_CAST_DEBUG<const VectorHelper<S>&>(*h.rep);
354     rep = new IndexedVectorHelper<S>(vh.getEltSize(), vh.getCppEltSize(), n,
355                                      vh.preferRowOrder_(), ix, n ? vh.getElt_(0) : 0, false);
356 
357     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()),
358         "MatrixHelper::ctor(const,indexed)",
359         "The actual matrix character of the indexed source did not "
360         "satisfy the specified handle character commitment.");
361 
362     rep->setMyHandle(*this);
363     rep->m_commitment = mc;
364 }
365 
366 // Construct a writable indexed view of a Vector.
367 template <class S>
MatrixHelper(const MatrixCommitment & mc,MatrixHelper & h,int n,const int * ix)368 MatrixHelper<S>::MatrixHelper(const MatrixCommitment& mc, MatrixHelper& h,
369                               int n, const int* ix) : rep(0) {
370     VectorHelper<S>& vh = SimTK_DYNAMIC_CAST_DEBUG<VectorHelper<S>&>(*h.rep);
371     rep = new IndexedVectorHelper<S>(vh.getEltSize(), vh.getCppEltSize(), n,
372                                      vh.preferRowOrder_(), ix, n ? vh.updElt_(0) : 0, true);
373 
374     SimTK_ERRCHK(mc.isSatisfiedBy(rep->getMatrixCharacter()),
375         "MatrixHelper::ctor(writable,indexed)",
376         "The actual matrix character of the indexed source did not "
377         "satisfy the specified handle character commitment.");
378 
379     rep->setMyHandle(*this);
380     rep->m_commitment = mc;
381 }
382 
383 // Perform deep assignment. "This" gets reallocated if necessary if it's an
384 // owner, but if this is a view the source and destination sizes must match.
385 template <class S> MatrixHelper<S>&
copyAssign(const MatrixHelper & h)386 MatrixHelper<S>::copyAssign(const MatrixHelper& h) {
387     if (rep->isOwner())
388         rep->resize(h.nrow(), h.ncol(), false);
389     else {
390         // OK if the sizes match.
391         assert(nrow()==h.nrow() && ncol()==h.ncol());
392     }
393 
394     rep->copyInFromCompatibleSource(*h.rep);
395     return *this;
396 }
397 // Like copy assign but the source has elements with the opposite interpretation
398 // of sign. Note that the result still has the original value, but the in-memory
399 // representation will be different, meaning flops will be burned here.
400 template <class S> MatrixHelper<S>&
negatedCopyAssign(const MatrixHelper<typename CNT<S>::TNeg> & h)401 MatrixHelper<S>::negatedCopyAssign(const MatrixHelper<typename CNT<S>::TNeg>& h) {
402     if (rep->isOwner())
403         rep->resize(h.nrow(), h.ncol(), false);
404     else {
405         // OK if the sizes match.
406         assert(nrow()==h.nrow() && ncol()==h.ncol());
407     }
408 
409     rep->negatedCopyInFromCompatibleSource(*h.rep);
410     return *this;
411 }
412 
413 // Create a read-only view of existing data. Element size of source and destination
414 // must match. The result will have no element filter unless the source already does.
415 template <class S> MatrixHelper<S>&
readOnlyViewAssign(const MatrixHelper & h)416 MatrixHelper<S>::readOnlyViewAssign(const MatrixHelper& h) {
417     SimTK_ERRCHK(getCharacterCommitment().isSatisfiedBy(h.getMatrixCharacter()),
418         "MatrixHelper<S>::readOnlyViewAssign()",
419         "The source matrix does not satisfy this handle's character commitment.");
420 
421     MatrixHelperRep<S>* newRep = h.getRep().createWholeView(false);
422     newRep->m_commitment = getRep().m_commitment;   // preserve commitment
423     newRep->m_canBeOwner = getRep().m_canBeOwner;
424     replaceRep(newRep);
425     return *this;
426 }
427 // Create a (possibly) writable view of existing data.
428 template <class S> MatrixHelper<S>&
writableViewAssign(MatrixHelper & h)429 MatrixHelper<S>::writableViewAssign(MatrixHelper& h) {
430     SimTK_ERRCHK(getCharacterCommitment().isSatisfiedBy(h.getMatrixCharacter()),
431         "MatrixHelper<S>::writableViewAssign()",
432         "The source matrix does not satisfy this handle's character commitment.");
433 
434     MatrixHelperRep<S>* newRep = h.getRep().createWholeView(true);
435     newRep->m_commitment = getRep().m_commitment;   // preserve commitment
436     newRep->m_canBeOwner = getRep().m_canBeOwner;
437     replaceRep(newRep);
438     return *this;
439 }
440 
441 template <class S> void
scaleBy(const typename CNT<S>::StdNumber & s)442 MatrixHelper<S>::scaleBy(const typename CNT<S>::StdNumber& s) {
443     rep->scaleBy(s);
444 }
445 template <class S> void
addIn(const MatrixHelper & h)446 MatrixHelper<S>::addIn(const MatrixHelper& h) {
447     rep->addIn(h);
448 }
449 template <class S> void
addIn(const MatrixHelper<typename CNT<S>::TNeg> & h)450 MatrixHelper<S>::addIn(const MatrixHelper<typename CNT<S>::TNeg>& h) {
451     subIn(reinterpret_cast<const MatrixHelper<S>&>(h));
452 }
453 template <class S> void
subIn(const MatrixHelper & h)454 MatrixHelper<S>::subIn(const MatrixHelper& h) {
455     rep->subIn(h);
456 }
457 template <class S> void
subIn(const MatrixHelper<typename CNT<S>::TNeg> & h)458 MatrixHelper<S>::subIn(const MatrixHelper<typename CNT<S>::TNeg>& h) {
459     addIn(reinterpret_cast<const MatrixHelper<S>&>(h));
460 }
461 template <class S> void
fillWith(const S * eltp)462 MatrixHelper<S>::fillWith(const S* eltp) {
463     rep->fillWith(eltp);
464 }
465 template <class S> void
copyInByRowsFromCpp(const S * elts)466 MatrixHelper<S>::copyInByRowsFromCpp(const S* elts) {
467     rep->copyInByRowsFromCpp(elts);
468 }
469 
470 template <class S> void
invertInPlace()471 MatrixHelper<S>::invertInPlace() {
472     rep->invertInPlace();
473 }
474 
475 template <class S> void
dump(const char * msg) const476 MatrixHelper<S>::dump(const char* msg) const {
477     rep->dump(msg);
478 }
479 
480 template <class S> const S*
getElt(int i,int j) const481 MatrixHelper<S>::getElt(int i, int j) const {return rep->getElt(i,j);}
482 template <class S> S*
updElt(int i,int j)483 MatrixHelper<S>::updElt(int i, int j)       {return rep->updElt(i,j);}
484 template <class S> void
getAnyElt(int i,int j,S * value) const485 MatrixHelper<S>::getAnyElt(int i, int j, S* value) const
486 {   return rep->getAnyElt(i,j,value); }
487 
488 template <class S> const S*
getElt(int i) const489 MatrixHelper<S>::getElt(int i) const {return rep->getElt(i);}
490 template <class S> S*
updElt(int i)491 MatrixHelper<S>::updElt(int i)       {return rep->updElt(i);}
492 template <class S> void
getAnyElt(int i,S * value) const493 MatrixHelper<S>::getAnyElt(int i, S* value) const
494 {   return rep->getAnyElt(i,value); }
495 
496 template <class S> int
nrow() const497 MatrixHelper<S>::nrow() const {
498     return rep->nrow();
499 }
500 template <class S> int
ncol() const501 MatrixHelper<S>::ncol() const {
502     return rep->ncol();
503 }
504 template <class S> ptrdiff_t
nelt() const505 MatrixHelper<S>::nelt() const {
506     return rep->nelt();
507 }
508 template <class S> int
length() const509 MatrixHelper<S>::length() const {
510     return rep->length();
511 }
512 
513 template <class S> void
resize(int m,int n)514 MatrixHelper<S>::resize    (int m, int n) {rep->resize(m,n,false);}
515 template <class S> void
resizeKeep(int m,int n)516 MatrixHelper<S>::resizeKeep(int m, int n) {rep->resize(m,n,true);}
517 
lockShape()518 template <class S> void MatrixHelper<S>::lockShape() {rep->lockHandle();}
unlockShape()519 template <class S> void MatrixHelper<S>::unlockShape() {rep->unlockHandle();}
520 
521 template <class S> void
sum(S * const answer) const522 MatrixHelper<S>::sum(S* const answer) const {
523     rep->sum(answer);
524 }
525 template <class S> void
colSum(int j,S * const answer) const526 MatrixHelper<S>::colSum(int j, S* const answer) const {
527     rep->colSum(j,answer);
528 }
529 template <class S> void
rowSum(int i,S * const answer) const530 MatrixHelper<S>::rowSum(int i, S* const answer) const {
531     rep->rowSum(i,answer);
532 }
533 template <class S> void
fillWithScalar(const typename CNT<S>::StdNumber & s)534 MatrixHelper<S>::fillWithScalar(const typename CNT<S>::StdNumber& s) {
535     rep->fillWithScalar(s);
536 }
537 
538 template <class S> bool
hasContiguousData() const539 MatrixHelper<S>::hasContiguousData() const {
540     return rep->hasContiguousData();
541 }
542 template <class S> ptrdiff_t
getContiguousDataLength() const543 MatrixHelper<S>::getContiguousDataLength() const {
544     assert(hasContiguousData());
545     return rep->nScalars();
546 }
547 template <class S> const S*
getContiguousData() const548 MatrixHelper<S>::getContiguousData() const {
549     assert(hasContiguousData());
550     return rep->m_data;
551 }
552 template <class S> S*
updContiguousData()553 MatrixHelper<S>::updContiguousData() {
554     assert(hasContiguousData());
555     return rep->m_data;
556 }
557 template <class S> void
replaceContiguousData(S * newData,ptrdiff_t length,bool takeOwnership)558 MatrixHelper<S>::replaceContiguousData(S* newData, ptrdiff_t length, bool takeOwnership) {
559     assert(length == getContiguousDataLength());
560     if (rep->m_owner) {
561         MatrixHelperRep<S>::deleteAllocatedMemory(rep->m_data);
562         rep->m_data=0;
563     }
564     rep->m_data = newData;
565     rep->m_owner = takeOwnership;
566 }
567 template <class S> void
replaceContiguousData(const S * newData,ptrdiff_t length)568 MatrixHelper<S>::replaceContiguousData(const S* newData, ptrdiff_t length) {
569     replaceContiguousData(const_cast<S*>(newData), length, false);
570     rep->m_writable = false;
571 }
572 template <class S> void
swapOwnedContiguousData(S * newData,ptrdiff_t length,S * & oldData)573 MatrixHelper<S>::swapOwnedContiguousData(S* newData, ptrdiff_t length, S*& oldData) {
574     assert(length == getContiguousDataLength());
575     assert(rep->m_owner);
576     oldData = rep->m_data;
577     rep->m_data = newData;
578 }
579 
580 
581 
582 
583 //----------------------------- MATRIX HELPER REP ------------------------------
584 //
585 //------------------------------------------------------------------------------
586 
587 template <class S> MatrixHelperRep<S>*
createOwnerMatrixHelperRep(int esz,int cppEsz,const MatrixCharacter & want)588 MatrixHelperRep<S>::createOwnerMatrixHelperRep(int esz, int cppEsz, const MatrixCharacter& want) {
589     SimTK_ASSERT2((esz==1&&cppEsz==1) || (esz>1&&cppEsz>=esz),
590         "MatrixHelperRep::createOwnerMatrixHelperRep(): bad element size esz=%d, cppEsz=%d", esz, cppEsz);
591 
592     MatrixHelperRep<S>* rep = 0;
593 
594     const int nr = want.nrow();
595     const int nc = want.ncol();
596     const MatrixStructure& structure = want.getStructure();
597     const MatrixStorage&   storage   = want.getStorage();
598     const bool rowOrder = (storage.getOrder() == MatrixStorage::RowOrder);
599 
600     switch (structure.getStructure()) {
601 
602       case MatrixStructure::Full:
603         if (rowOrder)
604             rep = (esz == 1 ? (RegularFullHelper<S>*)new FullRowOrderScalarHelper<S>(nr,nc)
605                             : (RegularFullHelper<S>*)new FullRowOrderEltHelper<S>(esz, cppEsz,nr,nc));
606         else
607             rep = (esz == 1 ? (RegularFullHelper<S>*)new FullColOrderScalarHelper<S>(nr,nc)
608                             : (RegularFullHelper<S>*)new FullColOrderEltHelper<S>(esz, cppEsz,nr,nc));
609         break;
610 
611       case MatrixStructure::Triangular:
612       case MatrixStructure::Symmetric:
613       case MatrixStructure::Hermitian:
614       case MatrixStructure::SkewSymmetric:
615       case MatrixStructure::SkewHermitian: {
616         const bool triangular = structure.getStructure() == MatrixStructure::Triangular;
617         const bool hermitian  =    structure.getStructure() == MatrixStructure::Hermitian
618                                 || structure.getStructure() == MatrixStructure::SkewHermitian;
619         const bool skew       =    structure.getStructure() == MatrixStructure::SkewSymmetric
620                                 || structure.getStructure() == MatrixStructure::SkewHermitian;
621 
622         if (/*storage.getPlacement() == MatrixStorage::Upper*/true)
623             rep = (esz == 1 ? (TriInFullHelper<S>*)new TriInFullUpperHelper<S>(1,1,nr,nc,
624                                                             triangular,hermitian,skew,rowOrder)
625                             : (TriInFullHelper<S>*)new TriInFullUpperHelper<S>(esz,cppEsz,nr,nc,
626                                                             triangular,hermitian,skew,rowOrder));
627         //else
628         //  rep = (esz == 1 ? (TriInFullHelper<S>*)new TriInFullLowerHelper<S>(1,1,nr,nc,
629          //                                               false,true,false,rowOrder)
630          //                 : (TriInFullHelper<S>*)new TriInFullLowerHelper<S>(esz,cppEsz,nr,nc,
631          //                                               false,true,false,rowOrder));
632         break;
633       }
634 
635       case MatrixStructure::Matrix1d: {
636         assert(nr==1 || nc==1);
637         const int length = nr*nc;
638         rep = (esz==1 ? (FullVectorHelper<S>*)new ContiguousVectorScalarHelper<S>(length,rowOrder)
639                       : (FullVectorHelper<S>*)new ContiguousVectorHelper<S>(esz,cppEsz,length,rowOrder));
640         break;
641       }
642 
643       default:
644           SimTK_ERRCHK1(!"not implemented", "MatrixHelperRep::createOwnerMatrixHelperRep()",
645               "Matrix structure commitment %s not implemented yet.",
646               MatrixStructure::name(structure.getStructure()));
647     }
648 
649     return rep;
650 }
651 
652 
653 template <class S> MatrixHelperRep<S>*
createExternalMatrixHelperRep(int esz,int cppEsz,const MatrixCharacter & actual,int spacing,S * data,bool canWrite)654 MatrixHelperRep<S>::createExternalMatrixHelperRep
655    (int esz, int cppEsz, const MatrixCharacter& actual,
656     int spacing, S* data, bool canWrite)
657 {
658     SimTK_ASSERT2((esz==1&&cppEsz==1) || (esz>1&&cppEsz>=esz),
659         "MatrixHelperRep::createExternalMatrixHelperRep(): bad element size esz=%d, cppEsz=%d", esz, cppEsz);
660 
661     MatrixHelperRep<S>* rep = 0;
662 
663     const int nr = actual.nrow();
664     const int nc = actual.ncol();
665     const MatrixStructure& structure = actual.getStructure();
666     const MatrixStorage&   storage   = actual.getStorage();
667     const bool rowOrder = (storage.getOrder() == MatrixStorage::RowOrder);
668 
669     switch (structure.getStructure()) {
670 
671       case MatrixStructure::Full:
672         if (rowOrder)
673             rep = (esz == 1 ? (RegularFullHelper<S>*)new FullRowOrderScalarHelper<S>(nr,nc,
674                                                             spacing,data,canWrite)
675                             : (RegularFullHelper<S>*)new FullRowOrderEltHelper<S>(esz, cppEsz,nr,nc,
676                                                             spacing,data,canWrite));
677         else
678             rep = (esz == 1 ? (RegularFullHelper<S>*)new FullColOrderScalarHelper<S>(nr,nc,
679                                                             spacing,data,canWrite)
680                             : (RegularFullHelper<S>*)new FullColOrderEltHelper<S>(esz, cppEsz,nr,nc,
681                                                             spacing,data,canWrite));
682         break;
683 
684       case MatrixStructure::Triangular:
685       case MatrixStructure::Symmetric:
686       case MatrixStructure::Hermitian:
687       case MatrixStructure::SkewSymmetric:
688       case MatrixStructure::SkewHermitian: {
689         const bool triangular = structure.getStructure() == MatrixStructure::Triangular;
690         const bool hermitian  =    structure.getStructure() == MatrixStructure::Hermitian
691                                 || structure.getStructure() == MatrixStructure::SkewHermitian;
692         const bool skew       =    structure.getStructure() == MatrixStructure::SkewSymmetric
693                                 || structure.getStructure() == MatrixStructure::SkewHermitian;
694 
695         if (/*storage.getPlacement() == MatrixStorage::Upper*/true)
696             rep = (esz == 1 ? (TriInFullHelper<S>*)new TriInFullUpperHelper<S>(1,1,nr,nc,
697                                                             triangular,hermitian,skew,rowOrder,
698                                                             spacing,data,canWrite)
699                             : (TriInFullHelper<S>*)new TriInFullUpperHelper<S>(esz,cppEsz,nr,nc,
700                                                             triangular,hermitian,skew,rowOrder,
701                                                             spacing,data,canWrite));
702         //else
703         //  rep = (esz == 1 ? (TriInFullHelper<S>*)new TriInFullLowerHelper<S>(1,1,nr,nc,
704          //                                               false,true,false,rowOrder)
705          //                 : (TriInFullHelper<S>*)new TriInFullLowerHelper<S>(esz,cppEsz,nr,nc,
706          //                                               false,true,false,rowOrder));
707         break;
708       }
709 
710       case MatrixStructure::Matrix1d: {
711         assert(nr==1 || nc==1);
712         const int length = nr*nc;
713         const int strideInElements = spacing/esz;
714         if (strideInElements > 1)
715             rep = (esz==1 ? (FullVectorHelper<S>*)new StridedVectorScalarHelper<S>(length,rowOrder,
716                                                             strideInElements,data,canWrite)
717                           : (FullVectorHelper<S>*)new StridedVectorHelper<S>(esz,cppEsz,length,rowOrder,
718                                                             strideInElements,data,canWrite));
719         else
720             rep = (esz==1 ? (FullVectorHelper<S>*)new ContiguousVectorScalarHelper<S>(length,rowOrder,
721                                                             data,canWrite)
722                           : (FullVectorHelper<S>*)new ContiguousVectorHelper<S>(esz,cppEsz,length,rowOrder,
723                                                             data,canWrite));
724         break;
725       }
726 
727       default:
728           SimTK_ERRCHK1(!"not implemented", "MatrixHelperRep::createOwnerMatrixHelperRep()",
729               "Matrix structure commitment %s not implemented yet.",
730               MatrixStructure::name(structure.getStructure()));
731     }
732 
733     return rep;
734 }
735 
736 template <class S> void
scaleBy(const typename CNT<S>::StdNumber & s)737 MatrixHelperRep<S>::scaleBy(const typename CNT<S>::StdNumber& s) {
738     // XXX -- really, really bad! Optimize for contiguous data!
739     for (int j=0; j<ncol(); ++j)
740         for (int i=0; i<nrow(); ++i)
741             scaleElt(updElt(i,j),s);
742 }
743 
744 template <class S> void
addIn(const MatrixHelper<S> & h)745 MatrixHelperRep<S>::addIn(const MatrixHelper<S>& h) {
746     const MatrixHelperRep& hrep = h.getRep();
747 
748     assert(nrow()==hrep.nrow() && ncol()==hrep.ncol());
749     assert(getEltSize()==hrep.getEltSize());
750     // XXX -- really, really bad! Optimize for contiguous data, missing views, etc.!
751     for (int j=0; j<ncol(); ++j)
752         for (int i=0; i<nrow(); ++i)
753             addToElt(updElt(i,j),hrep.getElt(i,j));
754 }
755 template <class S> void
addIn(const MatrixHelper<typename CNT<S>::TNeg> & nh)756 MatrixHelperRep<S>::addIn(const MatrixHelper<typename CNT<S>::TNeg>& nh) {
757     subIn(reinterpret_cast<const MatrixHelper<S>&>(nh));
758 }
759 
760 template <class S> void
subIn(const MatrixHelper<S> & h)761 MatrixHelperRep<S>::subIn(const MatrixHelper<S>& h) {
762     const MatrixHelperRep& hrep = h.getRep();
763 
764     assert(nrow()==hrep.nrow() && ncol()==hrep.ncol());
765     assert(getEltSize()==hrep.getEltSize());
766     // XXX -- really, really bad! Optimize for contiguous data, missing views, etc.!
767     for (int j=0; j<ncol(); ++j)
768         for (int i=0; i<nrow(); ++i)
769             subFromElt(updElt(i,j),hrep.getElt(i,j));
770 }
771 template <class S> void
subIn(const MatrixHelper<typename CNT<S>::TNeg> & nh)772 MatrixHelperRep<S>::subIn(const MatrixHelper<typename CNT<S>::TNeg>& nh) {
773     addIn(reinterpret_cast<const MatrixHelper<S>&>(nh));
774 }
775 
776 template <class S> void
fillWith(const S * eltp)777 MatrixHelperRep<S>::fillWith(const S* eltp) {
778     if (hasContiguousData()) {
779         const ptrdiff_t len = nelt();
780         if (getEltSize() == 1)
781             for (ptrdiff_t i = 0; i < len; i++)
782                 m_data[i] = *eltp;
783         else
784             for (ptrdiff_t i = 0; i < len; i++)
785                 for (int j = 0; j < getEltSize(); j++)
786                     m_data[i*getEltSize()+j] = eltp[j];
787     }
788     else {
789         for (int j=0; j<ncol(); ++j)
790             for (int i=0; i<nrow(); ++i)
791                 copyElt(updElt(i,j),eltp);
792     }
793 }
794 
795 // We're copying data from a C++ row-oriented matrix into our general
796 // Matrix. In addition to the row ordering, C++ may use different spacing
797 // for elements than Simmatrix does. Lucky we know that value!
798 template <class S> void
copyInByRowsFromCpp(const S * elts)799 MatrixHelperRep<S>::copyInByRowsFromCpp(const S* elts) {
800     const int cppRowSz = m_cppEltSize * ncol();
801     // XXX -- really, really bad! Optimize for contiguous data, missing views, etc.!
802     for (int j=0; j<ncol(); ++j)
803         for (int i=0; i<nrow(); ++i)
804             copyElt(updElt(i,j), elts + i*cppRowSz + j*m_cppEltSize);
805 }
806 
807 template <class S>
~MatrixHelperRep()808 MatrixHelperRep<S>::~MatrixHelperRep()
809 {
810     if (isOwner())
811         deleteAllocatedMemory(m_data);
812 }
813 
814 template <class S> static void
dumpElt(const S * p,int sz)815 dumpElt(const S* p, int sz) {
816     if (sz > 1) std::cout << "{";
817     for (int k=0; k<sz; ++k) {
818         if (k>0) std::cout << " ";
819         std::cout << *p++;
820     }
821     if (sz > 1) std::cout << "}";
822 }
823 
824 template <class S> void
dump(const char * msg) const825 MatrixHelperRep<S>::dump(const char* msg) const {
826     if (msg)
827         std::cout << std::string(msg) << std::endl;
828     std::cout << "Matrix " << nrow() << " X " << ncol() << " "
829               << getEltSize() << "-scalar entries:" << std::endl;
830     if (nrow()*ncol() == 0) {
831         std::cout << "<EMPTY>" << std::endl;
832         return;
833     }
834 
835     S* elt = new S[getEltSize()];
836     const std::streamsize oldsz = std::cout.precision(20);
837     for (int i=0; i<nrow(); ++i) {
838         for (int j=0; j<ncol(); ++j) {
839             if (j>0) std::cout << "\t";
840             getAnyElt(i,j,elt);
841             dumpElt(elt, getEltSize());
842         }
843         std::cout << std::endl;
844     }
845     std::cout.precision(oldsz);
846     delete[] elt;
847 }
848 
849 //----------------------------- RegularFullHelper ------------------------------
850 //------------------------------------------------------------------------------
851 
852 template <class S> VectorHelper<S>*
createDiagonalView_()853 RegularFullHelper<S>::createDiagonalView_() {
854     VectorHelper<S>* p = 0;
855     const int length = std::min(this->nrow(), this->ncol());
856     S*        data   = length ? this->updElt_(0,0) : 0;
857 
858     const int strideInScalars = length > 1 ? int(this->getElt_(1,1) - this->getElt_(0,0))
859                                            : this->m_eltSize;
860     const int strideInElements = strideInScalars / this->m_eltSize;
861 
862     // No need for a stride if there's 0 or 1 element, or if the stride is 1. TODO: scalar helper
863     if (strideInElements == 1) {
864         p = (this->m_eltSize==1)
865             ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(length, false, data, false)
866             : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
867                                                               length, false, data, false);
868         return p;
869     }
870 
871     p = (this->m_eltSize==1)
872         ? (VectorHelper<S>*)new StridedVectorScalarHelper<S>(length, false, strideInElements, data, false)
873         : (VectorHelper<S>*)new StridedVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
874                                                        length, false, strideInElements, data, false);
875     return p;
876 }
877 
878 template <class S> VectorHelper<S>*
createColumnView_(int j,int i,int m)879 RegularFullHelper<S>::createColumnView_(int j, int i, int m) {
880     VectorHelper<S>* p = 0;
881     S* data = m ? this->updElt_(i,j) : 0;
882 
883     const int strideInScalars = m > 1 ? int(this->getElt_(i+1,j) - this->getElt_(i,j)) : this->m_eltSize;
884     const int strideInElements = strideInScalars / this->m_eltSize;
885 
886     // No need for a stride if there's 0 or 1 element, or if the stride is 1. TODO: scalar helper
887     if (strideInElements == 1) {
888         p = (this->m_eltSize==1)
889             ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(m, false, data, false)
890             : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
891                                                               m, false, data, false);
892         return p;
893     }
894 
895     p = (this->m_eltSize==1)
896         ? (VectorHelper<S>*)new StridedVectorScalarHelper<S>(m, false, strideInElements, data, false)
897         : (VectorHelper<S>*)new StridedVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
898                                                        m, false, strideInElements, data, false);
899     return p;
900 }
901 
902 template <class S> VectorHelper<S>*
createRowView_(int i,int j,int n)903 RegularFullHelper<S>::createRowView_(int i, int j, int n) {
904     VectorHelper<S>* p = 0;
905     S* data = n ? this->updElt_(i,j) : 0;
906 
907     const int strideInScalars = n > 1 ? int(this->getElt_(i,j+1) - this->getElt_(i,j))
908                                       : this->m_eltSize;
909     const int strideInElements = strideInScalars / this->m_eltSize;
910 
911     // No need for a stride if there's 0 or 1 element, or if the stride is 1. TODO: scalar helper
912     if (strideInElements == 1) {
913         p = (this->m_eltSize==1)
914             ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(n, true, data, false)
915             : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
916                                                               n, true, data, false);
917         return p;
918     }
919 
920     p = (this->m_eltSize==1)
921         ? (VectorHelper<S>*)new StridedVectorScalarHelper<S>(n, true, strideInElements, data, false)
922         : (VectorHelper<S>*)new StridedVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
923                                                        n, true, strideInElements, data, false);
924     return p;
925 }
926 
927 
928 //------------------------------ TriInFullHelper -------------------------------
929 //------------------------------------------------------------------------------
930 
931 template <class S> VectorHelper<S>*
createDiagonalView_()932 TriInFullHelper<S>::createDiagonalView_() {
933     SimTK_ERRCHK_ALWAYS(!hasKnownDiagonal(), "TriInFullHelper::createDiagonalView_()",
934         "Diagonal view of a known-diagonal matrix is not yet implemented. Sorry.");
935 
936     VectorHelper<S>* p = 0;
937     const int length = std::min(this->nrow(), this->ncol());
938     S*        data   = length ? this->updElt_(0,0) : 0;
939 
940     const int strideInScalars = length > 1 ? int(this->getElt_(1,1) - this->getElt_(0,0))
941                                            : this->m_eltSize;
942     const int strideInElements = strideInScalars / this->m_eltSize;
943 
944     // No need for a stride if there's 0 or 1 element, or if the stride is 1. TODO: scalar helper
945     if (strideInElements == 1) {
946         p = (this->m_eltSize==1)
947             ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(length, false, data, false)
948             : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
949                                                               length, false, data, false);
950         return p;
951     }
952 
953     p = (this->m_eltSize==1)
954         ? (VectorHelper<S>*)new StridedVectorScalarHelper<S>(length, false, strideInElements, data, false)
955         : (VectorHelper<S>*)new StridedVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
956                                                        length, false, strideInElements, data, false);
957     return p;
958 }
959 
960 //------------------------------ INSTANTIATIONS --------------------------------
961 // Instantiations for each of the 12 Scalar types.
962 // It isn't actually necessary to instantiate these classes since they
963 // are only used internally. However, this is a good way to make sure that
964 // everything supplied at least compiles. Otherwise you won't find errors
965 // until all the code is actually used.
966 
967 #define INSTANTIATE(Helper)         \
968 template class Helper< float >;     \
969 template class Helper< double >;    \
970 template class Helper< std::complex<float> >;   \
971 template class Helper< std::complex<double> >;  \
972 template class Helper< conjugate<float> >;      \
973 template class Helper< conjugate<double> >;     \
974 template class Helper< negator< float > >;      \
975 template class Helper< negator< double > >;     \
976 template class Helper< negator< std::complex<float> > >;    \
977 template class Helper< negator< std::complex<double> > >;   \
978 template class Helper< negator< conjugate<float> > >;       \
979 template class Helper< negator< conjugate<double> > >
980 
981 INSTANTIATE(MatrixHelper);
982 INSTANTIATE(MatrixHelperRep);
983 
984 INSTANTIATE(FullHelper);
985 INSTANTIATE(RegularFullHelper);
986 
987 INSTANTIATE(TriHelper);
988 
989 INSTANTIATE(VectorHelper);
990 INSTANTIATE(FullVectorHelper);
991 INSTANTIATE(ContiguousVectorHelper);
992 INSTANTIATE(ContiguousVectorScalarHelper);
993 INSTANTIATE(StridedVectorHelper);
994 INSTANTIATE(StridedVectorScalarHelper);
995 INSTANTIATE(IndexedVectorHelper);
996 
997 } // namespace SimTK
998