From 4c2878f01ec3053444ee187b1370b8c08f6dd798 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:30:01 +0800 Subject: [PATCH] Add `zfp` and `zfpy` (#5172) Co-authored-by: Gyeongjae Choi --- .pre-commit-config.yaml | 2 +- docs/project/changelog.md | 1 + packages/libzfp/meta.yaml | 34 +++++++++ packages/zfpy/meta.yaml | 37 ++++++++++ .../zfpy/patches/Modernise-packaging.patch | 69 +++++++++++++++++ packages/zfpy/test_zfpy.py | 74 +++++++++++++++++++ 6 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 packages/libzfp/meta.yaml create mode 100644 packages/zfpy/meta.yaml create mode 100644 packages/zfpy/patches/Modernise-packaging.patch create mode 100644 packages/zfpy/test_zfpy.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b44923b45..d27f0871a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -82,7 +82,7 @@ repos: name: mypy-tests args: [--ignore-missing-imports] files: ^(packages/|docs|/conftest.py|src/tests) - exclude: (^packages/.*/setup.py|/src|^packages/aiohttp/aiohttp_patch.py$) + exclude: (^packages/.*/setup.py|/src|^packages/aiohttp/aiohttp_patch.py$|^packages/zfpy/test_zfpy.py$) additional_dependencies: *mypy-deps - repo: https://github.com/pre-commit/mirrors-prettier diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 8e806b154..04a3cb4ea 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -105,6 +105,7 @@ myst: - Added `tree-sitter-java` 0.23.2 {pr}`5102` - Added `tree-sitter-python` 0.23.2 {pr}`5102` - Added `Narwhals` 1.9.4 {pr}`5121` +- Added `libzfp` and `zfpy` 1.0.1 {pr}`5172` ## Version 0.26.3 diff --git a/packages/libzfp/meta.yaml b/packages/libzfp/meta.yaml new file mode 100644 index 000000000..7f66a8616 --- /dev/null +++ b/packages/libzfp/meta.yaml @@ -0,0 +1,34 @@ +package: + name: libzfp + version: 1.0.1 + tag: + - library + - static_library +source: + url: https://github.com/LLNL/zfp/archive/refs/tags/1.0.1.tar.gz + sha256: 4984db6a55bc919831966dd17ba5e47ca7ac58668f4fd278ebd98cd2200da66f + +build: + type: static_library + exports: requested + script: | + mkdir -p build + cd build + emcmake cmake ${CMAKE_ARGS} \ + -DBUILD_CFP=ON \ + -DBUILD_UTILITIES=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_SHARED_LIBS=OFF \ + -DZFP_ENABLE_PIC=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_FLAGS="-s WASM=1" \ + -DZFP_WITH_OPENMP=OFF \ + -DCMAKE_INSTALL_PREFIX=${WASM_LIBRARY_DIR} \ + .. + + emmake make -j ${PYODIDE_JOBS:-3} install + +about: + home: https://zfp.llnl.gov/ + summary: Open-source software for compressed floating-point arrays + license: BSD-3-Clause diff --git a/packages/zfpy/meta.yaml b/packages/zfpy/meta.yaml new file mode 100644 index 000000000..b883a3a76 --- /dev/null +++ b/packages/zfpy/meta.yaml @@ -0,0 +1,37 @@ +package: + name: zfpy + version: 1.0.1 + top-level: + - zfpy + +source: + # There's no sdist for zfpy on PyPI, so we get the tarball from the GitHub release instead + url: https://github.com/LLNL/zfp/archive/refs/tags/1.0.1.tar.gz + sha256: 4984db6a55bc919831966dd17ba5e47ca7ac58668f4fd278ebd98cd2200da66f + patches: + - patches/Modernise-packaging.patch + +build: + exports: requested + cflags: | + -I$(PYTHONINCLUDE) + -I$(WASM_LIBRARY_DIR)/include + ldflags: | + -L$(WASM_LIBRARY_DIR)/lib + +requirements: + host: + - libzfp + - numpy + run: + - numpy + +test: + imports: + - zfpy + +about: + home: https://zfp.llnl.gov/ + PyPI: https://pypi.org/project/zfpy/ + summary: zfp compression in Python + license: BSD-3-Clause diff --git a/packages/zfpy/patches/Modernise-packaging.patch b/packages/zfpy/patches/Modernise-packaging.patch new file mode 100644 index 000000000..411f7835b --- /dev/null +++ b/packages/zfpy/patches/Modernise-packaging.patch @@ -0,0 +1,69 @@ +From f36ef86e67185b16b65445092c40a3d75d0be45c Mon Sep 17 00:00:00 2001 +From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> +Date: Fri, 8 Nov 2024 02:46:00 +0530 +Subject: [PATCH] Modernise packaging + +This patch introduces a PEP 517/518 compliant build system using setuptools and +declarative metadata in pyproject.toml, and fixes syntax errors in setup.py. The +sources for the extension module are correctly specified as Cython source files. + +Some part of the credits go to the upstream +PR at https://github.com/LLNL/zfp/pull/234 + +Co-Authored-By: David M. Rogers <2243447+frobnitzem@users.noreply.github.com> +--- + pyproject.toml | 13 +++++++++++++ + setup.py | 18 +++++++++--------- + 2 files changed, 22 insertions(+), 9 deletions(-) + create mode 100644 pyproject.toml + +diff --git a/pyproject.toml b/pyproject.toml +new file mode 100644 +index 0000000..88adcc8 +--- /dev/null ++++ b/pyproject.toml +@@ -0,0 +1,13 @@ ++[build-system] ++requires = ["setuptools", "cython", "numpy"] ++build-backend = "setuptools.build_meta" ++ ++[project] ++name = "zfpy" ++version = "1.0.1" ++authors = [ ++ { name = "Peter Lindstrom", email = "zfp@llnl.gov" }, ++ { name = "Danielle Asher", email = "zfp@llnl.gov" }, ++] ++description = "zfp compression in Python" ++requires-python = ">=3.0" +diff --git a/setup.py b/setup.py +index fa2da6e..919fa7c 100644 +--- a/setup.py ++++ b/setup.py +@@ -2,14 +2,14 @@ from setuptools import setup, Extension + import numpy as np + + setup( +- name="zfpy", +- version="1.0.1", +- author="Peter Lindstrom, Danielle Asher", +- author_email="zfp@llnl.gov", + url="https://zfp.llnl.gov", +- description="zfp compression in Python", +- long_description="zfp is a compressed format for representing multidimensional floating-point and integer arrays. zfp provides compressed-array classes that support high throughput read and write random access to individual array elements. zfp also supports serial and parallel compression of whole arrays using both lossless and lossy compression with error tolerances. zfp is primarily written in C and C++ but also includes Python and Fortran bindings.", +- ext_modules=[Extension("zfpy", ["build/python/zfpy.c"], +- include_dirs=["include", np.get_include()], +- libraries=["zfp"], library_dirs=["build/lib64", "build/lib/Release"]), language_level = "3"] ++ ext_modules= ++ [ ++ Extension( ++ name="zfpy", ++ sources=["python/zfpy.pyx"], ++ include_dirs=["include", np.get_include()], ++ libraries=["zfp"], ++ library_dirs=["build/lib64", "build/lib"]) ++ ], + ) +-- +2.39.3 (Apple Git-146) + diff --git a/packages/zfpy/test_zfpy.py b/packages/zfpy/test_zfpy.py new file mode 100644 index 000000000..0896f323d --- /dev/null +++ b/packages/zfpy/test_zfpy.py @@ -0,0 +1,74 @@ +from pytest_pyodide import run_in_pyodide + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_compression(selenium): + import numpy as np + import zfpy + + my_array = np.arange(1, 20) + compressed_data = zfpy.compress_numpy(my_array) + decompressed_array = zfpy.decompress_numpy(compressed_data) + np.testing.assert_array_equal(my_array, decompressed_array) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_compression_with_tolerance(selenium): + import numpy as np + import zfpy + + my_array = np.linspace(0, 1, 1000) + compressed_data = zfpy.compress_numpy(my_array, tolerance=1e-3) + decompressed_array = zfpy.decompress_numpy(compressed_data) + np.testing.assert_allclose(my_array, decompressed_array, atol=1e-3) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_different_dimensions(selenium) -> None: + import numpy as np + import zfpy + + np.random.seed(42) + + # Test arrays; from 1D to 4D + for dimensions in range(1, 5): + # 1. test with uniform dimensions + shape1 = tuple([5] * dimensions) + array = np.random.rand(*shape1).astype(np.float64) + compressed1 = zfpy.compress_numpy(array, write_header=True) + decompressed1 = zfpy.decompress_numpy(compressed1) + np.testing.assert_array_equal(decompressed1, array) + + # 2. test with increasing dimensions + shape2 = tuple(range(2, 2 + dimensions)) + array = np.random.rand(*shape2).astype(np.float64) + compressed2 = zfpy.compress_numpy(array, write_header=True) + decompressed2 = zfpy.decompress_numpy(compressed2) + np.testing.assert_array_equal(decompressed2, array) + + +@run_in_pyodide(packages=["zfpy", "numpy"]) +def test_different_dtypes(selenium) -> None: + """Test ZFP compression/decompression with different numeric dtypes.""" + import numpy as np + import zfpy + + np.random.seed(42) + + shape = (5, 5) + num_elements = np.prod(shape) + + # Test floating-point types + for dtype in [np.float32, np.float64]: + elements = np.random.random_sample(num_elements) + array = np.reshape(elements, shape).astype(dtype) + compressed1 = zfpy.compress_numpy(array, write_header=True) + decompressed1 = zfpy.decompress_numpy(compressed1) + np.testing.assert_array_equal(decompressed1, array) + + # Test integer types + for dtype in [np.int32, np.int64]: + array = np.random.randint(low=-(2**30), high=2**30, size=shape, dtype=dtype) + compressed2 = zfpy.compress_numpy(array, write_header=True) + decompressed2 = zfpy.decompress_numpy(compressed2) + np.testing.assert_array_equal(decompressed2, array)