I've wrapped the 'NumericalIntegration' C++ library in Haskell. Here is the latest version of the package (the version on Hackage is older).
Here is the main part of the C++ code :
class Integrand {
private:
std::function<double(double)> f;
public:
Integrand(std::function<double(double)>& f_) : f(f_) {}
double operator()(const double x) const { return f(x); }
};
double integration(double f(double),
double lower,
double upper,
double relError,
int subdiv,
double* errorEstimate,
int* errorCode) {
// Define the integrand.
std::function<double(double)> f_ = [&](double x) { return f(x); };
Integrand integrand(f_);
// Define the integrator.
Eigen::Integrator<double> integrator(subdiv);
// Define a quadrature rule.
Eigen::Integrator<double>::QuadratureRule rule =
Eigen::Integrator<double>::GaussKronrod201;
// Define the desired absolute error.
double absError = 0.0;
// Integrate.
double result = integrator.quadratureAdaptive(integrand, lower, upper,
absError, relError, rule);
*errorEstimate = integrator.estimatedError();
*errorCode = integrator.errorCode();
return result;
}
And here is the main part of the Haskell code:
foreign import ccall safe "wrapper" funPtr
:: (Double -> Double) -> IO (FunPtr (Double -> Double))
foreign import ccall safe "integration" c_integration
:: FunPtr (Double -> Double) -> Double -> Double -> Double -> Int
-> Ptr Double -> Ptr Int -> IO Double
-- | Numerical integration.
integration :: (Double -> Double) -- ^ integrand
-> Double -- ^ lower bound
-> Double -- ^ upper bound
-> Double -- ^ desired relative error
-> Int -- ^ number of subdivisions
-> IO IntegralResult -- ^ value, error estimate, error code
integration f lower upper relError subdiv = do
errorEstimatePtr <- mallocBytes (sizeOf (0 :: Double))
errorCodePtr <- mallocBytes (sizeOf (0 :: Int))
fPtr <- funPtr f
result <-
c_integration fPtr lower upper relError subdiv errorEstimatePtr errorCodePtr
errorEstimate <- peek errorEstimatePtr
errorCode <- peek errorCodePtr
let out = IntegralResult {_value = result, _error = errorEstimate, _code = errorCode}
free errorEstimatePtr
free errorCodePtr
freeHaskellFunPtr fPtr
return out
This works but there's a problem regarding the error code of the integration. When the integration is ok, the error code should be 0. Sometimes it is 0, as expected. But sometimes it is a huge integer number, nonsensical, though the integration is fine.
Would you have an idea about this issue? Why this strange error code? Is there something bad in my code? I'm not fluent in C++ (nor in Haskell). But apart this strange error code, the library seems to work very well.
On the
x86_64platform, a HaskellIntis 64 bits while a Cintis 32 bits. (A Clongis 64 bits.) In your code, you are picking up garbage in the upper bytes and getting an absurdly large 64-bit integer whose lowest 32-bits are zero, no doubt.Anyway, there's a module
Foreign.Cthat contains newtypesCInt,CDouble, etc. that are intended to match the corresponding C types on the target platform, and I think it's considered good practice to always use those:Of course, since these are newtypes, there is the bother of wrapping and unwrapping, though usually
fromIntegraltakes care of that automatically, while also converting across bit sizes:But, as a less portable alternative, you could stick with
DoubleandInt32in yourforeign importdeclarations, provided you are only targeting platforms where Cints are 32 bits.Also note that, if you get the types right, then
mallocis a type-safe alternative tomallocBytes:The type of the
Ptrdetermines the correct number of bytes to allocate here.