diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 971b030..df174fc 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -24,15 +24,14 @@ jobs: - name: Install package and test dependencies run: | - python -m pip install pytest numpy - python -m pip install -e . + python -m pip install -e ".[test]" - name: Run tests run: python -m pytest tests/ - name: Install benchmark dependencies run: | - python -m pip install matplotlib zstandard + python -m pip install ".[benchmark]" - name: Run benchmarks run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 73d768d..c5b8fe9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,7 +11,7 @@ jobs: matrix: # os: [ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 @@ -23,9 +23,7 @@ jobs: - name: Install test dependencies run: | - python -m pip install pytest numpy - python -m pip install scikit-build-core pybind11 cmake - python -m pip install -e . + python -m pip install -e ".[test]" - name: Run tests run: python -m pytest tests/ @@ -49,25 +47,15 @@ jobs: - name: Install build dependencies run: | - python -m pip install cibuildwheel cmake + python -m pip install cibuildwheel - name: Build wheels run: python -m cibuildwheel --output-dir dist - env: - CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-* cp313-* pp38-* pp39-* pp310-* - CIBW_ARCHS_LINUX: x86_64 i686 - # CIBW_ARCHS_WINDOWS: AMD64 x86 - CIBW_ARCHS_MACOS: x86_64 arm64 - CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - CIBW_MANYLINUX_I686_IMAGE: manylinux2014 - CIBW_BEFORE_BUILD: pip install scikit-build-core pybind11 cmake - name: Build sdist run: | - pip install setuptools - python setup.py sdist --formats=gztar - env: - CIBW_BEFORE_BUILD: pip install pybind11 cmake + pip install build + python -m build -s -o dist - name: Upload wheels as artifacts uses: actions/upload-artifact@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0177ca5..32277eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ -cmake_minimum_required(VERSION 3.14) -project(simple_ans VERSION 0.1.0 LANGUAGES CXX) +cmake_minimum_required(VERSION 3.15) +project(simple_ans LANGUAGES CXX) -set(CMAKE_CXX_RELEASE_FLAGS "-O3 -march=native") +set(ARCH_FLAGS "-march=native" CACHE STRING "Architecture flags for the compiler") +set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${ARCH_FLAGS}") include(CheckIPOSupported) check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) @@ -61,18 +62,4 @@ endif() # Install rules -include(GNUInstallDirs) -install(TARGETS _simple_ans - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) -install(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/simple_ans -) - -# For editable installs, copy the extension module to the source directory -add_custom_command(TARGET _simple_ans POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/simple_ans/ - COMMENT "Copying extension module to source directory for editable install" -) +install(TARGETS _simple_ans LIBRARY DESTINATION simple_ans) diff --git a/README.md b/README.md index dc82d5d..e1d1e90 100644 --- a/README.md +++ b/README.md @@ -15,23 +15,25 @@ While there are certainly many ANS implementations that are parts of other packa ## Installation -First, install the required dependencies: +simple_ans is available on PyPI: -```bash -pip install pybind11 numpy +```console +pip install simple-ans ``` -Then install the package: +Developers may want to clone the repository and do an editable install: -```bash -pip install . +```console +git clone https://github.com/flatironinstitute/simple_ans.git +cd simple_ans +pip install -e . ``` -Or install from source: +For developers who want automatic rebuilds of the compiled extension: -```bash -cd simple_ans -pip install -e . +```console +pip install "scikit-build-core>=0.5.0" "pybind11>=2.11.1" "pip>=24" ninja +pip install -e . -Ceditable.rebuild=true --no-build-isolation ``` ## Usage @@ -64,6 +66,14 @@ compression_ratio = original_size / compressed_size print(f"Compression ratio: {compression_ratio:.2f}x") ``` +## Tests +To run the tests, install with the `test` extra and run `pytest`: + +```console +pip install "simple-ans[test]" +pytest tests/ +``` + ## Simple benchmark You can run a very simple benchmark that compares simple_ans with `zlib`, `zstandard`, and `lzma` at various compression levels for a toy dataset of quantized Gaussian noise. See [devel/benchmark.py](./devel/benchmark.py) and [devel/benchmark_ans_only.py](./devel/benchmark_ans_only.py). @@ -74,6 +84,12 @@ The benchmark.py also runs in a CI environment and produces the following graph: We see that for this example, the ANS-based compression ratio is higher than the other methods, almost reaching the theoretical ideal. The encode rate in MB/s is also fastest for simple_ans. The decode rate is faster than Zlib but slower than Zstandard. I think in principle, we should be able to speed up the decoding. Let me know if you have ideas for this. +To install the benchmark dependencies, use: + +```console +pip install .[benchmark] +``` + ## Extended benchmarks A more comprehensive benchmark ([devel/benchmark2.py](./devel/benchmark2.py)) tests the compression performance across different types of distributions: diff --git a/pyproject.toml b/pyproject.toml index 75b059c..3363e41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,28 +16,43 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", ] dependencies = [ "numpy>=1.21.0", ] +[project.optional-dependencies] +test = ["pytest"] +benchmark = ["matplotlib", "zstandard"] + [tool.scikit-build] -cmake.minimum-version = "3.15" +cmake.version = ">=3.15" cmake.args = [] -cmake.verbose = true +build.verbose = true sdist.include = ["simple_ans/cpp/*"] wheel.packages = ["simple_ans"] build-dir = "build" editable.mode = "redirect" -[tool.setuptools] -packages = ["simple_ans"] - [tool.scikit-build.cmake.define] -SKBUILD = "ON" CMAKE_POSITION_INDEPENDENT_CODE = "ON" + +[tool.cibuildwheel] +build-frontend = "build" +build = "cp39-* cp310-* cp311-* cp312-* cp313-* pp39-* pp310-*" +manylinux-x86_64-image = "manylinux2014" +test-command = "python -m pytest {project}/tests" +test-extras = ["test"] +config-settings = "cmake.define.ARCH_FLAGS=''" + +[tool.cibuildwheel.linux] +archs = ["x86_64"] + +[tool.cibuildwheel.macos] +archs = ["x86_64", "arm64"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 0a861b6..0000000 --- a/setup.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -This setup.py is kept for backwards compatibility with pip install -e. -The actual build configuration is in pyproject.toml and CMakeLists.txt. -""" - -from setuptools import setup - -if __name__ == "__main__": - setup()