diff --git a/Planning/my_planning_graph.py b/Planning/my_planning_graph.py index 47b3299..8d68c8c 100755 --- a/Planning/my_planning_graph.py +++ b/Planning/my_planning_graph.py @@ -304,13 +304,14 @@ def add_action_level(self, level): :return: adds A nodes to the current level in self.a_levels[level] ''' - # TODO add action A level to the planning graph as described in the Russell-Norvig text - # 1. determine what actions to add and create those PgNode_a objects - # 2. connect the nodes to the previous S literal level - # for example, the A0 level will iterate through all possible actions for the problem and add a PgNode_a to a_levels[0] - # set iff all prerequisite literals for the action hold in S0. This can be accomplished by testing - # to see if a proposed PgNode_a has prenodes that are a subset of the previous S level. Once an - # action node is added, it MUST be connected to the S node instances in the appropriate s_level set. + self.a_levels.append(set()) + for action in self.all_actions: + candidate_a = PgNode_a(action) + if candidate_a.prenodes.issubset(self.s_levels[level]): + for node_s in self.s_levels[level]: + self.a_levels[level].add(candidate_a) + candidate_a.parents.add(node_s) + node_s.children.add(candidate_a) def add_literal_level(self, level): ''' add an S (literal) level to the Planning Graph @@ -329,6 +330,12 @@ def add_literal_level(self, level): # may be "added" to the set without fear of duplication. However, it is important to then correctly create and connect # all of the new S nodes as children of all the A nodes that could produce them, and likewise add the A nodes to the # parent sets of the S nodes + self.s_levels.append(set()) + for node_a in self.a_levels[level-1]: + for node_s in node_a.effnodes: # states that the action could produce + node_a.children.add(node_s) + node_s.parents.add(node_a) + self.s_levels[level].add(node_s) def update_a_mutex(self, nodeset): ''' Determine and update sibling mutual exclusion for A-level nodes @@ -386,8 +393,8 @@ def inconsistent_effects_mutex(self, node_a1: PgNode_a, node_a2: PgNode_a) -> bo :param node_a2: PgNode_a :return: bool ''' - # TODO test for Inconsistent Effects between nodes - return False + return (set(node_a1.action.effect_add) & set(node_a2.action.effect_rem) | + set(node_a1.action.effect_rem) & set(node_a2.action.effect_add)) def interference_mutex(self, node_a1: PgNode_a, node_a2: PgNode_a) -> bool: ''' @@ -403,8 +410,10 @@ def interference_mutex(self, node_a1: PgNode_a, node_a2: PgNode_a) -> bool: :param node_a2: PgNode_a :return: bool ''' - # TODO test for Interference between nodes - return False + return (set(node_a1.action.effect_add) & set(node_a2.action.precond_neg)| + set(node_a2.action.effect_add) & set(node_a1.action.precond_neg)| + set(node_a1.action.effect_rem) & set(node_a2.action.precond_pos)| + set(node_a2.action.effect_rem) & set(node_a1.action.precond_pos)) def competing_needs_mutex(self, node_a1: PgNode_a, node_a2: PgNode_a) -> bool: ''' @@ -417,8 +426,18 @@ def competing_needs_mutex(self, node_a1: PgNode_a, node_a2: PgNode_a) -> bool: :return: bool ''' - # TODO test for Competing Needs between nodes - return False + a1_precond_mutexes = set() + a2_precond_mutexes = set() + + for item in node_a1.parents: + for m in item.mutex: + a1_precond_mutexes.add(m) + + for item in node_a2.parents: + for m in item.mutex: + a2_precond_mutexes.add(m) + + return bool((a1_precond_mutexes & node_a2.parents) | (a2_precond_mutexes & node_a1.parents)) def update_s_mutex(self, nodeset: set): ''' Determine and update sibling mutual exclusion for S-level nodes @@ -452,8 +471,7 @@ def negation_mutex(self, node_s1: PgNode_s, node_s2: PgNode_s) -> bool: :param node_s2: PgNode_s :return: bool ''' - # TODO test for negation between nodes - return False + return (node_s1.symbol == node_s2.symbol) and (node_s1.is_pos != node_s2.is_pos) def inconsistent_support_mutex(self, node_s1: PgNode_s, node_s2: PgNode_s): ''' @@ -471,8 +489,16 @@ def inconsistent_support_mutex(self, node_s1: PgNode_s, node_s2: PgNode_s): :param node_s2: PgNode_s :return: bool ''' - # TODO test for Inconsistent Support between nodes - return False + mutexes_s1 = set() + mutexes_s2 = set() + for p in node_s1.parents: + mutexes_s1 |= p.mutex + + for p in node_s2.parents: + mutexes_s2 |= p.mutex + + return (not bool(node_s1.parents - mutexes_s2) or not bool(node_s2.parents - mutexes_s1)) \ + and not bool(node_s1.parents & node_s2.parents) def h_levelsum(self) -> int: '''The sum of the level costs of the individual goals (admissible if goals independent) @@ -480,6 +506,9 @@ def h_levelsum(self) -> int: :return: int ''' level_sum = 0 - # TODO implement - # for each goal in the problem, determine the level cost, then add them together + for goal in self.problem.goal: + for level, s_nodes in enumerate(self.s_levels): + if PgNode_s(goal, True) in s_nodes: + level_sum += level + break return level_sum