diff --git a/.github/workflows/aot.yml b/.github/workflows/aot.yml index 8fc32fa2..1f53dee4 100644 --- a/.github/workflows/aot.yml +++ b/.github/workflows/aot.yml @@ -36,7 +36,7 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - run: python --version - - run: python -m pip install --user numpy + - run: python -m pip install --user numpy scipy - name: Install libpython2.7 for `find_libpython` test run: sudo apt-get install python2.7-dev if: ${{ matrix.python-version != '2.7' && matrix.os == 'ubuntu-latest' }} diff --git a/.github/workflows/system.yml b/.github/workflows/system.yml index 360ee9d0..f76089f0 100644 --- a/.github/workflows/system.yml +++ b/.github/workflows/system.yml @@ -64,7 +64,7 @@ jobs: with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - - run: python -m pip install --user numpy + - run: python -m pip install --user numpy scipy - run: python -m pip install virtualenv # virtualenv test with Python 2.7 is failing for some reason. # Skipping it for now. diff --git a/Project.toml b/Project.toml index df12e278..a970b8fc 100644 --- a/Project.toml +++ b/Project.toml @@ -10,6 +10,7 @@ Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" VersionParsing = "81def892-9a0e-5fdd-b105-ffc91e053289" [compat] diff --git a/src/conversions.jl b/src/conversions.jl index 0226f036..c183b98c 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -426,6 +426,9 @@ PyObject(a::BitArray) = PyObject(Array(a)) # NumPy conversions (multidimensional arrays) include("numpy.jl") +# SciPy conversions +include("scipy.jl") + ######################################################################### # PyDict: no-copy wrapping of a Julia object around a Python dictionary diff --git a/src/scipy.jl b/src/scipy.jl new file mode 100644 index 00000000..df581fc2 --- /dev/null +++ b/src/scipy.jl @@ -0,0 +1,19 @@ +using SparseArrays + +const scipysparse_ = PyNULL() + +function scipysparse() + if ispynull(scipysparse_) + copy!(scipysparse_, pyimport("scipy.sparse")) + end + return scipysparse_ +end + +function PyObject(S::SparseMatrixCSC) + scipysparse().csc_matrix((S.nzval, S.rowval .- 1, S.colptr .- 1), shape = size(S)) +end + +function convert(::Type{SparseMatrixCSC}, o::PyObject) + I, J, V = scipysparse().find(o) + return sparse(I .+ 1, J .+ 1, V, o.shape...) +end diff --git a/test/runtests.jl b/test/runtests.jl index 2e826080..0c1a1e08 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,7 @@ using PyCall using PyCall: hasproperty using Test, Dates, Serialization +using SparseArrays: SparseMatrixCSC, sparse, sprand filter(f, itr) = collect(Iterators.filter(f, itr)) filter(f, d::AbstractDict) = Base.filter(f, d) @@ -13,6 +14,11 @@ PYTHONEXECUTABLE=get(ENV,"PYTHONEXECUTABLE","") @testset "CI setup" begin if lowercase(get(ENV, "CI", "false")) == "true" @test !ispynull(pyimport_e("numpy")) + try + pyimport_conda("scipy.sparse", "scipy") + catch + end + @test !ispynull(pyimport_e("scipy.sparse")) end end @@ -615,6 +621,11 @@ const PyInt = pyversion < v"3" ? Int : Clonglong @test float(PyObject(1+2im)) === 1.0 + 2.0im @test float(PyObject([1,2,3]))[2] === 2.0 @test_throws ArgumentError float(pybuiltin("type")) + + # sparse array conversion + S = sprand(10, 10, 0.2) + PyS = PyObject(S) + @test convert(SparseMatrixCSC, PyS) == S end ######################################################################