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.
- pathos_ai-0.1.0/.github/workflows/ci.yml +11 -0
- pathos_ai-0.1.0/.github/workflows/pages.yml +59 -0
- pathos_ai-0.1.0/.github/workflows/publish.yaml +113 -0
- pathos_ai-0.1.0/.gitignore +25 -0
- pathos_ai-0.1.0/LICENSE +21 -0
- pathos_ai-0.1.0/PKG-INFO +265 -0
- pathos_ai-0.1.0/README.md +206 -0
- pathos_ai-0.1.0/benchmarks/.gitignore +2 -0
- pathos_ai-0.1.0/benchmarks/FINDINGS.md +235 -0
- pathos_ai-0.1.0/benchmarks/SPEC-STATUS.md +195 -0
- pathos_ai-0.1.0/benchmarks/__init__.py +0 -0
- pathos_ai-0.1.0/benchmarks/bench.py +568 -0
- pathos_ai-0.1.0/docs/api/algorithms.md +59 -0
- pathos_ai-0.1.0/docs/api/result.md +3 -0
- pathos_ai-0.1.0/docs/api/space.md +3 -0
- pathos_ai-0.1.0/docs/api/spaces.md +6 -0
- pathos_ai-0.1.0/docs/getting-started.md +136 -0
- pathos_ai-0.1.0/docs/guides/cancel-token.md +126 -0
- pathos_ai-0.1.0/docs/guides/modes-and-anytime.md +122 -0
- pathos_ai-0.1.0/docs/index.md +102 -0
- pathos_ai-0.1.0/docs/superpowers/plans/2026-05-29-pathos-implementation.md +3553 -0
- pathos_ai-0.1.0/examples/nqueens.py +31 -0
- pathos_ai-0.1.0/examples/puzzle8.py +52 -0
- pathos_ai-0.1.0/examples/route_planning.py +37 -0
- pathos_ai-0.1.0/examples/tictactoe.py +45 -0
- pathos_ai-0.1.0/examples/tsp.py +32 -0
- pathos_ai-0.1.0/mkdocs.yml +145 -0
- pathos_ai-0.1.0/pathos/__init__.py +5 -0
- pathos_ai-0.1.0/pathos/algorithms/__init__.py +7 -0
- pathos_ai-0.1.0/pathos/algorithms/adversarial.py +240 -0
- pathos_ai-0.1.0/pathos/algorithms/base.py +65 -0
- pathos_ai-0.1.0/pathos/algorithms/csp.py +196 -0
- pathos_ai-0.1.0/pathos/algorithms/evolutionary.py +360 -0
- pathos_ai-0.1.0/pathos/algorithms/informed.py +413 -0
- pathos_ai-0.1.0/pathos/algorithms/local.py +183 -0
- pathos_ai-0.1.0/pathos/algorithms/uninformed.py +219 -0
- pathos_ai-0.1.0/pathos/core/__init__.py +3 -0
- pathos_ai-0.1.0/pathos/core/cancel.py +34 -0
- pathos_ai-0.1.0/pathos/core/capabilities.py +14 -0
- pathos_ai-0.1.0/pathos/core/parallel.py +10 -0
- pathos_ai-0.1.0/pathos/core/result.py +52 -0
- pathos_ai-0.1.0/pathos/core/solver.py +122 -0
- pathos_ai-0.1.0/pathos/core/space.py +172 -0
- pathos_ai-0.1.0/pathos/spaces/__init__.py +4 -0
- pathos_ai-0.1.0/pathos/spaces/csp.py +89 -0
- pathos_ai-0.1.0/pathos/spaces/game.py +14 -0
- pathos_ai-0.1.0/pathos/spaces/graph.py +41 -0
- pathos_ai-0.1.0/pathos/spaces/tour.py +32 -0
- pathos_ai-0.1.0/pyproject.toml +81 -0
- pathos_ai-0.1.0/tests/__init__.py +0 -0
- pathos_ai-0.1.0/tests/conftest.py +0 -0
- pathos_ai-0.1.0/tests/test_adversarial.py +56 -0
- pathos_ai-0.1.0/tests/test_anytime_astar.py +187 -0
- pathos_ai-0.1.0/tests/test_anytime_metaheuristics.py +185 -0
- pathos_ai-0.1.0/tests/test_cancel_token.py +44 -0
- pathos_ai-0.1.0/tests/test_compatibility_guards.py +124 -0
- pathos_ai-0.1.0/tests/test_csp.py +50 -0
- pathos_ai-0.1.0/tests/test_epsilon.py +185 -0
- pathos_ai-0.1.0/tests/test_evolutionary.py +73 -0
- pathos_ai-0.1.0/tests/test_examples.py +44 -0
- pathos_ai-0.1.0/tests/test_ga_defaults.py +82 -0
- pathos_ai-0.1.0/tests/test_goal_aware_reporting.py +102 -0
- pathos_ai-0.1.0/tests/test_goal_preference.py +142 -0
- pathos_ai-0.1.0/tests/test_informed.py +71 -0
- pathos_ai-0.1.0/tests/test_local.py +38 -0
- pathos_ai-0.1.0/tests/test_mode.py +154 -0
- pathos_ai-0.1.0/tests/test_mode_auto.py +109 -0
- pathos_ai-0.1.0/tests/test_parallel.py +119 -0
- pathos_ai-0.1.0/tests/test_pso.py +106 -0
- pathos_ai-0.1.0/tests/test_score_for.py +123 -0
- pathos_ai-0.1.0/tests/test_solver.py +43 -0
- pathos_ai-0.1.0/tests/test_space.py +95 -0
- pathos_ai-0.1.0/tests/test_spaces_csp.py +50 -0
- pathos_ai-0.1.0/tests/test_spaces_game.py +35 -0
- pathos_ai-0.1.0/tests/test_spaces_graph.py +39 -0
- pathos_ai-0.1.0/tests/test_spaces_tour.py +24 -0
- pathos_ai-0.1.0/tests/test_timeout.py +102 -0
- 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
|
+
|
pathos_ai-0.1.0/LICENSE
ADDED
|
@@ -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.
|
pathos_ai-0.1.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://github.com/gia-uh/pathos/actions/workflows/ci.yml)
|
|
63
|
+
[](https://pypi.org/project/pathos-ai/)
|
|
64
|
+
[](https://pypi.org/project/pathos-ai/)
|
|
65
|
+
[](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
|
+
[](https://github.com/gia-uh/pathos/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/pathos-ai/)
|
|
5
|
+
[](https://pypi.org/project/pathos-ai/)
|
|
6
|
+
[](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)
|