Exemplo n.º 1
0
def reinstantiate_action_instances(task, old_instances):
    import pddl
    import instantiate
    # Recomputes the instances with without any pruned preconditions
    fluents = get_fluents(task)
    function_assignments = {
        fact.fluent: fact.expression
        for fact in task.init
        if isinstance(fact, pddl.f_expression.FunctionAssignment)
    }
    type_to_objects = instantiate.get_objects_by_type(task.objects, task.types)
    init_facts = set()
    fluent_facts = MockSet()
    new_instances = []
    state = set(task.init)
    for old_instance in old_instances:
        # TODO: better way of instantiating conditional effects (when not fluent)
        #new_instance = reinstantiate_action(old_instance)
        predicate_to_atoms = instantiate.get_atoms_by_predicate({
            a
            for a in state
            if isinstance(a, pddl.Atom) and (a.predicate in fluents)
        })
        action = old_instance.action
        var_mapping = old_instance.var_mapping
        new_instance = action.instantiate(var_mapping, init_facts,
                                          fluent_facts, type_to_objects,
                                          task.use_min_cost_metric,
                                          function_assignments,
                                          predicate_to_atoms)
        assert (new_instance is not None)
        new_instances.append(new_instance)
        apply_action(state, new_instance)
    new_instances.append(get_goal_instance(task.goal))  # TODO: move this?
    return new_instances
Exemplo n.º 2
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.º 3
0
def replace_derived(task, negative_init, action_instances):
    import pddl_to_prolog
    import build_model
    import axiom_rules
    import pddl

    original_actions = task.actions
    original_init = task.init
    task.actions = []
    function_assignments = {f.fluent: f.expression for f in task.init
                            if isinstance(f, pddl.f_expression.FunctionAssignment)}
    task.init = (set(task.init) | {a.negate() for a in negative_init}) - set(function_assignments)
    for instance in action_instances:
        #axiom_plan = extract_axiom_plan(task, instance, negative_from_name={}) # TODO: refactor this

        # TODO: just instantiate task?
        with Verbose(False):
            model = build_model.compute_model(pddl_to_prolog.translate(task))  # Changes based on init
        # fluent_facts = instantiate.get_fluent_facts(task, model)
        fluent_facts = MockSet()
        instantiated_axioms = instantiate_axioms(model, task.init, fluent_facts)
        goal_list = [] # TODO: include the goal?
        with Verbose(False):  # TODO: helpful_axioms prunes axioms that are already true (e.g. not Unsafe)
            helpful_axioms, axiom_init, _ = axiom_rules.handle_axioms([instance], instantiated_axioms, goal_list)
        axiom_from_atom = get_achieving_axioms(task.init | negative_init | set(axiom_init), helpful_axioms)
        # negated_from_name=negated_from_name)
        axiom_plan = []
        extract_axioms(axiom_from_atom, instance.precondition, axiom_plan)

        substitute_derived(axiom_plan, instance)
        assert(is_applicable(task.init, instance))
        apply_action(task.init, instance)
    task.actions = original_actions
    task.init = original_init
