예제 #1
0
    def add_atom(self, atom):
        if not is_atom(atom):
            return False
        head = atom.head
        if head in self.atoms:
            return False
        self.atoms.add(head)
        # TODO: doing this in a way that will eventually allow constants

        for i, stream in enumerate(self.streams):
            for j, domain_atom in enumerate(stream.domain):
                if get_prefix(head) != get_prefix(domain_atom):
                    continue
                if len(head.args) != len(get_args(domain_atom)):
                    raise ValueError(head, domain_atom)
                if any(
                        isinstance(b, Object) and (a != b)
                        for (a, b) in zip(head.args, get_args(domain_atom))):
                    continue
                self.atoms_from_domain[(i, j)].append(head)
                values = [
                    self.atoms_from_domain[(i, k)] if j != k else [head]
                    for k in range(len(stream.domain))
                ]
                domain = list(map(head_from_fact, stream.domain))
                #domain = stream.domain
                for combo in product(*values):
                    mapping = get_mapping(domain, combo)
                    if mapping is None:
                        continue
                    input_objects = tuple(mapping[p] for p in stream.inputs)
                    self._add_instance(stream, input_objects)
        return True
예제 #2
0
def fd_from_fact(fact):
    # TODO: convert to evaluation?
    prefix = get_prefix(fact)
    if prefix == NOT:
        return fd_from_fact(fact[1]).negate()
    if prefix == EQ:
        _, head, value = fact
        predicate = get_prefix(head)
        args = map(pddl_from_object, get_args(head))
        fluent = pddl.f_expression.PrimitiveNumericExpression(symbol=predicate,
                                                              args=args)
        expression = pddl.f_expression.NumericConstant(value)
        return pddl.f_expression.Assign(fluent, expression)
    args = map(pddl_from_object, get_args(fact))
    return pddl.Atom(prefix, args)
예제 #3
0
def create_domain(goal_facts):
    domain = make_domain()
    for fact in goal_facts: # TODO: consider removing this annoying check
        name = get_prefix(fact)
        parameters = ['?x{}'.format(i) for i in range(len(get_args(fact)))]
        add_predicate(domain, make_predicate(name, parameters))
    return domain
예제 #4
0
def create_disable_axiom(external_plan, use_parameters=True):
    # TODO: express constraint mutexes upfront
    # TODO: investigate why use_parameters=True hurts satisfaction
    # TODO: better mix optimization and sampling by determining a splitting point
    # TODO: be careful about the shared objects as parameters
    # TODO: need to block functions & predicates
    stream_plan, _ = partition_external_plan(external_plan)
    assert stream_plan
    #component_plan = stream_plan
    [unsatisfiable] = stream_plan[-1].get_unsatisfiable()
    component_plan = list(flatten(
        r.get_components() for r in stream_plan[:-1])) + list(unsatisfiable)
    increase_free_variables(component_plan)
    #output_objects = get_free_objects(component_plan) if use_parameters else set()
    constraints = [result.stream_fact for result in component_plan]
    optimistic_objects = {
        o
        for f in constraints for o in get_args(f)
        if isinstance(o, OptimisticObject)
    }  # TODO: consider case when variables are free
    #assert optimistic_objects <= output_objects
    #free_objects = list(optimistic_objects & output_objects) # TODO: need to return all variables
    free_objects = optimistic_objects
    parameters = ['?p{}'.format(i) for i in range(len(free_objects))]
    param_from_obj = get_mapping(free_objects, parameters)
    preconditions = substitute_expression(constraints, param_from_obj)
    effect = (UNSATISFIABLE, )
    axiom = make_axiom(parameters, preconditions, effect)
    #axiom.dump()
    return axiom
