1 /* Copyright (c) 2015  Gerald Knizia
2  *
3  * This file is part of the IboView program (see: http://www.iboview.org)
4  *
5  * IboView is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * IboView is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16  *
17  * Please see IboView documentation in README.txt for:
18  * -- A list of included external software and their licenses. The included
19  *    external software's copyright is not touched by this agreement.
20  * -- Notes on re-distribution and contributions to/further development of
21  *    the IboView software
22  */
23 
24 #ifndef CT8k_MATRIX_H
25 #define CT8k_MATRIX_H
26 
27 #include "CxTypes.h" // for size_t.
28 #include "CxAlgebra.h"
29 #include "CxMemoryStack.h"
30 #include "CxPodArray.h"
31 
32 #include <string>
33 #include <sstream>
34 
35 namespace ct {
36 
37 typedef double
38     FScalar;
39 
40 using std::size_t;
41 
42 /// Describes the layout of a matrix somewhere in memory, which this object
43 /// DOES NOT own.
44 /// fixme: replace uints by size_t...
45 struct FMatrixView
46 {
47     FScalar
48         *pData; ///< start of data of this matrix.
49     size_t
50         nRows, ///< number of rows (first index, fast)
51         nCols, ///< number of columns (second index, slow)
52         nRowSt, ///< number of words between adjacent rows. In default-alignment, nRowSt == 1
53         nColSt;  ///< number of words between adjacent cols. In default-alignment, nColSt == nRows
54     // Note: One of both strides should be 1.
55 
56     /// element retrieval operator: m(nRow,nCol) -> element stored at that
57     /// part of the matrix. Note: indices are 0-based!.
operatorFMatrixView58     inline FScalar& operator () ( size_t iRow, size_t iCol ){
59         assert( iRow <= nRows );
60         assert( iCol <= nCols );
61         return pData[nRowSt * iRow + nColSt * iCol];
62     };
operatorFMatrixView63     inline FScalar const& operator () ( size_t iRow, size_t iCol ) const {
64         return const_cast<FMatrixView *const>(this)->operator()( iRow, iCol );
65     };
66 
67     FScalar &operator [] (size_t iEntry) { return pData[iEntry]; }
68     FScalar const &operator []  (size_t iEntry) const { return pData[iEntry]; }
69 
FMatrixViewFMatrixView70     FMatrixView()
71         : pData(0)
72     {}
73 
74     FMatrixView( double *pData_, size_t nRows_, size_t nCols_, size_t nRowSt_ = 1, size_t nColSt_ = 0 )
pDataFMatrixView75         : pData(pData_), nRows(nRows_), nCols(nCols_),
76           nRowSt(nRowSt_), nColSt( (nColSt_ != 0)? nColSt_ : nRows )
77     {};
78 
79     inline size_t GetStridedSize() const;
80 
81     void Print( std::ostream &out, std::string const &Name = "" ) const;
82     /// sets matrix to zero
83     void Clear();
84     /// sets matrix to identity
85     void SetIdentity();
86 //     /// returns copy of diagonal elements of the matrix. Only valid for square matrices.
87 //     std::vector<FScalar> GetDiagonal() const;
88 
IsSquareFMatrixView89     bool IsSquare() const { return nRows == nCols; };
90     bool IsSymmetric(FScalar Thresh) const;
91 
92     double fRmsdFromIdentity() const;
93     double fRmsdFromZero() const;
94     double fRmsdFromSymmetry() const;
95 
96     /// if nRowSt==1, sets space between nRow and nColSt to zero (in order to
97     /// not have possible NaNs/Denorms in spaces in the matrices which are only
98     /// there for alignment purposes; e.g., ntb vs nt strides.
99     void ClearEmptySpace();
100 
101     /// returns size of this matrix would require in triangular storage (e.g.,
102     /// storing only the lower triangular part). If Sign == -1, size is
103     /// calculated for anti-symmetric matrix.
104     size_t TriangularStorageSize(int Sign = 1) const;
TriangularStorageSize1FMatrixView105     size_t TriangularStorageSize1() const { return TriangularStorageSize(1); };
106     void TriangularExpand(int Sign = 1, FScalar *pTriangData = 0);
107     // fOffDiagFactor: if given, off diagonal elements will be multiplied by this factor before reduction.
108     void TriangularReduce(int Sign = 1, FScalar *pTriangData = 0, FScalar fOffDiagFactor = 1.);
109     enum FTriangularTransformFlags {
110         TRIANG_Expand,
111         TRIANG_Reduce
112     };
113     void TriangularExpandOrReduce(int Sign, FScalar *pTriangData, FTriangularTransformFlags Direction);
114 };
115 
GetStridedSize()116 size_t FMatrixView::GetStridedSize() const {
117    if ( nRowSt == 1 )
118        return nCols * nColSt;
119    else {
120        if ( nColSt == 1 )
121            return nRows * nRowSt;
122        else
123            assert(0); // not supported.
124            return 0;
125    }
126 };
127 
128 
129 
130 // export version (can also be used in other files than CtMatrix.cpp)
131 void AssertCompatible1( FMatrixView const &A, FMatrixView const &B );
132 
133 // allocate a nRows x nCols matrix on Mem and return the given object.
MakeStackMatrix(size_t nRows,size_t nCols,FMemoryStack & Mem)134 inline FMatrixView MakeStackMatrix(size_t nRows, size_t nCols, FMemoryStack &Mem)
135 {
136    FMatrixView r(0, nRows, nCols);
137    Mem.Alloc(r.pData, nRows * nCols);
138    return r;
139 }
140 
141 uint const MXM_AddToDest = 0x1u;
142 uint const MXM_Add = MXM_AddToDest;
143 
144 /// Creates a view representing the transpose of the input matrix.
145 /// The returned view still references the original data (i.e., In and Out are aliased)
146 FMatrixView Transpose( FMatrixView const &In );
147 /// Out += f * In
148 void Add( FMatrixView &Out, FMatrixView const &In, FScalar fFactor = 1.0 );
149 /// Out = f * In
150 void Move( FMatrixView &Out, FMatrixView const &In, FScalar fFactor = 1.0 );
151 /// Out = A * B
152 void Mxm( FMatrixView &Out, FMatrixView const &A, FMatrixView const &B, uint Flags = 0 );
153 /// Out = f * A * B
154 void Mxm( FMatrixView &Out, FMatrixView const &A, FMatrixView const &B, FScalar fFactor, uint Flags = 0 );
155 /// Out = f * A * A^T for symmetric matrix Out [Note: Out will be symmetrized!].
156 void SyrkNT( FMatrixView &Out, FMatrixView const &A, FScalar fFactor = 1.0, uint Flags = 0 );
157 /// Out = f * A^T * A for symmetric matrix Out [Note: Out will be symmetrized!].
158 void SyrkTN( FMatrixView &Out, FMatrixView const &A, FScalar fFactor = 1.0, uint Flags = 0 );
159 
160 /// Chained matrix product: Out = A0 * A1 * A2....
161 void ChainMxm(FMatrixView Out, FMatrixView A0, FMemoryStack &Mem, uint Flags = 0);
162 void ChainMxm(FMatrixView Out, FMatrixView A0, FMatrixView A1, FMemoryStack &Mem, uint Flags = 0);
163 void ChainMxm(FMatrixView Out, FMatrixView A0, FMatrixView A1, FMatrixView A2, FMemoryStack &Mem, uint Flags = 0);
164 void ChainMxm(FMatrixView Out, FMatrixView A0, FMatrixView A1, FMatrixView A2, FMatrixView A3, FMemoryStack &Mem, uint Flags = 0);
165 void ChainMxm(FMatrixView Out, FMatrixView A0, FMatrixView A1, FMatrixView A2, FMatrixView A3, FMatrixView A4, FMemoryStack &Mem, uint Flags = 0);
166 void ChainMxm(FMatrixView Out, FMatrixView A0, FMatrixView A1, FMatrixView A2, FMatrixView A3, FMatrixView A4, FMatrixView A5, FMemoryStack &Mem, uint Flags = 0);
167 
168 
169 // Out = A * B, Out,B being vectors
170 void Mxva(FScalar *RESTRICT pOut, FMatrixView const &A, FScalar const *RESTRICT pIn);
171 // Out += A * B, Out,B being vectors
172 void Mxvb(FScalar *RESTRICT pOut, FMatrixView const &A, FScalar const *RESTRICT pIn);
173 /// trace of a matrix
174 FScalar Trace( FMatrixView const &In );
175 /// dot-product of two matrices. dot(A^T B) = Tr(A * B).
176 FScalar Dot( FMatrixView const &A, FMatrixView const &B );
177 /// scale matrix by a factor in place
178 void Scale( FMatrixView &Out, FScalar fFactor );
179 /// diagonalize a matrix in place and store the eigenvalues at pEigenValues.
180 /// Input matrix must be symmetric and pEigenValues must hold room for nRows==nCols
181 /// entries.
182 void Diagonalize( FMatrixView &InOut, FScalar *pEigenValues, FMemoryStack &Mem );
183 /// As Diagonalize(), but eigenvalues are returned in descending order.
184 void Diagonalize_LargeEwFirst( FMatrixView &InOut, FScalar *pEigenValues, FMemoryStack &Mem );
185 
186 // Factorize InAndTmp into U * diag(sigma) * Vt,
187 // where U and Vt are unitary. If InAndTmp is a nRows x nCols matrix,
188 // then on output U will have dimension nRows x nSig and Vt dimension nCols x nSig,
189 // where nSig = min(nRows,nCols).
190 // Note: Content of In is left alone (input is copied).
191 void ComputeSvd(FMatrixView &U, FScalar *pSigma, FMatrixView &Vt, FMatrixView In, FMemoryStack &Mem);
192 
193 // As ComputeSvd, but allocate output quantities (U, pSigma, and Vt) on Mem.
194 void AllocAndComputeSvd(FMatrixView &U, FScalar *&pSigma, FMatrixView &Vt, FMatrixView In, FMemoryStack &Mem);
195 void AllocAndComputeSvd(FMatrixView &U, FMatrixView &Sigma, FMatrixView &Vt, FMatrixView In, FMemoryStack &Mem);
196 
197 // calculate Cholesky factorization M = L * L^T
198 void CalcCholeskyFactors(FMatrixView M);
199 // invert lower triangular matrix L. Upper part ignored.
200 void InvertTriangularMatrix(FMatrixView L);
201 // Solve L X = B where L is a lower triangular matrix.
202 void TriangularSolve(FMatrixView RhsSol, FMatrixView const &L);
203 // set A := L * A where L is a lower triangular matrix (Side == 'L').
204 // or  A := A * L where L is a lower triangular matrix (Side == 'R')
205 void TriangularMxm(FMatrixView A, FMatrixView const &L, char Side, double fFactor = 1.0);
206 // solve linear least squares ||M * x - b||_2 -> min subject to ||x||_2 ->min
207 // using singular value decomposition of A. Singular values below fThrRel are
208 // treated as zero. A negative value indicates the usage of machine precision.
209 // Both M and RhsSol are overwritten.
210 //
211 // Notes:
212 //    - On input, RhsSol is the RHS and must have format M.nRows x nRhs
213 //      On output, RhsSol is the solution and is set to format M.nCols x nRhs
214 //    - This happens IN PLACE. That means that RhsSol must have a column stride
215 //      of at least std::max(M.nCols, M.nRows).
216 //    - Number of rows on RhsSol is adjusted as required on output.
217 void LeastSquaresSolve(FMatrixView M, FMatrixView &RhsSol, double fThrRel, FMemoryStack &Mem);
218 // Solve M * Sol == Rhs.
219 // Similar to LeastSquaresSolve, but Rhs and Sol are provided independently (and are copied
220 // if required for reasons of strides). Neither M nor Rhs is overwritten.
221 void LeastSquaresSolveSafe(FMatrixView const M, FMatrixView Sol, FMatrixView const Rhs, double fThrRel, FMemoryStack &Mem);
222 
223 
224 // // Solve X L = B where L is a lower triangular matrix.
225 // void TriangularSolveRight(FMatrixView RhsSol, FMatrixView const &L);
226 // Solve A X = B, where A = L*L^T and L is a lower triangular matrix.
227 void CholeskySolve(FMatrixView RhsAndSolution, FMatrixView const &L);
228 void CholeskySolve(double *pRhsAndSolution, FMatrixView const &L);
229 // Multiply B = A * X, where A = L*L^T and L is a lower triangular matrix.
230 void CholeskyMxm(FMatrixView RhsSol, FMatrixView const &L);
231 void CholeskyMxm(double *pRhsAndSolution, FMatrixView const &L);
232 
233 void Symmetrize(FMatrixView M, double Phase = 1);
234 
235 /// creates a view representing the sub-matrix of size nRow x nCol
236 /// starting at (iRow,iCol).
237 /// The returned view still references the original data (i.e., In and Out are aliased)
238 FMatrixView Select( FMatrixView const &In, size_t iRow, size_t iCol, size_t nRows, size_t nCols );
239 
240 // exactly same as above, but allowing for temporaries as first argument (C++ ftw...).
241 inline void Add0( FMatrixView Out, FMatrixView const &In, FScalar fFactor = 1.0 ) {
242     return Add(Out,In,fFactor);
243 }
244 inline void Move0( FMatrixView Out, FMatrixView const &In, FScalar fFactor = 1.0 ) {
245     return Move(Out, In, fFactor);
246 }
247 inline void Mxm0( FMatrixView Out, FMatrixView const &A, FMatrixView const &B, uint Flags = 0 ) {
248     return Mxm(Out, A, B, Flags);
249 }
250 inline void Mxm0( FMatrixView Out, FMatrixView const &A, FMatrixView const &B, FScalar fFactor, uint Flags = 0 ) {
251     return Mxm(Out, A, B, fFactor, Flags);
252 }
Scale0(FMatrixView Out,FScalar fFactor)253 inline void Scale0( FMatrixView Out, FScalar fFactor ) {
254     return Scale(Out, fFactor);
255 }
256 
257 
258 
259 // An interface to FMatrixView which owns its data.
260 // Will adjust to input dimensions of operations as long as the actual
261 // matrix size does not exceed nMaxSize.
262 struct FStackMatrix : public FMatrixView
263 {
FStackMatrixFStackMatrix264     FStackMatrix( size_t nMaxSize_, FMemoryStack *pMemoryStack_ )
265         : nMaxSize(nMaxSize_), pMemory(pMemoryStack_)
266     {
267         pMemory->Align(8);
268         pMemory->Alloc(pData, nMaxSize_);
269         Reshape(0,0);
270     };
271 
FStackMatrixFStackMatrix272     FStackMatrix( size_t nRows_, size_t nCols_, FMemoryStack *pMemoryStack_ )
273         : nMaxSize(nRows_*nCols_), pMemory(pMemoryStack_)
274     {
275         pMemory->Align(8);
276         pMemory->Alloc(pData, nMaxSize);
277         Reshape(nRows_, nCols_);
278     };
279 
~FStackMatrixFStackMatrix280     ~FStackMatrix(){ Free(); }
FreeFStackMatrix281     void Free() { if (pData) pMemory->Free(pData); pData = 0; }
ReshapeFStackMatrix282     void Reshape( size_t nRows_, size_t nCols_ ){
283         assert( nRows_ * nCols_ <= nMaxSize );
284         nRows = nRows_; nCols = nCols_; nRowSt = 1; nColSt = nRows_;
285     };
286 private:
287     size_t
288         nMaxSize;
289     FMemoryStack
290         *pMemory; // a
291 };
292 
293 // a matrix with data stored in a local heap-allocated array.
294 // Warning: This has *no* virtual destructor! and FMatrixView is not going to get one.
295 struct FHeapMatrix : public FMatrixView, public FIntrusivePtrDest
296 {
297     FHeapMatrix();
298     FHeapMatrix(FMatrixView v);
299     FHeapMatrix(size_t nRows, size_t nCols);
300     virtual ~FHeapMatrix();
301     void Reshape(size_t nRows_, size_t nCols_);
302 
303     FHeapMatrix(FHeapMatrix const &other);
304     void operator = (FHeapMatrix const &other);
305 protected:
306     TArray<FScalar>
307         m_Buffer;
308 };
309 typedef boost::intrusive_ptr<FHeapMatrix>
310     FHeapMatrixPtr;
311 
312 
313 /// Out = f * In
314 void Move( FStackMatrix &Out, FMatrixView const &In, FScalar fFactor = 1.0 );
315 /// Out = A * B
316 void Mxm( FStackMatrix &Out, FMatrixView const &A, FMatrixView const &B, uint Flags = 0 );
317 /// Out = f * A * B
318 void Mxm( FStackMatrix &Out, FMatrixView const &A, FMatrixView const &B, FScalar fFactor, uint Flags = 0 );
319 
320 void Move( FHeapMatrix &Out, FMatrixView const &In, FScalar fFactor = 1.0 );
321 
322 
323 // Note: the other operations from FMatrixView should work unchanged
324 // on FStackMatrix, because they do not require a change in the shape.
325 
326 void PrintMatrixGen( std::ostream &out, double const *pData,
327         size_t nRows, size_t nRowStride, size_t nCols, size_t nColStride, std::string const &Name );
328 void PrintMatrixGen( std::ostream &out, float const *pData,
329         size_t nRows, size_t nRowStride, size_t nCols, size_t nColStride, std::string const &Name );
330 
331 
332 struct FSmhOptions{
333     double
334         // ignore eigenvectors with eigenvalue < std::max(ThrAbs, MaxEw * ThrRel)
335         ThrAbs, ThrRel;
336     char const
337         *pDelMsg; // may be 0.
338     std::ostream
339         *pXout; // may be 0 if pDelMsg is 0.
340     FSmhOptions(double ThrAbs_ = 0, double ThrRel_ = 0, char const *pDelMsg_ = 0,
341             std::ostream *pXout_ = 0)
ThrAbsFSmhOptions342         : ThrAbs(ThrAbs_), ThrRel(ThrRel_), pDelMsg(pDelMsg_), pXout(pXout_)
343     {}
344 };
345 
346 // calculates M^{-1/2} in place.
347 void CalcSmhMatrix( FMatrixView M, FMemoryStack &Mem, FSmhOptions const &Opt );
348 // orthogonalizes C in place, where S is C's overlap matrix.
349 // both C and S are overwritten.
350 void OrthSchmidt1(FMatrixView S, FMatrixView C, FMemoryStack &Mem);
351 // symmetrically orthogonalize a set of vectors with respect to overlap matrix S.
352 void SymOrth(FMatrixView Orbs, FMatrixView const S, FMemoryStack &Mem, double fThrAbs=1e-15, double fThrRel=1e-15);
353 
354 
355 // permute rows of matrix such that
356 //    NewM[iRow,iCol] = OldM[P[iRow], iCol]
357 void PermuteRows(FMatrixView M, size_t const *P, FMemoryStack &Mem);
358 void Permute(FMatrixView &M, size_t const *pRowPerm, size_t const *pColPerm, FMemoryStack &Mem);
359 void WriteMatrixToFile(std::string const &FileName, FMatrixView const &M, std::string const &MatrixName, size_t *pRowPerm = 0, size_t *pColPerm = 0);
360 
361 void ArgSort1(size_t *pOrd, double const *pVals, size_t nValSt, size_t nVals, bool Reverse = false);
362 
363 
364 
365 
366 } // namespace ct
367 
368 #endif // CT8k_MATRIX_H
369 
370 // kate: space-indent on; tab-indent on; backspace-indent on; tab-width 4; indent-width 4; mixedindent off; indent-mode normal;
371