Exemplo n.º 4
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.º 5
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.º 6
0
def recover_axioms_plans(instantiated, action_instances):
    #axioms, axiom_init, _ = axiom_rules.handle_axioms(
    #    instantiated.actions, instantiated.axioms, instantiated.goal_list)

    new_action_instances = [
        copy.deepcopy(instance) for instance in action_instances
    ]

    axioms, axiom_init = instantiated.axioms, [
    ]  # TODO: bug when needing to reachieve negated
    axioms_from_effect = defaultdict(list)
    for axiom in axioms:
        axioms_from_effect[axiom.effect].append(axiom)
    axioms_from_name = get_derived_predicates(instantiated.task.axioms)

    state = set(instantiated.task.init) | set(axiom_init)
    axiom_plans = []
    for action in new_action_instances + [
            get_goal_instance(instantiated.task.goal)
    ]:
        all_conditions = list(get_precondition(action)) + list(
            flatten(cond
                    for cond, _ in action.add_effects + action.del_effects))
        axioms = backtrack_axioms(all_conditions, axioms_from_effect, set())
        axiom_from_atom, _ = get_achieving_axioms(state, axioms)

        action.applied_effects = []
        for effects in [action.add_effects, action.del_effects]:
            negate = (effects is action.del_effects)
            for i, (conditions, effect) in reversed(list(enumerate(effects))):
                if all(
                        literal_holds(state, literal) or (
                            literal in axiom_from_atom)
                        for literal in conditions):
                    action.precondition.extend(conditions)
                    effects[i] = ([], effect)
                    action.applied_effects.append(
                        effect.negate() if negate else effect)
                else:
                    effects.pop(i)

        # RuntimeError: Preimage fact ('new-axiom@0',) is not achievable!
        #precondition = action.precondition # TODO: strange bug if this applies
        precondition = [
            literal for literal in action.precondition
            if literal.predicate in axioms_from_name
        ]
        axiom_plans.append([])
        success = extract_axioms(state, axiom_from_atom, precondition,
                                 axiom_plans[-1])
        if not success:
            print(all_conditions)
            print(action)
            print(axioms)
            raise RuntimeError('Could not extract axioms')
        apply_action(state, action)
    return new_action_instances, axiom_plans
Exemplo n.º 7
0
def reinstantiate_action_instances(task, old_instances, **kwargs):
    # Recomputes the instances with without any pruned preconditions
    state = set(task.init)
    new_instances = []
    for old_instance in old_instances:
        # TODO: better way of instantiating conditional effects (when not fluent)
        new_instance = reinstantiate_action(state, old_instance, **kwargs)
        assert (new_instance is not None)
        new_instances.append(new_instance)
        apply_action(state, new_instance)
    new_instances.append(get_goal_instance(task.goal))  # TODO: move this?
    return new_instances
Exemplo n.º 8
0
def recover_axioms_plans2(instantiated, action_instances):
    #import axiom_rules
    #with Verbose(False):
    #    normalized_axioms, axiom_init, axiom_layer_dict = axiom_rules.handle_axioms(
    #        [], instantiated.axioms, instantiated.goal_list)
    #state = set(instantiated.task.init + axiom_init)
    normalized_axioms = instantiated.axioms  # TODO: ignoring negated because cannot reinstantiate correctly
    state = set(instantiated.task.init)
    fluents = get_fluents(state, action_instances)

    unprocessed_from_atom = defaultdict(list)
    fluents_from_axiom = {}
    remaining_from_axiom = {}
    for axiom in normalized_axioms:
        fluent_conditions = []
        for literal in axiom.condition:
            if literal.positive() in fluents:
                fluent_conditions.append(literal)
            elif not literal_holds(state, literal):
                fluent_conditions = None
                break
        if fluent_conditions is None:
            continue
        for literal in fluent_conditions:
            unprocessed_from_atom[literal].append(axiom)
        fluents_from_axiom[id(axiom)] = len(fluent_conditions)
        remaining_from_axiom[id(axiom)] = fluents_from_axiom[id(axiom)]
    static_axioms = [
        axiom for axiom, num in fluents_from_axiom.items() if num == 0
    ]

    axiom_plans = []
    for action in action_instances + [
            get_goal_instance(instantiated.task.goal)
    ]:
        axiom_from_atom = mark_iteration(state, unprocessed_from_atom,
                                         fluents_from_axiom,
                                         remaining_from_axiom, static_axioms)
        preimage = []
        for literal in action.precondition:
            if not literal_holds(state, literal):
                preimage.append(literal)
                assert literal in axiom_from_atom
        for cond, eff in (action.add_effects + action.del_effects):
            # TODO: add conditional effects that must hold here
            assert not cond
        axiom_plans.append([])
        assert extract_axioms(axiom_from_atom, preimage, axiom_plans[-1])
        apply_action(state, action)
    return axiom_plans