예제 #5
0
    def __init__(self, name, gen_fn, inputs, domain, outputs, certified, info):
        if info is None:
            info = StreamInfo(p_success=None, overhead=None)
        for p, c in Counter(outputs).items():
            if c != 1:
                raise ValueError(
                    'Output [{}] for stream [{}] is not unique'.format(
                        p, name))
        for p in set(inputs) & set(outputs):
            raise ValueError(
                'Parameter [{}] for stream [{}] is both an input and output'.
                format(p, name))
        parameters = {
            a
            for i in certified for a in get_args(i) if is_parameter(a)
        }
        for p in (parameters - set(inputs + outputs)):
            raise ValueError(
                'Parameter [{}] for stream [{}] is not included within outputs'
                .format(p, name))
        super(Stream, self).__init__(name, info, inputs, domain)
        # Each stream could certify a stream-specific fact as well
        if gen_fn == DEBUG:
            #gen_fn = from_fn(lambda *args: tuple(object() for _ in self.outputs))
            gen_fn = from_fn(lambda *args: tuple(
                DebugValue(name, args, o) for o in self.outputs))
        self.gen_fn = gen_fn
        self.outputs = tuple(outputs)
        self.certified = tuple(certified)
        self.constants.update(a for i in certified for a in get_args(i)
                              if not is_parameter(a))

        # TODO: generalize to a hierarchical sequence
        always_unique = False
        if always_unique:
            self.num_opt_fns = 0
            #self.opt_list_fn = get_unique_fn(self)
            self.opt_gen_fn = get_constant_gen_fn(self, None)
        else:
            self.num_opt_fns = 1
            self.opt_gen_fn = get_shared_gen_fn(self) if (
                self.info.opt_gen_fn is None) else self.info.opt_gen_fn
예제 #6
0
 def __init__(self, name, info, inputs, domain):
     super(External, self).__init__(name, info)
     for p, c in Counter(inputs).items():
         if c != 1:
             raise ValueError(
                 'Input [{}] for stream [{}] is not unique'.format(p, name))
     parameters = {
         a
         for i in domain for a in get_args(i) if is_parameter(a)
     }
     for p in (parameters - set(inputs)):
         raise ValueError(
             'Parameter [{}] for stream [{}] is not included within inputs'.
             format(p, name))
     self.inputs = tuple(inputs)
     self.domain = tuple(domain)
     self.constants = {
         a
         for i in domain for a in get_args(i) if not is_parameter(a)
     }
     self.instances = {}
예제 #7
0
 def __init__(self, head, fn, domain, info):
     if info is None:
         info = FunctionInfo(p_success=self._default_p_success,
                             overhead=self._default_overhead)
     super(Function, self).__init__(get_prefix(head), info, get_args(head),
                                    domain)
     self.head = head
     opt_fn = lambda *args: self._codomain()
     if fn == DEBUG:
         fn = opt_fn
     self.fn = fn
     self.opt_fn = opt_fn if (
         self.info.opt_fn is None) else self.info.opt_fn
예제 #8
0
def visualize_constraints(constraints,
                          filename='constraint_network.pdf',
                          use_functions=True):
    from pygraphviz import AGraph

    graph = AGraph(strict=True, directed=False)
    graph.node_attr['style'] = 'filled'
    graph.node_attr['shape'] = 'circle'
    graph.node_attr['fontcolor'] = 'black'
    graph.node_attr['colorscheme'] = 'SVG'
    graph.edge_attr['colorscheme'] = 'SVG'

    functions = set()
    heads = set()
    for fact in constraints:
        if get_prefix(fact) == EQ:
            functions.add(fact[1])
        else:
            heads.add(fact)
    heads.update(functions)

    objects = {a for head in heads for a in get_args(head)}
    optimistic_objects = filter(lambda o: isinstance(o, OptimisticObject),
                                objects)
    for opt_obj in optimistic_objects:
        graph.add_node(str(opt_obj), shape='circle', color=PARAMETER_COLOR)

    for head in heads:
        if not use_functions and (head in functions):
            continue
        # TODO: prune values w/o free parameters?
        name = str_from_tuple(head)
        color = COST_COLOR if head in functions else CONSTRAINT_COLOR
        graph.add_node(name, shape='box', color=color)
        for arg in get_args(head):
            if arg in optimistic_objects:
                graph.add_edge(name, str(arg))
    graph.draw(filename, prog='dot')
    return graph
