Пример #1
0
def variables_to_numbers(effect, conditions):
    new_effect_args = list(effect.args)
    rename_map = {}
    for i, arg in enumerate(effect.args):
        if arg[0] == "?":
            rename_map[arg] = i
            new_effect_args[i] = i
    new_effect = pddl.Atom(effect.predicate, new_effect_args)

    # There are three possibilities for arguments in conditions:
    # 1. They are variables that occur in the effect. In that case,
    #    they are replaced by the corresponding position in the
    #    effect, as indicated by the rename_map.
    # 2. They are constants. In that case, the unifier must guarantee
    #    that they are matched appropriately. In that case, they are
    #    not modified (remain strings denoting objects).
    # 3. They are variables that don't occur in the effect (are
    #    projected away). This is only allowed in projection rules.
    #    Such arguments are also not modified (remain "?x" strings).

    new_conditions = []
    for cond in conditions:
        new_cond_args = [rename_map.get(arg, arg) for arg in cond.args]
        new_conditions.append(pddl.Atom(cond.predicate, new_cond_args))
    return new_effect, new_conditions
Пример #2
0
 def build_rules(self, rules):
     axiom = self.owner
     app_rule_head = get_axiom_predicate(axiom)
     app_rule_body = condition_to_rule_body(axiom.parameters,
                                            self.condition)
     rules.append((app_rule_body, app_rule_head))
     params = axiom.parameters[:axiom.num_external_parameters]
     eff_rule_head = pddl.Atom(axiom.name, [par.name for par in params])
     eff_rule_body = [app_rule_head]
     rules.append((eff_rule_body, eff_rule_head))
Пример #3
0
 def push(self, predicate, args):
     self.num_pushes += 1
     eff_tuple = (predicate,) + tuple(args)
     if eff_tuple not in self.enqueued:
         self.enqueued.add(eff_tuple)
         new_elm = pddl.Atom(predicate, list(args))
         score = self.get_score(new_elm)
         hq.heappush(self.priority_queue, (score, next(self.tiebreak), new_elm))
         if not isinstance(predicate, pddl.Action):
             self.new_facts.append(new_elm)
Пример #4
0
def substitute_complicated_goal(task):
    goal = task.goal
    if isinstance(goal, pddl.Literal):
        return
    elif isinstance(goal, pddl.Conjunction):
        for item in goal.parts:
            if not isinstance(item, pddl.Literal):
                break
        else:
            return
    new_axiom = task.add_axiom([], goal)
    task.goal = pddl.Atom(new_axiom.name, new_axiom.parameters)
Пример #5
0
 def _rename_duplicate_variables(self, atom, new_conditions):
     used_variables = set()
     for i, var_name in enumerate(atom.args):
         if var_name[0] == "?":
             if var_name in used_variables:
                 new_var_name = "%s@%d" % (var_name, len(new_conditions))
                 atom = atom.replace_argument(i, new_var_name)
                 new_conditions.append(
                     pddl.Atom("=", [var_name, new_var_name]))
             else:
                 used_variables.add(var_name)
     return atom
Пример #6
0
    def remove_free_effect_variables(self):
        """Remove free effect variables like the variable Y in the rule
        p(X, Y) :- q(X). This is done by introducing a new predicate
        @object, setting it true for all objects, and translating the above
        rule to p(X, Y) :- q(X), @object(Y).
        After calling this, no new objects should be introduced!"""

        # Note: This should never be necessary for typed domains.
        # Leaving it in at the moment regardless.
        must_add_predicate = False
        for rule in self.rules:
            eff_vars = get_variables([rule.effect])
            cond_vars = get_variables(rule.conditions)
            if not eff_vars.issubset(cond_vars):
                must_add_predicate = True
                eff_vars -= cond_vars
                for var in sorted(eff_vars):
                    rule.add_condition(pddl.Atom("@object", [var]))
        if must_add_predicate:
            print("Unbound effect variables: Adding @object predicate.")
            self.facts += [
                Fact(pddl.Atom("@object", [obj])) for obj in self.objects
            ]
