MENU

Day 25: Interfacing with Other Languages

This article was first published on Python | datawookie , and kindly contributed to python-bloggers. (You can report issue about the content on this page here)
Want to share your content on python-bloggers? click here.

Julia has native support for calling C and FORTRAN functions. There are also add on packages which provide interfaces to C++, R and Python. We’ll have a brief look at the support for C and R here. Further details on these and the other supported languages can be found on github.

Why would you want to call other languages from within Julia? Here are a couple of reasons:

  • to access functionality which is not implemented in Julia;
  • to exploit some efficiency associated with another language.

The second reason should apply relatively seldom because, as we saw some time ago, Julia provides performance which rivals native C or FORTRAN code.

C

C functions are called via

ccall()
ccall(), where the name of the C function and the library it lives in are passed as a tuple in the first argument, followed by the return type of the function and the types of the function arguments, and finally the arguments themselves. It’s a bit klunky, but it works!

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> ccall((:sqrt, "libm"), Float64, (Float64,), 64.0)
8.0
julia> ccall((:sqrt, "libm"), Float64, (Float64,), 64.0) 8.0
julia> ccall((:sqrt, "libm"), Float64, (Float64,), 64.0)
8.0

It makes sense to wrap a call like that in a native Julia function.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> csqrt(x) = ccall((:sqrt, "libm"), Float64, (Float64,), x);
julia> csqrt(64.0)
8.0
julia> csqrt(x) = ccall((:sqrt, "libm"), Float64, (Float64,), x); julia> csqrt(64.0) 8.0
julia> csqrt(x) = ccall((:sqrt, "libm"), Float64, (Float64,), x);
julia> csqrt(64.0)
8.0

This function will not be vectorised by default (just try call

csqrt()
csqrt() on a vector!), but it’s a simple matter to produce a vectorised version using the
@vectorize_1arg
@vectorize_1arg macro.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> @vectorize_1arg Real csqrt;
julia> methods(csqrt)
# 4 methods for generic function "csqrt":
csqrt{T<:Real}(::AbstractArray{T<:Real,1}) at operators.jl:359
csqrt{T<:Real}(::AbstractArray{T<:Real,2}) at operators.jl:360
csqrt{T<:Real}(::AbstractArray{T<:Real,N}) at operators.jl:362
csqrt(x) at none:6
julia> @vectorize_1arg Real csqrt; julia> methods(csqrt) # 4 methods for generic function "csqrt": csqrt{T<:Real}(::AbstractArray{T<:Real,1}) at operators.jl:359 csqrt{T<:Real}(::AbstractArray{T<:Real,2}) at operators.jl:360 csqrt{T<:Real}(::AbstractArray{T<:Real,N}) at operators.jl:362 csqrt(x) at none:6
julia> @vectorize_1arg Real csqrt;
julia> methods(csqrt)
# 4 methods for generic function "csqrt":
csqrt{T<:Real}(::AbstractArray{T<:Real,1}) at operators.jl:359
csqrt{T<:Real}(::AbstractArray{T<:Real,2}) at operators.jl:360
csqrt{T<:Real}(::AbstractArray{T<:Real,N}) at operators.jl:362
csqrt(x) at none:6

Note that a few extra specialised methods have been introduced and now calling

csqrt()
csqrt() on a vector works perfectly.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> csqrt([1, 4, 9, 16])
4-element Array{Float64,1}:
1.0
2.0
3.0
4.0
julia> csqrt([1, 4, 9, 16]) 4-element Array{Float64,1}: 1.0 2.0 3.0 4.0
julia> csqrt([1, 4, 9, 16])
4-element Array{Float64,1}:
 1.0
 2.0
 3.0
 4.0

R

I’ll freely admit that I don’t dabble in C too often these days. R, on the other hand, is a daily workhorse. So being able to import R functionality into Julia is very appealing. The first thing that we need to do is load up a few packages, the most important of which is

RCall. There’s great documentation for the package here.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> using RCall
julia> using DataArrays, DataFrames
julia> using RCall julia> using DataArrays, DataFrames
julia> using RCall
julia> using DataArrays, DataFrames

