Skip to main content

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

CI PyPI Python License: MIT

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pathos_ai-0.2.0.tar.gz (97.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pathos_ai-0.2.0-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

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

Hashes for pathos_ai-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3ea338341b45db2d066a41f00fde4306769ca6d9ad625d204bbc4fa053d0cb25
MD5 c7b66c1c610fc35d7778a15f94d393c0
BLAKE2b-256 0b751dba4ebef033b235a75e8ba09fa935acf1c27f59f09218633f559ca3c993

See more details on using hashes here.

Provenance

The following attestation bundles were made for pathos_ai-0.2.0.tar.gz:

Publisher: publish.yaml on gia-uh/pathos

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for pathos_ai-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1cfb6c35ca0ba6a488c1498034b1481f0cbbf582787eec4a36c327558c1a47e7
MD5 5c18ea61c9ca3a9fa367514c4aa9b413
BLAKE2b-256 203464b63182044cee85b00cc78ef429a0febe7e02760137acb8ec0929b8234b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pathos_ai-0.2.0-py3-none-any.whl:

Publisher: publish.yaml on gia-uh/pathos

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page