3.2 FF Heuristic 的实现

相关知识点

hFF heuristic

• inadmissible
• compromise between hmax and hsum(Delete relaxation heuristics)
• makes sense when actions have unit costs
• 组成部分:1.relaxed reachability + 2.relaxed plan extraction

Step:

1.Builds the graph of reachable propositions and actions until a fix point or the goal is reached. Returns failure if the goal isn’t reachable.(Relaxed reachability algorithm)
过程描述:
relaxed reachability:对初始state 采取一个符合前提条件的action,将新的add effect 和原effect 集建立一个新的effect集,忽略掉delete effect,然后goal测试
(只要effect 集里面存在所有的goal effect,则true )不通过就循环该过程,直到test通过 返回所有effect集和对应的action 或者no actions 返回 false。

2.Works back from the last level to the first, selecting an action to achieve each goal, and inserting its precondition as new goals to be achieved.返回采用的action的数量即为hff(An action does not need to be applied more than once in a relaxed plan)


rom planning_task import Action, Task
from heuristics.heuristic_base import Heuristic
import heapq
import logging
class RelaxedFact:
    """This class represents a relaxed fact."""
    def __init__(self, name):
        """Construct a new relaxed fact.

        Keyword arguments:
        name -- the name of the relaxed fact.

        Member variables:
        name -- the name of the relaxed fact.
        precondition_of -- a list that contains all actions, this fact is a
                           precondition of.
        expanded -- stores whether this fact has been expanded during the
                    reachability forward pass.
        level -- stores the level value of this Fact (the one you get during relaxed reachability). This way we have a much more efficient implementation
        cheapest_achiever -- stores the cheapest action that was applied to reach this fact. Also this information can be obtained
                             during the relaxed reachability
        """
        self.name = name
        self.precondition_of = []
        self.expanded = False
        self.cheapest_achiever = None
        self.level = float('inf')


class RelaxedAction:
    """ This class represents a relaxed action (no delete effects)."""
    def __init__(self, name, preconditions, add_effects):
        """Construct a new relaxed action.

        Keyword arguments:
        name -- the name of the relaxed action.
        preconditions -- the preconditions of this action
        add_effects -- the add effects of this action

        Member variables:
        name -- the name of the relaxed action.
        preconditions -- the preconditions of this action
        counter -- alternative method to check whether all preconditions are
                   True
        add_effects -- the add effects of this action
        cost -- the cost for applying this action
        """
        self.name = name
        self.preconditions = preconditions
        self.add_effects = add_effects
        self.cost = 1
        self.counter = len(preconditions)


