F# Math (I.) - Numeric types in PowerPack

In this article, we'll briefly look at two numeric types that are available in F# PowerPack. The type complex represents complex numbers consisting of real and imaginary parts. Both parts are stored as a floating point numbers. The type BigRational represents rational numbers consisting of numerator and denominator of arbitrary sizes. Integers of arbitrary size are represented using BigInteger type that is available in .NET 4.0 (in the System.Numerics.dll assembly). On .NET 2.0, the BigInteger type is also a part of F# PowerPack.

This article is a part of a series that covers some F# and F# PowerPack features for numerical computing. Other articles in this series discuss matrices, defining custom numeric types and writing generic code. For links to other parts, see F# Math - Overview of F# PowerPack.

Using complex numbers

The first example demonstrates a few basic operations that can be done with complex numbers in F#. In order to run the code in the listing, you first need to reference FSharp.PowerPack.dll. This can be done either using #r directive in F# Interactive (when working in an F# Script file such as Script.fsx) or using the “Add Reference” dialog in Visual Studio when creating an F# project (or using -r:FSharp.PowerPack.dll option of the compiler):

 1: #r "FSharp.PowerPack.dll"
 2: open Microsoft.FSharp.Math;;
 3: 
 4: let c = complex -1.0 0.0;;
 5: val c : complex = -1r+0i
 6: 
 7: let i = sqrt c;;
 8: val i : Complex = 6.12303176911189e-17r+1i
 9: 
10: i * i;;
11: val it : Complex = -1r+1.22460635382238e-16i { ... }

After referencing PowerPack and opening the namespace that contains functionality for mathematical calculations, the snippet creates a number using the complex function. The first parameter represents the real part and the second one represents the imaginary part, so the example creates a complex number representing a real number -1. The next step uses sqrt function to calculate the square root. Since the calculation uses complex numbers, this is a valid operation and it returns the number i (with some rounding error). Next, the snippet calculates square of i to get a real number -1 again.

Note that both sqrt and * are standard operations that are available in the F# library and don’t handle the complex type explicitly. Instead, they work with any type that provides specific members. This is done using static member constraints, which are discussed in the last two articles of this series (see introduction for links).

Using rational numbers

In this section, we look at a similarly simple listing that shows working with the BigRational type. Literals of this type can be written more easily using the N suffix. The suffix can be only when the FSharp.PowerPack.dll library is referenced and it can be used to write rational numbers representing (arbitrary large) integers. To get a rational representing a fraction, we can use division operator:

 1: let r1 = 4N / 6N;;
 2: val r : BigRational = 2/3N
 3: 
 4: let r2 = pown r1 10;;
 5: val r : BigRational = 1024/59049N
 6: 
 7: let q1 = pown (1N / 2N) 5
 8: let q2 = pown (1N / 2N) 6;;
 9: val q1 : BigRational = 1/32N
10: val q2 : BigRational = 1/64N
11: 
12: q2 < r2 && q1 > r2;;
13: val it : bool = true

Literals such as 4N can be only created from integers. However, the overloaded division operator for BigRational returns a BigRational representing fractions. As you can see, the number is stored in a normalized form (this means there is no integer such that the numerator and denominator can be divided by it).

The next couple of lines show code that finds a number q such that (1/2)q is smaller the number r2 and (1/2)(q+1) is larger. Just like when working with complex numbers, we can use standard functions and operators such as pown and <. These are overloaded and work with any numeric type.

The literals of BigRational can be constructed using notation such as 4N. This is not a built-in language feature. In fact, you can define your own literals (using a limited set of suffixes) for your own numeric type. This is discussed in an article that explains how to define a custom numeric type (see introduction for links).

Calculating PI in F#

To look at some larger example that uses the BigRational type, let's write an F# snippet that calculates an estimate of the number π. The sample uses Gregory–Leibniz series that is explained on WikiPedia [3]. First few members of the series are 4/1 - 4/3 + 4/5 - 4/7 etc. It is quite easy to see the pattern - the denominator always increases by 2 and the operation varies between + and -. The series is quite simple to calculate, but it does not give a very precise estimate. However, it is good enough to demonstrate working with rational numbers.

We can make the code nicely functional by representing the series as a value of type seq<BigRational>. The following series function calculates a single element of the series (using denominator n) and then calls itself recursively to calculate the rest:

 1: /// Generates series that approximates PI 
 2: /// 4/1 - 4/3 + 4/5 - 4/7 + 4/9 (starting from q * 4/n)
 3: let rec series n q = 
 4:   seq { 
 5:     yield q * 4N / n
 6:     yield! series (n + 2N) (q * -1N) }
 7: 
 8: let pi1 = series 1N 1N |> Seq.take 10 |> Seq.reduce (+)
 9: val pi1 : BigRational = 44257352/14549535N
