Calling from Python

Calling Julia and CMBLensing.jl directly from Python is very transparent. This is made possible by the PyJulia package. You can install it into your Python environment with, e.g.:

$ pip install --user julia

Important: If your Python executable is statically-linked (this is quite often the case, e.g. its the default on Ubuntu and Conda) you need one extra step. Basically, instead of running python or ipython at the command line to launch your interpreter, run python-jl or python-jl -m IPython, respectively. If you use Jupyter, you'll need to edit your kernel.json file (you can find its location via jupyter kernelspec list) and change it to use python-jl.

The wrapper script python-jl does some special initializion but otherwise drops you into the Python/IPython interpreter that you are familiar with.

The PyJulia docs also give instructions on how to install a dynamically-linked Python executable which is the most ideal solution, and only slightly more work than above.

Basics of calling Julia

Once PyJulia is installed, you can access any Julia package Foo from the Python package julia.Foo, and everything pretty much works the same.

import julia.Base
julia.Base.cos(1) # <--- this is Julia's cosine function

You can also run arbitrary Julia code with the %julia cell magic (this is helpful if you want to use Julia language features or syntax which don't exist in Python):

%load_ext julia.magic
Initializing Julia interpreter. This may take some time...

For example, 1:10 is not valid Python syntax, but we can do:

%julia 1:10
range(1, 11)

The cell magic lets you interpolate values from Python into the Julia expression, which can be a convenient way to pass values back and forth:

x = %julia 1 + 2
%julia 2 * $x

Calling CMBLensing.jl

Via magic

The most robust way to call CMBLensing.jl from Python is just to wrap everything in Julia magic and interpolate things back and forth as-needed. Lets try and follow the Lensing a flat-sky map example from Python. First, we load the package:

using CMBLensing

Next, we simulate some data:

@unpack f,ϕ = load_sim(
    θpix  = 2,
    Nside = 256,
    T     = Float32,
    pol   = :I

Similarly, the rest of the commands from that example will work in Python if just called via Julia magic.

At any point, you can do whatever you'd like with any of the results stored in Julia variables, e.g. transferring the simulated maps back as Python arrays,

arr = %julia f.arr
array([[ 0.0000000e+00+0.00000000e+00j,  1.5971417e+05+1.26342852e+05j,
        -2.4775745e+05+2.54937125e+05j, ...,
        -5.7795631e+05+4.13471406e+05j, -2.4775747e+05-2.54937109e+05j,
       [-4.3337206e+05-9.77306312e+05j, -5.8721231e+05+4.18856250e+05j,
        -1.9172198e+05-3.64847562e+05j, ...,
        -1.9914020e+05-2.97223375e+05j, -6.7849525e+05+1.94087594e+05j,
       [ 1.9771350e+05-4.60698938e+05j, -4.0292041e+05+5.58114312e+05j,
        -3.3060055e+04+1.12627148e+05j, ...,
         3.9587100e+05+5.73407812e+04j, -6.8549242e+04+2.07401969e+05j,
       [ 1.8041639e+01+1.38472395e+01j, -3.7130058e+01+2.46780453e+01j,
         1.7245299e+00-4.23311729e+01j, ...,
         9.1855713e+01-9.41239834e+00j, -2.5981779e+01-7.36167288e+00j,
       [-2.4823092e+01+3.38999481e+01j,  5.1647453e+01-1.79746399e+01j,
        -2.2295048e+01-2.56557312e+01j, ...,
         4.9655060e+01-3.42778320e+01j,  1.0030525e+01+3.87599640e+01j,
       [ 5.7597290e+01+0.00000000e+00j, -1.3451367e+01+2.42531052e+01j,
         6.4969788e+01-1.03175840e+01j, ...,
        -7.8002419e+01-2.01269379e+01j,  6.4969795e+01+1.03175840e+01j,
        -1.3451367e+01-2.42531090e+01j]], dtype=complex64)

You can also pass variables back to Julia, e.g.

%julia f = FlatMap($arr);


You can also call Julia directly without magic, which sometimes offers more flexibility, although has some limitations.

To do so, first import CMBLensing. into Python. In Julia, using CMBLensing imports all of the CMBLensing symbols into the current namespace. In Python this is:

from julia.CMBLensing import *

If we want to call load_sim as before, we must take into account a few things:

  • You won't be able to use the @unpack macro since macros on arbitrary code don't exist in Python.
  • Float32 isn't imported into Python by default, so you'll need to specify the module.
  • The :P is invalid syntax in Python, you should use a string "P" instead.

Given all of that, the call will look like:

sim = load_sim(
    θpix  = 2, 
    Nside = 256, 
    T     = julia.Base.Float32, 
    pol   = "P"

If we wish to grab the lensing potential from the result, there's an additional consideration. Python does not differentiate between the characters ϕ (\phi) and φ (\varphi), and maps both of them back to φ (\varphi) in Julia, which unfortunately is the wrong one for CMBLensing (which instead makes extensive use of the variable name ϕ (\phi)). Thus, calling sim.ϕ from Python does not work. Instead, we have to do that part in Julia:

ϕ = %julia $sim.ϕ


To plot, we need to use the plot function from Julia's PyPlot, since this will know about plotting CMBLensing objects.

from julia.PyPlot import plot
%matplotlib inline
/tmp/ipykernel_368/ MatplotlibDeprecationWarning: The get_cmap function was deprecated in Matplotlib 3.7 and will be removed two minor releases later. Use ``matplotlib.colormaps[name]`` or ``matplotlib.colormaps.get_cmap(obj)`` instead.


For non-CMBLensing objects, this plot function will just pass-through to matplotlib, so will not affect affect your session otherwise.