source: Sophya/trunk/SophyaLib/TArray/tmatrix.cc@ 2574

Last change on this file since 2574 was 2574, checked in by cmv, 21 years ago

Optimisation produit de matrice a la sauce cmv 29/07/04

File size: 18.3 KB
Line 
1// $Id: tmatrix.cc,v 1.25 2004-07-29 08:40:49 cmv Exp $
2// C.Magneville 04/99
3#include "machdefs.h"
4#include <stdio.h>
5#include <stdlib.h>
6#include "pexceptions.h"
7#include "tmatrix.h"
8
9//#define DO_NOT_OPTIMIZE_PRODUCT
10
11/*!
12 \class SOPHYA::TMatrix
13 \ingroup TArray
14
15 The TMatrix class specializes the TArray class for representing
16 two dimensional arrays as matrices. Matrix and vector operations,
17 such as matrix multiplication or transposition is implemented.
18 \b Matrix is a typedef for double precision floating point matrix ( TMatrix<r_8> ).
19
20 \sa SOPHYA::TArray SOPHYA::TVector
21 \sa SOPHYA::Range \sa SOPHYA::Sequence
22 \sa SOPHYA::MathArray \sa SOPHYA::SimpleMatrixOperation
23
24 The following sample code illustrates vector-matrix multiplication
25 and matrix inversion, using simple gauss inversion.
26 \code
27 #include "array.h"
28 // ....
29 int n = 5; // Size of matrix and vectors here
30 Matrix a(n,n);
31 a = RandomSequence(RandomSequence::Gaussian, 0., 2.5);
32 Vector x(n);
33 x = RegularSequence(1.,3.);
34 Vector b = a*x;
35 cout << " ----- Vector x = \n " << x << endl;
36 cout << " ----- Vector b = a*x = \n " << b << endl;
37 SimpleMatrixOperation<r_8> smo;
38 Matrix inva = smo.Inverse(a);
39 cout << " ----- Matrix Inverse(a) = \n " << inva << endl;
40 cout << " ----- Matrix a*Inverse(a) = \n " << inva*a << endl;
41 cout << " ----- Matrix Inverse(a)*b (=Inv(a)*a*x) = \n " << inva*b << endl;
42 cout << " ----- Matrix x-Inverse(a)*b = (=0 ?)\n " << x-inva*b << endl;
43 \endcode
44
45 */
46
47////////////////////////////////////////////////////////////////
48//**** Createur, Destructeur
49//! Default constructor
50template <class T>
51TMatrix<T>::TMatrix()
52// Constructeur par defaut.
53 : TArray<T>()
54{
55 arrtype_ = 1; // Type = Matrix
56}
57
58//! constructor of a matrix with r lines et c columns.
59/*!
60 \param r : number of rows
61 \param c : number of columns
62 \param mm : define the memory mapping type
63 \sa ReSize
64 */
65template <class T>
66TMatrix<T>::TMatrix(sa_size_t r,sa_size_t c, short mm)
67// Construit une matrice de r lignes et c colonnes.
68 : TArray<T>()
69{
70 if ( (r == 0) || (c == 0) )
71 throw ParmError("TMatrix<T>::TMatrix(sa_size_t r,sa_size_t c) NRows or NCols = 0");
72 arrtype_ = 1; // Type = Matrix
73 ReSize(r, c, mm);
74}
75
76//! Constructor by copy
77/*!
78 \warning datas are \b SHARED with \b a.
79 \sa NDataBlock::NDataBlock(const NDataBlock<T>&)
80*/
81template <class T>
82TMatrix<T>::TMatrix(const TMatrix<T>& a)
83// Constructeur par copie
84 : TArray<T>(a)
85{
86 arrtype_ = 1; // Type = Matrix
87 UpdateMemoryMapping(a, SameMemoryMapping);
88}
89
90//! Constructor by copy
91/*!
92 \param share : if true, share data. If false copy data
93 */
94template <class T>
95TMatrix<T>::TMatrix(const TMatrix<T>& a, bool share)
96// Constructeur par copie avec possibilite de forcer le partage ou non.
97: TArray<T>(a, share)
98{
99 arrtype_ = 1; // Type = Matrix
100 UpdateMemoryMapping(a, SameMemoryMapping);
101}
102
103//! Constructor of a matrix from a TArray \b a
104template <class T>
105TMatrix<T>::TMatrix(const TArray<T>& a)
106: TArray<T>(a)
107{
108 if (a.NbDimensions() > 2)
109 throw SzMismatchError("TMatrix<T>::TMatrix(const TArray<T>& a) a.NbDimensions()>2 ");
110 if (a.NbDimensions() == 1) {
111 size_[1] = 1;
112 step_[1] = size_[0]*step_[0];
113 ndim_ = 2;
114 }
115 arrtype_ = 1; // Type = Matrix
116 UpdateMemoryMapping(a, SameMemoryMapping);
117}
118
119//! Constructor of a matrix from a TArray \b a
120/*!
121 \param a : TArray to be copied or shared
122 \param share : if true, share data. If false copy data
123 \param mm : define the memory mapping type
124 */
125template <class T>
126TMatrix<T>::TMatrix(const TArray<T>& a, bool share, short mm )
127: TArray<T>(a, share)
128{
129 if (a.NbDimensions() > 2)
130 throw SzMismatchError("TMatrix<T>::TMatrix(const TArray<T>& a, ...) a.NbDimensions()>2");
131 if (a.NbDimensions() == 1) {
132 size_[1] = 1;
133 step_[1] = size_[0]*step_[0];
134 ndim_ = 2;
135 }
136 arrtype_ = 1; // Type = Matrix
137 UpdateMemoryMapping(a, mm);
138}
139
140//! Constructor of a matrix from a TArray \b a , with a different data type
141template <class T>
142TMatrix<T>::TMatrix(const BaseArray& a)
143: TArray<T>()
144{
145 arrtype_ = 1; // Type = Matrix
146 UpdateMemoryMapping(a, SameMemoryMapping);
147 SetBA(a);
148}
149
150
151
152//! Destructor
153template <class T>
154TMatrix<T>::~TMatrix()
155{
156}
157
158//! Set matrix equal to \b a and return *this
159/*!
160 \warning Datas are copied (cloned) from \b a.
161 \sa NDataBlock::operator=(const NDataBlock<T>&)
162*/
163template <class T>
164TArray<T>& TMatrix<T>::Set(const TArray<T>& a)
165{
166 if (a.NbDimensions() > 2)
167 throw SzMismatchError("TMatrix<T>::Set(const TArray<T>& a) a.NbDimensions() > 2");
168 if ((arrtype_ == 2) && (a.NbDimensions() > 1) && (a.Size(0) > 1) && (a.Size(1) > 1) )
169 throw SzMismatchError("TMatrix<T>::Set(const TArray<T>& a) Size(0,1)>1 for Vector");
170 TArray<T>::Set(a);
171 if (NbDimensions() == 1) {
172 size_[1] = 1;
173 step_[1] = size_[0]*step_[0];
174 ndim_ = 2;
175 }
176 UpdateMemoryMapping(*this, SameMemoryMapping);
177 return(*this);
178}
179
180template <class T>
181TArray<T>& TMatrix<T>::SetBA(const BaseArray& a)
182{
183 if (a.NbDimensions() > 2)
184 throw SzMismatchError("TMatrix<T>::SetBA(const BaseArray& a) a.NbDimensions() > 2");
185 if ((arrtype_ == 2) && (a.NbDimensions() > 1) && (a.Size(0) > 1) && (a.Size(1) > 1) )
186 throw SzMismatchError("TMatrix<T>::Set(const TArray<T>& a) Size(0,1)>1 for Vector");
187 TArray<T>::SetBA(a);
188 if (NbDimensions() == 1) {
189 size_[1] = 1;
190 step_[1] = size_[0]*step_[0];
191 ndim_ = 2;
192 }
193 UpdateMemoryMapping(*this, SameMemoryMapping);
194 return(*this);
195}
196
197
198
199//! Resize the matrix
200/*!
201 \param r : number of rows
202 \param c : number of columns
203 \param mm : define the memory mapping type
204 (SameMemoryMapping,CMemoryMapping
205 ,FortranMemoryMapping,DefaultMemoryMapping)
206 */
207template <class T>
208void TMatrix<T>::ReSize(sa_size_t r, sa_size_t c, short mm)
209{
210 if(r==0||c==0)
211 throw(SzMismatchError("TMatrix::ReSize r or c==0 "));
212 if ((arrtype_ == 2) && (r > 1) && (c > 1))
213 throw(SzMismatchError("TMatrix::ReSize r>1&&c>1 for Vector "));
214 sa_size_t size[BASEARRAY_MAXNDIMS];
215 for(int_4 kk=0; kk<BASEARRAY_MAXNDIMS; kk++) size[kk] = 0;
216 if (mm == SameMemoryMapping) mm = GetMemoryMapping();
217 else if ( (mm != CMemoryMapping) && (mm != FortranMemoryMapping) )
218 mm = GetDefaultMemoryMapping();
219 if (mm == CMemoryMapping) {
220 size[0] = c; size[1] = r;
221 }
222 else {
223 size[0] = r; size[1] = c;
224 }
225 TArray<T>::ReSize(2, size, 1);
226 UpdateMemoryMapping(mm);
227}
228
229//! Re-allocate space for the matrix
230/*!
231 \param r : number of rows
232 \param c : number of columns
233 \param mm : define the memory mapping type
234 \param force : if true re-allocation is forced, if not it occurs
235 only if the required space is greater than the old one.
236 \sa ReSize
237 */
238template <class T>
239void TMatrix<T>::Realloc(sa_size_t r,sa_size_t c, short mm, bool force)
240{
241 if(r==0||c==0)
242 throw(SzMismatchError("TMatrix::Realloc r or c==0 "));
243 if ((arrtype_ == 2) && (r > 1) && (c > 1))
244 throw(SzMismatchError("TMatrix::Realloc r>1&&c>1 for Vector "));
245 sa_size_t size[BASEARRAY_MAXNDIMS];
246 for(int_4 kk=0; kk<BASEARRAY_MAXNDIMS; kk++) size[kk] = 0;
247 if (mm == SameMemoryMapping) mm = GetMemoryMapping();
248 else if ( (mm != CMemoryMapping) && (mm != FortranMemoryMapping) )
249 mm = GetDefaultMemoryMapping();
250 if (mm == CMemoryMapping) {
251 size[0] = c; size[1] = r;
252 }
253 else {
254 size[0] = r; size[1] = c;
255 }
256 TArray<T>::Realloc(2, size, 1, force);
257 UpdateMemoryMapping(mm);
258}
259
260// $CHECK$ Reza 03/2000 Doit-on declarer cette methode const ?
261//! Return a submatrix define by \b Range \b rline and \b rcol
262template <class T>
263TMatrix<T> TMatrix<T>::SubMatrix(Range rline, Range rcol) const
264{
265 short mm = GetMemoryMapping();
266 Range rx, ry;
267 if (mm == CMemoryMapping) { rx = rcol; ry = rline; }
268 else { ry = rcol; rx = rline; }
269 TMatrix sm(SubArray(rx, ry, Range(0), Range(0), Range(0)),true, mm);
270 sm.UpdateMemoryMapping(mm);
271 return(sm);
272}
273
274////////////////////////////////////////////////////////////////
275// Transposition
276//! Transpose matrix in place, by changing the memory mapping
277template <class T>
278TMatrix<T>& TMatrix<T>::TransposeSelf()
279{
280 short vt = (marowi_ == veceli_) ? ColumnVector : RowVector;
281 int_4 rci = macoli_;
282 macoli_ = marowi_;
283 marowi_ = rci;
284 veceli_ = (vt == ColumnVector ) ? marowi_ : macoli_;
285 return(*this);
286}
287
288
289//! Returns the transpose of the original matrix.
290/*!
291 The data is shared between the two matrices
292 \return return a new matrix
293 */
294template <class T>
295TMatrix<T> TMatrix<T>::Transpose() const
296{
297 TMatrix<T> tm(*this);
298 tm.TransposeSelf();
299 return tm;
300}
301
302//! Returns a new matrix, corresponding to the transpose of the original matrix
303/*!
304 \param mm : define the memory mapping type
305 (SameMemoryMapping,CMemoryMapping,FortranMemoryMapping)
306 \return return a new matrix
307 */
308template <class T>
309TMatrix<T> TMatrix<T>::Transpose(short mm) const
310{
311 if (mm == SameMemoryMapping) mm = GetMemoryMapping();
312 TMatrix<T> tm(NCols(), NRows(), mm);
313 for(sa_size_t i=0; i<NRows(); i++)
314 for(sa_size_t j=0; j<NCols(); j++)
315 tm(j,i) = (*this)(i,j);
316 return tm;
317}
318
319//! Rearrange data in memory according to \b mm
320/*!
321 \param mm : define the memory mapping type
322 (SameMemoryMapping,CMemoryMapping,FortranMemoryMapping)
323 \warning If identical, return a matrix that share the datas
324 */
325template <class T>
326TMatrix<T> TMatrix<T>::Rearrange(short mm) const
327{
328 if ( mm == SameMemoryMapping) mm = GetMemoryMapping();
329 else if ( (mm != CMemoryMapping) && (mm != FortranMemoryMapping) )
330 mm = GetDefaultMemoryMapping();
331
332 if (mm == GetMemoryMapping())
333 return (TMatrix<T>(*this, true));
334
335 TMatrix<T> tm(NRows(), NCols(), mm);
336 for(sa_size_t i=0; i<NRows(); i++)
337 for(sa_size_t j=0; j<NCols(); j++)
338 tm(i,j) = (*this)(i,j);
339 return tm;
340}
341
342//! Set the matrix to the identity matrix \b imx
343template <class T>
344TMatrix<T>& TMatrix<T>::SetIdentity(IdentityMatrix imx)
345{
346 if (ndim_ == 0) {
347 sa_size_t sz = imx.Size();
348 if (sz < 1) sz = 1;
349 ReSize(sz, sz);
350 }
351 T diag = (T)imx.Diag();
352 if (NRows() != NCols())
353 throw SzMismatchError("TMatrix::operator= (IdentityMatrix) NRows() != NCols()") ;
354 *this = (T) 0;
355 for(sa_size_t i=0; i<NRows(); i++) (*this)(i,i) = diag;
356
357 return (*this);
358}
359
360
361
362////////////////////////////////////////////////////////////////
363//**** Impression
364//! Return info on number of rows, column and type \b T
365template <class T>
366string TMatrix<T>::InfoString() const
367{
368 string rs = "TMatrix<";
369 rs += typeid(T).name();
370 char buff[64];
371 sprintf(buff, ">(NRows=%ld, NCols=%ld)", (long)NRows(), (long)NCols());
372 rs += buff;
373 return(rs);
374}
375
376//! Print matrix
377/*!
378 \param os : output stream
379 \param maxprt : maximum numer of print
380 \param si : if true, display attached DvList
381 \param ascd : if true, suppresses the display of line numbers,
382 suitable for ascii dump format.
383 \sa SetMaxPrint
384 */
385template <class T>
386void TMatrix<T>::Print(ostream& os, sa_size_t maxprt, bool si, bool ascd) const
387{
388 if (maxprt < 0) maxprt = max_nprt_;
389 sa_size_t npr = 0;
390 Show(os, si);
391 if (ndim_ < 1) return;
392 sa_size_t kc,kr;
393 for(kr=0; kr<size_[marowi_]; kr++) {
394 if ( (size_[marowi_] > 1) && (size_[macoli_] > 10) && ascd) cout << "----- Line= " << kr << endl;
395 for(kc=0; kc<size_[macoli_]; kc++) {
396 if(kc > 0) os << " ";
397 os << (*this)(kr, kc); npr++;
398 if (npr >= (sa_size_t) maxprt) {
399 if (npr < totsize_) os << "\n .... " << endl; return;
400 }
401 }
402 os << endl;
403 }
404 os << endl;
405}
406
407////////////////////////////////////////////////////////////////
408//**** Multiplication matricielle *****
409////////////////////////////////////////////////////////////////
410
411//! Return the matrix product C = (*this)*B
412/*!
413 \param mm : define the memory mapping type for the return matrix
414 */
415////////////// Routine de base sans optimisation //////////////
416/*
417template <class T>
418TMatrix<T> TMatrix<T>::Multiply(const TMatrix<T>& b, short mm) const
419{
420 if (NCols() != b.NRows())
421 throw(SzMismatchError("TMatrix<T>::Multiply(b) NCols() != b.NRows() ") );
422 if (mm == SameMemoryMapping) mm = GetMemoryMapping();
423 TMatrix<T> rm(NRows(), b.NCols(), mm);
424
425 const T * pea;
426 const T * peb;
427 T sum;
428 sa_size_t r,c,k;
429 sa_size_t stepa = Step(ColsKA());
430 sa_size_t stepb = b.Step(b.RowsKA());
431 // Calcul de C=rm = A*B (A=*this)
432 for(r=0; r<rm.NRows(); r++) // Boucle sur les lignes de A
433 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
434 sum = 0;
435 pea = &((*this)(r,0)); // 1er element de la ligne r de A
436 peb = &(b(0,c)); // 1er element de la colonne c de B
437 for(k=0; k<NCols(); k++) sum += pea[k*stepa]*peb[k*stepb];
438 rm(r,c) = sum;
439 }
440
441 return rm;
442}
443*/
444
445////////////// Routine optimisee //////////////
446template <class T>
447TMatrix<T> TMatrix<T>::Multiply(const TMatrix<T>& b, short mm) const
448{
449 if (NCols() != b.NRows())
450 throw(SzMismatchError("TMatrix<T>::Multiply(b) NCols() != b.NRows() ") );
451
452 // Commentaire: pas de difference de vitesse notable selon les mappings choisis pour la matrice produit "rm"
453 if (mm == SameMemoryMapping) mm = GetMemoryMapping();
454 TMatrix<T> rm(NRows(), b.NCols(), mm);
455 rm = (T) 0; // Rien ne garantit que rm soit mise a zero a la creation !
456
457 // Les "steps" pour l'adressage des colonnes de A et des lignes de B
458 sa_size_t stepa = Step(ColsKA());
459 sa_size_t stepb = b.Step(b.RowsKA());
460
461 // On decide si on optimise ou non selon les dimensions de A et B
462 // (il semble que optimiser ou non ne degrade pas notablement la vitesse pour les petites matrices)
463#ifdef DO_NOT_OPTIMIZE_PRODUCT
464 bool no_optim = true;
465#else
466 bool no_optim = false;
467 //NON juste pour memoire: if((stepa+stepb)*NCols()*sizeof(T)<100000) no_optim=true;
468#endif
469
470 // Calcul de C=rm = A*B (A=*this)
471 // Remember: C-like matrices are column packed
472 // Fortan-like matrices are line packed
473 sa_size_t r,c,k;
474 T sum;
475 const T * pe;
476
477 // Pas d'optimisation demandee
478 if( no_optim ) {
479 //cout<<"no_optim("<<no_optim<<") "<<stepa<<" "<<stepb<<endl;
480 const T * pea;
481 const T * peb;
482 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
483 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
484 sum = 0;
485 pea = &((*this)(r,0));
486 peb = &(b(0,c));
487 // On gagne un peu en remplacant "pea[k*stepa]" par "pea+=stepa" pour les grosses matrices
488 //for(k=0; k<NCols(); k++) sum += pea[k*stepa]*peb[k*stepb];
489 for(k=0; k<NCols(); k++) {sum += (*pea)*(*peb); pea+=stepa; peb+=stepb;}
490 rm(r,c) = sum;
491 }
492 }
493 }
494 // A.col est packed et B.row est packed (on a interet a optimiser quand meme)
495 else if(stepa==1 && stepb==1) {
496 //cout<<"A.col packed && B.row not packed "<<stepa<<" "<<stepb<<endl;
497 T * pea = new T[rm.NCols()];
498 const T * peb;
499 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
500 pe = &((*this)(r,0));
501 for(k=0; k<NCols(); k++) {pea[k] = *(pe++);}
502 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
503 sum = 0;
504 peb = &(b(0,c));
505 for(k=0; k<NCols(); k++) sum += *(peb++)*pea[k];
506 rm(r,c) = sum;
507 }
508 }
509 delete [] pea;
510 }
511 // A.col est packed et B.row n'est pas packed
512 else if(stepa==1 && stepb!=1) {
513 //cout<<"A.col packed && B.row not packed "<<stepa<<" "<<stepb<<endl;
514 const T * pea;
515 T * peb = new T[rm.NCols()];
516 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
517 pe = &(b(0,c));
518 for(k=0; k<NCols(); k++) {peb[k] = *pe; pe+=stepb;}
519 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
520 sum = 0;
521 pea = &((*this)(r,0));
522 for(k=0; k<NCols(); k++) sum += pea[k]*peb[k];
523 rm(r,c) = sum;
524 }
525 }
526 delete [] peb;
527 }
528 // A.col n'est pas packed et B.row est packed
529 else if(stepa!=1 && stepb==1) {
530 //cout<<"A.col not packed && B.row packed "<<stepa<<" "<<stepb<<endl;
531 T * pea = new T[rm.NCols()];
532 const T * peb;
533 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
534 pe = &((*this)(r,0));
535 for(k=0; k<NCols(); k++) {pea[k] = *pe; pe+=stepa;}
536 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
537 sum = 0;
538 peb = &(b(0,c));
539 for(k=0; k<NCols(); k++) sum += pea[k]*peb[k];
540 rm(r,c) = sum;
541 }
542 }
543 delete [] pea;
544 }
545 // A.col n'est pas packed et B.row n'est pas packed, stepb>stepa
546 else if(stepa!=1 && stepb!=1 && stepb>stepa) {
547 //cout<<"A.col not packed && B.row not packed ==> optimize on B "<<stepa<<" "<<stepb<<endl;
548 const T * pea;
549 T * peb = new T[rm.NCols()];
550 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
551 pe = &(b(0,c));
552 for(k=0; k<NCols(); k++) {peb[k] = *pe; pe+=stepb;}
553 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
554 sum = 0;
555 pea = &((*this)(r,0));
556 for(k=0; k<NCols(); k++) {sum += (*pea)*peb[k]; pea+=stepa;}
557 rm(r,c) = sum;
558 }
559 }
560 delete [] peb;
561 }
562 // A.col n'est pas packed et B.row n'est pas packed, stepa>=stepb
563 else if(stepa!=1 && stepb!=1) {
564 //cout<<"A.col not packed && B.row not packed ==> optimize on A "<<stepa<<" "<<stepb<<endl;
565 T * pea = new T[rm.NCols()];
566 const T * peb;
567 for(r=0; r<rm.NRows(); r++) { // Boucle sur les lignes de A
568 pe = &((*this)(r,0));
569 for(k=0; k<NCols(); k++) {pea[k] = *pe; pe+=stepa;}
570 for(c=0; c<rm.NCols(); c++) { // Boucle sur les colonnes de B
571 sum = 0;
572 peb = &(b(0,c));
573 for(k=0; k<NCols(); k++) {sum += pea[k]*(*peb); peb+=stepb;}
574 rm(r,c) = sum;
575 }
576 }
577 delete [] pea;
578 }
579 else {
580 cout<<"TMatrix<T>::Multiply(b) Optimize case not treated... Please report BUG !!! "<<endl;
581 throw(SzMismatchError("TMatrix<T>::Multiply(b) Optimize case not treated... Please report BUG !!! ") );
582 }
583
584 return rm;
585}
586
587///////////////////////////////////////////////////////////////
588#ifdef __CXX_PRAGMA_TEMPLATES__
589#pragma define_template TMatrix<uint_2>
590#pragma define_template TMatrix<uint_8>
591#pragma define_template TMatrix<int_4>
592#pragma define_template TMatrix<int_8>
593#pragma define_template TMatrix<r_4>
594#pragma define_template TMatrix<r_8>
595#pragma define_template TMatrix< complex<r_4> >
596#pragma define_template TMatrix< complex<r_8> >
597#endif
598
599#if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES)
600template class TMatrix<uint_2>;
601template class TMatrix<uint_8>;
602template class TMatrix<int_4>;
603template class TMatrix<int_8>;
604template class TMatrix<r_4>;
605template class TMatrix<r_8>;
606template class TMatrix< complex<r_4> >;
607template class TMatrix< complex<r_8> >;
608#endif
Note: See TracBrowser for help on using the repository browser.