예제 #1
0
def beam_search(problem, beam_width=1, graph=True):
    """
    A variant of breadth-first search where all nodes in the fringe
    are expanded, but the resulting new fringe is limited to have length
    beam_width, where the nodes with the worst value are dropped. The default
    beam width is 1, which yields greedy best-first search (i.e., hill
    climbing).

    There are different ways to implement beam search, namely best-first
    beam search and breadth-first beam search. According to:

        Wilt, C. M., Thayer, J. T., & Ruml, W. (2010). A comparison of
        greedy search algorithms. In Third Annual Symposium on Combinatorial
        Search.

    breadth-first beam search almost always performs better. They find that
    allowing the search to re-expand duplicate nodes if they have a lower cost
    improves search performance. Thus, our implementation is a breadth-first
    beam search that re-expand duplicate nodes with lower cost.

    :param problem: The problem to solve.
    :type problem: :class:`Problem`
    :param beam_width: The size of the beam (defaults to 1).
    :type beam_width: int
    :param graph_search: whether to use graph or tree search.
    :type graph_search: boolean
    """
    closed = {}
    fringe = PriorityQueue(node_value=problem.node_value)
    fringe.push(problem.initial)
    closed[problem.initial] = problem.initial.cost()

    while len(fringe) > 0:
        parents = []
        while len(fringe) > 0 and len(parents) < beam_width:
            parent = fringe.pop()
            if problem.goal_test(parent, problem.goal):
                yield SolutionNode(parent, problem.goal)
            parents.append(parent)
        fringe.clear()

        for node in parents:
            for s in problem.successors(node):
                if not graph:
                    fringe.push(s)
                elif s not in closed or s.cost() < closed[s]:
                    fringe.push(s)
                    closed[s] = s.cost()
예제 #2
0
def beam_search(problem, beam_width=1, graph_search=True):
    """
    A variant of breadth-first search where all nodes in the fringe
    are expanded, but the resulting new fringe is limited to have length
    beam_width, where the nodes with the worst value are dropped. The default
    beam width is 1, which yields greedy best-first search (i.e., hill climbing).

    There are different ways to implement beam search, namely best-first
    beam search and breadth-first beam search. According to:

        Wilt, C. M., Thayer, J. T., & Ruml, W. (2010). A comparison of
        greedy search algorithms. In Third Annual Symposium on Combinatorial
        Search.

    breadth-first beam search almost always performs better. They find that
    allowing the search to re-expand duplicate nodes if they have a lower cost
    improves search performance. Thus, our implementation is a breadth-first
    beam search that re-expand duplicate nodes with lower cost.

    :param problem: The problem to solve.
    :type problem: :class:`Problem`
    :param beam_width: The size of the beam (defaults to 1).
    :type beam_width: int
    :param graph_search: whether to use graph or tree search.
    :type graph_search: boolean
    """
    closed = {}
    fringe = PriorityQueue(node_value=problem.node_value)
    fringe.push(problem.initial)
    closed[problem.initial] = problem.initial.cost()

    while len(fringe) > 0:
        parents = []
        while len(fringe) > 0 and len(parents) < beam_width:
            parent = fringe.pop()
            if problem.goal_test(parent):
                yield parent
            parents.append(parent)
        fringe.clear()

        for node in parents:
            for s in problem.successors(node):
                if not graph_search:
                    fringe.push(s)
                elif s not in closed or s.cost() < closed[s]:
                    fringe.push(s)
                    closed[s] = s.cost()
예제 #3
0
def local_beam_search(problem, beam_width=1, max_sideways=0,
                      graph_search=True, cost_limit=float('-inf')):
    """
    A variant of :func:`py_search.informed_search.beam_search` that can be
    applied to local search problems.  When the beam width of 1 this approach
    yields behavior similar to :func:`hill_climbing`.

    :param problem: The problem to solve.
    :type problem: :class:`py_search.base.Problem`
    :param beam_width: The size of the search beam.
    :type beam_width: int
    :param max_sideways: Specifies the max number of contiguous sideways moves. 
    :type max_sideways: int
    :param graph_search: Whether to use graph search (no duplicates) or tree
        search (duplicates)
    :type graph_search: Boolean
    """
    b = None
    bv = float('inf')
    sideways_moves = 0

    fringe = PriorityQueue(node_value=problem.node_value)
    fringe.push(problem.initial)

    while len(fringe) < beam_width:
        fringe.push(problem.random_node())
    
    if graph_search:
        closed = set()
        closed.add(problem.initial)

    while len(fringe) > 0 and sideways_moves <= max_sideways:
        pv = fringe.peek_value()

        if pv > bv:
            yield b

        if pv == bv:
            sideways_moves += 1
        else:
            sideways_moves = 0

        parents = []
        while len(fringe) > 0 and len(parents) < beam_width:
            parent = fringe.pop()
            parents.append(parent)
        fringe.clear()

        b = parents[0]
        bv = pv

        for node in parents:
            for s in problem.successors(node):
                added = True
                if not graph_search:
                    fringe.push(s)
                elif s not in closed:
                    fringe.push(s)
                    closed.add(s)
                else:
                    added = False

                if added and fringe.peek_value() <= cost_limit:
                    yield fringe.peek()


    yield b
예제 #4
0
def local_beam_search(problem, beam_width=1, max_sideways=0, graph=True):
    """
    A variant of :func:`py_search.informed_search.beam_search` that can be
    applied to local search problems.  When the beam width of 1 this approach
    yields behavior similar to :func:`hill_climbing`.

    The problem.goal_test function can be used to terminate search early if a
    good enough solution has been found. If goal_test(node) return True, then
    search is immediately terminated and the node is returned.

    :param problem: The problem to solve.
    :type problem: :class:`py_search.base.Problem`
    :param beam_width: The size of the search beam.
    :type beam_width: int
    :param max_sideways: Specifies the max number of contiguous sideways moves.
    :type max_sideways: int
    :param graph: Whether to use graph search (no duplicates) or tree
        search (duplicates)
    :type graph: Boolean
    """
    b = None
    bv = float('inf')
    sideways_moves = 0

    fringe = PriorityQueue(node_value=problem.node_value)
    fringe.push(problem.initial)

    while len(fringe) < beam_width:
        fringe.push(problem.random_node())

    if graph:
        closed = set()
        closed.add(problem.initial)

    while len(fringe) > 0 and sideways_moves <= max_sideways:
        pv = fringe.peek_value()

        if bv < pv:
            yield SolutionNode(b, problem.goal)

        if pv == bv:
            sideways_moves += 1
        else:
            sideways_moves = 0

        parents = []
        while len(fringe) > 0 and len(parents) < beam_width:
            parent = fringe.pop()
            parents.append(parent)
        fringe.clear()

        b = parents[0]
        bv = pv

        for node in parents:
            if problem.goal_test(node, problem.goal):
                yield SolutionNode(node, problem.goal)

            for s in problem.successors(node):
                if not graph:
                    fringe.push(s)
                elif s not in closed:
                    fringe.push(s)
                    closed.add(s)

    yield SolutionNode(b, problem.goal)