def compile_to_exogenous_actions(evaluations, domain, streams): # TODO: version of this that operates on fluents of length one? # TODO: better instantiation when have full parameters fluent_predicates = get_fluents(domain) 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) future_fn = lambda a: rename_atom(a, future_map) new_streams = [] 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 new_streams.append(create_static_stream(stream, evaluations, fluent_predicates, future_fn)) stream_atom = new_streams[-1].certified[0] add_predicate(domain, make_predicate(get_prefix(stream_atom), get_args(stream_atom))) preconditions = [stream_atom] + list(stream.domain) effort = 1 # TODO: use stream info #effort = 1 if unit_cost else result.instance.get_effort() #if effort == INF: # continue domain.actions.append(make_action( name='call-{}'.format(stream.name), parameters=get_args(stream_atom), preconditions=preconditions, effects=stream.certified, cost=effort)) stream.certified = tuple(set(stream.certified) | set(map(future_fn, stream.certified))) if REPLACE_STREAM: streams.extend(new_streams) else: streams[:] = new_streams
def retrace_instantiation(fact, streams, evaluations, free_parameters, visited_facts, planned_results): # Makes two assumptions: # 1) Each stream achieves a "primary" fact that uses all of its inputs + outputs # 2) Outputs are only free parameters (no constants) if (evaluation_from_fact(fact) in evaluations) or (fact in visited_facts): return visited_facts.add(fact) for stream in streams: for cert in stream.certified: if get_prefix(fact) == get_prefix(cert): mapping = get_mapping(get_args(cert), get_args(fact)) # Should be same anyways if not all(p in mapping for p in (stream.inputs + stream.outputs)): # TODO: assumes another effect is sufficient for binding # Create arbitrary objects for inputs/outputs that aren't mentioned # Can lead to incorrect ordering continue input_objects = tuple(mapping[p] for p in stream.inputs) output_objects = tuple(mapping[p] for p in stream.outputs) if not all(out in free_parameters for out in output_objects): # Can only bind if free continue instance = stream.get_instance(input_objects) for new_fact in instance.get_domain(): retrace_instantiation(new_fact, streams, evaluations, free_parameters, visited_facts, planned_results) planned_results.append(instance.get_result(output_objects))
def retrace_instantiation(fact, streams, evaluations, visited_facts, planned_results): if (evaluation_from_fact(fact) in evaluations) or (fact in visited_facts): return visited_facts.add(fact) for stream in streams: for cert in stream.certified: if get_prefix(fact) == get_prefix(cert): mapping = get_mapping(get_args(cert), get_args(fact)) # Should be same anyways if not all(p in mapping for p in (stream.inputs + stream.outputs)): # TODO: assumes another effect is sufficient for binding # Create arbitrary objects for inputs/outputs that aren't mentioned # Can lead to incorrect ordering continue input_objects = tuple(mapping[p] for p in stream.inputs) output_objects = tuple(mapping[p] for p in stream.outputs) if not all( isinstance(out, OptimisticObject) for out in output_objects): # Can only bind if free continue instance = stream.get_instance(input_objects) for new_fact in instance.get_domain(): retrace_instantiation(new_fact, streams, evaluations, visited_facts, planned_results) result = instance.get_result(output_objects) planned_results.append(result)
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 compile_to_exogenous_axioms(evaluations, domain, streams): # TODO: no attribute certified import pddl fluent_predicates = get_fluents(domain) 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)) preconditions = [stream_atom] + list(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=make_preconditions(preconditions)), 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)))
def compile_to_exogenous_axioms(evaluations, domain, streams): # TODO: no attribute certified # TODO: recover the streams that are required import pddl fluent_predicates = get_fluents(domain) 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) future_fn = lambda a: rename_atom(a, future_map) derived_map = {p: 'd-{}'.format(p) for p in certified_predicates} derived_fn = lambda a: rename_atom(a, derived_map) # TODO: could prune streams that don't need this treatment 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) new_streams = [] for stream in list(streams): if not isinstance(stream, Stream): raise NotImplementedError(stream) new_streams.append( create_static_stream(stream, evaluations, fluent_predicates, future_fn)) stream_atom = new_streams[-1].certified[0] add_predicate( domain, make_predicate(get_prefix(stream_atom), get_args(stream_atom))) preconditions = [stream_atom] + list(map(derived_fn, stream.domain)) for certified_fact in stream.certified: derived_fact = derived_fn(certified_fact) external_params = get_args(derived_fact) internal_params = tuple(p for p in (stream.inputs + stream.outputs) if p not in get_args(derived_fact)) domain.axioms.extend([ make_axiom(parameters=external_params, preconditions=[certified_fact], derived=derived_fact), make_axiom(parameters=external_params + internal_params, preconditions=preconditions, derived=derived_fact), ]) stream.certified = tuple( set(stream.certified) | set(map(future_fn, stream.certified))) if REPLACE_STREAM: streams.extend(new_streams) else: streams[:] = new_streams
def get_mapping(atoms1, atoms2): assert len(atoms1) == len(atoms2) mapping = {} 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
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 = list(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 = list(map(pddl_from_object, get_args(fact))) return pddl.Atom(prefix, args)
def fn(literal, action): if literal.predicate not in predicate_map: return literal # TODO: other checks on only inputs stream = predicate_map[literal.predicate] mapping = remap_certified(literal, stream) if mapping is None: # TODO: this excludes typing. This is not entirely safe return literal output_args = set(mapping[arg] for arg in stream.outputs) if isinstance(action, pddl.Action): # TODO: unified Action/Axiom effects for effect in action.effects: if isinstance(effect, pddl.Effect) and (output_args & set(effect.literal.args)): raise RuntimeError('Fluent stream outputs cannot be in action effects: {}'.format( effect.literal.predicate)) elif not stream.is_negated: axiom = action raise RuntimeError('Fluent stream outputs cannot be in an axiom: {}'.format(axiom.name)) blocked_args = safe_apply_mapping(stream.inputs, mapping) blocked_literal = literal.__class__(stream.blocked_predicate, blocked_args).negate() if stream.is_negated: conditions = [blocked_literal] conditions.extend(pddl.Atom(get_prefix(fact), safe_apply_mapping(get_args(fact), mapping)) # fd_from_fact for fact in stream.domain) # TODO: be careful when using imply return pddl.Conjunction(conditions) # TODO: prune redundant conditions return pddl.Conjunction([literal, blocked_literal])
def remap_inputs(self, bindings): input_objects = remap_objects(self.instance.input_objects, bindings) fluent_facts = [(get_prefix(f),) + remap_objects(get_args(f), bindings) for f in self.instance.fluent_facts] new_instance = self.external.get_instance(input_objects, fluent_facts=fluent_facts) new_instance.opt_index = self.instance.opt_index return self.__class__(new_instance, self.output_objects, self.opt_index)
def create_static_stream(stream, evaluations, fluent_predicates, future_fn): 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 fluent_domain = list(filter(lambda a: get_prefix(a) in fluent_predicates, stream.domain)) static_domain = list(filter(lambda a: a not in fluent_domain, stream.domain)) new_domain = list(map(future_fn, static_domain)) stream_atom = ('{}-result'.format(stream.name),) + tuple(stream.inputs + stream.outputs) new_certified = [stream_atom] + list(map(future_fn, stream.certified)) static_stream = FutureStream(stream, new_domain, fluent_domain, new_certified) if REPLACE_STREAM: static_stream.gen_fn = from_fn(static_fn) static_stream.opt_gen_fn = static_opt_gen_fn return static_stream
def process_conditional_effect(effect, negative_from_predicate): import pddl new_parts = [] stream_facts = [] for disjunctive in get_conjunctive_parts(effect.condition): for literal in get_disjunctive_parts(disjunctive): # TODO: assert only one disjunctive part if isinstance(literal, pddl.Literal) and (literal.predicate in negative_from_predicate): stream = negative_from_predicate[literal.predicate] if not isinstance(stream, ConstraintStream): new_parts.append(literal) continue certified = find_unique( lambda f: get_prefix(f) == literal.predicate, stream.certified) mapping = get_mapping(get_args(certified), literal.args) stream_facts.append( fd_from_fact( substitute_expression(stream.stream_fact, mapping))) # TODO: add the negated literal as precondition here? else: new_parts.append(literal) return new_parts, stream_facts
def next_results(self, verbose=False): start_time = time.time() assert not self.enumerated self.enumerated = True input_values = self.get_input_values() value = self.external.fn(*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 < 0: raise ValueError( 'Function [{}] produced a negative value [{}]'.format( self.external.name, self.value)) if (self.value is not False) and verbose: start_call = 0 print('{}) {}{}={}'.format( start_call, get_prefix(self.external.head), str_from_object(self.get_input_values()), self.value)) results = [ self._Result(self, self.value, opt_index=None, optimistic=False) ] #if isinstance(self, PredicateInstance) and (self.value != self.external.opt_fn(*input_values)): # self.update_statistics(start_time, []) self.update_statistics(start_time, results) new_facts = [] return results, new_facts
def combine_optimizer_plan(stream_plan, functions): if not stream_plan: return stream_plan optimizer = get_optimizer(stream_plan[-1]) if optimizer is None: return stream_plan function_plan = list( filter( lambda r: get_prefix(r.instance.external.head) in optimizer. objectives, functions)) external_plan = stream_plan + function_plan if CLUSTER: partial_orders = get_partial_orders(external_plan) cluster_plans = get_connected_components(external_plan, partial_orders) else: cluster_plans = [external_plan] optimizer_plan = [] for cluster_plan in cluster_plans: if all(isinstance(r, FunctionResult) for r in cluster_plan): continue #if len(cluster_plan) == 1: # optimizer_plan.append(cluster_plan[0]) # continue stream = OptimizerStream(optimizer, cluster_plan) instance = stream.get_instance(stream.input_objects, fluent_facts=stream.fluent_facts) result = instance.get_result(stream.output_objects) optimizer_plan.append(result) return optimizer_plan
def check_problem(domain, streams, obj_from_constant): for action in domain.actions + domain.axioms: for p, c in Counter(action.parameters).items(): if c != 1: raise ValueError('Parameter [{}] for action [{}] is not unique'.format(p.name, action.name)) # TODO: check that no undeclared parameters & constants #action.dump() undeclared_predicates = set() for stream in streams: # TODO: domain.functions facts = list(stream.domain) if isinstance(stream, Stream): facts.extend(stream.certified) for fact in facts: name = get_prefix(fact) if name not in domain.predicate_dict: undeclared_predicates.add(name) elif len(get_args(fact)) != domain.predicate_dict[name].get_arity(): # predicate used with wrong arity: {} print('Warning! predicate used with wrong arity in stream [{}]: {}'.format(stream.name, fact)) for constant in stream.constants: if constant not in obj_from_constant: raise ValueError('Undefined constant in stream [{}]: {}'.format(stream.name, constant)) if undeclared_predicates: print('Warning! Undeclared predicates: {}'.format( sorted(undeclared_predicates))) # Undeclared predicate: {}
def extract_static_facts(plan, certificate, initial_confs): # TODO: use certificate instead # TODO: only keep objects used on the plan #static_facts = [] static_facts = [ f for f in certificate.all_facts if get_prefix(get_function(f)) in ['distance', 'trajtrajcollision'] ] for name, actions in partition_plan(plan).items(): last_element = None last_conf = initial_confs[name] for i, action in enumerate(actions): if action.name == 'print': r, n1, e, n2, q1, q2, t = action.args static_facts.extend([ ('PrintAction', ) + action.args, ('Assigned', r, e), ('Conf', r, q1), ('Conf', r, q2), ('Traj', r, t), ('CTraj', r, t), # (Start ?r ?n1 ?e ?q1) (End ?r ?e ?n2 ?q2) ('Transition', r, q2, last_conf), ]) if last_element is not None: static_facts.append(('Order', e, last_element)) last_element = e last_conf = q1 # TODO: save collision information else: raise NotImplementedError(action.name) static_facts.extend([ ('Transition', name, initial_confs[name], last_conf), ]) return static_facts
def next_results(self, accelerate=1, 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 as err: # print('Function [{}] expects {} inputs'.format(self.external.name, len(input_values))) # raise err 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 (self.value is not False) and verbose: print('0) {}{}={}'.format(get_prefix(self.external.head), str_from_object(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, []) self.update_statistics(start_time, results) new_facts = [] return results, new_facts
def reuse_facts(problem, certificate, skeleton): # TODO: repackage streams # TODO: recover the full axiom + action plan # TODO: recover the plan preimage annotated with use time # Some supporting args are quantified out and thus lack some facts new_facts = [] if skeleton is None: return new_facts reuse_objs = set() for action, args in skeleton: for arg in args: if (arg != WILD) and not is_parameter(arg): reuse_objs.add(hash_or_id(arg)) # The reuse relpose omission is due to the fact that the initial pose was selected # (which is populated in the initial state) order_predicate = ORDER_PREDICATE.format('') domain = parse_domain(problem.domain_pddl) fluents = get_fluents(domain) for fact in certificate.preimage_facts: predicate = get_prefix(fact) if (predicate in {order_predicate, EQ}) or (predicate in fluents): # Could technically evaluate functions as well continue if all( isinstance(arg, str) or (hash_or_id(arg) in reuse_objs) for arg in get_args(fact)): new_facts.append(fact) return new_facts
def remap_certified(literal, stream): certified = find_unique(lambda f: get_prefix(f) == literal.predicate, stream.certified) mapping = get_mapping(get_args(certified), literal.args) if not all(arg in mapping for arg in stream.inputs): # Certified must contain all inputs return None return mapping
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['fontcolor'] = 'black' #graph.node_attr['fontsize'] = 12 graph.node_attr['colorscheme'] = 'SVG' graph.edge_attr['colorscheme'] = 'SVG' #graph.graph_attr['rotate'] = 90 #graph.node_attr['fixedsize'] = True graph.node_attr['width'] = 0 graph.node_attr['height'] = 0.02 # Minimum height is 0.02 graph.node_attr['margin'] = 0 graph.graph_attr['rankdir'] = 'RL' graph.graph_attr['nodesep'] = 0.05 graph.graph_attr['ranksep'] = 0.25 #graph.graph_attr['pad'] = 0 # splines="false"; graph.graph_attr['outputMode'] = 'nodesfirst' graph.graph_attr['dpi'] = 300 functions = set() negated = set() heads = set() for fact in constraints: prefix = get_prefix(fact) if prefix in (EQ, MINIMIZE): functions.add(fact[1]) elif prefix == NOT: negated.add(fact[1]) else: heads.add(fact) heads.update(functions) heads.update(negated) 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_fact(head) if head in functions: color = COST_COLOR elif head in negated: color = NEGATED_COLOR else: color = 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') # neato | dot | twopi | circo | fdp | nop return graph
def recover_stream_plan(evaluations, current_plan, opt_evaluations, goal_expression, domain, node_from_atom, action_plan, axiom_plans, negative): # 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 = get_negative_predicates(negative) real_states, combined_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(combined_plan, []) stream_preimage = set(full_preimage) - real_states[0] negative_preimage = set( filter(lambda a: a.predicate in negative_from_name, stream_preimage)) function_plan.update( convert_negative(negative_preimage, negative_from_name, full_preimage, real_states)) positive_preimage = stream_preimage - negative_preimage step_from_fact = { fact_from_fd(l): full_preimage[l] for l in positive_preimage if not l.negated } target_facts = { fact for fact in step_from_fact.keys() if get_prefix(fact) != EQ } #stream_plan = reschedule_stream_plan(evaluations, target_facts, domain, stream_results) # visualize_constraints(map(fact_from_fd, target_facts)) stream_plan = [] for result in current_plan: if isinstance(result.external, Function) or (result.external in negative): function_plan.add( result) # Prevents these results from being pruned else: stream_plan.append(result) curr_evaluations = evaluations_from_stream_plan(evaluations, stream_plan, max_effort=None) extraction_facts = target_facts - set( map(fact_from_evaluation, curr_evaluations)) extract_stream_plan(node_from_atom, extraction_facts, stream_plan) stream_plan = postprocess_stream_plan(evaluations, domain, stream_plan, target_facts) stream_plan = convert_fluent_streams(stream_plan, real_states, action_plan, step_from_fact, node_from_atom) return stream_plan + list(function_plan)
def enforce_simultaneous(domain, externals): axiom_predicates = set() for axiom in domain.axioms: axiom_predicates.update(get_predicates(axiom.condition)) for external in externals: if (type(external) in [VariableStream, ConstraintStream]) and not external.info.simultaneous: predicates = {get_prefix(fact) for fact in external.certified} if predicates & axiom_predicates: external.info.simultaneous = True
def make_axiom(parameters, preconditions, derived): predicate = get_prefix(derived) external_parameters = list(get_args(derived)) internal_parameters = [p for p in parameters if p not in external_parameters] parameters = external_parameters + internal_parameters return pddl.Axiom(name=predicate, parameters=make_parameters(parameters), num_external_parameters=len(external_parameters), condition=make_preconditions(preconditions))
def partition_facts(domain, facts): fluents = get_fluents(domain) static_facts = [] fluent_facts = [] for fact in facts: if get_prefix(get_function(fact)).lower() in fluents: fluent_facts.append(fact) else: static_facts.append(fact) return static_facts, fluent_facts
def get_predicate_map(state_streams): predicate_map = {} for state_stream in state_streams: for fact in state_stream.certified: predicate = get_prefix(fact) if predicate in predicate_map: # TODO: could make a disjunctive condition instead raise NotImplementedError('Only one fluent stream can certify a predicate: {}'.format(predicate)) predicate_map[predicate] = state_stream return predicate_map
def get_predicate_map(state_streams): predicate_map = {} for state_stream in state_streams: for fact in state_stream.certified: predicate = get_prefix(fact) if predicate in predicate_map: # TODO: could make a conjunction condition instead raise NotImplementedError() predicate_map[predicate] = state_stream return predicate_map
def get_formula_operators(formula): if formula is None: return set() prefix = get_prefix(formula) if prefix not in OPERATORS: return set() operators = {prefix} for subformula in formula[1:]: operators.update(get_formula_operators(subformula)) return operators
def make_cost(cost): if cost is None: return cost fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]) try: expression = pddl.NumericConstant(cost) except TypeError: expression = pddl.PrimitiveNumericExpression( symbol=get_prefix(cost), args=list(map(pddl_from_object, get_args(cost)))) return pddl.Increase(fluent=fluent, expression=expression)
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) 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) preconditions = [stream_atom] + list(stream.domain) effort = 1 # TODO: use stream info #effort = 1 if unit_cost else result.instance.get_effort() #if effort == INF: # continue domain.actions.append( pddl.Action(name='call-{}'.format(stream.name), parameters=parameters, num_external_parameters=len(parameters), precondition=make_preconditions(preconditions), effects=make_effects(stream.certified), cost=make_cost(effort))) stream.certified = tuple( set(stream.certified) | set(map(rename_future, stream.certified)))
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) fn = get_procedure_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