PyCall numpy vdot Vector{Float32} returns Float64

I have been comparing numpy using PyCall with Julia recently and observed a strange behavior. When I call numpy from Julia and do a dot product of two Vector{Float32} vectors, the result is Float64.

However, when I run a similar code in python directly, the result is float32

Julia code:

using PyCall

np = pyimport("numpy")

x = Float32[1, 2, 3, 4]
y = Float32[5, 6, 7, 8]

println(typeof(np.vdot(x, y))) # Float64

Python code:

import numpy as np

x = np.array([1, 2, 3, 4], dtype=np.float32)
y = np.array([5, 6, 7, 8], dtype=np.float32)

print(np.vdot(x, y).dtype) # float32

Information about my environment

Julia:

julia> versioninfo()
Julia Version 1.11.3
Commit d63adeda50d (2025-01-21 19:42 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 Ă— Intel(R) Xeon(R) Gold 6338N CPU @ 2.20GHz
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, icelake-server)
Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores)
Environment:
  JULIA_GPG = 3673DF529D9049477F76B37566E3C7DC03D6E495
  JULIA_PATH = /usr/local/julia
  JULIA_VERSION = 1.11.3
  JULIA_EDITOR = code
  JULIA_NUM_THREADS =

(@v1.11) pkg> st
Status `~/.julia/environments/v1.11/Project.toml`
  [6e4b80f9] BenchmarkTools v1.6.0
  [438e738f] PyCall v1.96.4

Python:

Python 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> np.version.full_version
'1.24.2'

That’s just the automatic conversion, which doesn’t know about numpy scalars. It’s still actually doing the single precision calculation, as you can see if you tell PyCall to not automatically convert the result to a Julia type:

julia> pycall(np.vdot, PyObject, x, y)
PyObject 70.0

julia> pycall(np.vdot, PyObject, x, y).dtype
PyObject dtype('float32')

If you care about the Julia type being Float32, you can do pycall(np.vdot, Float32, x, y).

Alternatively, you can use PythonCall.jl, which never automatically converts the results — you have to convert things to native Julia types manually.

4 Likes

Thanks for the explanation but is there a reason why the automatic conversion in PyCall.jl does not automatically convert Numpy’s float32 to Julia’s Float32? Sorry, but I am not super familiar with the inner workings of the package.

Because the conversion code doesn’t know about Numpy. It just knows that the value is a subtype of float (because PyFloat_Type from the Python C API returns true) and it calls PyFloat_AsDouble from the Python C API.

We could add a numpy-specific scalar conversion pass, of course. But there’s no loss of data if a float32 is converted to a Float64.

1 Like