Example #1
0
def bugsy(initialState, goalTest, actions, successor,
           heuristic = lambda s: 0, maxNodes = 10000,
           visitF = None, expandF = None, hmax = float('inf'),
           prevExpandF = None, checkExpandF = None,
           multipleSuccessors = False,
           verbose = False, printFinal = True, maxHDelta = None,
           maxCost = float('inf'),
           fail = True,
           postFailScan = True,
           returnFirstGoal = False,
           w_f, w_t):
               
        """
        @param initialState: root of the search
        @param goalTest: function from state to Boolean
        @param actions: function from state to list of actions
        @param successor: function from state and action to next state and cost
        @param heuristic: function from state to estimated cost to reach a goal;
            defaults to a heuristic of 0, making this uniform cost search
        @param maxNodes: kill the search after it expands this many nodes
        @param visitF: ?
        @param expandF: ?
        @param hmax: max heuristic val
        @param prevExpandF: ?
        @param checkExpandF: ?
        @param multipleSuccessors: are there multiple successors of a state allowed?
        @param verbose: print a bunch of things while running
        @param printFinal: print final solution
        @param         
        @returns: path from initial state to a goal state as a list of
               (action, state) tuples and a list of path costs from start to
               each state in the path
        """
        
        somewhatVerbose = verbose
        verbose = False

        hVals = {}
        def getH(state):
            if not state in hVals:
                hv = heuristic(state)
                hVals[state] = hv
            return hVals[state]

        startNode = SearchNode(None, initialState, None, 0,
                               getH(initialState))
        if goalTest(initialState):
            return startNode.path(), [0]
        if startNode.heuristicCost >= hmax:
            trAlways('Root has infinite heuristic value', pause = True)
            return None, None
            
        agenda = []
        count = 1
        countExpanded = 0
        heappush(agenda, (0, count, startNode))
        #heap has cost, count = nodes visited, node class
        expanded = set([])
        while not agenda == [] and maxNodes > count:
            if verbose:
                print "agenda: ", agenda
            (util, _, n) = heappop(agenda)
            ######THIS SHOULDNT HAPPEN?#########
            if n.state in expanded:
                if prevExpandF: prevExpandF(n)
                if verbose:
                    print  "previously expanded: ", n.cost, n.state
                    raw_input('okay?')
                continue
            expanded.add(n.state)
            countExpanded += 1

            if checkExpandF:            # check legality on demand
                n = checkExpandF(n)      # possibly modify the node or set to None
                if n is None: continue

            if expandF: expandF(n)
            if verbose: print  "expanding node: ", n.cost, n.state
            if goalTest(n.state):
                # We're done!
                if somewhatVerbose or verbose:
                    print 'Found goal state', n.state
                if somewhatVerbose or verbose or printFinal:
                    print count, 'nodes visited;', \
                          countExpanded, 'states expanded;', \
                          'solution cost:', n.cost
                if getH(n.state) > 0:
                    debugMsg('heuristic', 'positive value at goal state',
                             n.state, getH(n.state))
                return n.path(), n.costs()
            if n.cost > maxCost:
                if True: #somewhatVerbose or verbose:
                    print "Search failed: exceeded max cost ", n.cost
                return None, None

            if getH(n.state)== 0:
                debugMsg('heuristic', 'zero value at non-goal state', n.state)

            applicableActions = actions(n.state)

            if len(applicableActions) == 0 and verbose:
                raw_input('no children')
            if somewhatVerbose or verbose:
                print "   ", n.cost, ":   expanding: ",  n
                print "         ", len(applicableActions), 'actions'
            if countExpanded % 10000 == 0:
                print 'cost', n.cost, 'nodes', count
            successors = set()
            for a in applicableActions:
                succ = successor(n.state, a)
                if not succ: continue
                if not multipleSuccessors:
                    succ = [succ]
                if verbose: print '        ', len(succ), 'successors'
                for (newS, cost) in succ:
                    if newS in successors: continue
                    else: successors.add(newS)
                    if verbose or somewhatVerbose:
                        print '           ', cost, newS
                    hValue = getH(newS)
                    if newS in expanded:
                        if prevExpandF and visitF:
                            newN = SearchNode(a, newS, n, cost, hValue)
                            visitF(n.state, n.cost, n.heuristicCost, a,
                                   newS, newN.cost, hValue)
                            prevExpandF(newN)
                            if maxHDelta and n.heuristicCost - hValue > maxHDelta:
                                print 'current h =', n.heuristicCost, 'new h =', hValue
                                raw_input('H delta exceeded')
                            if verbose:
                                print  "previously expanded: ", \
                                      newN.cost, newN.state
                                raw_input('okay?')
                    else:
                        count += 1
                        tr('h', hValue)
                        if hValue >= hmax: continue
                        newN = SearchNode(a, newS, n, cost, hValue)
                        if visitF: visitF(n.state, n.cost, n.heuristicCost, 
                                          a, newS, newN.cost, hValue)
                        if maxHDelta and n.heuristicCost - hValue > maxHDelta:
                            print 'current h =', n.heuristicCost, \
                              'new h =', hValue
                            raw_input('H delta exceeded')
                        if returnFirstGoal and goalTest(newS):
                            return newN.path(), newN.costs()
                        heappush(agenda,
                                 ((1 - greedy) * newN.cost + \
                                  greedy * hValue, count, newN))

        if somewhatVerbose or verbose or count >= maxNodes:
            print "Search failed after visiting ", count, " states."

        if postFailScan:
            while not agenda == []:
                (util, _, n) = heappop(agenda)
                if n.state in expanded: continue
                if checkExpandF:        # check legality on demand
                    n = checkExpandF(n) # possibly modify the node or set to None
                    if n is None: continue
                if goalTest(n.state):
                    if expandF: expandF(n)  # Treat like an expansion
                    print 'Found goal on agenda, returning it'
                    return n.path(), n.costs()

        return None, None
