VF_multiNonlinfit VD_multiNonlinfit VE_multiNonlinfit
VF_multiNonlinfitwW VD_multiNonlinfitwW VE_multiNonlinfitwW
Functionfit multiple X-Y data sets to a common model function, possibly non-linear in its parameters
Syntax C/C++#include <MFstd.h>
float VF_multiNonlinfit( fVector A, iVector AStatus, unsigned npars,
    VF_EXPERIMENT *ListOfExperiments, unsigned nexperiments,
    void (*modelfunc)(fVector YModel, fVector XModel, ui size, unsigned iexperiment),
    void (*derivatives)(fVector dYdAi,fVector X, ui size, unsigned ipar, unsigned iexperiment),
    VF_NONLINFITOPTIONS *FitOpts,
    VF_NONLINFITWORKSPACE *WorkSpace );
 
float VF_multiNonlinfitwW( fVector A, fMatrix Covar, iVector AStatus, unsigned npars,
    VF_EXPERIMENT *ListOfExperiments, unsigned nexperiments,
    void (*modelfunc)(fVector YModel, fVector X, ui size, unsigned iexperiment, fVector A),
    void (*derivatives)(fVector dYdAi, fVector X, ui size, unsigned ipar, unsigned iexperiment, fVector A, VF_NONLINFITWORKSPACE *ws),
    VF_NONLINFITOPTIONS *FitOpts,
    VF_NONLINFITWORKSPACE *WorkSpace );
Syntax C/C++ simplified#include <MFstd.h>
float VF_multiNonlinfit( fVector A, iVector AStatus, unsigned npars,
    VF_EXPERIMENT *ListOfExperiments, unsigned nexperiments,
    void (*modelfunc)(fVector YModel, fVector XModel, ui size, unsigned iexperiment, fVector A),
    void (*derivatives)(fVector dYdAi,fVector X, ui size, unsigned ipar, unsigned iexperiment, fVector A, VF_NONLINFITWORKSPACE *ws) );
 
float VF_multiNonlinfitwW( fVector A, fMatrix Covar, iVector AStatus, unsigned npars,
    VF_EXPERIMENT *ListOfExperiments, unsigned nexperiments,
    void (*modelfunc)(fVector YModel, fVector X, ui size, unsigned iexperiment, fVector A),
    void (*derivatives)(fVector dYdAi, fVector X, ui size, unsigned ipar, unsigned iexperiment, fVector A, VF_NONLINFITWORKSPACE *ws) );
Pascal/Delphiuses VFmnlfit;
function VF_multiNonlinfit( A: fVector; AStatus: iVector; nParameters: UInt;
    ListOfExperiments: PVF_EXPERIMENT; nexperiments: UInt;
    ModelFunc, Derivatives: Pointer;
    FitOpts: PVF_NONLINFITOPTIONS; WorkSpace: PMF_NONLINFITWORKSPACE ): Single;
 
function VF_multiNonlinfitwW( A: fVector; Covar: fMatrix; AStatus: iVector; nParameters: UInt;
    ListOfExperiments: PVF_EXPERIMENT; nexperiments: UInt;
    ModelFunc, Derivatives: Pointer;
    FitOpts: PVF_NONLINFITOPTIONS; WorkSpace: PMF_NONLINFITWORKSPACE ): Single;

Syntax of user-supplied ModelFunc and Derivatives:
procedure ModelFunc( YModel, X:fVector; size:UIntSize; iexperiment:UInt; A:fVector );
procedure Derivatives( dYdAi, X:fVector; size:UIntSize; ipar, iexperiment:UInt; A:fVector; ws:PVF_NONLINFITWORKSPACE );
Pascal/Delphi simplifieduses VFmnlfit;
function VF_multiNonlinfit( A: fVector; AStatus: iVector; nParameters: UInt;
    ListOfExperiments: PVF_EXPERIMENT; nexperiments: UInt;
    ModelFunc, Derivatives: Pointer ): Single;
 
function VF_multiNonlinfitwW( A: fVector; Covar: fMatrix; AStatus: iVector; nParameters: UInt;
    ListOfExperiments: PVF_EXPERIMENT; nexperiments: UInt;
    ModelFunc, Derivatives: Pointer ): Single;
DescriptionThe input data, contained in ListOfExperiments, are used to evaluate the parameter array A with npars elements ai of an arbitrary model function y = f(x).