예제 #9
0
def plan_functions(functions, externals):
    external_from_function = {}
    for external in filter(lambda e: isinstance(e, Function), externals):
        assert external.function not in external_from_function
        external_from_function[external.function] = external
    function_plan = set()
    for term in functions:
        if get_prefix(term) not in external_from_function:
            raise ValueError('{} is not implemented'.format(get_prefix(term)))
        external = external_from_function[get_prefix(term)]
        instance = external.get_instance(get_args(term))
        [result] = instance.next_optimistic()
        function_plan.add(result)
    print('Function plan:', str_from_object(function_plan))
    return function_plan
예제 #10
0
def compile_to_exogenous_actions(evaluations, domain, streams):
    import pddl
    # TODO: automatically derive fluents
    # TODO: version of this that operates on fluents of length one?
    # TODO: better instantiation when have full parameters
    # TODO: conversion from stream cost to real cost units?
    # TODO: any predicates derived would need to be replaced as well
    fluent_predicates = get_fluents(domain)
    domain_predicates = {get_prefix(a) for s in streams for a in s.domain}
    if not (domain_predicates & fluent_predicates):
        return

    certified_predicates = {get_prefix(a) for s in streams for a in s.certified}
    future_map = {p: 'f-{}'.format(p) for p in certified_predicates}
    augment_evaluations(evaluations, future_map)
    rename_future = lambda a: rename_atom(a, future_map)
    for stream in list(streams):
        if not isinstance(stream, Stream):
            raise NotImplementedError(stream)
        # TODO: could also just have conditions asserting that one of the fluent conditions fails
        streams.append(create_static_stream(stream, evaluations, fluent_predicates, rename_future))
        stream_atom = streams[-1].certified[0]
        parameters = [pddl.TypedObject(p, 'object') for p in get_args(stream_atom)]
        # TODO: add to predicates as well?
        domain.predicate_dict[get_prefix(stream_atom)] = pddl.Predicate(get_prefix(stream_atom), parameters)
        precondition = pddl.Conjunction(tuple(map(fd_from_fact, (stream_atom,) + tuple(stream.domain))))
        effects = [pddl.Effect(parameters=[], condition=pddl.Truth(),
                               literal=fd_from_fact(fact)) for fact in stream.certified]
        effort = 1 # TODO: use stream info
        #effort = 1 if unit_cost else result.instance.get_effort()
        #if effort == INF:
        #    continue
        fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[])
        expression = pddl.NumericConstant(int_ceil(effort)) # Integer
        cost = pddl.Increase(fluent=fluent, expression=expression) # Can also be None
        domain.actions.append(pddl.Action(name='call-{}'.format(stream.name),
                                          parameters=parameters,
                                          num_external_parameters=len(parameters),
                                          precondition=precondition, effects=effects, cost=cost))
        stream.certified = tuple(set(stream.certified) |
                                 set(map(rename_future, stream.certified)))
예제 #11
0
def planning_from_satisfaction(init, constraints):
    clusters = cluster_constraints(constraints)
    order_value_facts = [(ORDER_PREDICATE, 't{}'.format(i))
                         for i in range(len(clusters) + 1)]
    init.append(order_value_facts[0])
    goal_expression = order_value_facts[-1]
    order_facts = list(map(obj_from_value_expression, order_value_facts))
    bound_parameters = set()
    actions = []
    #constants = {}
    for i, cluster in enumerate(clusters):
        objectives = list(map(obj_from_value_expression, cluster.constraints))
        #free_parameters = cluster.parameters - bound_parameters
        existing_parameters = cluster.parameters & bound_parameters
        # TODO: confirm that negated predicates work as intended

        name = 'cluster-{}'.format(i)
        parameters = list(sorted(cluster.parameters))
        preconditions = [(ASSIGNED_PREDICATE, to_constant(p), p) for p in sorted(existing_parameters)] + \
                        get_constraints(objectives) + [order_facts[i]]
        effects = [(ASSIGNED_PREDICATE, to_constant(p), p) for p in parameters] + \
                  [order_facts[i+1], Not(order_facts[i])]

        costs = get_costs(objectives)
        cost = None
        if costs:
            assert len(costs) == 1
            cost = get_args(costs[0])[0]
        actions.append(
            make_action(name, parameters, preconditions, effects, cost))
        #actions[-1].dump()
        bound_parameters.update(cluster.parameters)

    predicates = [make_predicate(ORDER_PREDICATE, ['?x'])]
    domain = make_domain(predicates=predicates, actions=actions)
    return domain, goal_expression