Exemplo n.º 9
0
def recover_axioms_plans(instantiated, action_instances):
    task = instantiated.task
    derived_predicates = get_derived_predicates(task.axioms)
    state = set(task.init)
    axiom_plans = []
    for action_instance in action_instances + [get_goal_instance(task.goal)]:
        # TODO: apply all axiom_instances unaffected by negative conditions
        preimage = list(plan_preimage([action_instance], []))
        axiom_instances = filter(
            lambda ax: all(l.predicate in derived_predicates or literal_holds(
                state, l) for l in ax.condition), instantiated.axioms)
        # Only instantiate if preimage has goal
        axiom_plan = extraction_helper(state, axiom_instances, preimage)
        assert axiom_plan is not None
        axiom_plans.append(axiom_plan)
        apply_action(state, action_instance)
    return axiom_plans
Exemplo n.º 10
0
def simplify_conditional_effects(opt_task,
                                 action_instances,
                                 negative_from_name={}):
    # TODO: extract out the minimum set of conditional effects that are actually required
    # TODO: handle more general case where can choose to achieve particular conditional effects
    # will likely require planning with streams
    axioms_from_name = get_derived_predicates(opt_task.axioms)
    state = set(opt_task.init)
    for action_instance in action_instances:
        for effects in [
                action_instance.add_effects, action_instance.del_effects
        ]:
            for i, (conditions, effect) in reversed(list(enumerate(effects))):
                if any(c.predicate in axioms_from_name for c in conditions):
                    raise NotImplementedError(
                        'Conditional effects cannot currently involve derived predicates'
                    )
                neg_conditions = [
                    literal for literal in conditions
                    if literal.predicate in negative_from_name
                ]
                pos_conditions = [
                    literal for literal in conditions
                    if literal not in neg_conditions
                ]
                #if conditions_hold(real_state, opt_conditions):
                if conditions_hold(state, pos_conditions):
                    # Holds in optimistic state
                    # Assuming that must achieve all possible conditional effects
                    if neg_conditions:
                        # TODO: use unsatisfiable
                        # Assuming that negative conditions should not be achieved
                        if len(neg_conditions) != 1:
                            raise NotImplementedError()
                        action_instance.precondition.extend(
                            l.negate() for l in neg_conditions)
                        effects.pop(i)
                    else:
                        action_instance.precondition.extend(pos_conditions)
                        effects[i] = ([], effect)
                #elif not conditions_hold(opt_state, opt_conditions):
                else:
                    # Does not hold in optimistic state
                    effects.pop(i)
        apply_action(state, action_instance)
Exemplo n.º 11
0
def is_solution(domain, evaluations, action_plan, goal_expression):
    task = task_from_domain_problem(
        domain,
        get_problem(evaluations, goal_expression, domain, unit_costs=True))
    action_instances = get_action_instances(
        task, action_plan) + [get_goal_instance(task.goal)]
    #original_init = task.init
    task.init = set(task.init)
    for instance in action_instances:
        axiom_plan = extract_axiom_plan(task,
                                        instance,
                                        negative_from_name={},
                                        static_state=task.init)
        if axiom_plan is None:
            return False
        #substitute_derived(axiom_plan, instance)
        #if not is_applicable(task.init, instance):
        #    return False
        apply_action(task.init, instance)
    return True
Exemplo n.º 12
0
def apply_actions(domain, state, plan, unit_costs=False):
    # Goal serialization just assumes the tail of the plan includes an abstract action to achieve each condition
    static_state, _ = partition_facts(domain, state)
    print('Static:', static_state)
    # TODO: might need properties that involve an object that aren't useful yet
    evaluations = evaluations_from_init(state)
    #goal_exp = obj_from_value_expression(goal)
    goal_exp = None
    problem = get_problem(evaluations, goal_exp, domain, unit_costs)
    task = task_from_domain_problem(domain, problem)
    task.init = set(task.init)
    for instance in get_action_instances(
            task, transform_plan_args(plan, Object.from_value)):
        apply_action(task.init, instance)
    fluents = get_fluents(domain)
    fluent_state = [
        value_from_evaluation(evaluation_from_fd(atom)) for atom in task.init
        if isinstance(atom, pddl.Atom) and atom.predicate in fluents
    ]
    print('Fluent:', fluent_state)
    state = static_state + fluent_state
    return state
