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
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
12 changes: 8 additions & 4 deletions .github/workflows/conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ on:

jobs:
build:
runs-on: ${{ matrix .os }}
runs-on: ${{ matrix.runner_label }}
strategy:
matrix:
# https://github.com/s-weigand/setup-conda/issues/432
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- name: ubuntu-x64
runner_label: ubuntu-latest
- name: macos-arm64
runner_label: macos-latest

steps:
- uses: actions/checkout@v5
Expand All @@ -28,9 +31,10 @@ jobs:
conda install conda-build anaconda-client -y

- name: Bulid and upload
working-directory: python
env:
ANACONDA_API_TOKEN: ${{secrets.ANACONDA_TOKEN}}
run: |
python3 --version
conda config --set anaconda_upload yes
conda build conda-recipe --user SpM-lab
conda build conda-recipe --user SpM-lab --output-folder conda-bld
116 changes: 57 additions & 59 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,17 @@ This is a low-level binding for the [libsparseir](https://github.com/SpM-lab/lib
- Python >= 3.10
- CMake (for building the C++ library)
- C++11 compatible compiler
- numpy
- numpy >= 1.26.4
- scipy

### Optional Dependencies
### BLAS Support

- **OpenBLAS** (recommended for better performance)
- macOS: `brew install openblas`
- Ubuntu/Debian: `sudo apt install libopenblas-dev`
- CentOS/RHEL: `sudo yum install openblas-devel`
This package automatically uses SciPy's BLAS backend for optimal performance. No additional BLAS installation is required - SciPy will provide the necessary BLAS functionality.

## Build

### Install Dependencies and Build

**Option 1: Automatic build (recommended)**
```bash
# Build will automatically run prepare_build.py if needed
uv build
```

**Option 2: Manual preparation**
```bash
# First, prepare the build by copying necessary files from parent directory
python3 prepare_build.py
Expand All @@ -37,7 +28,7 @@ uv build

This will:
- Copy source files (`src/`, `include/`, `cmake/`) from the parent libsparseir directory
- Build the C++ libsparseir library using CMake with BLAS support
- Build the C++ libsparseir library using CMake with automatic BLAS support via SciPy
- Create both source distribution (sdist) and wheel packages

### Development Build
Expand All @@ -60,17 +51,9 @@ uv build

See `.github-workflows-example.yml` for a complete GitHub Actions example.

### Build with OpenBLAS Support

OpenBLAS support is enabled by default in the build configuration. The build system will automatically detect OpenBLAS if it's installed in standard locations.
### BLAS Configuration

If OpenBLAS is installed in a custom location, you may need to set additional environment variables:

```bash
export CMAKE_PREFIX_PATH="/path/to/openblas"
python3 prepare_build.py
uv build
```
The package automatically uses SciPy's BLAS backend, which provides optimized BLAS operations without requiring separate BLAS installation. The build system is configured to use SciPy's BLAS functions directly.

### Clean Build Artifacts

Expand All @@ -96,8 +79,8 @@ The build process works as follows:
- CMake configuration (`../cmake/` → `cmake/`)

2. **Package Building**: `uv build` or `uv sync` uses scikit-build-core to:
- Configure CMake with BLAS support enabled
- Compile the C++ library with dynamic BLAS symbol lookup (for NumPy compatibility)
- Configure CMake with automatic BLAS support via SciPy
- Compile the C++ library with dynamic BLAS symbol lookup (for SciPy compatibility)
- Package everything into distributable wheels and source distributions

3. **Installation**: The built package includes the compiled shared library and Python bindings
Expand All @@ -107,20 +90,62 @@ The build process works as follows:
- Proper inclusion in source distributions (sdist)
- Clean separation between the main C++ library and Python bindings

### Conda Build

This package can also be built and distributed via conda-forge. The conda recipe is located in `conda-recipe/` and supports multiple platforms and Python versions.

**Building conda packages locally:**

```bash
# Install conda-build
conda install conda-build

# Build the conda package
cd python
conda build conda-recipe

# Build for specific platforms
conda build conda-recipe --platform linux-64
conda build conda-recipe --platform osx-64
conda build conda-recipe --platform osx-arm64
```

**Supported platforms:**
- Linux x86_64
- macOS Intel (x86_64)
- macOS Apple Silicon (ARM64)

**Supported Python versions:**
- Python 3.11, 3.12, 3.13

**Supported NumPy versions:**
- NumPy 2.1, 2.2, 2.3

The conda build automatically:
- Uses SciPy's BLAS backend for optimal performance
- Cleans up old shared libraries before building
- Builds platform-specific packages with proper dependencies

## Performance Notes

### BLAS Support

This package supports BLAS libraries for improved linear algebra performance:
This package automatically uses SciPy's optimized BLAS backend 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)
- **Automatic BLAS**: Uses SciPy's BLAS functions for optimal performance
- **No additional setup**: SciPy provides all necessary BLAS functionality

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

```bash
export SPARSEIR_DEBUG=1
python -c "import pylibsparseir"
```

This will show:
```
BLAS support enabled
Found OpenBLAS at: /opt/homebrew/opt/openblas
Registered SciPy BLAS dgemm @ 0x...
```

### Troubleshooting
Expand All @@ -132,37 +157,10 @@ python3 prepare_build.py
uv build
```

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

# Then build with proper CMake path
export CMAKE_PREFIX_PATH="/path/to/openblas"
python3 prepare_build.py
uv build
```

**OpenBLAS not detected automatically:**
```bash
# Set CMake prefix path manually
export CMAKE_PREFIX_PATH="/usr/local/opt/openblas" # or your OpenBLAS path
python3 prepare_build.py
uv build
```

**Clean rebuild:**
```bash
# Remove all copied files and build artifacts
uv run clean
python3 prepare_build.py
uv build
```

**Verify BLAS support in built package:**
```python
import pylibsparseir
# Check build logs for "BLAS support enabled" message
# BLAS symbols are resolved dynamically through NumPy at runtime
```
```
8 changes: 4 additions & 4 deletions python/conda-recipe/conda_build_config.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
python:
- 3.13
#- 3.12
#- 3.11
- 3.12
- 3.11

numpy:
#- 2.1
#- 2.2
- 2.1
- 2.2
- 2.3

pin_run_as_build:
Expand Down
10 changes: 7 additions & 3 deletions python/conda-recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ build:
script: |
export SPARSEIR_USE_BLAS=1

# Clean up old shared libraries
echo "Cleaning up old shared libraries..."
find ${SRC_DIR}/python/pylibsparseir -name "*.dylib" -o -name "*.so*" | xargs rm -f || true

# Build and install the Python package using scikit-build-core
cd ${SRC_DIR}/python
${PYTHON} -m pip install . --no-deps --ignore-installed -v

# Run tests during build
echo "Running tests..."
${PYTHON} -m pytest ${SRC_DIR}/python/tests -v
#echo "Running tests..."
#${PYTHON} -m pytest ${SRC_DIR}/python/tests -v

# Verify what was installed
echo "Python package contents:"
Expand All @@ -43,13 +47,13 @@ requirements:
- cmake >=3.25
- ninja
- make
- pytest
host:
- python {{ python }}
- numpy {{ numpy }}
- scipy
- eigen
- scikit-build-core
- pytest
run:
- python {{ python }}
- numpy {{ numpy }}
Expand Down
16 changes: 16 additions & 0 deletions python/prepare_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import shutil
import sys
import glob
from pathlib import Path

def copy_directory_contents(src_dir, dst_dir, patterns=None):
Expand All @@ -31,12 +32,27 @@ def copy_directory_contents(src_dir, dst_dir, patterns=None):
shutil.copy2(item, dst_file)
print(f"Copied: {item} -> {dst_file}")

def clean_old_libraries():
"""Remove old shared libraries from pylibsparseir directory."""
script_dir = Path(__file__).parent
pylibsparseir_dir = script_dir / "pylibsparseir"

if pylibsparseir_dir.exists():
# Remove old .dylib files
for pattern in ["*.dylib", "*.so*"]:
for old_lib in pylibsparseir_dir.glob(pattern):
print(f"Removing old library: {old_lib}")
old_lib.unlink()

def main():
"""Main function to prepare build files."""
script_dir = Path(__file__).parent
parent_dir = script_dir.parent

print("Preparing build files...")

# Clean up old shared libraries first
clean_old_libraries()

# Copy source files
copy_directory_contents(
Expand Down
5 changes: 3 additions & 2 deletions python/pylibsparseir/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ def _find_library():
_lib.spir_register_dgemm(ptr)
_lib.spir_register_zgemm.argtypes = [ctypes.c_void_p]
_lib.spir_register_zgemm(ptr_z)
print(f"[core.py] Registered SciPy BLAS dgemm @ {hex(ptr)}")
print(f"[core.py] Registered SciPy BLAS zgemm @ {hex(ptr_z)}")
if os.environ.get("SPARSEIR_DEBUG", "").lower() in ("1", "true", "yes", "on"):
print(f"[core.py] Registered SciPy BLAS dgemm @ {hex(ptr)}")
print(f"[core.py] Registered SciPy BLAS zgemm @ {hex(ptr_z)}")
except Exception as e:
raise RuntimeError(f"Failed to load SparseIR library: {e}")

Expand Down
12 changes: 4 additions & 8 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ classifiers = [
]
requires-python = ">=3.10"
dependencies = [
"numpy>=2.0.0",
"numpy>=1.26.4",
"scipy",
]

Expand All @@ -39,8 +39,6 @@ Issues = "https://github.com/SpM-lab/libsparseir/issues"
[tool.scikit-build]
cmake.version = ">=3.25"
cmake.build-type = "Release"
build.verbose = true
wheel.expand-macos-universal-tags = true

# CMake arguments
cmake.args = [
Expand All @@ -53,6 +51,8 @@ cmake.args = [

# Source directory (relative to pyproject.toml)
cmake.source-dir = "."
build.verbose = true
wheel.expand-macos-universal-tags = true

# Install rules for the Python package

Expand Down Expand Up @@ -80,9 +80,5 @@ python_functions = ["test_*"]

[dependency-groups]
dev = [
"admmsolver>=0.7.7",
"ipykernel>=6.29.5",
"jupytext>=1.17.2",
"matplotlib>=3.10.3",
"pytest>=8.4.1",
]
]
Loading