예제 #12
0
def get_parameters(goal_facts):
    return {o for f in goal_facts for o in get_args(f) if isinstance(o, OptimisticObject)}
예제 #13
0
def get_parameters(expression):
    head = get_prefix(expression)
    if head in [NOT, MINIMIZE]:
        return get_parameters(get_args(expression)[0])
    return list(filter(is_parameter, get_args(expression)))
예제 #14
0
def rename_atom(atom, mapping):
    name = get_prefix(atom)
    if name not in mapping:
        return atom
    return (mapping[name],) + get_args(atom)
예제 #15
0
def compile_to_exogenous_axioms(evaluations, domain, streams):
    import pddl
    fluent_predicates = get_fluents(domain)
    domain_predicates = {get_prefix(a) for s in streams for a in s.domain}
    if not (domain_predicates & fluent_predicates):
        return

    certified_predicates = {get_prefix(a) for s in streams for a in s.certified}
    future_map = {p: 'f-{}'.format(p) for p in certified_predicates}
    augment_evaluations(evaluations, future_map)
    rename_future = lambda a: rename_atom(a, future_map)
    derived_map = {p: 'd-{}'.format(p) for p in certified_predicates}
    rename_derived = lambda a: rename_atom(a, derived_map)

    for action in domain.actions:
        action.precondition = replace_predicates(derived_map, action.precondition)
        for effect in action.effects:
            assert(isinstance(effect, pddl.Effect))
            effect.condition = replace_predicates(derived_map, effect.condition)
    for axiom in domain.axioms:
        axiom.condition = replace_predicates(derived_map, axiom.condition)

    #fluent_predicates.update(certified_predicates)
    for stream in list(streams):
        if not isinstance(stream, Stream):
            raise NotImplementedError(stream)
        streams.append(create_static_stream(stream, evaluations, fluent_predicates, rename_future))
        stream_atom = streams[-1].certified[0]
        domain.predicate_dict[get_prefix(stream_atom)] = pddl.Predicate(get_prefix(stream_atom), get_args(stream_atom))
        precondition = pddl.Conjunction(tuple(map(fd_from_fact, (stream_atom,) + tuple(map(rename_derived, stream.domain)))))
        for fact in stream.certified:
            derived_fact = fd_from_fact(rename_derived(fact))
            external_params = derived_fact.args
            internal_params = tuple(p for p in (stream.inputs + stream.outputs)
                                        if p not in derived_fact.args)
            parameters = tuple(pddl.TypedObject(p, 'object')
                                     for p in (external_params + internal_params))
            #precondition = pddl.Conjunction(tuple(map(fd_from_fact, [stream_atom] +
            #                                        list(map(rename_derived, stream.domain)))))
            #precondition = pddl.Disjunction([fd_from_fact(fact), precondition]) # TODO: quantifier
            domain.axioms.extend([
                pddl.Axiom(name=derived_fact.predicate, parameters=parameters,
                           num_external_parameters=len(external_params),
                           condition=precondition),
                pddl.Axiom(name=derived_fact.predicate,  parameters=parameters[:len(external_params)],
                           num_external_parameters=len(external_params),
                           condition=fd_from_fact(fact))])
        stream.certified = tuple(set(stream.certified) |
                                 set(map(rename_future, stream.certified)))