Exemplo n.º 13
0
def recover_axioms_plans(instantiated, action_instances):
    #axioms, axiom_init, _ = axiom_rules.handle_axioms(
    #    instantiated.actions, instantiated.axioms, instantiated.goal_list)
    axioms, axiom_init = instantiated.axioms, [
    ]  # TODO: bug when needing to reachieve negated
    axioms_from_effect = defaultdict(list)
    for axiom in axioms:
        axioms_from_effect[axiom.effect].append(axiom)

    state = set(instantiated.task.init) | set(axiom_init)
    axiom_plans = []
    for action in action_instances + [
            get_goal_instance(instantiated.task.goal)
    ]:
        all_conditions = list(get_precondition(action)) + list(
            flatten(cond
                    for cond, _ in action.add_effects + action.del_effects))
        axioms = backtrack_axioms(all_conditions, axioms_from_effect, set())
        axiom_from_atom, _ = get_achieving_axioms(state, axioms)
        action.applied_effects = []
        for effects in [action.add_effects, action.del_effects]:
            negate = effects is action.del_effects
            for i, (conditions, effect) in reversed(list(enumerate(effects))):
                if all(
                        literal_holds(state, literal) or (
                            literal in axiom_from_atom)
                        for literal in conditions):
                    action.precondition.extend(conditions)
                    effects[i] = ([], effect)
                    action.applied_effects.append(
                        effect.negate() if negate else effect)
                else:
                    effects.pop(i)
        axiom_plans.append([])
        assert extract_axioms(state, axiom_from_atom, action.precondition,
                              axiom_plans[-1])
        apply_action(state, action)
    return axiom_plans