Пример #7
0
def parse_literal(alist, type_dict, predicate_dict, negated=False):
    if alist[0] == "not":
        assert len(alist) == 2
        alist = alist[1]
        negated = not negated

    pred_id, arity = _get_predicate_id_and_arity(alist[0], type_dict,
                                                 predicate_dict)

    if arity != len(alist) - 1:
        raise SystemExit("predicate used with wrong arity: (%s)" %
                         " ".join(alist))

    if negated:
        return pddl.NegatedAtom(pred_id, alist[1:])
    else:
        return pddl.Atom(pred_id, alist[1:])
Пример #8
0
 def convert_trivial_rules(self):
     """Convert rules with an empty condition into facts.
     This must be called after bounding rule effects, so that rules with an
     empty condition must necessarily have a variable-free effect.
     Variable-free effects are the only ones for which a distinction between
     ground and symbolic atoms is not necessary."""
     must_delete_rules = []
     for i, rule in enumerate(self.rules):
         if not rule.conditions:
             assert not get_variables([rule.effect])
             self.add_fact(
                 pddl.Atom(rule.effect.predicate, rule.effect.args))
             must_delete_rules.append(i)
     if must_delete_rules:
         print("Trivial rules: Converted to facts.")
         for rule_no in must_delete_rules[::-1]:
             del self.rules[rule_no]
Пример #9
0
def expand_group(group, task, reachable_facts):
    result = []
    for fact in group:
        try:
            pos = list(fact.args).index("?X")
        except ValueError:
            result.append(fact)
        else:
            # NOTE: This could be optimized by only trying objects of the correct
            #       type, or by using a unifier which directly generates the
            #       applicable objects. It is not worth optimizing this at this stage,
            #       though.
            for obj in task.objects:
                newargs = list(fact.args)
                newargs[pos] = obj.name
                atom = pddl.Atom(fact.predicate, newargs)
                if atom in reachable_facts:
                    result.append(atom)
    return result
Пример #10
0
def parse_task(domain_pddl, task_pddl):
    domain_name, domain_requirements, types, type_dict, constants, predicates, predicate_dict, functions, actions, axioms \
                 = parse_domain_pddl(domain_pddl)
    task_name, task_domain_name, task_requirements, objects, init, goal, use_metric = parse_task_pddl(
        task_pddl, type_dict, predicate_dict)

    assert domain_name == task_domain_name
    requirements = pddl.Requirements(
        sorted(
            set(domain_requirements.requirements +
                task_requirements.requirements)))
    objects = constants + objects
    check_for_duplicates(
        [o.name for o in objects],
        errmsg="error: duplicate object %r",
        finalmsg="please check :constants and :objects definitions")
    init += [pddl.Atom("=", (obj.name, obj.name)) for obj in objects]

    return pddl.Task(domain_name, task_name, requirements, types, objects,
                     predicates, functions, init, goal, actions, axioms,
                     use_metric)
Пример #11
0
def condition_to_rule_body(parameters, condition):
    result = []
    for par in parameters:
        result.append(par.get_atom())
    if not isinstance(condition, pddl.Truth):
        if isinstance(condition, pddl.ExistentialCondition):
            for par in condition.parameters:
                result.append(par.get_atom())
            condition = condition.parts[0]
        if isinstance(condition, pddl.Conjunction):
            parts = condition.parts
        else:
            parts = (condition, )
        for part in parts:
            if isinstance(part, pddl.Falsity):
                # Use an atom in the body that is always false because
                # it is not initially true and doesn't occur in the
                # head of any rule.
                return [pddl.Atom("@always-false", [])]
            assert isinstance(
                part, pddl.Literal), "Condition not normalized: %r" % part
            if not part.negated:
                result.append(part)
    return result
