Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 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
37 changes: 37 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test Python-bindings on Linux

on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.12"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: build and install
run: |
pip install --verbose .
- name: Test with pytest
run: |
pytest -s test
142 changes: 141 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,144 @@ instances/mps/ex1010-pi.mps
TODO.md
perf.data*
bench.sh
coverage
coverage

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# skbuild_conan
_skbuild/
.conan/
**/CMakeUserPresets.json

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

24 changes: 24 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ add_executable(accft ${SOURCE})
set(LIBRARIES fmt::fmt pthread dl m)
target_link_libraries(accft PUBLIC ${LIBRARIES})

if (SKBUILD)
set(PYTHON_BINDINGS ON)
endif()

if (PYTHON_BINDINGS)
message(STATUS "PYTHON_BINDINGS: ${PYTHON_BINDINGS}")

# PyBind11
FetchContent_Declare(pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git GIT_TAG v2.13.6)
FetchContent_MakeAvailable(pybind11) # pybind11, essential
set(CMAKE_POSITION_INDEPENDENT_CODE ON) # The code needs to be compiled as PIC
# to build the shared lib for python.
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# Ensure fmt is compiled with -fPIC
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)

pybind11_add_module(_bindings ./src/pyaccft/_bindings.cpp)
target_link_libraries(_bindings PUBLIC fmt::fmt ${LIBRARIES})
# enable compilation warnings
target_compile_options(
_bindings PRIVATE "$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wall>")
target_compile_definitions(_bindings PRIVATE PYBIND11_DETAILED_ERROR_MESSAGES)
install(TARGETS _bindings DESTINATION ./src/pyaccft/)
endif()

########################################
############## Unit tests ##############
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ _Note: for rail2586, better results can be obtained emphasizing multipliers qual

The results for the other datasets can be found in the [`benchmarks`](benchmarks/) directory.


## Python Bindings

The project also provides Python bindings with a simplified interface for quick usage.
While you can also access much of the C++-interface directly, the primary purpose is to provide a point and shoot interface for the algorithm.
You can find more details in the separate [README](README.py.md).

## Coding Style

Regarding the source code, here you can find the general set of rules that we try to enforce. Since this is a project we work on in our free time, we haven't been afraid to experiment with simple rules and convention that we adjusted along the way when we felt something wasn't working for us.
Expand Down
60 changes: 60 additions & 0 deletions README.py.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!--
SPDX-FileCopyrightText: 2025 Dominik Krupke <krupked@gmail.com>
SPDX-License-Identifier: MIT
-->

# Python Bindings for the AC-CFT Set Cover Heuristic



## Install

We will publish the package on PyPI soon. For now, you can install the package by cloning the repository and running the following command in the root directory:

```bash
pip install --verbose .
```

## Usage

To use the `SetCoverSolver`, first create an instance of the solver. You can then add sets with their respective costs and solve the set cover problem. The solver will find the optimal selection of sets that covers all elements at the minimum cost.

Here is an example:

```python
from pyaccft import SetCoverSolver

solver = SetCoverSolver()
# Add sets with their respective costs
solver.add_set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], cost=10)
solver.add_set([0, 1, 2, 3, 4, 5], cost=5)
solver.add_set([0, 1, 2, 3, 4], cost=4)
solver.add_set([6, 7, 8, 9], cost=4)

# Solve the set cover problem
solver.solve()

# Retrieve and print the solution
solution = solver.get_solution()
print("The following sets have been selected:", solution)
print("The cost of the solution is:", solver.get_cost())
print("The lower bound of the solution is:", solver.get_lower_bound())
```

Ensure that all elements are zero-indexed and that every element between 0 and the maximum element appears in at least one set for a feasible solution.

## Configuration

You can configure the solver by setting the following parameters in `solve`:

- seed (int): Seed for the random number generator. Default is 0.
- time_limit (int): Time limit in seconds. Default is 0 (no limit).
- verbose (int): Verbosity level. Default is 2.
- epsilon (float): Epsilon value for objective comparisons. Default is 0.999.
- heur_iters (int): Number of iterations for the heuristic phase. Default is 250.
- alpha (float): Relative fixing fraction increment. Default is 1.1.
- beta (float): Relative cutoff value to terminate Refinement. Default is 1.0.
- abs_subgrad_exit (float): Minimum LBs delta to trigger subgradient termination. Default is 1.0.
- rel_subgrad_exit (float): Minimum LBs gap to trigger subgradient termination. Default is 0.001.
- use_unit_costs (bool): Solve the given instance setting columns cost to one. Default is False.

11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2025 Dominik Krupke <krupked@gmail.com>
# SPDX-License-Identifier: MIT

[build-system]
requires = [
"setuptools",
"scikit-build>=0.17.3",
"cmake>=3.23",
"ninja",
]
build-backend = "setuptools.build_meta"
33 changes: 33 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2025 Dominik Krupke <krupked@gmail.com>
# SPDX-License-Identifier: MIT

from pathlib import Path

from setuptools import find_packages
from skbuild import setup


def readme():
"""
:return: Content of README.md
"""

with Path("README.py.md").open() as file:
return file.read()


setup( # https://scikit-build.readthedocs.io/en/latest/usage.html#setup-options
name="pyaccft",
version="0.0.1",
author="TODO",
license="LICENSE",
description="Pybinding for accft",
long_description=readme(),
long_description_content_type="text/markdown",
packages=find_packages("src"), # Include all packages in `./src`.
package_dir={"": "src"}, # The root for our python package is in `./src`.
python_requires=">=3.10", # lowest python version supported.
install_requires=[ # Python Dependencies
],
cmake_minimum_required_version="3.23",
)
Loading