def __init__(self, ID=None, type_graph=None, name=None, Elements=None, plan_elm=None, Edges=None, Restrictions=None): if ID is None: ID = uuid4() if type_graph is None: type_graph = 'PlanElementGraph' if Elements is None: Elements = set() if Edges is None: Edges = set() if Restrictions is None: Restrictions = set() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() self.flaws = FlawLib() self.solved = False self.initial_dummy_step = None self.final_dummy_step = None if plan_elm is None: plan_elm = Element(ID=ID, typ=type_graph, name=name) super(PlanElementGraph, self).__init__(ID, type_graph, name, Elements, plan_elm, Edges, Restrictions)
def __init__(self, dummy_init_constructor, dummy_goal_constructor): self.ID = uuid4() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() # self.HierarchyGraph = HierarchyGraph() self.flaws = FlawLib() self.solved = False self.dummy = dummyTuple(dummy_init_constructor.instantiate(), dummy_goal_constructor.instantiate()) self.init = self.dummy.init.preconds self.goal = self.dummy.final.preconds self.steps = [self.dummy.init, self.dummy.final] # check if any existing steps are choices (instances of cndts of open conditions) self.dummy.final.update_choices(self) self.cndt_map = None self.threat_map = None # self.gstep_lib = ground_step_list # self.h_step_dict = dict() self.heuristic = float('inf') self.name = '' self.cost = 0 self.depth = 0
def __init__(self, operator, args, preconditions, effects, stepnum, height): # READ-ONLY ATTRIBUTES # # schema refers to the name of the operator self.schema = operator # Args are Argument or Actor "Element" types self.Args = args # ID used as "instance ID" self.ID = uuid4() # preconds is a list of GCond self.preconds = preconditions self.effects = effects # stepnum is the ground step constructor type self.stepnum = stepnum self.stepnumber = stepnum # height is 0 when primitive self.height = height if height > 0: self.sub_steps = [] self.sub_orderings = OrderingGraph() self.sub_links = CausalLinkGraph() self.dummy = dummyTuple(None, None) # depth starts at 0 and takes on value during planning self.depth = 0 self.cndts = None self.cndt_map = None self.threat_map = None self.threats = None self.cntg_mental = None self.instantiable = True # INSTANCE ATTRIBUTES # # risks are number of threat instances self.risks = list() self.choices = list() # choices are instances of cndt antecedents self.choice_map = dict() # self.num_choices = 0 # open preconditions which need causal link self.open_preconds = list(self.preconds)
def swap_substeps(self, gsteps, GL, decomp_step): # base case - sub-steps are all height = 0 primitive_substeps = [arg for arg in decomp_step.ground_subplan.elements if type(arg) == Operator and arg.height == 0] composite_substeps = [arg for arg in decomp_step.ground_subplan.elements if type(arg) == Operator and arg.height > 0] if len(composite_substeps) == 0: prim_dict = {step: gsteps[step.stepnumber].instantiate() for step in primitive_substeps} self.create_composite_gstep(gsteps, decomp_step, prim_dict) return prim_dict else: # links and orderings in intermediate stage # change_dict = {step.root: gsteps[step.stepnumber].instantiate() for step in decomp_step.ground_subplan.Root_Graphs} change_dict = {} # order steps by height, lowest to highest. step_list = [step for step in decomp_step.ground_subplan.Step_Graphs] step_list.sort(key=lambda x: x.height) do_not_add_as_substep = [] for step in step_list: Args = [decompile(arg, decomp_step.ground_subplan) for arg in step.Args] preconds = [GLiteral(p.name, [decompile(arg, p) for arg in p.Args], p.truth, p.replaced_ID, (p.name, p.truth) not in GL.non_static_preds) for p in step.Preconditions] effects = [GLiteral(e.name, [decompile(arg, e) for arg in e.Args], e.truth, e.replaced_ID, (e.name, e.truth) not in GL.non_static_preds) for e in step.Effects] schema = str(step) step_copy = GStep(schema, Args, preconds, effects, step.stepnumber, step.height) step_copy.ID = step.root.ID st_t = gsteps[step.stepnumber].instantiate() step_copy.swap_setup(st_t.cndts, st_t.cndt_map, st_t.threats, st_t.threat_map, st_t.cntg_mental) # give no children if step.height > 0: init_step = st_t.dummy[0] init_step.schema = "begin:" + str(step) final_step = st_t.dummy[1] final_step.schema = "finish:" + str(step) step_copy.dummy = dummyTuple(init_step, final_step) # step_copy.sub_steps = [] children = decomp_step.ground_subplan.DecompGraph.getNeighbors(step.root) step_copy.sub_steps = [change_dict[child] for child in children if child.typ != 'step-s'] do_not_add_as_substep.extend(step_copy.sub_steps) step_copy.sub_orderings = OrderingGraph() step_copy.sub_links = CausalLinkGraph() change_dict[step.root] = step_copy # self.sub_steps.append(step) self.create_composite_gstep(gsteps, decomp_step, change_dict, do_not_add_as_substep) return change_dict
def Actions_2_Plan(cls, Actions, h): # Used by Plannify if not checkHeight(Actions, h): return None elements = set().union(*[A.elements for A in Actions]) edges = set().union(*[A.edges for A in Actions]) Plan = cls(name='Action_2_Plan', Elements=elements, Edges=edges) for edge in Plan.edges: if edge.label == 'effect-of': elm = Plan.getElementById(edge.sink.ID) elm.replaced_ID = edge.sink.replaced_ID Plan.OrderingGraph = OrderingGraph() Plan.CausalLinkGraph = CausalLinkGraph() # Plan.Steps = [A.root for A in Actions] return Plan
class PlanElementGraph(ElementGraph): def __init__(self, ID=None, type_graph=None, name=None, Elements=None, plan_elm=None, Edges=None, Restrictions=None): if ID is None: ID = uuid4() if type_graph is None: type_graph = 'PlanElementGraph' if Elements is None: Elements = set() if Edges is None: Edges = set() if Restrictions is None: Restrictions = set() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() self.flaws = FlawLib() self.solved = False self.initial_dummy_step = None self.final_dummy_step = None if plan_elm is None: plan_elm = Element(ID=ID, typ=type_graph, name=name) super(PlanElementGraph, self).__init__(ID, type_graph, name, Elements, plan_elm, Edges, Restrictions) def __hash__(self): return hash(self.name) ^ hash(self.typ) ^ hash(self.ID) @classmethod def Actions_2_Plan(cls, Actions, h): # Used by Plannify if not checkHeight(Actions, h): return None elements = set().union(*[A.elements for A in Actions]) edges = set().union(*[A.edges for A in Actions]) Plan = cls(name='Action_2_Plan', Elements=elements, Edges=edges) for edge in Plan.edges: if edge.label == 'effect-of': elm = Plan.getElementById(edge.sink.ID) elm.replaced_ID = edge.sink.replaced_ID Plan.OrderingGraph = OrderingGraph() Plan.CausalLinkGraph = CausalLinkGraph() # Plan.Steps = [A.root for A in Actions] return Plan def UnifyActions(self, P, G): # Used by Plannify NG = G.deepcopy(replace_internals=True) for edge in list(NG.edges): if edge.sink.replaced_ID == -1: sink = copy.deepcopy(edge.sink) sink.replaced_ID = edge.sink.ID self.elements.add(sink) else: sink = P.getElmByRID(edge.sink.replaced_ID) if sink is None: sink = copy.deepcopy(edge.sink) self.elements.add(sink) source = P.getElmByRID(edge.source.replaced_ID) if source is None: source = copy.deepcopy(edge.source) self.elements.add(source) self.edges.add(Edge(source, sink, edge.label)) def deepcopy(self): new_self = copy.deepcopy(self) new_self.ID = uuid4() return new_self def RemoveSubgraph(self, literal): edges = list(self.edges) elm = self.getElementById(literal.ID) link = None self.elements.remove(elm) for edge in list(self.edges): if edge.source == elm: edges.remove(edge) if link is None: if edge.sink == elm: link = edge edges.remove(link) self.edges = set(edges) return link def ReplaceSubgraphs(self, literal_old, literal_new): edges = list(self.edges) elm = self.getElementById(literal_old.ID) link = None self.elements.remove(elm) for edge in list(self.edges): if edge.source == elm: edges.remove(edge) if link is None: if edge.sink == elm: link = edge edges.remove(link) link.sink = literal_new self.elements.add(literal_new) self.edges = set(edges) self.edges.add(link) return link def AddSubgraph(self, subgraph): self.elements.update(subgraph.elements) self.edges.update(subgraph.edges) def isInternallyConsistent(self): return self.OrderingGraph.isInternallyConsistent() and self.CausalLinkGraph.isInternallyConsistent() and \ super(PlanElementGraph, self).isInternallyConsistent() @property def Steps(self): return [ element for element in self.elements if type(element) is Operator ] @property def Step_Graphs(self): return [Action.subgraph(self, step) for step in self.Steps] @property def Steps_Sorted(self): pass def detectTCLFperCL(self, GL, causal_link): detectedThreatenedCausalLinks = set() for step in self.Steps: self.testThreat(GL, self.CausalLinkGraph.nonThreats, causal_link, step, detectedThreatenedCausalLinks) return detectedThreatenedCausalLinks def detectTCLFperStep(self, GL, step): detectedThreatenedCausalLinks = set() for causal_link in self.CausalLinkGraph.edges: self.testThreat(GL, self.CausalLinkGraph.nonThreats, causal_link, step, detectedThreatenedCausalLinks) return detectedThreatenedCausalLinks def testThreat(self, GL, nonThreats, causal_link, step, dTCLFs): if step in nonThreats[causal_link]: return if step == causal_link.source or step == causal_link.sink: nonThreats[causal_link].add(step) return if self.OrderingGraph.isPath(causal_link.sink, step): nonThreats[causal_link].add(step) return if self.OrderingGraph.isPath(step, causal_link.source): nonThreats[causal_link].add(step) return if step.stepnumber not in GL.threat_dict[causal_link.sink.stepnumber]: nonThreats[causal_link].add(step) return if test(Action.subgraph(self, step), causal_link): dTCLFs.add(TCLF((step, causal_link), 'tclf')) nonThreats[causal_link].add(step) #@clock def detectThreatenedCausalLinks(self, GL): detectedThreatenedCausalLinks = set() for causal_link in self.CausalLinkGraph.edges: for step in self.Steps: self.testThreat(GL, self.CausalLinkGraph.nonThreats, causal_link, step, detectedThreatenedCausalLinks) return detectedThreatenedCausalLinks def __repr__(self): c = '\ncost {} + heuristic {}'.format(self.cost, self.heuristic) steps = [''.join('\t' + str(step) + '\n' for step in self.Step_Graphs)] order = [ ''.join('\t' + str(ordering.source) + ' < ' + str(ordering.sink) + '\n' for ordering in self.OrderingGraph.edges) ] links = [ ''.join('\t' + str(cl) + '\n' for cl in self.CausalLinkGraph.edges) ] return 'PLAN: ' + str(self.ID) + c + '\n*Steps: \n' + ''.join(['{}'.format(step) for step in steps]) + \ '*Orderings:\n' + \ ''.join(['{}'.format(o) for o in order]) + '*CausalLinks:\n' + ''.join(['{}'.format(link) for link in links]) + '}'
class GPlan: def __init__(self, dummy_init_constructor, dummy_goal_constructor): self.ID = uuid4() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() # self.HierarchyGraph = HierarchyGraph() self.flaws = FlawLib() self.solved = False self.dummy = dummyTuple(dummy_init_constructor.instantiate(), dummy_goal_constructor.instantiate()) self.init = self.dummy.init.preconds self.goal = self.dummy.final.preconds self.steps = [self.dummy.init, self.dummy.final] # check if any existing steps are choices (instances of cndts of open conditions) self.dummy.final.update_choices(self) self.cndt_map = None self.threat_map = None # self.gstep_lib = ground_step_list # self.h_step_dict = dict() self.heuristic = float('inf') self.name = '' self.cost = 0 self.depth = 0 def __len__(self): return len(self.steps) def __getitem__(self, pos): return self.steps[pos] def __setitem__(self, item, pos): self.steps[pos] = item def index(self, step): for i, s in enumerate(self.steps): if s.ID == step.ID: return i print('{} {} {}'.format(self.name, step, step.ID)) for i, s in enumerate(self.steps): print('{} {} {}'.format(i, s, s.ID)) raise ValueError('{} with ID={} not found in plan {}'.format(step, step.ID, self.name)) def instantiate(self, add_to_name): new_self = copy.deepcopy(self) new_self.ID = uuid4() new_self.name += add_to_name # refresh attributes return new_self # @property # def cost(self): # return len(self.steps) - 2 def isInternallyConsistent(self): return self.OrderingGraph.isInternallyConsistent() and self.CausalLinkGraph.isInternallyConsistent() # Insert Methods # def insert(self, step, dni=None): # baseline condition: self.cost += 1 # self.cost += (2 * 2 + 1) - (step.height * step.height) if step.height > 0: return self.insert_decomp(step, dni) else: return self.insert_primitive(step) def insert_primitive(self, new_step): self.steps.append(new_step) # global orderings self.OrderingGraph.addEdge(self.dummy.init, new_step) self.OrderingGraph.addEdge(new_step, self.dummy.final) # add open conditions for new step for pre in new_step.open_preconds: self.flaws.insert(self, OPF(new_step, pre)) # check for causal link threats for edge in self.CausalLinkGraph.edges: # source, sink, condition = edge if edge.source.ID == new_step.ID: continue if edge.sink.ID == new_step.ID: continue if self.OrderingGraph.isPath(new_step, edge.source): continue if self.OrderingGraph.isPath(edge.sink, new_step): continue if new_step.stepnum in edge.sink.threat_map[edge.label.ID]: self.flaws.insert(self, TCLF(new_step, edge)) return None def insert_decomp(self, new_step, dni=None): # magic happens here swap_dict = dict() # sub dummy init d_i = new_step.dummy.init.instantiate() d_i.depth = new_step.depth swap_dict[new_step.dummy.init.ID] = d_i self.steps.append(d_i) # add flaws for each new_step precondition, but make s_need d_i and update cndt_map/ threat_map d_i.swap_setup(new_step.cndts, new_step.cndt_map, new_step.threats, new_step.threat_map, new_step.cntg_mental) for pre in new_step.open_preconds: self.flaws.insert(self, OPF(d_i, pre, new_step.height)) preconds = list(new_step.open_preconds) d_i.preconds = preconds d_i.open_preconds = preconds self.OrderingGraph.addEdge(self.dummy.init, d_i) self.OrderingGraph.addEdge(d_i, self.dummy.final) # sub dummy final d_f = new_step.dummy.final.instantiate(default_None_is_to_refresh_open_preconds=False) d_f.depth = new_step.depth swap_dict[new_step.dummy.final.ID] = d_f # d_f will be primitive, to allow any heighted applicable steps self.insert(d_f) # added this 2017-08-09 self.OrderingGraph.addEdge(d_i, d_f) self.OrderingGraph.addEdge(d_f, self.dummy.final) self.OrderingGraph.addEdge(self.dummy.init, d_f) # decomposition links # self.HierarchyGraph.addOrdering(new_step, d_i) # self.HierarchyGraph.addOrdering(new_step, d_f) # for sb_step in new_step.sub_steps: # self.HierarchyGraph.addOrdering(new_step, sb_step) # log who your family is new_step.dummy = dummyTuple(d_i, d_f) d_i.sibling = d_f d_f.sibling = d_i # sub steps for substep in new_step.sub_steps: new_substep = substep.instantiate(default_None_is_to_refresh_open_preconds=False) swap_dict[substep.ID] = new_substep # INCREMENT DEPTH new_substep.depth = new_step.depth + 1 if new_substep.depth > self.depth: self.depth = new_substep.depth if dni is not None: if substep in dni: continue poss_swaps = self.insert(new_substep) if poss_swaps is not None: swap_dict.update(poss_swaps) # if your substeps have children, make those children fit between your init and if new_substep.height > 0: self.OrderingGraph.addEdge(new_substep.dummy.final, d_f) self.OrderingGraph.addEdge(d_i, new_substep.dummy.init) else: self.OrderingGraph.addEdge(new_substep, d_f) self.OrderingGraph.addEdge(d_i, new_substep) # sub orderings for edge in new_step.sub_orderings.edges: if dni is not None: if edge.source in dni: continue elif edge.sink in dni: continue # try: source, sink = swap_dict[edge.source.ID], swap_dict[edge.sink.ID] # except: # pass if source.height > 0: source = source.dummy.final if sink.height > 0: sink = sink.dummy.init self.OrderingGraph.addLabeledEdge(source, sink, edge.label) # sub links for edge in new_step.sub_links.edges: # instantiating a GLiteral does not give it new ID (just returns deep copy) source, sink, label = swap_dict[edge.source.ID], swap_dict[edge.sink.ID], edge.label.instantiate() if source.height > 0: source = source.dummy.final if sink.height > 0: sink = sink.dummy.init clink = self.CausalLinkGraph.addEdge(source, sink, label) # check if this link is threatened for substep in new_step.sub_steps: if dni is not None and substep in dni: continue new_substep = swap_dict[substep.ID] if new_substep.ID in {clink.source.ID, clink.sink.ID}: continue if new_substep.stepnum not in clink.sink.threat_map[clink.label.ID]: continue if new_substep.height > 0: # decomp step compared to its dummy init and dummy final steps if self.OrderingGraph.isPath(new_substep.dummy.final, clink.source): continue if self.OrderingGraph.isPath(clink.sink, new_substep.dummy.init): continue self.flaws.insert(self, TCLF(new_substep.dummy.final, clink)) else: # primitive step gets the primitive treatment if self.OrderingGraph.isPath(new_substep, clink.source): continue if self.OrderingGraph.isPath(clink.sink, new_substep): continue self.flaws.insert(self, TCLF(new_substep, clink)) return swap_dict # print('check') # Resolve Methods # def resolve(self, new_step, s_need, p): if new_step.height > 0: self.resolve_with_decomp(new_step, s_need, p) else: self.resolve_with_primitive(new_step, s_need, p) def resolve_with_primitive(self, new_step, mutable_s_need, mutable_p): # operate on cloned plan mutable_s_need.fulfill(mutable_p) # add orderings self.OrderingGraph.addEdge(new_step, mutable_s_need) # add causal link c_link = self.CausalLinkGraph.addEdge(new_step, mutable_s_need, mutable_p) if mutable_p.ID in mutable_s_need.cntg_mental.keys() and new_step.stepnum in mutable_s_need.cntg_mental[mutable_p.ID]: self.OrderingGraph.addCntg(new_step, mutable_s_need) mutable_s_need.update_choices(self) # check if this link is threatened ignore_these = {mutable_s_need.ID, new_step.ID} # ignore_these = {mutable_s_need.stepnum, new_step.stepnum} for step in self.steps: if step.ID in ignore_these: continue if step.stepnum not in mutable_s_need.threat_map[mutable_p.ID]: continue if self.OrderingGraph.isPath(mutable_s_need, step): continue # only for reuse case, otherwise this check is superfluous if self.OrderingGraph.isPath(step, new_step): continue self.flaws.insert(self, TCLF(step, c_link)) # # check if adding this step threatens other causal links # for cl in self.CausalLinkGraph.edges: # if cl == c_link: # continue # if new_step.stepnum not in cl.sink.threat_map[cl.label.ID]: # continue # if self.OrderingGraph.isPath(new_step, cl.source): # continue # if self.OrderingGraph.isPath(cl.sink, new_step): # continue # self.flaws.insert(self, TCLF(new_step, cl)) def resolve_with_decomp(self, new_step, mutable_s_need, mutable_p): d_i, d_f = new_step.dummy # operate on cloned plan # mutable_s_need = self[s_index] # mutable_p = mutable_s_need.preconds[p_index] mutable_s_need.fulfill(mutable_p) # add ordering self.OrderingGraph.addEdge(d_f, mutable_s_need) # add causal link c_link = self.CausalLinkGraph.addEdge(d_f, mutable_s_need, mutable_p) if mutable_p.ID in mutable_s_need.cntg_mental.keys() and d_f.stepnum in mutable_s_need.cntg_mental[mutable_p.ID]: self.OrderingGraph.addCntg(new_step, mutable_s_need) mutable_s_need.update_choices(self) # check if df -> s_need is threatened ignore_these = {mutable_s_need.ID, d_f.ID, d_i.ID} for step in self.steps: # reminder: existing steps are primitive if step.ID in ignore_these: continue ### NOT SUFFICIENT: needs to not be a threat to any sub-step added... ### if step.stepnum not in mutable_s_need.threat_map[mutable_p.ID]: continue # check only for d_f, in case this step occurs between d_i and d_f if self.OrderingGraph.isPath(step, d_f): continue if self.OrderingGraph.isPath(mutable_s_need, step): continue self.flaws.insert(self, TCLF(step, c_link)) # # check if adding this step threatens other causal links # for cl in self.CausalLinkGraph.edges: # # all causal links are between primitive steps # if cl == c_link: # continue # if new_step.stepnum not in cl.sink.threat_map[cl.label.ID]: # continue # if self.OrderingGraph.isPath(d_f, cl.source): # continue # if self.OrderingGraph.isPath(cl.sink, d_f): # LOOK HERE TODO: DECIDE # continue # self.flaws.insert(self, TCLF(d_f, cl)) def __lt__(self, other): if self.cost / (1 + math.log2(self.depth+1)) + self.heuristic != other.cost / (1 + math.log2(other.depth+1)) + other.heuristic: return self.cost / (1 + math.log2(self.depth+1)) + self.heuristic < other.cost / (1 + math.log2(other.depth+1)) + other.heuristic # if self.cost - math.log2(self.depth+1) + self.heuristic != other.cost - math.log2(other.depth+1) + other.heuristic: # return self.cost - math.log2(self.depth+1) + self.heuristic < other.cost - math.log2(other.depth+1) + other.heuristic # if self.cost - self.depth + self.heuristic != other.cost - other.depth + other.heuristic: # return self.cost - self.depth + self.heuristic < other.cost - other.depth + other.heuristic if self.cost + self.heuristic != other.cost + other.heuristic: return (self.cost + self.heuristic) < (other.cost + other.heuristic) elif self.cost != other.cost: return self.cost < other.cost elif self.heuristic != other.heuristic: return self.heuristic < other.heuristic elif len(self.flaws) != len(other.flaws): return len(self.flaws) < len(other.flaws) elif len(self.CausalLinkGraph.edges) != len(other.CausalLinkGraph.edges): return len(self.CausalLinkGraph.edges) > len(other.CausalLinkGraph.edges) elif len(self.OrderingGraph.edges) != len(other.OrderingGraph.edges): return len(self.OrderingGraph.edges) > len(other.OrderingGraph.edges) elif sum([step.stepnum for step in self]) != sum([step.stepnum for step in other]): return sum([step.stepnum for step in self]) < sum([step.stepnum for step in other]) else: return self.OrderingGraph < other.OrderingGraph def __str__(self): return self.name # return 'GPlan{} c={} h={}\t'.format(self.ID[-4:], self.cost, self.heuristic) + \ # str(self.steps) + '\n' + str(self.OrderingGraph) + '\n' + str(self.CausalLinkGraph) def __repr__(self): return self.__str__()
class GPlan: def __init__(self, dummy_init_constructor, dummy_goal_constructor): self.ID = uuid4() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() self.flaws = FlawLib() self.solved = False self.dummy = dummyTuple(dummy_init_constructor.instantiate(), dummy_goal_constructor.instantiate()) self.init = self.dummy.init.preconds self.goal = self.dummy.final.preconds self.steps = [self.dummy.init, self.dummy.final] # check if any existing steps are choices (instances of cndts of open conditions) self.dummy.final.update_choices(self) self.cndt_map = None self.threat_map = None # self.gstep_lib = ground_step_list # self.h_step_dict = dict() self.heuristic = float('inf') def __len__(self): return len(self.steps) def __getitem__(self, pos): return self.steps[pos] def __setitem__(self, item, pos): self.steps[pos] = item def index(self, step): return self.steps.index(step) def instantiate(self): new_self = copy.deepcopy(self) new_self.ID = uuid4() # refresh attributes return new_self @property def cost(self): return len(self.steps) - 2 def isInternallyConsistent(self): return self.OrderingGraph.isInternallyConsistent( ) and self.CausalLinkGraph.isInternallyConsistent() # Insert Methods # def insert(self, step, dummy_dict=None): if step.height > 0: dummy_dict, sub_steps = self.insert_decomp(step, dummy_dict) return dummy_dict, sub_steps else: self.insert_primitive(step) return None, None # self.steps.append(step) # def insert_decomp(self, new_step, dummy_dict=None): # # magic happens here # swap_dict = dict() # # # sub dummy init # d_i = new_step.sub_dummy.sub_init.instantiate() # swap_dict[new_step.sub_dummy.sub_init.ID] = d_i # self.steps.append(d_i) # # # sub dummy final # d_f = new_step.sub_dummy.sub_final.instantiate(default_None_is_to_refresh_open_preconds=False) # swap_dict[new_step.sub_dummy.sub_final.ID] = d_f # self.steps.append(d_f) # # if dummy_dict is None: # dummy_dict = dict() # # # sub steps (recursively insert... then reuse later for existing links # for substep in new_step.sub_steps: # new_substep = substep.instantiate(default_None_is_to_refresh_open_preconds=False) # swap_dict[substep.ID] = new_substep # # # substep points to dummies of same subplan # dummy_dict[new_substep] = dummyTuple(d_i, d_f) # sub_dummy_dict = self.insert(new_substep) # if sub_dummy_dict is not None: # dummy_dict.update(sub_dummy_dict) # # # sub orderings # for edge in new_step.sub_orderings.edges: # self.OrderingGraph.addEdge(swap_dict[edge.source.ID], swap_dict[edge.sink.ID]) # # # sub links # for edge in new_step.sub_links.edges: # clink = self.CausalLinkGraph.addEdge(swap_dict[edge.source.ID], swap_dict[edge.sink.ID], # edge.label.instantiate()) # # check if this link is threatened # for substep in new_step.sub_steps: # new_substep = swap_dict[substep.ID] # if new_substep.ID in {clink.source.ID, clink.sink.ID}: # continue # if new_substep.stepnum not in clink.sink.threat_map[clink.label.ID]: # continue # if self.OrderingGraph.isPath(new_substep, clink.source): # continue # if self.OrderingGraph.isPath(clink.sink, new_substep): # continue # self.flaws.insert(self, TCLF(new_substep, clink)) # # # global orderings # self.OrderingGraph.addEdge(self.dummy.init, d_i) # self.OrderingGraph.addEdge(self.dummy.init, d_f) # self.OrderingGraph.addEdge(d_i, self.dummy.final) # self.OrderingGraph.addEdge(d_f, self.dummy.final) # # return dummy_dict def insert_primitive(self, new_step): self.steps.append(new_step) self.OrderingGraph.addEdge(self.dummy.init, new_step) self.OrderingGraph.addEdge(new_step, self.dummy.final) return None, None, None, new_step def resolve(self, new_step, s_index, p_index): if new_step.height > 0: self.resolve_with_decomp(new_step, s_index, p_index) else: self.resolve_with_primitive(new_step, s_index, p_index) def resolve_with_primitive(self, new_step, s_index, p_index): # operate on cloned plan mutable_s_need = self[s_index] mutable_p = mutable_s_need.preconds[p_index] mutable_s_need.fulfill(mutable_p) mutable_s_need.update_choices(self) # add orderings self.OrderingGraph.addEdge(new_step, mutable_s_need) # add causal link c_link = self.CausalLinkGraph.addEdge(new_step, mutable_s_need, mutable_p) # add open conditions for new step for pre in new_step.open_preconds: self.flaws.insert(self, OPF(new_step, pre)) # check if this link is threatened ignore_these = {mutable_s_need.ID, new_step.ID} for step in self.steps: if step.ID in ignore_these: continue if self.OrderingGraph.isPath(mutable_s_need, step): continue if step.stepnum in mutable_s_need.threats: self.flaws.insert(self, TCLF(step, c_link)) # check if adding this step threatens other causal links for cl in self.CausalLinkGraph.edges: if cl == c_link: continue if new_step.stepnum not in cl.sink.threat_map[cl.label]: continue if self.OrderingGraph.isPath(new_step, cl.source): continue if self.OrderingGraph.isPath(cl.sink, new_step): continue self.flaws.insert(self, TCLF(new_step, cl)) def resolve_with_decomp(self, new_step, s_index, p_index): d_i, d_f = new_step.dummy # operate on cloned plan mutable_s_need = self[s_index] mutable_p = mutable_s_need.preconds[p_index] mutable_s_need.fulfill(mutable_p) mutable_s_need.update_choices(self) # add orderings to rest of plan self.OrderingGraph.addEdge(d_f, mutable_s_need) # add causal link c_link = self.CausalLinkGraph.addEdge(d_f, mutable_s_need, mutable_p) # check if df -> s_need is threatened ignore_these = {mutable_s_need.ID, d_f.ID, d_i.ID} for step in self.steps: # existing steps must be primitive if step.ID in ignore_these: continue if self.OrderingGraph.isPath(step, d_f): continue if self.OrderingGraph.isPath(mutable_s_need, step): continue if step.stepnum in mutable_s_need.threats: self.flaws.insert(self, TCLF(step, c_link)) # check if adding this step threatens other causal links for cl in self.CausalLinkGraph.edges: # all causal links are between primitive steps if cl == c_link: continue if new_step.stepnum not in cl.sink.threat_map[cl.label]: continue if self.OrderingGraph.isPath(d_f, cl.source): continue if self.OrderingGraph.isPath(cl.sink, d_i): continue self.flaws.insert(self, DTCLF(d_i, d_f, cl)) def insert_primitive_0ld(self, new_step, s_index, p_index): # append primitive step self.steps.append(new_step) # operate on cloned plan mutable_s_need = self[s_index] mutable_p = mutable_s_need.preconds[p_index] mutable_s_need.fulfill(mutable_p) mutable_s_need.update_choices(self) # add orderings self.OrderingGraph.addEdge(new_step, mutable_s_need) self.OrderingGraph.addEdge(self.dummy.init, new_step) self.OrderingGraph.addEdge(new_step, self.dummy.final) # add causal link c_link = self.CausalLinkGraph.addEdge(new_step, mutable_s_need, mutable_p) # add open conditions for new step for pre in new_step.open_preconds: self.flaws.insert(self, OPF(new_step, pre)) # check if this link is threatened ignore_these = {mutable_s_need.ID, new_step.ID} for step in self.steps: if step.ID in ignore_these: continue if self.OrderingGraph.isPath(mutable_s_need, step): continue if step.stepnum in mutable_s_need.threats: self.flaws.insert(self, TCLF(step, c_link)) # check if adding this step threatens other causal links for cl in self.CausalLinkGraph.edges: if cl == c_link: continue if new_step.stepnum not in cl.sink.threat_map[cl.label]: continue if self.OrderingGraph.isPath(new_step, cl.source): continue if self.OrderingGraph.isPath(cl.sink, new_step): continue self.flaws.insert(self, TCLF(new_step, cl)) def insert_decomp(self, new_step): # magic happens here swap_dict = dict() # sub dummy init d_i = new_step.sub_dummy.sub_init.instantiate() swap_dict[new_step.sub_dummy.sub_init.ID] = d_i self.steps.append(d_i) # add flaws for each new_step precondition, but make s_need d_i and update cndt_map/ threat_map for pre in new_step.open_preconds: self.flaws.insert(self, OPF(d_i, pre)) d_i.swap_setup(new_step.cndts, new_step.cndt_map, new_step.threats, new_step.threat_map) # sub dummy final d_f = new_step.sub_dummy.sub_final.instantiate( default_None_is_to_refresh_open_preconds=False) swap_dict[new_step.sub_dummy.sub_final.ID] = d_f self.steps.append(d_f) # add flaws for each d_f pre for pre in d_f.open_preconds: self.flaws.insert(self, OPF(d_f, pre)) self.OrderingGraph.addEdge(self.dummy.init, d_i) self.OrderingGraph.addEdge(self.dummy.init, d_f) self.OrderingGraph.addEdge(d_i, self.dummy.final) self.OrderingGraph.addEdge(d_f, self.dummy.final) # sub steps for substep in new_step.sub_steps: new_substep = substep.instantiate( default_None_is_to_refresh_open_preconds=False) swap_dict[substep.ID] = new_substep if new_substep.height > 0: # check what links this new_substep is a source of. self.insert(new_substep) for open_condition in new_substep.open_preconds: self.flaws.insert(self, OPF(new_substep, open_condition)) # sub orderings for edge in new_step.sub_orderings.edges: source, sink = swap_dict[edge.source.ID], swap_dict[edge.sink.ID] if source.height > 0: source = source.dummy.final if sink.height > 0: sink = sink.dummy.init self.OrderingGraph.addEdge(source, sink) # sub links for edge in new_step.sub_links.edges: source, sink, label = swap_dict[edge.source.ID], swap_dict[ edge.sink.ID], edge.label.instantiate() if source.height > 0: source = source.dummy.final if sink.height > 0: sink = sink.dummy.init clink = self.CausalLinkGraph.addEdge(source, sink, label) # check if this link is threatened for substep in new_step.sub_steps: new_substep = swap_dict[substep.ID] if new_substep.ID in {clink.source.ID, clink.sink.ID}: continue if new_substep.stepnum not in clink.sink.threat_map[ clink.label.ID]: continue if new_substep.height > 0: # decomp step compared to its dummy init and dummy final steps if self.OrderingGraph.isPath(new_substep.dummy.final, clink.source): continue if self.OrderingGraph.isPath(clink.sink, new_substep.dummy.init): continue self.flaws.insert( self, DTCLF(new_substep.dummy.init, new_substep.dummy.final, clink)) else: # primitive step gets the primitive treatment if self.OrderingGraph.isPath(new_substep, clink.source): continue if self.OrderingGraph.isPath(clink.sink, new_substep): continue self.flaws.insert(self, TCLF(new_substep, clink)) # # operate on cloned plan # mutable_s_need = self[s_index] # mutable_p = mutable_s_need.preconds[p_index] # mutable_s_need.fulfill(mutable_p) # mutable_s_need.update_choices(self) # # # add orderings to rest of plan # self.OrderingGraph.addEdge(d_f, mutable_s_need) # # # add causal link # c_link = self.CausalLinkGraph.addEdge(d_f, mutable_s_need, mutable_p) # # # check if df -> s_need is threatened # ignore_these = {mutable_s_need.ID, d_f.ID, d_i.ID} # for step in self.steps: # # existing steps must be primitive # if step.ID in ignore_these: # continue # if self.OrderingGraph.isPath(step, d_f): # continue # if self.OrderingGraph.isPath(mutable_s_need, step): # continue # if step.stepnum in mutable_s_need.threats: # self.flaws.insert(self, TCLF(step, c_link)) # # # check if adding this step threatens other causal links # for cl in self.CausalLinkGraph.edges: # # all causal links are between primitive steps # if cl == c_link: # continue # if new_step.stepnum not in cl.sink.threat_map[cl.label]: # continue # if self.OrderingGraph.isPath(d_f, cl.source): # continue # if self.OrderingGraph.isPath(cl.sink, d_i): # continue # self.flaws.insert(self, DTCLF(d_i, d_f, cl)) def __lt__(self, other): if self.cost + self.heuristic != other.cost + other.heuristic: return (self.cost + self.heuristic) < (other.cost + other.heuristic) elif self.heuristic != other.heuristic: return self.heuristic < other.heuristic elif self.cost != other.cost: return self.cost < other.cost elif len(self.flaws) != len(other.flaws): return len(self.flaws) < len(other.flaws) elif sum([step.stepnum for step in self]) != sum([step.stepnum for step in other]): return sum([step.stepnum for step in self]) < sum( [step.stepnum for step in other]) else: return self.OrderingGraph < other.OrderingGraph def __str__(self): return 'GPlan{} c={} h={}\t'.format(self.ID[-4:], self.cost, self.heuristic) + \ str(self.steps) + '\n' + str(self.OrderingGraph) + '\n' + str(self.CausalLinkGraph) def __repr__(self): return self.__str__()
class PlanElementGraph(ElementGraph): def __init__(self, ID=None, type_graph=None, name=None, Elements=None, plan_elm=None, Edges=None, Restrictions=None): if ID is None: ID = uuid4() if type_graph is None: type_graph = 'PlanElementGraph' if Elements is None: Elements = set() if Edges is None: Edges = set() if Restrictions is None: Restrictions = set() self.OrderingGraph = OrderingGraph() self.CausalLinkGraph = CausalLinkGraph() self.DecompGraph = OrderingGraph() self.nonequals = set() self.flaws = FlawLib() self.solved = False self.initial_dummy_step = None self.final_dummy_step = None if plan_elm is None: plan_elm = Element(ID=ID, typ=type_graph, name=name) super(PlanElementGraph, self).__init__(ID, type_graph, name, Elements, plan_elm, Edges, Restrictions) def __hash__(self): return hash(self.name) ^ hash(self.typ) ^ hash(self.ID) def __getattr__(self, name): if name == 'Args': self.updateArgs() return self.Args else: raise AttributeError('no attribute {}'.format(name)) @classmethod def Actions_2_Plan(cls, Actions, h): # Used by Plannify if not checkHeight(Actions, h): return None elements = set().union(*[A.elements for A in Actions]) edges = set().union(*[A.edges for A in Actions]) new_ords = [] new_links = [] new_decomps = [] for A in Actions: if hasattr(A, "ground_subplan"): for elm in A.ground_subplan.elements: if elm not in elements and type(elm) == Operator: elm.arg_name = str(uuid4())[19:23] + elm.arg_name process_subplan(A.ground_subplan, elements, edges, new_ords, new_links, new_decomps) arg_dict = {op: op.arg_name for op in elements if type(op) == Operator} inv_map = {v: k for k, v in arg_dict.items()} new_edges = [] for edge in edges: must_replace = False source = edge.source sink = edge.sink if type(source) == Operator: source = inv_map[source.arg_name] must_replace = True if type(sink) == Operator: sink = inv_map[sink.arg_name] must_replace = True if must_replace: new_edges.append(Edge(source, sink, edge.label)) else: new_edges.append(edge) new_elements = [] for elm in elements: if type(elm) == Operator: new_elements.append(inv_map[elm.arg_name]) else: new_elements.append(elm) update_ords = [] update_links = [] update_decomps = [] for ord in new_ords: update_ords.append( Edge(inv_map[ord.source.arg_name], inv_map[ord.sink.arg_name], ord.label)) for link in new_links: update_links.append( Edge(inv_map[link.source.arg_name], inv_map[link.sink.arg_name], link.label)) for decomp in new_decomps: update_decomps.append( Edge(inv_map[decomp.source.arg_name], inv_map[decomp.sink.arg_name], decomp.label)) Plan = cls(name='Action_2_Plan', Elements=set(new_elements), Edges=set(new_edges)) Plan.OrderingGraph.edges = set(update_ords) Plan.CausalLinkGraph.edges = set(update_links) Plan.DecompGraph.edges = set(update_decomps) return Plan def deepcopy(self): new_self = copy.deepcopy(self) new_self.ID = uuid4() return new_self def AddSubgraph(self, subgraph): self.elements.update(subgraph.elements) self.edges.update(subgraph.edges) def isInternallyConsistent(self): return self.OrderingGraph.isInternallyConsistent() and self.CausalLinkGraph.isInternallyConsistent() and \ super(PlanElementGraph, self).isInternallyConsistent() @property def Steps(self): return [ element for element in self.elements if type(element) is Operator ] @property def Step_Graphs(self): return [Action.subgraph(self, step) for step in self.Steps] # @property # def Root_Graphs(self): # root_steps = [] # for s in self.Steps: # nobodys_descendant = True # for t in self.Steps: # if s == t: # continue # if s in self.rGetDescendants(t): # nobodys_descendant = False # break # if nobodys_descendant: # root_steps.append(s) # return [Action.subgraph(self, step) for step in root_steps] def __repr__(self): c = '\ncost {} + heuristic {}'.format(self.cost, self.heuristic) steps = [''.join('\t' + str(step) + '\n' for step in self.Step_Graphs)] order = [ ''.join('\t' + str(ordering.source) + ' < ' + str(ordering.sink) + '\n' for ordering in self.OrderingGraph.edges) ] links = [ ''.join('\t' + str(cl) + '\n' for cl in self.CausalLinkGraph.edges) ] return 'PLAN: ' + str(self.ID) + c + '\n*Steps: \n' + ''.join(['{}'.format(step) for step in steps]) + \ '*Orderings:\n' + \ ''.join(['{}'.format(o) for o in order]) + '*CausalLinks:\n' + ''.join(['{}'.format(link) for link in links]) + '}'
class GStep: """ Read-Only Ground Step """ def __init__(self, operator, args, preconditions, stepnum, height): # READ-ONLY ATTRIBUTES # # schema refers to the name of the operator self.schema = operator # Args are Argument or Actor "Element" types self.Args = args # ID used as "instance ID" self.ID = uuid4() # preconds is a list of GCond self.preconds = preconditions # stepnum is the ground step constructor type self.stepnum = stepnum self.stepnumber = stepnum # height is 0 when primitive self.height = height if height > 0: self.sub_steps = [] self.sub_orderings = OrderingGraph() self.sub_links = CausalLinkGraph() self.dummy = dummyTuple(None, None) # depth starts at 0 and takes on value during planning self.depth = 0 self.cndts = None self.cndt_map = None self.threat_map = None self.threats = None self.instantiable = True # INSTANCE ATTRIBUTES # # risks are number of threat instances self.risks = list() self.choices = list() # choices are instances of cndt antecedents self.choice_map = dict() # self.num_choices = 0 # open preconditions which need causal link self.open_preconds = list(self.preconds) # def to_json(self): # return '{}:{}, {}' # return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) # public methods # # def default(self): # def default(self, obj): # if hasattr(obj, 'to_json'): # return obj.to_json() # return json.JSONEncoder.default(self, obj) def setup(self, step_to_cndt, precond_to_cndt, step_to_threat, precond_to_threat): """ :param step_to_cndt: dict of form GStep -> GStep^k such as D[stepnum] -> [cndt antecedent step nums] :param precond_to_cndt: dict of form GLiteral -> GStep^k such as D[pre.ID] -> [cndt antecedent step nums] :param step_to_threat: dict of form GLiteral -> Gstep^k such as D[stepnum] -> [cndt threat step nums] """ self.cndts = list(step_to_cndt[self.stepnum]) self.cndt_map = { pre.ID: list(precond_to_cndt[pre.ID]) for pre in self.preconds } self.threats = list(step_to_threat[self.stepnum]) self.threat_map = { pre.ID: list(precond_to_threat[pre.ID]) for pre in self.preconds } def swap_setup(self, cndts, cndtmap, threats, threatmap): self.cndts = cndts self.cndt_map = cndtmap self.threats = threats self.threat_map = threatmap def swap_substeps(self, gsteps, decomp_step, num_GL_steps): change_dict = { step: gsteps[step.stepnumber].instantiate() for step in decomp_step.ground_subplan.Steps } self.sub_steps = list(change_dict.values()) for edge in decomp_step.ground_subplan.OrderingGraph.edges: self.sub_orderings.addEdge(change_dict[edge.source], change_dict[edge.sink]) for edge in decomp_step.ground_subplan.CausalLinkGraph.edges: new_sink = change_dict[edge.sink] # Condition.subgraph(subplan, edge.label) g_label = GLiteral(edge.label.name, edge.label.Args, edge.label.truth, -1, None) for p in new_sink.preconds: if p != g_label: continue self.sub_links.addEdge(change_dict[edge.source], new_sink, p) self.sub_orderings.addEdge(change_dict[edge.source], new_sink) new_sink.fulfill(p) break # set these babes to not be instantiable "fo' life" gsteps[decomp_step.sub_dummy_init.stepnumber].instantiable = False gsteps[decomp_step.sub_dummy_goal.stepnumber].instantiable = False init_step = gsteps[decomp_step.sub_dummy_init.stepnumber].instantiate() final_step = gsteps[ decomp_step.sub_dummy_goal.stepnumber].instantiate() for step in self.sub_steps: self.sub_orderings.addEdge(init_step, step) self.sub_orderings.addEdge(step, final_step) self.sub_orderings.addEdge(init_step, final_step) # reconfigure init step to be top cndt for all steps and goal for step in self.sub_steps: for other_step in self.sub_steps: if other_step == step: continue prioritize_cndt(other_step, step) prioritize_cndt(init_step, step) prioritize_cndt(step, final_step) prioritize_cndt(init_step, final_step) # add init_step as top cndt for all self.dummy = dummyTuple(init_step, final_step) def instantiate(self, default_refresh=None, default_None_is_to_refresh_open_preconds=None): new_self = copy.deepcopy(self) new_self.ID = uuid4() self.choice_map = dict() if default_refresh is None: self.risks = list() self.choices = list() if default_None_is_to_refresh_open_preconds is None: self.open_preconds = list(self.preconds) return new_self def fulfill(self, pre): if self.cndt_map is None: raise AttributeError('Cndt Map not found; run setup(xyz) first') if pre.ID not in self.cndt_map: raise ValueError('{} not found in cndt_map, id={}'.format( pre, pre.ID)) if pre not in self.preconds: raise ValueError( '{} found in cndt_map w/ id={}, but {} not found in preconds'. format(pre, pre.ID, pre)) # remove precondition from open precond if pre in self.open_preconds: self.open_preconds.remove(pre) else: print('pre: {} not found in {} to remove, allowed in some cases'. format(pre, self)) def update_choices(self, plan): choices = set() for pre in self.open_preconds: choice_nums = self.cndt_map[pre.ID] for step in plan.steps: if self.ID == step.ID: continue if plan.OrderingGraph.isPath(self, step): continue if step.stepnum in choice_nums: choices.add(step) self.choices = list(choices) def is_cndt(self, other): return other.stepnum in self.cndts def is_threat(self, other): return other.stepnum in self.threats # private hooks # def __hash__(self): return hash(self.ID) def __eq__(self, other): return self.ID == other.ID def __str__(self): args = str([ arg.name if not isinstance(arg, ElementGraph) else arg for arg in self.Args ]) return str(self.schema) + args + '_{}'.format(str(self.ID)[-4:]) def __repr__(self): return self.__str__()
class GStep: """ Read-Only Ground Step """ def __init__(self, operator, args, preconditions, effects, stepnum, height): # READ-ONLY ATTRIBUTES # # schema refers to the name of the operator self.schema = operator # Args are Argument or Actor "Element" types self.Args = args # ID used as "instance ID" self.ID = uuid4() # preconds is a list of GCond self.preconds = preconditions self.effects = effects # stepnum is the ground step constructor type self.stepnum = stepnum self.stepnumber = stepnum # height is 0 when primitive self.height = height if height > 0: self.sub_steps = [] self.sub_orderings = OrderingGraph() self.sub_links = CausalLinkGraph() self.dummy = dummyTuple(None, None) # depth starts at 0 and takes on value during planning self.depth = 0 self.cndts = None self.cndt_map = None self.threat_map = None self.threats = None self.cntg_mental = None self.instantiable = True # INSTANCE ATTRIBUTES # # risks are number of threat instances self.risks = list() self.choices = list() # choices are instances of cndt antecedents self.choice_map = dict() # self.num_choices = 0 # open preconditions which need causal link self.open_preconds = list(self.preconds) # def to_json(self): # return '{}:{}, {}' # return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) # public methods # # def default(self): # def default(self, obj): # if hasattr(obj, 'to_json'): # return obj.to_json() # return json.JSONEncoder.default(self, obj) def setup(self, step_to_cndt, precond_to_cndt, step_to_threat, precond_to_threat, cntg_mental): """ :param step_to_cndt: dict of form GStep -> GStep^k such as D[stepnum] -> [cndt antecedent step nums] :param precond_to_cndt: dict of form GLiteral -> GStep^k such as D[pre.ID] -> [cndt antecedent step nums] :param step_to_threat: dict of form GLiteral -> Gstep^k such as D[stepnum] -> [cndt threat step nums] """ self.cndts = list(step_to_cndt[self.stepnum]) self.cndt_map = {pre.ID: list(precond_to_cndt[pre.ID]) for pre in self.preconds} self.threats = list(step_to_threat[self.stepnum]) self.threat_map = {pre.ID: list(precond_to_threat[pre.ID]) for pre in self.preconds} self.cntg_mental = {pre.ID: list(cntg_mental[pre.ID]) for pre in self.preconds} def swap_setup(self, cndts, cndtmap, threats, threatmap, cntgmap): self.cndts = cndts self.cndt_map = cndtmap self.threats = threats self.threat_map = threatmap self.cntg_mental = cntgmap # for each sub-step in sub-plan, create gstep @clock def swap_substeps(self, gsteps, GL, decomp_step): # base case - sub-steps are all height = 0 primitive_substeps = [arg for arg in decomp_step.ground_subplan.elements if type(arg) == Operator and arg.height == 0] composite_substeps = [arg for arg in decomp_step.ground_subplan.elements if type(arg) == Operator and arg.height > 0] if len(composite_substeps) == 0: prim_dict = {step: gsteps[step.stepnumber].instantiate() for step in primitive_substeps} self.create_composite_gstep(gsteps, decomp_step, prim_dict) return prim_dict else: # links and orderings in intermediate stage # change_dict = {step.root: gsteps[step.stepnumber].instantiate() for step in decomp_step.ground_subplan.Root_Graphs} change_dict = {} # order steps by height, lowest to highest. step_list = [step for step in decomp_step.ground_subplan.Step_Graphs] step_list.sort(key=lambda x: x.height) do_not_add_as_substep = [] for step in step_list: Args = [decompile(arg, decomp_step.ground_subplan) for arg in step.Args] preconds = [GLiteral(p.name, [decompile(arg, p) for arg in p.Args], p.truth, p.replaced_ID, (p.name, p.truth) not in GL.non_static_preds) for p in step.Preconditions] effects = [GLiteral(e.name, [decompile(arg, e) for arg in e.Args], e.truth, e.replaced_ID, (e.name, e.truth) not in GL.non_static_preds) for e in step.Effects] schema = str(step) step_copy = GStep(schema, Args, preconds, effects, step.stepnumber, step.height) step_copy.ID = step.root.ID st_t = gsteps[step.stepnumber].instantiate() step_copy.swap_setup(st_t.cndts, st_t.cndt_map, st_t.threats, st_t.threat_map, st_t.cntg_mental) # give no children if step.height > 0: init_step = st_t.dummy[0] init_step.schema = "begin:" + str(step) final_step = st_t.dummy[1] final_step.schema = "finish:" + str(step) step_copy.dummy = dummyTuple(init_step, final_step) # step_copy.sub_steps = [] children = decomp_step.ground_subplan.DecompGraph.getNeighbors(step.root) step_copy.sub_steps = [change_dict[child] for child in children if child.typ != 'step-s'] do_not_add_as_substep.extend(step_copy.sub_steps) step_copy.sub_orderings = OrderingGraph() step_copy.sub_links = CausalLinkGraph() change_dict[step.root] = step_copy # self.sub_steps.append(step) self.create_composite_gstep(gsteps, decomp_step, change_dict, do_not_add_as_substep) return change_dict @clock def create_composite_gstep(self, gsteps, decomp_step, change_dict, do_not_add_as_substep=None): if do_not_add_as_substep is None: self.sub_steps = list(change_dict.values()) else: self.sub_steps = [item for item in change_dict.values() if item not in do_not_add_as_substep] for edge in decomp_step.ground_subplan.OrderingGraph.edges: source = change_dict[edge.source] # if source.height > 0: # source = change_dict[edge.source].dummy[1] sink = change_dict[edge.sink] # if sink.height > 0: # sink = change_dict[edge.sink].dummy[0] self.sub_orderings.addLabeledEdge(source, sink, edge.label) for edge in decomp_step.ground_subplan.CausalLinkGraph.edges: new_sink = change_dict[edge.sink] # Condition.subgraph(subplan, edge.label) g_label = GLiteral(edge.label.name, [decompile(arg, decomp_step.ground_subplan) for arg in edge.label.Args], edge.label.truth, -1, None) for p in new_sink.preconds: if p != g_label: continue self.sub_links.addEdge(change_dict[edge.source], new_sink, p) self.sub_orderings.addEdge(change_dict[edge.source], new_sink) new_sink.fulfill(p) break # set these babes to not be instantiable "fo' life" gsteps[decomp_step.sub_dummy_init.stepnumber].instantiable = False gsteps[decomp_step.sub_dummy_goal.stepnumber].instantiable = False init_step = gsteps[decomp_step.sub_dummy_init.stepnumber].instantiate() final_step = gsteps[decomp_step.sub_dummy_goal.stepnumber].instantiate() for step in self.sub_steps: self.sub_orderings.addEdge(init_step, step) self.sub_orderings.addEdge(step, final_step) self.sub_orderings.addEdge(init_step, final_step) # reconfigure init step to be top cndt for all steps and goal for step in self.sub_steps: for other_step in self.sub_steps: if other_step == step: continue prioritize_cndt(other_step, step) prioritize_cndt(init_step, step) prioritize_cndt(step, final_step) prioritize_cndt(init_step, final_step) # add init_step as top cndt for all self.dummy = dummyTuple(init_step, final_step) def compile_do_not_insert_list(self, eff_dict, step_swap_map): """ Compile a mapping from precondition IDs to sub_steps. 1. precondition matches THIS step as establisher 2. THIS step has height > 0 3. THIS step has a mapping from preconditino IDs to its sub_steps 4. mark these sub_steps as DO NOT INSERT during planning """ eff_ids = {eff.ID: eff for eff in self.effects} relevant_effects = defaultdict(list) for k, v in eff_dict.items(): for v_i in v: if v_i not in eff_ids.keys(): continue relevant_effects[eff_ids[v_i]].append(k) # relevant_effects = {eff_ids[v_i]: k for k, v in eff_dict.items() for v_i in v if v_i in eff_ids.keys()} # relevant_effects = {eff: eff_dict[eff.ID] for eff in self.effects} self.do_not_insert_map = {} for eff, pre_IDs in relevant_effects.items(): gstep_list = [step_swap_map[arg.root] for arg in eff.Args if type(arg) == Action] for pid in pre_IDs: self.do_not_insert_map[pid] = gstep_list def instantiate(self, default_refresh=None, default_None_is_to_refresh_open_preconds=None): new_self = copy.deepcopy(self) new_self.ID = uuid4() self.choice_map = dict() if default_refresh is None: self.risks = list() self.choices = list() if default_None_is_to_refresh_open_preconds is None: self.open_preconds = list(self.preconds) return new_self def fulfill(self, pre): if self.cndt_map is None: raise AttributeError('Cndt Map not found; run setup(xyz) first') if pre.ID not in self.cndt_map: raise ValueError('{} not found in cndt_map, id={}'.format(pre, pre.ID)) if pre not in self.preconds: raise ValueError('{} found in cndt_map w/ id={}, but {} not found in preconds'.format(pre, pre.ID, pre)) # remove precondition from open precond if pre in self.open_preconds: self.open_preconds.remove(pre) else: print('pre: {} not found in {} to remove, allowed in some cases'.format(pre, self)) def update_choices(self, plan): choices = set() for pre in self.open_preconds: choice_nums = self.cndt_map[pre.ID] for step in plan.steps: if self.ID == step.ID: continue if plan.OrderingGraph.isPath(self, step): continue if step.stepnum in choice_nums: choices.add(step) self.choices = list(choices) def is_cndt(self, other): return other.stepnum in self.cndts def is_threat(self, other): return other.stepnum in self.threats # private hooks # def __hash__(self): return hash(self.ID) def __eq__(self, other): return self.ID == other.ID def __str__(self): # if len(self.Args) > 0 and type(self.Args[0]) == str: # args = "" # else: # args = str([arg.name if not isinstance(arg, ElementGraph) else arg for arg in self.Args]) # return str(self.schema) + args + '_{}'.format(str(self.ID)[-4:]) return str(self.schema) + '_{}'.format(str(self.ID)[-4:]) def __repr__(self): return self.__str__()