{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Interoperability with Python: the `PyCall` package" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coming from the Python ecosystem, the range of packages available in Julia can seem somewhat limited.\n", "This is offset, however, by the ease of calling out to packages written in other languages from within Julia." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In particular, Python interoperability is very easy, thanks to the [`PyCall` package](https://github.com/stevengj/PyCall.jl). This is loaded with" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "using PyCall" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`PyCall` has a high-level interface that is designed so that the \"transport\" between Julia and Python is transparent from the user's point of view. For example, to import the Python `math` module, we do" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "@pyimport math" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "quote # /Users/dsanders/.julia/v0.3/PyCall/src/PyCall.jl, line 362:\n", " if PyCall.!(PyCall.isdefined(:math)) # line 363:\n", " const math = PyCall.pywrap(PyCall.pyimport(\"math\"))\n", " else # line 364:\n", " if PyCall.!(PyCall.isa(math,PyCall.Module)) # line 365:\n", " PyCall.error(\"@pyimport: \",:math,\" already defined\")\n", " end\n", " end # line 367:\n", " PyCall.nothing\n", "end" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "macroexpand(:(@pyimport math))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can now mix and match Python calls, labelled by the `math.` qualifier, and Julia calls:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-1.1102230246251565e-16" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "math.sin(0.3*math.pi) - sin(0.3*pi) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Array objects are automatically converted:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "3x4 Array{Float64,2}:\n", " 0.130444 0.9004 0.364677 0.321106\n", " 0.107789 0.0264795 0.369139 0.215905\n", " 0.723455 0.31165 0.110962 0.123888" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@pyimport numpy.random as nprandom\n", "nprandom.rand(3,4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's define a Julia function:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(anonymous function)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "objective = x -> cos(x) - x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the Julia syntax for an anonymous function (like `lambda` in Python)." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-3.989992496600445" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "objective(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can pass this Julia function to a Python module:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "ename": "LoadError", "evalue": "so not defined\nwhile loading In[6], in expression starting on line 1", "output_type": "error", "traceback": [ "so not defined\nwhile loading In[6], in expression starting on line 1" ] } ], "source": [ "?so.newton" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.7390851332151607" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@pyimport scipy.optimize as so\n", "so.newton(objective, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The main difference from Python is how to access member elements of Python structures." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia has ODE solvers in the `ODE.jl` and `Sundials.jl` packages. But we can also call Python solvers:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "@pyimport scipy.integrate as integrate" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "f (generic function with 1 method)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x,t) = -x " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "t = [0:0.1:10];" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [], "source": [ "soln = integrate.odeint(f, 1, t);" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "using PyPlot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the `PyPlot` module provides a higher-level wrapper than `PyCall` around `matplotlib`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "Figure(PyObject )" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1-element Array{Any,1}:\n", " PyObject " ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plot(t, soln)\n", "plot(t, exp(-t))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subtle difference from Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Accessing fields (properties) and methods of Python objects uses the `obj.a` and `obj.b()` syntax, where `obj` is a Python object.\n", "However, currently the `obj.b` syntax in Julia is restricted to accessing fields of Julia composite types.\n", "\n", "For this reason, to access fields and methods of Python objects via PyCall, it is necessary to use the syntax\n", "\n", "`obj[:a]` for fields, and\n", "\n", "`obj[:a]()` for methods\n", "\n", "Here, we are using the Julia syntax `:a` to mean the Julia symbol `a`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lower level" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The high-level `PyCall` interface is built on top of a lower-level interface which deals with the \"transport\" of objects between Python and Julia, based on a `PyObject` Julia type that wraps `PyObject*` in C, and represents a reference to a Python object.\n", "\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "PyObject 3" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "PyObject(3)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "5x5 Array{Float64,2}:\n", " 0.887502 0.0246364 0.937582 0.684068 0.720231 \n", " 0.384961 0.160604 0.814357 0.827761 0.221047 \n", " 0.125212 0.333403 0.877111 0.949292 0.152891 \n", " 0.782374 0.47016 0.687562 0.846015 0.155578 \n", " 0.827994 0.375204 0.991652 0.79604 0.0131533" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = rand(5, 5)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "PyObject array([[ 0.88750169, 0.02463637, 0.93758233, 0.68406787, 0.72023119],\n", " [ 0.38496148, 0.16060356, 0.81435705, 0.82776091, 0.22104671],\n", " [ 0.12521248, 0.33340264, 0.87711099, 0.94929151, 0.15289064],\n", " [ 0.78237384, 0.47015973, 0.68756236, 0.84601514, 0.15557803],\n", " [ 0.82799391, 0.37520387, 0.99165247, 0.79604021, 0.01315334]])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xx = PyObject(x)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "PyObject (constructor with 29 methods)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(xx)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1-element Array{Symbol,1}:\n", " :o" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names(xx)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Ptr{PyObject_struct} @0x00007fdeaf7ccb40" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xx.o" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(5,5)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# xx.shape in Python becomes:\n", "xx[:shape]" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(DataType,DataType)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(ans) # the result has already been translated back into a Julia object" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia arrays are passed into Python without a copy. By default the resulting Python array is copied when a result is requested in Julia; this can be avoided at a lower level using `pycall` and `PyArray`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise**: Use your favourite Python package from Julia!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Interoperability with C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia has a simple way to call C and Fortran functions in shared libraries, via the `ccall` function." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Base.ccall((symbol, library) or fptr, RetType, (ArgType1, ...), ArgVar1, ...)\n", "\n", " Call function in C-exported shared library, specified by\n", " \"(function name, library)\" tuple, where each component is a\n", " String or :Symbol. Alternatively, ccall may be used to call a\n", " function pointer returned by dlsym, but note that this usage is\n", " generally discouraged to facilitate future static compilation. Note\n", " that the argument type tuple must be a literal tuple, and not a\n", " tuple-valued variable or expression.\n" ] } ], "source": [ "help(\"ccall\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that we must specify:\n", "\n", "- the name of the function, as a Julia symbol or as a string\n", "- and the name of the shared library where it lives; these are given as an ordered pair (tuple)\n", "\n", "\n", "- the return type of the function\n", "\n", "- the argument types that the function accepts, as a tuple\n", "\n", "- and the arguments themselves" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A simple example is to call the clock function:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "17337016" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = ccall( (:clock, \"libc\"), Int32, ())" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "6646395" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Int32" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(t)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Base.ccall((symbol, library) or fptr, RetType, (ArgType1, ...), ArgVar1, ...)\n", "\n", " Call function in C-exported shared library, specified by\n", " \"(function name, library)\" tuple, where each component is a\n", " String or :Symbol. Alternatively, ccall may be used to call a\n", " function pointer returned by dlsym, but note that this usage is\n", " generally discouraged to facilitate future static compilation. Note\n", " that the argument type tuple must be a literal tuple, and not a\n", " tuple-valued variable or expression.\n" ] } ], "source": [ "help(\"ccall\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "Clong" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Ptr{Uint8} @0x00007faac9d15f05" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "path = ccall( (:getenv, \"libc\"), Ptr{Uint8}, (Ptr{Uint8},), \"PATH\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, `Ptr` denotes a pointer to the given type." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Ptr{Uint8} @0x00007fff50e46ed5" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "path" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "\"/Users/david/development/julia/usr/bin:/Users/david/anaconda/bin:/usr/local/bin:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/texbin:.:/Users/david/bin:/Users/david/Dropbox/bin\"" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bytestring(path)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Base.bytestring(::Ptr{Uint8}[, length])\n", "\n", " Create a string from the address of a C (0-terminated) string\n", " encoded in ASCII or UTF-8. A copy is made; the ptr can be safely\n", " freed. If \"length\" is specified, the string does not have to be\n", " 0-terminated.\n", "\n", "Base.bytestring(s)\n", "\n", " Convert a string to a contiguous byte array representation\n", " appropriate for passing it to C functions. The string will be\n", " encoded as either ASCII or UTF-8.\n" ] } ], "source": [ "?bytestring" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Ptr{T}" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Ptr" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Ptr{T}" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "methods(Ptr)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/html": [ "58-element Array{Method,1}:" ], "text/plain": [ "58-element Array{Method,1}:\n", " show{T}(io::IO,p::Ptr{T}) at show.jl:116 \n", " unsafe_store!{T}(p::Ptr{T},x,i::Integer) at pointer.jl:48 \n", " unsafe_store!{T}(p::Ptr{T},x) at pointer.jl:49 \n", " msync(p::Ptr{T},len::Integer,flags::Integer) at mmap.jl:70 \n", " msync(p::Ptr{T},len::Integer) at mmap.jl:72 \n", " integer(x::Ptr{T}) at pointer.jl:55 \n", " read!(from::IOBuffer,p::Ptr{T},nb::Int64) at iobuffer.jl:70 \n", " read!(from::IOBuffer,p::Ptr{T},nb::Integer) at iobuffer.jl:68 \n", " show{T}(io::IO,p::Ptr{T}) at show.jl:116 \n", " -(x::Ptr{T},y::Ptr{T}) at pointer.jl:64 \n", " -(x::Ptr{T},y::Integer) at pointer.jl:67 \n", " write(to::IOBuffer,p::Ptr{T},nb::Int64) at iobuffer.jl:237 \n", " write(to::IOBuffer,p::Ptr{T},nb::Integer) at iobuffer.jl:235 \n", " ⋮ \n", " gzbuffer(gz_file::Ptr{T},gz_buf_size::Integer) at /Users/david/.julia/GZip/src/GZip.jl:197\n", " show{T}(io::IO,p::Ptr{T}) at show.jl:116 \n", " write(to::IOBuffer,p::Ptr{T},nb::Int64) at iobuffer.jl:237 \n", " write(to::IOBuffer,p::Ptr{T},nb::Integer) at iobuffer.jl:235 \n", " write(to::IOBuffer,p::Ptr{T}) at iobuffer.jl:278 \n", " write(s::IOStream,p::Ptr{T},nb::Integer) at iostream.jl:159 \n", " write(s::AsyncStream,p::Ptr{T},nb::Integer) at stream.jl:790 \n", " write(s::GZipStream,p::Ptr{T},nb::Integer) at /Users/david/.julia/GZip/src/GZip.jl:448 \n", " write(s::IO,p::Ptr{T},n::Integer) at io.jl:91 \n", " gzwrite(s::GZipStream,p::Ptr{T},len::Integer) at /Users/david/.julia/GZip/src/GZip.jl:184 \n", " read(from::IOBuffer,p::Ptr{T},nb::Integer) at deprecated.jl:26 \n", " PyObject(p::Ptr{T}) at /Users/david/.julia/PyCall/src/conversions.jl:131 " ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "methodswith(Ptr)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "type F\n", " f::Function\n", " x::Float64\n", "end" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "hello (generic function with 1 method)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hello(x) = x^2" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "F(hello,3.5)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "myfunc = F(hello, 3.5)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "ename": "LoadError", "evalue": "myfunc not defined\nwhile loading In[4], in expression starting on line 1", "output_type": "error", "traceback": [ "myfunc not defined\nwhile loading In[4], in expression starting on line 1" ] } ], "source": [ "myfunc" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "hello (generic function with 1 method)" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "myfunc.f" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "12.25" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "myfunc.f(myfunc.x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Julia 0.3.8-pre", "language": "julia", "name": "julia 0.3" }, "language_info": { "name": "julia", "version": "0.3.10" } }, "nbformat": 4, "nbformat_minor": 0 }