Пример #12
0
def get_axiom_predicate(axiom):
    name = axiom
    variables = [par.name for par in axiom.parameters]
    if isinstance(axiom.condition, pddl.ExistentialCondition):
        variables += [par.name for par in axiom.condition.parameters]
    return pddl.Atom(name, variables)
Пример #13
0
 def build_rules(self, rules):
     rule_head = pddl.Atom("@goal-reachable", [])
     rule_body = condition_to_rule_body([], self.condition)
     rules.append((rule_body, rule_head))
Пример #14
0
 def add_rule(self, type, conditions, effect_vars):
     effect = pddl.Atom(next(self.name_generator), effect_vars)
     rule = pddl_to_prolog.Rule(conditions, effect)
     rule.type = type
     self.result.append(rule)
     return rule.effect
Пример #15
0
 def instantiate(self, parameters):
     args = ["?X"] * (len(self.order) + (self.omitted_pos != -1))
     for arg, argpos in zip(parameters, self.order):
         args[argpos] = arg
     return pddl.Atom(self.predicate, args)
Пример #16
0
def project_rule(rule, conditions, name_generator):
    predicate = next(name_generator)
    effect_variables = set(rule.effect.args) & get_variables(conditions)
    effect = pddl.Atom(predicate, sorted(effect_variables))
    projected_rule = Rule(conditions, effect)
    return projected_rule
Пример #17
0
def parse_task_pddl(task_pddl, type_dict, predicate_dict):
    iterator = iter(task_pddl)

    define_tag = next(iterator)
    assert define_tag == "define"
    problem_line = next(iterator)
    assert problem_line[0] == "problem" and len(problem_line) == 2
    yield problem_line[1]
    domain_line = next(iterator)
    assert domain_line[0] == ":domain" and len(domain_line) == 2
    yield domain_line[1]

    requirements_opt = next(iterator)
    if requirements_opt[0] == ":requirements":
        requirements = requirements_opt[1:]
        objects_opt = next(iterator)
    else:
        requirements = []
        objects_opt = requirements_opt
    yield pddl.Requirements(requirements)

    if objects_opt[0] == ":objects":
        yield parse_typed_list(objects_opt[1:])
        init = next(iterator)
    else:
        yield []
        init = objects_opt

    assert init[0] == ":init"
    initial = []
    initial_true = set()
    initial_false = set()
    initial_assignments = dict()
    for fact in init[1:]:
        if fact[0] == "=":
            try:
                assignment = parse_assignment(fact)
            except ValueError as e:
                raise SystemExit("Error in initial state specification\n" +
                                 "Reason: %s." % e)
            if not isinstance(assignment.expression, pddl.NumericConstant):
                raise SystemExit("Illegal assignment in initial state " +
                                 "specification:\n%s" % assignment)
            if assignment.fluent in initial_assignments:
                prev = initial_assignments[assignment.fluent]
                if assignment.expression == prev.expression:
                    print("Warning: %s is specified twice" % assignment,
                          "in initial state specification")
                else:
                    raise SystemExit("Error in initial state specification\n" +
                                     "Reason: conflicting assignment for " +
                                     "%s." % assignment.fluent)
            else:
                initial_assignments[assignment.fluent] = assignment
                initial.append(assignment)
        elif fact[0] == "not":
            atom = pddl.Atom(fact[1][0], fact[1][1:])
            check_atom_consistency(atom, initial_false, initial_true, False)
            initial_false.add(atom)
        else:
            atom = pddl.Atom(fact[0], fact[1:])
            check_atom_consistency(atom, initial_true, initial_false)
            initial_true.add(atom)
    initial.extend(initial_true)
    yield initial

    goal = next(iterator)
    assert goal[0] == ":goal" and len(goal) == 2
    yield parse_condition(goal[1], type_dict, predicate_dict)

    use_metric = False
    for entry in iterator:
        if entry[0] == ":metric":
            if entry[1] == "minimize" and entry[2][0] == "total-cost":
                use_metric = True
            else:
                assert False, "Unknown metric."
    yield use_metric

    for entry in iterator:
        assert False, entry
