VB Multi Variable Curve Fitting Example

← All NMath Code Examples


Imports System

Imports CenterSpace.NMath.Core

Namespace CenterSpace.NMath.Examples.VisualBasic

  Module MultiVariableCurveFittingExample

    The MultiVariableFunctionFitter&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 DoubleParameterizedFunctional.
    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 real valued function
    of two variables, x0 and x1, and three parameters, p0, p1, and p2, defined
    by the formula:
    p0*x1*x0^2 + p1*sin(x0) + p2*x1^3
    Private Class ParameterizedFunction
      Inherits DoubleParameterizedFunctional

      Creates an instance of our parameterized function. We must 
      initialize the base class with the dimension of our functions
      domain. Since our function is a function of two variables 
      we initialize the base class with 2.

      Public Sub New()
      End Sub

      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 DoubleVector) As Double

        Return Parameters(0) * X(1) * Math.Pow(X(0), 2.0) + Parameters(1) * Math.Sin(X(0)) + Parameters(2) * Math.Pow(X(1), 3.0)

      End Function

      Since the gradient of our function is rather easy to derive, we will
      override the GradientWithRespectToParams() function. Remember, this is
      the vector of partials with respect to the parameters, NOT the variables.

      <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 
      a 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 DoubleVector, ByRef Grad As DoubleVector)
        Grad(0) = X(0) * X(0) * X(1)
        Grad(1) = Math.Sin(X(0))
        Grad(2) = Math.Pow(X(1), 3)
      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 MultiVariableFunctionFitter fits a parameterized multivariable 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.

      For example, here is dataset from the Matlab docs, which fits a function
      z = f(x, y) to three-dimensional data describing a surface  

      Since the domain of the function has two dimensions, we use a two-column matrix to
      hold the x,y data.
      Dim XYValues As New DoubleMatrix(10, 2)
      XYValues(Slice.All, 0) = New DoubleVector("3.6 7.7 9.3 4.1 8.6 2.8 1.3 7.9 10.0 5.4")
      XYValues(Slice.All, 1) = New DoubleVector("16.5 150.6 263.1 24.7 208.5 9.9 2.7 163.9 325.0 54.3")

      Dim ZValues As New DoubleVector("95.09 23.11 60.63 48.59 89.12 76.97 45.68 1.84 82.17 44.47")

      Published starting guess in the space of the function parameters.
      Dim Start As New DoubleVector("10 10 10")

      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 ParameterizedFunction()
      Dim Fitter As New MultiVariableFunctionFitter(Of TrustRegionMinimizer)(F)
      Dim Solution As DoubleVector = Fitter.Fit(XYValues, ZValues, Start)


      Display the results
      Console.WriteLine("Fit #1")
      Console.WriteLine("Matlab solution: " & New DoubleVector("0.0074 -19.9749 -0.0000").ToString())
      Console.WriteLine("NMath solution: " & Solution.ToString())
      Console.WriteLine("NMath residual: " & Fitter.Minimizer.FinalResidual)

      The parameterized function used by the fitter may also be specified using a delegate.
      here we define a delegate for the same function
      p0*x1*x0^2 + p1*sin(x0) + p2*x1^3
      Dim XDimension As Integer = 2 The dimension of the domain of f.

      Dim FDelegate As Func(Of DoubleVector, DoubleVector, Double) = AddressOf Foo

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

      Now lets perform the fit again using some random data. First we generate
      50 random x,y points in range (0,10).
      XYValues = New DoubleMatrix(50, 2, New RandGenUniform(0, 10))

      The target solution.
      Dim Target As New DoubleVector("1 2 3")

      When calculating the z values, we add some noise, so the points
      dont lie exactly on the target surface.
      ZValues = New DoubleVector(50)
      Dim Rnd As New RandGenUniform(-1, 1)

      Dim I As Integer
      For I = 0 To ZValues.Length - 1
        ZValues(I) = FDelegate(Target, XYValues.Row(I)) + Rnd.Next()

      Perform the fit and display the results
      Solution = Fitter.Fit(XYValues, ZValues, 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("Press Enter Key")

    End Sub

    Private Function Foo(ByVal P As DoubleVector, ByVal X As DoubleVector) As Double
      Return P(0) * X(1) * Math.Pow(X(0), 2.0) + P(1) * Math.Sin(X(0)) + P(2) * Math.Pow(X(1), 3.0)
    End Function

  End Module

End Namespace

← All NMath Code Examples