LSQBase.h

Classes

LSQBase -- Basic class for the least squares fitting (full description)

class LSQBase

Types

enum normType

REAL = 0
Real solution (default)
COMPLEX = 1
Normal complex variables and condition equations
SEPARABLE = 3
Separable complex. Solve for n complex variables, but have condition equations with 2n terms. Alternating for the real and imaginary part of the unknowns
ASREAL = 7
Separable as if complex are just two reals. Will give the same result as having 2n real variables to solve for, with independent condition equations for the two sets of n unknowns.
CONJUGATE = 11
Separable with consecutive unknowns are each other's conjugate. Solve for n complex unknowns, with 2n condition equation arguments, alternatively valid for x0, conj(x0), x1, conj(x1)...

enum ErrorField

NC
Number of condition equations
SUMWEIGHT
Sum weights of condition equations
SUMLL
Sum known terms squared
CHI2
Calculated chi^2
N_ErrorField
Number of error fields

enum StateBit

INVERTED = 1
Inverted matrix present
TRIANGLE = 2*INVERTED,
Triangularised
NONLIN = 2*TRIANGLE,
Non-linear solution Non-linear solution
N_StateBit
Filler for cxx2html Filler for cxx2html

Interface

Public Members
LSQBase(uInt nUnknowns, uInt nKnowns=1, uInt nConstraints=0)
LSQBase(uInt nUnknowns, LSQBase::normType type, uInt nKnowns=1, uInt nConstraints=0)
LSQBase()
LSQBase(const LSQBase &other)
LSQBase &operator=(const LSQBase &other)
~LSQBase()
Bool invert(uInt &nRank, Bool doSVD=False)
void solve(Double sol[], Double sd[], Double mu[])
void solve(Float sol[], Double sd[], Double mu[])
Bool solveLoop(Double &fit, uInt &nRank, Double sol[], Double sd[], Double mu[], Bool doSVD=False)
Bool solveLoop(Double &fit, uInt &nRank, Float sol[], Double sd[], Double mu[], Bool doSVD=False)
void doDiagonal()
void makeNorm(const Double cEq[], Double weight, const Double obs[], Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Double cEq[], Double weight, Double obs, Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Float cEq[], Float weight, const Float obs[], Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Float cEq[], Float weight, Float obs, Bool doNorm=True, Bool doKnown=True)
void getConstraint(uInt &nMissing, Double *cEq) const
void makeConstraint(const Double cEq[])
void makeConstraint(const Float cEq[])
void reset()
void set(LSQBase::normType type)
void set(uInt nUnknowns, uInt nKnowns=1, uInt nConstraints=0)
void set(Int nUnknowns, Int nKnowns=1, Int nConstraints=0)
void set(Double factor=1e-8, Double LMFactor=1e-3)
Bool getCovariance(Double *covar)
Bool getCovariance(Float *covar)
Bool getErrors(Double *errors)
Bool getErrors(Float *errors)
Double getChi(uInt m=0) const
void debugIt(Double *&nEq, Double *&er, Double *&known, uInt *&piv, uInt &rank) const
Protected Members
Double *rowne(uInt i) const
Double *rowrt(uInt i) const
Double *rowru(uInt i) const
uInt lentri() const
static void swap(Double &x, Double &y)
static void swap(uInt &x, uInt &y)
void init()
void clear()
void deinit()
void solveMR(uInt nin)
Bool invertRect()
void save(Bool all=True)
void restore(Bool all=True)
void copy(const LSQBase &other, Bool all=True)
void mulDiagonal(Double fac)
void getWorkCEQ()
void getWorkSOL()
void getWorkCOV()

Description

Review Status

Reviewed By:
Neil Killeen
Date Reviewed:
2000/06/01
Programs:
Demos:
Tests:

Prerequisite

Etymology

From Least SQuares and Base

Synopsis

The LSQBase class contains the basic functions to do all the fitting described in the Note about fitting. It handles real, and complex equations; linear and non-linear solutions; regular (with optional constraints) or Singular Value Decomposition (SVD).
In essence they are a set of routines to generate normal equations (makeNorm()) in triangular form from a set of condition equations; to do a Cholesky-type decomposition of the normal equations (either regular or SVD) and test its rank (invert()); do a quasi inversion of the decomposed equations (solve()) to obtain the solution and/or the errors. All calculations are done in place. Methods to obtain additional information about the fitting process are available.

This class can be used as a stand-alone class outside of the aips++ environment. In that case the aips.h include file can be replaced if necessary by appropriate typedefs for Double, Float and uInt.
The LSQ class is derived from this class, and contains the Complex routines (and can be used outside the aips++ environment). As soon as aips++ understands the standard complex classes, the LSQBase and LSQ classes will be combined into the one LSQ class. Hence the examples given below are including LSQ.h, and creating LSQ objects.
The FitLSQ class is derived from LSQ, and provides an interface using the (aips++) Array classes.
All three types of objects are identical (LSQBase, LSQ, FitLSQ). They only differ in the allowable interface to generate the normal equations, and the interface to obtain information (like solution) from them.

