def get_stream_action(result, name, unit_cost, effect_scale=1): #from pddl_parser.parsing_functions import parse_action import pddl parameters = [] preconditions = [ fd_from_fact(fact) for fact in result.instance.get_domain() ] precondition = pddl.Conjunction(preconditions) effects = [ pddl.Effect(parameters=[], condition=pddl.Truth(), literal=fd_from_fact(fact)) for fact in result.get_certified() ] effort = 1 if unit_cost else result.instance.get_effort() if effort == INF: return None fluent = pddl.PrimitiveNumericExpression(symbol=TOTAL_COST, args=[]) expression = pddl.NumericConstant(int_ceil(effect_scale * effort)) # Integer cost = pddl.Increase(fluent=fluent, expression=expression) # Can also be None return pddl.Action(name=name, parameters=parameters, num_external_parameters=len(parameters), precondition=precondition, effects=effects, cost=cost)
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 analyze_goal(problem, use_actions=False, use_axioms=True, use_streams=True, blocked_predicates=[], **kwargs): # TODO: instantiate all goal partial states # TODO: remove actions/axioms that never could achieve a subgoal domain_pddl, constant_map, stream_pddl, stream_map, init, goal = problem evaluations = evaluations_from_init(init) init = set(fd_from_evaluations(evaluations)) # from pddlstream.algorithms.scheduling.recover_axioms import recover_axioms_plans results, instantiated = examine_instantiated(problem, **kwargs) # TODO: only do if the goals are derived if instantiated is None: return None #optimistic_init = set(instantiated.task.init) # This is like backchaining in a relaxed space condition_from_effect = defaultdict(set) if use_actions: # TODO: selectively ignore some conditions (e.g. HandEmpty) for action in instantiated.actions: for conditional, effect in action.add_effects: for condition in (action.precondition + conditional): if condition.predicate not in blocked_predicates: condition_from_effect[effect].add(condition) for conditional, effect in action.del_effects: for condition in (action.precondition + conditional): if condition.predicate not in blocked_predicates: condition_from_effect[effect.negate()].add(condition) if use_axioms: # TODO: axiom_rules.handle_axioms(...) # print('Axioms:', instantiated.axioms) for axiom in instantiated.axioms: #axiom = reinstantiate_axiom(axiom) #axiom.dump() for condition in axiom.condition: condition_from_effect[axiom.effect].add(condition) if use_streams: for result in results: for effect in result.certified: if get_prefix(effect) == EQ: continue for condition in result.domain: condition_from_effect[fd_from_fact(effect)].add(fd_from_fact(condition)) print('Goals:', list(map(fact_from_fd, instantiated.goal_list))) #all_subgoals = iterate_subgoals(instantiated.goal_list, axiom_from_effect) all_subgoals = recurse_subgoals(instantiated.goal_list, condition_from_effect) filtered_subgoals = [subgoal for subgoal in all_subgoals if subgoal in init] # TODO: return the goals as well? external_subgoals = [value_from_obj_expression(fact_from_fd(subgoal)) for subgoal in sorted(filtered_subgoals, key=lambda g: g.predicate) if not subgoal.predicate.startswith(INTERNAL_AXIOM)] print('Initial:', external_subgoals) return external_subgoals # TODO: decompose into simplified components
def add_optimizer_effects(instantiated, node_from_atom): # TODO: instantiate axioms with negative on effects for blocking # TODO: fluent streams using conditional effects. Special fluent predicate for inputs to constraint # TODO: bug! The FD instantiator prunes the result.external.stream_fact for instance in instantiated.actions: # TODO: need to handle case where a negative preconditions is used in an optimizer for condition, effect in (instance.add_effects + instance.del_effects): for literal in condition: fact = fact_from_fd(literal) if (fact in node_from_atom) and (node_from_atom[fact].result is not None): raise NotImplementedError(literal) facts = get_instance_facts(instance, node_from_atom) stream_plan = [] extract_stream_plan(node_from_atom, facts, stream_plan) # TODO: can detect if some of these are simultaneous and add them as preconditions for result in stream_plan: #if isinstance(result.external, ComponentStream): if True: # TODO: integrate sampler and optimizer treatments # TODO: need to make multiple versions if several ways of achieving the action atom = fd_from_fact(result.stream_fact) instantiated.atoms.add(atom) effect = (tuple(), atom) instance.add_effects.append(effect) instance.effect_mappings.append(effect + (None, None))
def instantiate_optimizer_axioms(instantiated, domain, results): # Needed for instantiating axioms before adding stream action effects # Otherwise, FastDownward will prune these unreachable axioms # TODO: compute this first and then apply the eager actions stream_init = { fd_from_fact(result.stream_fact) for result in results if isinstance(result, StreamResult) } evaluations = list( map(evaluation_from_fd, stream_init | instantiated.atoms)) temp_domain = make_domain( predicates=[make_predicate(UNSATISFIABLE, [])], axioms=[ax for ax in domain.axioms if ax.name == UNSATISFIABLE]) temp_problem = get_problem(evaluations, Not((UNSATISFIABLE, )), temp_domain) # TODO: UNSATISFIABLE might be in atoms making the goal always infeasible with Verbose(): # TODO: the FastDownward instantiation prunes static preconditions use_fd = False if using_optimizers(results) else FD_INSTANTIATE new_instantiated = instantiate_task(task_from_domain_problem( temp_domain, temp_problem), use_fd=use_fd, check_infeasible=False, prune_static=False) assert new_instantiated is not None instantiated.axioms.extend(new_instantiated.axioms) instantiated.atoms.update(new_instantiated.atoms)
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 add_stream_costs(node_from_atom, instantiated, unit_efforts, effort_weight): # TODO: instantiate axioms with negative on effects for blocking # TODO: fluent streams using conditional effects. Special fluent predicate for inputs to constraint # This strategy will only work for relaxed to ensure that the current state is applied for instance in instantiated.actions: # TODO: prune stream actions here? # Ignores conditional effect costs facts = [] for precondition in get_literals(instance.action.precondition): if precondition.negated: continue args = [instance.var_mapping.get(arg, arg) for arg in precondition.args] literal = precondition.__class__(precondition.predicate, args) fact = fact_from_fd(literal) if fact in node_from_atom: facts.append(fact) #effort = COMBINE_OP([0] + [node_from_atom[fact].effort for fact in facts]) stream_plan = [] extract_stream_plan(node_from_atom, facts, stream_plan) if unit_efforts: effort = len(stream_plan) else: effort = scale_cost(sum([0] + [r.instance.get_effort() for r in stream_plan])) if effort_weight is not None: instance.cost += effort_weight*effort # TODO: bug! The FD instantiator prunes the result.external.stream_fact for result in stream_plan: # TODO: need to make multiple versions if several ways of achieving the action if is_optimizer_result(result): fact = substitute_expression(result.external.stream_fact, result.get_mapping()) atom = fd_from_fact(fact) instantiated.atoms.add(atom) effect = (tuple(), atom) instance.add_effects.append(effect)
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 get_prefix(fact) != EQ] cost = None # TODO: effort? instance = pddl.PropositionalAction(name, precondition, effects, cost) stream_instances.append(instance) return stream_instances
def compile_fluents_as_attachments(domain, externals): import pddl state_streams = set( filter(lambda e: isinstance(e, Stream) and e.is_fluent(), externals)) # is_special if not state_streams: return externals predicate_map = get_predicate_map(state_streams) if predicate_map and (get_pyplanners_path() is None): # TODO: fluent streams with outputs # Could convert the free parameter to a constant raise NotImplementedError( 'Algorithm does not support fluent streams: {}'.format( [stream.name for stream in state_streams])) domain.constants.append(make_object(PLACEHOLDER.pddl)) for action in domain.actions: for effect in action.effects: # TODO: conditional effects if any(literal.predicate in predicate_map for literal in get_literals(effect.condition)): raise ValueError( 'Attachments cannot be in action effects: {}'.format( effect)) action.attachments = {} preconditions = set() for literal in get_conjunctive_parts(action.precondition): #if not isinstance(literal, pddl.Literal): # raise NotImplementedError('Only literals are supported: {}'.format(literal)) if not get_predicates(literal) & set(predicate_map): preconditions.add(literal) continue if not isinstance(literal, pddl.Literal): raise NotImplementedError(literal) # Drops the original precondition stream = predicate_map[literal.predicate] mapping = remap_certified(literal, stream) assert mapping is not None action.attachments[literal] = stream preconditions.update( pddl.Atom(EQ, (mapping[out], PLACEHOLDER.pddl)) for out in stream.outputs) preconditions.update( fd_from_fact(substitute_fact(fact, mapping)) for fact in stream.domain) action.precondition = pddl.Conjunction(preconditions).simplified() #fn = lambda l: pddl.Truth() if l.predicate in predicate_map else l #action.precondition = replace_literals(fn, action.precondition).simplified() #action.dump() return [ external for external in externals if external not in state_streams ]
def universal_to_conditional(action): import pddl new_parts = [] unsatisfiable = fd_from_fact((UNSATISFIABLE,)) for quant in get_conjunctive_parts(action.precondition): if isinstance(quant, pddl.UniversalCondition): condition = quant.parts[0] # TODO: normalize first? if isinstance(condition, pddl.Disjunction) or isinstance(condition, pddl.Literal): action.effects.append(pddl.Effect(quant.parameters, condition.negate(), unsatisfiable)) continue new_parts.append(quant) action.precondition = pddl.Conjunction(new_parts)
def optimizer_conditional_effects(domain, externals): import pddl #from pddlstream.algorithms.scheduling.negative import get_negative_predicates # TODO: extend this to predicates if UNIVERSAL_TO_CONDITIONAL: negative_streams = list(filter(lambda e: e.is_negated(), externals)) else: negative_streams = list( filter( lambda e: isinstance(e, ConstraintStream) and e.is_negated(), externals)) negative_from_predicate = get_predicate_map(negative_streams) if not negative_from_predicate: return for action in domain.actions: universal_to_conditional(action) new_effects = [] for effect in action.effects: if effect.literal.predicate != UNSATISFIABLE: new_effects.append(effect) continue 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) if not stream_facts: new_effects.append(effect) for stream_fact in stream_facts: new_effects.append( pddl.Effect(effect.parameters, pddl.Conjunction(new_parts), stream_fact)) action.effects = new_effects
def add_optimizer_axioms(results, instantiated): # Ends up being a little slower than version in optimizer.py when not blocking shared # TODO: add this to simultaneous import pddl results_from_instance = defaultdict(list) for result in results: results_from_instance[result.instance].append(result) optimizer_results = list(filter(is_optimizer_result, results)) optimizers = {result.external.optimizer for result in optimizer_results} for optimizer in optimizers: optimizer_facts = { substitute_expression(result.external.stream_fact, result.get_mapping()) for result in optimizer_results if result.external.optimizer is optimizer } facts_from_arg = defaultdict(list) for fact in optimizer_facts: for arg in get_args(fact): facts_from_arg[arg].append(fact) for stream in optimizer.streams: if not stream.instance.disabled: continue constraints = stream.instance.get_constraints() output_variables = [] for out in stream.output_objects: assert isinstance(out.param, UniqueOptValue) output_variables.append([ r.output_objects[out.param.output_index] for r in results_from_instance[out.param.instance] ]) for combo in product(*output_variables): mapping = get_mapping(stream.output_objects, combo) name = '({})'.join(UNSATISFIABLE) blocked = set(substitute_expression(constraints, mapping)) additional = { fact for arg in combo for fact in facts_from_arg[arg] } - blocked # TODO: like a partial disable, if something has no outputs, then adding things isn't going to help if stream.instance.enumerated and not stream.instance.successes: # Assumes the optimizer is submodular condition = list(map(fd_from_fact, blocked)) else: condition = list( map(fd_from_fact, blocked | set(map(Not, additional)))) effect = fd_from_fact((UNSATISFIABLE, )) instantiated.axioms.append( pddl.PropositionalAxiom(name, condition, effect)) instantiated.atoms.add(effect)
def add_optimizer_effects(instantiated, instance, stream_plan): # TODO: instantiate axioms with negative on effects for blocking # TODO: fluent streams using conditional effects. Special fluent predicate for inputs to constraint # This strategy will only work for relaxed to ensure that the current state is applied # TODO: bug! The FD instantiator prunes the result.external.stream_fact for result in stream_plan: if not is_optimizer_result(result): continue # TODO: need to make multiple versions if several ways of achieving the action atom = fd_from_fact( substitute_expression(result.external.stream_fact, result.get_mapping())) instantiated.atoms.add(atom) effect = (tuple(), atom) instance.add_effects.append(effect)
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 optimizer_conditional_effects(domain, externals): import pddl #from pddlstream.algorithms.scheduling.negative import get_negative_predicates # TODO: extend this to predicates negative_streams = list( filter(lambda e: isinstance(e, ConstraintStream) and e.is_negated(), externals)) negative_from_predicate = get_predicate_map(negative_streams) if not negative_from_predicate: return for action in domain.actions: universal_to_conditional(action) for effect in action.effects: if isinstance(effect, pddl.Effect) and (effect.literal.predicate == UNSATISFIABLE): condition = effect.condition new_parts = [] stream_fact = None for literal in get_conjuctive_parts(condition): if isinstance(literal, pddl.Literal) and ( literal.predicate in negative_from_predicate): if stream_fact is not None: raise NotImplementedError() stream = negative_from_predicate[literal.predicate] certified = find_unique( lambda f: get_prefix(f) == literal.predicate, stream.certified) mapping = get_mapping(get_args(certified), literal.args) stream_fact = substitute_expression( stream.stream_fact, mapping) else: new_parts.append(literal) if stream_fact is not None: effect.condition = pddl.Conjunction(new_parts) effect.literal = fd_from_fact(stream_fact)