Пример #1
0
 def _compute_links(self, state):
     """Helper method for mutex. Sets up state/action dependencies.
     """
     # Map from literal to all actions that it's a precond of.
     current_links = defaultdict(list)
     # Map from literal to all actions that it's an effect of.
     next_links = defaultdict(list)
     for lit in state.literals:
         pred = lit.predicate
         persist_pred = structs.Predicate("PERSIST" + pred.name, pred.arity,
                                          pred.var_types, pred.is_negative,
                                          pred.is_anti,
                                          pred.negated_as_failure)
         persist_lit = structs.Literal(persist_pred, lit.variables)
         current_links[lit].append(persist_lit)
         next_links[lit].append(persist_lit)
     for action in self._action_space.all_ground_literals(state):
         for op in self._learned_operators:
             assignments = self._preconds_satisfied(state, action,
                                                    op.preconds.literals)
             if assignments is None:
                 continue
             ground_preconds = [
                 structs.ground_literal(l, assignments)
                 for l in op.preconds.literals
             ]
             for precond in ground_preconds:
                 current_links[precond].append(action)
             ground_effects = [
                 structs.ground_literal(l, assignments)
                 for l in op.effects.literals
             ]
             for effect in ground_effects:
                 next_links[effect].append(action)
     return current_links, next_links
Пример #2
0
 def _get_predicted_next_state_ops(self, state, action, mode="max"):
     """WARNING: Only use this method when self._learned_operators is
     GROUND TRUTH OPS!!!
     """
     for op in self._learned_operators:
         assignments = self._preconds_satisfied(state, action,
                                                op.preconds.literals)
         if assignments is not None:
             ground_effects = []
             for l in op.effects.literals:
                 if isinstance(l, structs.ProbabilisticEffect):
                     if mode == "max":
                         # assume max-probability event
                         chosen_effect = l.max()
                     elif mode == "sample":
                         # sample an event
                         chosen_effect = l.sample()
                     else:
                         raise Exception("Invalid mode: {}".format(mode))
                     if chosen_effect == "NOCHANGE":
                         continue
                     if isinstance(chosen_effect,
                                   structs.LiteralConjunction):
                         for lit in chosen_effect.literals:
                             ground_effects.append(
                                 structs.ground_literal(lit, assignments))
                     else:
                         ground_effects.append(
                             structs.ground_literal(chosen_effect,
                                                    assignments))
                 else:
                     ground_effects.append(
                         structs.ground_literal(l, assignments))
             return self._execute_effects(state, ground_effects)
     return state  # no change
Пример #3
0
def _apply_effects(state, lifted_effects, assignments):
    """
    Update a state given lifted operator effects and
    assignments of variables to objects.

    Parameters
    ----------
    state : #TODO figure out type
        The state on which the effects are applied.
    lifted_effects : { Literal }
    assignments : { TypedEntity : TypedEntity }
        Maps variables to objects.
    """
    new_state = {lit for lit in state}

    for lifted_effect in lifted_effects:
        effect = ground_literal(lifted_effect, assignments)
        # Negative effect
        if effect.is_anti:
            literal = effect.inverted_anti
            if literal in new_state:
                new_state.remove(literal)
    for lifted_effect in lifted_effects:
        effect = ground_literal(lifted_effect, assignments)
        if not effect.is_anti:
            new_state.add(effect)
    return new_state
