← All NMath Code Examples
using System;
using CenterSpace.NMath.Core;
namespace CenterSpace.NMath.Examples.CSharp
{
class OneVariableCurveFittingExample
{
/// <summary>
/// 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
///
/// </summary>
class ThreeParamExponential : DoubleParameterizedFunction
{
/// <summary>
/// Override the abstract evaluate function.
/// </summary>
/// <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 override double Evaluate( DoubleVector parameters, double x )
{
if ( parameters.Length != 3 )
{
throw new InvalidArgumentException( "Incorrect number of function parameters to ThreeParameterExponential: " + parameters.Length );
}
return parameters[0] * Math.Exp( parameters[1] * x ) + parameters[2];
}
/// <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 override void GradientWithRespectToParams( DoubleVector parameters, double x, ref DoubleVector grad )
{
grad[0] = Math.Exp( parameters[1] * x );
grad[1] = parameters[0] * x * Math.Exp( parameters[1] * x );
grad[2] = 1.0;
}
}
/// <summary>
/// A .NET example in C# showing how to fit a generalized multivariable function to a set
/// of points.
/// </summary>
/// <remarks>
/// Uses the trust-region algorithm.
/// </remarks>
static void Main( string[] args )
{
// 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.
var xValues = new DoubleVector( "[-3 -2 -1 0 1 2 3]" );
var yValues = new DoubleVector( "[1 1.2 1.8 2.8 6.6 14.6 40]" );
// Starting guess in the space of the function parameters.
var start = 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.
var f = new ThreeParamExponential();
var fitter = new OneVariableFunctionFitter<TrustRegionMinimizer>( f );
DoubleVector solution = fitter.Fit( xValues, yValues, start );
Console.WriteLine();
// Display the results
Console.WriteLine( "Fit #1" );
Console.WriteLine( "NMath solution: " + solution );
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
Func<DoubleVector, double, double> fdelegate = delegate( DoubleVector p, double x )
{
if ( p.Length != 3 )
{
throw new InvalidArgumentException( "Incorrect number of function parameters to ThreeParameterExponential: " + p.Length );
}
return p[0] * Math.Exp( p[1] * x ) + p[2];
};
// 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 );
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).
var target = 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, new RandGenUniform( -.1, .1 ));
for ( int i = 0; i < yValues.Length; i++ )
{
yValues[i] += fdelegate( target, xValues[i] );
}
// Perform the fit and display the results
solution = fitter.Fit( xValues, yValues, start );
Console.WriteLine( "Fit #2" );
Console.WriteLine( "Target solution: " + target );
Console.WriteLine( "Actual solution: " + solution );
Console.WriteLine( "Residual: " + fitter.Minimizer.FinalResidual );
Console.WriteLine();
Console.WriteLine();
Console.WriteLine( "Press Enter Key" );
Console.Read();
} // Main
} // class
} // namespace
← All NMath Code Examples