From 2453960a9345ea810c30f7653e71a4da15fe4c22 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 14 Jan 2021 10:36:11 +0100 Subject: [PATCH 1/5] Document gotchas when interoperation with python --- docs/make.jl | 1 + docs/src/h5py.md | 105 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 docs/src/h5py.md diff --git a/docs/make.jl b/docs/make.jl index 8a3cc25ad..d64d82ef2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -24,6 +24,7 @@ makedocs(; pages=[ "Home" => "index.md", "Low-level library bindings" => "api_bindings.md", + "Python interoperability" => "h5py.md" ], ) diff --git a/docs/src/h5py.md b/docs/src/h5py.md new file mode 100644 index 000000000..da0cec0a9 --- /dev/null +++ b/docs/src/h5py.md @@ -0,0 +1,105 @@ +# Python interoperability + +When loading python created hdf5 files from Julia the dimensions of arrays are reversed. +The reason is that in python C-memory layout is the default, while Julia uses Fortran layout. +Here is an example: +```python +import h5py +import numpy as np +path = "created_by_h5py.h5" +file = h5py.File(path, "w") +arr1d = np.array([1,2,3]) +arr2d = np.array([[1,2,3], [4,5,6]]) +arr3d = np.array([[[1,2,3], [4,5,6]]]) +assert arr1d.shape == (3,) +assert arr2d.shape == (2,3) +assert arr3d.shape == (1,2,3) +file["1d"] = arr1d +file["2d"] = arr2d +file["3d"] = arr3d +file.close() +``` +When we try to load it from julia, dimensions are reversed: +```julia +using HDF5 +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = read(file["1d"]) + arr2d = read(file["2d"]) + arr3d = read(file["3d"]) + @assert size(arr1d) == (3,) + @assert size(arr2d) == (3,2) + @assert size(arr3d) == (3,2,1) +end +``` +To fix this, we can simply reverse the dimensions again: + +```julia +using HDF5 +function reversedims(arr) + dims = ntuple(identity, Val(ndims(arr))) + return permutedims(arr, reverse(dims)) +end + +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = reversedims(read(file["1d"])) + arr2d = reversedims(read(file["2d"])) + arr3d = reversedims(read(file["3d"])) + @assert arr1d == [1,2,3] + @assert arr2d == [1 2 3; 4 5 6] + @assert arr3d == reshape(arr2d, (1,2,3)) +end +``` +Similarly `reversedims` can be used before saving arrays intended for use from python. +If copying of data is undesirable, other options are: +* using Fortran memory layout on the python side +* using C-memory layout on the Julia side (e.g. a lazy variant of `reversedims`) + +The whole example as a Julia executable script: +```julia +using PyCall +py""" +import h5py +import numpy as np +path = "created_by_h5py.h5" +file = h5py.File(path, "w") +arr1d = np.array([1,2,3]) +arr2d = np.array([[1,2,3], [4,5,6]]) +arr3d = np.array([[[1,2,3], [4,5,6]]]) +assert arr1d.shape == (3,) +assert arr2d.shape == (2,3) +assert arr3d.shape == (1,2,3) +file["1d"] = arr1d +file["2d"] = arr2d +file["3d"] = arr3d +file.close() +""" + +using HDF5 +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = read(file["1d"]) + arr2d = read(file["2d"]) + arr3d = read(file["3d"]) + @assert size(arr1d) == (3,) + @assert size(arr2d) == (3,2) + @assert size(arr3d) == (3,2,1) +end + +using HDF5 +function reversedims(arr) + dims = ntuple(identity, Val(ndims(arr))) + return permutedims(arr, reverse(dims)) +end + +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = reversedims(read(file["1d"])) + arr2d = reversedims(read(file["2d"])) + arr3d = reversedims(read(file["3d"])) + @assert arr1d == [1,2,3] + @assert arr2d == [1 2 3; 4 5 6] + @assert arr3d == reshape(arr2d, (1,2,3)) +end +``` From a37abcb7ad824da37a85e517dc5c204563886d85 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 14 Jan 2021 10:49:42 +0100 Subject: [PATCH 2/5] mention PermutedDimsArray --- docs/src/h5py.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/h5py.md b/docs/src/h5py.md index da0cec0a9..d6d6b4ea9 100644 --- a/docs/src/h5py.md +++ b/docs/src/h5py.md @@ -54,7 +54,7 @@ end Similarly `reversedims` can be used before saving arrays intended for use from python. If copying of data is undesirable, other options are: * using Fortran memory layout on the python side -* using C-memory layout on the Julia side (e.g. a lazy variant of `reversedims`) +* using C-memory layout on the Julia side (e.g. replace `permutedims` by `PermutedDimsArray` above) The whole example as a Julia executable script: ```julia From 0aa5b0def10d767dc18ad354ca68824c88505e66 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Fri, 15 Jan 2021 13:13:59 +0100 Subject: [PATCH 3/5] test h5py interpo documentation --- docs/Project.toml | 2 ++ docs/literate/h5py.jl | 84 +++++++++++++++++++++++++++++++++++++++++++ docs/make.jl | 7 ++++ docs/src/h5py.md | 81 +++++++++++++++++++++-------------------- 4 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 docs/literate/h5py.jl diff --git a/docs/Project.toml b/docs/Project.toml index 8fac944ad..8ae25d440 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,6 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" +PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" diff --git a/docs/literate/h5py.jl b/docs/literate/h5py.jl new file mode 100644 index 000000000..8e5875362 --- /dev/null +++ b/docs/literate/h5py.jl @@ -0,0 +1,84 @@ +# # Python interoperability + +# When loading python created hdf5 files from Julia the dimensions of arrays are reversed. +# The reason is that in python C-memory layout is the default, while Julia uses Fortran layout. +# Here is an example: +using PyCall #hide +pyimport_conda("h5py", "h5py") #hide +pyimport_conda("numpy", "numpy") #hide +py""" #hide +import h5py +import numpy as np +path = "created_by_h5py.h5" +file = h5py.File(path, "w") +arr1d = np.array([1,2,3]) +arr2d = np.array([[1,2,3], [4,5,6]]) +arr3d = np.array([[[1,2,3], [4,5,6]]]) +assert arr1d.shape == (3,) +assert arr2d.shape == (2,3) +assert arr3d.shape == (1,2,3) +file["1d"] = arr1d +file["2d"] = arr2d +file["3d"] = arr3d +file.close() +""" #hide + +# When we try to load it from julia, dimensions are reversed: +using HDF5 +using Test +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = read(file["1d"]) + arr2d = read(file["2d"]) + arr3d = read(file["3d"]) + @test size(arr1d) == (3,) + @test size(arr2d) == (3,2) + @test size(arr3d) == (3,2,1) +end +# To fix this, we can simply reverse the dimensions again: + +function reversedims(arr) + return permutedims(arr, reverse(1:ndims(arr))) +end + +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = reversedims(read(file["1d"])) + arr2d = reversedims(read(file["2d"])) + arr3d = reversedims(read(file["3d"])) + @test arr1d == [1,2,3] + @test arr2d == [1 2 3; 4 5 6] + @test arr3d == reshape(arr2d, (1,2,3)) +end +# Similarly `reversedims` can be used before saving arrays intended for use from python. +# If copying of data is undesirable, other options are: +# * using Fortran memory layout on the python side +# * using C-memory layout on the Julia side (e.g. replace `permutedims` by `PermutedDimsArray` above) +# + +using HDF5 +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = read(file["1d"]) + arr2d = read(file["2d"]) + arr3d = read(file["3d"]) + @test size(arr1d) == (3,) + @test size(arr2d) == (3,2) + @test size(arr3d) == (3,2,1) +end + +using HDF5 +function reversedims(arr) + dims = ntuple(identity, Val(ndims(arr))) + return permutedims(arr, reverse(dims)) +end + +path = "created_by_h5py.h5" +h5open(path, "r") do file + arr1d = reversedims(read(file["1d"])) + arr2d = reversedims(read(file["2d"])) + arr3d = reversedims(read(file["3d"])) + @test arr1d == [1,2,3] + @test arr2d == [1 2 3; 4 5 6] + @test arr3d == reshape(arr2d, (1,2,3)) +end diff --git a/docs/make.jl b/docs/make.jl index d64d82ef2..287f7880d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,6 +11,13 @@ not_low_level_api(::typeof(HDF5.h5p_get_class_name)) = false not_low_level_api(::typeof(HDF5.h5t_get_member_name)) = false not_low_level_api(::typeof(HDF5.h5t_get_tag)) = false +# literate +using Literate +src_path = joinpath(@__DIR__, "literate", "h5py.jl") +md_dir = joinpath(@__DIR__, "src") +rm(joinpath(md_dir, "h5py.md"), force=true) +Literate.markdown(src_path, md_dir, ) + makedocs(; sitename="HDF5.jl", modules=[HDF5], diff --git a/docs/src/h5py.md b/docs/src/h5py.md index d6d6b4ea9..4a16864c8 100644 --- a/docs/src/h5py.md +++ b/docs/src/h5py.md @@ -1,9 +1,18 @@ +```@meta +EditURL = "/literate/h5py.jl" +``` + # Python interoperability When loading python created hdf5 files from Julia the dimensions of arrays are reversed. The reason is that in python C-memory layout is the default, while Julia uses Fortran layout. Here is an example: -```python + +```@example h5py +using PyCall #hide +pyimport_conda("h5py", "h5py") #hide +pyimport_conda("numpy", "numpy") #hide +py""" #hide import h5py import numpy as np path = "created_by_h5py.h5" @@ -18,27 +27,30 @@ file["1d"] = arr1d file["2d"] = arr2d file["3d"] = arr3d file.close() +""" #hide ``` + When we try to load it from julia, dimensions are reversed: -```julia + +```@example h5py using HDF5 +using Test path = "created_by_h5py.h5" h5open(path, "r") do file arr1d = read(file["1d"]) arr2d = read(file["2d"]) arr3d = read(file["3d"]) - @assert size(arr1d) == (3,) - @assert size(arr2d) == (3,2) - @assert size(arr3d) == (3,2,1) + @test size(arr1d) == (3,) + @test size(arr2d) == (3,2) + @test size(arr3d) == (3,2,1) end ``` + To fix this, we can simply reverse the dimensions again: -```julia -using HDF5 +```@example h5py function reversedims(arr) - dims = ntuple(identity, Val(ndims(arr))) - return permutedims(arr, reverse(dims)) + return permutedims(arr, reverse(1:ndims(arr))) end path = "created_by_h5py.h5" @@ -46,45 +58,27 @@ h5open(path, "r") do file arr1d = reversedims(read(file["1d"])) arr2d = reversedims(read(file["2d"])) arr3d = reversedims(read(file["3d"])) - @assert arr1d == [1,2,3] - @assert arr2d == [1 2 3; 4 5 6] - @assert arr3d == reshape(arr2d, (1,2,3)) + @test arr1d == [1,2,3] + @test arr2d == [1 2 3; 4 5 6] + @test arr3d == reshape(arr2d, (1,2,3)) end ``` -Similarly `reversedims` can be used before saving arrays intended for use from python. -If copying of data is undesirable, other options are: -* using Fortran memory layout on the python side -* using C-memory layout on the Julia side (e.g. replace `permutedims` by `PermutedDimsArray` above) -The whole example as a Julia executable script: -```julia -using PyCall -py""" -import h5py -import numpy as np -path = "created_by_h5py.h5" -file = h5py.File(path, "w") -arr1d = np.array([1,2,3]) -arr2d = np.array([[1,2,3], [4,5,6]]) -arr3d = np.array([[[1,2,3], [4,5,6]]]) -assert arr1d.shape == (3,) -assert arr2d.shape == (2,3) -assert arr3d.shape == (1,2,3) -file["1d"] = arr1d -file["2d"] = arr2d -file["3d"] = arr3d -file.close() -""" + Similarly `reversedims` can be used before saving arrays intended for use from python. + If copying of data is undesirable, other options are: + * using Fortran memory layout on the python side + * using C-memory layout on the Julia side (e.g. replace `permutedims` by `PermutedDimsArray` above) +```@example h5py using HDF5 path = "created_by_h5py.h5" h5open(path, "r") do file arr1d = read(file["1d"]) arr2d = read(file["2d"]) arr3d = read(file["3d"]) - @assert size(arr1d) == (3,) - @assert size(arr2d) == (3,2) - @assert size(arr3d) == (3,2,1) + @test size(arr1d) == (3,) + @test size(arr2d) == (3,2) + @test size(arr3d) == (3,2,1) end using HDF5 @@ -98,8 +92,13 @@ h5open(path, "r") do file arr1d = reversedims(read(file["1d"])) arr2d = reversedims(read(file["2d"])) arr3d = reversedims(read(file["3d"])) - @assert arr1d == [1,2,3] - @assert arr2d == [1 2 3; 4 5 6] - @assert arr3d == reshape(arr2d, (1,2,3)) + @test arr1d == [1,2,3] + @test arr2d == [1 2 3; 4 5 6] + @test arr3d == reshape(arr2d, (1,2,3)) end ``` + +--- + +*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* + From 2439603a870761e7c7a4278242cec3ba61be8ba9 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Mon, 18 Jan 2021 13:39:27 +0100 Subject: [PATCH 4/5] remove Literate as doc dependence --- docs/Project.toml | 1 - docs/literate/h5py.jl | 84 ------------------------------------------- docs/make.jl | 7 +--- docs/src/h5py.md | 9 ----- 4 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 docs/literate/h5py.jl diff --git a/docs/Project.toml b/docs/Project.toml index 8ae25d440..a800298a5 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,5 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" diff --git a/docs/literate/h5py.jl b/docs/literate/h5py.jl deleted file mode 100644 index 8e5875362..000000000 --- a/docs/literate/h5py.jl +++ /dev/null @@ -1,84 +0,0 @@ -# # Python interoperability - -# When loading python created hdf5 files from Julia the dimensions of arrays are reversed. -# The reason is that in python C-memory layout is the default, while Julia uses Fortran layout. -# Here is an example: -using PyCall #hide -pyimport_conda("h5py", "h5py") #hide -pyimport_conda("numpy", "numpy") #hide -py""" #hide -import h5py -import numpy as np -path = "created_by_h5py.h5" -file = h5py.File(path, "w") -arr1d = np.array([1,2,3]) -arr2d = np.array([[1,2,3], [4,5,6]]) -arr3d = np.array([[[1,2,3], [4,5,6]]]) -assert arr1d.shape == (3,) -assert arr2d.shape == (2,3) -assert arr3d.shape == (1,2,3) -file["1d"] = arr1d -file["2d"] = arr2d -file["3d"] = arr3d -file.close() -""" #hide - -# When we try to load it from julia, dimensions are reversed: -using HDF5 -using Test -path = "created_by_h5py.h5" -h5open(path, "r") do file - arr1d = read(file["1d"]) - arr2d = read(file["2d"]) - arr3d = read(file["3d"]) - @test size(arr1d) == (3,) - @test size(arr2d) == (3,2) - @test size(arr3d) == (3,2,1) -end -# To fix this, we can simply reverse the dimensions again: - -function reversedims(arr) - return permutedims(arr, reverse(1:ndims(arr))) -end - -path = "created_by_h5py.h5" -h5open(path, "r") do file - arr1d = reversedims(read(file["1d"])) - arr2d = reversedims(read(file["2d"])) - arr3d = reversedims(read(file["3d"])) - @test arr1d == [1,2,3] - @test arr2d == [1 2 3; 4 5 6] - @test arr3d == reshape(arr2d, (1,2,3)) -end -# Similarly `reversedims` can be used before saving arrays intended for use from python. -# If copying of data is undesirable, other options are: -# * using Fortran memory layout on the python side -# * using C-memory layout on the Julia side (e.g. replace `permutedims` by `PermutedDimsArray` above) -# - -using HDF5 -path = "created_by_h5py.h5" -h5open(path, "r") do file - arr1d = read(file["1d"]) - arr2d = read(file["2d"]) - arr3d = read(file["3d"]) - @test size(arr1d) == (3,) - @test size(arr2d) == (3,2) - @test size(arr3d) == (3,2,1) -end - -using HDF5 -function reversedims(arr) - dims = ntuple(identity, Val(ndims(arr))) - return permutedims(arr, reverse(dims)) -end - -path = "created_by_h5py.h5" -h5open(path, "r") do file - arr1d = reversedims(read(file["1d"])) - arr2d = reversedims(read(file["2d"])) - arr3d = reversedims(read(file["3d"])) - @test arr1d == [1,2,3] - @test arr2d == [1 2 3; 4 5 6] - @test arr3d == reshape(arr2d, (1,2,3)) -end diff --git a/docs/make.jl b/docs/make.jl index 287f7880d..c8dd43f9e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,12 +11,6 @@ not_low_level_api(::typeof(HDF5.h5p_get_class_name)) = false not_low_level_api(::typeof(HDF5.h5t_get_member_name)) = false not_low_level_api(::typeof(HDF5.h5t_get_tag)) = false -# literate -using Literate -src_path = joinpath(@__DIR__, "literate", "h5py.jl") -md_dir = joinpath(@__DIR__, "src") -rm(joinpath(md_dir, "h5py.md"), force=true) -Literate.markdown(src_path, md_dir, ) makedocs(; sitename="HDF5.jl", @@ -33,6 +27,7 @@ makedocs(; "Low-level library bindings" => "api_bindings.md", "Python interoperability" => "h5py.md" ], + strict=true, ) deploydocs(; diff --git a/docs/src/h5py.md b/docs/src/h5py.md index 4a16864c8..899108839 100644 --- a/docs/src/h5py.md +++ b/docs/src/h5py.md @@ -1,7 +1,3 @@ -```@meta -EditURL = "/literate/h5py.jl" -``` - # Python interoperability When loading python created hdf5 files from Julia the dimensions of arrays are reversed. @@ -97,8 +93,3 @@ h5open(path, "r") do file @test arr3d == reshape(arr2d, (1,2,3)) end ``` - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - From 77731206ec8b60725c395bee79c9a0305c6b6cbe Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Mon, 25 Jan 2021 14:21:08 +0100 Subject: [PATCH 5/5] try fix docs python deps --- docs/Project.toml | 1 + docs/src/h5py.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index a800298a5..4850a623c 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" diff --git a/docs/src/h5py.md b/docs/src/h5py.md index 899108839..6b1ab6827 100644 --- a/docs/src/h5py.md +++ b/docs/src/h5py.md @@ -6,8 +6,9 @@ Here is an example: ```@example h5py using PyCall #hide -pyimport_conda("h5py", "h5py") #hide -pyimport_conda("numpy", "numpy") #hide +import Conda #hide +Conda.add("h5py") #hide +Conda.add("numpy") #hide py""" #hide import h5py import numpy as np