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
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)
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
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
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
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 = {}
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
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
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
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)))
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
def get_parameters(goal_facts): return {o for f in goal_facts for o in get_args(f) if isinstance(o, OptimisticObject)}
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)))
def rename_atom(atom, mapping): name = get_prefix(atom) if name not in mapping: return atom return (mapping[name],) + get_args(atom)
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)))