pathos-ai 0.1.0__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 (78) hide show
  1. pathos_ai-0.1.0/.github/workflows/ci.yml +11 -0
  2. pathos_ai-0.1.0/.github/workflows/pages.yml +59 -0
  3. pathos_ai-0.1.0/.github/workflows/publish.yaml +113 -0
  4. pathos_ai-0.1.0/.gitignore +25 -0
  5. pathos_ai-0.1.0/LICENSE +21 -0
  6. pathos_ai-0.1.0/PKG-INFO +265 -0
  7. pathos_ai-0.1.0/README.md +206 -0
  8. pathos_ai-0.1.0/benchmarks/.gitignore +2 -0
  9. pathos_ai-0.1.0/benchmarks/FINDINGS.md +235 -0
  10. pathos_ai-0.1.0/benchmarks/SPEC-STATUS.md +195 -0
  11. pathos_ai-0.1.0/benchmarks/__init__.py +0 -0
  12. pathos_ai-0.1.0/benchmarks/bench.py +568 -0
  13. pathos_ai-0.1.0/docs/api/algorithms.md +59 -0
  14. pathos_ai-0.1.0/docs/api/result.md +3 -0
  15. pathos_ai-0.1.0/docs/api/space.md +3 -0
  16. pathos_ai-0.1.0/docs/api/spaces.md +6 -0
  17. pathos_ai-0.1.0/docs/getting-started.md +136 -0
  18. pathos_ai-0.1.0/docs/guides/cancel-token.md +126 -0
  19. pathos_ai-0.1.0/docs/guides/modes-and-anytime.md +122 -0
  20. pathos_ai-0.1.0/docs/index.md +102 -0
  21. pathos_ai-0.1.0/docs/superpowers/plans/2026-05-29-pathos-implementation.md +3553 -0
  22. pathos_ai-0.1.0/examples/nqueens.py +31 -0
  23. pathos_ai-0.1.0/examples/puzzle8.py +52 -0
  24. pathos_ai-0.1.0/examples/route_planning.py +37 -0
  25. pathos_ai-0.1.0/examples/tictactoe.py +45 -0
  26. pathos_ai-0.1.0/examples/tsp.py +32 -0
  27. pathos_ai-0.1.0/mkdocs.yml +145 -0
  28. pathos_ai-0.1.0/pathos/__init__.py +5 -0
  29. pathos_ai-0.1.0/pathos/algorithms/__init__.py +7 -0
  30. pathos_ai-0.1.0/pathos/algorithms/adversarial.py +240 -0
  31. pathos_ai-0.1.0/pathos/algorithms/base.py +65 -0
  32. pathos_ai-0.1.0/pathos/algorithms/csp.py +196 -0
  33. pathos_ai-0.1.0/pathos/algorithms/evolutionary.py +360 -0
  34. pathos_ai-0.1.0/pathos/algorithms/informed.py +413 -0
  35. pathos_ai-0.1.0/pathos/algorithms/local.py +183 -0
  36. pathos_ai-0.1.0/pathos/algorithms/uninformed.py +219 -0
  37. pathos_ai-0.1.0/pathos/core/__init__.py +3 -0
  38. pathos_ai-0.1.0/pathos/core/cancel.py +34 -0
  39. pathos_ai-0.1.0/pathos/core/capabilities.py +14 -0
  40. pathos_ai-0.1.0/pathos/core/parallel.py +10 -0
  41. pathos_ai-0.1.0/pathos/core/result.py +52 -0
  42. pathos_ai-0.1.0/pathos/core/solver.py +122 -0
  43. pathos_ai-0.1.0/pathos/core/space.py +172 -0
  44. pathos_ai-0.1.0/pathos/spaces/__init__.py +4 -0
  45. pathos_ai-0.1.0/pathos/spaces/csp.py +89 -0
  46. pathos_ai-0.1.0/pathos/spaces/game.py +14 -0
  47. pathos_ai-0.1.0/pathos/spaces/graph.py +41 -0
  48. pathos_ai-0.1.0/pathos/spaces/tour.py +32 -0
  49. pathos_ai-0.1.0/pyproject.toml +81 -0
  50. pathos_ai-0.1.0/tests/__init__.py +0 -0
  51. pathos_ai-0.1.0/tests/conftest.py +0 -0
  52. pathos_ai-0.1.0/tests/test_adversarial.py +56 -0
  53. pathos_ai-0.1.0/tests/test_anytime_astar.py +187 -0
  54. pathos_ai-0.1.0/tests/test_anytime_metaheuristics.py +185 -0
  55. pathos_ai-0.1.0/tests/test_cancel_token.py +44 -0
  56. pathos_ai-0.1.0/tests/test_compatibility_guards.py +124 -0
  57. pathos_ai-0.1.0/tests/test_csp.py +50 -0
  58. pathos_ai-0.1.0/tests/test_epsilon.py +185 -0
  59. pathos_ai-0.1.0/tests/test_evolutionary.py +73 -0
  60. pathos_ai-0.1.0/tests/test_examples.py +44 -0
  61. pathos_ai-0.1.0/tests/test_ga_defaults.py +82 -0
  62. pathos_ai-0.1.0/tests/test_goal_aware_reporting.py +102 -0
  63. pathos_ai-0.1.0/tests/test_goal_preference.py +142 -0
  64. pathos_ai-0.1.0/tests/test_informed.py +71 -0
  65. pathos_ai-0.1.0/tests/test_local.py +38 -0
  66. pathos_ai-0.1.0/tests/test_mode.py +154 -0
  67. pathos_ai-0.1.0/tests/test_mode_auto.py +109 -0
  68. pathos_ai-0.1.0/tests/test_parallel.py +119 -0
  69. pathos_ai-0.1.0/tests/test_pso.py +106 -0
  70. pathos_ai-0.1.0/tests/test_score_for.py +123 -0
  71. pathos_ai-0.1.0/tests/test_solver.py +43 -0
  72. pathos_ai-0.1.0/tests/test_space.py +95 -0
  73. pathos_ai-0.1.0/tests/test_spaces_csp.py +50 -0
  74. pathos_ai-0.1.0/tests/test_spaces_game.py +35 -0
  75. pathos_ai-0.1.0/tests/test_spaces_graph.py +39 -0
  76. pathos_ai-0.1.0/tests/test_spaces_tour.py +24 -0
  77. pathos_ai-0.1.0/tests/test_timeout.py +102 -0
  78. pathos_ai-0.1.0/tests/test_uninformed.py +73 -0
