Skip to content

Commit f58c94d

Browse files
author
Denis Jelovina
committed
Squash: combined commits 9c8ebec..3591d1d
Summary of included commits: 3591d1d Denis Jelovina 2025-10-24 Squash: Combined changes from 9f634c7..HEAD — merge all temp commit 9f634c7 Denis Jelovina 2025-10-22 chore(pyalp): remove vendored pybind11 and unused CMake; add cibuildwheel workflow and config 0f9840c Denis Jelovina 2025-10-22 pyalp packaging and CI: bundle prebuilt bindings, setup pinned pybind11, and add wheel smoke-test 1e4cd6e Denis Jelovina 2025-10-22 Remove deprecated Python bindings and related files a7c9900 Denis Jelovina 2025-10-22 Convert pybind11 to submodule pinned at 8d503e30be400ad431d3d140707803e87e75fad7 3b9e5b5 Petros Anastasiadis 2025-09-30 Removed numpy2alp older stuff - left .cpp ffr 1403a2a Petros Anastasiadis 2025-09-30 Added installation instructions for python part 95d51a8 Denis Jelovina 2025-06-02 Add doccumentation for test.py 58aae6a Denis Jelovina 2025-05-26 compile messages 6ba4c3e Denis Jelovina 2025-05-26 add submodule 03a00d4 Denis Jelovina 2025-05-26 disable pyalp by defualt a471cdd Denis Jelovina 2025-05-26 add pybind11 submodule 8a4ade9 Denis Jelovina 2025-05-26 refactor code add support for multiple types 68dea3d Denis Jelovina 2025-05-23 add conjugate gradient 427594b Denis Jelovina 2025-05-23 add numpy to c++ examples Diffstat: .github/workflows/pyalp-ci.yml | 169 +++++++++++++++++++++++++ .github/workflows/pyalp-publish.yml | 175 ++++++++++++++++++++++++++ .gitignore | 3 +- .gitmodules | 3 + CMakeLists.txt | 22 +++- cmake/CompileFlags.cmake | 20 ++- pyalp/CMakeLists.txt | 1 + pyalp/PINNED_PYBIND11 | 1 + pyalp/extern/pybind11 | 1 + pyalp/pyproject.toml | 30 +++++ pyalp/setup.py | 114 +++++++++++++++++ pyalp/src/CMakeLists.txt | 111 ++++++++++++++++ pyalp/src/conjugate_gradient.hpp | 109 ++++++++++++++++ pyalp/src/matrix_wrappers.hpp | 115 +++++++++++++++++ pyalp/src/numpy2alp.cpp | 57 +++++++++ pyalp/src/pyalp.cpp | 84 +++++++++++++ pyalp/src/pyalp.egg-info/PKG-INFO | 65 ++++++++++ pyalp/src/pyalp.egg-info/SOURCES.txt | 12 ++ pyalp/src/pyalp.egg-info/dependency_links.txt | 1 + pyalp/src/pyalp.egg-info/requires.txt | 1 + pyalp/src/pyalp.egg-info/top_level.txt | 1 + pyalp/src/pyalp/CMakeLists.txt | 16 +++ pyalp/src/pyalp/__init__.py | 41 ++++++ pyalp/src/pyalp/bindings.cpp | 56 +++++++++ pyalp/src/pyalp/your_module.py | 4 + pyalp/src/python2alp.cpp | 9 ++ pyalp/src/utils.hpp | 20 +++ pyalp/src/vector_wrappers.hpp | 65 ++++++++++ pyproject.toml | 4 + setup.py | 10 ++ tests/CMakeLists.txt | 4 + tests/python/CMakeLists.txt | 23 ++++ tests/python/numpy_array_print.py | 5 + tests/python/test.py | 65 ++++++++++ tools/make_wheel_from_so.py | 109 ++++++++++++++++ tools/smoke_test_pyalp.py | 66 ++++++++++ 36 files changed, 1589 insertions(+), 3 deletions(-)
1 parent 5123737 commit f58c94d

36 files changed

+1589
-3
lines changed

