Skip to content
Open
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
49 changes: 28 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: CI
name: CI workflow

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

jobs:
build:
Expand All @@ -13,27 +13,34 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.12"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m 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: Test with pytest
env:
PYTHONPATH: ${{ github.workspace }}/src

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Install dependencies and build package using uv
run: |
pytest
uv sync --extra dev
uv build

- name: Run linting with ruff
run: uv run ruff check .

- name: Run formatting check with ruff
run: uv run ruff format --check .

- name: Run type checking with mypy
run: uv run mypy src

- name: Run tests with coverage using pytest
run: uv run pytest
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Generate API documentation
on:
push:
branches: [ "main" ]
branches: ["main"]

jobs:
deploy:
Expand Down
46 changes: 24 additions & 22 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
name: Upload Python Package
name: Publish to PyPI

on:
release:
types: [published]

permissions:
contents: read

jobs:
deploy:

publish:
name: Build and Publish to PyPI
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true
cache-dependency-glob: "uv.lock"

- name: Set up Python
run: uv python install

- name: Install build dependencies
run: uv sync --all-extras

- name: Build the project
run: uv build

- name: Publish to PyPI
run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
49 changes: 34 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/pycqa/pydocstyle
rev: 6.3.0
hooks:
- id: pydocstyle
args: [--convention=google, --add-ignore=D100,D104]
# General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: detect-private-key # Basic security check for secrets

# Python formatting and linting with Ruff
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff-format
- id: ruff
args: [--fix]

# Type checking with mypy
# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: v1.11.2
# hooks:
# - id: mypy

# Documentation linting with pydocstyle
- repo: https://github.com/pycqa/pydocstyle
rev: 6.3.0
hooks:
- id: pydocstyle

# Enforce Conventional Commits in commit messages
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.3.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
41 changes: 25 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# ✨halfspace✨

`halfspace` is an open-source, light-weight Python module for modelling and solving mixed-integer convex optimization problems of the form:
`halfspace` is an open-source, lightweight Python module for modeling and solving mixed-integer convex optimization problems of the form:

$$
\begin{align}
Expand All @@ -21,11 +21,15 @@ It is built on top of the high-performance Python `mip` module and uses a cuttin
This implementation is based on the approach outlined in [Boyd & Vandenberghe (2008)](https://see.stanford.edu/materials/lsocoee364b/05-localization_methods_notes.pdf) - see Chapter 6.


## Quick start
## Installation

You can install `halfspace` using `pip` as follows:
`halfspace` requires Python 3.12:

```bash
# Using uv (recommended)
uv add halfspace-optimizer

# Using pip
pip install halfspace-optimizer
```

Expand Down Expand Up @@ -107,29 +111,34 @@ import logging
logging.getLogger().setLevel(logging.INFO)
```

The logging frequency can be adjusted as desiredusing the model's `log_freq` attribute.
The logging frequency can be adjusted as desired using the model's `log_freq` attribute.

## Development

Clone the repository using `git`:

```bash
# Clone and setup
git clone https://github.com/joshivanhoe/halfspace
````
cd halfspace
uv venv .venv --python 3.12
source .venv/bin/activate
uv sync --extra dev

Create a fresh virtual environment using `venv` or `conda`.
Activate the environment and navigate to the cloned `halfspace` directory.
Install a locally editable version of the package using `pip`:
# Setup pre-commit hooks
uv run pre-commit install
uv run pre-commit install --hook-type commit-msg

```bash
pip install -e .
# Run tests
uv run pytest
```

To check the installation has worked, you can run the tests (with coverage metrics) using `pytest` as follows:
### Pre-commit Hooks

This project uses pre-commit hooks for code quality (Ruff, mypy, pydocstyle, conventional commits). Hooks run automatically on commit, or manually with:

```bash
pytest --cov=halfspace tests/
uv run pre-commit run --all-files
```

Contributions are welcome! To see our development priorities, refer to the [open issues](https://github.com/joshivanhoe/halfspace/issues).
Please submit a pull request with a clear description of the changes you've made.
### Contributing

Contributions welcome! See [open issues](https://github.com/joshivanhoe/halfspace/issues) for priorities. Ensure pre-commit hooks pass before submitting PRs.
76 changes: 66 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,83 @@
[build-system]
requires = ["setuptools >= 61.0"]
build-backend = "setuptools.build_meta"
requires = ["hatchling"]
build-backend = "hatchling.build"


[project]
name = "halfspace-optimizer"
version = "0.1.1"
authors = [
{ name="Joshua Ivanhoe", email="joshua.k.ivanhoe@gmail.com" },
{name="Joshua Ivanhoe", email="joshua.k.ivanhoe@gmail.com"},
]
description = "Cutting-plane solver for mixed-integer convex optimization problems"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">=3.9,<3.12"
requires-python = ">=3.12,<3.13"
keywords = ["optimization", "mixed-integer", "convex", "cutting-plane", "solver"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering :: Mathematics",
]
dependencies = [
"mip>=1.15.0,<2.0.0",
"numpy>=2.3.3,<3.0.0",
"pandas>=2.3.2,<3.0.0",
]
dynamic = ["dependencies"]

[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}

[project.urls]
Homepage = "https://github.com/joshivanhoe/halfspace"
Issues = "https://github.com/joshivanhoe/halfspace/issues"

[project.optional-dependencies]
dev = [
"ruff",
"mypy",
"pre-commit",
"pytest",
"pytest-cov",
]

[tool.ruff]
line-length = 100
exclude = [
"*.ipynb",
]

[tool.ruff.lint]
extend-select = ["D", "I"] # pydocstyle
fixable = ["ALL"]
extend-ignore = [
"D100", # Missing docstring in public module
"D104", # Missing docstring in public package
"D105", # Missing docstring in magic method
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["D"] # no documentation checks required for tests

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.mypy]
ignore_missing_imports = true
install_types = true
non_interactive = true
check_untyped_defs = true

[tool.coverage.run]
source = ["src/halfspace"]
omit = ["*/tests/*"]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
]

[tool.hatch.build.targets.wheel]
packages = ["src/halfspace"]
7 changes: 0 additions & 7 deletions requirements.txt

This file was deleted.

3 changes: 3 additions & 0 deletions src/halfspace/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
"""The `halfspace` module implements a modelling class for mixed-integer convex optimization problems."""

from .model import Model

__all__ = ["Model"]
Loading
Loading