def dump_new_values(self, new_values=[]): if (not new_values and VERBOSE_FAILURES) or \ (new_values and self.info.verbose): print('iter={}, outs={}) {}:{}->{}'.format( self.get_iteration(), len(new_values), self.external.name, str_from_object(self.get_input_values()), str_from_object(new_values)))
def next_results(self, accelerate=1, verbose=False): # TODO: prune repeated values all_new_values = [] all_new_facts = [] all_results = [] start_calls = self.num_calls for attempt in range(accelerate): if all_results or self.enumerated: break start_time = time.time() new_values, new_facts = self._next_outputs() self._check_output_values(new_values) self._check_wild_facts(new_facts) new_results = [self.get_result(map(Object.from_value, output_values), list_index=list_index) for list_index, output_values in enumerate(new_values)] all_new_values.extend(new_values) all_new_facts.extend(new_facts) all_results.extend(new_results) self.update_statistics(start_time, new_results) if verbose and (VERBOSE_FAILURES or all_new_values): print('{}-{}) {}:{}->{}'.format(start_calls, self.num_calls, self.external.name, str_from_object(self.get_input_values()), str_from_object(all_new_values))) if verbose and all_new_facts: # TODO: format all_new_facts print('{}-{}) {}:{}->{}'.format(start_calls, self.num_calls, self.external.name, str_from_object(self.get_input_values()), all_new_facts)) return all_results, list(map(obj_from_value_expression, all_new_facts))
def next_results(self, verbose=False): assert not self.enumerated start_time = time.time() start_calls = self.num_calls new_values, new_facts = self._next_outputs() self._check_output_values(new_values) self._check_wild_facts(new_facts) new_objects = [tuple(map(Object.from_value, output_values)) for output_values in new_values] new_objects = list(filter(lambda o: o not in self.previous_outputs, new_objects)) self.previous_outputs.update(new_objects) # Only counting new outputs as successes new_results = [self.get_result(output_objects, list_index=list_index, optimistic=False) for list_index, output_objects in enumerate(new_objects)] self.update_statistics(start_time, new_results) if verbose: if VERBOSE_FAILURES or new_values: print('{}) {}:{}->{}'.format(start_calls, self.external.name, str_from_object(self.get_input_values()), str_from_object(new_values))) if VERBOSE_WILD and new_facts: # TODO: format all_new_facts print('{}) {}:{}->{}'.format(start_calls, self.external.name, str_from_object(self.get_input_values()), new_facts)) facts = list(map(obj_from_value_expression, new_facts)) if not self.external.outputs and self.successes: # Set of possible outputs is exhausted self.enumerated = True return new_results, facts
def next_results(self, verbose=False): assert not self.enumerated start_time = time.time() start_history = len(self.history) new_values, new_facts = self._next_outputs() self._check_output_values(new_values) self._check_wild_facts(new_facts) if verbose: if (not new_values and VERBOSE_FAILURES) or \ (new_values and self.info.verbose): print('iter={}, outs={}) {}:{}->{}'.format( self.num_calls, len(new_values), self.external.name, str_from_object(self.get_input_values()), str_from_object(new_values))) if VERBOSE_WILD and new_facts: # TODO: format all_new_facts print('iter={}, facts={}) {}:{}->{}'.format( self.num_calls, self.external.name, str_from_object(self.get_input_values()), new_facts, len(new_facts))) objects = [objects_from_values(output_values) for output_values in new_values] new_objects = list(filter(lambda o: o not in self.previous_outputs, objects)) self.previous_outputs.update(new_objects) # Only counting new outputs as successes new_results = [self.get_result(output_objects, list_index=list_index, optimistic=False) for list_index, output_objects in enumerate(new_objects)] if start_history < len(self.history): self.update_statistics(start_time, new_results) new_facts = list(map(obj_from_value_expression, new_facts)) self.successful |= any(r.is_successful() for r in new_results) self.num_calls += 1 # Must be after get_result #if self.external.is_test() and self.successful: # # Set of possible test stream outputs is exhausted (excluding wild) # self.enumerated = True return new_results, new_facts
def dump_instantiated(instantiated): print('Instantiated frequencies:\n' 'Atoms: {}\n' 'Actions: {}\n' 'Axioms: {}'.format( str_from_object( Counter(atom.predicate for atom in instantiated.atoms)), str_from_object( Counter(action.action.name for action in instantiated.actions)), str_from_object( Counter(axiom.axiom.name for axiom in instantiated.axioms))))
def dump_assignment(solution): bindings, cost, evaluations = solution print() print('Solved: {}'.format(bindings is not None)) print('Cost: {}'.format(cost)) print('Total facts: {}'.format(len(evaluations))) print('Fact counts: {}'.format(str_from_object(Counter(map(get_prefix, evaluations))))) if bindings is None: return print('Assignments:') for param in sorted(bindings): print('{} = {}'.format(param, str_from_object(bindings[param])))
def main(): parser = argparse.ArgumentParser() parser.add_argument('-p', '--problem', default='mirror', help='The name of the problem to solve') parser.add_argument('-a', '--algorithm', default='incremental', help='Specifies the algorithm') parser.add_argument('-c', '--cfree', action='store_true', help='Disables collisions') parser.add_argument('-d', '--deterministic', action='store_true', help='Uses a deterministic sampler') parser.add_argument('-g', '--gurobi', action='store_true', help='Uses gurobi') parser.add_argument('-n', '--number', default=1, type=int, help='The number of blocks') parser.add_argument('-o', '--optimal', action='store_true', help='Runs in an anytime mode') parser.add_argument('-s', '--skeleton', action='store_true', help='Enforces skeleton plan constraints') parser.add_argument('-t', '--max_time', default=30, type=int, help='The max time') parser.add_argument('-u', '--unit', action='store_true', help='Uses unit costs') parser.add_argument('-v', '--visualize', action='store_true', help='Visualizes graphs') args = parser.parse_args() print('Arguments:', args) np.set_printoptions(precision=2) if args.deterministic: random.seed(seed=0) np.random.seed(seed=0) print('Random seed:', get_random_seed()) problem_from_name = {fn.__name__: fn for fn in PROBLEMS} if args.problem not in problem_from_name: raise ValueError(args.problem) print('Problem:', args.problem) problem_fn = problem_from_name[args.problem] tamp_problem = problem_fn(args.number) print(tamp_problem) pddlstream_problem = pddlstream_from_tamp(tamp_problem, collisions=not args.cfree, use_stream=not args.gurobi, use_optimizer=args.gurobi) print('Constants:', str_from_object(pddlstream_problem.constant_map)) print('Initial:', sorted_str_from_list(pddlstream_problem.init)) print('Goal:', str_from_object(pddlstream_problem.goal)) success_cost = 0 if args.optimal else INF planner = 'max-astar' #planner = 'ff-wastar1' with Profiler(field='cumtime', num=20): if args.algorithm == 'incremental': solution = solve_incremental(pddlstream_problem, complexity_step=1, planner=planner, unit_costs=args.unit, success_cost=success_cost, max_time=args.max_time, verbose=False) else: raise ValueError(args.algorithm) print_solution(solution) plan, cost, evaluations = solution if plan is not None: display_plan(tamp_problem, plan)
def main(max_time=20): """ Creates and solves the 2D motion planning problem. """ parser = create_parser() args = parser.parse_args() print('Arguments:', args) obstacles = [create_box((.5, .5), (.2, .2))] regions = { 'env': create_box((.5, .5), (1, 1)), 'green': create_box((.8, .8), (.4, .4)), } goal = 'green' if goal not in regions: goal = ARRAY([1, 1]) max_distance = 0.25 # 0.2 | 0.25 | 0.5 | 1.0 problem, samples, roadmap = create_problem(goal, obstacles, regions, max_distance=max_distance) print('Initial:', str_from_object(problem.init)) print('Goal:', str_from_object(problem.goal)) constraints = PlanConstraints(max_cost=1.25) # max_cost=INF) with Profiler(field='tottime', num=10): solution = solve_incremental(problem, constraints=constraints, unit_costs=args.unit, success_cost=0, max_time=max_time, verbose=False) print_solution(solution) plan, cost, evaluations = solution #viewer = draw_environment(obstacles, regions) #for sample in samples: # viewer.draw_point(sample) #user_input('Continue?') # TODO: use the same viewer here draw_roadmap(roadmap, obstacles, regions) # TODO: do this in realtime user_input('Continue?') if plan is None: return segments = [args for name, args in plan] draw_solution(segments, obstacles, regions) user_input('Finish?')
def main(max_time=20): """ Creates and solves the 2D motion planning problem. """ obstacles = [create_box((.5, .5), (.2, .2))] regions = { 'env': create_box((.5, .5), (1, 1)), 'green': create_box((.8, .8), (.4, .4)), } goal = 'green' if goal not in regions: goal = array([1, 1]) max_distance = 0.25 # 0.2 | 0.25 | 0.5 | 1.0 problem, samples, roadmap = create_problem(goal, obstacles, regions, max_distance=max_distance) print('Initial:', str_from_object(problem.init)) print('Goal:', str_from_object(problem.goal)) pr = cProfile.Profile() pr.enable() solution = solve_incremental(problem, unit_costs=False, max_cost=0, max_time=max_time, verbose=False) pr.disable() pstats.Stats(pr).sort_stats('tottime').print_stats(10) print_solution(solution) plan, cost, evaluations = solution #viewer = draw_environment(obstacles, regions) #for sample in samples: # viewer.draw_point(sample) #user_input('Continue?') # TODO: use the same viewer here draw_roadmap(roadmap, obstacles, regions) # TODO: do this in realtime user_input('Continue?') if plan is None: return segments = [args for name, args in plan] draw_solution(segments, obstacles, regions) user_input('Finish?')
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 process_stream_queue(instantiator, store, complexity_limit=INF, verbose=False): instances = [] results = [] num_successes = 0 while not store.is_terminated() and instantiator and ( instantiator.min_complexity() <= complexity_limit): instance = instantiator.pop_stream() if instance.enumerated: continue instances.append(instance) new_results = process_instance(instantiator, store, instance, verbose=verbose) results.extend(new_results) num_successes += bool(new_results) # TODO: max_results? if verbose: print('Eager Calls: {} | Successes: {} | Results: {} | Counts: {}'. format( len(instances), num_successes, len(results), str_from_object( Counter(instance.external.name for instance in instances)))) return len(instances)
def __repr__(self): return '{}(holding={}, placed={})'.format( self.__class__.__name__, self.holding, str_from_object({ name: self.pose_dists[name].surface_dist for name in self.placed }))
def dump_new_facts(self, new_facts=[]): if VERBOSE_WILD and new_facts: # TODO: format all_new_facts print('iter={}, facts={}) {}:{}->{}'.format( self.get_iteration(), self.external.name, str_from_object(self.get_input_values()), new_facts, len(new_facts)))
def instantiate_condition(action, is_static, args_from_predicate): parameters = {p.name for p in action.parameters} #if not parameters: # yield {} # return static_conditions = list( filter(is_static, get_literals(get_precondition(action)))) static_parameters = set( filter(is_parameter, flatten(atom.args for atom in static_conditions))) if not (parameters <= static_parameters): raise NotImplementedError( 'Could not instantiate action {} due to parameters: {}'.format( action.name, str_from_object(parameters - static_parameters))) atoms_from_cond = { condition: args_from_predicate[condition.predicate, get_constants(condition)] for condition in static_conditions } conditions, atoms = zip(*atoms_from_cond.items()) relations = [ Relation(conditions[index].args, atoms[index]) for index in compute_order(conditions, atoms) ] solution = solve_satisfaction(relations) for element in solution.body: yield solution.get_mapping(element)
def create_visualizations(evaluations, stream_plan, iteration): # TODO: place it in the temp_dir? # TODO: decompose any joint streams for result in stream_plan: create_synthesizer_visualizations(result, iteration) filename = ITERATION_TEMPLATE.format(iteration) # visualize_stream_plan(stream_plan, path) constraints = set() # TODO: approximates needed facts using produced ones for stream in stream_plan: constraints.update( filter(lambda f: evaluation_from_fact(f) not in evaluations, stream.get_certified())) print('Constraints:', str_from_object(constraints)) visualize_constraints(constraints, os.path.join(CONSTRAINT_NETWORK_DIR, filename)) from pddlstream.retired.synthesizer import decompose_stream_plan decomposed_plan = decompose_stream_plan(stream_plan) if len(decomposed_plan) != len(stream_plan): visualize_stream_plan(decompose_stream_plan(stream_plan), os.path.join(STREAM_PLAN_DIR, filename)) #visualize_stream_plan_bipartite(stream_plan, os.path.join(STREAM_PLAN_DIR, 'fused_' + filename)) visualize_stream_plan(stream_plan, os.path.join(STREAM_PLAN_DIR, 'fused_' + filename))
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 read_raw_masses(stackings): if not stackings: return {} weights = { SCALE_FROM_BUS[bus]: weight for bus, weight in read_scales().items() } print('Raw weights (oz):', str_from_object(weights)) for scale in stackings: if scale not in weights: # TODO: return which scale failed print('Scale {} is not turned on!'.format(scale)) return None #assert all(0 < weight for weight in weights.values()) # TODO: check that objects are on masses = { stackings[scale]: KG_PER_OZ * weight for scale, weight in weights.items() } print('Raw masses (kg):', str_from_object(masses)) return masses
def estimate_masses(masses, bowl_masses, material=None): print('Bowl masses (kg):', bowl_masses) #material_masses = {bowl: max(0.0, mass - bowl_masses[bowl]) # for bowl, mass in masses.items()} material_masses = { bowl: mass - bowl_masses[bowl] for bowl, mass in masses.items() } print('Material masses (kg):', str_from_object(material_masses)) #if material is not None: # estimate_particles(masses, material) return material_masses
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 next_results(self, verbose=False): assert not self.enumerated start_time = time.time() start_calls = self.num_calls start_history = len(self.history) value = self._compute_output() if (value is not False) and verbose: print('{}) {}{}={}'.format( start_calls, get_prefix(self.external.head), str_from_object(self.get_input_values()), value)) new_results = [self._Result(self, value, optimistic=False)] new_facts = [] if start_history < len(self.history): self.update_statistics(start_time, new_results) self.successful |= any(r.is_successful() for r in new_results) return new_results, new_facts
def next_results(self, verbose=False): assert not self.enumerated start_time = time.time() start_history = len(self.history) value = self._compute_output() new_results = [self._Result(self, value, optimistic=False)] new_facts = [] if (value is not False) and verbose: # TODO: str(new_results[-1]) print('iter={}, outs={}) {}{}={:.3f}'.format( self.get_iteration(), len(new_results), get_prefix(self.external.head), str_from_object(self.get_input_values()), value)) if start_history <= len(self.history) - 1: self.update_statistics(start_time, new_results) self.successful |= any(r.is_successful() for r in new_results) return new_results, new_facts
def score_masses(args, task, ros_world, init_total_mass, bowl_masses, final_raw_masses): # TODO: assert that mass is not zero if object known to be above # TODO: unify this with the simulator scoring cup_name, bowl_name = REQUIREMENT_FNS[args.problem](ros_world) name_from_type = { 'cup': cup_name, } if POUR: name_from_type.update({ 'bowl': bowl_name, }) final_masses = estimate_masses(final_raw_masses, bowl_masses, args.material) if POUR: final_total_mass = sum(final_masses.values()) print('Total mass:', final_total_mass) print( 'Spilled (%):', clip(init_total_mass - final_total_mass, min_value=0, max_value=1)) score = {'total_mass': init_total_mass} score.update({ 'mass_in_{}'.format(ty): final_masses[name] for ty, name in name_from_type.items() }) if POUR: final_percentages = { name: mass / init_total_mass for name, mass in final_masses.items() } print('Percentages (%):', str_from_object(final_percentages)) #score.update({'fraction_in_{}'.format(ty): final_percentages[name] # for ty, name in name_from_type.items()}) if task.holding: [spoon_name] = task.holding spoon_capacity = SPOON_CAPACITIES[get_type(spoon_name), args.material] fraction_filled = final_masses[cup_name] / spoon_capacity print('Spoon percentage filled (%):', fraction_filled) score.update({ 'mass_in_spoon': final_masses[cup_name], #'fraction_in_spoon': final_masses[cup_name] / init_total_mass, #'spoon_capacity': spoon_capacity, #'fraction_filled': fraction_filled, }) return score
def decompose_discrete(results, **kwargs): data_from_discrete = {} for result in results: key = frozenset((feature, result[FEATURE][feature]) for feature in DISCRETE_FEATURES if feature in result[FEATURE]) data_from_discrete.setdefault(key, []).append(result) sorted_triplets = [] for key, key_data in data_from_discrete.items(): category_values = get_category_values(key_data, **kwargs) scores = category_values[SCORE]['score'] if SCORE in category_values else [] sorted_triplets.append((dict(key), len(scores), compute_percentiles(scores, **kwargs))) for i, (discrete, num, percentiles) in enumerate( sorted(sorted_triplets, key=lambda t: (t[-1][-1], t[1]), reverse=True)): print('{}) {} | Num: {} | %: {}'.format( i, str_from_object(discrete), num, percentiles.round(3).tolist())) #analyze_data(data_name, data_from_discrete[key]) return data_from_discrete
def run_trials(trials, data_path=None, num_cores=False, **kwargs): # https://stackoverflow.com/questions/15314189/python-multiprocessing-pool-hangs-at-join # https://stackoverflow.com/questions/39884898/large-amount-of-multiprocessing-process-causing-deadlock # TODO: multiprocessing still seems to hang on one thread before starting assert (get_python_version() == 3) results = [] if not trials: return results start_time = time.time() serial = (num_cores is False) # None is the max number failures = 0 scored = 0 try: for result in map_general(run_trial, trials, serial, num_cores=num_cores, **kwargs): num_trials = len(results) + failures print( '{}\nTrials: {} | Successes: {} | Failures: {} | Scored: {} | Time: {:.3f}' .format(SEPARATOR, num_trials, len(results), failures, scored, elapsed_time(start_time))) print('Result:', str_from_object(result)) if result is None: failures += 1 print('Error! Trial resulted in an exception') continue scored += int(result.get('score', None) is not None) results.append(result) write_results(data_path, results) # except BaseException as e: # traceback.print_exc() # e finally: print(SEPARATOR) safe_rm_dir(TEMP_DIRECTORY) write_results(data_path, results) print('Hours: {:.3f}'.format(elapsed_time(start_time) / HOURS_TO_SECS)) # TODO: make a generator version of this return results
def main(): parser = create_parser() parser.add_argument('-enable', action='store_true', help='Enables rendering during planning') parser.add_argument('-teleport', action='store_true', help='Teleports between configurations') parser.add_argument('-simulate', action='store_true', help='Simulates the system') parser.add_argument('-viewer', action='store_true', help='Enable the viewer and visualizes the plan') args = parser.parse_args() print('Arguments:', args) connect(use_gui=args.viewer) robot, names, movable = load_world() print('Objects:', names) saver = WorldSaver() problem = pddlstream_from_problem(robot, movable=movable, teleport=args.teleport) _, _, _, stream_map, init, goal = problem print('Init:', init) print('Goal:', goal) print('Streams:', str_from_object(set(stream_map))) with Profiler(): with LockRenderer(lock=not args.enable): solution = solve(problem, algorithm=args.algorithm, unit_costs=args.unit, success_cost=INF) saver.restore() print_solution(solution) plan, cost, evaluations = solution if (plan is None) or not has_gui(): disconnect() return command = postprocess_plan(plan) if args.simulate: wait_for_user('Simulate?') command.control() else: wait_for_user('Execute?') #command.step() command.refine(num_steps=10).execute(time_step=0.001) wait_for_user('Finish?') disconnect()
def solve_serialized(initial_problem, stream_info={}, unit_costs=False, unit_efforts=False, verbose=True, retain_facts=True, **kwargs): # TODO: be careful of CanMove deadends domain_pddl, constant_map, stream_pddl, stream_map, init, goal = initial_problem _, _, domain, streams = parse_problem(initial_problem, stream_info, constraints=None, unit_costs=unit_costs, unit_efforts=unit_efforts) static_init, _ = partition_facts( domain, init) # might not be able to reprove static_int #global_all, global_preimage = [], [] global_plan = [] global_cost = 0 state = list(init) goals = serialize_goal(goal) # TODO: instead just track how the true init updates for i in range(len(goals)): # TODO: option in algorithms to pass in existing facts for stream in streams: stream.reset() goal = And(*goals[:i + 1]) print('Goal:', str_from_object(goal)) # No strict need to reuse streams because generator functions #local_problem = PDDLProblem(domain_pddl, constant_map, stream_pddl, stream_map, state, goal) local_problem = PDDLProblem(domain_pddl, constant_map, streams, None, state, goal) with Verbose(verbose): solution = solve_focused(local_problem, stream_info=stream_info, unit_costs=unit_costs, unit_efforts=unit_efforts, verbose=True, **kwargs) print_solution(solution) local_plan, local_cost, local_certificate = solution if local_plan is None: # TODO: replan upon failure global_certificate = Certificate(all_facts={}, preimage_facts=None) return None, INF, global_certificate if retain_facts: state = local_certificate.all_facts else: _, fluent_facts = partition_facts(domain, state) state = static_init + fluent_facts + local_certificate.preimage_facts # TODO: include functions #print('State:', state) # TODO: indicate when each fact is used # TODO: record failed facts global_plan.extend( local_plan) # TODO: compute preimage of the executed plan global_cost += local_cost static_state, _ = partition_facts(domain, state) #global_all.extend(partition_facts(domain, local_certificate.all_facts)[0]) #global_preimage.extend(static_state) print('Static:', static_state) state = apply_actions(domain, state, local_plan, unit_costs=unit_costs) print(SEPARATOR) #user_input('Continue?') # TODO: could also just test the goal here # TODO: constrain future plan skeletons global_certificate = Certificate(all_facts={}, preimage_facts=None) return global_plan, global_cost, global_certificate
def solve_abstract(problem, constraints=PlanConstraints(), stream_info={}, replan_actions=set(), unit_costs=False, success_cost=INF, max_time=INF, max_iterations=INF, max_memory=INF, initial_complexity=0, complexity_step=1, max_complexity=INF, max_skeletons=INF, search_sample_ratio=0, bind=True, max_failures=0, unit_efforts=False, max_effort=INF, effort_weight=None, reorder=True, visualize=False, verbose=True, **search_kwargs): """ Solves a PDDLStream problem by first planning with optimistic stream outputs and then querying streams :param problem: a PDDLStream problem :param constraints: PlanConstraints on the set of legal solutions :param stream_info: a dictionary from stream name to StreamInfo altering how individual streams are handled :param replan_actions: the actions declared to induce replanning for the purpose of deferred stream evaluation :param unit_costs: use unit action costs rather than numeric costs :param success_cost: the exclusive (strict) upper bound on plan cost to successfully terminate :param max_time: the maximum runtime :param max_iterations: the maximum number of search iterations :param max_memory: the maximum amount of memory :param initial_complexity: the initial stream complexity limit :param complexity_step: the increase in the stream complexity limit per iteration :param max_complexity: the maximum stream complexity limit :param max_skeletons: the maximum number of plan skeletons (max_skeletons=None indicates not adaptive) :param search_sample_ratio: the desired ratio of sample time / search time when max_skeletons!=None :param bind: if True, propagates parameter bindings when max_skeletons=None :param max_failures: the maximum number of stream failures before switching phases when max_skeletons=None :param unit_efforts: use unit stream efforts rather than estimated numeric efforts :param max_effort: the maximum amount of stream effort :param effort_weight: a multiplier for stream effort compared to action costs :param reorder: if True, reorder stream plans to minimize the expected sampling overhead :param visualize: if True, draw the constraint network and stream plan as a graphviz file :param verbose: if True, print the result of each stream application :param search_kwargs: keyword args for the search subroutine :return: a tuple (plan, cost, evaluations) where plan is a sequence of actions (or None), cost is the cost of the plan (INF if no plan), and evaluations is init expanded using stream applications """ # TODO: select whether to search or sample based on expected success rates # TODO: no optimizers during search with relaxed_stream_plan # TODO: locally optimize only after a solution is identified # TODO: replan with a better search algorithm after feasible # TODO: change the search algorithm and unit costs based on the best cost use_skeletons = (max_skeletons is not None) #assert implies(use_skeletons, search_sample_ratio > 0) eager_disabled = (effort_weight is None ) # No point if no stream effort biasing num_iterations = eager_calls = 0 complexity_limit = initial_complexity evaluations, goal_exp, domain, externals = parse_problem( problem, stream_info=stream_info, constraints=constraints, unit_costs=unit_costs, unit_efforts=unit_efforts) identify_non_producers(externals) enforce_simultaneous(domain, externals) compile_fluent_streams(domain, externals) # TODO: make effort_weight be a function of the current cost # if (effort_weight is None) and not has_costs(domain): # effort_weight = 1 load_stream_statistics(externals) if visualize and not has_pygraphviz(): visualize = False print( 'Warning, visualize=True requires pygraphviz. Setting visualize=False' ) if visualize: reset_visualizations() streams, functions, negative, optimizers = partition_externals( externals, verbose=verbose) eager_externals = list(filter(lambda e: e.info.eager, externals)) positive_externals = streams + functions + optimizers has_optimizers = bool(optimizers) # TODO: deprecate assert implies(has_optimizers, use_skeletons) ################ store = SolutionStore(evaluations, max_time, success_cost, verbose, max_memory=max_memory) skeleton_queue = SkeletonQueue(store, domain, disable=not has_optimizers) disabled = set() # Max skeletons after a solution while (not store.is_terminated()) and ( num_iterations < max_iterations) and (complexity_limit <= max_complexity): num_iterations += 1 eager_instantiator = Instantiator( eager_externals, evaluations) # Only update after an increase? if eager_disabled: push_disabled(eager_instantiator, disabled) if eager_externals: eager_calls += process_stream_queue( eager_instantiator, store, complexity_limit=complexity_limit, verbose=verbose) ################ print( '\nIteration: {} | Complexity: {} | Skeletons: {} | Skeleton Queue: {} | Disabled: {} | Evaluations: {} | ' 'Eager Calls: {} | Cost: {:.3f} | Search Time: {:.3f} | Sample Time: {:.3f} | Total Time: {:.3f}' .format(num_iterations, complexity_limit, len(skeleton_queue.skeletons), len(skeleton_queue), len(disabled), len(evaluations), eager_calls, store.best_cost, store.search_time, store.sample_time, store.elapsed_time())) optimistic_solve_fn = get_optimistic_solve_fn( goal_exp, domain, negative, replan_actions=replan_actions, reachieve=use_skeletons, max_cost=min(store.best_cost, constraints.max_cost), max_effort=max_effort, effort_weight=effort_weight, **search_kwargs) # TODO: just set unit effort for each stream beforehand if (max_skeletons is None) or (len(skeleton_queue.skeletons) < max_skeletons): disabled_axioms = create_disabled_axioms( skeleton_queue) if has_optimizers else [] if disabled_axioms: domain.axioms.extend(disabled_axioms) stream_plan, opt_plan, cost = iterative_plan_streams( evaluations, positive_externals, optimistic_solve_fn, complexity_limit, max_effort=max_effort) for axiom in disabled_axioms: domain.axioms.remove(axiom) else: stream_plan, opt_plan, cost = OptSolution( INFEASIBLE, INFEASIBLE, INF) # TODO: apply elsewhere ################ #stream_plan = replan_with_optimizers(evaluations, stream_plan, domain, externals) or stream_plan stream_plan = combine_optimizers(evaluations, stream_plan) #stream_plan = get_synthetic_stream_plan(stream_plan, # evaluations # [s for s in synthesizers if not s.post_only]) #stream_plan = recover_optimistic_outputs(stream_plan) if reorder: # TODO: this blows up memory wise for long stream plans stream_plan = reorder_stream_plan(store, stream_plan) num_optimistic = sum(r.optimistic for r in stream_plan) if stream_plan else 0 action_plan = opt_plan.action_plan if is_plan(opt_plan) else opt_plan print('Stream plan ({}, {}, {:.3f}): {}\nAction plan ({}, {:.3f}): {}'. format(get_length(stream_plan), num_optimistic, compute_plan_effort(stream_plan), stream_plan, get_length(action_plan), cost, str_from_plan(action_plan))) if is_plan(stream_plan) and visualize: log_plans(stream_plan, action_plan, num_iterations) create_visualizations(evaluations, stream_plan, num_iterations) ################ if (stream_plan is INFEASIBLE) and (not eager_instantiator) and ( not skeleton_queue) and (not disabled): break if not is_plan(stream_plan): print('No plan: increasing complexity from {} to {}'.format( complexity_limit, complexity_limit + complexity_step)) complexity_limit += complexity_step if not eager_disabled: reenable_disabled(evaluations, domain, disabled) #print(stream_plan_complexity(evaluations, stream_plan)) if not use_skeletons: process_stream_plan(store, domain, disabled, stream_plan, opt_plan, cost, bind=bind, max_failures=max_failures) continue ################ #optimizer_plan = replan_with_optimizers(evaluations, stream_plan, domain, optimizers) optimizer_plan = None if optimizer_plan is not None: # TODO: post process a bound plan print('Optimizer plan ({}, {:.3f}): {}'.format( get_length(optimizer_plan), compute_plan_effort(optimizer_plan), optimizer_plan)) skeleton_queue.new_skeleton(optimizer_plan, opt_plan, cost) allocated_sample_time = (search_sample_ratio * store.search_time) - store.sample_time \ if len(skeleton_queue.skeletons) <= max_skeletons else INF if skeleton_queue.process(stream_plan, opt_plan, cost, complexity_limit, allocated_sample_time) is INFEASIBLE: break ################ summary = store.export_summary() summary.update({ 'iterations': num_iterations, 'complexity': complexity_limit, 'skeletons': len(skeleton_queue.skeletons), }) print('Summary: {}'.format(str_from_object( summary, ndigits=3))) # TODO: return the summary write_stream_statistics(externals, verbose) return store.extract_solution()
def __repr__(self): return '{}:{}->{}'.format(self.external.name, str_from_object(self.instance.input_objects), str_from_object(self.output_objects))
def solve_incremental(problem, constraints=PlanConstraints(), unit_costs=False, success_cost=INF, max_iterations=INF, max_time=INF, max_memory=INF, initial_complexity=0, complexity_step=1, max_complexity=INF, verbose=False, **search_args): """ Solves a PDDLStream problem by alternating between applying all possible streams and searching :param problem: a PDDLStream problem :param constraints: PlanConstraints on the set of legal solutions :param max_time: the maximum amount of time to apply streams :param max_iterations: the maximum amount of search iterations :param initial_complexity: the stream complexity on the first iteration :param complexity_step: the increase in the complexity limit after each iteration :param max_complexity: the maximum stream complexity :param unit_costs: use unit action costs rather than numeric costs :param success_cost: an exclusive (strict) upper bound on plan cost to terminate :param verbose: if True, this prints the result of each stream application :param search_args: keyword args for the search subroutine :return: a tuple (plan, cost, evaluations) where plan is a sequence of actions (or None), cost is the cost of the plan, and evaluations is init but expanded using stream applications """ # max_complexity = 0 => current # complexity_step = INF => exhaustive # success_cost = terminate_cost = decision_cost evaluations, goal_expression, domain, externals = parse_problem( problem, constraints=constraints, unit_costs=unit_costs) store = SolutionStore( evaluations, max_time, success_cost, verbose, max_memory=max_memory) # TODO: include other info here? if UPDATE_STATISTICS: load_stream_statistics(externals) static_externals = compile_fluents_as_attachments(domain, externals) num_iterations = num_calls = 0 complexity_limit = initial_complexity instantiator = Instantiator(static_externals, evaluations) num_calls += process_stream_queue(instantiator, store, complexity_limit, verbose=verbose) while not store.is_terminated() and ( num_iterations <= max_iterations) and (complexity_limit <= max_complexity): num_iterations += 1 print( 'Iteration: {} | Complexity: {} | Calls: {} | Evaluations: {} | Solved: {} | Cost: {} | Time: {:.3f}' .format(num_iterations, complexity_limit, num_calls, len(evaluations), store.has_solution(), store.best_cost, store.elapsed_time())) plan, cost = solve_finite(evaluations, goal_expression, domain, max_cost=min(store.best_cost, constraints.max_cost), **search_args) if is_plan(plan): store.add_plan(plan, cost) if not instantiator: break if complexity_step is None: # TODO: option to select the next k-smallest complexities complexity_limit = instantiator.min_complexity() else: complexity_limit += complexity_step num_calls += process_stream_queue(instantiator, store, complexity_limit, verbose=verbose) #retrace_stream_plan(store, domain, goal_expression) #print('Final queue size: {}'.format(len(instantiator))) summary = store.export_summary() summary.update({ # TODO: integrate into store 'iterations': num_iterations, 'complexity': complexity_limit, # TODO: optimal, infeasible, etc... }) print(str_from_object(summary)) # TODO: return the summary if UPDATE_STATISTICS: write_stream_statistics(externals, verbose) return store.extract_solution()
def str_from_plan(plan): if not is_plan(plan): return str(plan) return str_from_object(list(map(str_from_action, plan)))