.github/workflows/pyalp-ci.yml

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
name: pyalp CI
2+
3+
# Run only on pushes that create tags starting with 'pyalp'
4+
on:
5+
push:
6+
tags: [ 'pyalp*' ]
7+
8+
jobs:
9+
build-bindings:
10+
name: Build C++ bindings
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout (with submodules)
14+
uses: actions/checkout@v4
15+
with:
16+
submodules: 'recursive'
17+
fetch-depth: 0
18+
19+
- name: Verify pinned pybind11 submodule commit
20+
# Fail early if the checked-out pybind11 is not the pinned commit
21+
run: |
22+
set -euo pipefail
23+
# Prefer top-level pinned file so it survives moves; fallback to submodule path
24+
if [ -f pyalp/PINNED_PYBIND11 ];
25+
then
26+
PINNED_SHA=$(cat pyalp/PINNED_PYBIND11 | tr -d '\n')
27+
elif [ -f pyalp/extern/pybind11/PINNED_COMMIT ];
28+
then
29+
PINNED_SHA=$(cat pyalp/extern/pybind11/PINNED_COMMIT | tr -d '\n')
30+
else
31+
echo "No pinned commit file found (tried pyalp/PINNED_PYBIND11 and pyalp/extern/pybind11/PINNED_COMMIT)" >&2
32+
exit 2
33+
fi
34+
echo "Expected pybind11 commit: $PINNED_SHA"
35+
ACTUAL=$(git -C pyalp/extern/pybind11 rev-parse HEAD || true)
36+
echo "Found pybind11 commit: $ACTUAL"
37+
if [ "$ACTUAL" != "$PINNED_SHA" ];
38+
then
39+
echo "ERROR: pybind11 submodule commit does not match pinned commit" >&2
40+
exit 2
41+
fi
42+
43+
- name: Install build dependencies
44+
run: |
45+
set -euo pipefail
46+
sudo apt-get update
47+
# libnuma-dev provides NUMA headers/libraries needed by FindNuma.cmake
48+
sudo apt-get install -y build-essential cmake ninja-build pkg-config python3-venv python3-dev python3-pip libnuma-dev
49+
50+
- name: Configure and build pyalp bindings
51+
run: |
52+
set -euo pipefail
53+
mkdir -p build_alp
54+
cmake -S . -B build_alp -DENABLE_PYALP=ON
55+
# Only attempt to build pyalp targets if the pyalp CMake directory exists
56+
if [ -f pyalp/CMakeLists.txt ];
57+
then
58+
echo "pyalp CMakeLists found — building pyalp targets"
59+
cmake --build build_alp --target pyalp_ref -- -j || true
60+
cmake --build build_alp --target pyalp_omp -- -j || true
61+
else
62+
echo "pyalp directory or CMakeLists not present — skipping pyalp targets"
63+
fi
64+
65+
- name: Find and list built shared objects
66+
run: |
67+
set -euo pipefail
68+
echo "Searching for shared objects under build_alp and pyalp"
69+
find build_alp -name "*.so" -maxdepth 8 -print || true
70+
find pyalp -name "*.so" -maxdepth 8 -print || true
71+
72+
- name: Collect built shared objects into artifacts/
73+
run: |
74+
set -euo pipefail
75+
mkdir -p artifacts
76+
# copy any discovered .so files into a flat artifacts directory so upload-artifact can find them
77+
find build_alp -name "*.so" -print0 | xargs -0 -I{} bash -lc 'cp -v "{}" artifacts/ || true' || true
78+
find pyalp -name "*.so" -print0 | xargs -0 -I{} bash -lc 'cp -v "{}" artifacts/ || true' || true
79+
echo "Artifacts now contains:" && ls -la artifacts || true
80+
81+
- name: Upload built bindings
82+
uses: actions/upload-artifact@v4
83+
with:
84+
name: pyalp-so
85+
path: |
86+
build_alp/**/*.so
87+
artifacts/**/*.so
88+
pyalp/**/pyalp*.so
89+
pyalp/**/_pyalp*.so
90+
pyalp/**/libpyalp*.so
91+
pyalp/**/*.so
92+
93+
build-wheel-and-test:
94+
name: Build wheel from prebuilt .so and smoke-test
95+
runs-on: ubuntu-latest
96+
needs: build-bindings
97+
steps:
98+
- name: Checkout
99+
uses: actions/checkout@v4
100+
101+
- name: Download built bindings
102+
uses: actions/download-artifact@v4
103+
with:
104+
name: pyalp-so
105+
path: artifacts
106+
107+
- name: Show downloaded artifacts
108+
run: ls -la artifacts || true
109+
110+
- name: Prepare wheel inputs
111+
id: prep
112+
run: |
113+
set -euo pipefail
114+
# List candidate shared-object files for debugging
115+
echo "Candidate .so files in artifacts:" && find artifacts -type f -name "*.so" -print || true
116+
# Find likely candidates (prefer _pyalp, pyalp, libpyalp)
117+
SO_PATH=$(find artifacts \( -name "_pyalp*.so" -o -name "pyalp*.so" -o -name "libpyalp*.so" -o -name "*.so" \) | head -n1)
118+
if [ -z "$SO_PATH" ];
119+
then
120+
echo "ERROR: no built .so artifact found to package" >&2
121+
echo "Artifacts listing:" && ls -la artifacts || true
122+
exit 2
123+
fi
124+
echo "so_path=$SO_PATH" >> "$GITHUB_OUTPUT"
125+
# Prefer helper located inside pyalp/ but fall back to top-level tools/
126+
if [ -f pyalp/tools/make_wheel_from_so.py ]; then
127+
echo "builder=pyalp/tools/make_wheel_from_so.py" >> "$GITHUB_OUTPUT"
128+
else
129+
echo "builder=tools/make_wheel_from_so.py" >> "$GITHUB_OUTPUT"
130+
fi
131+
# Derive Python version from the .so filename (e.g., cpython-311 -> 3.11, cp312 -> 3.12)
132+
PY_VER=""
133+
if [[ "$SO_PATH" =~ cpython-([0-9]{3}) ]];
134+
then
135+
n=${BASH_REMATCH[1]}
136+
PY_VER="${n:0:1}.${n:1}"
137+
elif [[ "$SO_PATH" =~ cp([0-9]{2,3}) ]];
138+
then
139+
n=${BASH_REMATCH[1]}
140+
PY_VER="${n:0:1}.${n:1}"
141+
fi
142+
echo "python_version=$PY_VER" >> "$GITHUB_OUTPUT"
143+
144+
- name: Run wheel builder
145+
run: |
146+
set -euo pipefail
147+
echo "builder=${{ steps.prep.outputs.builder }}"
148+
echo "so=${{ steps.prep.outputs.so_path }}"
149+
python3 "${{ steps.prep.outputs.builder }}" "${{ steps.prep.outputs.so_path }}" --out-dir dist_wheel
150+
151+
- name: Show wheel
152+
run: ls -la dist_wheel || true
153+
154+
- name: Set up Python matching built extension
155+
if: ${{ steps.prep.outputs.python_version != '' }}
156+
uses: actions/setup-python@v5
157+
with:
158+
python-version: ${{ steps.prep.outputs.python_version }}
159+
160+
- name: Smoke test wheel in venv
161+
run: |
162+
set -euo pipefail
163+
python3 -V
164+
which python3
165+
python3 -m venv venv
166+
. venv/bin/activate
167+
pip install --upgrade pip wheel
168+
pip install dist_wheel/*.whl
169+
tools/smoke_test_pyalp.py
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
name: pyalp wheels (cibuildwheel)
2+
3+
on:
4+
push:
5+
tags: [ 'pyalp.v*' ]
6+
workflow_dispatch: {}
7+
8+
jobs:
9+
build-wheels:
10+
name: Build wheels on ${{ matrix.os }}
11+
runs-on: ${{ matrix.os }}
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
os: [ubuntu-latest, macos-latest]
16+
steps:
17+
- name: Checkout (with submodules)
18+
uses: actions/checkout@v4
19+
with:
20+
submodules: recursive
21+
fetch-depth: 0
22+
23+
- name: Verify pinned pybind11 submodule commit
24+
if: runner.os == 'Linux' || runner.os == 'macOS'
25+
shell: bash
26+
run: |
27+
set -euo pipefail
28+
if [ -f pyalp/PINNED_PYBIND11 ];
29+
then
30+
PINNED_SHA=$(tr -d '\n' < pyalp/PINNED_PYBIND11)
31+
elif [ -f pyalp/extern/pybind11/PINNED_COMMIT ];
32+
then
33+
PINNED_SHA=$(tr -d '\n' < pyalp/extern/pybind11/PINNED_COMMIT)
34+
else
35+
echo "No pinned commit file found (pyalp/PINNED_PYBIND11 or pyalp/extern/pybind11/PINNED_COMMIT)" >&2
36+
exit 2
37+
fi
38+
ACTUAL=$(git -C pyalp/extern/pybind11 rev-parse HEAD || true)
39+
echo "Expected pybind11 commit: $PINNED_SHA"
40+
echo "Found pybind11 commit: $ACTUAL"
41+
test "$ACTUAL" = "$PINNED_SHA"
42+
43+
- name: Set up Python
44+
uses: actions/setup-python@v5
45+
with:
46+
python-version: '3.11'
47+
48+
- name: Install cibuildwheel
49+
run: |
50+
python -m pip install --upgrade pip
51+
python -m pip install cibuildwheel==2.21.3
52+
53+
- name: Build wheels
54+
env:
55+
CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*"
56+
CIBW_SKIP: "*-musllinux* pp*"
57+
CIBW_ARCHS_LINUX: "x86_64"
58+
CIBW_ARCHS_MACOS: "arm64"
59+
CIBW_BUILD_VERBOSITY: "1"
60+
# Ensure submodule headers are used by setup.py
61+
CIBW_ENVIRONMENT: >
62+
PYTHONUTF8=1
63+
CIBW_ENVIRONMENT_MACOS: >
64+
PYTHONUTF8=1
65+
MACOSX_DEPLOYMENT_TARGET=15.0
66+
# Prebuild the CMake-based extension via top-level CMake so all variables/options are defined.
67+
CIBW_BEFORE_BUILD: |
68+
python -m pip install --upgrade pip
69+
python -m pip install cmake ninja
70+
echo "[cibw] Working directory and contents:"; pwd; ls -la
71+
echo "[cibw] Checking for pyalp CMakeLists:"; ls -la pyalp || true; if [ -f pyalp/CMakeLists.txt ];
72+
then echo "found pyalp/CMakeLists.txt"; else echo "pyalp/CMakeLists.txt NOT found"; fi
73+
# If the wrapper CMakeLists.txt wasn't copied (e.g., untracked file when cibuildwheel uses git ls-files), create a minimal shim
74+
if [ ! -f pyalp/CMakeLists.txt ];
75+
then
76+
echo "[cibw] Creating pyalp/CMakeLists.txt shim (add_subdirectory(src)) for wheel build"
77+
printf '%s\n' 'add_subdirectory(src)' > pyalp/CMakeLists.txt
78+
fi
79+
# Ensure no stale extension from a previous ABI remains in the source tree
80+
rm -f pyalp/src/pyalp/_pyalp*.so || true
81+
# Overwrite root setup.py inside the container to delegate packaging to pyalp/setup.py (keep git root clean)
82+
printf '%s\n' "import os, runpy; ROOT=os.path.dirname(os.path.abspath(__file__)); PKG=os.path.join(ROOT, 'pyalp'); os.chdir(PKG); runpy.run_path(os.path.join(PKG, 'setup.py'), run_name='__main__')" > setup.py
83+
# Configure from repository root; enable pyalp and choose NUMA setting per-platform
84+
PYEXEC=$(python -c 'import sys; print(sys.executable)')
85+
# Use a per-ABI build directory to avoid cross-ABI contamination
86+
ABI_TAG=$(python -c 'import sys; print(f"cp{sys.version_info[0]}{sys.version_info[1]}")')
87+
BUILD_DIR="build/${ABI_TAG}"
88+
# Enable NUMA on Linux runners (for linux wheels), keep disabled elsewhere.
89+
if [ "$(uname -s)" = "Linux" ];
90+
then
91+
echo "[cibw] Linux build container detected — attempting to install NUMA dev libs"
92+
# Try package managers commonly present in manylinux containers. Ignore failures
93+
if command -v yum >/dev/null 2>&1;
94+
then
95+
yum -y install numactl-devel || true
96+
elif command -v apt-get >/dev/null 2>&1;
97+
then
98+
apt-get update || true
99+
apt-get install -y libnuma-dev || true
100+
fi
101+
NUMA_FLAG="-DWITH_NUMA=ON"
102+
else
103+
# On macOS install Homebrew libomp but do NOT export CPPFLAGS/LDFLAGS.
104+
# Exporting CPPFLAGS was the cause of incorrect header ordering; instead
105+
# pass a CMake prefix hint so FindOpenMP can locate libomp without
106+
# prepending include paths to the global compiler invocation.
107+
if command -v brew >/dev/null 2>&1;
108+
then
109+
echo "[cibw] Homebrew detected — ensuring libomp is available"
110+
# Only install if not already present to avoid reinstall warnings
111+
if ! brew list libomp >/dev/null 2>&1; then
112+
brew install libomp
113+
fi
114+
115+
# Locate libomp installation
116+
if [ -d "/opt/homebrew/opt/libomp" ]; then
117+
HOMEBREW_LIBOMP_DIR="/opt/homebrew/opt/libomp"
118+
elif [ -d "/usr/local/opt/libomp" ]; then
119+
HOMEBREW_LIBOMP_DIR="/usr/local/opt/libomp"
120+
else
121+
HOMEBREW_LIBOMP_DIR=""
122+
fi
123+
124+
if [ -n "${HOMEBREW_LIBOMP_DIR}" ]; then
125+
CMAKE_PREFIX_HINT="-DCMAKE_PREFIX_PATH=${HOMEBREW_LIBOMP_DIR}"
126+
echo "[cibw] Using libomp from ${HOMEBREW_LIBOMP_DIR}"
127+
else
128+
CMAKE_PREFIX_HINT=""
129+
fi
130+
fi
131+
NUMA_FLAG="-DWITH_NUMA=OFF"
132+
# Set macOS deployment target for arm64 to match libomp requirement
133+
export MACOSX_DEPLOYMENT_TARGET=15.0
134+
OSX_DEPLOY_FLAG="-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}"
135+
fi
136+
# Clean build directory to prevent CMake caching issues
137+
rm -rf "${BUILD_DIR}"
138+
# On macOS, add flag to downgrade template keyword warning from error to warning
139+
if [ "$(uname -s)" = "Darwin" ];
140+
then
141+
MACOS_FLAGS="-DCMAKE_CXX_FLAGS=-Wno-error=missing-template-arg-list-after-template-kw"
142+
else
143+
MACOS_FLAGS=""
144+
fi
145+
# For wheel builds, request portable flags (avoid -march=native) and disable
146+
# interprocedural optimization (LTO) to improve portability of the produced wheels.
147+
PORTABLE_FLAG="-DALP_PORTABLE_BUILD=ON"
148+
LTO_FLAG="-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF"
149+
cmake -S . -B "${BUILD_DIR}" -G Ninja -DCMAKE_BUILD_TYPE=Release -DENABLE_PYALP=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_FIND_FRAMEWORK=NEVER ${MACOS_FLAGS} ${NUMA_FLAG} ${CMAKE_PREFIX_HINT:-} ${OSX_DEPLOY_FLAG:-} ${PORTABLE_FLAG} ${LTO_FLAG} -DPython3_EXECUTABLE="${PYEXEC}"
150+
cmake --build "${BUILD_DIR}" --target pyalp_ref --parallel
151+
run: |
152+
# Build from repository root so the full CMake project is available in the container
153+
python -m cibuildwheel --output-dir wheelhouse .
154+
155+
- name: Upload wheels
156+
uses: actions/upload-artifact@v4
157+
with:
158+
name: pyalp-wheels-${{ matrix.os }}
159+
path: wheelhouse/*.whl
160+
161+
# Placeholder for publish job; enable when versioning is PEP 440 compliant
162+
# publish:
163+
# needs: build-wheels
164+
# runs-on: ubuntu-latest
165+
# permissions:
166+
# id-token: write
167+
# steps:
168+
# - name: Download wheels
169+
# uses: actions/download-artifact@v4
170+
# with:
171+
# path: dist
172+
# - name: Publish to PyPI
173+
# uses: pypa/gh-action-pypi-publish@release/v1
174+
# with:
175+
# packages-dir: dist

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ paths.mk
88
[Bb]uild*/
99
[Oo]bj*/
1010
[Ii]nstall*/
11-
cmake-build-*/
11+
cmake-build-*/
12+
.venv/

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "pyalp/extern/pybind11"]
2+
path = pyalp/extern/pybind11
3+
url = https://github.com/pybind/pybind11

0 commit comments

Comments
 (0)