Пример #4
0
    def _update_objects_from_state(self, state):
        # Check whether the objects have changed
        # If so, we need to recompute things
        if state.objects == self._objects:
            return

        # Parent class update
        super()._update_objects_from_state(state)

        # Recompute all ground operators
        # Associate each ground action literal with ground preconditions
        self._ground_action_to_pos_preconds = {}
        self._ground_action_to_neg_preconds = {}
        for ground_action in self._all_ground_literals:
            operator = self._action_predicate_to_operators[
                ground_action.predicate]
            lifted_preconds = operator.preconds.literals
            subs = dict(zip(operator.params, ground_action.variables))
            subs.update(zip(self.domain.constants, self.domain.constants))
            preconds = [ground_literal(lit, subs) for lit in lifted_preconds]
            pos_preconds, neg_preconds = set(), set()
            for p in preconds:
                if p.is_negative:
                    neg_preconds.add(p.positive)
                else:
                    pos_preconds.add(p)
            self._ground_action_to_pos_preconds[ground_action] = pos_preconds
            self._ground_action_to_neg_preconds[ground_action] = neg_preconds
Пример #5
0
def _compute_new_state_from_lifted_effects(lifted_effects, assignments, new_literals):
    for lifted_effect in lifted_effects:
        if lifted_effect == NoChange():
            continue
        effect = ground_literal(lifted_effect, assignments)
        # Negative effect
        if effect.is_anti:
            literal = effect.inverted_anti
            if literal in new_literals:
                new_literals.remove(literal)
    for lifted_effect in lifted_effects:
        if lifted_effect == NoChange():
            continue
        effect = ground_literal(lifted_effect, assignments)
        if not effect.is_anti:
            new_literals.add(effect)
    return new_literals
Пример #6
0
def ground_literal_multi(lit, multi_sigma):
    """
    """
    out = []
    vals_for_vars = [multi_sigma[v] for v in lit.variables]
    for choice in itertools.product(*vals_for_vars):
        subs = dict(zip(lit.variables, choice))
        ground_lit = ground_literal(lit, subs)
        out.append(ground_lit)
    return out
Пример #7
0
 def _get_predicted_next_state_ops(self, state, action):
     """
     """
     for op in self._learned_operators:
         assignments = self._preconds_satisfied(state, action,
                                                op.preconds.literals)
         if assignments is not None:
             ground_effects = [structs.ground_literal(l, assignments) \
                 for l in op.effects.literals]
             return self._execute_effects(state, ground_effects)
     return state  # no change
Пример #8
0
 def _get_ground_effects(self, state, action):
     for op in self._learned_operators:
         assignments = self._preconds_satisfied(state, action,
                                                op.preconds.literals)
         if assignments is not None:
             ground_effects = [
                 structs.ground_literal(l, assignments)
                 for l in op.effects.literals
             ]
             return ground_effects
     return None
Пример #9
0
def _apply_effects(state, lifted_effects, assignments):
    """
    Update a state given lifted operator effects and
    assignments of variables to objects.

    Parameters
    ----------
    state : State
        The state on which the effects are applied.
    lifted_effects : { Literal }
    assignments : { TypedEntity : TypedEntity }
        Maps variables to objects.
    """
    new_literals = set(state.literals)
    determinized_lifted_effects = []
    # Handle probabilistic effects.
    for lifted_effect in lifted_effects:
        if isinstance(lifted_effect, ProbabilisticEffect):
            chosen_effect = lifted_effect.sample()
            if chosen_effect == "NOCHANGE":
                continue
            if isinstance(chosen_effect, LiteralConjunction):
                for lit in chosen_effect.literals:
                    determinized_lifted_effects.append(lit)
            else:
                determinized_lifted_effects.append(chosen_effect)
        else:
            determinized_lifted_effects.append(lifted_effect)

    for lifted_effect in determinized_lifted_effects:
        effect = ground_literal(lifted_effect, assignments)
        # Negative effect
        if effect.is_anti:
            literal = effect.inverted_anti
            if literal in new_literals:
                new_literals.remove(literal)
    for lifted_effect in determinized_lifted_effects:
        effect = ground_literal(lifted_effect, assignments)
        if not effect.is_anti:
            new_literals.add(effect)
    return state.with_literals(new_literals)