Exemplo n.º 14
0
def recover_stream_plan(evaluations,
                        goal_expression,
                        domain,
                        stream_results,
                        action_plan,
                        negative,
                        unit_costs,
                        optimize=True):
    import pddl_to_prolog
    import build_model
    import pddl
    import axiom_rules
    import instantiate
    # Universally quantified conditions are converted into negative axioms
    # Existentially quantified conditions are made additional preconditions
    # Universally quantified effects are instantiated by doing the cartesian produce of types (slow)
    # Added effects cancel out removed effects

    opt_evaluations = evaluations_from_stream_plan(evaluations, stream_results)
    opt_task = task_from_domain_problem(
        domain,
        get_problem(opt_evaluations, goal_expression, domain, unit_costs))
    real_task = task_from_domain_problem(
        domain, get_problem(evaluations, goal_expression, domain, unit_costs))
    function_assignments = {
        fact.fluent: fact.expression
        for fact in opt_task.init  # init_facts
        if isinstance(fact, pddl.f_expression.FunctionAssignment)
    }
    type_to_objects = instantiate.get_objects_by_type(opt_task.objects,
                                                      opt_task.types)
    results_from_head = get_results_from_head(opt_evaluations)

    action_instances = []
    for name, args in action_plan:  # TODO: negative atoms in actions
        candidates = []
        for action in opt_task.actions:
            if action.name != name:
                continue
            if len(action.parameters) != len(args):
                raise NotImplementedError(
                    'Existential quantifiers are not currently '
                    'supported in preconditions: {}'.format(name))
            variable_mapping = {
                p.name: a
                for p, a in zip(action.parameters, args)
            }
            instance = action.instantiate(variable_mapping, set(), MockSet(),
                                          type_to_objects,
                                          opt_task.use_min_cost_metric,
                                          function_assignments)
            assert (instance is not None)
            candidates.append(((action, args), instance))
        if not candidates:
            raise RuntimeError(
                'Could not find an applicable action {}'.format(name))
        action_instances.append(candidates)
    action_instances.append([(None, get_goal_instance(opt_task.goal))])

    axioms_from_name = get_derived_predicates(opt_task.axioms)
    negative_from_name = {n.name: n for n in negative}
    opt_task.actions = []
    opt_state = set(opt_task.init)
    real_state = set(real_task.init)
    preimage_plan = []
    function_plan = set()
    for layer in action_instances:
        for pair, instance in layer:
            nonderived_preconditions = [
                l for l in instance.precondition
                if l.predicate not in axioms_from_name
            ]
            #nonderived_preconditions = instance.precondition
            if not conditions_hold(opt_state, nonderived_preconditions):
                continue
            opt_task.init = opt_state
            original_axioms = opt_task.axioms
            axiom_from_action = get_necessary_axioms(instance, original_axioms,
                                                     negative_from_name)
            opt_task.axioms = []
            opt_task.actions = axiom_from_action.keys()
            # TODO: maybe it would just be better to drop the negative throughout this process until this end
            with Verbose(False):
                model = build_model.compute_model(
                    pddl_to_prolog.translate(
                        opt_task))  # Changes based on init
            opt_task.axioms = original_axioms

            opt_facts = instantiate.get_fluent_facts(
                opt_task, model) | (opt_state - real_state)
            mock_fluent = MockSet(lambda item: (
                item.predicate in negative_from_name) or (item in opt_facts))
            instantiated_axioms = instantiate_necessary_axioms(
                model, real_state, mock_fluent, axiom_from_action)
            with Verbose(False):
                helpful_axioms, axiom_init, _ = axiom_rules.handle_axioms(
                    [instance], instantiated_axioms, [])
            axiom_from_atom = get_achieving_axioms(opt_state, helpful_axioms,
                                                   axiom_init,
                                                   negative_from_name)
            axiom_plan = []  # Could always add all conditions
            extract_axioms(axiom_from_atom, instance.precondition, axiom_plan)
            # TODO: test if no derived solution

            # TODO: compute required stream facts in a forward way and allow opt facts that are already known required
            for effects in [instance.add_effects, instance.del_effects]:
                for i, (conditions, effect) in enumerate(effects[::-1]):
                    if any(c.predicate in axioms_from_name
                           for c in conditions):
                        raise NotImplementedError(
                            'Conditional effects cannot currently involve derived predicates'
                        )
                    if conditions_hold(real_state, conditions):
                        # Holds in real state
                        effects[i] = ([], effect)
                    elif not conditions_hold(opt_state, conditions):
                        # Does not hold in optimistic state
                        effects.pop(i)
                    else:
                        # TODO: handle more general case where can choose to achieve particular conditional effects
                        raise NotImplementedError(
                            'Conditional effects cannot currently involve certified predicates'
                        )
            #if any(conditions for conditions, _ in instance.add_effects + instance.del_effects):
            #    raise NotImplementedError('Conditional effects are not currently supported: {}'.format(instance.name))

            # TODO: add axiom init to reset state?
            apply_action(opt_state, instance)
            apply_action(real_state, instance)
            preimage_plan.extend(axiom_plan + [instance])
            if not unit_costs and (pair is not None):
                function_plan.update(
                    extract_function_results(results_from_head, *pair))
            break
        else:
            raise RuntimeError('No action instances are applicable')

    preimage = plan_preimage(preimage_plan, set())
    preimage -= set(real_task.init)
    negative_preimage = set(
        filter(lambda a: a.predicate in negative_from_name, preimage))
    preimage -= negative_preimage
    # visualize_constraints(map(fact_from_fd, preimage))
    # TODO: prune with rules
    # TODO: linearization that takes into account satisfied goals at each level
    # TODO: can optimize for all streams & axioms all at once

    for literal in negative_preimage:
        negative = negative_from_name[literal.predicate]
        instance = negative.get_instance(map(obj_from_pddl, literal.args))
        value = not literal.negated
        if instance.enumerated:
            assert (instance.value == value)
        else:
            function_plan.add(
                PredicateResult(instance, value, opt_index=instance.opt_index))

    node_from_atom = get_achieving_streams(evaluations, stream_results)
    preimage_facts = list(
        map(fact_from_fd, filter(lambda l: not l.negated, preimage)))
    stream_plan = []
    extract_stream_plan(node_from_atom, preimage_facts, stream_plan)
    if not optimize:  # TODO: detect this based on unique or not
        return stream_plan + list(function_plan)

    # TODO: search in space of partially ordered plans
    # TODO: local optimization - remove one and see if feasible

    reschedule_problem = get_problem(evaluations,
                                     And(*preimage_facts),
                                     domain,
                                     unit_costs=True)
    reschedule_task = task_from_domain_problem(domain, reschedule_problem)
    reschedule_task.actions, stream_result_from_name = get_stream_actions(
        stream_results)
    new_plan, _ = solve_from_task(reschedule_task,
                                  planner='max-astar',
                                  debug=False)
    # TODO: investigate admissible heuristics
    if new_plan is None:
        return stream_plan + list(function_plan)

    new_stream_plan = [stream_result_from_name[name] for name, _ in new_plan]
    return new_stream_plan + list(function_plan)
