summaryrefslogtreecommitdiff
path: root/htk_io/src/KaldiLib/Matrix.tcc
diff options
context:
space:
mode:
Diffstat (limited to 'htk_io/src/KaldiLib/Matrix.tcc')
-rw-r--r--htk_io/src/KaldiLib/Matrix.tcc796
1 files changed, 796 insertions, 0 deletions
diff --git a/htk_io/src/KaldiLib/Matrix.tcc b/htk_io/src/KaldiLib/Matrix.tcc
new file mode 100644
index 0000000..f6ffb8f
--- /dev/null
+++ b/htk_io/src/KaldiLib/Matrix.tcc
@@ -0,0 +1,796 @@
+
+/** @file Matrix.tcc
+ * This is an internal header file, included by other library headers.
+ * You should not attempt to use it directly.
+ */
+
+
+#ifndef TNet_Matrix_tcc
+#define TNet_Matrix_tcc
+
+//#pragma GCC system_header
+
+#include <cstdlib>
+#include <cmath>
+#include <cfloat>
+#include <fstream>
+#include <iomanip>
+#include <typeinfo>
+#include <algorithm>
+#include <limits>
+#include <vector>
+#include "Common.h"
+
+#ifndef _XOPEN_SOURCE
+ #define _XOPEN_SOURCE 600
+#endif
+
+
+#ifdef HAVE_ATLAS
+extern "C"{
+ #include <cblas.h>
+}
+#endif
+
+
+#include "Common.h"
+#include "Vector.h"
+namespace TNet
+{
+
+//******************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ Init(const size_t rows,
+ const size_t cols,
+ bool clear)
+ {
+ if(mpData != NULL) Destroy();
+ if(rows*cols == 0){
+ assert(rows==0 && cols==0);
+ mMRows=rows;
+ mMCols=cols;
+#ifdef STK_MEMALIGN_MANUAL
+ mpFreeData=NULL;
+#endif
+ mpData=NULL;
+ return *this;
+ }
+ // initialize some helping vars
+ size_t skip;
+ size_t real_cols;
+ size_t size;
+ void* data; // aligned memory block
+ void* free_data; // memory block to be really freed
+
+ // compute the size of skip and real cols
+ skip = ((16 / sizeof(_ElemT)) - cols % (16 / sizeof(_ElemT))) % (16 / sizeof(_ElemT));
+ real_cols = cols + skip;
+ size = rows * real_cols * sizeof(_ElemT);
+
+ // allocate the memory and set the right dimensions and parameters
+
+ if (NULL != (data = stk_memalign(16, size, &free_data)))
+ {
+ mpData = static_cast<_ElemT *> (data);
+#ifdef STK_MEMALIGN_MANUAL
+ mpFreeData = static_cast<_ElemT *> (free_data);
+#endif
+ mMRows = rows;
+ mMCols = cols;
+ mStride = real_cols;
+ }
+ else
+ {
+ throw std::bad_alloc();
+ }
+ if(clear) Zero();
+ return *this;
+ } //
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ template<typename _ElemU>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ Copy(const Matrix<_ElemU> & rM, MatrixTrasposeType Trans)
+ {
+ if(Trans==NO_TRANS){
+ assert(mMRows == rM.Rows() && mMCols == rM.Cols());
+ for(size_t i = 0; i < mMRows; i++)
+ (*this)[i].Copy(rM[i]);
+ return *this;
+ } else {
+ assert(mMCols == rM.Rows() && mMRows == rM.Cols());
+ for(size_t i = 0; i < mMRows; i++)
+ for(size_t j = 0; j < mMCols; j++)
+ (*this)(i,j) = rM(j,i);
+ return *this;
+ }
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ CopyVectorSplicedRows(const Vector<_ElemT> &rV, const size_t nRows, const size_t nCols) {
+ assert(rV.Dim() == nRows*nCols);
+ mMRows = nRows;
+ mMCols = nCols;
+ Init(nRows,nCols,true);
+ for(size_t r=0; r<mMRows; r++)
+ for(size_t c=0; c<mMCols; c++)
+ (*this)(r,c) = rV(r*mMCols + c);
+
+ return *this;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ RemoveRow(size_t i)
+ {
+ assert(i < mMRows && "Access out of matrix");
+ for(size_t j = i + 1; j < mMRows; j++)
+ (*this)[j - 1].Copy((*this)[j]);
+ mMRows--;
+ return *this;
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ // The destructor
+ template<typename _ElemT>
+ void
+ Matrix<_ElemT>::
+ Destroy()
+ {
+ // we need to free the data block if it was defined
+#ifndef STK_MEMALIGN_MANUAL
+ if (NULL != mpData) free(mpData);
+#else
+ if (NULL != mpData) free(mpFreeData);
+ mpFreeData = NULL;
+#endif
+
+ mpData = NULL;
+ mMRows = mMCols = 0;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+// template<typename _ElemT>
+// void
+// Matrix<_ElemT>::
+// VectorizeRows(Vector<_ElemT> &rV) {
+//#ifdef PARANIOD
+// assert(rV.Dim() == mMRows*mMCols);
+//#endif
+// for(size_t r=0; r<mMRows; r++) {
+// rV.Range((r-1)*mMCols, mMCols).Copy((*this)[r]);
+// }
+// }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ bool
+ Matrix<_ElemT>::
+ LoadHTK(const char* pFileName)
+ {
+ HtkHeader htk_hdr;
+
+ FILE *fp = fopen(pFileName, "rb");
+ if(!fp)
+ {
+ return false;
+ }
+
+ read(fileno(fp), &htk_hdr, sizeof(htk_hdr));
+
+ swap4(htk_hdr.mNSamples);
+ swap4(htk_hdr.mSamplePeriod);
+ swap2(htk_hdr.mSampleSize);
+ swap2(htk_hdr.mSampleKind);
+
+ Init(htk_hdr.mNSamples, htk_hdr.mSampleSize / sizeof(float));
+
+ size_t i;
+ size_t j;
+ if (typeid(_ElemT) == typeid(float))
+ {
+ for (i=0; i< Rows(); ++i) {
+ read(fileno(fp), (*this).pRowData(i), Cols() * sizeof(float));
+
+ for(j = 0; j < Cols(); j++) {
+ swap4(((*this)(i,j)));
+ }
+ }
+ }
+ else
+ {
+ float *pmem = new (std::nothrow) float[Cols()];
+ if (!pmem)
+ {
+ fclose(fp);
+ return false;
+ }
+
+ for(i = 0; i < Rows(); i++) {
+ read(fileno(fp), pmem, Cols() * sizeof(float));
+
+ for (j = 0; j < Cols(); ++j) {
+ swap4(pmem[j]);
+ (*this)(i,j) = static_cast<_ElemT>(pmem[j]);
+ }
+ }
+ delete [] pmem;
+ }
+
+ fclose(fp);
+
+ return true;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ DotMul(const ThisType& a)
+ {
+ size_t i;
+ size_t j;
+
+ for (i = 0; i < mMRows; ++i) {
+ for (j = 0; j < mMCols; ++j) {
+ (*this)(i,j) *= a(i,j);
+ }
+ }
+ return *this;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ _ElemT &
+ Matrix<_ElemT>::
+ Sum() const
+ {
+ double sum = 0.0;
+
+ for (size_t i = 0; i < Rows(); ++i) {
+ for (size_t j = 0; j < Cols(); ++j) {
+ sum += (*this)(i,j);
+ }
+ }
+
+ return sum;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ Scale(_ElemT alpha)
+ {
+#if 0
+ for (size_t i = 0; i < Rows(); ++i)
+ for (size_t j = 0; j < Cols(); ++j)
+ (*this)(i,j) *= alpha;
+#else
+ for (size_t i = 0; i < Rows(); ++i) {
+ _ElemT* p_data = pRowData(i);
+ for (size_t j = 0; j < Cols(); ++j) {
+ *p_data++ *= alpha;
+ }
+ }
+#endif
+ return *this;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ ScaleRows(const Vector<_ElemT>& scale) // scales each row by scale[i].
+ {
+ assert(scale.Dim() == Rows());
+ size_t M = Rows(), N = Cols();
+
+ for (size_t i = 0; i < M; i++) {
+ _ElemT this_scale = scale(i);
+ for (size_t j = 0; j < N; j++) {
+ (*this)(i,j) *= this_scale;
+ }
+ }
+ return *this;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ ScaleCols(const Vector<_ElemT>& scale) // scales each column by scale[i].
+ {
+ assert(scale.Dim() == Cols());
+ for (size_t i = 0; i < Rows(); i++) {
+ for (size_t j = 0; j < Cols(); j++) {
+ _ElemT this_scale = scale(j);
+ (*this)(i,j) *= this_scale;
+ }
+ }
+ return *this;
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ Add(const Matrix<_ElemT>& rMatrix)
+ {
+ assert(rMatrix.Cols() == Cols());
+ assert(rMatrix.Rows() == Rows());
+
+#if 0
+ //this can be slow
+ for (size_t i = 0; i < Rows(); i++) {
+ for (size_t j = 0; j < Cols(); j++) {
+ (*this)(i,j) += rMatrix(i,j);
+ }
+ }
+#else
+ //this will be faster (but less secure)
+ for(size_t i=0; i<Rows(); i++) {
+ const _ElemT* p_src = rMatrix.pRowData(i);
+ _ElemT* p_dst = pRowData(i);
+ for(size_t j=0; j<Cols(); j++) {
+ *p_dst++ += *p_src++;
+ }
+ }
+#endif
+ return *this;
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ AddScaled(_ElemT alpha, const Matrix<_ElemT>& rMatrix)
+ {
+ assert(rMatrix.Cols() == Cols());
+ assert(rMatrix.Rows() == Rows());
+
+#if 0
+ //this can be slow
+ for (size_t i = 0; i < Rows(); i++) {
+ for (size_t j = 0; j < Cols(); j++) {
+ (*this)(i,j) += rMatrix(i,j) * alpha;
+ }
+ }
+#else
+ /*
+ //this will be faster (but less secure)
+ for(size_t i=0; i<Rows(); i++) {
+ const _ElemT* p_src = rMatrix.pRowData(i);
+ _ElemT* p_dst = pRowData(i);
+ for(size_t j=0; j<Cols(); j++) {
+ *p_dst++ += *p_src++ * alpha;
+ }
+ }
+ */
+
+ //let's use BLAS
+ for(size_t i=0; i<Rows(); i++) {
+ (*this)[i].BlasAxpy(alpha, rMatrix[i]);
+ }
+#endif
+ return *this;
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT>&
+ Matrix<_ElemT>::
+ ApplyLog()
+ {
+
+#if 0
+ //this can be slow
+ for (size_t i = 0; i < Rows(); i++) {
+ for (size_t j = 0; j < Cols(); j++) {
+ (*this)(i,j) = += _LOG((*this)(i,j));
+ }
+ }
+#else
+ //this will be faster (but less secure)
+ for(size_t i=0; i<Rows(); i++) {
+ _ElemT* p_data = pRowData(i);
+ for(size_t j=0; j<Cols(); j++) {
+ *p_data = _LOG(*p_data);
+ p_data++;
+ }
+ }
+#endif
+ return *this;
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ Zero()
+ {
+ for(size_t row=0;row<mMRows;row++)
+ memset(mpData + row*mStride, 0, sizeof(_ElemT)*mMCols);
+ return *this;
+ }
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ Matrix<_ElemT> &
+ Matrix<_ElemT>::
+ Unit()
+ {
+ for(size_t row=0;row<std::min(mMRows,mMCols);row++){
+ memset(mpData + row*mStride, 0, sizeof(_ElemT)*mMCols);
+ (*this)(row,row) = 1.0;
+ }
+ return *this;
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ void
+ Matrix<_ElemT>::
+ PrintOut(char* file)
+ {
+ FILE* f = fopen(file, "w");
+ unsigned i,j;
+ fprintf(f, "%dx%d\n", this->mMRows, this->mMCols);
+
+ for(i=0; i<this->mMRows; i++)
+ {
+ _ElemT* row = (*this)[i];
+
+ for(j=0; j<this->mStride; j++){
+ fprintf(f, "%20.17f ",row[j]);
+ }
+ fprintf(f, "\n");
+ }
+
+ fclose(f);
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ void
+ Matrix<_ElemT>::
+ ReadIn(char* file)
+ {
+ FILE* f = fopen(file, "r");
+ int i = 0;
+ int j = 0;
+ fscanf(f, "%dx%d\n", &i,&j);
+ fprintf(stderr, "%dx%d\n", i,j);
+
+ for(i=0; i<this->mMRows; i++)
+ {
+ _ElemT* row = (*this)[i];
+
+ for(j=0; j<this->mStride; j++){
+ fscanf(f, "%f ",&row[j]);
+ }
+ //fprintf(f, "\n");
+ }
+
+ fclose(f);
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ void Save (std::ostream &rOut, const Matrix<_ElemT> &rM)
+ {
+ for (size_t i = 0; i < rM.Rows(); i++) {
+ for (size_t j = 0; j < rM.Cols(); j++) {
+ rOut << rM(i,j) << ' ';
+ }
+ rOut << '\n';
+ }
+ if(rOut.fail())
+ throw std::runtime_error("Failed to write matrix to stream");
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ std::ostream &
+ operator << (std::ostream & rOut, const Matrix<_ElemT> & rM)
+ {
+ rOut << "m " << rM.Rows() << ' ' << rM.Cols() << '\n';
+ Save(rOut, rM);
+ return rOut;
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ void Load (std::istream & rIn, Matrix<_ElemT> & rM)
+ {
+ if(MatrixVectorIostreamControl::Flags(rIn, ACCUMULATE_INPUT)) {
+ for (size_t i = 0; i < rM.Rows(); i++) {
+ std::streamoff pos = rIn.tellg();
+ for (size_t j = 0; j < rM.Cols(); j++) {
+ _ElemT tmp;
+ rIn >> tmp;
+ rM(i,j) += tmp;
+ if(rIn.fail()){
+ throw std::runtime_error("Failed to read matrix from stream. File position is "+to_string(pos));
+ }
+ }
+ }
+ } else {
+ for (size_t i = 0; i < rM.Rows(); i++) {
+ std::streamoff pos = rIn.tellg();
+ for (size_t j = 0; j < rM.Cols(); j++) {
+ rIn >> rM(i,j);
+ if(rIn.fail()){
+ throw std::runtime_error("Failed to read matrix from stream. File position is "+to_string(pos));
+ }
+
+ }
+ }
+ }
+ }
+
+
+ //****************************************************************************
+ //****************************************************************************
+ template<typename _ElemT>
+ std::istream &
+ operator >> (std::istream & rIn, Matrix<_ElemT> & rM)
+ {
+ while(isascii(rIn.peek()) && isspace(rIn.peek())) rIn.get(); // eat up space.
+ if(rIn.peek() == 'm'){ // "new" format: m <nrows> <ncols> \n 1.0 0.2 4.3 ...
+ rIn.get();// eat up the 'm'.
+ long long int nrows=-1; rIn>>nrows;
+ long long int ncols=-1; rIn>>ncols;
+ if(rIn.fail()||nrows<0||ncols<0){ throw std::runtime_error("Failed to read matrix from stream: no size\n"); }
+
+ size_t nrows2 = size_t(nrows), ncols2 = size_t(ncols);
+ assert((long long int)nrows2 == nrows && (long long int)ncols2 == ncols);
+
+ if(rM.Rows()!=nrows2 || rM.Cols()!=ncols2) rM.Init(nrows2,ncols2);
+ }
+ Load(rIn,rM);
+ return rIn;
+ }
+
+
+
+ //****************************************************************************
+ //****************************************************************************
+ // Constructor
+ template<typename _ElemT>
+ SubMatrix<_ElemT>::
+ SubMatrix(const Matrix<_ElemT>& rT, // Matrix cannot be const because SubMatrix can change its contents. Would have to have a ConstSubMatrix or something...
+ const size_t ro,
+ const size_t r,
+ const size_t co,
+ const size_t c)
+ {
+ assert(ro >= 0 && ro <= rT.Rows());
+ assert(co >= 0 && co <= rT.Cols());
+ assert(r > 0 && r <= rT.Rows() - ro);
+ assert(c > 0 && c <= rT.Cols() - co);
+ // point to the begining of window
+ Matrix<_ElemT>::mMRows = r;
+ Matrix<_ElemT>::mMCols = c;
+ Matrix<_ElemT>::mStride = rT.Stride();
+ Matrix<_ElemT>::mpData = rT.pData_workaround() + co + ro * rT.Stride();
+ }
+
+
+
+#ifdef HAVE_ATLAS
+
+ template<>
+ Matrix<float> &
+ Matrix<float>::
+ BlasGer(const float alpha, const Vector<float>& rA, const Vector<float>& rB);
+
+
+ template<>
+ Matrix<double> &
+ Matrix<double>::
+ BlasGer(const double alpha, const Vector<double>& rA, const Vector<double>& rB);
+
+
+ template<>
+ Matrix<float>&
+ Matrix<float>::
+ BlasGemm(const float alpha,
+ const Matrix<float>& rA, MatrixTrasposeType transA,
+ const Matrix<float>& rB, MatrixTrasposeType transB,
+ const float beta);
+
+ template<>
+ Matrix<double>&
+ Matrix<double>::
+ BlasGemm(const double alpha,
+ const Matrix<double>& rA, MatrixTrasposeType transA,
+ const Matrix<double>& rB, MatrixTrasposeType transB,
+ const double beta);
+
+ template<>
+ Matrix<float>&
+ Matrix<float>::
+ Axpy(const float alpha,
+ const Matrix<float>& rA, MatrixTrasposeType transA);
+
+ template<>
+ Matrix<double>&
+ Matrix<double>::
+ Axpy(const double alpha,
+ const Matrix<double>& rA, MatrixTrasposeType transA);
+
+ template <> // non-member so automatic namespace lookup can occur.
+ double TraceOfProduct(const Matrix<double> &A, const Matrix<double> &B);
+
+ template <> // non-member so automatic namespace lookup can occur.
+ double TraceOfProductT(const Matrix<double> &A, const Matrix<double> &B);
+
+ template <> // non-member so automatic namespace lookup can occur.
+ float TraceOfProduct(const Matrix<float> &A, const Matrix<float> &B);
+
+ template <> // non-member so automatic namespace lookup can occur.
+ float TraceOfProductT(const Matrix<float> &A, const Matrix<float> &B);
+
+
+
+#else // HAVE_ATLAS
+ #error Routines in this section are not implemented yet without BLAS
+#endif // HAVE_ATLAS
+
+ template<class _ElemT>
+ bool
+ Matrix<_ElemT>::
+ IsSymmetric(_ElemT cutoff) const {
+ size_t R=Rows(), C=Cols();
+ if(R!=C) return false;
+ _ElemT bad_sum=0.0, good_sum=0.0;
+ for(size_t i=0;i<R;i++){
+ for(size_t j=0;j<i;j++){
+ _ElemT a=(*this)(i,j),b=(*this)(j,i), avg=0.5*(a+b), diff=0.5*(a-b);
+ good_sum += fabs(avg); bad_sum += fabs(diff);
+ }
+ good_sum += fabs((*this)(i,i));
+ }
+ if(bad_sum > cutoff*good_sum) return false;
+ return true;
+ }
+
+ template<class _ElemT>
+ bool
+ Matrix<_ElemT>::
+ IsDiagonal(_ElemT cutoff) const{
+ size_t R=Rows(), C=Cols();
+ _ElemT bad_sum=0.0, good_sum=0.0;
+ for(size_t i=0;i<R;i++){
+ for(size_t j=0;j<C;j++){
+ if(i==j) good_sum += (*this)(i,j);
+ else bad_sum += (*this)(i,j);
+ }
+ }
+ return (!(bad_sum > good_sum * cutoff));
+ }
+
+ template<class _ElemT>
+ bool
+ Matrix<_ElemT>::
+ IsUnit(_ElemT cutoff) const {
+ size_t R=Rows(), C=Cols();
+ if(R!=C) return false;
+ _ElemT bad_sum=0.0;
+ for(size_t i=0;i<R;i++)
+ for(size_t j=0;j<C;j++)
+ bad_sum += fabs( (*this)(i,j) - (i==j?1.0:0.0));
+ return (bad_sum <= cutoff);
+ }
+
+ template<class _ElemT>
+ bool
+ Matrix<_ElemT>::
+ IsZero(_ElemT cutoff)const {
+ size_t R=Rows(), C=Cols();
+ _ElemT bad_sum=0.0;
+ for(size_t i=0;i<R;i++)
+ for(size_t j=0;j<C;j++)
+ bad_sum += fabs( (*this)(i,j) );
+ return (bad_sum <= cutoff);
+ }
+
+ template<class _ElemT>
+ _ElemT
+ Matrix<_ElemT>::
+ FrobeniusNorm() const{
+ size_t R=Rows(), C=Cols();
+ _ElemT sum=0.0;
+ for(size_t i=0;i<R;i++)
+ for(size_t j=0;j<C;j++){
+ _ElemT tmp = (*this)(i,j);
+ sum += tmp*tmp;
+ }
+ return sqrt(sum);
+ }
+
+ template<class _ElemT>
+ _ElemT
+ Matrix<_ElemT>::
+ LargestAbsElem() const{
+ size_t R=Rows(), C=Cols();
+ _ElemT largest=0.0;
+ for(size_t i=0;i<R;i++)
+ for(size_t j=0;j<C;j++)
+ largest = std::max(largest, (_ElemT)fabs((*this)(i,j)));
+ return largest;
+ }
+
+
+
+ // Uses SVD to compute the eigenvalue decomposition of a symmetric positive semidefinite
+ // matrix:
+ // (*this) = rU * diag(rS) * rU^T, with rU an orthogonal matrix so rU^{-1} = rU^T.
+ // Does this by computing svd (*this) = U diag(rS) V^T ... answer is just U diag(rS) U^T.
+ // Throws exception if this failed to within supplied precision (typically because *this was not
+ // symmetric positive definite).
+
+
+
+ template<class _ElemT>
+ _ElemT
+ Matrix<_ElemT>::
+ LogAbsDeterminant(_ElemT *DetSign){
+ _ElemT LogDet;
+ Matrix<_ElemT> tmp(*this);
+ tmp.Invert(&LogDet, DetSign, false); // false== output not needed (saves some computation).
+ return LogDet;
+ }
+
+}// namespace TNet
+
+// #define TNet_Matrix_tcc
+#endif