Monday, 9 April 2007

Foreign Function Interface (FFI)

Interoperability with native-code libraries is an unsung hero in the F# world. This little-discussed feature makes it easy to interface to libraries like LAPACK, FFTW and legacy code written in unmanaged languages.

The following F# snippet illustrates minimal use of the F# foreign function interface to implement a very fast discrete sine transform function that looks and acts just like an ordinary F# function.

The following module binds the primitive C functions exported by the excellent FFTW library:
module Primitive =
open System.Runtime.InteropServices
open Microsoft.FSharp.NativeInterop

[<dllimport(@"libfftw3-3.dll", entrypoint="fftw_malloc">]
extern double *fftw_malloc(int size);

[<dllimport(@"libfftw3-3.dll", entrypoint="fftw_free">]
extern void fftw_free(double *data);

[<dllimport(@"libfftw3-3.dll", entrypoint="fftw_plan_r2r_1d">]
extern void *fftw_plan_r2r_1d(int n, double *i, double *o,
int kind, int flags);

[<dllimport(@"libfftw3-3.dll", entrypoint="fftw_destroy_plan">]
extern void destroy_plan(void *plan);

[<dllimport(@"libfftw3-3.dll", entrypoint="fftw_execute">]
extern void execute(void *plan);

The following function allocates an array using the FFTW library's custom allocation function to ensure optimal data alignment:
open Primitive

let make n =
let ptr = fftw_malloc(8*n)
let na = NativeArray.FromPtr(ptr, n)
ptr, na

Finally, the following function uses the FFTW library to plan how to do a Fourier transform before actually doing it:
let dst a =
let n = Array.length a
let ptr, arr = make n
Array.iteri (fun i x -> arr.[i] <- x) a
let plan = fftw_plan_r2r_1d(n, ptr, ptr, 7, 0)
execute plan
destroy_plan plan
let s = 1. / sqrt(float(2*(n + 1)))
let a = Array.init n (fun i -> s * arr.[i])
fftw_free ptr
a

That is all that is required to the use C interface of this part of the FFTW library from F# code.

The F# distribution contains extensive bindings for the LAPACK linear algebra library, which covers the other main facet of scientific computation.

The design and use of these techniques will be described in detail in my forthcoming book F# for Scientists.

4 comments:

Anonymous said...

F# Version 1.9.6.16 there are problems with:
arr.[i] <- x

Flying Frog Consultancy Ltd. said...

With later versions of F#, you will need to add a type annotation. Something like "let (ptr, arr: float array) = make n" on the previous line.

Anonymous said...

Thanks, but I have yet a few problems also with your suggestion:

I have changed:

let na = NativeArray.FromPtr(ptr, n)
with
let na = NativeArray>double<(ptr, n)

but I get type mismatch error:

Type mismatch. Expecting a nativeptr;double< * float array but given a nativeptr;double< * NativeArray;double<. The type 'float array' does not match the type 'NativeArray;double<'

If I don't use native array all seems working, that is if I use:

let na : float array = Array.create n 0.0

What I have to do?

Thanks

Anonymous said...

Sorry I have swapped < with >