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

on:
pull_request:
branches:
- main
push:
branches:
- "dev-[0-9]*-[0-9]*-[0-9]*"

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ".[dev]"
pip install requests types-requests

- name: Lint with Black
run: black --check qaekwy

- name: Lint with Pylint
run: pylint qaekwy

- name: Type check with MyPy
run: mypy qaekwy

- name: Test with Pytest + coverage
run: |
python -m pytest --cov=qaekwy --cov-fail-under=80 tests
68 changes: 31 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
*A modern, open-source Python framework for declarative constraint programming and combinatorial optimization*.

![GitHub License](https://img.shields.io/github/license/alex-87/qaekwy-python) ![PyPI - Version](https://img.shields.io/pypi/v/qaekwy)

## Overview

Qaekwy is a Python library designed for modeling and solving combinatorial optimization and constraint satisfaction problems.

It provides a clean, Pythonic interface for defining variables, constraints, and objectives, enabling a natural *define-and-solve* workflow. Qaekwy manages the interaction with the solver engine, allowing users to focus entirely on expressing the structure of their problems.

Perfect for:
#### Perfect for

* 🎓 **Learning** — Model real problems in minutes
* 👩‍🏫 **Teaching** — Demonstrate CSP concepts with no setup
* 🔬 **Research & Prototyping** — Explore models, heuristics, and ideas fast

* 🎓 **Learning**: Students can *quickly model* and solve problems.
* 👩‍🏫 **Teaching**: Instructors can *demo core CSP concepts* with **minimal setup**.
* 🌟 **Discovering**: Researchers can *explore* strategies, heuristics, and models.
## 📚 Documentation

Visit the [Qaekwy Documentation](https://docs.qaekwy.io/) for guides, teaching resources, and detailed examples.

## 🚀 Quick Start

Expand All @@ -26,11 +28,11 @@ Perfect for:

### Installation

Install the production-ready client via PyPI:

`pip install qaekwy`
```shell
pip install qaekwy
```

### Your First Model
### 🌱 Your First Model

```python
import qaekwy as qw
Expand All @@ -42,12 +44,9 @@ y = m.integer_variable("y", (-10, 10))
z = m.integer_variable("z", (-10, 10))

m.constraint(x + 2*y + 3*z <= 15)
m.constraint(x + y >= 5)
m.maximize(x)

m.minimize(z)

solution = m.solve_one()
solution.pretty_print()
m.solve_one(searcher="bab").pretty_print()
```

*Output*:
Expand All @@ -56,14 +55,12 @@ solution.pretty_print()
----------------------------------------
Solution:
----------------------------------------
x: 6
y: 8
z: -3
x: 10
y: 2
z: -4
----------------------------------------
```

*That's it.*


## Capabilities

Expand All @@ -79,16 +76,14 @@ Configure solver behavior using explicit search strategies such as Depth-First S
Transparent handling of model serialization and execution on the Qaekwy Cloud Solver instance.


## 📚 Documentation

Visit the [Qaekwy Documentation](https://docs.qaekwy.io/) for guides, teaching resources, and detailed examples.

## Examples

---
### 🔢 Constraint Programming -- Sudoku

### 🧩 Constraint Programming Example: Sudoku
Here is a complete example solving a [Sudoku](https://en.wikipedia.org/wiki/Sudoku) grid:

Here is a complete example solving a Sudoku grid:
> The objective is to fill a 9 × 9 grid with digits so that each column, each row, and each
> of the nine 3 × 3 subgrids that compose the grid contains all of the digits from 1 to 9.

```python
import qaekwy as qw
Expand All @@ -108,7 +103,7 @@ my_problem = [
[0, 2, 0, 0, 0, 0, 5, 7, 0]
]

# Initialize the model container
# Instantiate the model
m = qw.Model()

# Create a 9x9 matrix of integer variables
Expand All @@ -122,7 +117,7 @@ for i in range(9):
# Ensure all variables in column 'i' are unique
m.constraint_distinct(grid.col(i))

# Iterate in steps of 3 (0, 3, 6) to find the top-left corner of each block
# Iterate over 3x3 blocks
for i in range(0, 9, 3):
for j in range(0, 9, 3):
# Extract the 3x3 block and enforce uniqueness
Expand Down Expand Up @@ -161,7 +156,7 @@ grid: (9 x 9 matrix)
----------------------------------------
```

### 🎒 Optimization Example: Knapsack Problem
### 🎒 Optimization -- Knapsack Problem

Here is a complete example solving a basic resource allocation problem ([The Knapsack Problem](https://en.wikipedia.org/wiki/Knapsack_problem)):

Expand Down Expand Up @@ -207,18 +202,18 @@ print(f"Max Value: {solution.total_value}")
# Output: Max Value: 9
```

#### 💡 Core Concepts
## 💡 Core Concepts

<p align="center">
<img alt="Qaekwy core concept" src="https://qaekwy.io/q1.png" width="50%">
</p>

##### The Model
### The Model

The `qw.Model` acts as the container for your variables and constraints. It also manages the interaction with
the underlying solver engine.

##### The Variables
#### The Variables

Here are examples of variable creation in the model:

Expand All @@ -230,7 +225,7 @@ capacity = m.integer_variable("capacity", domain=(0, 100))
grid = m.integer_matrix("grid", rows=9, cols=9, domain=(1, 9))
```

##### The Constraints
#### The Constraints

Constraints are logical assertions that must be true in any valid solution.

Expand All @@ -239,7 +234,7 @@ Constraints are logical assertions that must be true in any valid solution.
m.constraint(x * 2 < qw.math.power(y, 2) + 5)
```

#### Modeling Capabilities
### Modeling Capabilities

Qaekwy supports:

Expand Down Expand Up @@ -278,9 +273,9 @@ m.constraint(sum(mat.col(0)) > arr[2])

- `solve_one()` — find one feasible or optimal solution
- `solve()` — returns a list of solutions
- minimize(...) / maximize(...) — Set one or more objectives on variables
- `minimize(...)` / `maximize(...)` — Set one or more objectives on variables
- Searchers such as DFS, Branch-and-Bound, etc.
- Cloud-based Solver instance (please, refer to [Terms & Conditions](https://docs.qaekwy.io/docs/terms-and-conditions/))
- Cloud-based Solver instance (*please, refer to [Terms & Conditions](https://docs.qaekwy.io/docs/terms-and-conditions/)*)


#### Integration
Expand All @@ -291,7 +286,6 @@ The model is then sent to the Qaekwy Cloud Engine through REST API.
<img alt="Qaekwy Integration" src="https://qaekwy.io/q2.png" width="50%">
</p>

---

## License

Expand Down
68 changes: 68 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "qaekwy"
dynamic = ["version"]
description = "Qaekwy, a modern, open-source Python framework for declarative constraint programming and combinatorial optimization"
readme = "README.md"
requires-python = ">=3.9"
license = "EUPL-1.2"
authors = [
{name = "Alexis LE GOADEC", email = "alex@qaekwy.io"},
]
keywords = [
"optimization", "constraint programming", "combinatorial optimization",
"operations research", "constraint satisfaction", "constraint solver",
"solver", "CSP", "optimization library", "optimization framework",
"mathematical optimization", "discrete optimization", "optimization modeling",
"constraint modeling", "declarative programming", "modeling language",
"DSL", "define-and-solve", "scheduling", "routing", "planning",
"resource allocation", "assignment", "decision support", "open source", "Python"
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: Science/Research",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"requests"
]

[project.optional-dependencies]
types = [
"types-requests",
]
dev = [
"pytest>=7.0",
"pytest-cov",
"black",
"mypy",
"pylint",
"build",
"twine",
]

[project.urls]
Homepage = "https://qaekwy.io"
Documentation = "https://docs.qaekwy.io"
"Issue Tracker" = "https://github.com/alex-87/qaekwy-python/issues"
Source = "https://github.com/alex-87/qaekwy-python"

[tool.setuptools.dynamic]
version = {attr = "qaekwy.__metadata__.__version__"}

[tool.setuptools.packages.find]
where = ["."]
include = ["qaekwy*"]

[tool.pylint]
disable = ["R0801", "R0917", "R0913", "R0902", "R0903", "C0301", "R0911", "R0912"]
27 changes: 13 additions & 14 deletions qaekwy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
"""QAekwy API Module Initialization."""

from qaekwy.api.model import Model
from qaekwy.api.exceptions import SolverError
from qaekwy.core.model.variable.branch import (
BranchBooleanVal,
BranchBooleanVar,
BranchIntegerVal,
BranchIntegerVar,
BranchFloatVal,
BranchFloatVar,
)
from qaekwy.core.model.cutoff import (
from .api.exceptions import SolverError
from .api.model import Model
from .core.model import function as math
from .core.model.cutoff import (
Cutoff,
CutoffConstant,
CutoffFibonacci,
CutoffGeometric,
CutoffLinear,
CutoffLuby,
CutoffGeometric,
CutoffRandom,
MetaCutoffAppender,
MetaCutoffMerger,
MetaCutoffRepeater,
)

import qaekwy.core.model.function as math
from .core.model.variable.branch import (
BranchBooleanVal,
BranchBooleanVar,
BranchFloatVal,
BranchFloatVar,
BranchIntegerVal,
BranchIntegerVar,
)

__all__ = [
"Model",
Expand Down
4 changes: 2 additions & 2 deletions qaekwy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import argparse

from qaekwy.__metadata__ import (
from .__metadata__ import (
__author__,
__copyright__,
__license__,
Expand All @@ -29,5 +29,5 @@
print(f"Licensed under the {__license__}")
print(f"You may obtain a copy of the License at {__license_url__}")
print(
"You are free to use, modify, and redistribute this work under the terms of this licence."
"You are free to use, modify, and redistribute this work under the terms of this license."
)
2 changes: 1 addition & 1 deletion qaekwy/__metadata__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
__license_url__ = "https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12"
__software__ = "Qaekwy"
__author_email__ = "alex@qaekwy.io"
__version__ = "0.3.0"
__version__ = "0.3.1"
Loading