solvor 0.3.2__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solvor-0.3.4/.github/workflows/ci.yml +209 -0
- {solvor-0.3.2 → solvor-0.3.4}/CONTRIBUTING.md +20 -11
- {solvor-0.3.2 → solvor-0.3.4}/LICENSE +1 -1
- {solvor-0.3.2 → solvor-0.3.4}/PKG-INFO +45 -22
- {solvor-0.3.2 → solvor-0.3.4}/README.md +40 -18
- {solvor-0.3.2 → solvor-0.3.4}/pyproject.toml +6 -5
- {solvor-0.3.2 → solvor-0.3.4}/solvor/__init__.py +2 -0
- solvor-0.3.4/solvor/anneal.py +75 -0
- {solvor-0.3.2 → solvor-0.3.4}/solvor/bayesian.py +20 -30
- {solvor-0.3.2 → solvor-0.3.4}/solvor/cp.py +16 -28
- solvor-0.3.4/solvor/dlx.py +249 -0
- {solvor-0.3.2 → solvor-0.3.4}/solvor/flow.py +24 -26
- {solvor-0.3.2 → solvor-0.3.4}/solvor/genetic.py +27 -33
- {solvor-0.3.2 → solvor-0.3.4}/solvor/gradient.py +27 -30
- {solvor-0.3.2 → solvor-0.3.4}/solvor/milp.py +21 -28
- {solvor-0.3.2 → solvor-0.3.4}/solvor/sat.py +26 -30
- {solvor-0.3.2 → solvor-0.3.4}/solvor/simplex.py +17 -25
- {solvor-0.3.2 → solvor-0.3.4}/solvor/tabu.py +24 -28
- solvor-0.3.4/tests/test_anneal.py +177 -0
- solvor-0.3.4/tests/test_bayesian.py +182 -0
- solvor-0.3.4/tests/test_cp.py +181 -0
- solvor-0.3.4/tests/test_dlx.py +282 -0
- solvor-0.3.4/tests/test_flow.py +244 -0
- solvor-0.3.4/tests/test_genetic.py +207 -0
- solvor-0.3.4/tests/test_gradient.py +207 -0
- solvor-0.3.4/tests/test_milp.py +120 -0
- solvor-0.3.4/tests/test_sat.py +187 -0
- solvor-0.3.4/tests/test_simplex.py +131 -0
- solvor-0.3.4/tests/test_tabu.py +191 -0
- solvor-0.3.2/.github/workflows/ci.yml +0 -25
- solvor-0.3.2/solvor/anneal.py +0 -80
- solvor-0.3.2/tests/test_solvers.py +0 -316
- {solvor-0.3.2 → solvor-0.3.4}/.github/workflows/publish.yml +0 -0
- {solvor-0.3.2 → solvor-0.3.4}/.gitignore +0 -0
- {solvor-0.3.2 → solvor-0.3.4}/solvor/types.py +0 -0
- {solvor-0.3.2 → solvor-0.3.4}/tests/__init__.py +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, dev]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v4
|
|
17
|
+
|
|
18
|
+
- name: Install dependencies
|
|
19
|
+
run: uv sync --extra dev
|
|
20
|
+
|
|
21
|
+
- name: Run linter
|
|
22
|
+
run: uv run ruff check solvor/
|
|
23
|
+
|
|
24
|
+
# Detect which solvers have changed
|
|
25
|
+
changes:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
outputs:
|
|
28
|
+
simplex: ${{ steps.filter.outputs.simplex }}
|
|
29
|
+
milp: ${{ steps.filter.outputs.milp }}
|
|
30
|
+
anneal: ${{ steps.filter.outputs.anneal }}
|
|
31
|
+
tabu: ${{ steps.filter.outputs.tabu }}
|
|
32
|
+
sat: ${{ steps.filter.outputs.sat }}
|
|
33
|
+
cp: ${{ steps.filter.outputs.cp }}
|
|
34
|
+
bayesian: ${{ steps.filter.outputs.bayesian }}
|
|
35
|
+
genetic: ${{ steps.filter.outputs.genetic }}
|
|
36
|
+
flow: ${{ steps.filter.outputs.flow }}
|
|
37
|
+
gradient: ${{ steps.filter.outputs.gradient }}
|
|
38
|
+
dlx: ${{ steps.filter.outputs.dlx }}
|
|
39
|
+
types: ${{ steps.filter.outputs.types }}
|
|
40
|
+
tests: ${{ steps.filter.outputs.tests }}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: dorny/paths-filter@v3
|
|
44
|
+
id: filter
|
|
45
|
+
with:
|
|
46
|
+
filters: |
|
|
47
|
+
simplex:
|
|
48
|
+
- 'solvor/simplex.py'
|
|
49
|
+
- 'tests/test_simplex.py'
|
|
50
|
+
milp:
|
|
51
|
+
- 'solvor/milp.py'
|
|
52
|
+
- 'solvor/simplex.py'
|
|
53
|
+
- 'tests/test_milp.py'
|
|
54
|
+
anneal:
|
|
55
|
+
- 'solvor/anneal.py'
|
|
56
|
+
- 'tests/test_anneal.py'
|
|
57
|
+
tabu:
|
|
58
|
+
- 'solvor/tabu.py'
|
|
59
|
+
- 'tests/test_tabu.py'
|
|
60
|
+
sat:
|
|
61
|
+
- 'solvor/sat.py'
|
|
62
|
+
- 'tests/test_sat.py'
|
|
63
|
+
cp:
|
|
64
|
+
- 'solvor/cp.py'
|
|
65
|
+
- 'solvor/sat.py'
|
|
66
|
+
- 'tests/test_cp.py'
|
|
67
|
+
bayesian:
|
|
68
|
+
- 'solvor/bayesian.py'
|
|
69
|
+
- 'tests/test_bayesian.py'
|
|
70
|
+
genetic:
|
|
71
|
+
- 'solvor/genetic.py'
|
|
72
|
+
- 'tests/test_genetic.py'
|
|
73
|
+
flow:
|
|
74
|
+
- 'solvor/flow.py'
|
|
75
|
+
- 'tests/test_flow.py'
|
|
76
|
+
gradient:
|
|
77
|
+
- 'solvor/gradient.py'
|
|
78
|
+
- 'tests/test_gradient.py'
|
|
79
|
+
dlx:
|
|
80
|
+
- 'solvor/dlx.py'
|
|
81
|
+
- 'tests/test_dlx.py'
|
|
82
|
+
types:
|
|
83
|
+
- 'solvor/types.py'
|
|
84
|
+
- 'solvor/__init__.py'
|
|
85
|
+
tests:
|
|
86
|
+
- 'tests/**'
|
|
87
|
+
- 'pyproject.toml'
|
|
88
|
+
|
|
89
|
+
# Run tests for changed solvers only
|
|
90
|
+
test-simplex:
|
|
91
|
+
needs: changes
|
|
92
|
+
if: ${{ needs.changes.outputs.simplex == 'true' || needs.changes.outputs.types == 'true' }}
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/checkout@v4
|
|
96
|
+
- uses: astral-sh/setup-uv@v4
|
|
97
|
+
- run: uv sync --extra dev
|
|
98
|
+
- run: uv run pytest tests/test_simplex.py -v
|
|
99
|
+
|
|
100
|
+
test-milp:
|
|
101
|
+
needs: changes
|
|
102
|
+
if: ${{ needs.changes.outputs.milp == 'true' || needs.changes.outputs.types == 'true' }}
|
|
103
|
+
runs-on: ubuntu-latest
|
|
104
|
+
steps:
|
|
105
|
+
- uses: actions/checkout@v4
|
|
106
|
+
- uses: astral-sh/setup-uv@v4
|
|
107
|
+
- run: uv sync --extra dev
|
|
108
|
+
- run: uv run pytest tests/test_milp.py -v
|
|
109
|
+
|
|
110
|
+
test-anneal:
|
|
111
|
+
needs: changes
|
|
112
|
+
if: ${{ needs.changes.outputs.anneal == 'true' || needs.changes.outputs.types == 'true' }}
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v4
|
|
116
|
+
- uses: astral-sh/setup-uv@v4
|
|
117
|
+
- run: uv sync --extra dev
|
|
118
|
+
- run: uv run pytest tests/test_anneal.py -v
|
|
119
|
+
|
|
120
|
+
test-tabu:
|
|
121
|
+
needs: changes
|
|
122
|
+
if: ${{ needs.changes.outputs.tabu == 'true' || needs.changes.outputs.types == 'true' }}
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v4
|
|
126
|
+
- uses: astral-sh/setup-uv@v4
|
|
127
|
+
- run: uv sync --extra dev
|
|
128
|
+
- run: uv run pytest tests/test_tabu.py -v
|
|
129
|
+
|
|
130
|
+
test-sat:
|
|
131
|
+
needs: changes
|
|
132
|
+
if: ${{ needs.changes.outputs.sat == 'true' || needs.changes.outputs.types == 'true' }}
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
steps:
|
|
135
|
+
- uses: actions/checkout@v4
|
|
136
|
+
- uses: astral-sh/setup-uv@v4
|
|
137
|
+
- run: uv sync --extra dev
|
|
138
|
+
- run: uv run pytest tests/test_sat.py -v
|
|
139
|
+
|
|
140
|
+
test-cp:
|
|
141
|
+
needs: changes
|
|
142
|
+
if: ${{ needs.changes.outputs.cp == 'true' || needs.changes.outputs.types == 'true' }}
|
|
143
|
+
runs-on: ubuntu-latest
|
|
144
|
+
steps:
|
|
145
|
+
- uses: actions/checkout@v4
|
|
146
|
+
- uses: astral-sh/setup-uv@v4
|
|
147
|
+
- run: uv sync --extra dev
|
|
148
|
+
- run: uv run pytest tests/test_cp.py -v
|
|
149
|
+
|
|
150
|
+
test-bayesian:
|
|
151
|
+
needs: changes
|
|
152
|
+
if: ${{ needs.changes.outputs.bayesian == 'true' || needs.changes.outputs.types == 'true' }}
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
steps:
|
|
155
|
+
- uses: actions/checkout@v4
|
|
156
|
+
- uses: astral-sh/setup-uv@v4
|
|
157
|
+
- run: uv sync --extra dev
|
|
158
|
+
- run: uv run pytest tests/test_bayesian.py -v
|
|
159
|
+
|
|
160
|
+
test-genetic:
|
|
161
|
+
needs: changes
|
|
162
|
+
if: ${{ needs.changes.outputs.genetic == 'true' || needs.changes.outputs.types == 'true' }}
|
|
163
|
+
runs-on: ubuntu-latest
|
|
164
|
+
steps:
|
|
165
|
+
- uses: actions/checkout@v4
|
|
166
|
+
- uses: astral-sh/setup-uv@v4
|
|
167
|
+
- run: uv sync --extra dev
|
|
168
|
+
- run: uv run pytest tests/test_genetic.py -v
|
|
169
|
+
|
|
170
|
+
test-flow:
|
|
171
|
+
needs: changes
|
|
172
|
+
if: ${{ needs.changes.outputs.flow == 'true' || needs.changes.outputs.types == 'true' }}
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
steps:
|
|
175
|
+
- uses: actions/checkout@v4
|
|
176
|
+
- uses: astral-sh/setup-uv@v4
|
|
177
|
+
- run: uv sync --extra dev
|
|
178
|
+
- run: uv run pytest tests/test_flow.py -v
|
|
179
|
+
|
|
180
|
+
test-gradient:
|
|
181
|
+
needs: changes
|
|
182
|
+
if: ${{ needs.changes.outputs.gradient == 'true' || needs.changes.outputs.types == 'true' }}
|
|
183
|
+
runs-on: ubuntu-latest
|
|
184
|
+
steps:
|
|
185
|
+
- uses: actions/checkout@v4
|
|
186
|
+
- uses: astral-sh/setup-uv@v4
|
|
187
|
+
- run: uv sync --extra dev
|
|
188
|
+
- run: uv run pytest tests/test_gradient.py -v
|
|
189
|
+
|
|
190
|
+
test-dlx:
|
|
191
|
+
needs: changes
|
|
192
|
+
if: ${{ needs.changes.outputs.dlx == 'true' || needs.changes.outputs.types == 'true' }}
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
steps:
|
|
195
|
+
- uses: actions/checkout@v4
|
|
196
|
+
- uses: astral-sh/setup-uv@v4
|
|
197
|
+
- run: uv sync --extra dev
|
|
198
|
+
- run: uv run pytest tests/test_dlx.py -v
|
|
199
|
+
|
|
200
|
+
# Full test suite on main branch or when test infrastructure changes
|
|
201
|
+
test-all:
|
|
202
|
+
needs: changes
|
|
203
|
+
if: ${{ github.ref == 'refs/heads/main' || needs.changes.outputs.tests == 'true' }}
|
|
204
|
+
runs-on: ubuntu-latest
|
|
205
|
+
steps:
|
|
206
|
+
- uses: actions/checkout@v4
|
|
207
|
+
- uses: astral-sh/setup-uv@v4
|
|
208
|
+
- run: uv sync --extra dev
|
|
209
|
+
- run: uv run pytest tests/ -v
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Contributing to
|
|
1
|
+
# Contributing to solvOR
|
|
2
2
|
|
|
3
|
-
Thanks for your interest in contributing to
|
|
3
|
+
Thanks for your interest in contributing to solvor!
|
|
4
4
|
|
|
5
5
|
## Development Setup
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ uv sync --extra dev
|
|
|
16
16
|
uv run pytest
|
|
17
17
|
|
|
18
18
|
# Run linter
|
|
19
|
-
uv run ruff check
|
|
19
|
+
uv run ruff check solvor/
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Code Style
|
|
@@ -33,7 +33,7 @@ Follow the project's style:
|
|
|
33
33
|
|
|
34
34
|
## Terminology
|
|
35
35
|
|
|
36
|
-
Use consistent naming across all
|
|
36
|
+
Use consistent naming across all solvors:
|
|
37
37
|
|
|
38
38
|
| Term | Usage |
|
|
39
39
|
|------|-------|
|
|
@@ -44,26 +44,35 @@ Use consistent naming across all solvers:
|
|
|
44
44
|
| `neighbors` | Adjacent solutions (metaheuristics) |
|
|
45
45
|
| `minimize` | Boolean flag (default `True`) |
|
|
46
46
|
|
|
47
|
-
## Adding a New
|
|
47
|
+
## Adding a New Solvor
|
|
48
48
|
|
|
49
|
-
1. Create `
|
|
49
|
+
1. Create `solvor/<solver_name>.py`
|
|
50
50
|
2. Import shared types: `from solvor.types import Status, Result`
|
|
51
51
|
3. Export `Status`, `Result`, and main solver function in `__all__`
|
|
52
|
-
4. Add exports to `
|
|
53
|
-
5.
|
|
52
|
+
4. Add exports to `solvor/__init__.py`
|
|
53
|
+
5. Create `tests/test_<solver_name>.py` with comprehensive tests
|
|
54
54
|
6. Update `README.md` with usage examples
|
|
55
|
+
7. Add the solver to `.github/workflows/ci.yml` path filters
|
|
55
56
|
|
|
56
57
|
## Testing
|
|
57
58
|
|
|
58
|
-
|
|
59
|
+
Each solver has its own test file (`tests/test_<solver_name>.py`). Tests should cover:
|
|
59
60
|
- Basic functionality
|
|
60
|
-
- Edge cases (empty input, infeasible, etc.)
|
|
61
|
+
- Edge cases (empty input, infeasible, single variable, etc.)
|
|
61
62
|
- Minimize and maximize modes
|
|
63
|
+
- Parameter variations
|
|
64
|
+
- Stress tests with larger inputs
|
|
62
65
|
|
|
63
66
|
```bash
|
|
67
|
+
# Run all tests
|
|
64
68
|
uv run pytest tests/ -v
|
|
69
|
+
|
|
70
|
+
# Run tests for a specific solver
|
|
71
|
+
uv run pytest tests/test_simplex.py -v
|
|
65
72
|
```
|
|
66
73
|
|
|
74
|
+
The CI runs tests selectively based on which files changed - only the affected solver's tests run.
|
|
75
|
+
|
|
67
76
|
## Pull Requests
|
|
68
77
|
|
|
69
78
|
1. Fork the repo
|
|
@@ -77,4 +86,4 @@ uv run pytest tests/ -v
|
|
|
77
86
|
1. Usable > perfect
|
|
78
87
|
2. Readable > clever
|
|
79
88
|
3. Simple > general
|
|
80
|
-
4. Fast, but understandable
|
|
89
|
+
4. Fast, but understandable
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright [
|
|
189
|
+
Copyright [2025] [S.T. Grond]
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solvor
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Solvor your optimization needs.
|
|
5
5
|
Project-URL: Repository, https://github.com/StevenBtw/solvor
|
|
6
|
+
Project-URL: PyPI, https://pypi.org/project/solvor
|
|
6
7
|
Author: Steven Bytheway
|
|
7
|
-
License:
|
|
8
|
+
License: Apache-2.0
|
|
8
9
|
License-File: LICENSE
|
|
9
10
|
Keywords: linear-programming,metaheuristics,operations-research,optimization,sat,solver
|
|
10
11
|
Classifier: Development Status :: 3 - Alpha
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved ::
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
15
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
15
16
|
Requires-Python: >=3.14
|
|
16
17
|
Provides-Extra: dev
|
|
17
18
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
18
|
-
Requires-Dist: ruff>=0.14.
|
|
19
|
+
Requires-Dist: ruff>=0.14.10; extra == 'dev'
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
21
|
-
#
|
|
22
|
+
# solvOR
|
|
22
23
|
|
|
23
24
|

|
|
24
25
|
[](LICENSE)
|
|
@@ -27,15 +28,16 @@ Solvor your optimization needs..
|
|
|
27
28
|
|
|
28
29
|
## What's in the box?
|
|
29
30
|
|
|
30
|
-
| Category |
|
|
31
|
+
| Category | Solvors | Use Case |
|
|
31
32
|
|----------|---------|----------|
|
|
32
33
|
| **Linear/Integer** | `solve_lp`, `solve_milp` | Resource allocation, scheduling |
|
|
33
|
-
| **Constraint** | `solve_sat
|
|
34
|
+
| **Constraint** | `solve_sat` | Sudoku, configuration, puzzles |
|
|
34
35
|
| **Local Search** | `anneal`, `tabu_search` | TSP, combinatorial optimization |
|
|
35
36
|
| **Population** | `evolve` | When you want nature to do the work |
|
|
36
37
|
| **Continuous** | `gradient_descent`, `momentum`, `adam` | ML, curve fitting |
|
|
37
38
|
| **Black-box** | `bayesian_opt` | Hyperparameter tuning, expensive functions |
|
|
38
39
|
| **Graph** | `max_flow`, `min_cost_flow`, `solve_assignment` | Matching, transportation |
|
|
40
|
+
| **Exact Cover** | `solve_exact_cover` | Sudoku, N-Queens, tiling puzzles |
|
|
39
41
|
|
|
40
42
|
---
|
|
41
43
|
|
|
@@ -69,13 +71,13 @@ print(result.solution) # {'x': 3, 'y': 7}
|
|
|
69
71
|
|
|
70
72
|
---
|
|
71
73
|
|
|
72
|
-
##
|
|
74
|
+
## Solvors
|
|
73
75
|
|
|
74
76
|
<details>
|
|
75
77
|
<summary><strong>Linear & Integer Programming</strong></summary>
|
|
76
78
|
|
|
77
79
|
### solve_lp
|
|
78
|
-
|
|
80
|
+
For resource allocation, blending, production planning. Finds the exact optimum for linear objectives with linear constraints.
|
|
79
81
|
|
|
80
82
|
```python
|
|
81
83
|
# minimize 2x + 3y subject to x + y >= 4, x <= 3
|
|
@@ -87,7 +89,7 @@ result = solve_lp(
|
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
### solve_milp
|
|
90
|
-
|
|
92
|
+
When some variables must be integers. Diet problems, scheduling with discrete slots, set covering.
|
|
91
93
|
|
|
92
94
|
```python
|
|
93
95
|
# same as above, but x must be integer
|
|
@@ -100,7 +102,7 @@ result = solve_milp(c=[2, 3], A=[[-1, -1], [1, 0]], b=[-4, 3], integers=[0])
|
|
|
100
102
|
<summary><strong>Constraint Programming</strong></summary>
|
|
101
103
|
|
|
102
104
|
### solve_sat
|
|
103
|
-
|
|
105
|
+
For "is this configuration valid?" problems. Dependencies, exclusions, implications - anything that boils down to boolean constraints.
|
|
104
106
|
|
|
105
107
|
```python
|
|
106
108
|
# (x1 OR x2) AND (NOT x1 OR x3) AND (NOT x2 OR NOT x3)
|
|
@@ -109,7 +111,7 @@ print(result.solution) # {1: True, 2: False, 3: True}
|
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
### Model (CP-SAT)
|
|
112
|
-
|
|
114
|
+
For puzzles and scheduling with "all different", arithmetic, and logical constraints. Sudoku, N-Queens, timetabling.
|
|
113
115
|
|
|
114
116
|
```python
|
|
115
117
|
m = Model()
|
|
@@ -128,7 +130,7 @@ result = m.solve()
|
|
|
128
130
|
<summary><strong>Metaheuristics</strong></summary>
|
|
129
131
|
|
|
130
132
|
### anneal
|
|
131
|
-
Simulated annealing
|
|
133
|
+
Simulated annealing, accepts worse solutions probabilistically.
|
|
132
134
|
|
|
133
135
|
```python
|
|
134
136
|
result = anneal(
|
|
@@ -141,7 +143,7 @@ result = anneal(
|
|
|
141
143
|
```
|
|
142
144
|
|
|
143
145
|
### tabu_search
|
|
144
|
-
|
|
146
|
+
Greedy local search with memory. Prevents cycling back to recent solutions, forcing exploration of new territory. More deterministic than anneal.
|
|
145
147
|
|
|
146
148
|
```python
|
|
147
149
|
result = tabu_search(
|
|
@@ -153,7 +155,7 @@ result = tabu_search(
|
|
|
153
155
|
```
|
|
154
156
|
|
|
155
157
|
### evolve
|
|
156
|
-
|
|
158
|
+
Population-based search. More overhead than anneal/tabu, but better diversity and parallelizable.
|
|
157
159
|
|
|
158
160
|
```python
|
|
159
161
|
result = evolve(
|
|
@@ -171,7 +173,7 @@ result = evolve(
|
|
|
171
173
|
<summary><strong>Continuous Optimization</strong></summary>
|
|
172
174
|
|
|
173
175
|
### gradient_descent / momentum / adam
|
|
174
|
-
|
|
176
|
+
Follow the slope downhill. Great for polishing solutions from other methods if your objective is differentiable. Adam adapts learning rates per parameter - usually the default choice.
|
|
175
177
|
|
|
176
178
|
```python
|
|
177
179
|
def grad_fn(x):
|
|
@@ -182,7 +184,7 @@ print(result.solution) # [~0, ~0]
|
|
|
182
184
|
```
|
|
183
185
|
|
|
184
186
|
### bayesian_opt
|
|
185
|
-
|
|
187
|
+
When each evaluation is expensive (think hyperparameter tuning, simulations). Builds a surrogate model to guess where to sample next instead of brute-forcing.
|
|
186
188
|
|
|
187
189
|
```python
|
|
188
190
|
def expensive_fn(x):
|
|
@@ -198,7 +200,7 @@ result = bayesian_opt(expensive_fn, bounds=[(0, 1), (0, 1)], max_iter=30)
|
|
|
198
200
|
<summary><strong>Network Flow</strong></summary>
|
|
199
201
|
|
|
200
202
|
### max_flow
|
|
201
|
-
|
|
203
|
+
"How much can I push through this network?" Assigning workers to tasks, finding bottlenecks. The max-flow min-cut theorem gives you bottleneck analysis for free.
|
|
202
204
|
|
|
203
205
|
```python
|
|
204
206
|
graph = {
|
|
@@ -213,7 +215,7 @@ print(result.solution) # edge flows dict
|
|
|
213
215
|
```
|
|
214
216
|
|
|
215
217
|
### min_cost_flow / solve_assignment
|
|
216
|
-
|
|
218
|
+
"What's the cheapest way to route X units?" Transportation, logistics, matching with costs.
|
|
217
219
|
|
|
218
220
|
```python
|
|
219
221
|
# Assignment problem: 3 workers, 3 tasks
|
|
@@ -229,11 +231,31 @@ result = solve_assignment(costs)
|
|
|
229
231
|
|
|
230
232
|
</details>
|
|
231
233
|
|
|
234
|
+
<details>
|
|
235
|
+
<summary><strong>Exact Cover</strong></summary>
|
|
236
|
+
|
|
237
|
+
### solve_exact_cover
|
|
238
|
+
For "place these pieces without overlap" or "fill this grid with exactly one of each" problems. Sudoku, pentomino tiling, scheduling where every slot must be filled exactly once.
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# Tiling problem: cover all columns with non-overlapping rows
|
|
242
|
+
matrix = [
|
|
243
|
+
[1, 1, 0, 0], # row 0 covers columns 0, 1
|
|
244
|
+
[0, 1, 1, 0], # row 1 covers columns 1, 2
|
|
245
|
+
[0, 0, 1, 1], # row 2 covers columns 2, 3
|
|
246
|
+
[1, 0, 0, 1], # row 3 covers columns 0, 3
|
|
247
|
+
]
|
|
248
|
+
result = solve_exact_cover(matrix)
|
|
249
|
+
# result.solution = (0, 2) or (1, 3) - rows that cover all columns exactly once
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
</details>
|
|
253
|
+
|
|
232
254
|
---
|
|
233
255
|
|
|
234
256
|
## Result Format
|
|
235
257
|
|
|
236
|
-
All
|
|
258
|
+
All solvors return a consistent `Result` namedtuple:
|
|
237
259
|
|
|
238
260
|
```python
|
|
239
261
|
Result(
|
|
@@ -249,7 +271,7 @@ Result(
|
|
|
249
271
|
|
|
250
272
|
## When to use what?
|
|
251
273
|
|
|
252
|
-
| Problem |
|
|
274
|
+
| Problem | Solvor |
|
|
253
275
|
|---------|--------|
|
|
254
276
|
| Linear constraints, continuous variables | `solve_lp` |
|
|
255
277
|
| Linear constraints, some integers | `solve_milp` |
|
|
@@ -260,6 +282,7 @@ Result(
|
|
|
260
282
|
| Smooth, differentiable | `adam` |
|
|
261
283
|
| Expensive black-box | `bayesian_opt` |
|
|
262
284
|
| Assignment, matching, flow | `max_flow`, `solve_assignment` |
|
|
285
|
+
| Exact cover, tiling, N-Queens | `solve_exact_cover` |
|
|
263
286
|
|
|
264
287
|
---
|
|
265
288
|
|
|
@@ -278,4 +301,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
|
278
301
|
|
|
279
302
|
## License
|
|
280
303
|
|
|
281
|
-
[Apache 2.0 License](LICENSE) - free for personal and commercial use.
|
|
304
|
+
[Apache 2.0 License](LICENSE) - free for personal and commercial use.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# solvOR
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
[](LICENSE)
|
|
@@ -7,15 +7,16 @@ Solvor your optimization needs..
|
|
|
7
7
|
|
|
8
8
|
## What's in the box?
|
|
9
9
|
|
|
10
|
-
| Category |
|
|
10
|
+
| Category | Solvors | Use Case |
|
|
11
11
|
|----------|---------|----------|
|
|
12
12
|
| **Linear/Integer** | `solve_lp`, `solve_milp` | Resource allocation, scheduling |
|
|
13
|
-
| **Constraint** | `solve_sat
|
|
13
|
+
| **Constraint** | `solve_sat` | Sudoku, configuration, puzzles |
|
|
14
14
|
| **Local Search** | `anneal`, `tabu_search` | TSP, combinatorial optimization |
|
|
15
15
|
| **Population** | `evolve` | When you want nature to do the work |
|
|
16
16
|
| **Continuous** | `gradient_descent`, `momentum`, `adam` | ML, curve fitting |
|
|
17
17
|
| **Black-box** | `bayesian_opt` | Hyperparameter tuning, expensive functions |
|
|
18
18
|
| **Graph** | `max_flow`, `min_cost_flow`, `solve_assignment` | Matching, transportation |
|
|
19
|
+
| **Exact Cover** | `solve_exact_cover` | Sudoku, N-Queens, tiling puzzles |
|
|
19
20
|
|
|
20
21
|
---
|
|
21
22
|
|
|
@@ -49,13 +50,13 @@ print(result.solution) # {'x': 3, 'y': 7}
|
|
|
49
50
|
|
|
50
51
|
---
|
|
51
52
|
|
|
52
|
-
##
|
|
53
|
+
## Solvors
|
|
53
54
|
|
|
54
55
|
<details>
|
|
55
56
|
<summary><strong>Linear & Integer Programming</strong></summary>
|
|
56
57
|
|
|
57
58
|
### solve_lp
|
|
58
|
-
|
|
59
|
+
For resource allocation, blending, production planning. Finds the exact optimum for linear objectives with linear constraints.
|
|
59
60
|
|
|
60
61
|
```python
|
|
61
62
|
# minimize 2x + 3y subject to x + y >= 4, x <= 3
|
|
@@ -67,7 +68,7 @@ result = solve_lp(
|
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
### solve_milp
|
|
70
|
-
|
|
71
|
+
When some variables must be integers. Diet problems, scheduling with discrete slots, set covering.
|
|
71
72
|
|
|
72
73
|
```python
|
|
73
74
|
# same as above, but x must be integer
|
|
@@ -80,7 +81,7 @@ result = solve_milp(c=[2, 3], A=[[-1, -1], [1, 0]], b=[-4, 3], integers=[0])
|
|
|
80
81
|
<summary><strong>Constraint Programming</strong></summary>
|
|
81
82
|
|
|
82
83
|
### solve_sat
|
|
83
|
-
|
|
84
|
+
For "is this configuration valid?" problems. Dependencies, exclusions, implications - anything that boils down to boolean constraints.
|
|
84
85
|
|
|
85
86
|
```python
|
|
86
87
|
# (x1 OR x2) AND (NOT x1 OR x3) AND (NOT x2 OR NOT x3)
|
|
@@ -89,7 +90,7 @@ print(result.solution) # {1: True, 2: False, 3: True}
|
|
|
89
90
|
```
|
|
90
91
|
|
|
91
92
|
### Model (CP-SAT)
|
|
92
|
-
|
|
93
|
+
For puzzles and scheduling with "all different", arithmetic, and logical constraints. Sudoku, N-Queens, timetabling.
|
|
93
94
|
|
|
94
95
|
```python
|
|
95
96
|
m = Model()
|
|
@@ -108,7 +109,7 @@ result = m.solve()
|
|
|
108
109
|
<summary><strong>Metaheuristics</strong></summary>
|
|
109
110
|
|
|
110
111
|
### anneal
|
|
111
|
-
Simulated annealing
|
|
112
|
+
Simulated annealing, accepts worse solutions probabilistically.
|
|
112
113
|
|
|
113
114
|
```python
|
|
114
115
|
result = anneal(
|
|
@@ -121,7 +122,7 @@ result = anneal(
|
|
|
121
122
|
```
|
|
122
123
|
|
|
123
124
|
### tabu_search
|
|
124
|
-
|
|
125
|
+
Greedy local search with memory. Prevents cycling back to recent solutions, forcing exploration of new territory. More deterministic than anneal.
|
|
125
126
|
|
|
126
127
|
```python
|
|
127
128
|
result = tabu_search(
|
|
@@ -133,7 +134,7 @@ result = tabu_search(
|
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
### evolve
|
|
136
|
-
|
|
137
|
+
Population-based search. More overhead than anneal/tabu, but better diversity and parallelizable.
|
|
137
138
|
|
|
138
139
|
```python
|
|
139
140
|
result = evolve(
|
|
@@ -151,7 +152,7 @@ result = evolve(
|
|
|
151
152
|
<summary><strong>Continuous Optimization</strong></summary>
|
|
152
153
|
|
|
153
154
|
### gradient_descent / momentum / adam
|
|
154
|
-
|
|
155
|
+
Follow the slope downhill. Great for polishing solutions from other methods if your objective is differentiable. Adam adapts learning rates per parameter - usually the default choice.
|
|
155
156
|
|
|
156
157
|
```python
|
|
157
158
|
def grad_fn(x):
|
|
@@ -162,7 +163,7 @@ print(result.solution) # [~0, ~0]
|
|
|
162
163
|
```
|
|
163
164
|
|
|
164
165
|
### bayesian_opt
|
|
165
|
-
|
|
166
|
+
When each evaluation is expensive (think hyperparameter tuning, simulations). Builds a surrogate model to guess where to sample next instead of brute-forcing.
|
|
166
167
|
|
|
167
168
|
```python
|
|
168
169
|
def expensive_fn(x):
|
|
@@ -178,7 +179,7 @@ result = bayesian_opt(expensive_fn, bounds=[(0, 1), (0, 1)], max_iter=30)
|
|
|
178
179
|
<summary><strong>Network Flow</strong></summary>
|
|
179
180
|
|
|
180
181
|
### max_flow
|
|
181
|
-
|
|
182
|
+
"How much can I push through this network?" Assigning workers to tasks, finding bottlenecks. The max-flow min-cut theorem gives you bottleneck analysis for free.
|
|
182
183
|
|
|
183
184
|
```python
|
|
184
185
|
graph = {
|
|
@@ -193,7 +194,7 @@ print(result.solution) # edge flows dict
|
|
|
193
194
|
```
|
|
194
195
|
|
|
195
196
|
### min_cost_flow / solve_assignment
|
|
196
|
-
|
|
197
|
+
"What's the cheapest way to route X units?" Transportation, logistics, matching with costs.
|
|
197
198
|
|
|
198
199
|
```python
|
|
199
200
|
# Assignment problem: 3 workers, 3 tasks
|
|
@@ -209,11 +210,31 @@ result = solve_assignment(costs)
|
|
|
209
210
|
|
|
210
211
|
</details>
|
|
211
212
|
|
|
213
|
+
<details>
|
|
214
|
+
<summary><strong>Exact Cover</strong></summary>
|
|
215
|
+
|
|
216
|
+
### solve_exact_cover
|
|
217
|
+
For "place these pieces without overlap" or "fill this grid with exactly one of each" problems. Sudoku, pentomino tiling, scheduling where every slot must be filled exactly once.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
# Tiling problem: cover all columns with non-overlapping rows
|
|
221
|
+
matrix = [
|
|
222
|
+
[1, 1, 0, 0], # row 0 covers columns 0, 1
|
|
223
|
+
[0, 1, 1, 0], # row 1 covers columns 1, 2
|
|
224
|
+
[0, 0, 1, 1], # row 2 covers columns 2, 3
|
|
225
|
+
[1, 0, 0, 1], # row 3 covers columns 0, 3
|
|
226
|
+
]
|
|
227
|
+
result = solve_exact_cover(matrix)
|
|
228
|
+
# result.solution = (0, 2) or (1, 3) - rows that cover all columns exactly once
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
</details>
|
|
232
|
+
|
|
212
233
|
---
|
|
213
234
|
|
|
214
235
|
## Result Format
|
|
215
236
|
|
|
216
|
-
All
|
|
237
|
+
All solvors return a consistent `Result` namedtuple:
|
|
217
238
|
|
|
218
239
|
```python
|
|
219
240
|
Result(
|
|
@@ -229,7 +250,7 @@ Result(
|
|
|
229
250
|
|
|
230
251
|
## When to use what?
|
|
231
252
|
|
|
232
|
-
| Problem |
|
|
253
|
+
| Problem | Solvor |
|
|
233
254
|
|---------|--------|
|
|
234
255
|
| Linear constraints, continuous variables | `solve_lp` |
|
|
235
256
|
| Linear constraints, some integers | `solve_milp` |
|
|
@@ -240,6 +261,7 @@ Result(
|
|
|
240
261
|
| Smooth, differentiable | `adam` |
|
|
241
262
|
| Expensive black-box | `bayesian_opt` |
|
|
242
263
|
| Assignment, matching, flow | `max_flow`, `solve_assignment` |
|
|
264
|
+
| Exact cover, tiling, N-Queens | `solve_exact_cover` |
|
|
243
265
|
|
|
244
266
|
---
|
|
245
267
|
|
|
@@ -258,4 +280,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
|
258
280
|
|
|
259
281
|
## License
|
|
260
282
|
|
|
261
|
-
[Apache 2.0 License](LICENSE) - free for personal and commercial use.
|
|
283
|
+
[Apache 2.0 License](LICENSE) - free for personal and commercial use.
|