Example #2
0
def bugsy(initialState, goalTest, actions, successor,
           heuristic = lambda s: 0, maxNodes = 10000,
           visitF = None, expandF = None, hmax = float('inf'),
           prevExpandF = None, checkExpandF = None,
           multipleSuccessors = False,
           verbose = False, printFinal = True, maxHDelta = None,
           maxCost = float('inf'),
           fail = True,
           postFailScan = True,
           returnFirstGoal = False,
           w_f, w_t):
               
        """
        @param initialState: root of the search
        @param goalTest: function from state to Boolean
        @param actions: function from state to list of actions
        @param successor: function from state and action to next state and cost
        @param heuristic: function from state to estimated cost to reach a goal;
            defaults to a heuristic of 0, making this uniform cost bugsy
        @param maxNodes: kill the search after it expands this many nodes
        @param visitF: function occurring on visits
        @param expandF: function occurring on expansion
        @param hmax: max heuristic val
        @param prevExpandF: function occurring if previously node has been expanded
        @param checkExpandF: check legality of expanding node n?
        @param multipleSuccessors: are there multiple successors of a state allowed?
        @param verbose: print a bunch of things while running
        @param printFinal: print final solution
        @param maxHDelta: maximum change in heuristic across parent-child nodes
        @param maxCost: maximum cost of a state allowed
        @param fail: ?
        @param postFailScan: perform a scan after failure
        @param returnFirstGoal: I want to return first goal found
        @param w_f: weight on estimated cost of solution
        @param w_t: weight on estimated time to reach solution from current expansion
        
        @returns: path from initial state to a goal state as a list of
               (action, state) tuples and a list of path costs from start to
               each state in the path
        """
        
        ########### HELPER METHODS #############
        def getH(state):
            if not state in hVals:
                hv = heuristic(state)
                hVals[state] = hv
            return hVals[state]
        
        def getUtil(node, delay, t_exp, w_f, w_t):
            """
            calculate the utility of a state based on estimated cost and time to obtain solution
            
            @param node: a SearchNode
            @param delay: estimated delay expansions for each of the remaining steps from current node
            @param t_exp: estimated time to expand a node, in seconds
            @param w_f: weight on the cost function
            @param w_t: weight on the time to obtain solution
            
            @returns: floating point utility of a node
            """
            return w_f*(node.cost + node.heuristicCost) + w_t*(node.heuristicCost * delay * t_exp)

        def updateEstimates(delay, t_exp, agenda, w_f, w_t):
            """
            @param delay: most recent global delay estimate
            @param t_exp: most recent global t_exp estimate
            @param agenda: agenda to be re-sorted based on most recent estimates, assumes agenda has elements of the form (utility, count, SearchNode)
            @param w_f: weight on cost function
            @param w_t: weight on time to obtain solution
            
            @returns: newly heapified agenda
            """
            newAgenda = []
            for item in agenda:
                heappush(newAgenda, (getUtil(item[2], delay, t_exp, w_f, w_t), item[1], item[2]))
            return newAgenda
        def newDelay(delayList, current_count, node_count):
            """
            @param delayList: list of past delay estimates
            @param current_count: count of the expansions completed
            @param node_count: count of expansions at generation of agenda item
            
            @returns: a new global average delay estimate to be added to list
            """
            n_d = len(delayList) + 1.
            new_delay = (1/n_d) * (current_count - node_count) + (n_d-1)/n_d * delayList[-1]
            return new_delay
        def newT_exp(t_exp_list, t_exp):
            """
            @param t_exp_list: list of past t_exp estimates
            @param t_exp: new estimate to average
            
            @returns: a new global average t_exp estimate to be added to list
            """
            n_t = len(t_exp_list) + 1.
            new_t_exp = (1/n_t) * t_exp + (n_t-1)/n_t * t_exp_list[-1]
            return new_t_exp
        def searchFinished(n, somewhatVerbose, verbose, count):
            """
            things we do when goal state is expanded
            
            @param n: current node
            @somewhatVerbose: how much printing
            @verbose: more conditionals on printing
            @param count: steps of expansion taken
            
            @returns: 
            """
            if somewhatVerbose or verbose:
                print 'Found goal state', n.state
            if somewhatVerbose or verbose or printFinal:
                print count, 'nodes visited;', \
                        countExpanded, 'states expanded;', \
                        'solution cost:', n.cost
            if getH(n.state) > 0:
                debugMsg('heuristic', 'positive value at goal state',
                         n.state, getH(n.state))
            return None            
        ########### End Helper Methods ############
        
        
        ########### BUGSY INITIALIZATION #############
        delayList = []
        t_expList = []
        global_delay = 0
        global_t_exp = 0
        epsilon_0 = 10**(-12)
        somewhatVerbose = verbose
        verbose = False
        hVals = {}
        startNode = SearchNode(None, initialState, None, 0,
                               getH(initialState))
        if goalTest(initialState):
            return startNode.path(), [0]
        if startNode.heuristicCost >= hmax:
            trAlways('Root has infinite heuristic value', pause = True)
            return None, None   
        agenda = []
        count = 1
        countExpanded = 0
        heappush(agenda, (0, count, startNode)) #agenda objects have cost = utility, count = nodes visited, node class
        expanded = set([]) #set of states, different than agenda objects
        ############ End Initialization #############
        
        ############## BUGSY SEARCH #################
        while not agenda == [] and maxNodes > count:
        ### 0. BUGSY ESTIMATES UPDATE: on powers of 2, update agenda with current estimates
            if (abs(math.log(count, 2)-math.floor(math.log(count, 2))) < epsilon_0 or \
                    abs(math.log(count, 2)-math.ceil(math.log(count, 2))) < epsilon_0) and \
                    count > 1:
                global_delay = delayList[-1]
                global_t_exp = t_expList[-1]
                agenda = updateEstimates(global_delay, global_t_exp, agenda, w_f, w_t)
                if verbose:
                    print 'updated delay: ', global_delay, ', updated t_exp: ', global_t_exp 
                
        ### 1. PREPARE TO EXPAND: pop off of heap, test if goal, and check legal actions/maxCost. print a bunch of things
            if verbose:
                print "agenda: ", agenda
            (util, n_count, n) = heappop(agenda)
            start_expansion_time = time.clock()
            if count > 1: delayList.append(newDelay(delayList, count, n_count))
            if n.state in expanded: #should this ever happen?
                if prevExpandF: prevExpandF(n)
                if verbose:
                    print  "previously expanded: ", n.cost, n.state
                    raw_input('okay?')
                continue
            expanded.add(n.state)
            countExpanded += 1
            if checkExpandF:            # check legality on demand
                n = checkExpandF(n)      # possibly modify the node or set to None
                if n is None: continue      
            if expandF: expandF(n)
            if verbose: print  "expanding node: ", n.cost, n.state
            if goalTest(n.state):
                searchFinished(n, somewhatVerbose, Verbose, count)
                return n.path(), n.costs()
            if n.cost > maxCost:
                if True: #somewhatVerbose or verbose:
                    print "Search failed: exceeded max cost ", n.cost
                return None, None
            if getH(n.state)== 0:
                debugMsg('heuristic', 'zero value at non-goal state', n.state)
            applicableActions = actions(n.state)
            if len(applicableActions) == 0 and verbose:
                raw_input('no children')
            if somewhatVerbose or verbose:
                print "   ", n.cost, ":   expanding: ",  n
                print "         ", len(applicableActions), 'actions'
            if countExpanded % 10000 == 0:
                print 'cost', n.cost, 'nodes', count
            successors = set()  
            
        ### 2. EXPAND BY EXPLORING ACTIONS: add children to agenda
            for a in applicableActions:
                succ = successor(n.state, a)
                if not succ: continue
                if not multipleSuccessors:
                    succ = [succ]
                if verbose: print '        ', len(succ), 'successors'
                for (newS, cost) in succ:
                    if newS in successors: continue
                    else: successors.add(newS)
                    if verbose or somewhatVerbose:
                        print '           ', cost, newS
                    hValue = getH(newS)
                    if newS in expanded:
                        if prevExpandF and visitF:
                            newN = SearchNode(a, newS, n, cost, hValue)
                            visitF(n.state, n.cost, n.heuristicCost, a,
                                   newS, newN.cost, hValue)
                            prevExpandF(newN)
                            if maxHDelta and n.heuristicCost - hValue > maxHDelta:
                                print 'current h =', n.heuristicCost, 'new h =', hValue
                                raw_input('H delta exceeded')
                            if verbose:
                                print  "previously expanded: ", \
                                      newN.cost, newN.state
                                raw_input('okay?')
                    else:
                        count += 1
                        tr('h', hValue)
                        if hValue >= hmax: continue
                        newN = SearchNode(a, newS, n, cost, hValue)
                        if visitF: visitF(n.state, n.cost, n.heuristicCost, 
                                          a, newS, newN.cost, hValue)
                        if maxHDelta and n.heuristicCost - hValue > maxHDelta:
                            print 'current h =', n.heuristicCost, \
                              'new h =', hValue
                            raw_input('H delta exceeded')
                        if returnFirstGoal and goalTest(newS):
                            return newN.path(), newN.costs()
                        util = getUtil(newN, global_delay, global_t_exp, w_f, w_t)
                        heappush(agenda,
                                 (util, count, newN))
            #done expanding, add new time of expansion estimate                     
            t_exp_list.append(newT_exp(t_exp_list, time.clock() - start_expansion_time))
        ### 3. EXPANSIONS TERMINATED: the BUGSY search failed to return a solution        
        if somewhatVerbose or verbose or count >= maxNodes:
            print "Search failed after visiting ", count, " states."
        if postFailScan:
            while not agenda == []:
                (util, _, n) = heappop(agenda)
                if n.state in expanded: continue
                if checkExpandF:        # check legality on demand
                    n = checkExpandF(n) # possibly modify the node or set to None
                    if n is None: continue
                if goalTest(n.state):
                    if expandF: expandF(n)  # Treat like an expansion
                    print 'Found goal on agenda, returning it'
                    return n.path(), n.costs()
        return None, None