Exemplo n.º 1
0
    def __init__(self, optimizer, external_plan):
        optimizer.streams.append(self)
        self.optimizer = optimizer
        self.stream_plan, self.function_plan = partition_external_plan(
            external_plan)
        inputs, domain, outputs, certified, functions, self.macro_from_micro, \
            self.input_objects, self.output_objects, self.fluent_facts = get_cluster_values(external_plan)

        hint = {}
        for result, mapping in safe_zip(self.stream_plan,
                                        self.macro_from_micro):
            if isinstance(result, StreamResult):
                for param, obj in safe_zip(result.external.outputs,
                                           result.output_objects):
                    if isinstance(obj, Object):
                        hint[mapping[param]] = obj.value
        self.objectives = certified + functions
        gen_fn = get_list_gen_fn(optimizer.procedure,
                                 inputs,
                                 outputs,
                                 self.objectives,
                                 hint=hint)
        #assert len(self.get_cluster_plans()) == 1
        super(OptimizerStream,
              self).__init__(optimizer.name, gen_fn, inputs, domain, outputs,
                             certified, optimizer.info)
Exemplo n.º 2
0
def solve_pddlstream_satisfaction(stream_pddl,
                                  stream_map,
                                  init,
                                  constraints,
                                  incremental=False,
                                  **kwargs):
    # TODO: prune set of streams based on constraints
    domain, goal = planning_from_satisfaction(init, constraints)
    constant_map = {}
    problem = PDDLProblem(domain, constant_map, stream_pddl, stream_map, init,
                          goal)

    if incremental:
        plan, cost, facts = solve_incremental(problem, **kwargs)
    else:
        plan, cost, facts = solve_focused(problem, **kwargs)
    if plan is None:
        return None, cost, facts
    assert len(plan) == len(domain.actions)

    bindings = {}
    for action, (name, args) in safe_zip(domain.actions, plan):
        assert action.name == name
        for param, arg in safe_zip(action.parameters, args):
            name = param.name
            assert bindings.get(name, arg) is arg
            bindings[name] = arg
    return bindings, cost, facts
Exemplo n.º 3
0
 def create_hint(self):
     hint = {}
     for result, mapping in safe_zip(self.stream_plan, self.macro_from_micro):
         if isinstance(result, StreamResult):
             for param, obj in safe_zip(result.external.outputs, result.output_objects):
                 if isinstance(obj, Object):
                     hint[mapping[param]] = obj.value
     return hint
Exemplo n.º 4
0
def test_mapping(atoms1, atoms2):
    mapping = {}
    for a1, a2 in safe_zip(atoms1, atoms2):
        assert a1.function == a2.function
        for arg1, arg2 in safe_zip(a1.args, a2.args):
            if mapping.get(arg1, arg2) == arg2:
                mapping[arg1] = arg2
            else:
                return None
    return mapping
Exemplo n.º 5
0
def bindings_from_plan(problem, plan):
    if plan is None:
        return None
    domain = problem[0]
    bindings = {}
    for action, (name, args) in safe_zip(domain.actions, plan):
        assert action.name == name
        for param, arg in safe_zip(action.parameters, args):
            name = param.name
            assert bindings.get(name, arg) is arg
            bindings[name] = arg
    return bindings
