Production-ready classical AI search algorithms — declare your problem, the solver picks the algorithm. Includes an anytime A* cascade by default.
Project description
PATHOS — Python AI Search Library
Production-ready classical AI search algorithms for Python. No machine learning. Pure search.
Documentation · PyPI · Examples
Philosophy
Define your problem, not your algorithm. PATHOS inspects the capabilities you declare and selects the best algorithm automatically.
from pathos import Space
space = Space().initial("Madrid")
@space.successors
def neighbors(city):
for next_city, km in roads[city]:
yield next_city, next_city
@space.goal
def reached(city): return city == "Lisboa"
@space.heuristic
def h(city): return straight_line_km(city, "Lisboa")
result = space.solver().solve()
# → Uses A* automatically (has successors + goal + heuristic)
print(result.path, result.cost, result.algorithm)
Install
pip install pathos-ai
Algorithm Families
| Declare | Algorithms Available |
|---|---|
@evaluate |
Simulated Annealing, Genetic Algorithm, DE, PSO |
@successors + @goal |
BFS, DFS, IDDFS (DFS is non-optimal — for shortest paths prefer BFS/UCS) |
@successors + @evaluate |
Hill Climbing, Tabu Search, Simulated Annealing — cascaded by AnytimeLocal under mode="auto" |
@successors + @goal + @heuristic |
A*, IDA*, Greedy Best-First |
@successors + @goal + @heuristic + @evaluate |
Weighted A*, UCS |
.adversarial() + @terminal + @utility |
Minimax, Alpha-Beta, Negamax, MCTS — iterative-deepened by AnytimeAdversarial under mode="auto" with PV-first move ordering |
CSPSpace + @constraint |
Backtracking, Forward Checking, Min-Conflicts (with @evaluate) — cascaded by AnytimeCSP under mode="auto" |
Specialized Spaces
from pathos import GraphSpace, CSPSpace, TourSpace, GameSpace
# Graph search (auto-provides successors from adjacency)
space = GraphSpace(graph=city_graph).initial("A")
# Constraint satisfaction (auto-provides successors + goal)
csp = CSPSpace(variables=["X", "Y", "Z"])
# Tour optimization (TSP — auto-provides 2-opt neighborhood)
tour = TourSpace(nodes=cities, distances=dist_matrix)
# Adversarial games (auto-sets adversarial mode)
game = GameSpace().initial(board)
Modes — exact, approximate, auto
The solver() factory selects an algorithm based on the space's declared
capabilities and the mode you ask for. Three modes:
# Default: anytime cascade. Always-ready, gives best incumbent under
# the budget (1h if you don't set one). Optimal if it finishes.
space.solver().solve()
space.solver(timeout=60).solve()
# Single-shot admissible algorithm. No timeout default; runs to
# completion or until you cut it off.
space.solver(mode="exact").solve()
# Single-shot bounded-suboptimal algorithm. Faster than exact.
space.solver(mode="approximate").solve()
Under mode="auto" (default), AnytimeAStar wins selection on A*-family
spaces and runs a cascade [Greedy, WAStar(5,3,2,1.5), AStar], keeping
the best incumbent across phases. On a generous budget the final A* phase
returns the proven-optimal answer; on a tight budget you get the best
incumbent so far instead of not_found.
On CSP-shaped spaces (CSPSpace or any space whose initial state is a
partial-assignment dict), AnytimeCSP wins selection under mode="auto"
and runs a cascade [MinConflicts, Backtracking] — MinConflicts is only
included when @evaluate is declared (it greedily picks the lowest-
violation child). The first phase that returns a consistent complete
assignment wins; if MinConflicts gives up, Backtracking takes over.
On pure-optimization spaces (@successors + @evaluate, no @goal),
AnytimeLocal wins selection under mode="auto" and runs a cascade
[HillClimbing, SimulatedAnnealing, TabuSearch] — a fast-probe followed
by two escape phases. The best (lowest-cost) incumbent across all phases
is returned; cancellation mid-cascade keeps whichever incumbent was
already planted. AnytimeLocal does not declare @goal, so on
goal-bearing spaces the selector keeps AnytimeAStar (with @heuristic)
or the uninformed goal algorithms in charge.
On adversarial spaces (.adversarial() + @terminal + @utility),
AnytimeAdversarial wins selection under mode="auto" and iteratively
deepens from depth 1 to max_depth over AlphaBeta (2-player) or
Negamax (3+ player), threading the previous depth's principal
variation as pv_hint into the next phase for move ordering — α-β
pruning becomes substantially more effective with a good first move.
Cancellation mid-deepening keeps the best move from the deepest
completed depth.
SearchResult.epsilon tells you the quality bound: 1.0 is proven
optimal, >1.0 is ε-bounded (cost ≤ ε × optimal), inf is unbounded
(greedy), None means the algorithm doesn't report a bound (e.g.
metaheuristics).
result = space.solver(timeout=10).solve()
if result.optimal:
print(f"Optimal: cost {result.cost}")
else:
print(f"ε-bounded ({result.epsilon}): cost {result.cost}")
Every metaheuristic is naturally anytime regardless of mode — set a
timeout and the algorithm returns its best individual seen so far
when the budget runs out (cooperative CancelToken protocol).
Parallel Evaluation
Population-based algorithms (GA, DE, LocalBeamSearch) support multiprocessing via .parallel(n):
# Evaluate all candidates in parallel across 4 processes
space = Space().initial(lambda: random_genome()).parallel(4)
# evaluate fn must be a module-level function (picklable)
def fitness(genome): return -sum(genome)
space.evaluate(fitness)
result = GeneticAlgorithm(space, pop_size=200, generations=500).solve()
Pass n=1 (default) for serial execution. Falls back automatically when population size is 1.
Direct Algorithm Access
from pathos.algorithms import AStar, GeneticAlgorithm, AlphaBeta
result = AStar(space).solve() # bypass auto-selection
SearchResult
Every algorithm returns a uniform SearchResult:
result.solution # final state
result.path # list of (action, state) steps
result.cost # total cost
result.algorithm # algorithm name
result.nodes_expanded
result.elapsed # seconds
result.found # bool
Performance
Reference numbers from python -m benchmarks.bench --repeat 3 on an Intel
i7-6820HQ @ 2.70 GHz, Python 3.13. Algorithms are the ones auto-selected by
space.solver(). Reproduce with the same command; raw records dumped via
--json.
N-Queens (Backtracking, CSPSpace)
| N | elapsed (s, median) | nodes expanded |
|---|---|---|
| 6 | 0.0003 | 31 |
| 8 | 0.0022 | 113 |
| 10 | 0.0031 | 102 |
| 12 | 0.0149 | 261 |
| 14 | 0.1726 | 1 899 |
| 16 | 1.2363 | 10 052 |
TSP (TabuSearch, TourSpace, 100 iters)
| cities | elapsed (s, median) | tour cost (median) |
|---|---|---|
| 5 | 0.0014 | 197.8 |
| 8 | 0.0060 | 253.3 |
| 12 | 0.0224 | 272.2 |
| 16 | 0.0439 | 354.9 |
| 20 | 0.0800 | 383.4 |
| 25 | 0.1593 | 409.3 |
8-Puzzle (A* + Manhattan)
| scramble depth | elapsed (s, median) | nodes expanded | solution length |
|---|---|---|---|
| 10 | 0.0001 | 12 | 10 |
| 20 | 0.0042 | 461 | 20 |
| 30 | 0.0097 | 1 397 | 24 |
| 40 | 0.0127 | 1 728 | 26 |
| 50 | 0.0086 | 1 186 | 22 |
Solution length plateaus around 22–26 because the 8-puzzle state-space diameter is ~31 — deeper scrambles don't make harder instances.
Examples
License
MIT — gia-uh
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pathos_ai-0.2.0.tar.gz.
File metadata
- Download URL: pathos_ai-0.2.0.tar.gz
- Upload date:
- Size: 97.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ea338341b45db2d066a41f00fde4306769ca6d9ad625d204bbc4fa053d0cb25
|
|
| MD5 |
c7b66c1c610fc35d7778a15f94d393c0
|
|
| BLAKE2b-256 |
0b751dba4ebef033b235a75e8ba09fa935acf1c27f59f09218633f559ca3c993
|
Provenance
The following attestation bundles were made for pathos_ai-0.2.0.tar.gz:
Publisher:
publish.yaml on gia-uh/pathos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pathos_ai-0.2.0.tar.gz -
Subject digest:
3ea338341b45db2d066a41f00fde4306769ca6d9ad625d204bbc4fa053d0cb25 - Sigstore transparency entry: 1711976661
- Sigstore integration time:
-
Permalink:
gia-uh/pathos@aa73e6f7468c689dab09ae19b616a99b58f6585e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/gia-uh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@aa73e6f7468c689dab09ae19b616a99b58f6585e -
Trigger Event:
release
-
Statement type:
File details
Details for the file pathos_ai-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pathos_ai-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cfb6c35ca0ba6a488c1498034b1481f0cbbf582787eec4a36c327558c1a47e7
|
|
| MD5 |
5c18ea61c9ca3a9fa367514c4aa9b413
|
|
| BLAKE2b-256 |
203464b63182044cee85b00cc78ef429a0febe7e02760137acb8ec0929b8234b
|
Provenance
The following attestation bundles were made for pathos_ai-0.2.0-py3-none-any.whl:
Publisher:
publish.yaml on gia-uh/pathos
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pathos_ai-0.2.0-py3-none-any.whl -
Subject digest:
1cfb6c35ca0ba6a488c1498034b1481f0cbbf582787eec4a36c327558c1a47e7 - Sigstore transparency entry: 1711976670
- Sigstore integration time:
-
Permalink:
gia-uh/pathos@aa73e6f7468c689dab09ae19b616a99b58f6585e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/gia-uh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yaml@aa73e6f7468c689dab09ae19b616a99b58f6585e -
Trigger Event:
release
-
Statement type: