Convert Numpy Arrays to Julia Arrays by Default PythonCall/JuliaCall

I am currently writing an interface between a Python program and a Julia Package; I would like to call the functions in the Julia package from Python. The functions in the Julia package all have type declarations, and for better or for worse, I am not able to change this . I could do is write function wrappers for all functions in the package, but I would like to avoid that if possible.

When the Julia functions have basic type declarations, like Int64, Float, or Bool, the conversion seems to happen, but not for numpy arrays.

Is there a way to have PythonCall/JuliaCall automatically convert numpy arrays into Julia arrays when passing them as arguments to a function? For example, I would like to define the Julia function:

function matrix_func(x::Array{Float64, 2})
    Do some stuff
end

And then, on the Python side:

import numpy as np
from juliacall import Main as jl

my_array = np.array([[1,2,3], [4,5,6]])
jl.include("File containing matrix_func definition")
jl.matrix_func(my_array)

For my use case, I am completely fine with performing copying operations. I am aware that copying operations will allocate memory unnecessarily, but in my case I’m only calling a few functions from Julia to set up a Julia object which contains all the data needed for our task, after which everything will be done in Julia.

According to the documentation here: Python to Julia · PythonCall & JuliaCall, the user can set custom conversion rules, but I do not understand whether I can make use of this to perform automatic type conversion when calling Julia functions from Python, or if this is only for implementing/overriding the behavior for when the user calls pyconvert directly.

You can be less restrictive in the type; instead do:

function matrix_func(x::AbstractArray)
...

The reason is that np.ndarray is converted to PyArray, which is a subtype of a Julia AbstractArray{T,N}. cf. Redirecting to https://juliapy.github.io/PythonCall.jl/dev/pythoncall-reference/

1 Like

@jd-foster’s answer is excellent, but it sounds like you do not have control over the Julia functions you are calling. In which case, you’ll need to do some explicit conversion.

The simplest way is to instead call

jl.matrix_func(juliacall.convert(jl.Array[jl.Float64, 2], my_array))

which explicitly converts my_array to an Array{Float64, 2} before passing it to the function.

Or you could create a new Julia function

jl.seval("matrix_func_wrapped(x) = matrix_func(convert(Matrix, x))")
jl.matrix_func_wrapped(my_array)

or if you’re happy with piracy you could add a method to matrix_func:

jl.seval("matrix_func(x) = matrix_func(convert(Matrix{Float64}, x))")
jl.matrix_func(my_array)
3 Likes

I agree that this would be a good solution, but unfortunately in practice matrix_func is defined in a package which I am not allowed to alter (if I could, I would). But like you said, np.ndarray is converted to PyArray. Is there a way to have it instead be converted to Array{T,N}? Or would this require a pretty substantial rewrite of PythonCall/JuliaCall?

Of course, I should have read your question more closely.
The answer from cjdoris is fairly definitive, given that he is the developer of PythonCall and JuliaCall.

1 Like

Thanks! I think wrapping/pirating the original function will be the most convenient method for me in the long term. I appreciate the help!