Exemplo n.º 6
0
def recover_negative_axioms(real_task, opt_task, axiom_plans, action_plan,
                            negative_from_name):
    action_plan = reinstantiate_action_instances(opt_task, action_plan)
    axiom_plans = list(map(reinstantiate_axiom_instances, axiom_plans))
    axioms_from_name = get_derived_predicates(opt_task.axioms)

    # TODO: could instead just accumulate difference between real and opt
    opt_task.init = set(opt_task.init)
    real_states = [set(real_task.init)]
    preimage_plan = []
    for axiom_plan, action_instance in safe_zip(axiom_plans, action_plan):
        for literal in action_instance.precondition:
            # TODO: check conditional effects
            if literal.predicate in negative_from_name:
                raise NotImplementedError(
                    'Negated predicates not currently supported within actions: {}'
                    .format(literal.predicate))
        simplify_conditional_effects(real_states[-1], opt_task.init,
                                     action_instance, axioms_from_name)
        preimage = list(plan_preimage(axiom_plan + [action_instance], []))
        assert conditions_hold(
            opt_task.init,
            (l for l in preimage if l.predicate not in axioms_from_name))
        new_axiom_plan = extract_axiom_plan(opt_task,
                                            preimage,
                                            negative_from_name,
                                            static_state=real_states[-1])
        assert new_axiom_plan is not None
        preimage_plan.extend(new_axiom_plan + axiom_plan + [action_instance])
        if action_instance.name != GOAL_NAME:
            apply_action(opt_task.init, action_instance)
            real_states.append(set(real_states[-1]))
            apply_action(real_states[-1], action_instance)
    return real_states, preimage_plan
Exemplo n.º 7
0
def recover_negative_axioms(real_task, opt_task, axiom_plans, action_plan,
                            negative_from_name):
    action_plan = reinstantiate_action_instances(opt_task, action_plan)
    simplify_conditional_effects(opt_task, action_plan, negative_from_name)
    axiom_plans = list(map(reinstantiate_axiom_instances, axiom_plans))
    axioms_from_name = get_derived_predicates(opt_task.axioms)

    # TODO: could instead just accumulate difference between real and opt
    opt_task.init = set(opt_task.init)
    real_states = [set(real_task.init)]
    preimage_plan = []
    for axiom_plan, action_instance in safe_zip(axiom_plans, action_plan):
        preimage = list(plan_preimage(axiom_plan + [action_instance], []))
        assert conditions_hold(
            opt_task.init,
            (l for l in preimage if (l.predicate not in axioms_from_name and (
                l.predicate not in negative_from_name))))
        # TODO: only add derived facts and negative facts to fluent state to make normalizing easier
        new_axiom_plan = extract_axiom_plan(opt_task,
                                            preimage,
                                            negative_from_name,
                                            static_state=opt_task.init)
        #static_state=real_states[-1])
        assert new_axiom_plan is not None
        preimage_plan.extend(new_axiom_plan + axiom_plan + [action_instance])
        if action_instance.name != GOAL_NAME:
            apply_action(opt_task.init, action_instance)
            real_states.append(set(real_states[-1]))
            apply_action(real_states[-1], action_instance)
    return real_states, preimage_plan
Exemplo n.º 8
0
def simple_from_durative_action(durative_actions, fluents):
    import pddl
    simple_actions = {}
    for action in durative_actions:
        parameters = convert_parameters(action.parameters)
        conditions = list(map(convert_condition, action.condition))
        start_effects, end_effects = action.effects
        over_effects = []
        effects = list(
            map(convert_effects, [start_effects, over_effects, end_effects]))

        static_condition = pddl.Conjunction(
            list({
                literal
                for condition in conditions
                for literal in get_conjunctive_parts(condition.simplified())
                if literal.predicate not in fluents
            }))
        actions = []
        for i, (condition, effect) in enumerate(safe_zip(conditions, effects)):
            actions.append(
                pddl.Action(
                    SIMPLE_TEMPLATE.format(action.name, i), parameters,
                    len(parameters),
                    pddl.Conjunction([static_condition,
                                      condition]).simplified(), effect, None))
            #actions[-1].dump()
        simple_actions[action] = actions
    return simple_actions
