[TOC]
Imports System
Imports CenterSpace.NMath.Core
Imports CenterSpace.NMath.Analysis
Namespace CenterSpace.NMath.Analysis.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 it's 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 fintite 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 VB.NET showing how to fit a generalized multivariable function to a set
' of points.
' Uses the trust-region algorithm.
Sub Main()
Console.WriteLine()
' Class OneVariableFunctionFitter fits a parameterized function to a
' set of points. In the space of the function parameters, begining 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 arguement.
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)
' 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 caculating 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
[TOC]