Пример #18
0
def translate_strips_conditions_aux(conditions, dictionary, ranges):
    condition = {}
    for fact in conditions:
        if fact.negated:
            # we handle negative conditions later, because then we
            # can recognize when the negative condition is already
            # ensured by a positive condition
            continue
        for var, val in dictionary.get(fact, ()):
            # The default () here is a bit of a hack. For goals (but
            # only for goals!), we can get static facts here. They
            # cannot be statically false (that would have been
            # detected earlier), and hence they are statically true
            # and don't need to be translated.
            # TODO: This would not be necessary if we dealt with goals
            # in the same way we deal with operator preconditions etc.,
            # where static facts disappear during grounding. So change
            # this when the goal code is refactored (also below). (**)
            if (condition.get(var) is not None
                    and val not in condition.get(var)):
                # Conflicting conditions on this variable: Operator invalid.
                return None
            condition[var] = {val}

    def number_of_values(var_vals_pair):
        var, vals = var_vals_pair
        return len(vals)

    for fact in conditions:
        if fact.negated:
            ## Note: here we use a different solution than in Sec. 10.6.4
            ## of the thesis. Compare the last sentences of the third
            ## paragraph of the section.
            ## We could do what is written there. As a test case,
            ## consider Airport ADL tasks with only one airport, where
            ## (occupied ?x) variables are encoded in a single variable,
            ## and conditions like (not (occupied ?x)) do occur in
            ## preconditions.
            ## However, here we avoid introducing new derived predicates
            ## by treat the negative precondition as a disjunctive
            ## precondition and expanding it by "multiplying out" the
            ## possibilities.  This can lead to an exponential blow-up so
            ## it would be nice to choose the behaviour as an option.
            done = False
            new_condition = {}
            atom = pddl.Atom(fact.predicate, fact.args)  # force positive
            for var, val in dictionary.get(atom, ()):
                # see comment (**) above
                poss_vals = set(range(ranges[var]))
                poss_vals.remove(val)

                if condition.get(var) is None:
                    assert new_condition.get(var) is None
                    new_condition[var] = poss_vals
                else:
                    # constrain existing condition on var
                    prev_possible_vals = condition.get(var)
                    done = True
                    prev_possible_vals.intersection_update(poss_vals)
                    if len(prev_possible_vals) == 0:
                        # Conflicting conditions on this variable:
                        # Operator invalid.
                        return None

            if not done and len(new_condition) != 0:
                # we did not enforce the negative condition by constraining
                # an existing condition on one of the variables representing
                # this atom. So we need to introduce a new condition:
                # We can select any from new_condition and currently prefer the
                # smallest one.
                candidates = sorted(new_condition.items(),
                                    key=number_of_values)
                var, vals = candidates[0]
                condition[var] = vals

        def multiply_out(condition):  # destroys the input
            sorted_conds = sorted(condition.items(), key=number_of_values)
            flat_conds = [{}]
            for var, vals in sorted_conds:
                if len(vals) == 1:
                    for cond in flat_conds:
                        cond[var] = vals.pop()  # destroys the input here
                else:
                    new_conds = []
                    for cond in flat_conds:
                        for val in vals:
                            new_cond = deepcopy(cond)
                            new_cond[var] = val
                            new_conds.append(new_cond)
                    flat_conds = new_conds
            return flat_conds

    return multiply_out(condition)
Пример #19
0
 def push(self, predicate, args):
     self.num_pushes += 1
     eff_tuple = (predicate,) + tuple(args)
     if eff_tuple not in self.enqueued:
         self.enqueued.add(eff_tuple)
         self.queue.append(pddl.Atom(predicate, list(args)))