We immediately have access to R’s builtin data sets and we can display them using

rprint()
rprint().

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> rprint(:HairEyeColor)
, , Sex = Male
Eye
Hair Brown Blue Hazel Green
Black 32 11 10 3
Brown 53 50 25 15
Red 10 10 7 7
Blond 3 30 5 8
, , Sex = Female
Eye
Hair Brown Blue Hazel Green
Black 36 9 5 2
Brown 66 34 29 14
Red 16 7 7 7
Blond 4 64 5 8
julia> rprint(:HairEyeColor) , , Sex = Male Eye Hair Brown Blue Hazel Green Black 32 11 10 3 Brown 53 50 25 15 Red 10 10 7 7 Blond 3 30 5 8 , , Sex = Female Eye Hair Brown Blue Hazel Green Black 36 9 5 2 Brown 66 34 29 14 Red 16 7 7 7 Blond 4 64 5 8
julia> rprint(:HairEyeColor)
, , Sex = Male

       Eye
Hair    Brown Blue Hazel Green
  Black    32   11    10     3
  Brown    53   50    25    15
  Red      10   10     7     7
  Blond     3   30     5     8

, , Sex = Female

       Eye
Hair    Brown Blue Hazel Green
  Black    36    9     5     2
  Brown    66   34    29    14
  Red      16    7     7     7
  Blond     4   64     5     8

We can also copy those data across from R to Julia.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> airquality = DataFrame(:airquality);
julia> head(airquality)
6x6 DataFrame
| Row | Ozone | Solar.R | Wind | Temp | Month | Day |
|-----|-------|---------|------|------|-------|-----|
| 1 | 41 | 190 | 7.4 | 67 | 5 | 1 |
| 2 | 36 | 118 | 8.0 | 72 | 5 | 2 |
| 3 | 12 | 149 | 12.6 | 74 | 5 | 3 |
| 4 | 18 | 313 | 11.5 | 62 | 5 | 4 |
| 5 | NA | NA | 14.3 | 56 | 5 | 5 |
| 6 | 28 | NA | 14.9 | 66 | 5 | 6 |
julia> airquality = DataFrame(:airquality); julia> head(airquality) 6x6 DataFrame | Row | Ozone | Solar.R | Wind | Temp | Month | Day | |-----|-------|---------|------|------|-------|-----| | 1 | 41 | 190 | 7.4 | 67 | 5 | 1 | | 2 | 36 | 118 | 8.0 | 72 | 5 | 2 | | 3 | 12 | 149 | 12.6 | 74 | 5 | 3 | | 4 | 18 | 313 | 11.5 | 62 | 5 | 4 | | 5 | NA | NA | 14.3 | 56 | 5 | 5 | | 6 | 28 | NA | 14.9 | 66 | 5 | 6 |
julia> airquality = DataFrame(:airquality);
julia> head(airquality)
6x6 DataFrame
| Row | Ozone | Solar.R | Wind | Temp | Month | Day |
|-----|-------|---------|------|------|-------|-----|
| 1   | 41    | 190     | 7.4  | 67   | 5     | 1   |
| 2   | 36    | 118     | 8.0  | 72   | 5     | 2   |
| 3   | 12    | 149     | 12.6 | 74   | 5     | 3   |
| 4   | 18    | 313     | 11.5 | 62   | 5     | 4   |
| 5   | NA    | NA      | 14.3 | 56   | 5     | 5   |
| 6   | 28    | NA      | 14.9 | 66   | 5     | 6   |

rcopy()
rcopy() provides a high-level interface to function calls in R.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> rcopy("runif(3)")
3-element Array{Float64,1}:
0.752226
0.683104
0.290194
julia> rcopy("runif(3)") 3-element Array{Float64,1}: 0.752226 0.683104 0.290194
julia> rcopy("runif(3)")
3-element Array{Float64,1}:
 0.752226
 0.683104
 0.290194

However, for some complex objects there is no simple way to translate between R and Julia, and in these cases

