Beispiel #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
Beispiel #2
0
def get_mapping(atoms1, atoms2, initial={}):
    assert len(atoms1) == len(atoms2)
    mapping = initial.copy()
    for a1, a2 in zip(atoms1, atoms2):
        assert (get_prefix(a1) == get_prefix(a2))
        #for arg1, arg2 in zip(get_args(a1), a2.args): # TODO: this is because eval vs predicate
        for arg1, arg2 in zip(a1.args, a2.args):
            if mapping.get(arg1, arg2) == arg2:
                mapping[arg1] = arg2
            else:
                return None
    return mapping
Beispiel #3
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)))
Beispiel #4
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)
Beispiel #5
0
def create_static_stream(stream, evaluations, fluent_predicates, get_future):
    def static_fn(*input_values):
        instance = stream.get_instance(tuple(map(Object.from_value, input_values)))
        if all(evaluation_from_fact(f) in evaluations for f in instance.get_domain()):
            return None
        return tuple(FutureValue(stream.name, input_values, o) for o in stream.outputs)

    #opt_evaluations = None
    def static_opt_gen_fn(*input_values):
        instance = stream.get_instance(tuple(map(Object.from_value, input_values)))
        if all(evaluation_from_fact(f) in evaluations for f in instance.get_domain()):
            return
        for output_values in stream.opt_gen_fn(*input_values):
            yield output_values
        # TODO: need to replace regular opt_gen_fn to update opt_evaluations
        # if I want to prevent switch from normal to static in opt

    # Focused algorithm naturally biases against using future because of axiom layers
    stream_name = 'future-{}'.format(stream.name)
    gen_fn = from_fn(static_fn)
    static_domain = list(filter(lambda a: get_prefix(a) not in fluent_predicates, stream.domain))
    new_domain = list(map(get_future, static_domain))
    stream_atom = ('{}-result'.format(stream.name),) + tuple(stream.inputs + stream.outputs)
    new_certified = [stream_atom] + list(map(get_future, stream.certified))
    static_stream = Stream(stream_name, gen_fn, stream.inputs, new_domain,
                          stream.outputs, new_certified, stream.info)
    static_stream.opt_gen_fn = static_opt_gen_fn
    return static_stream
Beispiel #6
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)))
Beispiel #7
0
def get_stream_instances(stream_plan):
    import pddl
    # TODO: something that inverts the negative items
    stream_instances = []  # TODO: could even apply these to the state directly
    for result in stream_plan:
        name = result.instance.external.name
        precondition = list(map(fd_from_fact, result.instance.get_domain()))
        effects = [([], fd_from_fact(fact)) for fact in result.get_certified()
                   if not get_prefix(fact) == EQ]
        cost = None  # TODO: effort?
        instance = pddl.PropositionalAction(name, precondition, effects, cost)
        stream_instances.append(instance)
    return stream_instances
Beispiel #8
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
Beispiel #9
0
def parse_common(lisp_list, stream_map, stream_info):
    assert (2 <= len(lisp_list) <= 3)
    head = tuple(lisp_list[1])
    assert (is_head(head))
    name = get_prefix(head)
    if stream_map == DEBUG:
        fn = DEBUG
    else:
        if name not in stream_map:
            raise ValueError('Undefined external function: {}'.format(name))
        fn = stream_map[name]
    domain = []
    if len(lisp_list) == 3:
        domain = list_from_conjunction(lisp_list[2])
    info = stream_info.get(name, None)
    return head, fn, domain, info
Beispiel #10
0
 def next_results(self, verbose=False):
     start_time = time.time()
     assert not self.enumerated
     self.enumerated = True
     input_values = self.get_input_values()
     try:
         value = self.external.fn(*input_values)
     except TypeError:
         raise TypeError('Function [{}] expects {} inputs'.format(
             self.external.name, len(input_values)))
     self.value = self.external._codomain(value)
     # TODO: cast the inputs and test whether still equal?
     #if not (type(self.value) is self.external._codomain):
     #if not isinstance(self.value, self.external._codomain):
     if self.value != value:
         raise ValueError(
             'Function [{}] produced a nonintegral value [{}]. '
             'FastDownward only supports integral costs. '
             'To "use" real costs, scale each cost by a large factor, '
             'capturing the most significant bits.'.format(
                 self.external.name, self.value))
     if self.value < 0:
         raise ValueError(
             'Function [{}] produced a negative value [{}]'.format(
                 self.external.name, self.value))
     if verbose:
         print('{}{}={}'.format(get_prefix(self.external.head),
                                str_from_tuple(self.get_input_values()),
                                self.value))
     results = [self.external._Result(self, self.value)]
     if isinstance(self, PredicateInstance) and (
             self.value != self.external.opt_fn(*input_values)):
         self.update_statistics(start_time,
                                [])  # TODO: do this more automatically
     else:
         self.update_statistics(start_time, results)
     return results
Beispiel #11
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
Beispiel #12
0
 def add_fact(fact):
     head, color = (fact[1], COST_COLOR) if get_prefix(fact) == EQ else (fact, CONSTRAINT_COLOR)
     s_fact = str_from_tuple(head)
     graph.add_node(s_fact, shape='box', color=color)
     return s_fact
Beispiel #13
0
def rename_atom(atom, mapping):
    name = get_prefix(atom)
    if name not in mapping:
        return atom
    return (mapping[name],) + get_args(atom)