Exemplo n.º 15
0
def recover_stream_plan(evaluations, current_plan, opt_evaluations,
                        goal_expression, domain, node_from_atom, action_plan,
                        axiom_plans, negative, replan_step):
    # Universally quantified conditions are converted into negative axioms
    # Existentially quantified conditions are made additional preconditions
    # Universally quantified effects are instantiated by doing the cartesian produce of types (slow)
    # Added effects cancel out removed effects
    # TODO: node_from_atom is a subset of opt_evaluations (only missing functions)
    real_task = task_from_domain_problem(
        domain, get_problem(evaluations, goal_expression, domain))
    opt_task = task_from_domain_problem(
        domain, get_problem(opt_evaluations, goal_expression, domain))
    negative_from_name = {
        external.blocked_predicate: external
        for external in negative if external.is_negated()
    }
    real_states, full_plan = recover_negative_axioms(real_task, opt_task,
                                                     axiom_plans, action_plan,
                                                     negative_from_name)
    function_plan = compute_function_plan(opt_evaluations, action_plan)

    full_preimage = plan_preimage(full_plan,
                                  [])  # Does not contain the stream preimage!
    negative_preimage = set(
        filter(lambda a: a.predicate in negative_from_name, full_preimage))
    negative_plan = convert_negative(negative_preimage, negative_from_name,
                                     full_preimage, real_states)
    function_plan.update(negative_plan)
    # TODO: OrderedDict for these plans

    # TODO: this assumes that actions do not negate preimage goals
    positive_preimage = {
        l
        for l in (set(full_preimage) - real_states[0] - negative_preimage)
        if not l.negated
    }
    steps_from_fact = {
        fact_from_fd(l): full_preimage[l]
        for l in positive_preimage
    }
    last_from_fact = {
        fact: min(steps)
        for fact, steps in steps_from_fact.items() if get_prefix(fact) != EQ
    }
    #stream_plan = reschedule_stream_plan(evaluations, target_facts, domain, stream_results)
    # visualize_constraints(map(fact_from_fd, target_facts))

    for result, step in function_plan.items():
        for fact in result.get_domain():
            last_from_fact[fact] = min(step, last_from_fact.get(fact, INF))

    # TODO: get_steps_from_stream
    stream_plan = []
    last_from_stream = dict(function_plan)
    for result in current_plan:  # + negative_plan?
        # TODO: actually compute when these are needed + dependencies
        last_from_stream[result] = 0
        if isinstance(result.external, Function) or (result.external
                                                     in negative):
            if len(action_plan) != replan_step:
                raise NotImplementedError(
                )  # TODO: deferring negated optimizers
            # Prevents these results from being pruned
            function_plan[result] = replan_step
        else:
            stream_plan.append(result)

    curr_evaluations = evaluations_from_stream_plan(evaluations,
                                                    stream_plan,
                                                    max_effort=None)
    extraction_facts = set(last_from_fact) - set(
        map(fact_from_evaluation, curr_evaluations))
    extract_stream_plan(node_from_atom, extraction_facts, stream_plan)

    # Recomputing due to postprocess_stream_plan
    stream_plan = postprocess_stream_plan(evaluations, domain, stream_plan,
                                          last_from_fact)
    node_from_atom = get_achieving_streams(evaluations,
                                           stream_plan,
                                           max_effort=None)
    fact_sequence = [set(result.get_domain())
                     for result in stream_plan] + [extraction_facts]
    for facts in reversed(fact_sequence):  # Bellman ford
        for fact in facts:  # could flatten instead
            result = node_from_atom[fact].result
            if result is None:
                continue
            step = last_from_fact[fact] if result.is_deferrable() else 0
            last_from_stream[result] = min(step,
                                           last_from_stream.get(result, INF))
            for domain_fact in result.instance.get_domain():
                last_from_fact[domain_fact] = min(
                    last_from_stream[result],
                    last_from_fact.get(domain_fact, INF))
    stream_plan.extend(function_plan)

    # Useful to recover the correct DAG
    partial_orders = set()
    for child in stream_plan:
        # TODO: account for fluent objects
        for fact in child.get_domain():
            parent = node_from_atom[fact].result
            if parent is not None:
                partial_orders.add((parent, child))
    #stream_plan = topological_sort(stream_plan, partial_orders)

    bound_objects = set()
    for result in stream_plan:
        if (last_from_stream[result]
                == 0) or not result.is_deferrable(bound_objects=bound_objects):
            for ancestor in get_ancestors(result, partial_orders) | {result}:
                # TODO: this might change descendants of ancestor. Perform in a while loop.
                last_from_stream[ancestor] = 0
                if isinstance(ancestor, StreamResult):
                    bound_objects.update(out for out in ancestor.output_objects
                                         if out.is_unique())

    #local_plan = [] # TODO: not sure what this was for
    #for fact, step in sorted(last_from_fact.items(), key=lambda pair: pair[1]): # Earliest to latest
    #    print(step, fact)
    #    extract_stream_plan(node_from_atom, [fact], local_plan, last_from_fact, last_from_stream)

    # Each stream has an earliest evaluation time
    # When computing the latest, use 0 if something isn't deferred
    # Evaluate each stream as soon as possible
    # Option to defer streams after a point in time?
    # TODO: action costs for streams that encode uncertainty
    state = set(real_task.init)
    remaining_results = list(stream_plan)
    first_from_stream = {}
    #assert 1 <= replan_step # Plan could be empty
    for step, instance in enumerate(action_plan):
        for result in list(remaining_results):
            # TODO: could do this more efficiently if need be
            domain = result.get_domain() + get_fluent_domain(result)
            if conditions_hold(state, map(fd_from_fact, domain)):
                remaining_results.remove(result)
                certified = {
                    fact
                    for fact in result.get_certified()
                    if get_prefix(fact) != EQ
                }
                state.update(map(fd_from_fact, certified))
                if step != 0:
                    first_from_stream[result] = step
        # TODO: assumes no fluent axiom domain conditions
        apply_action(state, instance)
    #assert not remaining_results # Not true if retrace
    if first_from_stream:
        replan_step = min(replan_step, *first_from_stream.values())

    eager_plan = []
    results_from_step = defaultdict(list)
    for result in stream_plan:
        earliest_step = first_from_stream.get(result, 0)
        latest_step = last_from_stream.get(result, 0)
        assert earliest_step <= latest_step
        defer = replan_step <= latest_step
        if not defer:
            eager_plan.append(result)
        # We only perform a deferred evaluation if it has all deferred dependencies
        # TODO: make a flag that also allows dependencies to be deferred
        future = (earliest_step != 0) or defer
        if future:
            future_step = latest_step if defer else earliest_step
            results_from_step[future_step].append(result)

    # TODO: some sort of obj side-effect bug that requires obj_from_pddl to be applied last (likely due to fluent streams)
    eager_plan = convert_fluent_streams(eager_plan, real_states, action_plan,
                                        steps_from_fact, node_from_atom)
    combined_plan = []
    for step, action in enumerate(action_plan):
        combined_plan.extend(result.get_action()
                             for result in results_from_step[step])
        combined_plan.append(
            transform_action_args(pddl_from_instance(action), obj_from_pddl))

    # TODO: the returned facts have the same side-effect bug as above
    # TODO: annotate when each preimage fact is used
    preimage_facts = {
        fact_from_fd(l)
        for l in full_preimage if (l.predicate != EQ) and not l.negated
    }
    for negative_result in negative_plan:  # TODO: function_plan
        preimage_facts.update(negative_result.get_certified())
    for result in eager_plan:
        preimage_facts.update(result.get_domain())
        # Might not be able to regenerate facts involving the outputs of streams
        preimage_facts.update(
            result.get_certified())  # Some facts might not be in the preimage
    # TODO: record streams and axioms
    return eager_plan, OptPlan(combined_plan, preimage_facts)