Arguments:
Avector of size npars; returns the coefficients
Covarmatrix of dimensions [npars, npars]; returns the covariances of the coefficients
AStatusvector of size npars; decides which parameters are treated as free or as fixed
nparstotal number of parameters
ListOfExperimentsinput data, see chap. 13.4
nexperimentsnumber of data sets in ListOfExperiments
modelfuncuser-defined model function
derivativesuser-defined function, calculating the partial derivatives with respect to all parameters (may be NULL / nil; in this case, the partial derivatives are calculated numerically)
FitOptspointer to a structure containing options, see chap. 13.3
WorkSpacepointer to a structure holding internal variables, see chap. 13.3
Return Value"goodness-of-fit" value. Depending on the function variant and the chosen figure-of-merit, this is:
VF_multiNonlinfitwW with FitOptions.FigureOfMerit=0 (least-square fitting): c2 (chi-square) = S) (Ymod[i] - Y[i] )2 * InvVar[i] );
VF_multiNonlinfitwW with FitOptions.FigureOfMerit=1 (robust fitting): |c| (chi-abs) = S( abs(Ymod[i] - Y[i] ) * InvVar[i] );
VF_multiNonlinfit with FitOptions.FigureOfMerit=0 (least-square fitting, all std errors assumed=1.0): c2 = S( (Ymod[i] - Y[i] )2 ];
VF_multiNonlinfit with FitOptions.FigureOfMerit=1 (robust fitting): |c| (chi-abs) = S( abs(Ymod[i] - Y[i] ) );
where Ymod means the theoretical Y values of all experiments as calculated from the model function with the best parameter set found. For robust fitting, InvVar actually does not have the meaning of inverse variances; one could choose weights as inverse absolute uncertainties instead.
 
In order to use default options and built-in workspace, FitOpts and WorkSpace may be set to NULL / nil. In the simplified syntax variant, both of them are missing.
The model function (and, consequently, the vector A as well) may actually contain more parameters than you wish to treat as adjustable. This is why you have to provide an additional vector, AStatus, which contains the necessary information about which parameters are to be held fixed at their input values (AStatus[i] = 0) and which are free (AStatus[i] = 1). All parameters must be initialized in A prior to calling VF_multiNonlinfit. The better your initial guess of the parameters, the faster VF_multiNonlinfit shall converge. The argument npars denotes the total number of parameters in A (not only the free parameters!).

The input data must be combined into sets of the type VF_EXPERIMENT. Assuming you have two sets of X-Y data, contained in the vectors X1, Y1 (and, for VF_multiLinfitwW, InvVar1) of size1 elements, and X2, Y2 (and InvVar2) of size2 elements, you have to construct a list of experiments as in the following example:

Constructing list of experiments in C/C++ VF_EXPERIMENT ExpList[2];
ExpList[0].X = X1;  ExpList[0].Y = Y1;  ExpList[0].size = size1;
ExpList[1].X = X2;  ExpList[1].Y = Y2;  ExpList[1].size = size2;
/* for the weighted variant, set additionally: */
ExpList[0].InvVar = InvVar1;  ExpList[0].WeightOfExperiment = wt1;
ExpList[1].InvVar = InvVar2;  ExpList[1].WeightOfExperiment = wt2;
 
Constructing list of experiments in Pascal/Delphi var ExpList: array[0..1] of VF_EXPERIMENT;
begin
  ...
  ExpList[0].X := X1;  ExpList[0].Y := Y1;  
  ExpList[0].size := size1;
  ExpList[1].X := X2;  ExpList[1].Y := Y2;  
  ExpList[1].size := size2;
    /* for the weighted variant, set additionally: */
  ExpList[0].InvVar := InvVar1;  
  ExpList[0].WeightOfExperiment := wt1;
  ExpList[1].InvVar := InvVar2;  
  ExpList[1].WeightOfExperiment := wt2;
  ...
end;

 
Both C/C++ and Pascal/Delphi You must provide a model function "modelfunc" which, for a given vector of x-values, must calculate the corresponding "theoretical" y-values. In C/C++, it has to be defined as
 
Model function for C/C++ void _cdecl MyFunc( fVector Y, fVector X, ui size, unsigned iexperiment, fVector A )
{
  for (ui i=0; i<size; i++ )
    Y[i] = f( X[i] );
}
f( X[i] )
is any arbitrary function, which may be as complicated as you like and your application needs. The only condition is that it have no singularities, at least within the parameter space specified by upper and lower boundaries (see NONLINFITOPTIONS).

The argument iexperiment with which MyFunc shall be called by VF_multiNonlinfit allows to distinguish between parameters that are common to all experiments and others that belong to individual experiments. For example, each experiment's Y values might be scaled by an individual constant C. In this case, A has to contain as many scales C as there are experiments. In MyFunc, you would have to code this as something like:
  if( iexperiment == 0 ) Y[i] *= A[5];  else Y[i] *= A[6];

In addition to the model function, VF_multiNonlinfit needs the partial derivatives of Y with respect to all parameters A[ipar], according to your model. If you know them analytically, you should write a function MyDerivs. If you happen to know only some, but not all of the partial derivatives, you may rely on VF_multiNonlinfit_autoDeriv to calculate the unknown derivatives numerically.

Partial derivatives coded for C/C++
 
void _cdecl MyDerivs( fVector dYdAi, fVector X, ui size, unsigned ipar, unsigned iexperiment, fVector A, VF_NONLINFITWORKSPACE *ws )
{
  ui i;
  switch( ipar )
  {
    case 0: for(i=0; i<size; i++ )
              dYdAi[i] = part_derv_of_Y_w_resp_to_A0( X[i] );
    break;
    case 1: for(i=0; i<size; i++ )
              dYdAi[i] = part_derv_of_Y_w_resp_to_A1( X[i] );
    break;
    default: /* for all derivatives we don't know: */
            VF_multiNonlinfit_autoDeriv(
               dYdAi, X, size:UIntSize; ipar, iexperiment, A, ws );
  }
}

