Calling the NAG Library from Microsoft F#

As noted in NAG Library for .NET, the required declarations to call the NAG Library from .NET are made available in C# and VB.NET but they may also be used for F#. Please see Calling the NAG Library from Microsoft C# for details on obtaining the declarations and mapping between .NET and NAG Library types.

It is possible to edit the declarations to be in F# syntax (which is very similar). The exact syntax required is in Microsoft's F# reference. However, this is unnecessary at is simpler to compile the supplied C# declarations and reference the resulting library from your F# project. Using C# to compile the required declarations also makes it possible to use C# to handle some other aspects of the interface to the library and to provide a more idiomatic .NET interface to F#.

In the example below, we use the example quadrature routine d01ah. This is a small enough example to describe here but is large enough to demonstrate issues that arise, including a callback function (to be integrated). We give the commands for the Windows implementation of .NET, but the process is similar on other platforms. Also of course one may use Visual studio rather than the command line if you prefer.

First, in d01ah-helper.cs we use C# to compile the provided declarations for d01ahf. In addition this defines a small .NET function d01ah which uses a nested function declaration to convert a function taking an argument by value to one taking an argument by reference, as required by the low level Library interface.

This is compiled with a command such as

csc /platform:x64 /target:library d01ah-helper.cs


This produces a library d01ah-helper.dll which may be referenced from any .NET project.

In d01ah_1.fs we show the example program from the Fortran Library expressed in F#.

The example shows the evaluation of an approximation of π using the identity;

$$\int_0^1\frac{4}{1+x^2} \,dx = \pi$$

The example may be compiled as follows

fsc /platform:x64 /r:d01ah-helper.dll d01ah_1.fs


Producing the output

Approximation to the integral =   3.14159
Estimated relative error = 5.85e-009
Number of function evaluations = 15


For comparison, we show the C# version using the same helper library. This may be compiled with csc, producing essentially the same output when run

csc /platform:x64 /r:d01ah-helper.dll d01ah_cs1.cs


As can be seen, the F# interface is very similar to the C# here, but F# more naturally supports a more functional style. In the second example, we build on the first interface but define a DefInt function using purely F# constructs without needing to explicitly reference the native library interfaces. Here we pre-set the error bounds and only return the value of the integral, not the associated information, and provide a more natural curried interface. This takes the function to be integrated, and the lower and upper bounds and returns the result. The function may be given inline if desired.

The call to d01ah in this example we use an inline lambda function and the example program becomes just

printfn "π is approx %f" (DefInt (fun x -> 4.0/(1.0+x*x)) 0.0 1.0)


once DefInt is defined, which produces the output

π is approx 3.141593

Note that now the F# type inference can work naturally, there is no need to specify any delegate type for the callback and the function is called with a curried signature DefInt f a b rather than taking a tuple DefInt(f,a,b).

The above examples might be compared to the standard NAG Library example calculating the same interval.

Code used for the above examples

d01ah-helper.cs

using System;
using System.ComponentModel;
using System.Runtime.InteropServices; /* Provides mappings between C# and native code */

namespace NumericalAlgorithmsGroup
{
{

/* NAG routine declarations copied from flcsdnet64.cs */

/* Delegate object, note that the scalar parameter is 'ref' */
public delegate double D01AHF_F_DELEGATE(
ref double X
);

[DllImport("NLW6I271E_nag.dll")]
public static extern double D01AHF(
ref double A,
ref double B,
ref double EPSR,
ref int NPTS,
ref double RELERR,
D01AHF_F_DELEGATE F,
ref int NLIMIT,
ref int IFAIL
);

public delegate double d01ah_delegate(double x);

public static double d01ah(double a, double b, double epsr, d01ah_delegate f, int nlimit,
out int npts, out double relerr)
{
double result = 0.0;
int ifail = 1;
npts = 0;
relerr = 0.0;
double ff(ref double x)
{
return f(x);
}
result=D01AHF(ref a, ref b, ref epsr, ref npts, ref relerr, ff, ref nlimit,  ref ifail);
if (ifail == 1 || ifail == 2)
{

// you may want to use WarningException d01ahEx = new WarningException
// or some message logger, depending on the application
Console.WriteLine(
string.Format("d01ahf returned ifail {}, result may not be accurate", ifail));
ifail=0;
}
if (ifail != 0)
{
throw new Exception(
String.Format("d01ahf returned ifail {0}", ifail));
}
return result;
}
}
}


d01ah_1.fs

open System
open NumericalAlgorithmsGroup
let npts = ref 0
let relerr = ref 0.0
let f x =
4.0/(1.0+x*x)

printfn "Approximation to the integral = %9.5f"
printfn "Estimated relative error = %9.2e" !relerr
printfn "Number of function evaluations = %i" !npts


d01ah_cs1

using System;
using NumericalAlgorithmsGroup;

namespace NumericalAlgorithmsGroup
{
{
public static void Main()
{
double epsr = 1.0e-5;
int nlimit= 0;
int npts = 0;
double relerr = 0.0;
double result = 0;

double a = 0.0;
double b = 1.0;

result = Quadrature.d01ah(a, b, epsr, f, nlimit, out npts, out relerr);

Console.WriteLine("Approximation to the integral = {0, 8:f5}", result);
Console.WriteLine("Estimated relative error = {0, 9:e2}", relerr);
Console.WriteLine("Number of function evaluations = {0}", npts);

}
public static double f(double x)
{
return 4.0 / (1.0 + x * x);
}
}
}


d01ah_2.fs

open System
open NumericalAlgorithmsGroup

let DefInt f a b =
let epsrel = 1e-5
let npts = ref 0
let relerr = ref 0.0