Exemplo n.º 9
0
def recover_negative_axioms(real_task, opt_task, axiom_plans, action_plan,
                            negative_from_name):
    action_plan = reinstantiate_action_instances(
        opt_task, action_plan, negative_from_name=negative_from_name)
    # https://github.com/caelan/pddlstream/commit/18b303e19bbab9f8e0016fbb2656f461067e1e94#diff-55454a85485551f9139e20a446b56a83L53
    #simplify_conditional_effects(opt_task, action_plan, negative_from_name)
    axiom_plans = list(map(reinstantiate_axiom_instances, axiom_plans))
    axioms_from_name = get_derived_predicates(opt_task.axioms)

    # TODO: could instead just accumulate difference between real and opt
    opt_task.init = set(opt_task.init)
    real_states = [set(real_task.init)]
    preimage_plan = []
    for axiom_plan, action_instance in safe_zip(axiom_plans, action_plan):
        preimage = [
            l for l in plan_preimage(axiom_plan + [action_instance])
            if (l.predicate in axioms_from_name)
        ]
        #assert conditions_hold(opt_task.init, conditions)
        # TODO: only add derived facts and negative facts to fluent state to make normalizing easier
        negative_axiom_plan = extract_axiom_plan(opt_task,
                                                 preimage,
                                                 negative_from_name,
                                                 static_state=opt_task.init)
        #static_state=real_states[-1])
        assert negative_axiom_plan is not None
        preimage_plan.extend(negative_axiom_plan + axiom_plan +
                             [action_instance])
        if action_instance.name != GOAL_NAME:
            apply_action(opt_task.init, action_instance)
            real_states.append(set(real_states[-1]))
            apply_action(real_states[-1], action_instance)
    return real_states, preimage_plan
Exemplo n.º 10
0
def simple_from_durative_action(durative_actions, fluents):
    from pddlstream.algorithms.algorithm import get_predicates
    import pddl
    simple_actions = {}
    for action in durative_actions:
        parameters = convert_parameters(action.parameters)
        conditions = list(map(convert_condition, action.condition))
        start_effects, end_effects = action.effects
        over_effects = []
        effects = list(
            map(convert_effects, [start_effects, over_effects, end_effects]))

        static_condition = pddl.Conjunction(
            list({
                part
                for condition in conditions
                for part in get_conjunctive_parts(condition.simplified())
                if not isinstance(part, pddl.Truth)
                and not (get_predicates(part) & fluents)
            }))
        # TODO: deal with case where there are fluents
        actions = []
        for i, (condition, effect) in enumerate(safe_zip(conditions, effects)):
            # TODO: extract the durations by pretending they are action costs
            actions.append(
                pddl.Action(
                    SIMPLE_TEMPLATE.format(action.name, i), parameters,
                    len(parameters),
                    pddl.Conjunction([static_condition,
                                      condition]).simplified(), effect, None))
            #actions[-1].dump()
        simple_actions[action] = actions
    return simple_actions
Exemplo n.º 11
0
def is_useful_atom(atom, conditions_from_predicate):
    # TODO: this is currently a bottleneck. Instantiate for all actions along the plan first? (apply before checking)
    if not isinstance(atom, pddl.Atom):
        return False
    for atom2 in conditions_from_predicate[atom.predicate]:
        if all(is_parameter(a2) or (a1 == a2) for a1, a2 in safe_zip(atom.args, atom2.args)):
            return True
    return False
Exemplo n.º 12
0
def bindings_from_plan(plan_skeleton, action_plan):
    if action_plan is None:
        return None
    bindings = {}
    for (args1,), (args2,) in safe_zip(plan_skeleton, action_plan):
        parameter_names = [o.value for o in args1]
        bindings.update(get_mapping(parameter_names, args2))
    return bindings
Exemplo n.º 13
0
def update_bindings(bindings, opt_result, result):
    if not isinstance(result, StreamResult):
        return bindings
    new_bindings = bindings.copy()
    for opt, obj in safe_zip(opt_result.output_objects, result.output_objects):
        assert (opt not in new_bindings)  # TODO: return failure if conflicting bindings
        new_bindings[opt] = obj
    return new_bindings
