def get_cluster_values(stream_plan): param_from_obj = {} macro_from_micro = [] inputs, domain, outputs, certified, functions = [], set(), [], set(), set() input_objects, output_objects = [], [] fluent_facts = [] for result in stream_plan: local_mapping = {} # global_from_local stream = result.instance.external add_result_inputs(result, param_from_obj, local_mapping, inputs, input_objects) domain.update(set(substitute_expression(stream.domain, local_mapping)) - certified) if isinstance(result, PredicateResult): # functions.append(Equal(stream.head, result.value)) # TODO: do I need the new mapping here? mapping = {inp: param_from_obj[inp] for inp in result.instance.input_objects} functions.update(substitute_expression(result.get_certified(), mapping)) elif isinstance(result, FunctionResult): functions.add(substitute_expression(Minimize(stream.head), local_mapping)) else: fluent_facts.extend(result.instance.fluent_facts) add_result_outputs(result, param_from_obj, local_mapping, outputs, output_objects) certified.update(substitute_expression(stream.certified, local_mapping)) macro_from_micro.append(local_mapping) #assert not fluent_facts return inputs, domain, outputs, certified, functions, \ macro_from_micro, input_objects, output_objects, fluent_facts
def get_constraints(self): output_mapping = get_mapping(self.external.outputs, self.external.output_objects) output_mapping.update(self.get_mapping()) #constraints = substitute_expression(self.external.certified, output_mapping) constraints = [] for i, result in enumerate(self.external.stream_plan): macro_fact = substitute_expression(result.external.stream_fact, self.external.macro_from_micro[i]) constraints.append(substitute_expression(macro_fact, output_mapping)) # TODO: I think I should be able to just disable the fluent fact from being used in that context return constraints
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 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 optimistic_stream_grounding(stream_instance, bindings, evaluations, opt_evaluations, bind=True, immediate=False): # TODO: combination for domain predicates evaluation_set = set(evaluations) opt_instances = [] if not bind: bindings = {} input_objects = [ bindings.get(i, [i]) for i in stream_instance.input_objects ] for combo in product(*input_objects): mapping = dict(zip(stream_instance.input_objects, combo)) domain = set( map(evaluation_from_fact, substitute_expression( stream_instance.get_domain(), mapping))) # TODO: could just instantiate first if domain <= opt_evaluations: instance = stream_instance.external.get_instance(combo) if (instance.opt_index != 0) and (not immediate or (domain <= evaluation_set)): instance.opt_index -= 1 opt_instances.append(instance) return opt_instances
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 get_stream_actions(results, unique_binding=False, effort_scale=1, **kwargs): result_from_name = {} stream_actions = [] for i, result in enumerate(results): #if not isinstance(stream_result, StreamResult): if type(result) == FunctionResult: continue effort = compute_result_effort(result, **kwargs) if effort == INF: continue # TODO: state constraints # TODO: selectively negate axioms result_name = '{}-{}'.format(result.external.name, i) #result_name = '{}_{}_{}'.format(result.external.name, # No spaces & parens # ','.join(map(pddl_from_object, result.instance.input_objects)), # ','.join(map(pddl_from_object, result.output_objects))) assert result_name not in result_from_name result_from_name[result_name] = result preconditions = list(result.instance.get_domain()) effects = list(result.get_certified()) if unique_binding: enforce_single_binding(result, preconditions, effects) if is_optimizer_result( result): # These effects don't seem to be pruned effects.append( substitute_expression(result.external.stream_fact, result.get_mapping())) stream_actions.append( make_action(result_name, [], preconditions, effects, effort_scale * effort)) return stream_actions, result_from_name
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 __init__(self, instance, output_objects, opt_index=None): super(StreamResult, self).__init__(instance, opt_index) self.output_objects = tuple(output_objects) self.mapping = dict( zip(self.instance.external.outputs, self.output_objects)) self.mapping.update(instance.mapping) self.certified = substitute_expression( self.instance.external.certified, self.get_mapping())
def __init__(self, external, input_objects): self.external = external self.input_objects = tuple(input_objects) self.enumerated = False self.disabled = False self.opt_index = 0 self.results_history = [] self.mapping = dict(zip(self.external.inputs, self.input_objects)) for constant in self.external.constants: self.mapping[constant] = Object.from_name(constant) self.domain = substitute_expression(self.external.domain, self.get_mapping())
def get_unsatisfiable(self): constraints = substitute_expression(self.external.certified, self.external.mapping) index_from_constraint = {c: i for i, c in enumerate(constraints)} # TODO: compute connected components result_from_index = defaultdict(set) for result in self.external.stream_plan: for fact in result.get_certified(): if fact in index_from_constraint: result_from_index[index_from_constraint[fact]].add(result) # TODO: add implied results #orders = get_partial_orders(self.external.stream_plan) return [{result for index in cluster for result in result_from_index[index]} for cluster in prune_dominated(self.infeasible)]
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 optimistic_stream_instantiation(instance, bindings, evaluations, opt_evaluations, only_immediate=False): # TODO: combination for domain predicates new_instances = [] for input_combo in product(*[bindings.get(i, [i]) for i in instance.input_objects]): mapping = get_mapping(instance.input_objects, input_combo) domain_evaluations = set(map(evaluation_from_fact, substitute_expression( instance.get_domain(), mapping))) # TODO: could just instantiate first if domain_evaluations <= opt_evaluations: new_instance = instance.external.get_instance(input_combo) if (new_instance.opt_index != 0) and (not only_immediate or (domain_evaluations <= evaluations)): new_instance.opt_index -= 1 new_instances.append(new_instance) return new_instances
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 optimistic_stream_instantiation(instance, bindings, opt_evaluations, only_immediate=False): # TODO: combination for domain predicates new_instances = [] input_candidates = [bindings.get(i, [i]) for i in instance.input_objects] if only_immediate and not all(len(candidates) == 1 for candidates in input_candidates): return new_instances for input_combo in product(*input_candidates): mapping = get_mapping(instance.input_objects, input_combo) domain_evaluations = set(map(evaluation_from_fact, substitute_expression( instance.get_domain(), mapping))) # TODO: could just instantiate first if domain_evaluations <= opt_evaluations: new_instance = instance.external.get_instance(input_combo) # TODO: method for eagerly evaluating some of these? if not new_instance.is_refined(): new_instance.refine() new_instances.append(new_instance) return new_instances
def get_stream_actions(results, unique_binding=False, unit_efforts=True, effort_scale=1): #from pddl_parser.parsing_functions import parse_action import pddl stream_result_from_name = {} stream_actions = [] for i, result in enumerate(results): #if not isinstance(stream_result, StreamResult): if type(result) == FunctionResult: continue effort = get_instance_effort(result.instance, unit_efforts) if effort == INF: continue # TODO: state constraints # TODO: selectively negate axioms result_name = '{}-{}'.format(result.external.name, i) #result_name = '{}_{}_{}'.format(result.external.name, # No spaces & parens # ','.join(map(pddl_from_object, result.instance.input_objects)), # ','.join(map(pddl_from_object, result.output_objects))) assert result_name not in stream_result_from_name stream_result_from_name[result_name] = result preconditions = list(result.instance.get_domain()) effects = list(result.get_certified()) #if ORDER_OUTPUT: # enforce_output_order(result, preconditions, effects) if unique_binding: enforce_single_binding(result, preconditions, effects) if is_optimizer_result( result): # These effects don't seem to be pruned effects.append( substitute_expression(result.external.stream_fact, result.get_mapping())) parameters = [] # Usually all parameters are external stream_actions.append( pddl.Action(name=result_name, parameters=parameters, num_external_parameters=len(parameters), precondition=make_preconditions(preconditions), effects=make_effects(effects), cost=make_cost(effort_scale * effort))) # Can also be None return stream_actions, stream_result_from_name
def _add_disabled_axiom(self, domain): # TODO: be careful about the shared objects as parameters for results in self._get_unsatisfiable(): constraints = {r.stream_fact for r in results } # r.get_domain(), r.get_certified() # TODO: what if free parameter is a placeholder value #free_objects = list({o for f in constraints for o in get_args(f) if isinstance(o, OptimisticObject)}) free_objects = list({ o for f in constraints for o in get_args(f) if o in self.external.output_objects }) parameters = ['?p{}'.format(i) for i in range(len(free_objects))] preconditions = substitute_expression( constraints, get_mapping(free_objects, parameters)) disabled_axiom = make_axiom(parameters, preconditions, (UNSATISFIABLE, )) #self._disabled_axioms.append(disabled_axiom) domain.axioms.append(disabled_axiom)
def apply_rules_to_streams(rules, streams): # TODO: can actually this with multiple condition if stream certified contains all # TODO: do also when no domain conditions processed_rules = deque(rules) while processed_rules: rule = processed_rules.popleft() if len(rule.domain) != 1: continue [rule_fact] = rule.domain rule.info.p_success = 0 # Need not be applied for stream in streams: if not isinstance(stream, Stream): continue for stream_fact in stream.certified: if get_prefix(rule_fact) == get_prefix(stream_fact): mapping = get_mapping(get_args(rule_fact), get_args(stream_fact)) new_facts = set(substitute_expression(rule.certified, mapping)) - set(stream.certified) stream.certified = stream.certified + tuple(new_facts) if new_facts and (stream in rules): processed_rules.append(stream)
def _get_unsatisfiable(self): if not self.infeasible: yield set(self.external.stream_plan) constraints = substitute_expression(self.external.certified, self.external.mapping) index_from_constraint = {c: i for i, c in enumerate(constraints)} # TODO: just prune any subsets #sets = [] #for s1 in self.infeasible: # for s2 in self.infeasible: # else: # sets.append(s1) # TODO: compute connected components result_from_index = defaultdict(set) for result in self.external.stream_plan: for fact in result.get_certified(): if fact in index_from_constraint: result_from_index[index_from_constraint[fact]].add(result) for cluster in self.infeasible: yield { result for index in cluster for result in result_from_index[index] }
def ground_stream_instances(stream_instance, bindings, evaluations, opt_evaluations, plan_index): # TODO: combination for domain predicates evaluation_set = set(evaluations) combined_evaluations = evaluation_set | opt_evaluations real_instances = [] opt_instances = [] input_objects = [ bindings.get(i, [i]) for i in stream_instance.input_objects ] for combo in product(*input_objects): mapping = dict(zip(stream_instance.input_objects, combo)) domain = set( map(evaluation_from_fact, substitute_expression(stream_instance.get_domain(), mapping))) if domain <= combined_evaluations: instance = stream_instance.external.get_instance(combo) immediate = False if immediate: if domain <= evaluation_set: if instance.opt_index == 0: real_instances.append(instance) else: instance.opt_index -= 1 opt_instances.append(instance) else: opt_instances.append(instance) else: #if (instance.opt_index == 0) and (domain <= evaluation_set): if (plan_index == 0) and (domain <= evaluation_set): real_instances.append(instance) else: if instance.opt_index != 0: instance.opt_index -= 1 opt_instances.append(instance) return real_instances, opt_instances
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)
def certified(self): if self._certified is None: self._certified = substitute_expression(self.external.certified, self.get_mapping()) return self._certified
def stream_fact(self): if self._stream_fact is None: self._stream_fact = substitute_expression( self.external.stream_fact, self.mapping) return self._stream_fact
def get_head(self): return substitute_expression(self.external.head, self.get_mapping())
def get_objectives(self): return substitute_expression(self.external.objectives, self.get_mapping())
def gen_fn(*input_values): mapping = get_mapping(inputs, input_values) targets = substitute_expression(certified, mapping) return procedure(outputs, targets)
def gen_fn(*input_values): # TODO: take in guess values for inputs? assert (len(inputs) == len(input_values)) mapping = dict(zip(inputs, input_values)) targets = substitute_expression(certified | functions, mapping) return synthesizer.gen_fn(outputs, targets) # TODO: could also return a map
def get_functions(self): return substitute_expression(self.instance.external.functions, self.get_mapping())
def head(self): if self._head is None: self._head = substitute_expression(self.external.head, self.get_mapping()) return self._head
def get_synth_stream(self, stream_plan): key = frozenset(stream_plan) if key in self.macro_results: return self.macro_results[key] param_from_obj = {} macro_from_micro = [] inputs, domain, outputs, certified = [], set(), [], set() input_objects, output_objects = [], [] functions = set() streams = [] for result in stream_plan: local_mapping = {} stream = result.instance.external for inp, input_object in zip(stream.inputs, result.instance.input_objects): # TODO: only do optimistic parameters? # if isinstance() if input_object not in param_from_obj: param_from_obj[input_object] = '?i{}'.format(len(inputs)) inputs.append(param_from_obj[input_object]) input_objects.append(input_object) local_mapping[inp] = param_from_obj[input_object] domain.update( set(substitute_expression(stream.domain, local_mapping)) - certified) if isinstance(result, PredicateResult): # functions.append(Equal(stream.head, result.value)) mapping = { inp: param_from_obj[inp] for inp in result.instance.input_objects } functions.update( substitute_expression(result.get_certified(), mapping)) elif isinstance(result, FunctionResult): functions.add( substitute_expression(Minimize(stream.head), local_mapping)) else: for out, output_object in zip(stream.outputs, result.output_objects): if output_object not in param_from_obj: param_from_obj[output_object] = '?o{}'.format( len(outputs)) outputs.append(param_from_obj[output_object]) output_objects.append(output_object) local_mapping[out] = param_from_obj[output_object] certified.update( substitute_expression(stream.certified, local_mapping)) streams.append(stream) macro_from_micro.append(local_mapping) gen_fn = self.get_gen_fn(inputs, outputs, certified | functions) mega_stream = SynthStream(self, gen_fn, inputs=tuple(inputs), domain=domain, outputs=tuple(outputs), certified=certified, streams=streams, macro_from_micro=macro_from_micro) mega_instance = mega_stream.get_instance(input_objects) self.macro_results[key] = SynthStreamResult(mega_instance, output_objects) return self.macro_results[key]