@@ -0,0 +1,11 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - uses: actions/setup-python@v5
9
+ with: { python-version: "3.11" }
10
+ - run: pip install -e ".[dev]"
11
+ - run: pytest --cov=pathos --cov-report=term-missing
@@ -0,0 +1,59 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ # Allow only one concurrent deployment, skipping runs queued between the
9
+ # run in-progress and latest queued. Do not cancel in-progress runs — we
10
+ # want the active deploy to finish so the published site stays consistent.
11
+ concurrency:
12
+ group: pages
13
+ cancel-in-progress: false
14
+
15
+ permissions:
16
+ contents: read
17
+ pages: write
18
+ id-token: write
19
+
20
+ jobs:
21
+ build:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v4
26
+ with:
27
+ fetch-depth: 0
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.11"
33
+ cache: pip
34
+ cache-dependency-path: pyproject.toml
35
+
36
+ - name: Install dependencies
37
+ run: pip install -e ".[dev]"
38
+
39
+ - name: Configure Pages
40
+ uses: actions/configure-pages@v5
41
+
42
+ - name: Build documentation
43
+ run: mkdocs build --strict
44
+
45
+ - name: Upload Pages artifact
46
+ uses: actions/upload-pages-artifact@v3
47
+ with:
48
+ path: site/
49
+
50
+ deploy:
51
+ needs: build
52
+ runs-on: ubuntu-latest
53
+ environment:
54
+ name: github-pages
55
+ url: ${{ steps.deployment.outputs.page_url }}
56
+ steps:
57
+ - name: Deploy to GitHub Pages
58
+ id: deployment
59
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,113 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ dry_run:
9
+ description: "Build and validate only — do not publish to PyPI"
10
+ required: false
11
+ default: "true"
12
+ type: choice
13
+ options:
14
+ - "true"
15
+ - "false"
16
+
17
+ # Don't allow concurrent publishes for the same release.
18
+ concurrency:
19
+ group: pypi-publish-${{ github.event.release.tag_name || github.run_id }}
20
+ cancel-in-progress: false
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ jobs:
26
+ # Gate: must pass tests on the tagged commit before we publish anything.
27
+ test:
28
+ name: Test (Python ${{ matrix.python }})
29
+ runs-on: ubuntu-latest
30
+ strategy:
31
+ fail-fast: true
32
+ matrix:
33
+ python: ["3.11", "3.12", "3.13"]
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: ${{ matrix.python }}
39
+ cache: pip
40
+ cache-dependency-path: pyproject.toml
41
+ - run: pip install -e ".[dev]"
42
+ - run: pytest --tb=short --ignore=tests/test_examples.py
43
+ - run: python -m mypy --strict pathos/
44
+
45
+ build:
46
+ name: Build distribution
47
+ needs: test
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ with:
52
+ fetch-depth: 0
53
+ - uses: actions/setup-python@v5
54
+ with:
55
+ python-version: "3.11"
56
+ cache: pip
57
+ cache-dependency-path: pyproject.toml
58
+
59
+ - name: Install build + twine
60
+ run: pip install --upgrade build twine
61
+
62
+ - name: Confirm version matches release tag
63
+ if: github.event_name == 'release'
64
+ run: |
65
+ PYPROJECT_VERSION=$(python -c "import tomllib; f=open('pyproject.toml','rb'); print(tomllib.load(f)['project']['version'])")
66
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
67
+ echo "pyproject.toml version: $PYPROJECT_VERSION"
68
+ echo "release tag (sans 'v'): $TAG_VERSION"
69
+ if [ "$PYPROJECT_VERSION" != "$TAG_VERSION" ]; then
70
+ echo "::error::pyproject.toml version ($PYPROJECT_VERSION) does not match release tag ($TAG_VERSION)"
71
+ exit 1
72
+ fi
73
+
74
+ - name: Build sdist + wheel
75
+ run: python -m build
76
+
77
+ - name: Validate distribution metadata
78
+ run: twine check --strict dist/*
79
+
80
+ - name: Upload artifact
81
+ uses: actions/upload-artifact@v4
82
+ with:
83
+ name: dist
84
+ path: dist/
85
+ retention-days: 7
86
+
87
+ publish:
88
+ name: Publish to PyPI (Trusted Publisher)
89
+ needs: build
90
+ # Only publish for real releases, not dry-run dispatch.
91
+ if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.dry_run == 'false')
92
+ runs-on: ubuntu-latest
93
+ environment:
94
+ name: pypi
95
+ url: https://pypi.org/project/pathos-ai/
96
+ permissions:
97
+ # OIDC token for PyPI Trusted Publishing — no API token in secrets.
98
+ id-token: write
99
+ steps:
100
+ - name: Download artifact
101
+ uses: actions/download-artifact@v4
102
+ with:
103
+ name: dist
104
+ path: dist/
105
+
106
+ - name: Publish to PyPI
107
+ uses: pypa/gh-action-pypi-publish@release/v1
108
+ with:
109
+ packages-dir: dist/
110
+ # Don't allow duplicate version uploads — if 0.1.0 is already on
111
+ # PyPI and someone re-tags, fail loudly rather than silently skip.
112
+ skip-existing: false
113
+ verbose: true
@@ -0,0 +1,25 @@
1
+ # Python bytecode
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+
8
+ # Distribution
9
+ dist/
10
+ build/
11
+ *.egg-info/
12
+
13
+ # Virtual envs
14
+ .venv/
15
+ venv/
16
+
17
+ # Dev
18
+ .env
19
+
20
+ # Worktrees
21
+ .worktrees/
22
+
23
+ # mkdocs build output
24
+ site/
25
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gia-uh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,265 @@
1
+ Metadata-Version: 2.4
2
+ Name: pathos-ai
3
+ Version: 0.1.0
4
+ Summary: Production-ready classical AI search algorithms — declare your problem, the solver picks the algorithm. Includes an anytime A* cascade by default.
5
+ Project-URL: Homepage, https://uhgia.org/pathos/
6
+ Project-URL: Documentation, https://uhgia.org/pathos/
7
+ Project-URL: Repository, https://github.com/gia-uh/pathos
8
+ Project-URL: Issues, https://github.com/gia-uh/pathos/issues
9
+ Project-URL: Changelog, https://github.com/gia-uh/pathos/releases
10
+ Author-email: Alejandro Piad Morffis <apiad@apiad.net>
11
+ Maintainer-email: Alejandro Piad Morffis <apiad@apiad.net>
12
+ License: MIT License
13
+
14
+ Copyright (c) 2026 gia-uh
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights
19
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ copies of the Software, and to permit persons to whom the Software is
21
+ furnished to do so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in
24
+ all copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
+ THE SOFTWARE.
33
+ License-File: LICENSE
34
+ Keywords: a-star,ai,anytime,constraint-satisfaction,csp,genetic-algorithm,graph-search,heuristic,mcts,metaheuristics,minimax,optimization,particle-swarm,planning,search,simulated-annealing
35
+ Classifier: Development Status :: 4 - Beta
36
+ Classifier: Intended Audience :: Developers
37
+ Classifier: Intended Audience :: Education
38
+ Classifier: Intended Audience :: Science/Research
39
+ Classifier: License :: OSI Approved :: MIT License
40
+ Classifier: Operating System :: MacOS
41
+ Classifier: Operating System :: POSIX
42
+ Classifier: Operating System :: POSIX :: Linux
43
+ Classifier: Programming Language :: Python :: 3
44
+ Classifier: Programming Language :: Python :: 3 :: Only
45
+ Classifier: Programming Language :: Python :: 3.11
46
+ Classifier: Programming Language :: Python :: 3.12
47
+ Classifier: Programming Language :: Python :: 3.13
48
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
49
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
50
+ Classifier: Typing :: Typed
51
+ Requires-Python: >=3.11
52
+ Provides-Extra: dev
53
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
54
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == 'dev'
55
+ Requires-Dist: mypy>=1.8; extra == 'dev'
56
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
57
+ Requires-Dist: pytest>=7.4; extra == 'dev'
58
+ Description-Content-Type: text/markdown
59
+
60
+ # PATHOS — Python AI Search Library
61
+
62
+ [![CI](https://github.com/gia-uh/pathos/actions/workflows/ci.yml/badge.svg)](https://github.com/gia-uh/pathos/actions/workflows/ci.yml)
63
+ [![PyPI](https://img.shields.io/pypi/v/pathos-ai.svg)](https://pypi.org/project/pathos-ai/)
64
+ [![Python](https://img.shields.io/pypi/pyversions/pathos-ai.svg)](https://pypi.org/project/pathos-ai/)
65
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
66
+
67
+ Production-ready classical AI search algorithms for Python. No machine learning. Pure search.
68
+
69
+ **[Documentation](https://gia-uh.github.io/pathos)** · [PyPI](https://pypi.org/project/pathos-ai/) · [Examples](examples/)
70
+
71
+ ## Philosophy
72
+
73
+ Define your *problem*, not your algorithm. PATHOS inspects the capabilities you declare and selects the best algorithm automatically.
74
+
75
+ ```python
76
+ from pathos import Space
77
+
78
+ space = Space().initial("Madrid")
79
+
80
+ @space.successors
81
+ def neighbors(city):
82
+ for next_city, km in roads[city]:
83
+ yield next_city, next_city
84
+
85
+ @space.goal
86
+ def reached(city): return city == "Lisboa"
87
+
88
+ @space.heuristic
89
+ def h(city): return straight_line_km(city, "Lisboa")
90
+
91
+ result = space.solver().solve()
92
+ # → Uses A* automatically (has successors + goal + heuristic)
93
+ print(result.path, result.cost, result.algorithm)
94
+ ```
95
+
96
+ ## Install
97
+
98
+ ```bash
99
+ pip install pathos-ai
100
+ ```
101
+
102
+ ## Algorithm Families
103
+
104
+ | Declare | Algorithms Available |
105
+ |---------|---------------------|
106
+ | `@evaluate` | Simulated Annealing, Genetic Algorithm, DE, PSO |
107
+ | `@successors + @goal` | BFS, DFS, IDDFS *(DFS is non-optimal — for shortest paths prefer BFS/UCS)* |
108
+ | `@successors + @evaluate` | Hill Climbing, Tabu Search |
109
+ | `@successors + @goal + @heuristic` | A*, IDA*, Greedy Best-First |
110
+ | `@successors + @goal + @heuristic + @evaluate` | Weighted A*, UCS |
111
+ | `.adversarial() + @terminal + @utility` | Minimax, Alpha-Beta, MCTS |
112
+ | `CSPSpace + @constraint` | Backtracking, Forward Checking |
113
+
114
+ ## Specialized Spaces
115
+
116
+ ```python
117
+ from pathos import GraphSpace, CSPSpace, TourSpace, GameSpace
118
+
119
+ # Graph search (auto-provides successors from adjacency)
120
+ space = GraphSpace(graph=city_graph).initial("A")
121
+
122
+ # Constraint satisfaction (auto-provides successors + goal)
123
+ csp = CSPSpace(variables=["X", "Y", "Z"])
124
+
125
+ # Tour optimization (TSP — auto-provides 2-opt neighborhood)
126
+ tour = TourSpace(nodes=cities, distances=dist_matrix)
127
+
128
+ # Adversarial games (auto-sets adversarial mode)
129
+ game = GameSpace().initial(board)
130
+ ```
131
+
132
+ ## Modes — exact, approximate, auto
133
+
134
+ The `solver()` factory selects an algorithm based on the space's declared
135
+ capabilities and the mode you ask for. Three modes:
136
+
137
+ ```python
138
+ # Default: anytime cascade. Always-ready, gives best incumbent under
139
+ # the budget (1h if you don't set one). Optimal if it finishes.
140
+ space.solver().solve()
141
+ space.solver(timeout=60).solve()
142
+
143
+ # Single-shot admissible algorithm. No timeout default; runs to
144
+ # completion or until you cut it off.
145
+ space.solver(mode="exact").solve()
146
+
147
+ # Single-shot bounded-suboptimal algorithm. Faster than exact.
148
+ space.solver(mode="approximate").solve()
149
+ ```
150
+
151
+ Under `mode="auto"` (default), `AnytimeAStar` wins selection on A\*-family
152
+ spaces and runs a cascade `[Greedy, WAStar(5,3,2,1.5), AStar]`, keeping
153
+ the best incumbent across phases. On a generous budget the final A\* phase
154
+ returns the proven-optimal answer; on a tight budget you get the best
155
+ incumbent so far instead of `not_found`.
156
+
157
+ `SearchResult.epsilon` tells you the quality bound: `1.0` is proven
158
+ optimal, `>1.0` is ε-bounded (cost ≤ ε × optimal), `inf` is unbounded
159
+ (greedy), `None` means the algorithm doesn't report a bound (e.g.
160
+ metaheuristics).
161
+
162
+ ```python
163
+ result = space.solver(timeout=10).solve()
164
+ if result.optimal:
165
+ print(f"Optimal: cost {result.cost}")
166
+ else:
167
+ print(f"ε-bounded ({result.epsilon}): cost {result.cost}")
168
+ ```
169
+
170
+ Every metaheuristic is naturally anytime regardless of mode — set a
171
+ timeout and the algorithm returns its best individual seen so far
172
+ when the budget runs out (cooperative `CancelToken` protocol).
173
+
174
+ ## Parallel Evaluation
175
+
176
+ Population-based algorithms (GA, DE, LocalBeamSearch) support multiprocessing via `.parallel(n)`:
177
+
178
+ ```python
179
+ # Evaluate all candidates in parallel across 4 processes
180
+ space = Space().initial(lambda: random_genome()).parallel(4)
181
+
182
+ # evaluate fn must be a module-level function (picklable)
183
+ def fitness(genome): return -sum(genome)
184
+ space.evaluate(fitness)
185
+
186
+ result = GeneticAlgorithm(space, pop_size=200, generations=500).solve()
187
+ ```
188
+
189
+ Pass `n=1` (default) for serial execution. Falls back automatically when population size is 1.
190
+
191
+ ## Direct Algorithm Access
192
+
193
+ ```python
194
+ from pathos.algorithms import AStar, GeneticAlgorithm, AlphaBeta
195
+
196
+ result = AStar(space).solve() # bypass auto-selection
197
+ ```
198
+
199
+ ## SearchResult
200
+
201
+ Every algorithm returns a uniform `SearchResult`:
202
+
203
+ ```python
204
+ result.solution # final state
205
+ result.path # list of (action, state) steps
206
+ result.cost # total cost
207
+ result.algorithm # algorithm name
208
+ result.nodes_expanded
209
+ result.elapsed # seconds
210
+ result.found # bool
211
+ ```
212
+
213
+ ## Performance
214
+
215
+ Reference numbers from `python -m benchmarks.bench --repeat 3` on an Intel
216
+ i7-6820HQ @ 2.70 GHz, Python 3.13. Algorithms are the ones auto-selected by
217
+ `space.solver()`. Reproduce with the same command; raw records dumped via
218
+ `--json`.
219
+
220
+ **N-Queens (Backtracking, CSPSpace)**
221
+
222
+ | N | elapsed (s, median) | nodes expanded |
223
+ |---|---:|---:|
224
+ | 6 | 0.0003 | 31 |
225
+ | 8 | 0.0022 | 113 |
226
+ | 10 | 0.0031 | 102 |
227
+ | 12 | 0.0149 | 261 |
228
+ | 14 | 0.1726 | 1 899 |
229
+ | 16 | 1.2363 | 10 052 |
230
+
231
+ **TSP (TabuSearch, TourSpace, 100 iters)**
232
+
233
+ | cities | elapsed (s, median) | tour cost (median) |
234
+ |---:|---:|---:|
235
+ | 5 | 0.0014 | 197.8 |
236
+ | 8 | 0.0060 | 253.3 |
237
+ | 12 | 0.0224 | 272.2 |
238
+ | 16 | 0.0439 | 354.9 |
239
+ | 20 | 0.0800 | 383.4 |
240
+ | 25 | 0.1593 | 409.3 |
241
+
242
+ **8-Puzzle (A\* + Manhattan)**
243
+
244
+ | scramble depth | elapsed (s, median) | nodes expanded | solution length |
245
+ |---:|---:|---:|---:|
246
+ | 10 | 0.0001 | 12 | 10 |
247
+ | 20 | 0.0042 | 461 | 20 |
248
+ | 30 | 0.0097 | 1 397 | 24 |
249
+ | 40 | 0.0127 | 1 728 | 26 |
250
+ | 50 | 0.0086 | 1 186 | 22 |
251
+
252
+ Solution length plateaus around 22–26 because the 8-puzzle state-space
253
+ diameter is ~31 — deeper scrambles don't make harder instances.
254
+
255
+ ## Examples
256
+
257
+ - [Route Planning (A*)](examples/route_planning.py)
258
+ - [TSP (SA + GA)](examples/tsp.py)
259
+ - [N-Queens (CSP)](examples/nqueens.py)
260
+ - [Tic-tac-toe (Alpha-Beta)](examples/tictactoe.py)
261
+ - [8-Puzzle (A*)](examples/puzzle8.py)
262
+
263
+ ## License
264
+
265
+ MIT — [gia-uh](https://github.com/gia-uh)
@@ -0,0 +1,206 @@
1
+ # PATHOS — Python AI Search Library
2
+
3
+ [![CI](https://github.com/gia-uh/pathos/actions/workflows/ci.yml/badge.svg)](https://github.com/gia-uh/pathos/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/pathos-ai.svg)](https://pypi.org/project/pathos-ai/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/pathos-ai.svg)](https://pypi.org/project/pathos-ai/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
+
8
+ Production-ready classical AI search algorithms for Python. No machine learning. Pure search.
9
+
10
+ **[Documentation](https://gia-uh.github.io/pathos)** · [PyPI](https://pypi.org/project/pathos-ai/) · [Examples](examples/)
11
+
12
+ ## Philosophy
13
+
14
+ Define your *problem*, not your algorithm. PATHOS inspects the capabilities you declare and selects the best algorithm automatically.
15
+
16
+ ```python
17
+ from pathos import Space
18
+
19
+ space = Space().initial("Madrid")
20
+
21
+ @space.successors
22
+ def neighbors(city):
23
+ for next_city, km in roads[city]:
24
+ yield next_city, next_city
25
+
26
+ @space.goal
27
+ def reached(city): return city == "Lisboa"
28
+
29
+ @space.heuristic
30
+ def h(city): return straight_line_km(city, "Lisboa")
31
+
32
+ result = space.solver().solve()
33
+ # → Uses A* automatically (has successors + goal + heuristic)
34
+ print(result.path, result.cost, result.algorithm)
35
+ ```
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install pathos-ai
41
+ ```
42
+
43
+ ## Algorithm Families
44
+
45
+ | Declare | Algorithms Available |
46
+ |---------|---------------------|
47
+ | `@evaluate` | Simulated Annealing, Genetic Algorithm, DE, PSO |
48
+ | `@successors + @goal` | BFS, DFS, IDDFS *(DFS is non-optimal — for shortest paths prefer BFS/UCS)* |
49
+ | `@successors + @evaluate` | Hill Climbing, Tabu Search |
50
+ | `@successors + @goal + @heuristic` | A*, IDA*, Greedy Best-First |
51
+ | `@successors + @goal + @heuristic + @evaluate` | Weighted A*, UCS |
52
+ | `.adversarial() + @terminal + @utility` | Minimax, Alpha-Beta, MCTS |
53
+ | `CSPSpace + @constraint` | Backtracking, Forward Checking |
54
+
55
+ ## Specialized Spaces
56
+
57
+ ```python
58
+ from pathos import GraphSpace, CSPSpace, TourSpace, GameSpace
59
+
60
+ # Graph search (auto-provides successors from adjacency)
61
+ space = GraphSpace(graph=city_graph).initial("A")
62
+
63
+ # Constraint satisfaction (auto-provides successors + goal)
64
+ csp = CSPSpace(variables=["X", "Y", "Z"])
65
+
66
+ # Tour optimization (TSP — auto-provides 2-opt neighborhood)
67
+ tour = TourSpace(nodes=cities, distances=dist_matrix)
68
+
69
+ # Adversarial games (auto-sets adversarial mode)
70
+ game = GameSpace().initial(board)
71
+ ```
72
+
73
+ ## Modes — exact, approximate, auto
74
+
75
+ The `solver()` factory selects an algorithm based on the space's declared
76
+ capabilities and the mode you ask for. Three modes:
77
+
78
+ ```python
79
+ # Default: anytime cascade. Always-ready, gives best incumbent under
80
+ # the budget (1h if you don't set one). Optimal if it finishes.
81
+ space.solver().solve()
82
+ space.solver(timeout=60).solve()
83
+
84
+ # Single-shot admissible algorithm. No timeout default; runs to
85
+ # completion or until you cut it off.
86
+ space.solver(mode="exact").solve()
87
+
88
+ # Single-shot bounded-suboptimal algorithm. Faster than exact.
89
+ space.solver(mode="approximate").solve()
90
+ ```
91
+
92
+ Under `mode="auto"` (default), `AnytimeAStar` wins selection on A\*-family
93
+ spaces and runs a cascade `[Greedy, WAStar(5,3,2,1.5), AStar]`, keeping
94
+ the best incumbent across phases. On a generous budget the final A\* phase
95
+ returns the proven-optimal answer; on a tight budget you get the best
96
+ incumbent so far instead of `not_found`.
97
+
98
+ `SearchResult.epsilon` tells you the quality bound: `1.0` is proven
99
+ optimal, `>1.0` is ε-bounded (cost ≤ ε × optimal), `inf` is unbounded
100
+ (greedy), `None` means the algorithm doesn't report a bound (e.g.
101
+ metaheuristics).
102
+
103
+ ```python
104
+ result = space.solver(timeout=10).solve()
105
+ if result.optimal:
106
+ print(f"Optimal: cost {result.cost}")
107
+ else:
108
+ print(f"ε-bounded ({result.epsilon}): cost {result.cost}")
109
+ ```
110
+
111
+ Every metaheuristic is naturally anytime regardless of mode — set a
112
+ timeout and the algorithm returns its best individual seen so far
113
+ when the budget runs out (cooperative `CancelToken` protocol).
114
+
115
+ ## Parallel Evaluation
116
+
117
+ Population-based algorithms (GA, DE, LocalBeamSearch) support multiprocessing via `.parallel(n)`:
118
+
119
+ ```python
120
+ # Evaluate all candidates in parallel across 4 processes
121
+ space = Space().initial(lambda: random_genome()).parallel(4)
122
+
123
+ # evaluate fn must be a module-level function (picklable)
124
+ def fitness(genome): return -sum(genome)
125
+ space.evaluate(fitness)
126
+
127
+ result = GeneticAlgorithm(space, pop_size=200, generations=500).solve()
128
+ ```
129
+
130
+ Pass `n=1` (default) for serial execution. Falls back automatically when population size is 1.
131
+
132
+ ## Direct Algorithm Access
133
+
134
+ ```python
135
+ from pathos.algorithms import AStar, GeneticAlgorithm, AlphaBeta
136
+
137
+ result = AStar(space).solve() # bypass auto-selection
138
+ ```
139
+
140
+ ## SearchResult
141
+
142
+ Every algorithm returns a uniform `SearchResult`:
143
+
144
+ ```python
145
+ result.solution # final state
146
+ result.path # list of (action, state) steps
147
+ result.cost # total cost
148
+ result.algorithm # algorithm name
149
+ result.nodes_expanded
150
+ result.elapsed # seconds
151
+ result.found # bool
152
+ ```
153
+
154
+ ## Performance
155
+
156
+ Reference numbers from `python -m benchmarks.bench --repeat 3` on an Intel
157
+ i7-6820HQ @ 2.70 GHz, Python 3.13. Algorithms are the ones auto-selected by
158
+ `space.solver()`. Reproduce with the same command; raw records dumped via
159
+ `--json`.
160
+
161
+ **N-Queens (Backtracking, CSPSpace)**
162
+
163
+ | N | elapsed (s, median) | nodes expanded |
164
+ |---|---:|---:|
165
+ | 6 | 0.0003 | 31 |
166
+ | 8 | 0.0022 | 113 |
167
+ | 10 | 0.0031 | 102 |
168
+ | 12 | 0.0149 | 261 |
169
+ | 14 | 0.1726 | 1 899 |
170
+ | 16 | 1.2363 | 10 052 |
171
+
172
+ **TSP (TabuSearch, TourSpace, 100 iters)**
173
+
174
+ | cities | elapsed (s, median) | tour cost (median) |
175
+ |---:|---:|---:|
176
+ | 5 | 0.0014 | 197.8 |
177
+ | 8 | 0.0060 | 253.3 |
178
+ | 12 | 0.0224 | 272.2 |
179
+ | 16 | 0.0439 | 354.9 |
180
+ | 20 | 0.0800 | 383.4 |
181
+ | 25 | 0.1593 | 409.3 |
182
+
183
+ **8-Puzzle (A\* + Manhattan)**
184
+
185
+ | scramble depth | elapsed (s, median) | nodes expanded | solution length |
186
+ |---:|---:|---:|---:|
187
+ | 10 | 0.0001 | 12 | 10 |
188
+ | 20 | 0.0042 | 461 | 20 |
189
+ | 30 | 0.0097 | 1 397 | 24 |
190
+ | 40 | 0.0127 | 1 728 | 26 |
191
+ | 50 | 0.0086 | 1 186 | 22 |
192
+
193
+ Solution length plateaus around 22–26 because the 8-puzzle state-space
194
+ diameter is ~31 — deeper scrambles don't make harder instances.
195
+
196
+ ## Examples
197
+
198
+ - [Route Planning (A*)](examples/route_planning.py)
199
+ - [TSP (SA + GA)](examples/tsp.py)
200
+ - [N-Queens (CSP)](examples/nqueens.py)
201
+ - [Tic-tac-toe (Alpha-Beta)](examples/tictactoe.py)
202
+ - [8-Puzzle (A*)](examples/puzzle8.py)
203
+
204
+ ## License
205
+
206
+ MIT — [gia-uh](https://github.com/gia-uh)