Again, the argument iexperiment allows you to treat "private" parameters of the individual experiments differently from the shared parameters.
A call to VF_multiNonlinfit will look like:
VF_multiNonlinfit( A, AStatus, npars, ExpList, 2, MyFunc, MyDerivs );
or
VF_multiNonlinfit( A, AStatus, npars, ExpList, 2, MyFunc, MyDerivs, &FitOpts, &WorkSpace );
In case you do not know any of the partial derivatives, do not define MyDerivs, but call VF_multiNonlinfit with derivatives = NULL:
VF_multiNonlinfit( A, AStatus, npars, ExpList, 2, MyFunc, NULL );
 
Model function for Pascal/Delphi In Pascal/Delphi, the model function has to be defined as
procedure MyFunc( Y, X:fVector; size:UIntSize; iexperiment:UInt; A:fVector );
var i:UIntSize;
begin
  for i:=0 to size-1 do
    VF_Pelement( Y, i )^ := f( VF_element( X, i ) );
end;
f( X[i] )
is any arbitrary function, which may be as complicated as you like and your application needs. The only condition is that it have no singularities, at least within the parameter space specified by upper and lower boundaries (see chapter 13.3).

The argument iexperiment with which MyFunc shall be called by VF_multiNonlinfit allows to distinguish between parameters that are common to all experiments and others that belong to individual experiments. For example, each experiment's Y values might be scaled by an individual constant C. In this case, A has to contain as many scales C as there are experiments. In MyFunc, you would have to code this as something like:
  if iexperiment = 0 then
         VF_Pelement(Y,i)^ := VF_element(Y,i) * VF_element(A,5)
  else VF_Pelement(Y,i)^ := VF_element(Y,i) * VF_element(A,6);

In addition to the model function, VF_multiNonlinfit needs the partial derivatives of Y with respect to all parameters A[ipar], according to your model. If you know them analytically, you should write a function MyDerivs. If you happen to know only some, but not all of the partial derivatives, you may rely on VF_multiNonlinfit_autoDeriv to calculate the unknown derivatives numerically.

Partial derivatives coded for Pascal/Delphi
 
procedure MyDerivs( dYdAi, X:fVector; size:UIntSize; ipar, iexperiment:UInt; A:fVector; ws:PVF_NONLINFITWORKSPACE );
var i:UIntSize;
begin
  case ipar of
    0: begin
      for i:=0 to size-1 do
        VF_Pelement( dYdAi, i )^ :=
        part_derv_of_Y_w_resp_to_A0(VF_element( X, i )); end;
    1: begin
      for i:=0 to size-1 do
        VF_Pelement( dYdAi, i )^ :=
        part_derv_of_Y_w_resp_to_A0(VF_element( X, i )); end;
  else (* for all derivatives we don't know: *)
    VF_multiNonlinfit_autoDeriv(
       dYdAi, X, size:UIntSize; ipar, iexperiment, A, ws );
  end;
end;

Again, the argument iexperiment allows you to treat "private" parameters of the individual experiments differently from the shared parameters.
A call to VF_multiNonlinfit will look like:
VF_multiNonlinfit( A, AStatus, npars, @ExpList, 2, @MyFunc, @MyDerivs, @FitOpts, @WorkSpace );
or with simplified syntax:
VF_multiNonlinfit( A, AStatus, npars, @ExpList, 2, @MyFunc, @MyDerivs );
Note the address-of operator in front of "ExpList", "MyFunc.", and "MyDerivs". In case you do not know any of the partial derivatives, do not define MyDerivs, but call VF_multiNonlinfit with derivatives = nil:
VF_multiNonlinfit( A, AStatus, npars, @ExpList, 2, @MyFunc, nil );

In the weighted variant, VF_multiNonlinfitwW, the vector ExpList[i].InvVar of each experiment has to contain the inverse of the variances of the individual X-Y data points, and the matrix MCovar will be filled with the covariances of the parameters ai on output: MCovari,j = covariance( ai, aj ).
 
Both C/C++ and Pascal/Delphi: For the many different options controlling nonlinear data-fitting functions, please consult chapter 13.3. Helper functions for breaking off excessively long fitting runs and for the monitoring of these often very time-consuming procedures are summarized in chapter 13.5 and, in the special case of VF_multiNonlinfit, described here.
Multi-threading restrictionsThe multi-threading restrictions, present in OptiVec versions up to and including 6.1, have been lifted with v6.2.

These functions should not be called while the FPU is set to reduced accuracy, or else they might hang in an infinite loop. See V_setFPAccuracy.

Error handlingIf the number of free parameters (i.e., those with AStatus[i] = 1) exceeds the total number of data points, an error message "Invalid parameter(s)" is generated and the program aborted.
Return valuein case of success: goodness-of-fit parameter c2 (chi-square);
in case of failure: -1
See alsoVF_setNonlinfitOptions,   MF_multiNonlinfit,   VF_linfit,   chapter 13,  FITDEMO*.*

MatrixLib Table of Contents  OptiVec home