Exemplo n.º 14
0
def convert_fluent_streams(stream_plan, real_states, action_plan,
                           step_from_fact, node_from_atom):
    #return stream_plan
    import pddl
    assert len(real_states) == len(action_plan) + 1
    steps_from_stream = get_steps_from_stream(stream_plan, step_from_fact,
                                              node_from_atom)

    # TODO: ensure that derived facts aren't in fluents?
    # TODO: handle case where costs depend on the outputs
    _, outgoing_edges = neighbors_from_orders(
        get_partial_orders(stream_plan,
                           init_facts=map(
                               fact_from_fd,
                               filter(lambda f: isinstance(f, pddl.Atom),
                                      real_states[0]))))
    static_plan = []
    fluent_plan = []
    for result in stream_plan:
        external = result.external
        if isinstance(result, FunctionResult) or (result.opt_index != 0) or (
                not external.is_fluent):
            static_plan.append(result)
            continue
        if outgoing_edges[result]:
            # No way of taking into account the binding of fluent inputs when preventing cycles
            raise NotImplementedError(
                'Fluent stream is required for another stream: {}'.format(
                    result))
        #if (len(steps_from_stream[result]) != 1) and result.output_objects:
        #    raise NotImplementedError('Fluent stream required in multiple states: {}'.format(result))
        for state_index in steps_from_stream[result]:
            new_output_objects = [
                #OptimisticObject.from_opt(out.value, object())
                OptimisticObject.from_opt(
                    out.value, UniqueOptValue(result.instance, object(), name))
                for name, out in safe_zip(result.external.outputs,
                                          result.output_objects)
            ]
            if new_output_objects and (state_index <= len(action_plan) - 1):
                # TODO: check that the objects aren't used in any effects
                instance = copy.copy(action_plan[state_index])
                action_plan[state_index] = instance
                output_mapping = get_mapping(
                    list(map(pddl_from_object, result.output_objects)),
                    list(map(pddl_from_object, new_output_objects)))
                instance.var_mapping = {
                    p: output_mapping.get(v, v)
                    for p, v in instance.var_mapping.items()
                }
            new_instance = get_fluent_instance(external,
                                               result.instance.input_objects,
                                               real_states[state_index])
            # TODO: handle optimistic here
            new_result = new_instance.get_result(new_output_objects,
                                                 opt_index=result.opt_index)
            fluent_plan.append(new_result)
    return static_plan + fluent_plan
Exemplo n.º 15
0
def bindings_from_plan(plan_skeleton, action_plan):
    if action_plan is None:
        return None
    bindings = {}
    for (name1, args1), (name2, args2) in safe_zip(plan_skeleton, action_plan):
        assert name1 == name2
        parameter_names = [o.value for o in args1]
        bindings.update(get_mapping(parameter_names, args2))
    return bindings
Exemplo n.º 16
0
 def wrap_optimistic(self, output_values, call_index):
     output_objects = []
     for name, value in safe_zip(self.external.outputs, output_values):
         unique = UniqueOptValue(instance=self,
                                 sequence_index=call_index,
                                 output=name)  # object()
         param = unique if (
             self.opt_index
             == 0) else value  # TODO: make a proper abstraction generator
         output_objects.append(OptimisticObject.from_opt(value, param))
     return tuple(output_objects)
Exemplo n.º 17
0
 def update_instances(self):
     updated = False
     for index, (opt_result, attempt) in enumerate(safe_zip(self.remaining_results, self.stream_attempts)):
         if self.enumerated:
             return updated
         if opt_result.instance.num_calls != attempt:
             updated = True
             for new_result in opt_result.instance.get_results(start=attempt):
                 self._instantiate(index, new_result)
             self.stream_attempts[index] = opt_result.instance.num_calls
             self.enumerated |= opt_result.instance.enumerated
     return updated
Exemplo n.º 18
0
def recover_optimistic_outputs(stream_plan):
    if not is_plan(stream_plan):
        return stream_plan
    new_mapping = {}
    new_stream_plan = []
    for result in stream_plan:
        new_result = result.remap_inputs(new_mapping)
        new_stream_plan.append(new_result)
        if isinstance(new_result, StreamResult):
            opt_result = new_result.instance.opt_results[
                0]  # TODO: empty if disabled
            new_mapping.update(
                safe_zip(new_result.output_objects, opt_result.output_objects))
    return new_stream_plan
