Пример #1
0
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__()
Пример #2
0
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):
        # baseline condition:
        # self.cost += 1
        # self.cost += (2 * 2 + 1) - (step.height * step.height)
        if step.height > 0:
            self.insert_decomp(step)
        else:
            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))

    def insert_decomp(self, new_step):
        # 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)
        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

            self.insert(new_substep)

            # 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)

        # 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:
            # 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:
                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))

    # 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)

        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)

        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__()
Пример #3
0
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__()