Source code for concepts.algorithm.search.heuristic_search

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File   : heuristic_search.py
# Author : Jiayuan Mao
# Email  : maojiayuan@gmail.com
# Date   : 11/17/2022
#
# This file is part of Project Concepts.
# Distributed under terms of the MIT license.

"""Generic implementation of A* search."""

from dataclasses import dataclass
from typing import TypeVar, List, Callable, Iterator, Tuple, Set
import heapq as hq

__all__ = ['SearchNode', 'QueueNode', 'run_heuristic_search', 'backtrace_plan']

State = TypeVar('State')
Action = TypeVar('Action')


[docs] @dataclass class SearchNode(object): """A node object corresponding to the current search state, containing the state, the parent node, the last action, the cost, and the depth.""" state: State """The current state.""" parent: 'SearchNode' """The parent node.""" action: Action """The action that leads to the current state.""" cost: float """The cost of the path from the root to the current state.""" g: float """The estimated cost-to-go."""
[docs] @dataclass class QueueNode(object): """A node object in the queue, containing the priority and the search node.""" priority: float """The priority of the node.""" node: SearchNode """The search node.""" @property def state(self): return self.node.state
[docs] def __iter__(self): yield self.priority yield self.node
def __lt__(self, other): # so we don't need the tie-breaker. return self.priority < other.priority
[docs] def backtrace_plan(node: SearchNode, nr_expansions: int) -> Tuple[List[State], List[Action], List[float], int]: """Backtrace the plan from the goal node. Args: node: a search node where the goal is satisfied. nr_expansions: the number of expansions. This value will be returned by this function. Returns: a tuple of (state_sequence, action_sequence, cost_sequence, nr_expansions). """ state_sequence = [] action_sequence = [] cost_sequence = [] while node.parent is not None: action_sequence.insert(0, node.action) state_sequence.insert(0, node.state) cost_sequence.insert(0, node.cost) node = node.parent state_sequence.insert(0, node.state) return state_sequence, action_sequence, cost_sequence, nr_expansions