Exemplo n.º 19
0
def compute_pruning_orders(results,
                           stats_fn=Performance.get_statistics,
                           tiebreaker_fn=lambda v: None):
    # TODO: reason about pairs that don't have a (transitive) ordering
    # TODO: partial orders make this heuristic not optimal
    # TODO: use result.external.name to cluster?
    dominates = lambda v1, v2: all(s1 <= s2 for s1, s2 in safe_zip(stats_fn(v1), stats_fn(v2))) \
                               and tiebreaker_fn(v1) <= tiebreaker_fn(v2)
    effort_orders = set()
    for v1, v2 in combinations(results, r=2):  # randomize
        if dominates(v1, v2):
            effort_orders.add((v1, v2))  # Includes equality
        elif dominates(v2, v1):
            effort_orders.add((v2, v1))
    return effort_orders
Exemplo n.º 20
0
 def _add_combinations_relation(self, stream, atoms):
     if not all(atoms):
         return
     # TODO: might be a bug here?
     domain = list(map(head_from_fact, stream.domain))
     # TODO: compute this first?
     relations = [
         Relation(filter(is_parameter, domain[index].args), [
             tuple(a for a, b in safe_zip(atom.args, domain[index].args)
                   if is_parameter(b)) for atom in atoms[index]
         ]) for index in compute_order(domain, atoms)
     ]
     solution = solve_satisfaction(relations)
     for element in solution.body:
         mapping = solution.get_mapping(element)
         input_objects = safe_apply_mapping(stream.inputs, mapping)
         self.push_instance(stream.get_instance(input_objects))
Exemplo n.º 21
0
def optimistic_stream_evaluation(evaluations, stream_plan, use_bindings=True):
    # TODO: can also use the instantiator and operate directly on the outputs
    # TODO: could bind by just using new_evaluations
    evaluations = set(evaluations) # Converts to a set for subset testing
    opt_evaluations = set(evaluations)
    new_results = []
    bindings = {} # TODO: report the depth considered
    for opt_result in stream_plan: # TODO: just refine the first step of the plan
        for new_instance in optimistic_stream_instantiation(
                opt_result.instance, (bindings if use_bindings else {}), opt_evaluations):
            for new_result in new_instance.next_optimistic():
                opt_evaluations.update(map(evaluation_from_fact, new_result.get_certified()))
                new_results.append(new_result)
                if isinstance(new_result, StreamResult): # Could not add if same value
                    for opt, obj in safe_zip(opt_result.output_objects, new_result.output_objects):
                        bindings.setdefault(opt, []).append(obj)
    return new_results, bindings
Exemplo n.º 22
0
def get_action_instances(task, action_plan):
    type_to_objects = instantiate.get_objects_by_type(task.objects, task.types)
    function_assignments = get_function_assignments(task)
    predicate_to_atoms = instantiate.get_atoms_by_predicate(task.init)
    fluent_facts = MockSet()
    init_facts = set()
    action_instances = []
    for name, objects in action_plan:
        # TODO: what if more than one action of the same name due to normalization?
        # Normalized actions have same effects, so I just have to pick one
        # TODO: conditional effects and internal parameters
        action = find_unique(lambda a: a.name == name, task.actions)
        args = list(map(pddl_from_object, objects))
        variable_mapping = {p.name: a for p, a in safe_zip(action.parameters, args)}
        instance = action.instantiate(variable_mapping, init_facts, fluent_facts, type_to_objects,
                                      task.use_min_cost_metric, function_assignments, predicate_to_atoms)
        assert (instance is not None)
        action_instances.append(instance)
    return action_instances