Пример #10
0
    def find_unique_matching_effect_index(self, transition):
        """Find the unique effect index that matches the transition.

        Note that the noise outcome always holds, but only return it
        if no other effects hold.

        Used for quickly learning effect probabilities.
        """
        state, action, effects = transition
        cache_key = hash((frozenset(state), action, frozenset(effects)))
        if cache_key not in self._effect_cache:
            sigma = self.find_substitutions(state, action)
            try:
                assert sigma is not None, "Rule assumed to cover transition"
            except AssertionError:
                import ipdb
                ipdb.set_trace()
            selected_outcome_idx = None
            noise_outcome_idx = None
            for i, outcome in enumerate(self.effects):
                if NOISE_OUTCOME in outcome:
                    assert noise_outcome_idx is None
                    noise_outcome_idx = i
                else:
                    ground_outcome = {
                        ground_literal(lit, sigma)
                        for lit in outcome
                    }
                    match = False
                    # Check if the ground outcome is equivalent to the effects
                    # before Anti's have been applied
                    if sorted(ground_outcome) == sorted(effects):
                        match = True
                    # Check if the ground outcome is equivalent to the effects
                    # after Anti's have been applied
                    else:
                        for lit in set(ground_outcome):
                            if lit.is_anti and lit.inverted_anti in ground_outcome:
                                ground_outcome.remove(lit)
                                ground_outcome.remove(lit.inverted_anti)
                        if sorted(ground_outcome) == sorted(effects):
                            match = True
                    if match:
                        if selected_outcome_idx is not None:
                            raise MultipleOutcomesPossible()
                        selected_outcome_idx = i
            if selected_outcome_idx is not None:
                result = selected_outcome_idx
            else:
                assert noise_outcome_idx is not None
                result = noise_outcome_idx
            self._effect_cache[cache_key] = result
        return self._effect_cache[cache_key]
Пример #11
0
 def _get_approx_reachable_set(self):
     obs = self._get_observation()
     old_ops = self.domain.operators
     self.domain.operators = self._delete_relaxed_ops
     prev_len = 0
     while prev_len != len(obs):  # do the fixed-point iteration
         prev_len = len(obs)
         for action in self.action_space.all_ground_literals():
             selected_operator, assignment = self._select_operator(action)
             if assignment is not None:
                 for lifted_effect in selected_operator.effects.literals:
                     effect = ground_literal(lifted_effect, assignment)
                     assert not effect.is_anti  # should be relaxed
                     obs.add(effect)
     self.domain.operators = old_ops
     return obs
Пример #12
0
def get_ground_conds(conds,
                     objs,
                     type_to_parent_types=None,
                     allow_duplicates=True):
    if type_to_parent_types:
        type_to_parent_types = dict(type_to_parent_types)
    vrs = sorted({v for lit in conds for v in lit.variables})
    var_types = [v.var_type for v in vrs]
    for choice in get_object_combinations(
            objs,
            len(var_types),
            var_types=var_types,
            allow_duplicates=allow_duplicates,
            type_to_parent_types=type_to_parent_types):
        assignment = dict(zip(vrs, choice))
        ground_conds = {ground_literal(lit, assignment) for lit in conds}
        yield (assignment, ground_conds)
Пример #13
0
def parse_plan_step(plan_step,
                    operators,
                    action_predicates,
                    objects,
                    operators_as_actions=False):
    plan_step_split = plan_step.split()

    if operators_as_actions:
        action_predicate = [a for a in action_predicates \
            if a.name.lower() == plan_step_split[0].lower()][0]
        object_names = plan_step_split[1:]
        args = []
        for name in object_names:
            matches = [o for o in objects if o.name == name]
            assert len(matches) == 1
            args.append(matches[0])
        return action_predicate(*args)

    # Get the operator from its name
    operator = None
    for op in operators:
        if op.name.lower() == plan_step_split[0]:
            operator = op
            break
    assert operator is not None, "Unknown operator '{}'".format(
        plan_step_split[0])

    assert len(plan_step_split) == len(operator.params) + 1
    object_names = plan_step_split[1:]
    args = []
    for name in object_names:
        matches = [o for o in objects if o.name == name]
        assert len(matches) == 1
        args.append(matches[0])
    assignments = dict(zip(operator.params, args))

    for cond in operator.preconds.literals:
        if cond.predicate in action_predicates:
            ground_action = ground_literal(cond, assignments)
            return ground_action

    import ipdb
    ipdb.set_trace()
    raise Exception("Unrecognized plan step: `{}`".format(str(plan_step)))
