// @(#)root/matrix:$Name: $:$Id: TMatrixD.cxx,v 1.48 2003/10/28 22:39:30 brun Exp $
// Author: Fons Rademakers 03/11/97
/*************************************************************************
* Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/
//////////////////////////////////////////////////////////////////////////
// //
// Linear Algebra Package //
// //
// The present package implements all the basic algorithms dealing //
// with vectors, matrices, matrix columns, rows, diagonals, etc. //
// //
// Matrix elements are arranged in memory in a COLUMN-wise //
// fashion (in FORTRAN's spirit). In fact, it makes it very easy to //
// feed the matrices to FORTRAN procedures, which implement more //
// elaborate algorithms. //
// //
// Unless otherwise specified, matrix and vector indices always start //
// with 0, spanning up to the specified limit-1. However, there are //
// constructors to which one can specify aribtrary lower and upper //
// bounds, e.g. TMatrixD m(1,10,1,5) defines a matrix that ranges, and //
// that can be addresses, from 1..10, 1..5 (a(1,1)..a(10,5)). //
// //
// The present package provides all facilities to completely AVOID //
// returning matrices. Use "TMatrixD A(TMatrixD::kTransposed,B);" and //
// other fancy constructors as much as possible. If one really needs //
// to return a matrix, return a TLazyMatrixD object instead. The //
// conversion is completely transparent to the end user, e.g. //
// "TMatrixD m = THaarMatrix(5);" and _is_ efficient. //
// //
// Since TMatrixD et al. are fully integrated in ROOT they of course //
// can be stored in a ROOT database. //
// //
// //
// How to efficiently use this package //
// ----------------------------------- //
// //
// 1. Never return complex objects (matrices or vectors) //
// Danger: For example, when the following snippet: //
// TMatrixD foo(int n) //
// { //
// TMatrixD foom(n,n); fill_in(foom); return foom; //
// } //
// TMatrixD m = foo(5); //
// runs, it constructs matrix foo:foom, copies it onto stack as a //
// return value and destroys foo:foom. Return value (a matrix) //
// from foo() is then copied over to m (via a copy constructor), //
// and the return value is destroyed. So, the matrix constructor is //
// called 3 times and the destructor 2 times. For big matrices, //
// the cost of multiple constructing/copying/destroying of objects //
// may be very large. *Some* optimized compilers can cut down on 1 //
// copying/destroying, but still it leaves at least two calls to //
// the constructor. Note, TLazyMatrices (see below) can construct //
// TMatrixD m "inplace", with only a _single_ call to the //
// constructor. //
// //
// 2. Use "two-address instructions" //
// "void TMatrixD::operator += (const TMatrixD &B);" //
// as much as possible. //
// That is, to add two matrices, it's much more efficient to write //
// A += B; //
// than //
// TMatrixD C = A + B; //
// (if both operand should be preserved, //
// TMatrixD C = A; C += B; //
// is still better). //
// //
// 3. Use glorified constructors when returning of an object seems //
// inevitable: //
// "TMatrixD A(TMatrixD::kTransposed,B);" //
// "TMatrixD C(A,TMatrixD::kTransposeMult,B);" //
// //
// like in the following snippet (from $ROOTSYS/test/vmatrix.cxx) //
// that verifies that for an orthogonal matrix T, T'T = TT' = E. //
// //
// TMatrixD haar = THaarMatrix(5); //
// TMatrixD unit(TMatrixD::kUnit,haar); //
// TMatrixD haar_t(TMatrixD::kTransposed,haar); //
// TMatrixD hth(haar,TMatrixD::kTransposeMult,haar); //
// TMatrixD hht(haar,TMatrixD::kMult,haar_t); //
// TMatrixD hht1 = haar; hht1 *= haar_t; //
// VerifyMatrixIdentity(unit,hth); //
// VerifyMatrixIdentity(unit,hht); //
// VerifyMatrixIdentity(unit,hht1); //
// //
// 4. Accessing row/col/diagonal of a matrix without much fuss //
// (and without moving a lot of stuff around): //
// //
// TMatrixD m(n,n); TVectorD v(n); TMatrixDDiag(m) += 4; //
// v = TMatrixDRow(m,0); //
// TMatrixDColumn m1(m,1); m1(2) = 3; // the same as m(2,1)=3; //
// Note, constructing of, say, TMatrixDDiag does *not* involve any //
// copying of any elements of the source matrix. //
// //
// 5. It's possible (and encouraged) to use "nested" functions //
// For example, creating of a Hilbert matrix can be done as follows: //
// //
// void foo(const TMatrixD &m) //
// { //
// TMatrixD m1(TMatrixD::kZero,m); //
// struct MakeHilbert : public TElementPosAction { //
// void Operation(Double_t &element) { element = 1./(fI+fJ-1); } //
// }; //
// m1.Apply(MakeHilbert()); //
// } //
// //
// of course, using a special method THilbertMatrix() is //
// still more optimal, but not by a whole lot. And that's right, //
// class MakeHilbert is declared *within* a function and local to //
// that function. It means one can define another MakeHilbert class //
// (within another function or outside of any function, that is, in //
// the global scope), and it still will be OK. Note, this currently //
// is not yet supported by the interpreter CINT. //
// //
// Another example is applying of a simple function to each matrix //
// element: //
// //
// void foo(TMatrixD &m, TMatrixD &m1) //
// { //
// typedef double (*dfunc_t)(double); //
// class ApplyFunction : public TElementActionD { //
// dfunc_t fFunc; //
// void Operation(Double_t &element) { element=fFunc(element); } //
// public: //
// ApplyFunction(dfunc_t func):fFunc(func) {} //
// }; //
// ApplyFunction x(TMath::Sin); //
// m.Apply(x); //
// } //
// //
// Validation code $ROOTSYS/test/vmatrix.cxx and vvector.cxx contain //
// a few more examples of that kind. //
// //
// 6. Lazy matrices: instead of returning an object return a "recipe" //
// how to make it. The full matrix would be rolled out only when //
// and where it's needed: //
// TMatrixD haar = THaarMatrix(5); //
// THaarMatrix() is a *class*, not a simple function. However //
// similar this looks to a returning of an object (see note #1 //
// above), it's dramatically different. THaarMatrix() constructs a //
// TLazyMatrixD, an object of just a few bytes long. A
// "TMatrixD(const TLazyMatrixD &recipe)" constructor follows the //
// recipe and makes the matrix haar() right in place. No matrix //
// element is moved whatsoever! //
// //
// The implementation is based on original code by //
// Oleg E. Kiselyov (oleg@pobox.com). //
// //
//////////////////////////////////////////////////////////////////////////
#include "TMatrixD.h"
#include "TROOT.h"
#include "TClass.h"
#include "TPluginManager.h"
#include "TVirtualUtilHist.h"
#include "TMatrixDUtils.h"
ClassImp(TMatrixD)
//______________________________________________________________________________
TMatrixD::TMatrixD(Int_t no_rows, Int_t no_cols)
{
Allocate(no_rows, no_cols);
}
//______________________________________________________________________________
TMatrixD::TMatrixD(Int_t row_lwb, Int_t row_upb, Int_t col_lwb, Int_t col_upb)
{
Allocate(row_upb-row_lwb+1, col_upb-col_lwb+1, row_lwb, col_lwb);
}
//______________________________________________________________________________
TMatrixD::TMatrixD(Int_t no_rows, Int_t no_cols,
const Double_t *elements, Option_t *option)
{
// option="F": array elements contains the matrix stored column-wise
// like in Fortran, so a[i,j] = elements[i+no_rows*j],
// else it is supposed that array elements are stored row-wise
// a[i,j] = elements[i*no_cols+j]
Allocate(no_rows, no_cols);
SetElements(elements,option);
}
//______________________________________________________________________________
TMatrixD::TMatrixD(Int_t row_lwb, Int_t row_upb, Int_t col_lwb, Int_t col_upb,
const Double_t *elements, Option_t *option)
{
Allocate(row_upb-row_lwb+1, col_upb-col_lwb+1, row_lwb, col_lwb);
SetElements(elements,option);
}
//______________________________________________________________________________
TMatrixD::TMatrixD(EMatrixCreatorsOp1 op, const TMatrixD &prototype)
{
// Create a matrix applying a specific operation to the prototype.
// Example: TMatrixD a(10,12); ...; TMatrixD b(TMatrixD::kTransposed, a);
// Supported operations are: kZero, kUnit, kTransposed, kInverted and kInvertedPosDef.
Invalidate();
if (!prototype.IsValid()) {
Error("TMatrixD(EMatrixCreatorOp1)", "prototype matrix not initialized");
return;
}
switch(op) {
case kZero:
Allocate(prototype.fNrows, prototype.fNcols,
prototype.fRowLwb, prototype.fColLwb);
break;
case kUnit:
Allocate(prototype.fNrows, prototype.fNcols,
prototype.fRowLwb, prototype.fColLwb);
UnitMatrix();
break;
case kTransposed:
Transpose(prototype);
break;
case kInverted:
Invert(prototype);
break;
case kInvertedPosDef:
InvertPosDef(prototype);
break;
default:
Error("TMatrixD(EMatrixCreatorOp1)", "operation %d not yet implemented", op);
}
}
//______________________________________________________________________________
TMatrixD::TMatrixD(const TMatrixD &a, EMatrixCreatorsOp2 op, const TMatrixD &b)
{
// Create a matrix applying a specific operation to two prototypes.
// Example: TMatrixD a(10,12), b(12,5); ...; TMatrixD c(a, TMatrixD::kMult, b);
// Supported operations are: kMult (a*b), kTransposeMult (a'*b),
// kInvMult,kInvPosDefMult (a^(-1)*b) and kAtBA (a'*b*a).
Invalidate();
if (!a.IsValid()) {
Error("TMatrixD(EMatrixCreatorOp2)", "matrix a not initialized");
return;
}
if (!b.IsValid()) {
Error("TMatrixD(EMatrixCreatorOp2)", "matrix b not initialized");
return;
}
switch(op) {
case kMult:
AMultB(a, b);
break;
case kTransposeMult:
AtMultB(a, b);
break;
default:
Error("TMatrixD(EMatrixCreatorOp2)", "operation %d not yet implemented", op);
}
}
//______________________________________________________________________________
TMatrixD::TMatrixD(const TMatrixD &another) : TObject(another)
{
if (another.IsValid()) {
Allocate(another.fNrows, another.fNcols, another.fRowLwb, another.fColLwb);
*this = another;
} else
Error("TMatrixD(const TMatrixD&)", "other matrix is not valid");
}
//______________________________________________________________________________
TMatrixD::TMatrixD(const TLazyMatrixD &lazy_constructor)
{
Allocate(lazy_constructor.fRowUpb-lazy_constructor.fRowLwb+1,
lazy_constructor.fColUpb-lazy_constructor.fColLwb+1,
lazy_constructor.fRowLwb, lazy_constructor.fColLwb);
lazy_constructor.FillIn(*this);
}
//______________________________________________________________________________
void TMatrixD::Allocate(Int_t no_rows, Int_t no_cols, Int_t row_lwb, Int_t col_lwb)
{
// Allocate new matrix. Arguments are number of rows, columns, row
// lowerbound (0 default) and column lowerbound (0 default).
Invalidate();
if (no_rows <= 0) {
Error("Allocate", "no of rows has to be positive");
return;
}
if (no_cols <= 0) {
Error("Allocate", "no of columns has to be positive");
return;
}
fNrows = no_rows;
fNcols = no_cols;
fRowLwb = row_lwb;
fColLwb = col_lwb;
fNelems = fNrows * fNcols;
fElements = new Double_t[fNelems];
if (fElements)
memset(fElements, 0, fNelems*sizeof(Double_t));
if (fNcols == 1) { // Only one col - fIndex is dummy actually
fIndex = &fElements;
return;
}
fIndex = new Double_t*[fNcols];
if (fIndex)
memset(fIndex, 0, fNcols*sizeof(Double_t*));
Int_t i;
Double_t *col_p;
for (i = 0, col_p = &fElements[0]; i < fNcols; i++, col_p += fNrows)
fIndex[i] = col_p;
}
//______________________________________________________________________________
TMatrixD::~TMatrixD()
{
// TMatrixD destructor.
Clear();
Invalidate();
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Apply(const TElementActionD &action)
{
if (!IsValid())
Error("Apply(TElementActionD&)", "matrix not initialized");
else
for (Double_t *ep = fElements; ep < fElements+fNelems; ep++)
action.Operation(*ep);
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Apply(const TElementPosActionD &action)
{
// Apply action to each element of the matrix. To action the location
// of the current element is passed. The matrix is traversed in the
// natural (that is, column by column) order.
if (!IsValid()) {
Error("Apply(TElementPosActionD&)", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
for (action.fJ = fColLwb; action.fJ < fColLwb+fNcols; action.fJ++)
for (action.fI = fRowLwb; action.fI < fRowLwb+fNrows; action.fI++)
action.Operation(*ep++);
Assert(ep == fElements+fNelems);
return *this;
}
//______________________________________________________________________________
void TMatrixD::Clear(Option_t *)
{
// delete dynamic structures
if (IsValid()) {
if (fNcols > 1) {
delete [] fIndex; fIndex = 0;
}
delete [] fElements; fElements = 0;
}
}
//______________________________________________________________________________
void TMatrixD::Draw(Option_t *option)
{
// Draw this matrix using an intermediate histogram
// The histogram is named "TMatrixD" by default and no title
//create the hist utility manager (a plugin)
TVirtualUtilHist *util = (TVirtualUtilHist*)gROOT->GetListOfSpecials()->FindObject("R__TVirtualUtilHist");
if (!util) {
TPluginHandler *h;
if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualUtilHist"))) {
if (h->LoadPlugin() == -1)
return;
h->ExecPlugin(0);
util = (TVirtualUtilHist*)gROOT->GetListOfSpecials()->FindObject("R__TVirtualUtilHist");
}
}
util->PaintMatrix(*this,option);
}
//______________________________________________________________________________
void TMatrixD::GetElements(Double_t *elements, Option_t *option) const
{
if (!IsValid()) {
Error("GetElements", "matrix is not initialized");
return;
}
TString opt = option;
opt.ToUpper();
if (opt.Contains("F"))
memcpy(elements,fElements,fNelems*sizeof(Double_t));
else
{
for (Int_t irow = 0; irow < fNrows; irow++)
{
for (Int_t icol = 0; icol < fNcols; icol++)
elements[irow+icol*fNrows] = fElements[irow*fNcols+icol];
}
}
}
//______________________________________________________________________________
void TMatrixD::ResizeTo(Int_t nrows, Int_t ncols)
{
// Set size of the matrix to nrows x ncols
// New dynamic elemenst are created, the overlapping part of the old ones are
// copied to the new structures, then the old elements are deleleted.
if (IsValid()) {
if (fNrows == nrows && fNcols == ncols)
return;
Double_t *elements_old = fElements;
Double_t **index_old = fIndex;
const Int_t nrows_old = fNrows;
const Int_t ncols_old = fNcols;
Allocate(nrows, ncols);
if (!IsValid()) {
Error("ResizeTo", "resizing failed");
return;
}
const Int_t ncols_copy = TMath::Min(fNcols,ncols_old);
const Int_t nrows_copy = TMath::Min(fNrows,nrows_old);
for (Int_t i = 0; i < ncols_copy; i++) {
memcpy(fIndex[i],index_old[i],nrows_copy*sizeof(Double_t));
}
if (ncols_old != 1) delete [] index_old;
delete [] elements_old;
} else {
Allocate(nrows, ncols);
}
}
//______________________________________________________________________________
void TMatrixD::ResizeTo(Int_t row_lwb, Int_t row_upb, Int_t col_lwb, Int_t col_upb)
{
// Set size of the matrix to [row_lwb:row_upb] x [col_lwb:col_upb]
// New dynamic elemenst are created, the overlapping part of the old ones are
// copied to the new structures, then the old elements are deleleted.
const Int_t new_nrows = row_upb - row_lwb + 1;
const Int_t new_ncols = col_upb - col_lwb + 1;
if (IsValid()) {
if (fNrows == new_nrows && fNcols == new_ncols &&
fRowLwb == row_lwb && fColLwb == col_lwb)
return;
Double_t *elements_old = fElements;
Double_t **index_old = fIndex;
const Int_t nrows_old = fNrows;
const Int_t ncols_old = fNcols;
const Int_t rowLwb_old = fRowLwb;
const Int_t colLwb_old = fColLwb;
Allocate(new_nrows, new_ncols, row_lwb, col_lwb);
if (!IsValid()) {
Error("ResizeTo", "resizing failed");
return;
}
const Int_t rowLwb_copy = TMath::Max(fRowLwb,rowLwb_old);
const Int_t colLwb_copy = TMath::Max(fColLwb,colLwb_old);
const Int_t rowUpb_copy = TMath::Min(fRowLwb+fNrows-1,rowLwb_old+nrows_old-1);
const Int_t colUpb_copy = TMath::Min(fColLwb+fNcols-1,colLwb_old+ncols_old-1);
const Int_t nrows_copy = rowUpb_copy-rowLwb_copy+1;
const Int_t ncols_copy = colUpb_copy-colLwb_copy+1;
if (nrows_copy > 0 && ncols_copy > 0) {
for (Int_t i = 0; i < ncols_copy; i++) {
const Int_t iColOld = colLwb_copy+i-colLwb_old;
const Int_t iColNew = colLwb_copy+i-fColLwb;
const Int_t rowOldOff = rowLwb_copy-rowLwb_old;
const Int_t rowNewOff = rowLwb_copy-fRowLwb;
memcpy(fIndex[iColNew]+rowNewOff,index_old[iColOld]+rowOldOff,nrows_copy*sizeof(Double_t));
}
}
if (ncols_old != 1) delete [] index_old;
delete [] elements_old;
} else {
Allocate(new_nrows, new_ncols, row_lwb, col_lwb);
}
}
//______________________________________________________________________________
void TMatrixD::ResizeTo(const TMatrixD &m)
{
ResizeTo(m.GetRowLwb(), m.GetRowUpb(), m.GetColLwb(), m.GetColUpb());
}
//______________________________________________________________________________
TMatrixD &TMatrixD::MakeSymmetric()
{
// symmetrize matrix (matrix needs to be a square one).
if (!IsValid()) {
Error("MakeSymmetric", "matrix not initialized");
return *this;
}
if (fNrows != fNcols) {
Error("MakeSymmetric", "matrix to symmetrize must be square");
return *this;
}
Int_t irow;
for (irow = 0; irow < fNrows; irow++) {
Int_t icol;
for (icol = 0; icol < irow; icol++) {
fElements[irow*fNrows+icol] += fElements[icol*fNrows+irow];
fElements[irow*fNrows+icol] /= 2.0;
fElements[icol*fNrows+irow] = fElements[irow*fNrows+icol];
}
}
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::UnitMatrix()
{
// make a unit matrix (matrix need not be a square one). The matrix
// is traversed in the natural (that is, column by column) order.
if (!IsValid()) {
Error("UnitMatrix", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
Int_t i, j;
for (j = 0; j < fNcols; j++)
for (i = 0; i < fNrows; i++)
*ep++ = (i==j ? 1.0 : 0.0);
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator=(Double_t val)
{
// Assign val to every element of the matrix.
if (!IsValid()) {
Error("operator=", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
*ep++ = val;
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator+=(Double_t val)
{
// Add val to every element of the matrix.
if (!IsValid()) {
Error("operator+=", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
*ep++ += val;
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator-=(Double_t val)
{
// Subtract val from every element of the matrix.
if (!IsValid()) {
Error("operator-=", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
*ep++ -= val;
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator*=(Double_t val)
{
// Multiply every element of the matrix with val.
if (!IsValid()) {
Error("operator*=", "matrix not initialized");
return *this;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
*ep++ *= val;
return *this;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator==(Double_t val) const
{
// Are all matrix elements equal to val?
if (!IsValid()) {
Error("operator==", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ == val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator!=(Double_t val) const
{
// Are all matrix elements not equal to val?
if (!IsValid()) {
Error("operator!=", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ != val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator<(Double_t val) const
{
// Are all matrix elements < val?
if (!IsValid()) {
Error("operator<", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ < val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator<=(Double_t val) const
{
// Are all matrix elements <= val?
if (!IsValid()) {
Error("operator<=", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ <= val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator>(Double_t val) const
{
// Are all matrix elements > val?
if (!IsValid()) {
Error("operator>", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ > val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
Bool_t TMatrixD::operator>=(Double_t val) const
{
// Are all matrix elements >= val?
if (!IsValid()) {
Error("operator>=", "matrix not initialized");
return kFALSE;
}
Double_t *ep = fElements;
while (ep < fElements+fNelems)
if (!(*ep++ >= val))
return kFALSE;
return kTRUE;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator=(const TLazyMatrixD &lazy_constructor)
{
if (!IsValid()) {
Error("operator=(const TLazyMatrixD&)", "matrix is not initialized");
return *this;
}
if (lazy_constructor.fRowUpb != GetRowUpb() ||
lazy_constructor.fColUpb != GetColUpb() ||
lazy_constructor.fRowLwb != GetRowLwb() ||
lazy_constructor.fColLwb != GetColLwb()) {
Error("operator=(const TLazyMatrixD&)", "matrix is incompatible with "
"the assigned Lazy matrix");
return *this;
}
lazy_constructor.FillIn(*this);
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::operator=(const TMatrixD &source)
{
if (this != &source && AreCompatible(*this, source)) {
TObject::operator=(source);
memcpy(fElements, source.fElements, fNelems*sizeof(Double_t));
}
return *this;
}
//______________________________________________________________________________
Double_t &TMatrixD::operator()(Int_t rown, Int_t coln)
{
return (Double_t&)((*(const TMatrixD *)this)(rown,coln));
}
//______________________________________________________________________________
const TMatrixDRow TMatrixD::operator[](int rown) const
{
return TMatrixDRow(*this,rown);
}
//______________________________________________________________________________
TMatrixDRow TMatrixD::operator[](int rown)
{
return TMatrixDRow(*this,rown);
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Abs()
{
// Take an absolute value of a matrix, i.e. apply Abs() to each element.
if (!IsValid()) {
Error("Abs", "matrix not initialized");
return *this;
}
Double_t *ep;
for (ep = fElements; ep < fElements+fNelems; ep++)
*ep = TMath::Abs(*ep);
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Sqr()
{
// Square each element of the matrix.
if (!IsValid()) {
Error("Sqr", "matrix not initialized");
return *this;
}
Double_t *ep;
for (ep = fElements; ep < fElements+fNelems; ep++)
*ep = (*ep) * (*ep);
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Sqrt()
{
// Take square root of all elements.
if (!IsValid()) {
Error("Sqrt", "matrix not initialized");
return *this;
}
Double_t *ep;
for (ep = fElements; ep < fElements+fNelems; ep++)
if (*ep >= 0)
*ep = TMath::Sqrt(*ep);
else
Error("Sqrt", "(%d,%d)-th element, %g, is negative, can't take the square root",
(ep-fElements) % fNrows + fRowLwb,
(ep-fElements) / fNrows + fColLwb, *ep);
return *this;
}
//______________________________________________________________________________
Bool_t operator==(const TMatrixD &im1, const TMatrixD &im2)
{
// Check to see if two matrices are identical.
if (!AreCompatible(im1, im2)) return kFALSE;
return (memcmp(im1.fElements, im2.fElements, im1.fNelems*sizeof(Double_t)) == 0);
}
//______________________________________________________________________________
TMatrixD &operator+=(TMatrixD &target, const TMatrixD &source)
{
// Add the source matrix to the target matrix.
if (!AreCompatible(target, source)) {
Error("operator+=", "matrices are not compatible");
return target;
}
Double_t *sp = source.fElements;
Double_t *tp = target.fElements;
for ( ; tp < target.fElements+target.fNelems; )
*tp++ += *sp++;
return target;
}
//______________________________________________________________________________
TMatrixD &operator-=(TMatrixD &target, const TMatrixD &source)
{
// Subtract the source matrix from the target matrix.
if (!AreCompatible(target, source)) {
Error("operator-=", "matrices are not compatible");
return target;
}
Double_t *sp = source.fElements;
Double_t *tp = target.fElements;
for ( ; tp < target.fElements+target.fNelems; )
*tp++ -= *sp++;
return target;
}
//______________________________________________________________________________
TMatrixD operator+(const TMatrixD &source1, const TMatrixD &source2)
{
TMatrixD target(source1);
target += source2;
return target;
}
//______________________________________________________________________________
TMatrixD operator-(const TMatrixD &source1, const TMatrixD &source2)
{
TMatrixD target(source1);
target -= source2;
return target;
}
//______________________________________________________________________________
TMatrixD operator*(const TMatrixD &source1, const TMatrixD &source2)
{
TMatrixD target(source1,TMatrixD::kMult,source2);
return target;
}
//______________________________________________________________________________
TMatrixD &Add(TMatrixD &target, Double_t scalar, const TMatrixD &source)
{
// Modify addition: target += scalar * source.
if (!AreCompatible(target, source)) {
Error("Add", "matrices are not compatible");
return target;
}
Double_t *sp = source.fElements;
Double_t *tp = target.fElements;
for ( ; tp < target.fElements+target.fNelems; )
*tp++ += scalar * (*sp++);
return target;
}
//______________________________________________________________________________
TMatrixD &ElementMult(TMatrixD &target, const TMatrixD &source)
{
// Multiply target by the source, element-by-element.
if (!AreCompatible(target, source)) {
Error("ElementMult", "matrices are not compatible");
return target;
}
Double_t *sp = source.fElements;
Double_t *tp = target.fElements;
for ( ; tp < target.fElements+target.fNelems; )
*tp++ *= *sp++;
return target;
}
//______________________________________________________________________________
TMatrixD &ElementDiv(TMatrixD &target, const TMatrixD &source)
{
// Divide target by the source, element-by-element.
if (!AreCompatible(target, source)) {
Error("ElementDiv", "matrices are not compatible");
return target;
}
Double_t *sp = source.fElements;
Double_t *tp = target.fElements;
for ( ; tp < target.fElements+target.fNelems; )
*tp++ /= *sp++;
return target;
}
//______________________________________________________________________________
Double_t TMatrixD::RowNorm() const
{
// Row matrix norm, MAX{ SUM{ |M(i,j)|, over j}, over i}.
// The norm is induced by the infinity vector norm.
if (!IsValid()) {
Error("RowNorm", "matrix not initialized");
return 0.0;
}
Double_t *ep = fElements;
Double_t norm = 0;
// Scan the matrix row-after-row
while (ep < fElements+fNrows) {
Int_t j;
Double_t sum = 0;
// Scan a row to compute the sum
for (j = 0; j < fNcols; j++, ep += fNrows)
sum += TMath::Abs(*ep);
ep -= fNelems - 1; // Point ep to the beginning of the next row
norm = TMath::Max(norm, sum);
}
Assert(ep == fElements + fNrows);
return norm;
}
//______________________________________________________________________________
Double_t TMatrixD::ColNorm() const
{
// Column matrix norm, MAX{ SUM{ |M(i,j)|, over i}, over j}.
// The norm is induced by the 1 vector norm.
if (!IsValid()) {
Error("ColNorm", "matrix not initialized");
return 0.0;
}
Double_t *ep = fElements;
Double_t norm = 0;
// Scan the matrix col-after-col (i.e. in the natural order of elems)
while (ep < fElements+fNelems) {
Int_t i;
Double_t sum = 0;
// Scan a col to compute the sum
for (i = 0; i < fNrows; i++)
sum += TMath::Abs(*ep++);
norm = TMath::Max(norm, sum);
}
Assert(ep == fElements + fNelems);
return norm;
}
//______________________________________________________________________________
Double_t TMatrixD::E2Norm() const
{
// Square of the Euclidian norm, SUM{ m(i,j)^2 }.
if (!IsValid()) {
Error("E2Norm", "matrix not initialized");
return 0.0;
}
Double_t *ep;
Double_t sum = 0;
for (ep = fElements; ep < fElements+fNelems; ep++)
sum += (*ep) * (*ep);
return sum;
}
//______________________________________________________________________________
Double_t E2Norm(const TMatrixD &m1, const TMatrixD &m2)
{
// Square of the Euclidian norm of the difference between two matrices.
if (!AreCompatible(m1, m2)) {
Error("E2Norm", "matrices are not compatible");
return 0.0;
}
Double_t *mp1 = m1.fElements;
Double_t *mp2 = m2.fElements;
Double_t sum = 0;
for (; mp1 < m1.fElements+m1.fNelems; mp1++, mp2++)
sum += (*mp1 - *mp2) * (*mp1 - *mp2);
return sum;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::NormByDiag(const TVectorD &v, Option_t *option)
{
// b(i,j) = a(i,j)/sqrt(abs*(v(i)*v(j)))
if (!IsValid()) {
Error("NormByDiag", "matrix not initialized");
return *this;
}
if (!v.IsValid()) {
Error("NormByDiag", "vector is not initialized");
return *this;
}
const Int_t nMax = TMath::Max(fNrows,fNcols);
if (v.fNrows < nMax) {
Error("NormByDiag", "norm vector is too short");
return *this;
}
TString opt(option);
opt.ToUpper();
const Int_t divide = (opt.Contains("D")) ? 1 : 0;
const Double_t* pV = v.fElements;
Double_t *mp = fElements;
Int_t irow;
if (divide) {
for (irow = 0; irow < fNrows; irow++) {
Int_t icol;
for (icol = 0; icol < fNcols; icol++) {
Double_t val = TMath::Sqrt(TMath::Abs(pV[irow]*pV[icol]));
Assert(val != 0.0);
mp[irow*fNcols+icol] /= val;
}
}
} else {
for (irow = 0; irow < fNrows; irow++) {
Int_t icol;
for (icol = 0; icol < fNcols; icol++) {
Double_t val = TMath::Sqrt(TMath::Abs(pV[irow]*pV[icol]));
mp[irow*fNcols+icol] *= val;
}
}
}
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::NormByColumn(const TVectorD &v, Option_t *option)
{
// Multiply/divide a matrix columns with a vector:
// matrix(i,j) *= v(i)
if (!IsValid()) {
Error("NormByColumn", "matrix not initialized");
return *this;
}
if (!v.IsValid()) {
Error("NormByColumn", "vector is not initialized");
return *this;
}
if (fNcols != v.fNrows) {
Error("NormByColumn", "matrix cannot be normed column-wise by this vector");
return *this;
}
TString opt(option);
opt.ToUpper();
const Int_t divide = (opt.Contains("D")) ? 1 : 0;
const Double_t* pv = v.fElements;
Double_t *mp = fElements;
Int_t i;
if (divide) {
for ( ; mp < fElements + fNelems; pv++)
for (i = 0; i < fNrows; i++) {
Assert(*pv != 0.0);
*mp++ /= *pv;
}
} else {
for ( ; mp < fElements + fNelems; pv++)
for (i = 0; i < fNrows; i++)
*mp++ *= *pv;
}
return *this;
}
//______________________________________________________________________________
TMatrixD &TMatrixD::NormByRow(const TVectorD &v, Option_t *option)
{
// Multiply/divide a matrix row with a vector:
// matrix(i,j) *= v(j)
if (!IsValid()) {
Error("NormByRow", "matrix not initialized");
return *this;
}
if (!v.IsValid()) {
Error("NormByRow", "vector is not initialized");
return *this;
}
if (fNcols != v.fNrows) {
Error("NormByRow", "matrix cannot be normed column-wise by this vector");
return *this;
}
TString opt(option);
opt.ToUpper();
const Int_t divide = (opt.Contains("D")) ? 1 : 0;
const Double_t* pv = v.fElements;
Double_t *mp = fElements;
Int_t i;
if (divide) {
for ( ; mp < fElements + fNelems; pv = v.fElements) {
for (i = 0; i < fNrows; i++)
{
Assert(*pv != 0.0);
*mp++ /= *pv++;
}
}
} else {
for ( ; mp < fElements + fNelems; pv = v.fElements)
for (i = 0; i < fNrows; i++)
*mp++ *= *pv++;
}
return *this;
}
//______________________________________________________________________________
void TMatrixD::Print(Option_t *) const
{
// Print the matrix as a table of elements (zeros are printed as dots).
if (!IsValid()) {
Error("Print", "matrix not initialized");
return;
}
printf("nMatrix %dx%d is as follows", fNrows, fNcols);
Int_t cols_per_sheet = 5;
Int_t sheet_counter;
for (sheet_counter = 1; sheet_counter <= fNcols; sheet_counter += cols_per_sheet) {
printf("n\n |");
Int_t i, j;
for (j = sheet_counter; j < sheet_counter+cols_per_sheet && j <= fNcols; j++)
printf(" %6d |", j+fColLwb-1);
printf("n%sn",
"------------------------------------------------------------------");
for (i = 1; i <= fNrows; i++) {
printf("%4d |", i+fRowLwb-1);
for (j = sheet_counter; j < sheet_counter+cols_per_sheet && j <= fNcols; j++)
printf("%11.4g ", (*this)(i+fRowLwb-1, j+fColLwb-1));
printf("n");
}
}
printf("n");
}
//______________________________________________________________________________
void TMatrixD::Transpose(const TMatrixD &prototype)
{
// Transpose a matrix.
if (!prototype.IsValid()) {
Error("Transpose", "prototype matrix not initialized");
return;
}
Allocate(prototype.fNcols, prototype.fNrows,
prototype.fColLwb, prototype.fRowLwb);
Double_t *rsp = prototype.fElements; // Row source pointer
Double_t *tp = fElements;
// (This: target) matrix is traversed in the natural, column-wise way,
// whilst the source (prototype) matrix is scanned row-by-row
while (tp < fElements + fNelems) {
Double_t *sp = rsp++; // sp = @ms[j,i] for i=0
// Move tp to the next elem in the col and sp to the next el in the curr row
while (sp < prototype.fElements + prototype.fNelems)
*tp++ = *sp, sp += prototype.fNrows;
}
Assert(tp == fElements + fNelems &&
rsp == prototype.fElements + prototype.fNrows);
}
//______________________________________________________________________________
TMatrixD &TMatrixD::Invert(Double_t *determ_ptr)
{
// The most general (Gauss-Jordan) matrix inverse
//
// This method works for any matrix (which of course must be square and
// non-singular). Use this method only if none of specialized algorithms
// (for symmetric, tridiagonal, etc) matrices isn't applicable/available.
// Also, the matrix to invert has to be _well_ conditioned:
// Gauss-Jordan eliminations (even with pivoting) perform poorly for
// near-singular matrices (e.g., Hilbert matrices).
//
// The method inverts matrix inplace and returns the determinant if
// determ_ptr was specified as not 0. Determinant will be exactly zero
// if the matrix turns out to be (numerically) singular. If determ_ptr is
// 0 and matrix happens to be singular, throw up.
//
// The algorithm perform inplace Gauss-Jordan eliminations with
// full pivoting. It was adapted from my Algol-68 "translation" (ca 1986)
// of the FORTRAN code described in
// Johnson, K. Jeffrey, "Numerical methods in chemistry", New York,
// N.Y.: Dekker, c1980, 503 pp, p.221
//
// Note, since it's much more efficient to perform operations on matrix
// columns rather than matrix rows (due to the layout of elements in the
// matrix), the present method implements a "transposed" algorithm.
if (!IsValid()) {
Error("Invert(Double_t*)", "matrix not initialized");
return *this;
}
if (fNrows != fNcols) {
Error("Invert(Double_t*)", "matrix to invert must be square");
return *this;
}
Double_t determinant = 1;
const Double_t singularity_tolerance = 1e-35;
if (fNrows <= 3) {
Double_t *m = fElements;
if (fNrows == 1) {
determinant = m[0];
} else if (fNrows == 2) {
determinant = m[0]*m[3]-m[1]*m[2];
} else if (fNrows == 3) {
determinant = m[0]*(m[4]*m[8]-m[7]*m[5])
-m[3]*(m[1]*m[8]-m[7]*m[2])
+m[6]*(m[1]*m[5]-m[4]*m[2]);
}
if (TMath::Abs(determinant) < singularity_tolerance) {
if (determ_ptr) {
*determ_ptr = 0;
return *this;
} else {
Error("Invert(Double_t*)", "matrix turns out to be singular: can't invert");
return *this;
}
}
if (fNrows == 1) {
m[0] = 1/m[0];
} else if (fNrows == 2) {
Double_t *o = new Double_t[fNelems];
memcpy(o,m,fNelems*sizeof(Double_t));
m[0] = o[3]/determinant;
m[1] = -o[1]/determinant;
m[2] = -o[2]/determinant;
m[3] = o[0]/determinant;
delete [] o;
} else if (fNrows == 3) {
Double_t *o = new Double_t[fNelems];
memcpy(o,m,fNelems*sizeof(Double_t));
m[0] = +(o[4]*o[8]-o[5]*o[7])/determinant;
m[1] = -(o[1]*o[8]-o[2]*o[7])/determinant;
m[2] = +(o[1]*o[5]-o[2]*o[4])/determinant;
m[3] = -(o[3]*o[8]-o[5]*o[6])/determinant;
m[4] = +(o[0]*o[8]-o[2]*o[6])/determinant;
m[5] = -(o[0]*o[5]-o[2]*o[3])/determinant;
m[6] = +(o[3]*o[7]-o[4]*o[6])/determinant;
m[7] = -(o[0]*o[7]-o[1]*o[6])/determinant;
m[8] = +(o[0]*o[4]-o[1]*o[3])/determinant;
delete [] o;
}
if (determ_ptr)
*determ_ptr = determinant;
return *this;
}
const Int_t symmetric = IsSymmetric();
// condition the matrix
TVectorD diag(fNrows);
if (symmetric) {
diag = TMatrixDDiag(*this);
for (Int_t idx = 0; idx < diag.fNrows; idx++) {
if (diag.fElements[idx] == 0.0) diag.fElements[idx] = 1.0;
}
this->NormByDiag(diag);
}
// Locations of pivots (indices start with 0)
struct Pivot_t { int row, col; } *const pivots = new Pivot_t[fNcols];
Bool_t *const was_pivoted = new Bool_t[fNrows];
memset(was_pivoted, 0, fNrows*sizeof(Bool_t));
Pivot_t *pivotp;
for (pivotp = &pivots[0]; pivotp < &pivots[fNcols]; pivotp++) {
Int_t prow = 0, pcol = 0; // Location of a pivot to be
{ // Look through all non-pivoted cols
Double_t max_value = 0; // (and rows) for a pivot (max elem)
for (Int_t j = 0; j < fNcols; j++)
if (!was_pivoted[j]) {
Double_t *cp;
Int_t k;
Double_t curr_value = 0;
for (k = 0, cp = fIndex[j]; k < fNrows; k++, cp++)
if (!was_pivoted[k] && (curr_value = TMath::Abs(*cp)) > max_value)
max_value = curr_value, prow = k, pcol = j;
}
if (max_value < singularity_tolerance) {
// free allocated heap memory before returning
delete [] pivots;
delete [] was_pivoted;
if (determ_ptr) {
*determ_ptr = 0;
return *this;
} else {
Error("Invert(Double_t*)", "matrix turns out to be singular: can't invert");
return *this;
}
}
pivotp->row = prow;
pivotp->col = pcol;
}
// Swap prow-th and pcol-th columns to bring the pivot to the diagonal
if (prow != pcol) {
Double_t *cr = fIndex[prow];
Double_t *cc = fIndex[pcol];
for (Int_t k = 0; k < fNrows; k++) {
Double_t temp = *cr; *cr++ = *cc; *cc++ = temp;
}
}
was_pivoted[prow] = kTRUE;
{ // Normalize the pivot column and
Double_t *pivot_cp = fIndex[prow];
Double_t pivot_val = pivot_cp[prow]; // pivot is at the diagonal
determinant *= pivot_val; // correct the determinant
pivot_cp[prow] = kTRUE;
for (Int_t k=0; k < fNrows; k++)
*pivot_cp++ /= pivot_val;
}
{ // Perform eliminations
Double_t *pivot_rp = fElements + prow; // pivot row
for (Int_t k = 0; k < fNrows; k++, pivot_rp += fNrows)
if (k != prow) {
Double_t temp = *pivot_rp;
*pivot_rp = 0;
Double_t *pivot_cp = fIndex[prow]; // pivot column
Double_t *elim_cp = fIndex[k]; // elimination column
for (Int_t l = 0; l < fNrows; l++)
*elim_cp++ -= temp * *pivot_cp++;
}
}
}
Int_t no_swaps = 0; // Swap exchanged *rows* back in place
for (pivotp = &pivots[fNcols-1]; pivotp >= &pivots[0]; pivotp--)
if (pivotp->row != pivotp->col) {
no_swaps++;
Double_t *rp = fElements + pivotp->row;
Double_t *cp = fElements + pivotp->col;
for (Int_t k = 0; k < fNcols; k++, rp += fNrows, cp += fNrows) {
Double_t temp = *rp; *rp = *cp; *cp = temp;
}
}
// revert our scaling
if (symmetric) {
this->NormByDiag(diag);
if (determ_ptr) {
Int_t irow;
for (irow = 0; irow < fNrows; irow++)
determinant *= TMath::Abs(diag(irow));
}
}
if (determ_ptr)
*determ_ptr = (no_swaps & 1 ? -determinant : determinant);
delete [] was_pivoted;
delete [] pivots;
return *this;
}
//______________________________________________________________________________
Bool_t TMatrixD::IsSymmetric() const
{
if (!IsValid()) {
Error("IsSymmetric", "matrix not initialized");
return 0;
}
if (fNrows != fNcols) {
Error("IsSymmetric", "matrix is not square");
return 0;
}
if (fRowLwb != fColLwb) {
Error("IsSymmetric", "row and column start at different values");
return 0;
}
Int_t irow;
for (irow = 0; irow < fNrows; irow++) {
Int_t icol;
for (icol = 0; icol < irow; icol++) {
if (fElements[irow*fNrows+icol] != fElements[icol*fNrows+irow]) {
return 0;
}
}
}
return 1;
}
//______________________________________________________________________________
void TMatrixD::Invert(const TMatrixD &m)
{
// Allocate new matrix and set it to inv(m).
if (!m.IsValid()) {
Error("Invert(const TMatrixD&)", "matrix m not initialized");
return;
}
ResizeTo(m);
*this = m; // assignment operator
Invert(0);
}
//______________________________________________________________________________
TMatrixD &TMatrixD::InvertPosDef()
{
if (!IsValid()) {
Error("InvertPosDef(Double_t*)", "matrix not initialized");
return *this;
}
if (fNrows != fNcols) {
Error("InvertPosDef(Double_t*)", "matrix to invert must be square");
return *this;
}
if (fNrows <= 3) {
const Double_t singularity_tolerance = 1e-35;
Double_t determinant = 1;
Double_t *m = fElements;
if (fNrows == 1) {
determinant = m[0];
} else if (fNrows == 2) {
determinant = m[0]*m[3]-m[1]*m[2];
} else if (fNrows == 3) {
determinant = m[0]*(m[4]*m[8]-m[7]*m[5])
-m[3]*(m[1]*m[8]-m[7]*m[2])
+m[6]*(m[1]*m[5]-m[4]*m[2]);
}
if (TMath::Abs(determinant) < singularity_tolerance) {
Error("InvertPosDef", "matrix turns out to be singular: can't invert");
return *this;
}
if (fNrows == 1) {
m[0] = 1/m[0];
} else if (fNrows == 2) {
Double_t *o = new Double_t[fNelems];
memcpy(o,m,fNelems*sizeof(Double_t));
m[0] = o[3]/determinant;
m[1] = -o[1]/determinant;
m[2] = m[1];
m[3] = o[0]/determinant;
delete [] o;
} else if (fNrows == 3) {
Double_t *o = new Double_t[fNelems];
memcpy(o,m,fNelems*sizeof(Double_t));
m[0] = +(o[4]*o[8]-o[5]*o[7])/determinant;
m[1] = -(o[3]*o[8]-o[5]*o[6])/determinant;
m[2] = +(o[3]*o[7]-o[4]*o[6])/determinant;
m[3] = m[1];
m[4] = +(o[0]*o[8]-o[2]*o[6])/determinant;
m[5] = -(o[0]*o[7]-o[1]*o[6])/determinant;
m[6] = m[2];
m[7] = m[5];
m[8] = +(o[0]*o[4]-o[1]*o[3])/determinant;
delete [] o;
}
return *this;
}
const Int_t n = fNrows;
Double_t *pa = fElements;
Double_t *pu = new Double_t[n*n];
// step 1: Cholesky decomposition
if (Pdcholesky(pa,pu,n))
{
Error("InvertPosDef","matrix not positive definite ?, Gauss-Jordan inversion applied");
delete [] pu;
Invert(0);
return *this;
}
const Int_t off_n = (n-1)*n;
Int_t i,l;
for (i = 0; i < n; i++)
{
const Int_t off_i = i*n;
// step 2: Forward substitution
for (l = i; l < n; l++)
{
if (l == i)
pa[off_n+l] = 1./pu[l*n+l];
else
{
pa[off_n+l] = 0.;
for (Int_t k = i; k <= l-1; k++)
pa[off_n+l] = pa[off_n+l]-pu[k*n+l]*pa[off_n+k];
pa[off_n+l] = pa[off_n+l]/pu[l*n+l];
}
}
// step 3: Back substitution
for (l = n-1; l >= i; l--)
{
const Int_t off_l = l*n;
if (l == n-1)
pa[off_i+l] = pa[off_n+l]/pu[off_l+l];
else
{
pa[off_i+l] = pa[off_n+l];
for (Int_t k = n-1; k >= l+1; k--)
pa[off_i+l] = pa[off_i+l]-pu[off_l+k]*pa[off_i+k];
pa[off_i+l] = pa[off_i+l]/pu[off_l+l];
}
}
}
// Fill lower triangle symmetrically
if (n > 1)
{
for (Int_t i = 0; i < n; i++)
{
for (Int_t l = 0; l <= i-1; l++)
pa[i*n+l] = pa[l*n+i];
}
}
delete [] pu;
return *this;
}
//______________________________________________________________________________
Int_t TMatrixD::Pdcholesky(const Double_t *a, Double_t *u, const Int_t n)
{
// Program Pdcholesky inverts a positiv definite (n x n) - matrix A,
// using the Cholesky decomposition
//
// Input: a - (n x n)- Matrix A
// n - dimensions n of matrices
//
// Output: u - (n x n)- Matrix U so that U^T . U = A
// return - 0 decomposition succesful
// - 1 decomposition failed
memset(u,0,n*n*sizeof(Double_t));
Int_t status = 0;
for (Int_t k = 0; k < n; k++)
{
Double_t s = 0.;
const Int_t off_k = k*n;
for (Int_t j = k; j < n; j++)
{
if (k > 0)
{
s = 0.;
for (Int_t l = 0; l <= k-1; l++)
{
const Int_t off_l = l*n;
s += u[off_l+k]*u[off_l+j];
}
}
u[off_k+j] = a[off_k+j]-s;
if (k == j)
{
if (u[off_k+j] < 0) status = 1;
u[off_k+j] = TMath::Sqrt(TMath::Abs(u[off_k+j]));
}
else
u[off_k+j] = u[off_k+j]/u[off_k+k];
}
}
return status;
}
//______________________________________________________________________________
void TMatrixD::InvertPosDef(const TMatrixD &m)
{
// Allocate new matrix and set it to inv(m).
if (!m.IsValid()) {
Error("InvertPosDef(const TMatrixD&)", "matrix m not initialized");
return;
}
ResizeTo(m);
*this = m; // assignment operator
InvertPosDef();
}
//____________________________________________________________________
const TMatrixD TMatrixD::EigenVectors(TVectorD &eigenValues) const
{
// Return a matrix containing the eigen-vectors; also fill the
// supplied vector with the eigen values.
if (IsSymmetric())
{
TMatrixD eigenVectors = *this;
eigenValues.ResizeTo(fNrows);
TVectorD offDiag(fNrows);
// Tridiagonalize matrix
MakeTridiagonal(eigenVectors,eigenValues,offDiag);
// Make eigenvectors and -values
MakeEigenVectors(eigenValues,offDiag,eigenVectors);
// Order eigenvalues and -vectors
EigenSort(eigenVectors,eigenValues);
return eigenVectors;
}
else
{
Error("EigenVectors","Not yet implemented for non-symmetric matrix");
return *this;
}
}
//____________________________________________________________________
void TMatrixD::MakeTridiagonal(TMatrixD &a, TVectorD &d, TVectorD &e)
{
// The comments in this algorithm are modified version of those in
// "Numerical ...". Please refer to that book (web-page) for more on
// the algorithm.
//
/*
PRIVATE METHOD:
The basic idea is to perform
orthogonal transformation, where
each transformation eat away the off-diagonal elements, except the
inner most.
*/
//
const Int_t n = a.fNrows;
if (!a.IsValid()) {
gROOT->Error("Maketridiagonal", "matrix not initialized");
return;
}
if (a.fNrows != a.fNcols) {
gROOT->Error("Maketridiagonal", "matrix to tridiagonalize must be square");
return;
}
if (!a.IsSymmetric()) {
gROOT->Error("MakeTridiagonal", "Can only tridiagonalise symmetric matrix");
a.Zero();
d.Zero();
e.Zero();
return;
}
Double_t *pa = a.fElements;
Double_t *pd = d.fElements;
Double_t *pe = e.fElements;
Int_t i;
for (i = n-1; i > 0; i--) {
const Int_t l = i-1;
Double_t h = 0;
Double_t scale = 0;
if (l > 0) {
for (Int_t k = 0; k <= l; k++)
scale += TMath::Abs(pa[i+k*n]);
if (scale == 0)
// Skip transformation
pe[i] = pa[i+l*n];
else {
Int_t k;
for (k = 0; k <= l; k++) {
// Use scaled elements of a for transformation
pa[i+k*n] /= scale;
// Calculate sigma in h
h += pa[i+k*n]*pa[i+k*n];
}
Double_t f = pa[i+l*n];
Double_t g = (f >= 0. ? -TMath::Sqrt(h) : TMath::Sqrt(h));
pe[i] = scale*g;
h -= f*g; // Now h is eq. (11.2.4) in "Numerical ..."
pa[i+l*n] = f-g;
f = 0;
Int_t j;
for (j = 0; j <= l; j++) {
// Store the u/H in ith column of a;
pa[j+i*n] = pa[i+j*n]/h;
// Form element A dot u in g;
g = 0;
Int_t k;
for (k = 0; k <= j; k++)
g += pa[j+k*n]*pa[i+k*n];
for (k = j+1; k <= l; k++)
g += pa[k+j*n]*pa[i+k*n];
// Form element of vector p in temporarily unused element of
// e
pe[j] = g/h;
f += pe[j]*pa[i+j*n];
}
// Form K eq (11.2.11)
const Double_t hh = f/(h+h);
// Form vector q and store in e overwriting p
for (j = 0; j <= l; j++) {
f = pa[i+j*n];
pe[j] = g = pe[j]-hh*f;
Int_t k;
for (k = 0; k <= j; k++)
// Reduce a, eq (11.2.13)
pa[j+k*n] -= (f*pe[k]+g*pa[i+k*n]);
}
}
}
else
pe[i] = pa[i+l*n];
pd[i] = h;
}
pd[0] = 0;
pe[0] = 0;
for (i = 0; i < n; i++) {
// Begin accumulation of transformation matrix
const Int_t l = i-1;
if (pd[i]) {
// This block is skipped if i = 0;
Int_t j;
for (j = 0; j <= l; j++) {
Double_t g = 0;
Int_t k;
for (k = 0; k <= l; k++)
// Use vector u/H stored in a to form P dot Q
g += pa[i+k*n]*pa[k+j*n];
for (k = 0; k <= l; k++)
pa[k+j*n] -= g*pa[k+i*n];
}
}
pd[i] = pa[i+i*n];
pa[i+i*n] = 1;
Int_t j;
for (j = 0; j <= l; j++) {
pa[j+i*n] = pa[i+j*n] = 0;
}
}
}
//____________________________________________________________________
void TMatrixD::MakeEigenVectors(TVectorD &d, TVectorD &e, TMatrixD &z)
{
//
/*
PRIVATE METHOD:
Find eigenvalues and vectors of tridiagonalised covariance matrix
according to the QL with implicit shift algorithm from
Numerical Recipes in C
section 11.3.
The basic idea is to find matrices
and
so that
, where
is orthogonal and
is lower triangular. The QL algorithm
consist of a
sequence of orthogonal transformations