1 /**********************************************************************************************/
2 /* Fast symmetric matrix with dynamically expandable size. */
3 /* Only part can be used for matrix operations. It is defined as: */
4 /* fNCols: rows built by constructor (GetSizeBooked) */
5 /* fNRows: number of rows added dynamically (automatically added on assignment to row) */
7 /* fNRowIndex: total size (fNCols+fNRows), GetSize */
8 /* fRowLwb : actual size to used for given operation, by default = total size, GetSizeUsed */
10 /* Author: ruben.shahoyan@cern.ch */
12 /**********************************************************************************************/
20 #include "AliSymMatrix.h"
26 ClassImp(AliSymMatrix)
29 AliSymMatrix* AliSymMatrix::fgBuffer = 0;
30 Int_t AliSymMatrix::fgCopyCnt = 0;
31 //___________________________________________________________
32 AliSymMatrix::AliSymMatrix()
33 : fElems(0),fElemsAdd(0)
39 //___________________________________________________________
40 AliSymMatrix::AliSymMatrix(Int_t size)
41 : AliMatrixSq(),fElems(0),fElemsAdd(0)
45 fNrowIndex = fNcols = fRowLwb = size;
46 fElems = new Double_t[fNcols*(fNcols+1)/2];
53 //___________________________________________________________
54 AliSymMatrix::AliSymMatrix(const AliSymMatrix &src)
55 : AliMatrixSq(src),fElems(0),fElemsAdd(0)
57 fNrowIndex = fNcols = src.GetSize();
59 fRowLwb = src.GetSizeUsed();
61 int nmainel = fNcols*(fNcols+1)/2;
62 fElems = new Double_t[nmainel];
63 nmainel = src.fNcols*(src.fNcols+1)/2;
64 memcpy(fElems,src.fElems,nmainel*sizeof(Double_t));
65 if (src.GetSizeAdded()) { // transfer extra rows to main matrix
66 Double_t *pnt = fElems + nmainel;
67 int ncl = src.GetSizeBooked() + 1;
68 for (int ir=0;ir<src.GetSizeAdded();ir++) {
69 memcpy(pnt,src.fElemsAdd[ir],ncl*sizeof(Double_t));
81 //___________________________________________________________
82 AliSymMatrix::~AliSymMatrix()
85 if (--fgCopyCnt < 1 && fgBuffer) {delete fgBuffer; fgBuffer = 0;}
88 //___________________________________________________________
89 AliSymMatrix& AliSymMatrix::operator=(const AliSymMatrix& src)
93 TObject::operator=(src);
94 if (GetSizeBooked()!=src.GetSizeBooked() && GetSizeAdded()!=src.GetSizeAdded()) {
95 // recreate the matrix
96 if (fElems) delete[] fElems;
97 for (int i=0;i<GetSizeAdded();i++) delete[] fElemsAdd[i];
100 fNrowIndex = src.GetSize();
101 fNcols = src.GetSize();
103 fRowLwb = src.GetSizeUsed();
104 fElems = new Double_t[GetSize()*(GetSize()+1)/2];
105 int nmainel = src.GetSizeBooked()*(src.GetSizeBooked()+1);
106 memcpy(fElems,src.fElems,nmainel*sizeof(Double_t));
107 if (src.GetSizeAdded()) { // transfer extra rows to main matrix
108 Double_t *pnt = fElems + nmainel*sizeof(Double_t);
109 int ncl = src.GetSizeBooked() + 1;
110 for (int ir=0;ir<src.GetSizeAdded();ir++) {
112 memcpy(pnt,src.fElemsAdd[ir],ncl*sizeof(Double_t));
113 pnt += ncl*sizeof(Double_t);
119 memcpy(fElems,src.fElems,GetSizeBooked()*(GetSizeBooked()+1)/2*sizeof(Double_t));
120 int ncl = GetSizeBooked() + 1;
121 for (int ir=0;ir<GetSizeAdded();ir++) { // dynamic rows
123 memcpy(fElemsAdd[ir],src.fElemsAdd[ir],ncl*sizeof(Double_t));
131 //___________________________________________________________
132 AliSymMatrix& AliSymMatrix::operator+=(const AliSymMatrix& src)
135 if (GetSizeUsed() != src.GetSizeUsed()) {
136 AliError("Matrix sizes are different");
139 for (int i=0;i<GetSizeUsed();i++) for (int j=i;j<GetSizeUsed();j++) (*this)(j,i) += src(j,i);
143 //___________________________________________________________
144 void AliSymMatrix::Clear(Option_t*)
146 if (fElems) {delete[] fElems; fElems = 0;}
149 for (int i=0;i<GetSizeAdded();i++) delete[] fElemsAdd[i];
153 fNrowIndex = fNcols = fNrows = fRowLwb = 0;
157 //___________________________________________________________
158 Float_t AliSymMatrix::GetDensity() const
160 // get fraction of non-zero elements
162 for (int i=GetSizeUsed();i--;) for (int j=i+1;j--;) if (TMath::Abs(GetEl(i,j))>DBL_MIN) nel++;
163 return 2.*nel/( (GetSizeUsed()+1)*GetSizeUsed() );
166 //___________________________________________________________
167 void AliSymMatrix::Print(Option_t* option) const
169 printf("Symmetric Matrix: Size = %d (%d rows added dynamically), %d used\n",GetSize(),GetSizeAdded(),GetSizeUsed());
170 TString opt = option; opt.ToLower();
171 if (opt.IsNull()) return;
172 opt = "%"; opt += 1+int(TMath::Log10(double(GetSize()))); opt+="d|";
173 for (Int_t i=0;i<GetSizeUsed();i++) {
175 for (Int_t j=0;j<=i;j++) printf("%+.3e|",GetEl(i,j));
180 //___________________________________________________________
181 void AliSymMatrix::MultiplyByVec(Double_t *vecIn,Double_t *vecOut) const
183 // fill vecOut by matrix*vecIn
184 // vector should be of the same size as the matrix
185 for (int i=GetSizeUsed();i--;) {
187 for (int j=GetSizeUsed();j--;) vecOut[i] += vecIn[j]*GetEl(i,j);
192 //___________________________________________________________
193 AliSymMatrix* AliSymMatrix::DecomposeChol()
195 // Return a matrix with Choleski decomposition
196 // Adopted from Numerical Recipes in C, ch.2-9, http://www.nr.com
197 // consturcts Cholesky decomposition of SYMMETRIC and
198 // POSITIVELY-DEFINED matrix a (a=L*Lt)
199 // Only upper triangle of the matrix has to be filled.
200 // In opposite to function from the book, the matrix is modified:
201 // lower triangle and diagonal are refilled.
203 if (!fgBuffer || fgBuffer->GetSizeUsed()!=GetSizeUsed()) {
206 fgBuffer = new AliSymMatrix(*this);
209 printf("Failed to allocate memory for Choleski decompostions\n");
213 else (*fgBuffer) = *this;
215 AliSymMatrix& mchol = *fgBuffer;
217 for (int i=0;i<GetSizeUsed();i++) {
218 Double_t *rowi = mchol.GetRow(i);
219 for (int j=i;j<GetSizeUsed();j++) {
220 Double_t *rowj = mchol.GetRow(j);
221 double sum = rowj[i];
222 for (int k=i-1;k>=0;k--) if (rowi[k]&&rowj[k]) sum -= rowi[k]*rowj[k];
224 if (sum <= 0.0) { // not positive-definite
225 printf("The matrix is not positive definite [%e]\n"
226 "Choleski decomposition is not possible\n",sum);
229 rowi[i] = TMath::Sqrt(sum);
231 } else rowj[i] = sum/rowi[i];
237 //___________________________________________________________
238 Bool_t AliSymMatrix::InvertChol()
240 // Invert matrix using Choleski decomposition
242 AliSymMatrix* mchol = DecomposeChol();
244 printf("Failed to invert the matrix\n");
253 //___________________________________________________________
254 void AliSymMatrix::InvertChol(AliSymMatrix* pmchol)
256 // Invert matrix using Choleski decomposition, provided the Cholseki's L matrix
259 AliSymMatrix& mchol = *pmchol;
261 // Invert decomposed triangular L matrix (Lower triangle is filled)
262 for (int i=0;i<GetSizeUsed();i++) {
263 mchol(i,i) = 1.0/mchol(i,i);
264 for (int j=i+1;j<GetSizeUsed();j++) {
265 Double_t *rowj = mchol.GetRow(j);
267 for (int k=i;k<j;k++) if (rowj[k]) {
268 double &mki = mchol(k,i); if (mki) sum -= rowj[k]*mki;
270 rowj[i] = sum/rowj[j];
274 // take product of the inverted Choleski L matrix with its transposed
275 for (int i=GetSizeUsed();i--;) {
276 for (int j=i+1;j--;) {
278 for (int k=i;k<GetSizeUsed();k++) {
279 double &mik = mchol(i,k);
281 double &mjk = mchol(j,k);
282 if (mjk) sum += mik*mjk;
292 //___________________________________________________________
293 Bool_t AliSymMatrix::SolveChol(Double_t *b, Bool_t invert)
295 // Adopted from Numerical Recipes in C, ch.2-9, http://www.nr.com
296 // Solves the set of n linear equations A x = b,
297 // where a is a positive-definite symmetric matrix.
298 // a[1..n][1..n] is the output of the routine CholDecomposw.
299 // Only the lower triangle of a is accessed. b[1..n] is input as the
300 // right-hand side vector. The solution vector is returned in b[1..n].
305 AliSymMatrix *pmchol = DecomposeChol();
307 printf("SolveChol failed\n");
311 AliSymMatrix& mchol = *pmchol;
313 for (i=0;i<GetSizeUsed();i++) {
314 Double_t *rowi = mchol.GetRow(i);
315 for (sum=b[i],k=i-1;k>=0;k--) if (rowi[k]&&b[k]) sum -= rowi[k]*b[k];
319 for (i=GetSizeUsed()-1;i>=0;i--) {
320 for (sum=b[i],k=i+1;k<GetSizeUsed();k++) if (b[k]) {
321 double &mki=mchol(k,i); if (mki) sum -= mki*b[k];
326 if (invert) InvertChol(pmchol);
331 //___________________________________________________________
332 Bool_t AliSymMatrix::SolveChol(TVectorD &b, Bool_t invert)
334 return SolveChol((Double_t*)b.GetMatrixArray(),invert);
338 //___________________________________________________________
339 Bool_t AliSymMatrix::SolveChol(Double_t *brhs, Double_t *bsol,Bool_t invert)
341 memcpy(bsol,brhs,GetSizeUsed()*sizeof(Double_t));
342 return SolveChol(bsol,invert);
345 //___________________________________________________________
346 Bool_t AliSymMatrix::SolveChol(TVectorD &brhs, TVectorD &bsol,Bool_t invert)
349 return SolveChol(bsol,invert);
352 //___________________________________________________________
353 void AliSymMatrix::AddRows(int nrows)
356 Double_t **pnew = new Double_t*[nrows+fNrows];
357 for (int ir=0;ir<fNrows;ir++) pnew[ir] = fElemsAdd[ir]; // copy old extra rows
358 for (int ir=0;ir<nrows;ir++) {
359 int ncl = GetSize()+1;
360 pnew[fNrows] = new Double_t[ncl];
361 memset(pnew[fNrows],0,ncl*sizeof(Double_t));
371 //___________________________________________________________
372 void AliSymMatrix::Reset()
374 // if additional rows exist, regularize it
377 for (int i=0;i<fNrows;i++) delete[] fElemsAdd[i];
378 delete[] fElemsAdd; fElemsAdd = 0;
379 fNcols = fRowLwb = fNrowIndex;
380 fElems = new Double_t[GetSize()*(GetSize()+1)/2];
383 if (fElems) memset(fElems,0,GetSize()*(GetSize()+1)/2*sizeof(Double_t));
387 //___________________________________________________________
389 void AliSymMatrix::AddToRow(Int_t r, Double_t *valc,Int_t *indc,Int_t n)
391 // for (int i=n;i--;) {
392 // (*this)(indc[i],r) += valc[i];
398 AddRows(r-fNrowIndex+1);
399 row = &((fElemsAdd[r-fNcols])[0]);
401 else row = &fElems[GetIndex(r,0)];
405 if (indc[i]>r) continue;
406 row[indc[i]] += valc[i];
409 if (nadd == n) return;
413 if (indc[i]>r) (*this)(indc[i],r) += valc[i];
419 //___________________________________________________________
420 Double_t* AliSymMatrix::GetRow(Int_t r)
424 AddRows(r-GetSize()+1);
425 printf("create %d of %d\n",r, nn);
426 return &((fElemsAdd[r-GetSizeBooked()])[0]);
428 else return &fElems[GetIndex(r,0)];
432 //___________________________________________________________
433 int AliSymMatrix::SolveSpmInv(double *vecB, Bool_t stabilize)
435 // Solution a la MP1: gaussian eliminations
436 /// Obtain solution of a system of linear equations with symmetric matrix
437 /// and the inverse (using 'singular-value friendly' GAUSS pivot)
444 int nGlo = GetSizeUsed();
445 bool *bUnUsed = new bool[nGlo];
446 double *rowMax,*colMax=0;
447 rowMax = new double[nGlo];
450 colMax = new double[nGlo];
451 for (Int_t i=nGlo; i--;) rowMax[i] = colMax[i] = 0.0;
452 for (Int_t i=nGlo; i--;) for (Int_t j=i+1;j--;) {
453 double vl = TMath::Abs(Query(i,j));
454 if (vl<DBL_MIN) continue;
455 if (vl > rowMax[i]) rowMax[i] = vl; // Max elemt of row i
456 if (vl > colMax[j]) colMax[j] = vl; // Max elemt of column j
458 if (vl > rowMax[j]) rowMax[j] = vl; // Max elemt of row j
459 if (vl > colMax[i]) colMax[i] = vl; // Max elemt of column i
462 for (Int_t i=nGlo; i--;) {
463 if (TMath::Abs(rowMax[i])>DBL_MIN) rowMax[i] = 1./rowMax[i]; // Max elemt of row i
464 if (TMath::Abs(colMax[i])>DBL_MIN) colMax[i] = 1./colMax[i]; // Max elemt of column i
469 for (Int_t i=nGlo; i--;) bUnUsed[i] = true;
471 if (!fgBuffer || fgBuffer->GetSizeUsed()!=GetSizeUsed()) {
474 fgBuffer = new AliSymMatrix(*this);
477 printf("Failed to allocate memory for matrix inversion buffer\n");
481 else (*fgBuffer) = *this;
483 if (stabilize) for (int i=0;i<nGlo; i++) { // Small loop for matrix equilibration (gives a better conditioning)
484 for (int j=0;j<=i; j++) {
485 double vl = Query(i,j);
486 if (TMath::Abs(vl)>DBL_MIN) SetEl(i,j, TMath::Sqrt(rowMax[i])*vl*TMath::Sqrt(colMax[j]) ); // Equilibrate the V matrix
488 for (int j=i+1;j<nGlo;j++) {
489 double vl = Query(j,i);
490 if (TMath::Abs(vl)>DBL_MIN) fgBuffer->SetEl(j,i,TMath::Sqrt(rowMax[i])*vl*TMath::Sqrt(colMax[j]) ); // Equilibrate the V matrix
494 for (Int_t j=nGlo; j--;) fgBuffer->DiagElem(j) = TMath::Abs(QueryDiag(j)); // save diagonal elem absolute values
496 for (Int_t i=0; i<nGlo; i++) {
500 for (Int_t j=0; j<nGlo; j++) { // First look for the pivot, ie max unused diagonal element
502 if (bUnUsed[j] && (TMath::Abs(vl=QueryDiag(j))>TMath::Max(TMath::Abs(vPivot),eps*fgBuffer->QueryDiag(j)))) {
508 if (iPivot >= 0) { // pivot found
510 bUnUsed[iPivot] = false; // This value is used
512 DiagElem(iPivot) = -vPivot; // Replace pivot by its inverse
514 for (Int_t j=0; j<nGlo; j++) {
515 for (Int_t jj=0; jj<nGlo; jj++) {
516 if (j != iPivot && jj != iPivot) {// Other elements (!!! do them first as you use old matV[k][j]'s !!!)
517 double &r = j>=jj ? (*this)(j,jj) : (*fgBuffer)(jj,j);
518 r -= vPivot* ( j>iPivot ? Query(j,iPivot) : fgBuffer->Query(iPivot,j) )
519 * ( iPivot>jj ? Query(iPivot,jj) : fgBuffer->Query(jj,iPivot));
524 for (Int_t j=0; j<nGlo; j++) if (j != iPivot) { // Pivot row or column elements
525 (*this)(j,iPivot) *= vPivot;
526 (*fgBuffer)(iPivot,j) *= vPivot;
530 else { // No more pivot value (clear those elements)
531 for (Int_t j=0; j<nGlo; j++) {
534 for (Int_t k=0; k<nGlo; k++) {
536 if (j!=k) (*fgBuffer)(j,k) = 0;
540 break; // No more pivots anyway, stop here
544 for (Int_t i=0; i<nGlo; i++) for (Int_t j=0; j<nGlo; j++) {
545 double vl = TMath::Sqrt(colMax[i])*TMath::Sqrt(rowMax[j]); // Correct matrix V
546 if (i>=j) (*this)(i,j) *= vl;
547 else (*fgBuffer)(j,i) *= vl;
550 for (Int_t j=0; j<nGlo; j++) {
552 for (Int_t jj=0; jj<nGlo; jj++) { // Reverse matrix elements
554 if (j>=jj) vl = (*this)(j,jj) = -Query(j,jj);
555 else vl = (*fgBuffer)(j,jj) = -fgBuffer->Query(j,jj);
556 rowMax[j] += vl*vecB[jj];
560 for (Int_t j=0; j<nGlo; j++) {
561 vecB[j] = rowMax[j]; // The final result
566 if (stabilize) delete [] colMax;