Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/CI_C_API_Python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ on:
jobs:
test:
name: Test
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version:
- "3.10"
- "3.11"
- "3.12"

steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install -y libeigen3-dev libopenblas-dev

- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install eigen openblas
- uses: actions/checkout@v4
- name: Install uv and set the python version
uses: astral-sh/setup-uv@v6
Expand All @@ -33,8 +41,12 @@ jobs:
- name: Run uv sync
run: uv sync
working-directory: python
env:
SPARSEIR_USE_BLAS: 1

- name: Run tests
# For example, using `pytest`
run: uv run pytest tests
working-directory: python
env:
SPARSEIR_USE_BLAS: 1
74 changes: 69 additions & 5 deletions .github/workflows/PublishPyPI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: Python Build & Publish (Trusted)
on:
push:
tags: ["v*"] # 例: v0.1.0 を push で発火
#pull_request: # for debugging
# branches: [main] # for debugging
workflow_dispatch:

jobs:
Expand All @@ -11,14 +13,69 @@ jobs:
strategy:
matrix:
include:
# Linux builds
- os: ubuntu-latest
cibw_archs: "auto"
#- os: windows-latest
# cibw_archs: "AMD64"
deployment_target: ""
openblas_path: ""
py_tag: "cp310"
- os: ubuntu-latest
cibw_archs: "auto"
deployment_target: ""
openblas_path: ""
py_tag: "cp311"
- os: ubuntu-latest
cibw_archs: "auto"
deployment_target: ""
openblas_path: ""
py_tag: "cp312"
- os: ubuntu-latest
cibw_archs: "auto"
deployment_target: ""
openblas_path: ""
py_tag: "cp313"
# macOS Intel builds
- os: macos-13 # Intel Mac
cibw_archs: "x86_64"
deployment_target: "13.0"
openblas_path: "/usr/local/opt/openblas"
py_tag: "cp310"
- os: macos-13 # Intel Mac
cibw_archs: "x86_64"
deployment_target: "13.0"
openblas_path: "/usr/local/opt/openblas"
py_tag: "cp311"
- os: macos-13 # Intel Mac
cibw_archs: "x86_64"
deployment_target: "13.0"
openblas_path: "/usr/local/opt/openblas"
py_tag: "cp312"
- os: macos-13 # Intel Mac
cibw_archs: "x86_64"
deployment_target: "13.0"
openblas_path: "/usr/local/opt/openblas"
py_tag: "cp313"
# macOS Apple Silicon builds
- os: macos-latest # Apple Silicon Mac
cibw_archs: "arm64"
deployment_target: "15.0"
openblas_path: "/opt/homebrew/opt/openblas"
py_tag: "cp310"
- os: macos-latest # Apple Silicon Mac
cibw_archs: "arm64"
deployment_target: "15.0"
openblas_path: "/opt/homebrew/opt/openblas"
py_tag: "cp311"
- os: macos-latest # Apple Silicon Mac
cibw_archs: "arm64"
deployment_target: "15.0"
openblas_path: "/opt/homebrew/opt/openblas"
py_tag: "cp312"
- os: macos-latest # Apple Silicon Mac
cibw_archs: "arm64"
deployment_target: "15.0"
openblas_path: "/opt/homebrew/opt/openblas"
py_tag: "cp313"
steps:
- uses: actions/checkout@v4
- name: remove python/.gitignore
Expand All @@ -30,16 +87,23 @@ jobs:
CIBW_DEPENDENCY_VERSIONS: "latest"
CIBW_BUILD_VERBOSITY: 1
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux_2_28"
CIBW_ENVIRONMENT_MACOS: "MACOSX_DEPLOYMENT_TARGET=11.0"
CIBW_BUILD: "${{ matrix.py_tag }}-*"
CIBW_ENVIRONMENT_MACOS: "MACOSX_DEPLOYMENT_TARGET=${{ matrix.deployment_target || '11.0' }} SPARSEIR_USE_BLAS=1"
CIBW_ARCHS: ${{ matrix.cibw_archs }}
CIBW_SKIP: "*-manylinux_i686 *-musllinux_i686"
# CIBW_BUILD: "cp312-*"
# Install OpenBLAS using micromamba (conda-forge) - separate for manylinux and musllinux
CIBW_BEFORE_ALL_MANYLINUX: "curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba && ./bin/micromamba create -y -p /opt/openblas && ./bin/micromamba install -y -c conda-forge openblas -p /opt/openblas --no-deps"
CIBW_BEFORE_ALL_MUSLLINUX: "apk add --no-cache bzip2 && curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba && ./bin/micromamba create -y -p /opt/openblas && ./bin/micromamba install -y -c conda-forge openblas -p /opt/openblas --no-deps"
CIBW_BEFORE_ALL_MACOS: "brew install openblas"
# Set environment variables to help CMake find OpenBLAS
CIBW_ENVIRONMENT_MANYLINUX: "SPARSEIR_USE_BLAS=1"
CIBW_ENVIRONMENT_MUSLLINUX: "SPARSEIR_USE_BLAS=1"
with:
package-dir: ./python
output-dir: dist
- uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}-${{ matrix.cibw_archs }}
name: wheels-${{ matrix.os }}-${{ matrix.cibw_archs }}-${{ matrix.py_tag }}
path: dist/*

publish-testpypi:
Expand Down
73 changes: 73 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ This is a low-level binding for the [libsparseir](https://github.com/SpM-lab/lib
- C++11 compatible compiler
- numpy

### Optional Dependencies

- **OpenBLAS** (recommended for better performance)
- macOS: `brew install openblas`
- Ubuntu/Debian: `sudo apt install libopenblas-dev`
- CentOS/RHEL: `sudo yum install openblas-devel`

## Build

### Install Dependencies and Build
Expand All @@ -22,6 +29,30 @@ This will:
- Build the C++ libsparseir library using CMake
- Install the Python package in development mode

### Build with OpenBLAS Support

To enable OpenBLAS support for improved performance:

```bash
# Set environment variable to enable BLAS
export SPARSEIR_USE_BLAS=1
uv sync
```

Or for a single build:

```bash
SPARSEIR_USE_BLAS=1 uv sync
```

The build system will automatically detect OpenBLAS if it's installed in standard locations. If OpenBLAS is installed in a custom location, you may need to set additional environment variables:

```bash
export CMAKE_PREFIX_PATH="/path/to/openblas"
export SPARSEIR_USE_BLAS=1
uv sync
```

### Clean Build Artifacts

To remove build artifacts and files copied from the parent directory:
Expand All @@ -35,3 +66,45 @@ This will remove:
- Copied source files: `include/`, `src/`, `fortran/`, `cmake/`, `CMakeLists.txt`
- Compiled libraries: `pylibsparseir/*.so`, `pylibsparseir/*.dylib`, `pylibsparseir/*.dll`
- Cache directories: `pylibsparseir/__pycache__`

## Performance Notes

### BLAS Support

This package supports BLAS libraries for improved linear algebra performance:

- **With OpenBLAS**: Significant performance improvements for matrix operations
- **Without BLAS**: Uses Eigen's built-in implementations (still efficient, but slower for large matrices)

The build system will automatically detect and use OpenBLAS if available. You can verify BLAS support by checking the build output for messages like:

```
BLAS support enabled
Found OpenBLAS at: /opt/homebrew/opt/openblas
```

### Troubleshooting

**Build fails with "Could NOT find BLAS":**
```bash
# Install OpenBLAS first
brew install openblas # macOS
sudo apt install libopenblas-dev # Ubuntu

# Then force BLAS detection
SPARSEIR_USE_BLAS=1 uv sync
```

**OpenBLAS not detected automatically:**
```bash
# Set CMake prefix path manually
export CMAKE_PREFIX_PATH="/usr/local/opt/openblas" # or your OpenBLAS path
export SPARSEIR_USE_BLAS=1
uv sync
```

**Verify BLAS support in built package:**
```python
import pylibsparseir
# Check build logs or library dependencies to confirm BLAS linking
```
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pylibsparseir"
version = "0.1.1"
version = "0.1.2"
description = "Python bindings for the libsparseir library, providing efficient sparse intermediate representation for many-body physics calculations"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
79 changes: 78 additions & 1 deletion python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,85 @@ def run(self):
'-DCMAKE_CXX_STANDARD_REQUIRED=ON',
]

# Add architecture-specific flags for macOS
# Import platform module here before using it
import platform

# Only enable BLAS if we can find it or if explicitly requested
enable_blas = os.environ.get('SPARSEIR_USE_BLAS', '').lower() in ('1', 'on', 'true', 'yes')
if not enable_blas:
# Try to detect if BLAS is available
blas_paths = [
'/usr/lib64/openblas',
'/usr/lib/openblas',
'/usr/local/lib64/openblas',
'/usr/local/lib/openblas',
'/opt/homebrew/opt/openblas/lib',
'/usr/local/opt/openblas/lib'
]

for path in blas_paths:
if os.path.exists(path):
enable_blas = True
print(f"Found BLAS library path: {path}", flush=True)
break

if enable_blas:
cmake_args.append('-DSPARSEIR_USE_BLAS=ON')
print("BLAS support enabled", flush=True)

# Try to detect and prefer OpenBLAS
openblas_found = False

# Check common OpenBLAS installation paths
openblas_paths = []
if platform.system() == 'Darwin':
openblas_paths = [
'/opt/homebrew/opt/openblas',
'/usr/local/opt/openblas',
'/opt/local' # MacPorts
]
else: # Linux and others
openblas_paths = [
'/usr/local',
'/usr',
'/opt/openblas',
'/usr/lib64/openblas',
'/usr/lib/openblas'
]

# Look for OpenBLAS
for path in openblas_paths:
if path.endswith('openblas'):
# Direct path to OpenBLAS directory
lib_path = path
include_path = path.replace('lib', 'include').replace('openblas', '')
else:
lib_path = os.path.join(path, 'lib')
include_path = os.path.join(path, 'include')

# Check for OpenBLAS library
possible_libs = [
os.path.join(lib_path, 'libopenblas.dylib'), # macOS
os.path.join(lib_path, 'libopenblas.so'), # Linux
os.path.join(lib_path, 'libopenblas.a'), # Static
]

if any(os.path.exists(lib) for lib in possible_libs):
print(f"Found OpenBLAS at: {path}", flush=True)
cmake_args.extend([
f'-DCMAKE_PREFIX_PATH={path}',
f'-DBLAS_ROOT={path}',
])
openblas_found = True
break

if not openblas_found:
print("OpenBLAS not found in standard locations, using system BLAS", flush=True)
else:
print("BLAS support disabled - no BLAS libraries found", flush=True)
openblas_found = False

# Add architecture-specific flags for macOS
if platform.system() == 'Darwin':
# Get the target architecture from environment or system
target_arch = os.environ.get('ARCHFLAGS', '').replace('-arch ', '')
Expand Down
Loading