rcopy()
rcopy() fails. We can see in the case below that the object of class
lm
lm returned by
lm()
lm() does not diffuse intact across the R-Julia membrane.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> "fit <- lm(bwt ~ ., data = MASS::birthwt)" |> rcopy
ERROR: `rcopy` has no method matching rcopy(::LangSxp)
in rcopy at no file
in map_to! at abstractarray.jl:1311
in map_to! at abstractarray.jl:1320
in map at abstractarray.jl:1331
in rcopy at /home/colliera/.julia/v0.3/RCall/src/sexp.jl:131
in rcopy at /home/colliera/.julia/v0.3/RCall/src/iface.jl:35
in |> at operators.jl:178
julia> "fit <- lm(bwt ~ ., data = MASS::birthwt)" |> rcopy ERROR: `rcopy` has no method matching rcopy(::LangSxp) in rcopy at no file in map_to! at abstractarray.jl:1311 in map_to! at abstractarray.jl:1320 in map at abstractarray.jl:1331 in rcopy at /home/colliera/.julia/v0.3/RCall/src/sexp.jl:131 in rcopy at /home/colliera/.julia/v0.3/RCall/src/iface.jl:35 in |> at operators.jl:178
julia> "fit <- lm(bwt ~ ., data = MASS::birthwt)" |> rcopy
ERROR: `rcopy` has no method matching rcopy(::LangSxp)
 in rcopy at no file
 in map_to! at abstractarray.jl:1311
 in map_to! at abstractarray.jl:1320
 in map at abstractarray.jl:1331
 in rcopy at /home/colliera/.julia/v0.3/RCall/src/sexp.jl:131
 in rcopy at /home/colliera/.julia/v0.3/RCall/src/iface.jl:35
 in |> at operators.jl:178

But the call to

lm()
lm() was successful and we can still look at the results.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> rprint(:fit)
Call:
lm(formula = bwt ~ ., data = MASS::birthwt)
Coefficients:
(Intercept) low age lwt race
3612.51 -1131.22 -6.25 1.05 -100.90
smoke ptl ht ui ftv
-174.12 81.34 -181.95 -336.78 -7.58
julia> rprint(:fit) Call: lm(formula = bwt ~ ., data = MASS::birthwt) Coefficients: (Intercept) low age lwt race 3612.51 -1131.22 -6.25 1.05 -100.90 smoke ptl ht ui ftv -174.12 81.34 -181.95 -336.78 -7.58
julia> rprint(:fit)

Call:
lm(formula = bwt ~ ., data = MASS::birthwt)

Coefficients:
(Intercept)          low          age        lwt         race
    3612.51     -1131.22        -6.25       1.05      -100.90
      smoke          ptl           ht         ui          ftv
    -174.12        81.34      -181.95    -336.78        -7.58

You can use R to generate plots with either the base functionality or that provided by libraries like ggplot2 or lattice.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
julia> reval("plot(1:10)"); # Will pop up a graphics window...
julia> reval("library(ggplot2)");
julia> rprint("ggplot(MASS::birthwt, aes(x = age, y = bwt)) + geom_point() + theme_classic()")
julia> reval("dev.off()") # ... and close the window.
julia> reval("plot(1:10)"); # Will pop up a graphics window... julia> reval("library(ggplot2)"); julia> rprint("ggplot(MASS::birthwt, aes(x = age, y = bwt)) + geom_point() + theme_classic()") julia> reval("dev.off()") # ... and close the window.
julia> reval("plot(1:10)");             # Will pop up a graphics window...
julia> reval("library(ggplot2)");
julia> rprint("ggplot(MASS::birthwt, aes(x = age, y = bwt)) + geom_point() + theme_classic()")
julia> reval("dev.off()")               # ... and close the window.

Watch the videos below for some other perspectives on multi-language programming with Julia. Also check out the complete code for today (including examples with C++, FORTRAN and Python) on github.



To leave a comment for the author, please follow the link and comment on their blog: Python | datawookie .

Want to share your content on python-bloggers? click here.