The normal operation of the class consists of the following steps:

A class can be re-used by issuing the reset() command, or set() of new values. If an unknown has not been used in the condition equations at all, the doDiagonal() will make sure a proper solution is obtained, with missing unknowns zeroed.

Most of the calculations are done in place; however, the copy constructor and assignment copy all information, and make it possible to continue with the same (partial) normal equations after e.g. an interim solution.

A debugIt() method will provide access to all internal information.

Example

See the tLSQ.cc program for extensive examples. The following example will first create 2 condition equations for 3 unknowns (the third is degenerate). It will first create normal equations for a 2 unknown solution and solve; then it will create normal equations for a 3 unknown solution, and solve (note that the degenerate will be set to 0. The last one will use SVD and one condition equation.r
      #include <aips/aips.h>
      #include <aips/Fitting/LSQ.h>
    #include <iostream>
      
      int main() {
        // Condition equations for x+y=2; x-y=4;
        Double ce[2][3] = {{1, 1, 0}, {1, -1, 0}};
        Double m[2] = {2, 4};
        // Solution and error area
        Double sol[3];
        Double sd, mu;
        uInt rank;
        Bool ok;
      
        // LSQ area
        LSQ fit(2);
      
        // Make normal equation
        for (uInt i=0; i<2; i++) fit.makeNorm(ce[i], 1.0, m[i]);
        // Invert(decompose) and show
        ok = fit.invert(rank);
        cout << "ok? " << ok << "; rank: " << rank << endl;
        // Solve and show
        if (ok) {
          fit.solve(sol, &sd, &mu);
          for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl;
          cout << "sd: "<< sd << "; mu: " << mu << endl;
        };
        cout << "----------" << endl; 
      
        // Retry with 3 unknowns: note auto fill of unmentioned one
        fit.set(uInt(3));
        for (uInt i=0; i<2; i++) fit.makeNorm(ce[i], 1.0, m[i]);
        ok = fit.invert(rank);
        cout << "ok? " << ok << "; rank: " << rank << endl;
        if (ok) {
          fit.solve(sol, &sd, &mu);
          for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl;
          cout << "sd: "<< sd << "; mu: " << mu << endl; 
        };
        cout << "----------" << endl; 
      
        // Retry with 3 unknowns; but 1 condition equation and use SVD
        fit.reset();
        for (uInt i=0; i<1; i++) fit.makeNorm(ce[i], 1.0, m[i]);
        ok = fit.invert(rank, True);
        cout << "ok? " << ok << "; rank: " << rank << endl;
        if (ok) {
          fit.solve(sol, &sd, &mu);
          for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl;
          cout << "sd: "<< sd << "; mu: " << mu << endl; 
        };
        cout << "----------" << endl; 
      
        // Without SVD it would be:
        fit.reset();
        for (uInt i=0; i<1; i++) fit.makeNorm(ce[i], 1.0, m[i]);
        ok = fit.invert(rank);
        cout << "ok? " << ok << "; rank: " << rank << endl;
        if (ok) {
          fit.solve(sol, &sd, &mu);
          for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl;
          cout << "sd: "<< sd << "; mu: " << mu << endl; 
        };
        cout << "----------" << endl; 
      
        exit(0);
      }
    
Which will produce the output:
   ok? 1; rank: 2
   Sol0: 3
   Sol1: -1
   sd: 0; mu: 0
   ----------
   ok? 1; rank: 3
   Sol0: 3
   Sol1: -1
   Sol2: 0
   sd: 0; mu: 0
   ----------
   ok? 1; rank: 2
   Sol0: 1
   Sol1: 1
   Sol2: 0
   sd: 0; mu: 0
   ----------
   ok? 0; rank: 2
   ----------

Motivation

The class was written to be able to do complex, real standard and SVD solutions in a simple and fast way.

To Do

Member Description

enum normType

Types of variables to be solved for

LSQBase(uInt nUnknowns, uInt nKnowns=1, uInt nConstraints=0)

Construct an object with the number of unknowns, knowns and constraints, and type, using the default collinearity factor and the default Levenberg-Marquardt adjustment factor.

Assume real

LSQBase(uInt nUnknowns, LSQBase::normType type, uInt nKnowns=1, uInt nConstraints=0)

Construct an object with the number of unknowns, knowns and constraints, and type, using the default collinearity factor and the default Levenberg-Marquardt adjustment factor.

Allow complex/real specification

LSQBase()

Default constructor (empty, only usable after a set(nUnknowns))

LSQBase(const LSQBase &other)

Copy constructor (deep copy)

LSQBase &operator=(const LSQBase &other)

Assignment (deep copy)

~LSQBase()

Bool invert(uInt &nRank, Bool doSVD=False)

Triangularize the normal equations and determine the rank nRank of the normal equations and, in the case of an SVD solution, the constraint equations. The collinearity factor is used to determine if the system can be solved (in essence it is the square of the sine of the angle between a column in the normal equations and the plane suspended by the other columns: if too parallel, the equations are degenerate). If doSVD is given as False, False is returned if rank not maximal, else an SVD solution is done.

void solve(Double sol[], Double sd[], Double mu[])
void solve(Float sol[], Double sd[], Double mu[])

Solve normal equations. The solution will be given in sol, with the adjustment error mu, and the standard deviation sd. I.e. mu is per unit weight, sd per observation.

Bool solveLoop(Double &fit, uInt &nRank, Double sol[], Double sd[], Double mu[], Bool doSVD=False)
Bool solveLoop(Double &fit, uInt &nRank, Float sol[], Double sd[], Double mu[], Bool doSVD=False)

Solve a loop in a non-linear set. The fit argument returns for each loop a goodness of fit indicator. If it is >0; more loops are necessary. If it is negative, and has an absolute value of say less than .001, it is probably ok, and the iterations can be stopped. Other arguments are as for solve() and invert(). The sol is used for both input (parameter guess) and output. This non-linear solution will only work for 1 known.

void doDiagonal()

Make diagonal element 1 if zero (Note that this is always called when invert() is called).

void makeNorm(const Double cEq[], Double weight, const Double obs[], Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Double cEq[], Double weight, Double obs, Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Float cEq[], Float weight, const Float obs[], Bool doNorm=True, Bool doKnown=True)
void makeNorm(const Float cEq[], Float weight, Float obs, Bool doNorm=True, Bool doKnown=True)

Make normal equations using the cEq condition equations (with nUnknowns elements) and a weight weight, given the known observed values obs (with nKnowns elements). doNorm and doKnown can be used to e.g. re-use existing normal equations, i.e. the condition equations, but make a new known side (i.e. new observations).

void getConstraint(uInt &nMissing, Double *cEq) const

Get the nMissing (the rank deficiency, or missing rank) constraint equations as cEq[nUnknowns][nMissing]. Note that nMissing will be equal to the number of unknowns (nUnknowns, or double that for the complex case) minus the rank as returned from the invert() method.

void makeConstraint(const Double cEq[])
void makeConstraint(const Float cEq[])

Set constraint equations cEq (with [nUnknowns][nConstraints] elements).

void reset()

Reset status to empty

void set(LSQBase::normType type)

Set new type

void set(uInt nUnknowns, uInt nKnowns=1, uInt nConstraints=0)
void set(Int nUnknowns, Int nKnowns=1, Int nConstraints=0)

Set new sizes

void set(Double factor=1e-8, Double LMFactor=1e-3)

Set new factors (collinearity factor, and Levenberg-Marquardt LMFactor)

Bool getCovariance(Double *covar)
Bool getCovariance(Float *covar)

Get the covariance matrix (of size nUnknowns * nUnknowns)

Bool getErrors(Double *errors)
Bool getErrors(Float *errors)

Get main diagonal of covariance function (of size nUnknowns)

Double getChi(uInt m=0) const

Get chi^2 for mth known (m = 0..nKnowns)

void debugIt(Double *&nEq, Double *&er, Double *&known, uInt *&piv, uInt &rank) const

Debug (nEq = normal equation; er = error info vector; known = known vector; piv = pivot vector; rank = rank). Only useful if you know about the internals.

enum ErrorField

Offset of fields in error_p data area.

enum StateBit

Bits that can be set/referenced

Double *rowne(uInt i) const

Get row pointer in normal equation

Double *rowrt(uInt i) const
Double *rowru(uInt i) const

Get pointer in rectangular array

uInt lentri() const

Get length of triangular array

static void swap(Double &x, Double &y)
static void swap(uInt &x, uInt &y)

Swap data

void init()

Initialise areas

void clear()

Clear areas

void deinit()

De-initialise area

void solveMR(uInt nin)

Solve missing rank part

Bool invertRect()

Invert rectangular matrix (i.e. when constraints present)

void save(Bool all=True)

Save current status (or part)

void restore(Bool all=True)

Restore current status

void copy(const LSQBase &other, Bool all=True)

Copy data. If all False, only the relevant data for non-linear solution are copied (normal equations, knows and errors).

void mulDiagonal(Double fac)

Multiply diagonal with 1+fac

void getWorkCEQ()
void getWorkSOL()
void getWorkCOV()

Get work areas for condition equations, solutions, covariance