Exemplo n.º 23
0
def process_stream_plan_branch(store, domain, disabled, stream_plan, action_plan, cost):
    if not is_plan(stream_plan):
        return
    stream_plan = [result for result in stream_plan if result.optimistic]
    if not stream_plan:
        store.add_plan(action_plan, cost)
        return
    free_objects = get_free_objects(stream_plan)
    bindings = defaultdict(set)
    for opt_result in stream_plan:
        opt_inputs = [inp for inp in opt_result.instance.input_objects if inp in free_objects]
        inp_bindings = [bindings[inp] for inp in opt_inputs]
        for combo in product(*inp_bindings):
            bound_result = opt_result.remap_inputs(get_mapping(opt_inputs, combo))
            bound_instance = bound_result.instance
            if bound_instance.enumerated or not is_instance_ready(store.evaluations, bound_instance):
                continue # Disabled
            new_results = process_instance(store, domain, bound_instance)
            if not bound_instance.enumerated:
                disabled.add(bound_instance)
            if isinstance(opt_result, StreamResult):
                for new_result in new_results:
                    for out, obj in safe_zip(opt_result.output_objects, new_result.output_objects):
                        bindings[out].add(obj)
Exemplo n.º 24
0
def solve_pyplanners(instantiated,
                     planner=None,
                     max_planner_time=DEFAULT_MAX_TIME,
                     max_cost=INF):
    if instantiated is None:
        return None, INF

    # https://github.mit.edu/caelan/stripstream/blob/c8c6cd1d6bd5e2e8e31cd5603e28a8e0d7bb2cdc/stripstream/algorithms/search/pyplanners.py
    pyplanners_path = get_pyplanners_path()
    if pyplanners_path is None:
        raise RuntimeError(
            'Must clone https://github.com/caelan/pyplanners '
            'and set the environment variable {} to its path'.format(
                PYPLANNERS_VAR))
    if pyplanners_path not in sys.path:
        sys.path.append(pyplanners_path)

    # TODO: could operate on translated SAS instead
    from strips.states import State, PartialState
    from strips.operators import Action, Axiom
    from strips.utils import solve_strips, default_derived_plan
    import pddl

    # TODO: PLUSONE costs
    pyplanner = dict(DEFAULT_PYPLANNER)
    if isinstance(planner, dict):
        pyplanner.update(planner)

    fd_action_from_py_action = {}
    py_actions = []
    for action in instantiated.actions:
        #action.dump()
        py_action = Action({'fd_action': action})
        py_action.conditions = set(action.precondition)
        py_action.effects = set()
        for condition, effect in action.del_effects:
            assert not condition
            py_action.effects.add(effect.negate())
        for condition, effect in action.add_effects:
            assert not condition
            py_action.effects.add(effect)
        py_action.cost = action.cost
        py_action.test, fd_action_from_py_action[
            py_action] = get_attachment_test(action)
        py_actions.append(py_action)

    py_axioms = []
    for axiom in instantiated.axioms:
        #axiom.dump()
        py_axiom = Axiom({'fd_axiom_id':
                          id(axiom)})  # Not hashable for some reason
        py_axiom.conditions = set(axiom.condition)
        py_axiom.effects = {axiom.effect}
        py_axioms.append(py_axiom)

    goal = PartialState(instantiated.goal_list)
    fluents = {f.positive() for f in goal.conditions}
    for py_operator in py_actions + py_axioms:
        fluents.update(f.positive() for f in py_operator.conditions)

    initial = State(atom for atom in instantiated.task.init
                    if isinstance(atom, pddl.Atom) and (atom in fluents))

    plan, state_space = solve_strips(initial,
                                     goal,
                                     py_actions,
                                     py_axioms,
                                     max_time=max_planner_time,
                                     max_cost=max_cost,
                                     **pyplanner)
    if plan is None:
        return None, INF

    #fd_plan = [action.fd_action for action in plan.operators]
    states = plan.get_states()  # get_states | get_derived_states
    fd_plan = [
        fd_action_from_py_action[action][state]
        for state, action in safe_zip(states[:-1], plan.operators)
    ]
    actions = [pddl_from_instance(action) for action in fd_plan]
    #print(actions)
    cost = plan.cost / get_cost_scale()

    return actions, cost
