← All NMath Code Examples
Imports System
Imports CenterSpace.NMath.Core
Namespace CenterSpace.NMath.Examples.VisualBasic
Module OneVariableCurveFittingExample
The OneVariableFunctionFitter<T> 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 lets 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
dont 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