unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DataFitXD2_TLB;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  // ActiveX Unit is required if using PSafeArray
  // COM functions and data structures
  ActiveX;

const

  DECIMAL_SEPARATOR: String = '/';
  DATE_SEPARATOR: String = '.';
  MISSING_QUALIFIER: String = ' ';

procedure TForm1.Button1Click(Sender: TObject);

var

  dfxData: DataCollection;
  dfxModel: NonlinearRegression;
  dfxDefinition: ModelDefinition;
  F: TextFile;
  index: integer;
  count: integer;
  roots: PSafeArray;
  lBound, uBound: integer;
  result: double;

begin


  // Create the DataCollection object and import the tabbed delimited data
  // file thurber.dat. The file contains 2 columns of named data labeled
  // Density and Electron Mobility (the first non-blank line in the file
  // contains the series names). The first column is the independent
  // variable X (Density), the second column is the dependent
  // variable Y (Electron Mobility). This will add 2 DataSeries objects to
  // the DataCollection, one named "Density" the other named "Electron Mobility".

  dfxData := CoDataCollection.Create;
  dfxData.ImportNamedSeriesFromFile('thurber.dat', dfxDelimiterTab,
                                    DECIMAL_SEPARATOR, DATE_SEPARATOR,
                                    MISSING_QUALIFIER);

  // Set the RegressionKey properties for the DataCollection according to the
  // above description.
  dfxData.Item['Density'].RegressionKey := 'X';
  dfxData.Item['Electron Mobility'].RegressionKey := 'Y';

  // The model being solved is:
  // Y = (B1+B2*x+B3*x^2+B4*x^3) / (1+B5*x+B6*x^2+B7*x^3)
  // where B1 through B7 are the fitting parameters, and X
  // is the dependent variable "Density".
  // Create the model definition object so that we may define
  // a multi-line user-defined model.

  dfxDefinition := CoModelDefinition.Create;
  dfxDefinition.Description := 'Semiconductor electron mobility modeling';
  dfxDefinition.F1 := '(B1 + B2 * x + B3 * x ^ 2 + B4 * x ^ 3)';
  dfxDefinition.F2 := '(1 + B5 * x + B6 * x ^ 2 + B7 * x ^ 3)';
  dfxDefinition.Y := 'F1/F2';

  // Create an instance of the NonlinearRegression object
  dfxModel := CoNonlinearRegression.Create;

  // Define the multi-line user-defined model
  // according to the above definition in dfxDefinition.
  // Since we are using a ModelDefinition object to define
  // the model, use the DefineUserModelSub method.
  dfxModel.DefineUserModelSub(dfxDefinition, dfxLocaleEnglish);

  // Define the intial estimates for the model since this is a
  // user-defined model. There are seven parameters, B1 through B7.
  dfxModel.RegressionParameters.Item['B1'].InitialEstimate := 1000.0;
  dfxModel.RegressionParameters.Item['B2'].InitialEstimate := 1000.0;
  dfxModel.RegressionParameters.Item['B3'].InitialEstimate := 400;
  dfxModel.RegressionParameters.Item['B4'].InitialEstimate := 40;
  dfxModel.RegressionParameters.Item['B5'].InitialEstimate := 0.7;
  dfxModel.RegressionParameters.Item['B6'].InitialEstimate := 0.3;
  dfxModel.RegressionParameters.Item['B7'].InitialEstimate := 0.03;

  // Set the regression solution options
  dfxModel.MaxIterations := 100;
  dfxModel.MaxUnchangedIterations := 10;
  dfxModel.RegressionTolerance := 0.0000000001;

  // Fit the model to the DataCollection dfxData
  dfxModel.FitCollection(dfxData);

  // If the model solved successfully, display some sample results. Otherwise,
  // display the solution log information to diagnose why it may have failed.
  if (dfxModel.IsSolved) then
    begin
      try
        AssignFile(F, 'output.dat');
        Rewrite(F);
        Writeln(F, 'Results for model: ');
        Writeln(F, Format('F1 = %s', [dfxModel.ModelDefinition.F1]));
        Writeln(F, Format('F2 = %s', [dfxModel.ModelDefinition.F2]));
        Writeln(F, Format('F2 = %s', [dfxModel.ModelDefinition.Y]));
        Writeln(F, Format('Description = %s', [dfxModel.ModelDefinition.Y]));
        Writeln(F, 'Data set: Thurber.dat');
        Writeln(F, Format('Independent Variable (X): %s', [dfxModel.ResultTables.Item[1].Name]));
        Writeln(F, Format('Dependent Variable (Y): %s', [dfxModel.ResultTables.Item[2].Name]));
        Writeln(F, Format('Number of Observations = %d', [dfxModel.NumObservations]));
        Writeln(F, Format('XMin = %10.10f', [dfxModel.ResultTables.Item[1].Minimum]));
        Writeln(F, Format('XMax = %10.10f', [dfxModel.ResultTables.Item[1].Maximum]));
        Writeln(F, Format('YMin = %10.10f', [dfxModel.ResultTables.Item[2].Minimum]));
        Writeln(F, Format('YMax = %10.10f', [dfxModel.ResultTables.Item[2].Maximum]));
        Writeln(F, '');

        // Print the parameter names, values and standard errors
        Writeln(F, 'Parameter Results:');
        count := dfxModel.RegressionParameters.count;
        for index := 1 to count do
          begin
            Writeln(F, Format('%s = %10.10f, Standard Error = %10.10f',
                              [dfxModel.RegressionParameters.Item[index].Name,
                               dfxModel.RegressionParameters.Item[index].Value,
                               dfxModel.RegressionParameters.Item[index].StandardError]));
          end;
        Writeln(F, '');

        // Print goodness of fit information
        Writeln(F, Format('Number of Nonlinear Iterations = %d', [dfxModel.NumIterations]));
        Writeln(F, Format('Residual Standard Error = %10.10f', [dfxModel.StandardError]));
        Writeln(F, Format('R2 Value = %10.10f', [dfxModel.R2]));
        Writeln(F, Format('Regression Sum of Squares = %10.10f', [dfxModel.SSR]));
        Writeln(F, Format('Residual Sum of Squares = %10.10f', [dfxModel.SSE]));
        Writeln(F, '');

        // Other sample output
            
        // Single Predicted value
        Writeln(F, Format('Value of Y at X = 0.79: %10.10f', [dfxModel.PredictValue(0.79)]));

        // Value of X at specified Y (Root)
        Writeln(F, 'Value(s) of X at Y = 1408.1046: ');
        Roots := dfxModel.CalculateRoots(1408.1046, -5.0, 2.0, 100);
        SafeArrayGetLBound(roots, 1, lBound);
        SafeArrayGetUBound(roots, 1, uBound);
        For index := lBound to uBound do
          begin
            SafeArrayGetElement(roots, index, result);
            Writeln(F, Format('Root at X = %10.10f', [result]));
          end;

         // Derivative of Y at X = 2.1
         Writeln(F, Format('Derivative at X = 2.1: %10.10f', [dfxModel.CalculateDerivative(2.1)]));

         // Integration from X = -2.1 to 2.1
         Writeln(F, Format('Integral from -2.1 to 2.1: %10.10f', [dfxModel.CalculateIntegral(-2.1, 2.1)]));

        CloseFile(F);
        MessageDlg('Solution is complete, see output.dat', mtInformation, [mbOk],0);

      except
        on E:Exception do
          MessageDlg(Format('Error %s.', [E.Message]), mtError, [mbOk],0);
      end;

    end
  else
    begin
      try
        dfxModel.SaveLog('log.txt', false);
        MessageDlg('Solution failed, see log.txt.', mtError, [mbOk],0);
      except
        on E:Exception do
          MessageDlg(Format('Error %s.', [E.Message]), mtError, [mbOk],0);
      end;
    end;


end;

end.
