Lots more information in the documentation that will likely prove useful to call C from Julia.
Can call C functions directly from shared C libraries using ccall
Shared C libraries called by (:function, "library")
:function is the exported C function name
"library" is the name of the shared library
If C library is available as a shared library only needs to be on system LOAD_PATH, Otherwise need to compile it and put the library on the LOAD_PATH. Will see in example later.
ccall
ccall
is like an ordinary function call, can use in REPLccall( (:function, “library”),
Return Type (i.e. Int32),
Tuple of Input Types. (i.e. (Int32, Int64, ...) ),
Arguments to pass to C from Julia )
Note: ccall automatically converts Julia type to C type. See Julia documentation for a list of type correspondences.
In practice, can be useful to wrap ccall
in a Julia function to:
A Julia package that wraps for John Burharkdt's C code to generate nodes and weights for sparse grid integration.
Approximate a $D$-variate integral of the form
$$\int_{\Omega} g(x)f(x)dx$$where $\Omega\in D$ and $f(x)$ is a weighting function. A quadrature rule prescribes a $D\times R$ matrix of nodes $X$ and a $R$-vector of weights $w$ depending on $\Omega$ and $f$. The approximation is calculated as
$$g(X)w$$The built-in families of integration rules are identified by a 3-letter key:
As a general rule, the versions with the nested univariate rules are more efficient in the sense that they require fewer nodes to achieve the same level of polynomial exactness.
The C function I want to use, nwspgr
, calls nested functions. See lines 3377-3428 in 'sparse_grids_hw.c'.
void nwspgr ( void rule ( int n, double x[], double w[] ),
int rule_order ( int l ),
int dim, int k, int r_size, int *s_size, double nodes[], double weights[] )
Problems: Nested C functions are not compatible with ccall
(as far as i know!)
$\rightarrow$ Added 3 functions in C so can use Julia's ccall
to run nwspgr
See lines 6061-6492 in 'sparse_grids_hw.c' and the header file: 'sparse_grids_hw.h'.
- int nwspgr_rule_size ( void rule ( int n, double x[], double w[] ),
int rule_order ( int l ), int dim, int k, int r_size);
- int nwspgr_rule_size_wrapper (int rule , int d, int k);
- void nwspgr_wrapper (int rule , int d, int k, double x[], double w[]);
Two steps:
To use the code in Julia compile the C-code to create a dynamic library ready to be called by Julia. If using the 'sparse_grids_hw_jl.sh', it creates a dynamic library called 'sparselib.dylib' in a new folder, /lib.
To enable Julia to find the newly created library, add the path where the library is stored to Julia's DL_LOAD_PATH. One way to do this is to add the line
push!(Sys.DL_LOAD_PATH,"[Path to new library's folder]")
to ~/.juliarc.jl.
To output nodes and weights specify which integration rule to use, the number of dimensions, and the level accuracy of the integration rule. The constructor function is:
nw = nwspgr( rule, dims, level )
where rule is one of {"GQU","KPU","GQN","KPN"} and nw
is an instance of
type nwspgr
rule :: ASCIIString
dims :: Int64
level :: Int64
nodes :: Array{Float64,2}
weights :: Vector{Float64}
end
Note: The maximum dimension supported by the code is 20.
Julia code to where the Julia nwspgr
type is defined is contained in the file sparse_grids_hw.jl.
Recall that nwspgr
in C-code was nested $\rightarrow$ added separate C functions to make compatible with Julia ccall
syntax.
So, in the constructor function in Julia there are ccall
's to these `ccall' compatible C functions.
ccall
for nwspgr_rule_size_wrapper
to tell Julia the correct size of the Array needed to store the nodes and weights.
ccall
for nwspgr_wrapper
is passed a pointer to correctly sized arrays created in Julia and fills them in with nodes and weights.
Note: ccall
on passes a reference location in memory of array, not the array itself.
Managing memory in non-standard ccall
of C functions (i.e. nested functions)
General rule stated in Julia docs: The program that created the memory needs to clean it up once it is finished with it.
How to avoid problems? If passing Julia created array for C-code to fill in, make sure Julia array has correct size when array is initiated. Helps to avoid segmentation faults...
Note: Segmentation faults occur due to bad memory management. For example, can occur when a program tries to grab memory it thinks is free only to find there is something already there (or the reverse).
Integral to approximate:
$$\mathcal{I}=\int_{0}^{1}\cdots\int_{0}^{1}\underbrace{{\displaystyle \prod_{d=1}^{D}\frac{1}{\sqrt{2\pi\sigma^{2}}}e^{-\frac{1}{2}\left(\frac{x_{d}-\mu}{\sigma}\right)^{2}}}}_{g(x)}dx_{D}\cdots dx_{1}$$where $x_{d}\sim N\left(\mu,\sigma\right)$.
Let $\mu=0$ and $\sigma=2$, then the "truth":
$$\mathcal{I} = \frac{1}{2} \operatorname{erf} \left(\frac{1}{ \sigma\sqrt{2}}\right)$$because $\Phi(x) = \frac{1}{2}\left(1+\operatorname{erf}\left(\frac{x-\mu}{ \sigma\sqrt{2}}\right)\right)$
In [ ]:
include("./Git/SparseGridsHW/test/nwspgr_demo.jl")
Integral to approximate:
$$\mathcal{I}=\int_{-\infty}^{\infty}\cdots\int_{-\infty}^{\infty}\mathcal{P}(x)f\left(x\right)dx_{D}\cdots dx_{1}$$where $\mathcal{P}(x)=4x_{1}^{2}-3x_{2}^{3}+x_{3}^{9}-2x_{4}^{4}$ and $x\sim N\left(\mu,\Sigma\right)$.
In [ ]:
include("./Git/SparseGridsHW/test/nwspgr_mvrn.jl")