10: float pi1
11: val it : float = 3.041839619
12: 
13: let pi2 = series 1N 1N |> Seq.take 300 |> Seq.reduce (+)
14: val pi2 : BigRational = (...)
15: float pi2
16: val it : float = 3.13825933

If we added all elements of the series, we would get the precise value of π. Unfortunately, we don't have infinite space and time, so the snippet just takes first few elements (using Seq.take) and then adds all elements using Seq.reduce. Every second element is already negative, because the series function multiplies the elements by q, which alternates between 1 and -1, so we can use the + operator as the aggregation function.

Using F# numeric types from C#

The PowerPack library is mainly designed as a library with additional functionality for F#. However, it is a standard .NET library and can be referenced from C# too. Moreover, F# represents overloaded operators in the same way as C#, so the types BigRational and Complex (and also BigInteger when using .NET 2.0) can be quite nicely used from C#.

The following example re-implements the previous F# snippet to calculate the number π. It follows the same approach as F#. The method Series generates elements of the Gregory–Leibniz series. The implementation uses loop instead of recursion, because recursive iterators cannot be written efficiently in C#:

static IEnumerable<BigRational> Series() {
  var n = BigRational.FromInt(1);
  var q = BigRational.FromInt(1);
  while (true) {
    yield return q * BigRational.FromInt(4) / n;
    n = n + BigRational.FromInt(2);
    q = q * BigRational.FromInt(-1);
  }
}

As you can see, we can create BigRational from integers using a static method BigRational.FromInt. The type also implements standard overloaded operators (the snippet uses +, * and /) and a few additional operations, such as BigRational.PowN (to calculate power). To get approximate value of π as a floating point number, we can write:

var pi = Series().Take(300).Aggregate((a, b) => a + b);
Console.WriteLine(BigRational.ToDouble(pi));

In C#, we can use LINQ to take 300 elements of the series and sum them. The resulting rational number can be converted to floating point using BigRational.ToDouble:

Summary

Using F# PowerPack from C# is just a bonus. The library is mainly designed for F#, but it follows standard .NET programming patterns, so it can be nicely used from C# too. The F# PowerPack implementation of rational numbers is likely good enough for many applications, but if you need more features or better performance, you can check out a list of numerical libraries for .NET (and F#) on MSDN [4].

References & Links

namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Math
val c : complex

Full name: Untitled.c

  type: complex
  implements: System.IComparable
  inherits: System.ValueType
Multiple items
val complex : float -> float -> complex

Full name: Microsoft.FSharp.Math.ComplexTopLevelOperators.complex

--------------------

type complex = Complex

Full name: Microsoft.FSharp.Math.complex

  type: complex
  implements: System.IComparable
  inherits: System.ValueType
val i : Complex

Full name: Untitled.i

  type: Complex
  implements: System.IComparable
  inherits: System.ValueType
val sqrt : 'T -> 'U (requires member Sqrt)

Full name: Microsoft.FSharp.Core.Operators.sqrt
val r1 : BigRational

Full name: Untitled.r1

  type: BigRational
  implements: System.IComparable
val r2 : BigRational

Full name: Untitled.r2

  type: BigRational
  implements: System.IComparable
val pown : 'T -> int -> 'T (requires member get_One and member ( * ) and member ( / ))

Full name: Microsoft.FSharp.Core.Operators.pown
val q1 : BigRational

Full name: Untitled.q1

  type: BigRational
  implements: System.IComparable
val q2 : BigRational

Full name: Untitled.q2

  type: BigRational
  implements: System.IComparable
val series : BigRational -> BigRational -> seq<BigRational>

Full name: Untitled.series

Generates szeries that approximates PI
 4/1 - 4/3 + 4/5 - 4/7 + 4/9 (starting from q * 4/n)

val n : BigRational

  type: BigRational
  implements: System.IComparable
val q : BigRational

  type: BigRational
  implements: System.IComparable
Multiple items
val seq : seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------

type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>

  type: seq<'T>
  inherits: System.Collections.IEnumerable
val pi1 : BigRational

Full name: Untitled.pi1

  type: BigRational
  implements: System.IComparable
module Seq

from Microsoft.FSharp.Collections
val take : int -> seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.take
val reduce : ('T -> 'T -> 'T) -> seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.reduce
Multiple items
val float : 'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------

type float = System.Double

Full name: Microsoft.FSharp.Core.float

  type: float
  implements: System.IComparable
  implements: System.IFormattable
  implements: System.IConvertible
  implements: System.IComparable<float>
  implements: System.IEquatable<float>
  inherits: System.ValueType


--------------------

type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>

  type: float<'Measure>
  implements: System.IComparable
  implements: System.IConvertible
  implements: System.IFormattable
  implements: System.IComparable<float<'Measure>>
  implements: System.IEquatable<float<'Measure>>
  inherits: System.ValueType
val pi2 : BigRational

Full name: Untitled.pi2

  type: BigRational
  implements: System.IComparable

Discuss on twitter, .
Send corrections via GitHub pull requests.