diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..c0838a9 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +FROM ghcr.io/astral-sh/uv:latest AS uv + +FROM mcr.microsoft.com/devcontainers/base:2-ubuntu24.04 + +# install needed packages +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install \ + build-essential \ + cmake \ + gcc \ + gdb \ + libgmp-dev \ + ninja-build + + +COPY --from=uv --chown=vscode /uv /uvx /bin/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e2beb68 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,57 @@ +{ + "name": "PauliEngine development environment", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "capAdd": [ + "SYS_PTRACE" + ], + "securityOpt": [ + "seccomp=unconfined" + ], + "remoteUser": "vscode", + "customizations": { + "vscode": { + "settings": { + "python.defaultInterpreterPath": "${containerEnv:UV_PROJECT_ENVIRONMENT}/bin/python", + "python.terminal.activateEnvInCurrentTerminal": true, + "terminal.integrated.defaultProfile.linux": "zsh", + "terminal.integrated.profiles.linux": { + "zsh": { + "path": "/bin/zsh" + } + } + }, + "extensions": [ + "MS-vsliveshare.vsliveshare", + "benjamin-simmonds.pythoncpp-debug", + "charliermarsh.ruff", + "cschlosser.doxdocgen", + "ms-python.python", + "ms-toolsai.jupyter", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools", + "ms-vscode.cpptools-themes", + "mutantdino.resourcemonitor", + "njpwerner.autodocstring", + "streetsidesoftware.code-spell-checker", + "streetsidesoftware.code-spell-checker-scientific-terms", + "tamasfe.even-better-toml", + "vivaxy.vscode-conventional-commits" + ] + } + }, + "containerEnv": { + "PYTHONUNBUFFERED": "True", + "WORKSPACE": "${containerWorkspaceFolder}", + "UV_LINK_MODE": "copy", + "UV_PROJECT_ENVIRONMENT": "/home/vscode/.venv" + }, + "postCreateCommand": "./.devcontainer/postCreateCommand.sh", + "postStartCommand": "./.devcontainer/postStartCommand.sh", + "hostRequirements": { + "cpus": 4, + "memory": "6gb" + } +} \ No newline at end of file diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh new file mode 100755 index 0000000..8c5c040 --- /dev/null +++ b/.devcontainer/postCreateCommand.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +# install uv and uvx shell completions +echo 'eval "$(uv generate-shell-completion zsh)"' >> /home/vscode/.zshrc +echo 'eval "$(uvx --generate-shell-completion zsh)"' >> /home/vscode/.zshrc + +# set up GDB to view contents of STL containers +gcc_version="$(gcc --version | awk 'NR==1 {print $NF}')" +cat <> ~/.gdbinit +python +import sys +sys.path.insert(0, "/usr/share/gcc-$gcc_version/python") +from libstdcxx.v6.printers import register_libstdcxx_printers +register_libstdcxx_printers (None) +end +EOT + +# install conan +uv venv +source /home/vscode/.venv/bin/activate +uv pip install conan + +# setup conan profile and install dependencies +conan profile detect --force +conan install . --output-folder=build --build=missing -pr ./conan_profile \ No newline at end of file diff --git a/.devcontainer/postStartCommand.sh b/.devcontainer/postStartCommand.sh new file mode 100755 index 0000000..5a44871 --- /dev/null +++ b/.devcontainer/postStartCommand.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# install project with all dependencies +uv sync --all-groups --all-extras + +# install pre-commit hooks +uv run pre-commit install --install-hooks diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000..7c51009 --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,3 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..56629a1 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,160 @@ +name: Publish PauliEngine Python sdist and wheels to PyPI + +on: + workflow_dispatch: + inputs: + force-upload: + description: 'Upload artifacts without a corresponding release' + required: false + default: false + type: boolean + release: + types: + - published + pull_request: + # PRs trigger for the "opened", "reopened", "synchronize" events (default) and + # "ready_for_review" + paths: + - .github/workflows/deploy.yml + types: + - opened + - reopened + - synchronize # when new commits are pushed to the PR + - ready_for_review # when the PR is un-drafted + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + COLUMNS: 120 + FORCE_COLOR: 3 + +jobs: + build-sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 # such that setuptools_scm can do its job correctly + + - name: Build SDist + run: | + pipx install uv + pipx run build --verbose --sdist --installer=uv + + - uses: actions/upload-artifact@v6 + with: + name: cibw-sdist + path: dist/*.tar.gz + + build-wheels: + # in combination with the PR types selection, this top-level if statement + # skips running the workflow for Draft PRs + if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} + name: Build Python wheels for ${{ matrix.os }} + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + os: [ linux-intel, linux-arm, macos-arm, windows-intel, windows-arm] + include: + - archs: native + platform: auto + - os: linux-intel + runs-on: ubuntu-latest + - os: linux-arm + runs-on: ubuntu-24.04-arm + - os: macos-arm + runs-on: macos-latest + - os: windows-intel + runs-on: windows-latest + - os: windows-arm + runs-on: windows-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: pypa/cibuildwheel@v3.3.1 + with: + extras: "uv" + env: + # target platform + CIBW_PLATFORM: ${{ matrix.platform }} + # architectures to build + CIBW_ARCHS: ${{ matrix.archs }} + # use uv and build + CIBW_BUILD_FRONTEND: "build[uv]" + # increase pip debugging output + CIBW_BUILD_VERBOSITY: 1 + CIBW_DEBUG_TRACEBACK: TRUE + CIBW_BEFORE_ALL_LINUX: | + dnf install -y ninja-build + # install conan and C++ dependencies for the project + pip install conan + conan profile detect --force + + # install symengine + conan install . --output-folder=build --build=missing -pr ./conan_profile + CIBW_BEFORE_ALL_MACOS: | + # install conan and C++ dependencies for the project + pip install conan + conan profile detect --force + + # install symengine + conan install . --output-folder=build --build=missing -pr ./conan_profile + CIBW_BEFORE_ALL_WINDOWS: | + pip install delvewheel conan && conan profile detect --force && conan install . --output-folder=build --build=missing -pr conan_profile + # do not activate architecture-dependent compiler flags + # 4 jobs in parallel, but do not start new ones if load average exceeds 5 + CIBW_ENVIRONMENT_LINUX: + CMAKE_TOOLCHAIN_FILE=build/conan_toolchain.cmake + CIBW="true" + SKBUILD_BUILD_TOOL_ARGS="-j4;-l5" + # set deployment target for C++20 compatibility + # do not activate architecture-dependent compiler flags + # 4 jobs in parallel, but do not start new ones if load average exceeds 5 + CIBW_ENVIRONMENT_MACOS: > + CIBW="true" + MACOSX_DEPLOYMENT_TARGET=15.0 + SKBUILD_BUILD_TOOL_ARGS="-j4;-l5" + CIBW_ENVIRONMENT_WINDOWS: > + CIBW="true" + SKBUILD_BUILD_TOOL_ARGS="/m:4" + CMAKE_TOOLCHAIN_FILE=build\conan_toolchain.cmake + # build 3.10, 3.11, 3.12, 3.13 (with GIL), and 3.14 (with GIL) + CIBW_BUILD: "cp310-* cp311-* cp312-* cp313-* cp314-*" + # skip musl builds, and PyPy builds + CIBW_SKIP: "*-musllinux_*" + # use abi3audit to catch issues with Limited API wheels + CIBW_REPAIR_WHEEL_COMMAND_LINUX: | + auditwheel repair -w {dest_dir} {wheel} + if [[ "{wheel}" == *abi3* ]]; then + pipx run abi3audit --strict --report {wheel} + else + echo "{wheel} is not abi3, skipping abi3audit" + fi + # use abi3audit to catch issues with Limited API wheels + CIBW_REPAIR_WHEEL_COMMAND_MACOS: | + delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} + if [[ "{wheel}" == *abi3* ]]; then + pipx run abi3audit --strict --report {wheel} + else + echo "{wheel} is not abi3, skipping abi3audit" + fi + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: | + delvewheel repair -w {dest_dir} {wheel} + if [[ "{wheel}" == *abi3* ]]; then + pipx run abi3audit --strict --report {wheel} + else + echo "{wheel} is not abi3, skipping abi3audit" + fi + CIBW_TEST_COMMAND: python -m pytest {package}/tests + CIBW_TEST_GROUPS: "test" + + - name: Upload wheels + uses: actions/upload-artifact@v6 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: wheelhouse/*.whl \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7102273..c648027 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ Thumbs.db .bat .cmake .env -config.json \ No newline at end of file +config.json + +__pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8441899 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,35 @@ +default_language_version: + python: python3 + +default_stages: [pre-commit, pre-push] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + args: ["--maxkb=1024"] + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: destroyed-symlinks + - id: end-of-file-fixer + - id: forbid-submodules + - id: no-commit-to-branch + args: ['--pattern', '[^a-zA-Z0-9_\/-]'] + - id: trailing-whitespace + + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.11.7 + hooks: + - id: ruff + # Run the Ruff formatter. + - id: ruff-format + args: [--check] + + - repo: https://github.com/astral-sh/uv-pre-commit + # uv version. + rev: 0.7.7 + hooks: + - id: uv-lock diff --git a/src/.vscode/settings.json b/.vscode/settings.json similarity index 100% rename from src/.vscode/settings.json rename to .vscode/settings.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..07c78cd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,104 @@ +cmake_minimum_required(VERSION 3.28...4.2.1) + +if(SKBUILD) + # this value doesn't really matter, as it will be set by scikit-build-core + set(PROJECT_VERSION "0.1.0") +else() + include(FetchContent) + FetchContent_Declare( + CMakeExtraUtils + QUIET + URL + https://github.com/LecrisUT/CMakeExtraUtils/archive/refs/tags/v0.4.1.tar.gz + SOURCE_DIR + ${CMAKE_CURRENT_BINARY_DIR}/external/upstream/_srcs/CMakeExtraUtils + ) + FetchContent_MakeAvailable(CMakeExtraUtils) + + include(DynamicVersion) + dynamic_version(PROJECT_PREFIX pauliengine_) +endif() + +project(pauliengine LANGUAGES CXX VERSION ${PROJECT_VERSION}) + +# if CMAKE_BUILD_TYPE undefined, we set it to Release +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() + + +# Options handling utilities Macro for printing an option in a consistent manner +# Written by Lori A. Burns (@loriab) and Ryan M. Richard (@ryanmrichard) Syntax: +# print_option(