Using Boost Math Library in C#

Hello,

In this post I will share with you a lazy way to expose boost.math library to a C# project using C++/CLI. It is lazy and very wrong to do it this way, but I would like to share it with you anyway. I am doing this because of the following reasons:

  • Mixing different languages and frameworks is very difficult and error-prone. I have looked through tons of sites on ways to expose unmanaged functionality to the managed code, and apart from PInvoke, I have not found any stable and simple solution. Hopefully the step-by-step solution I am sharing may turn out to be helpful to someone one day.
  • My approach is by far not the best way to expose the Boost library to C#. But it is dead easy. I am aware of this, and if you know a better way – please let me know by contributing to this post.
  • There are times when all you need is a quick hack to get something done. This solution is just that, and it should not, ideally, be a part of a production system used by many people. Keep this in mind.

Now that the disclaimer part is over, here is the step-by-step solution.

Limitations

The limitations of this solution are huge. Firstly, it cannot be used it a multi-threaded environment. Boost library has an atomic component that is not CLR compatible. Inside Boost.Math (v. 1.57.0), the Bernoulli distribution class uses atomic data type, I believe for concurrency. My solution does not work with atomic and therefore won’t let you utilize the Bernoulli distribution functions.

A second important limitation has to do with the overall approach. I am not a C++/CLI expert, and my approach is limited to what I know about the language. However, the C++/CLI dll is the bridge between Boost and C# I am about to show. In my solution it requires turning everything into a function that returns some basic value type. The function can then be used by a C# program, but the return type always has to be a basic value type. For example, imagine you need to create an instance of a normal distribution with some known mean m and standard deviation s. You are then planning to use this instance to calculate quantiles and cdf. C++/CLI won’t let you to return an instance of unmanaged boost::math::normal class to C#. So, you need to keep the class inside the C++/CLI code, and instead expose the functions that return some probability from cdf or a quantile as doubles.

Solution Summary

After downloading the Boost library,  create a Visual C++ CLR class library. Add the boost library location as the additional include directory to the project.  Add a new C++/CLI ref class exposing the required boost functionality to the solution’s header file. This is done as function calls accepting basic value type arguments and returning basic value type parameters. Modify the atomic file by commenting out the error definition for managed code, and compile the solution as a dll. This dll can be referenced by a C# project and the underlying methods that expose the Boost.Math library functions can be called from your C# code.

Step-by-Step Solution

1. The latest stable boost library can be downloaded from http://www.boost.org. I am using version 1.57.0. Unzip its contents to a local folder (e.g. C:\Program Files (x86)\boost).

2. Using Visual Studio, create a new C++ CLR Class Library project. I am using VS 2013, and the location of a class library project for me is Visual C++->CLR->Class Library. Give it whatever name you want (e.g. TestClassLibrary). The project solution will contain header files, resource files and source files. You will only need to modify the header file, i.e. the TestClassLibrary.h.

3. Right-click on the project name and go to Properties. On the C/C++ properties, add the location of your boost library to the ‘Additional Include Directories‘. If you want, you may also add the same location to the additional #using directories. I don’t do this because I fully qualify the boost namespaces.

4. Let’s say that for this example we would like to expose two functions from Boost.Math: calculate the inverse of the incomplete beta function and calculate some quantile of a normal distribution with custom mean m and standard deviation s. To do this, add the following code to the solution’s header file:

// TestClassLibrary.h

#pragma once
#include <boost\math\special_functions\beta.hpp>
#include <boost\math\distributions\normal.hpp>

using namespace System;

namespace TestClassLibrary {

	public ref class BoostMathExpose
	{
	public:
		double static InverseIncompleteBeta(double a, double b, double x)
		{
			return boost::math::ibeta_inv(a, b, x);
		}
		
		double static NormalDistribution(double m, double s, double quantile)
		{
			boost::math::normal sol = boost::math::normal::normal_distribution(m, s);
			return boost::math::quantile(sol, quantile);
		}

	};
}

5. If you try to compile this code, you are going to get a compilation error about atomic class not being compatible with /clr or /clr:pure switch. To fix this, I resolved to commenting out the three lines that cause this error. You can find the relevant atomic file in C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include directory. You need to be logged in as an administrator to change this file. Because it is marked as read-only, make a copy of the modified file somewhere else (e.g. Desktop folder) and then copy and paste the modified file replacing the original. As I mentioned before, because you are disabling this warning, bad things can happen if you attempt to use these methods in a multi-threaded environment. The lines to comment out are:

#ifdef _M_CEE
#error is not supported when compiling with /clr or /clr:pure.
#endif /* _M_CEE */

6.Once you make the above change, the C++/CLI solution will compile and produce a dll. Create a C# project from which you are planning to call the dll functions. In the project solution explorer add a reference to the dll file (e.g. TestClassLibrary.dll). Once done, you will be able to see two methods InverseIncompleteBeta and NormalDistribution. Here is an example C# code that calls these methods:

using System;

namespace TestUsingCLI
{
    class EntryPoint
    {
        static void Main(string[] args)
        {
            double a = 2;
            double b = 3;
            double x = 0.4;
            double answer = TestClassLibrary.BoostMathExpose.InverseIncompleteBeta(a, b, x);

            double mean = 70;
            double std = 4;
            double quantile = 0.90;
            double answer2 = TestClassLibrary.BoostMathExpose.NormalDistribution(mean, std, quantile);

            Console.WriteLine(answer.ToString()+" "+answer2.ToString());
            Console.ReadLine();
        }
    }
}

That is all. Thanks for reading.