Exemplo n.º 25
0
def get_pairs(sequence):
    sequence = list(sequence)
    return safe_zip(sequence[:-1], sequence[1:])
Exemplo n.º 26
0
def is_instance(atom, schema):
    return (atom.function == schema.function) and \
            all(is_parameter(b) or (a == b)
                for a, b in safe_zip(atom.args, schema.args))
Exemplo n.º 27
0
def add_plan_constraints(constraints,
                         domain,
                         evaluations,
                         goal_exp,
                         internal=False):
    if (constraints is None) or (constraints.skeletons is None):
        return goal_exp
    import pddl
    # TODO: can search over skeletons first and then fall back
    # TODO: unify this with the constraint ordering
    # TODO: can constrain to use a plan prefix
    prefix = '_' if internal else ''
    assigned_predicate = ASSIGNED_PREDICATE.format(prefix)
    group_predicate = GROUP_PREDICATE.format(prefix)
    order_predicate = ORDER_PREDICATE.format(prefix)
    for group in constraints.groups:
        for value in constraints.groups[group]:
            # TODO: could make all constants groups (like an equality group)
            fact = (group_predicate, to_obj(group), to_obj(value))
            add_fact(evaluations, fact, result=INTERNAL_EVALUATION)
    new_actions = []
    new_goals = []
    for num, skeleton in enumerate(constraints.skeletons):
        # TODO: change the prefix for these
        order_facts = [(order_predicate, to_obj('n{}'.format(num)),
                        to_obj('t{}'.format(step)))
                       for step in range(len(skeleton) + 1)]
        add_fact(evaluations, order_facts[0], result=INTERNAL_EVALUATION)
        new_goals.append(order_facts[-1])
        bound_parameters = set()
        for step, (name, args) in enumerate(skeleton):
            # TODO: could also just remove the free parameter from the action
            new_action = deepcopy(
                find_unique(lambda a: a.name == name, domain.actions))
            constant_pairs = [(a, p.name)
                              for a, p in safe_zip(args, new_action.parameters)
                              if not is_parameter(a) and a != WILD]
            skeleton_parameters = list(filter(is_parameter, args))
            existing_parameters = [
                p for p in skeleton_parameters if p in bound_parameters
            ]
            local_from_global = {
                a: p.name
                for a, p in safe_zip(args, new_action.parameters)
                if is_parameter(a)
            }

            group_preconditions = [
                (group_predicate if is_hashable(a) and
                 (a in constraints.groups) else EQ, to_obj(a), p)
                for a, p in constant_pairs
            ]
            new_preconditions = make_assignment_facts(assigned_predicate, local_from_global, existing_parameters) + \
                                group_preconditions + [order_facts[step]]
            new_action.precondition = pddl.Conjunction([
                new_action.precondition,
                make_preconditions(new_preconditions)
            ]).simplified()

            new_effects = make_assignment_facts(assigned_predicate, local_from_global, skeleton_parameters) \
                          + [Not(order_facts[step]), order_facts[step + 1]]
            new_action.effects.extend(make_effects(new_effects))
            # TODO: should also negate the effects of all other sequences here

            new_actions.append(new_action)
            bound_parameters.update(skeleton_parameters)
            #new_action.dump()
    add_predicate(domain, make_predicate(order_predicate, ['?num', '?step']))
    if constraints.exact:
        domain.actions[:] = []
    domain.actions.extend(new_actions)
    new_goal_exp = And(goal_exp, Or(*new_goals))
    return new_goal_exp
