VB One Variable Curve Fitting Example

← All NMath Code Examples

 

Imports System

Imports CenterSpace.NMath.Core
Imports CenterSpace.NMath.Analysis

Namespace CenterSpace.NMath.Analysis.Examples.VisualBasic

  Module OneVariableCurveFittingExample

    ' The OneVariableFunctionFitter&ltT&gt Needs a parameterized function
    ' and a set of data points. One way to specify the parameterized function,
    ' and optionally its gradient with respect to the parameters, is to
    ' implement an instance of the abstract class DoubleParameterizedFunction.
    ' You must overwrite the Evaluate() method which computes and returns the
    ' parameterized function value at a specified set of parameters and 
    ' point. It is optional to overwrite the GradientWithRespectToParams() method.
    ' If you do not overwrite it, a numerical approximation using finite differences
    ' will be used to approximate the gradient if it is needed.
    ' 
    ' Here the parameterized function we are defining is a three parameter 
    ' exponential function given by the formula
    ' 
    ' p0 * exp(p1 * x) + p2
    ' 
    Private Class ThreeParamExponential
      Inherits DoubleParameterizedFunction

      ' Override the abstract evaluate function.
      ' <param name="parameters">The parameter values.</param>
      ' <param name="x">The point to evaluate at.</param>
      ' <returns>The value of the parameterized function at the given
      ' point and parameters.</returns>
      Public Overrides Function Evaluate(ByVal Parameters As DoubleVector, ByVal X As Double) As Double

        If (Parameters.Length <> 3) Then
          Throw New InvalidArgumentException("Incorrect number of function parameters to ThreeParameterExponential: " & Parameters.Length)
        End If
        Return Parameters(0) * Math.Exp(Parameters(1) * X) + Parameters(2)


      End Function

      ' <summary>
      ' Since the gradient of our function is rather easy to derive, we will
      ' override the GradientWithRespectToParams() function. Remember, this is
      ' the vector of partial derivatives with respect to the parameters, NOT the variables.
      ' </summary>
      ' <param name="parameters">Evaluate the gradient at these parameter values.</param>
      ' <param name="x">Evaluate the gradient at this point.</param>
      ' <param name="grad">Place the value of the gradient in this vector.</param>
      ' <remarks>Note how this function does not return the gradient as a new
      ' vector, but places the gradient value in a vector supplied by the 
      ' calling routine. This is for optimization purposes. The curve fitter uses 
      ' an optimization algorithm that will most likely be iterative, and thus may 
      ' need to evaluate the gradient many times. Having the vector 
      ' passed in to the routine allows the calling code to allocate space for the 
      ' gradient once and reuse it on successive calls, thus avoiding the potential 
      ' of allocating a large number of small objects on the managed heap.</remarks>
      Public Overrides Sub GradientWithRespectToParams(ByVal Parameters As DoubleVector, ByVal X As Double, ByRef Grad As DoubleVector)

        Grad(0) = Math.Exp(Parameters(1) * X)
        Grad(1) = Parameters(0) * X * Math.Exp(Parameters(1) * X)
        Grad(2) = 1.0

      End Sub

    End Class

    ' A .NET example in Visual Basic showing how to fit a generalized multivariable function to a set 
    ' of points.
    ' Uses the trust-region algorithm.
    Sub Main()

      ' Class OneVariableFunctionFitter fits a parameterized function to a
      ' set of points. In the space of the function parameters, beginning at a specified
      ' starting point, the Fit() method finds a minimum (possibly local) in the sum of
      ' the squared residuals with respect to the data. Fit() uses a nonlinear least
      ' squares minimizer specified as a generic argument.

      Dim XValues As DoubleVector = New DoubleVector("[-3 -2 -1 0 1 2 3]")
      Dim YValues As DoubleVector = New DoubleVector("[1 1.2 1.8 2.8 6.6 14.6 40]")

      ' Starting guess in the space of the function parameters.
      Dim start As New DoubleVector("[1 .6 .7]")

      ' Construct a curve fitting object for our function, then perform the fit. We will use the
      ' TrustRegionMinimizer implementation of the non-linear least squares minimizer to find the optimal
      ' set of parameters. 
      Dim F As New ThreeParamExponential()
      Dim Fitter As OneVariableFunctionFitter(Of TrustRegionMinimizer) = New OneVariableFunctionFitter(Of TrustRegionMinimizer)(F)
      Dim Solution As DoubleVector = Fitter.Fit(XValues, YValues, start)

      Console.WriteLine()

      ' Display the results
      Console.WriteLine("Fit #1")
      Console.WriteLine("NMath solution: " & Solution.ToString())
      Console.WriteLine("NMath residual: " & Fitter.Minimizer.FinalResidual)
      Console.WriteLine()

      ' The parameterized function used by the fitter may also be specified using a delegate.
      ' Here we define a delegate for the same three parameter exponential function
      ' p0*exp(p1*x) + p2
      Dim fdelegate As Func(Of DoubleVector, Double, Double) = AddressOf ThreeParamExponentialFunction

      ' The delegate for the parameterized function may be used directly in OneVariableFunctionFitter
      ' constructors, or may be wrapped by the DoubleParameterizedDelegate, which implements 
      ' DoubleParameterizedFunction. Here we do the latter.
      ' Note that we do not supply the gradient with respect
      ' to parameters. The gradient will be computed using a finite difference algorithm if
      ' needed.
      Fitter.Function = New DoubleParameterizedDelegate(fdelegate)
      ' Perform the fit and display the results
      Solution = Fitter.Fit(XValues, YValues, start)
      Console.WriteLine("Fit #1 (Repeated without user specified Partial Derivatives)")
      Console.WriteLine("NMath solution: " & Solution.ToString())
      Console.WriteLine("NMath residual: " & Fitter.Minimizer.FinalResidual)
      Console.WriteLine()

      ' Now let's perform the fit again using some random data. First we generate
      ' 50 random x,y points in range (-4,4).
      XValues = New DoubleVector(50, New RandGenUniform(-4, 4))

      '/ The target solution (parameter values).
      Dim Target As New DoubleVector("2 1 1")

      ' When calculating the y values, we add some noise, so the points
      ' don't lie exactly on the target curve.
      YValues = New DoubleVector(50)
      Dim Rnd As New RandGenUniform(-0.1, 0.1)

      Dim I As Integer
      For I = 0 To YValues.Length - 1
        YValues(I) = fdelegate(Target, XValues(I)) + Rnd.Next()
      Next

      ' Perform the fit and display the results
      Solution = Fitter.Fit(XValues, YValues, start)
      Console.WriteLine("Fit #2")
      Console.WriteLine("Target solution: " & Target.ToString())
      Console.WriteLine("Actual solution: " & Solution.ToString())
      Console.WriteLine("Residual: " & Fitter.Minimizer.FinalResidual)
      Console.WriteLine()

      Console.WriteLine()
      Console.WriteLine("Press Enter Key")
      Console.Read()

    End Sub


    Private Function ThreeParamExponentialFunction(ByVal P As DoubleVector, ByVal X As Double) As Double

      If (P.Length <> 3) Then
        Throw New InvalidArgumentException("Incorrect number of function parameters to ThreeParameterExponential: " & P.Length)
      End If

      Return P(0) * Math.Exp(P(1) * X) + P(2)

    End Function

  End Module

End Namespace

← All NMath Code Examples
Top