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.
Files changed (36) hide show
  1. solvor-0.3.4/.github/workflows/ci.yml +209 -0
  2. {solvor-0.3.2 → solvor-0.3.4}/CONTRIBUTING.md +20 -11
  3. {solvor-0.3.2 → solvor-0.3.4}/LICENSE +1 -1
  4. {solvor-0.3.2 → solvor-0.3.4}/PKG-INFO +45 -22
  5. {solvor-0.3.2 → solvor-0.3.4}/README.md +40 -18
  6. {solvor-0.3.2 → solvor-0.3.4}/pyproject.toml +6 -5
  7. {solvor-0.3.2 → solvor-0.3.4}/solvor/__init__.py +2 -0
  8. solvor-0.3.4/solvor/anneal.py +75 -0
  9. {solvor-0.3.2 → solvor-0.3.4}/solvor/bayesian.py +20 -30
  10. {solvor-0.3.2 → solvor-0.3.4}/solvor/cp.py +16 -28
  11. solvor-0.3.4/solvor/dlx.py +249 -0
  12. {solvor-0.3.2 → solvor-0.3.4}/solvor/flow.py +24 -26
  13. {solvor-0.3.2 → solvor-0.3.4}/solvor/genetic.py +27 -33
  14. {solvor-0.3.2 → solvor-0.3.4}/solvor/gradient.py +27 -30
  15. {solvor-0.3.2 → solvor-0.3.4}/solvor/milp.py +21 -28
  16. {solvor-0.3.2 → solvor-0.3.4}/solvor/sat.py +26 -30
  17. {solvor-0.3.2 → solvor-0.3.4}/solvor/simplex.py +17 -25
  18. {solvor-0.3.2 → solvor-0.3.4}/solvor/tabu.py +24 -28
  19. solvor-0.3.4/tests/test_anneal.py +177 -0
  20. solvor-0.3.4/tests/test_bayesian.py +182 -0
  21. solvor-0.3.4/tests/test_cp.py +181 -0
  22. solvor-0.3.4/tests/test_dlx.py +282 -0
  23. solvor-0.3.4/tests/test_flow.py +244 -0
  24. solvor-0.3.4/tests/test_genetic.py +207 -0
  25. solvor-0.3.4/tests/test_gradient.py +207 -0
  26. solvor-0.3.4/tests/test_milp.py +120 -0
  27. solvor-0.3.4/tests/test_sat.py +187 -0
  28. solvor-0.3.4/tests/test_simplex.py +131 -0
  29. solvor-0.3.4/tests/test_tabu.py +191 -0
  30. solvor-0.3.2/.github/workflows/ci.yml +0 -25
  31. solvor-0.3.2/solvor/anneal.py +0 -80
  32. solvor-0.3.2/tests/test_solvers.py +0 -316
  33. {solvor-0.3.2 → solvor-0.3.4}/.github/workflows/publish.yml +0 -0
  34. {solvor-0.3.2 → solvor-0.3.4}/.gitignore +0 -0
  35. {solvor-0.3.2 → solvor-0.3.4}/solvor/types.py +0 -0
  36. {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 Solvor
1
+ # Contributing to solvOR
2
2
 
3
- Thanks for your interest in contributing to Solvor!
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 src/
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 solvers:
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 Solver
47
+ ## Adding a New Solvor
48
48
 
49
- 1. Create `src/solvor/<solver_name>.py`
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 `src/solvor/__init__.py`
53
- 5. Add tests to `tests/test_solvers.py`
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
- All solvers must have tests covering:
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 [yyyy] [name of copyright owner]
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.2
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: MIT
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 :: MIT License
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.9; extra == 'dev'
19
+ Requires-Dist: ruff>=0.14.10; extra == 'dev'
19
20
  Description-Content-Type: text/markdown
20
21
 
21
- # Solvor
22
+ # solvOR
22
23
 
23
24
  ![Python 3.14+](https://img.shields.io/badge/python-3.14+-blue.svg)
24
25
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
@@ -27,15 +28,16 @@ Solvor your optimization needs..
27
28
 
28
29
  ## What's in the box?
29
30
 
30
- | Category | Solvers | Use Case |
31
+ | Category | Solvors | Use Case |
31
32
  |----------|---------|----------|
32
33
  | **Linear/Integer** | `solve_lp`, `solve_milp` | Resource allocation, scheduling |
33
- | **Constraint** | `solve_sat`, `Model` | Sudoku, configuration, puzzles |
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
- ## Solvers
74
+ ## Solvors
73
75
 
74
76
  <details>
75
77
  <summary><strong>Linear & Integer Programming</strong></summary>
76
78
 
77
79
  ### solve_lp
78
- Two-phase simplex for linear programming.
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
- Branch and bound for mixed-integer problems.
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
- DPLL with unit propagation and clause learning.
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
- Constraint programming via SAT encoding.
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 - accepts worse solutions probabilistically.
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
- Maintains a "tabu list" of recent moves to escape local optima.
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
- Genetic algorithm with selection, crossover, mutation.
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
- First-order methods for differentiable functions.
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
- Gaussian process surrogate for expensive black-box functions.
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
- Ford-Fulkerson with BFS (Edmonds-Karp).
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
- Successive shortest paths for minimum cost flow.
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 solvers return a consistent `Result` namedtuple:
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 | Solver |
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
- # Solvor
1
+ # solvOR
2
2
 
3
3
  ![Python 3.14+](https://img.shields.io/badge/python-3.14+-blue.svg)
4
4
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
@@ -7,15 +7,16 @@ Solvor your optimization needs..
7
7
 
8
8
  ## What's in the box?
9
9
 
10
- | Category | Solvers | Use Case |
10
+ | Category | Solvors | Use Case |
11
11
  |----------|---------|----------|
12
12
  | **Linear/Integer** | `solve_lp`, `solve_milp` | Resource allocation, scheduling |
13
- | **Constraint** | `solve_sat`, `Model` | Sudoku, configuration, puzzles |
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
- ## Solvers
53
+ ## Solvors
53
54
 
54
55
  <details>
55
56
  <summary><strong>Linear & Integer Programming</strong></summary>
56
57
 
57
58
  ### solve_lp
58
- Two-phase simplex for linear programming.
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
- Branch and bound for mixed-integer problems.
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
- DPLL with unit propagation and clause learning.
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
- Constraint programming via SAT encoding.
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 - accepts worse solutions probabilistically.
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
- Maintains a "tabu list" of recent moves to escape local optima.
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
- Genetic algorithm with selection, crossover, mutation.
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
- First-order methods for differentiable functions.
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
- Gaussian process surrogate for expensive black-box functions.
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
- Ford-Fulkerson with BFS (Edmonds-Karp).
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
- Successive shortest paths for minimum cost flow.
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 solvers return a consistent `Result` namedtuple:
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 | Solver |
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.