Skip to content

Commit

Permalink
Pip package
Browse files Browse the repository at this point in the history
  • Loading branch information
gaetanserre committed Nov 15, 2024
1 parent de99d1d commit f57864b
Show file tree
Hide file tree
Showing 20 changed files with 8,928 additions and 221 deletions.
23 changes: 9 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ jobs:
python -m pip install --upgrade pip
# Runs a single command using the runners shell
- name: Build GKLS
- name: Install gkls
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/.local/lib
sh build_and_install.sh
pip install .
python3 -c "import gkls"
- name: Upload artifact
uses: actions/upload-artifact@master
with:
name: linux-artifact
path: |
pygkls/libpygkls.so
pygkls/pygkls*.so
build/**/*.so
build-arm-macos:
runs-on: macos-latest
Expand All @@ -45,15 +44,12 @@ jobs:

- name: Install dependencies
run: |
brew uninstall python --ignore-dependencies
brew cleanup python
python -m pip install --upgrade pip
python -m pip install pybind11
- name: Build GKLS
run: |
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$HOME/.local/lib
sh build_and_install.sh
pip install .
python3 -c "import gkls"
- name: Download artifact
uses: actions/download-artifact@master
Expand All @@ -64,8 +60,7 @@ jobs:
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
body: "Please, make sure to put `pygkls*.so` in the Python package directory and `libpygkls.{so, dylib}` in the dinamyc library directory."
body: "Release ${{ github.ref }}"
files: |
*.so
pygkls/*.so
pygkls/*.dylib
build/**/*.so
build/**/*.dylib
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.30)
project(gkls)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_FLAGS "-fPIC -O3")

find_package(Python REQUIRED COMPONENTS Development)

file(GLOB_RECURSE SRC_FILES src/*.cc)

include_directories(include ${Python_INCLUDE_DIRS})

add_library(${EXT_NAME} SHARED ${SRC_FILES} ${CYTHON_CPP_FILE})

link_libraries(${EXT_NAME} ${Python_LIBRARIES})

add_executable(example src/example.cc)
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
## pyGKLS

pyGKLS is a Python wrapper for the GKLS generator of global optimization test functions ([Giavano et al., 2003](https://dl.acm.org/doi/10.1145/962437.962444)). It uses the original C implementation of the generator and provides a Python interface using Cython to generate the test functions. pyGKLS also encompass a C++ wrapper around the original C implementation to provide a more user-friendly interface that can be used in C++ projects (see `pygkls/src/example.cc`).
pyGKLS is a Python wrapper for the GKLS generator of global optimization test functions ([Giavano et al., 2003](https://dl.acm.org/doi/10.1145/962437.962444)). It uses the original C implementation of the generator and provides a Python interface using Cython to generate the test functions. pyGKLS also encompass a C++ wrapper around the original C implementation to provide a more user-friendly interface that can be used in C++ projects (see `src/example.cc`).

### Random number generator
The original GKLS generator uses a random number generator based introduced by Knuth in his book "The Art of Computer Programming". pyGKLS uses the Mersenne Twister random number generator from the C++ standard library to generate random numbers.

### Installation
To install pyGKLS, one need `Python 3.12` or later, `CMake 3.28` or later, and a C++ compiler that supports C++20. One also need to have `python3-dev` installed. Then clone the repository and run the following commands:
To install pyGKLS, one need `Python 3.12` or later, `CMake 3.28` or later, and a C++ compiler that supports C++23. Then clone the repository and run the following commands:
```bash
./build_and_install.sh
pip install .
```
This will build the C++ dynamic library and the Cython package. Then, it will copy the Cython package to the Python site-packages directory and the shared library to `$HOME/.local/lib`. Make sure that `LD_LIBRARY_PATH` (or `DYLD_LIBRARY_PATH` for macOS) includes `$HOME/.local/lib`.
This will build the C++ dynamic library and the Cython package.

### Usage
The Python interface is simple and easy to use. Here is an example of how to generate a GKLS function:
```python
import pygkls
import gkls

pygkls.init()
gkls.init()

x = [0.5, 0.5]

print(f"D_f = {pygkls.get_d_f(x)}")
print(f"D2_f = {pygkls.get_d2_f(x)}")
print(f"ND_f = {pygkls.get_nd_f(x)}")
print(f"D_f = {gkls.get_d_f(x)}")
print(f"D2_f = {gkls.get_d2_f(x)}")
print(f"ND_f = {gkls.get_nd_f(x)}")

print(f"D_grad = {pygkls.get_d_grad(x)}")
print(f"D2_grad = {pygkls.get_d2_grad(x)}")
print(f"D_grad = {gkls.get_d_grad(x)}")
print(f"D2_grad = {gkls.get_d2_grad(x)}")

print(f"D2_hessian = {pygkls.get_d2_hess(x)}")
print(f"D2_hessian = {gkls.get_d2_hess(x)}")
```
Arguments can be passed to the `init` function to control the properties of the generated function. The `init` function has the following signature:
```python
Expand Down
63 changes: 63 additions & 0 deletions extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import os
import platform
import shutil
from pathlib import Path
from subprocess import check_call

from setuptools import Extension
from setuptools.command.build_ext import build_ext


def create_directory(path: Path):
if path.exists():
shutil.rmtree(path)
path.mkdir(exist_ok=True, parents=True)
return path


class GKLSBuildExtension(Extension):
def __init__(self, name: str, version: str):
super().__init__(name, sources=[])
# Source dir should be at the root directory
self.source_dir = Path(__file__).parent.absolute()
self.version = version


class GKLSBuild(build_ext):
def run(self):
try:
check_call(["cmake", "--version"])
except OSError:
raise RuntimeError("CMake must be installed")

if platform.system() not in ("Windows", "Linux", "Darwin"):
raise RuntimeError(f"Unsupported os: {platform.system()}")

for ext in self.extensions:
if isinstance(ext, GKLSBuildExtension):
self.build_extension(ext)

@property
def config(self):
return "Debug" if self.debug else "Release"

def build_extension(self, ext: Extension):
ext_dir = Path(self.get_ext_fullpath(ext.name)).parent.absolute()
create_directory(ext_dir)

pkg_name = "gkls"
ext_suffix = os.popen("python3-config --extension-suffix").read().strip()
lib_name = pkg_name + ext_suffix
ext_name = ".".join((lib_name).split(".")[:-1])
lib_ext_dir = Path(f"{ext_dir}/{lib_name}")

# Compile the Cython file
os.system(f"cython --cplus -3 {pkg_name}.pyx -o {pkg_name}.cc")

# Compile the C++ files
os.system(
"cd build "
f"&& cmake -DEXT_NAME={ext_name} -DCYTHON_CPP_FILE={pkg_name}.cc .. "
"&& make -j "
f"&& mv lib{lib_name} {lib_ext_dir} "
)
Loading

0 comments on commit f57864b

Please sign in to comment.