Exemplo n.º 16
0
def recover_stream_plan(evaluations, goal_expression, domain, stream_results, action_plan, negative, unit_costs):
    import pddl
    import instantiate
    # Universally quantified conditions are converted into negative axioms
    # Existentially quantified conditions are made additional preconditions
    # Universally quantified effects are instantiated by doing the cartesian produce of types (slow)
    # Added effects cancel out removed effects

    real_task = task_from_domain_problem(domain, get_problem(evaluations, goal_expression, domain, unit_costs))
    node_from_atom = get_achieving_streams(evaluations, stream_results)
    opt_evaluations = apply_streams(evaluations, stream_results)
    opt_task = task_from_domain_problem(domain, get_problem(opt_evaluations, goal_expression, domain, unit_costs))
    function_assignments = {fact.fluent: fact.expression for fact in opt_task.init  # init_facts
                            if isinstance(fact, pddl.f_expression.FunctionAssignment)}
    type_to_objects = instantiate.get_objects_by_type(opt_task.objects, opt_task.types)
    results_from_head = get_results_from_head(opt_evaluations)
    action_instances = instantiate_actions(opt_task, type_to_objects, function_assignments, action_plan)
    negative_from_name = get_negative_predicates(negative)
    axioms_from_name = get_derived_predicates(opt_task.axioms)

    opt_task.init = set(opt_task.init)
    real_states = [set(real_task.init)] # TODO: had old way of doing this (~July 2018)
    preimage_plan = []
    function_plan = set()
    for layer in action_instances:
        for pair, action_instance in layer:
            axiom_plan = extract_axiom_plan(opt_task, action_instance, negative_from_name,
                                            static_state=real_states[-1])
            if axiom_plan is None:
                continue
            simplify_conditional_effects(real_states[-1], opt_task.init, action_instance, axioms_from_name)
            preimage_plan.extend(axiom_plan + [action_instance])
            apply_action(opt_task.init, action_instance)
            real_states.append(set(real_states[-1]))
            apply_action(real_states[-1], action_instance)
            if not unit_costs and (pair is not None):
                function_result = extract_function_results(results_from_head, *pair)
                if function_result is not None:
                    function_plan.add(function_result)
            break
        else:
            raise RuntimeError('No action instances are applicable')

    # TODO: could instead just accumulate difference between real and opt
    full_preimage = plan_preimage(preimage_plan, [])
    stream_preimage = set(full_preimage) - real_states[0]
    negative_preimage = set(filter(lambda a: a.predicate in negative_from_name, stream_preimage))
    positive_preimage = stream_preimage - negative_preimage
    function_plan.update(convert_negative(negative_preimage, negative_from_name, full_preimage, real_states))

    step_from_fact = {fact_from_fd(l): full_preimage[l] for l in positive_preimage if not l.negated}
    target_facts = list(step_from_fact.keys())
    #stream_plan = reschedule_stream_plan(evaluations, target_facts, domain, stream_results)
    stream_plan = []
    extract_stream_plan(node_from_atom, target_facts, stream_plan)
    stream_plan = prune_stream_plan(evaluations, stream_plan, target_facts)
    stream_plan = convert_fluent_streams(stream_plan, real_states, step_from_fact, node_from_atom)
    # visualize_constraints(map(fact_from_fd, stream_preimage))

    if DO_RESCHEDULE: # TODO: detect this based on unique or not
        # TODO: maybe test if partial order between two ways of achieving facts, if not prune
        new_stream_plan = reschedule_stream_plan(evaluations, target_facts, domain, stream_plan)
        if new_stream_plan is not None:
            stream_plan = new_stream_plan
    return stream_plan + list(function_plan)