This macro shows several ways to perform a linear least-squares analysis .
To keep things simple we fit a straight line to 4 data points The first 4 methods use the linear algebra package to find x such that min \( (A x - b)^T (A x - b) \) where A and b are calculated with the data points and the functional expression :
- Normal equations: Expanding the expression \( (A x - b)^T (A x - b) \) and taking the derivative wrt x leads to the "Normal Equations": \( A^T A x = A^T b \) where \( A^T A \) is a positive definite matrix. Therefore, a Cholesky decomposition scheme can be used to calculate its inverse . This leads to the solution \( x = (A^T A)^-1 A^T b \) . All this is done in routine NormalEqn . We made it a bit more complicated by giving the data weights . Numerically this is not the best way to proceed because effectively the condition number of \( A^T A \) is twice as large as that of A, making inversion more difficult
- SVD One can show that solving \( A x = b \) for x with A of size \( (m x n) \) and \( m > n \) through a Singular Value Decomposition is equivalent to minimizing \( (A x - b)^T (A x - b) \) Numerically , this is the most stable method of all 5
- Pseudo Inverse Here we calculate the generalized matrix inverse ("pseudo inverse") by solving \( A X = Unit \) for matrix \( X \) through an SVD . The formal expression for is \( X = (A^T A)^-1 A^T \) . Then we multiply it by \( b \) . Numerically, not as good as 2 and not as fast . In general it is not a good idea to solve a set of linear equations with a matrix inversion .
- Pseudo Inverse , brute force The pseudo inverse is calculated brute force through a series of matrix manipulations . It shows nicely some operations in the matrix package, but is otherwise a big "no no" .
- Least-squares analysis with Minuit An objective function L is minimized by Minuit, where \( L = sum_i { (y - c_0 -c_1 * x / e)^2 } \) Minuit will calculate numerically the derivative of L wrt c_0 and c_1 . It has not been told that these derivatives are linear in the parameters c_0 and c_1 . For ill-conditioned linear problems it is better to use the fact it is a linear fit as in 2 .
Another interesting thing is the way we assign data to the vectors and matrices through adoption . This allows data assignment without physically moving bytes around .
USAGE
This macro can be executed via CLING or via ACLIC
- via the interpreter, do
- via ACLIC
root > .x solveLinear.C+
R__EXTERN TSystem * gSystem
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
Perform the fit y = c0 + c1 * x in four different ways
- 1. solve through Normal Equations
- 2. solve through SVD
- 3. solve with pseudo inverse
- 4. solve with pseudo inverse, calculated brute force
- 5. Minuit through TGraph
All solutions are the same within tolerance of 1e-12
{
cout << "Perform the fit y = c0 + c1 * x in four different ways" << endl;
cout << " - 1. solve through Normal Equations" << endl;
cout << " - 2. solve through SVD" << endl;
for (
Int_t irow = 0; irow < A.GetNrows(); irow++) {
}
const TVectorD c_svd = svd.Solve(yw,ok);
cout << " - 3. solve with pseudo inverse" << endl;
c_pseudo1 *= pseudo1;
cout << " - 4. solve with pseudo inverse, calculated brute force" << endl;
c_pseudo2 *= pseudo2;
cout << " - 5. Minuit through TGraph" << endl;
if (same)
cout << " All solutions are the same within tolerance of " << eps << endl;
else
cout << " Some solutions differ more than the allowed tolerance of " << eps << endl;
}
TVectorD NormalEqn(const TMatrixD &A, const TVectorD &b)
Solve min {(A .
TMatrixTRow< Double_t > TMatrixDRow
TMatrixTColumn< Double_t > TMatrixDColumn
Single Value Decomposition class.
virtual Double_t GetParameter(Int_t ipar) const
A TGraphErrors is a TGraph with error bars.
virtual TFitResultPtr Fit(const char *formula, Option_t *option="", Option_t *goption="", Axis_t xmin=0, Axis_t xmax=0)
Fit this graph with function with name fname.
TMatrixT< Element > & T()
TMatrixT< Element > & Invert(Double_t *det=nullptr)
Invert the matrix and calculate its determinant.
TVectorT< Element > & Use(Int_t lwb, Int_t upb, Element *data)
Use the array data to fill the vector lwb..upb].
Bool_t VerifyVectorIdentity(const TVectorT< Element > &m1, const TVectorT< Element > &m2, Int_t verbose, Element maxDevAllow)
Verify that elements of the two vectors are equal within maxDevAllow .
- Author
- Eddy Offermann
Definition in file solveLinear.C.