Пример #14
0
def get_transition_likelihood(transition, rule, p_min=P_MIN, ndr_settings=None):
    """Calculate the likelihood of a transition for a rule that covers it
    """
    try:
        effect_idx = rule.find_unique_matching_effect_index(transition)
        prob, outcome = rule.effect_probs[effect_idx], rule.effects[effect_idx]
        # Non-noise outcome
        if NOISE_OUTCOME not in outcome:
            transition_likelihood = prob
        # Noise outcome
        else:
            transition_likelihood = p_min * prob
        # if transition_likelihood == 0.:
            # import ipdb; ipdb.set_trace()
    except MultipleOutcomesPossible:
        state, action, effects = transition
        sigma = rule.find_substitutions(state, action)
        assert sigma is not None, "Rule assumed to cover transition"
        transition_likelihood = 0.
        for prob, outcome in zip(rule.effect_probs, rule.effects):
            if NOISE_OUTCOME in outcome:
                # c.f. equation 3 in paper
                transition_likelihood += p_min * prob
            else:
                ground_outcome = {ground_literal(lit, sigma) for lit in outcome}
                # Check if the ground outcome is equivalent to the effects
                # before Anti's have been applied
                if sorted(ground_outcome) == sorted(effects):
                    transition_likelihood += prob
                # Check if the ground outcome is equivalent to the effects
                # after Anti's have been applied
                else:
                    for lit in set(ground_outcome):
                        if lit.is_anti and lit.inverted_anti in ground_outcome:
                            ground_outcome.remove(lit)
                            ground_outcome.remove(lit.inverted_anti)
                    if sorted(ground_outcome) == sorted(effects):
                        transition_likelihood += prob
    return transition_likelihood
Пример #15
0
def parse_plan_step(plan_step, operators, action_predicates):
    plan_step_split = plan_step.split()

    # Get the operator from its name
    operator = None
    for op in operators:
        if op.name.lower() == plan_step_split[0]:
            operator = op
            break
    assert operator is not None, "Unknown operator '{}'".format(
        plan_step_split[0])

    assert len(plan_step_split) == len(operator.params) + 1
    args = plan_step_split[1:]
    assignments = dict(zip(operator.params, args))
    for cond in operator.preconds.literals:
        if cond.predicate in action_predicates:
            ground_action = ground_literal(cond, assignments)
            return ground_action

    import ipdb
    ipdb.set_trace()
    raise Exception("Unrecognized plan step: `{}`".format(str(plan_step)))
Пример #16
0
    def _execute_effects(self, lifted_effects, assignments):
        """
        Update the state given lifted operator effects and
        assignments of variables to objects.

        Parameters
        ----------
        lifted_effects : { Literal }
        assignments : { TypedEntity : TypedEntity }
            Maps variables to objects.
        """
        new_state = {lit for lit in self._state}

        for lifted_effect in lifted_effects:
            effect = ground_literal(lifted_effect, assignments)
            # Negative effect
            if effect.is_anti:
                literal = effect.inverted_anti
                if literal in new_state:
                    new_state.remove(literal)
            else:
                new_state.add(effect)

        self._state = new_state
Пример #17
0
 def _predict(self, state, action, ind):
     lifted_effects = self._effects[ind]
     sigma = self.find_substitutions(state, action)
     return {ground_literal(e, sigma) for e in lifted_effects}