Exemplo n.º 28
0
def add_plan_constraints(constraints,
                         domain,
                         evaluations,
                         goal_exp,
                         internal=False):
    if (constraints is None) or (constraints.skeletons is None):
        return goal_exp
    import pddl
    # TODO: unify this with the constraint ordering
    # TODO: can constrain to use a plan prefix
    prefix = get_internal_prefix(internal)
    assigned_predicate = ASSIGNED_PREDICATE.format(prefix)
    bound_predicate = BOUND_PREDICATE.format(prefix)
    group_predicate = GROUP_PREDICATE.format(prefix)
    order_predicate = ORDER_PREDICATE.format(prefix)
    new_facts = []
    for group in constraints.groups:
        for value in constraints.groups[group]:
            # TODO: could make all constants groups (like an equality group)
            fact = (group_predicate, to_obj(group), to_obj(value))
            new_facts.append(fact)
    new_actions = []
    new_goals = []
    for num, skeleton in enumerate(constraints.skeletons):
        actions, orders = skeleton
        incoming_orders, _ = neighbors_from_orders(orders)
        order_facts = [(order_predicate, to_obj('n{}'.format(num)),
                        to_obj('t{}'.format(step)))
                       for step in range(len(actions))]
        for step, (name, args) in enumerate(actions):
            # TODO: could also just remove the free parameter from the action
            new_action = deepcopy(
                find_unique(lambda a: a.name == name, domain.actions))
            local_from_global = {
                a: p.name
                for a, p in safe_zip(args, new_action.parameters)
                if is_parameter(a)
            }

            ancestors, descendants = get_ancestors(step,
                                                   orders), get_descendants(
                                                       step, orders)
            parallel = set(range(
                len(actions))) - ancestors - descendants - {step}

            parameters = set(filter(is_parameter, args))
            ancestor_parameters = parameters & set(
                filter(is_parameter,
                       (p for idx in ancestors for p in actions[idx][1])))
            #descendant_parameters = parameters & set(filter(is_parameter, (p for idx in descendants for p in actions[idx][1])))
            parallel_parameters = parameters & set(
                filter(is_parameter,
                       (p for idx in parallel for p in actions[idx][1])))

            #bound_preconditions = [Imply(bound, assigned) for bound, assigned in safe_zip(bound_facts, assigned_facts)]
            bound_condition = pddl.Conjunction([
                pddl.Disjunction(
                    map(fd_from_fact, [
                        Not((bound_predicate, to_constant(p))),
                        (assigned_predicate, to_constant(p),
                         local_from_global[p])
                    ])) for p in parallel_parameters
            ])
            existing_preconditions = [(assigned_predicate, to_constant(p),
                                       local_from_global[p])
                                      for p in ancestor_parameters]

            constant_pairs = [(a, p.name)
                              for a, p in safe_zip(args, new_action.parameters)
                              if is_constant(a)]
            group_preconditions = [
                (group_predicate if is_hashable(a) and
                 (a in constraints.groups) else EQ, to_obj(a), p)
                for a, p in constant_pairs
            ]
            order_preconditions = [
                order_facts[idx] for idx in incoming_orders[step]
            ]
            new_preconditions = existing_preconditions + group_preconditions + order_preconditions + [
                Not(order_facts[step])
            ]
            new_action.precondition = pddl.Conjunction([
                new_action.precondition, bound_condition,
                make_preconditions(new_preconditions)
            ]).simplified()

            new_parameters = parameters - ancestors
            bound_facts = [(bound_predicate, to_constant(p))
                           for p in new_parameters]
            assigned_facts = [(assigned_predicate, to_constant(p),
                               local_from_global[p]) for p in new_parameters]
            new_effects = bound_facts + assigned_facts + [order_facts[step]]
            new_action.effects.extend(make_effects(new_effects))
            # TODO: should also negate the effects of all other sequences here

            new_actions.append(new_action)
            #new_action.dump()
        new_goals.append(
            And(*[order_facts[idx] for idx in incoming_orders[GOAL_INDEX]]))

    add_predicate(domain, make_predicate(order_predicate, ['?num', '?step']))
    if constraints.exact:
        domain.actions[:] = []
    domain.actions.extend(new_actions)
    new_goal_exp = And(goal_exp, Or(*new_goals))
    for fact in new_facts:
        add_fact(evaluations, fact, result=INTERNAL_EVALUATION)
    return new_goal_exp