class _RelaxationHeuristic(Heuristic):
    """This class is the base class for all relaxation heuristics. 
       You need to look into this code to understand how to compute
       the hff heurstic
    """
    def __init__(self, task):
        """Construct a instance of _RelaxationHeuristic.

        Keyword arguments:
        task -- an instance of the Task class.

        Member variables:
        facts -- a dict that maps from fact names to fact objects
        actions -- a list of actions
        init -- the set of facts that define the initial state
        goals -- the set of facts that define the goal state
        tie_breaker -- a tie breaker needed for qeueing
        eval -- a function that is used to evaluate the cost of applying an
                action
        """
        self.facts = dict()
        self.actions = []
        self.goals = task.goals
        self.init = task.initial_state
        self.tie_breaker = 0
        self.start_state = RelaxedFact("start")

        # Create relaxed facts for all facts in the task description.
        for fact in task.facts:
            self.facts[fact] = RelaxedFact(fact)

        for op in task.actions:
            # Relax actions and add them to action list.
            ro = RelaxedAction(op.name, op.preconditions, op.add_effects)
            self.actions.append(ro)

            # Initialize precondition_of-list for each fact
            for var in op.preconditions:
                self.facts[var].precondition_of.append(ro)

            # Handle actions that have no preconditions.
            if not op.preconditions:
                # We add this action to the precondtion_of list of the start
                # state. This way it can be applied to the start state. This
                # helps also when the initial state is empty.
                self.start_state.precondition_of.append(ro)

    def __call__(self, node):
        """This function is called whenever the heuristic needs to be computed.

        Keyword arguments:
        node -- the current state
        """
        state = node.state
        state = set(state)

        # Reset level and set to default values.
        self.init_level(state)

        # Construct the priority queue.
        heap = []
        # Add a dedicated start state, to cope with actions without
        # preconditions and empty initial state.
        heapq.heappush(heap, (0, self.tie_breaker, self.start_state))
        self.tie_breaker += 1

        for fact in state:
            # Its order is determined by the level the facts.
            # As a tie breaker we use a simple counter.
            heapq.heappush(heap, (self.facts[fact].level, self.tie_breaker,
                                  self.facts[fact]))
            self.tie_breaker += 1

        # Call the reachability search that performs the forward pass.
        self.reachability(heap)

        # Extract the goal heuristic.
        h_value = self.calc_goal_h()

        return h_value

    def init_level(self, state):
        """
        This function resets all member variables that store information that
        needs to be recomputed for each call of the heuristic.
        """
        def reset_fact(fact):
            fact.expanded = False
            fact.cheapest_achiever = None
            if fact.name in state:
                fact.level = 0
                fact.sa_set = set()
            else:
                fact.sa_set = None
                fact.level = float('inf')
        # Reset start state
        reset_fact(self.start_state)

        # Reset facts.
        for fact in self.facts.values():
            reset_fact(fact)

        # Reset actions.
        for action in self.actions:
            action.counter = len(action.preconditions)

    def get_cost(self, action, pre):
        """This function calculated the cost of applying an action, and is an utility 
           function for the reachability algorithm.
        """

        if action.preconditions:
            # If this action has preconditions, we sum over the
            # heuristic values of all preconditions.
            cost = sum([self.facts[pre].level
                              for pre in action.preconditions])
        else:
            # If there are no preconditions for this action, its cost is 0.
            cost = 0

        # this function return the cost of getting to that precondition
        return cost + action.cost

    

    def finished(self, achieved_goals, queue):
        """
        This function is used as a stopping criterion for the reachability search
        """
        return achieved_goals == self.goals or not queue

    def reachability(self, queue):
        """This function is an implementation of a reachability search.
        here levels are organised in a queue so that always the smaller level is expanded first. Every time
        a new fact is achieved by some action in some previous level, this fact may activate new actions. So on and so forth
        till the goal is reached.
        """
        # Stores the achieved goals and facts.
        achieved_goals = set()
        while not self.finished(achieved_goals, queue):
            # Get the fact with the lowest level value.
            (dist, tie, fact) = heapq.heappop(queue)
            # If this node is part of the goal, we add to the goal set, which is used
            # in the finished testing
            if fact.name in self.goals:
                achieved_goals.add(fact.name)
            # Check whether we already expanded this fact.
            if not fact.expanded:
                # Iterate over all actions this fact is a precondition of.
                for action in fact.precondition_of:
                    # Decrease the precondition counter. This is an efficient way to establish whether that action
                    # is reached in thecomputation
                    action.counter -= 1
                    # Check whether all preconditions are True and we can apply
                    # this action.
                    if action.counter <= 0:
                        for n in action.add_effects:
                            rel_fact = self.facts[n]
                            # Calculate the cost of applying this action. This will determine the best achiever
                            tmp_dist = self.get_cost(action,fact)
                            if tmp_dist < rel_fact.level:
                                # If the new costs are cheaper, then the old
                                # costs, we change the rel_fact level value
                                rel_fact.level = tmp_dist
                                rel_fact.cheapest_achiever = action
                                # And push it on the queue.
                                heapq.heappush(queue, (tmp_dist,
                                                       self.tie_breaker,
                                                       rel_fact))
                                self.tie_breaker += 1
                # Finally the fact is marked as expanded.
                fact.expanded = True

class hff(_RelaxationHeuristic):
    """ This class is an implementation of the hFF heuristic.

    It derives from the _RelaxationHeuristic class.
    """
    def __init__(self, task):
        """Construct a hFFHeuristic.
        """
        super().__init__(task)


    def calc_goal_h(self):
        # store the actions from goal states to initial state.
        relaxed_plan = set()
        # a dic, map from levels to the goal facts, means which goal is reached in which level.
        goals_store_dic = {}
        # a list, store relaxed goals
        rel_goal_list = []
        # the maximum level of the plan
        level_max = 0

        # change all the goal states into relaxed states
        for fact in self.goals:
            rel_fact = self.facts[fact]
            rel_goal_list.append(rel_fact)

        # calculate the maximum level of the plan
        for rel_fact in rel_goal_list:
            if level_max < rel_fact.level:
                level_max = rel_fact.level

        # establish a set for every level to store corresponding facts
        for index in range(0,level_max + 1):
            goals_store_dic[index] = set()
        # add relaxed goal states into the corresponding set
        for rel_fact in rel_goal_list:
            goals_store_dic[rel_fact.level].add(rel_fact)


        # go back from the goals level to initial level, then calculate the heuristic value
        for index in range(level_max,0,-1):

            # if the goal_fact set in the current level is not empty
            while goals_store_dic[index]!= set():
                # pop a relaxed goal fact
                rel_fact = goals_store_dic[index].pop()

                # obtain the cheapest action that was applied to reach this fact
                action = rel_fact.cheapest_achiever

                if action != None :
                    # add this action into the plan set
                    relaxed_plan.add(action)

                    # add its preconditions into goals_store_dic of the last level
                    for condition_name in action.preconditions:
                        condition = self.facts[condition_name]
                        goals_store_dic[index - 1].add(condition)


        return len(relaxed_plan)

评价:没有对目标不可达的情况进行讨论

你可能感兴趣的:(3.2 FF Heuristic 的实现)