def recursive_solve_stream_plan(evaluations, streams, functions, stream_results, solve_stream_plan, depth): combined_plan, cost = solve_stream_plan(stream_results) stream_plan, action_plan = separate_plan(combined_plan, action_info=None, terminate=False, stream_only=False) #print('Depth: {}\n' # 'Stream plan: {}\n' # 'Action plan: {}'.format(depth, stream_plan, action_plan)) if stream_plan is None: return stream_plan, cost, depth plan_index = get_stream_plan_index(stream_plan) if plan_index == 0: return combined_plan, cost, depth stream_results, bindings = optimistic_process_stream_plan( evaluations, stream_plan) double_bindings = { v: k for k, values in bindings.items() if 2 <= len(values) for v in values } stream_results += optimistic_process_streams( evaluations_from_stream_plan(evaluations, stream_results), streams, double_bindings=double_bindings) stream_results += optimistic_process_streams( evaluations_from_stream_plan(evaluations, stream_results), functions) return recursive_solve_stream_plan(evaluations, streams, functions, stream_results, solve_stream_plan, depth + 1)
def hierarchical_plan_streams(evaluations, externals, results, optimistic_solve_fn, complexity_limit, depth, constraints, **effort_args): if MAX_DEPTH <= depth: return None, INF, depth combined_plan, cost = optimistic_solve_fn(evaluations, results, constraints) if not is_plan(combined_plan): return combined_plan, cost, depth stream_plan, action_plan = separate_plan(combined_plan, stream_only=False) #dump_plans(stream_plan, action_plan, cost) #create_visualizations(evaluations, stream_plan, depth) #print(depth, get_length(stream_plan)) if is_refined(stream_plan): return combined_plan, cost, depth new_results, bindings = optimistic_stream_evaluation( evaluations, stream_plan) if not CONSTRAIN_STREAMS and not CONSTRAIN_PLANS: return None, INF, depth + 1 if CONSTRAIN_STREAMS: next_results = compute_stream_results(evaluations, new_results, externals, **effort_args) else: next_results, _ = optimistic_process_streams(evaluations, externals, complexity_limit, **effort_args) next_constraints = None if CONSTRAIN_PLANS: next_constraints = compute_skeleton_constraints(action_plan, bindings) return hierarchical_plan_streams(evaluations, externals, next_results, optimistic_solve_fn, complexity_limit, depth + 1, next_constraints, **effort_args)
def hierarchical_plan_streams(evaluations, externals, results, optimistic_solve_fn, complexity_limit, depth, constraints, **effort_args): if MAX_DEPTH <= depth: return None, INF, depth combined_plan, cost = optimistic_solve_fn(evaluations, results, constraints) if not is_plan(combined_plan): return combined_plan, cost, depth stream_plan, action_plan = separate_plan(combined_plan, stream_only=False) #dump_plans(stream_plan, action_plan, cost) #create_visualizations(evaluations, stream_plan, depth) #print(depth, get_length(stream_plan)) #print('Stream plan ({}, {:.3f}): {}\nAction plan ({}, {:.3f}): {}'.format( # get_length(stream_plan), compute_plan_effort(stream_plan), stream_plan, # get_length(action_plan), cost, str_from_plan(action_plan))) #if is_plan(stream_plan): # for result in stream_plan: # effort = compute_result_effort(result, unit_efforts=True) # if effort != 0: # print(result, effort) #print() if is_refined(stream_plan): return combined_plan, cost, depth new_results, bindings = optimistic_stream_evaluation(evaluations, stream_plan) if not CONSTRAIN_STREAMS and not CONSTRAIN_PLANS: return None, INF, depth + 1 if CONSTRAIN_STREAMS: next_results = compute_stream_results(evaluations, new_results, externals, **effort_args) else: next_results, _ = optimistic_process_streams(evaluations, externals, complexity_limit, **effort_args) next_constraints = None if CONSTRAIN_PLANS: next_constraints = compute_skeleton_constraints(action_plan, bindings) return hierarchical_plan_streams(evaluations, externals, next_results, optimistic_solve_fn, complexity_limit, depth + 1, next_constraints, **effort_args)
def reorder_combined_plan(evaluations, combined_plan, action_info, domain, **kwargs): if not is_plan(combined_plan): return combined_plan stream_plan, action_plan = separate_plan(combined_plan) orders = get_combined_orders(evaluations, stream_plan, action_plan, domain) _, out_orders = neighbors_from_orders(orders) valid_combine = lambda v, subset: out_orders[v] <= subset def stats_fn(operator): if isinstance(operator, Result): return get_stream_stats(operator) name, _ = operator info = action_info[name] return info.p_success, info.overhead return dynamic_programming(combined_plan, valid_combine, stats_fn, **kwargs)
def solve_focused(problem, stream_info={}, action_info={}, synthesizers=[], max_time=INF, max_cost=INF, unit_costs=None, effort_weight=None, eager_layers=1, visualize=False, verbose=True, postprocess=False, **search_kwargs): """ Solves a PDDLStream problem by first hypothesizing stream outputs and then determining whether they exist :param problem: a PDDLStream problem :param action_info: a dictionary from stream name to ActionInfo for planning and execution :param stream_info: a dictionary from stream name to StreamInfo altering how individual streams are handled :param max_time: the maximum amount of time to apply streams :param max_cost: a strict upper bound on plan cost :param effort_weight: a multiplier for stream effort compared to action costs :param eager_layers: the number of eager stream application layers per iteration :param visualize: if True, it draws the constraint network and stream plan as a graphviz file :param verbose: if True, this prints 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, and evaluations is init but expanded using stream applications """ # TODO: return to just using the highest level samplers at the start search_sampling_ratio = 1 solve_stream_plan_fn = relaxed_stream_plan if effort_weight is None else simultaneous_stream_plan # TODO: warning check if using simultaneous_stream_plan or sequential_stream_plan with non-eager functions num_iterations = 0 search_time = sample_time = 0 store = SolutionStore(max_time, max_cost, verbose) # TODO: include other info here? evaluations, goal_expression, domain, stream_name, externals = parse_problem( problem, stream_info) compile_to_exogenous(evaluations, domain, externals) if unit_costs is None: unit_costs = not has_costs(domain) full_action_info = get_action_info(action_info) load_stream_statistics(stream_name, externals + synthesizers) if visualize: clear_visualizations() # TODO: somehow Functions became no longer eager? eager_externals = list( filter(lambda e: e.info.eager or type(e) == Function, externals)) streams, functions, negative = partition_externals(externals) queue = SkeletonQueue(store, evaluations) # TODO: decide max_sampling_time based on total search_time or likelihood estimates # TODO: switch to searching if believe chance of search better than sampling while not store.is_terminated(): num_iterations += 1 print( '\nIteration: {} | Queue: {} | Evaluations: {} | Cost: {} ' '| Search Time: {:.3f} | Sample Time: {:.3f} | Total Time: {:.3f}'. format(num_iterations, len(queue), len(evaluations), store.best_cost, search_time, sample_time, store.elapsed_time())) start_time = time.time() layered_process_stream_queue( Instantiator(evaluations, eager_externals), evaluations, store, eager_layers) solve_stream_plan = lambda sr: solve_stream_plan_fn( evaluations, goal_expression, domain, sr, negative, max_cost=store.best_cost, #max_cost=min(store.best_cost, max_cost), unit_costs=unit_costs, **search_kwargs) #combined_plan, cost = solve_stream_plan(populate_results(evaluations, streams + functions)) combined_plan, cost = iterative_solve_stream_plan( evaluations, streams, functions, solve_stream_plan) if action_info: combined_plan = reorder_combined_plan(evaluations, combined_plan, full_action_info, domain) print('Combined plan: {}'.format(combined_plan)) stream_plan, action_plan = separate_plan(combined_plan, full_action_info) stream_plan = reorder_stream_plan( stream_plan) # TODO: is this strictly redundant? stream_plan = get_synthetic_stream_plan(stream_plan, synthesizers) print('Stream plan: {}\n' 'Action plan: {}'.format(stream_plan, action_plan)) search_time += elapsed_time(start_time) start_time = time.time() if stream_plan is None: if not queue: break queue.process_until_success() #queue.fairly_process() else: if visualize: create_visualizations(evaluations, stream_plan, num_iterations) queue.new_skeleton(stream_plan, action_plan, cost) queue.greedily_process() sample_time += elapsed_time(start_time) start_time = time.time() queue.timed_process(search_sampling_ratio * search_time - sample_time) sample_time += elapsed_time(start_time) if postprocess and (not unit_costs): locally_optimize(evaluations, store, goal_expression, domain, functions, negative, synthesizers) write_stream_statistics(stream_name, externals + synthesizers, verbose) return revert_solution(store.best_plan, store.best_cost, evaluations)
def solve_focused(problem, constraints=PlanConstraints(), stream_info={}, action_info={}, synthesizers=[], max_time=INF, max_iterations=INF, max_skeletons=INF, unit_costs=False, success_cost=INF, complexity_step=1, unit_efforts=False, max_effort=INF, effort_weight=None, reorder=True, search_sample_ratio=0, visualize=False, verbose=True, **search_kwargs): """ Solves a PDDLStream problem by first hypothesizing stream outputs and then determining whether they exist :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 action_info: a dictionary from stream name to ActionInfo for planning and execution :param synthesizers: a list of StreamSynthesizer objects :param max_time: the maximum amount of time to apply streams :param max_iterations: the maximum number of search iterations :param max_iterations: the maximum number of plan skeletons to consider :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 unit_efforts: use unit stream efforts rather than estimated numeric efforts :param complexity_step: the increase in the effort limit after each failure :param max_effort: the maximum amount of effort to consider for streams :param effort_weight: a multiplier for stream effort compared to action costs :param reorder: if True, stream plans are reordered to minimize the expected sampling overhead :param search_sample_ratio: the desired ratio of search time / sample time :param visualize: if True, it draws the constraint network and stream plan as a graphviz file :param verbose: if True, this prints 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, and evaluations is init but 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 num_iterations = search_time = sample_time = eager_calls = 0 complexity_limit = float(INITIAL_COMPLEXITY) eager_disabled = effort_weight is None # No point if no stream effort biasing evaluations, goal_exp, domain, externals = parse_problem( problem, stream_info=stream_info, constraints=constraints, unit_costs=unit_costs, unit_efforts=unit_efforts) store = SolutionStore(evaluations, max_time, success_cost, verbose) full_action_info = get_action_info(action_info) load_stream_statistics(externals + synthesizers) if visualize and not has_pygraphviz(): visualize = False print('Warning, visualize=True requires pygraphviz. Setting visualize=False') if visualize: reset_visualizations() streams, functions, negative = partition_externals(externals, verbose=verbose) eager_externals = list(filter(lambda e: e.info.eager, externals)) skeleton_queue = SkeletonQueue(store, goal_exp, domain) disabled = set() while (not store.is_terminated()) and (num_iterations < max_iterations): start_time = time.time() num_iterations += 1 eager_instantiator = Instantiator(eager_externals, evaluations) # Only update after an increase? if eager_disabled: push_disabled(eager_instantiator, disabled) 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, search_time, sample_time, store.elapsed_time())) optimistic_solve_fn = get_optimistic_solve_fn(goal_exp, domain, negative, max_cost=min(store.best_cost, constraints.max_cost), unit_efforts=unit_efforts, max_effort=max_effort, effort_weight=effort_weight, **search_kwargs) if (max_skeletons is not None) and (len(skeleton_queue.skeletons) < max_skeletons): combined_plan, cost = iterative_plan_streams(evaluations, externals, optimistic_solve_fn, complexity_limit, unit_efforts=unit_efforts, max_effort=max_effort) else: combined_plan, cost = INFEASIBLE, INF if action_info: combined_plan = reorder_combined_plan(evaluations, combined_plan, full_action_info, domain) print('Combined plan: {}'.format(combined_plan)) stream_plan, action_plan = separate_plan(combined_plan, full_action_info) #stream_plan = replan_with_optimizers(evaluations, stream_plan, domain, externals) 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]) if reorder: stream_plan = reorder_stream_plan(stream_plan) # This may be redundant when using reorder_combined_plan print('Stream plan ({}, {:.3f}): {}\nAction plan ({}, {:.3f}): {}'.format( get_length(stream_plan), 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) search_time += elapsed_time(start_time) if (stream_plan is INFEASIBLE) and (not eager_instantiator) and (not skeleton_queue) and (not disabled): break start_time = time.time() if not is_plan(stream_plan): complexity_limit += complexity_step if not eager_disabled: reenable_disabled(evaluations, domain, disabled) elif not stream_plan: store.add_plan(action_plan, cost) if max_skeletons is None: process_stream_plan(store, domain, disabled, stream_plan) else: allocated_sample_time = (search_sample_ratio * search_time) - sample_time skeleton_queue.process(stream_plan, action_plan, cost, complexity_limit, allocated_sample_time) sample_time += elapsed_time(start_time) write_stream_statistics(externals + synthesizers, verbose) return store.extract_solution()