def parse_problem(problem, stream_info={}, constraints=None, unit_costs=False, unit_efforts=False): # TODO: just return the problem if already written programmatically #reset_globals() # Prevents use of satisfaction.py domain_pddl, constant_map, stream_pddl, stream_map, init, goal = problem domain = parse_domain(domain_pddl) #domain = domain_pddl if not isinstance(domain, Domain): #assert isinstance(domain, str) # raw PDDL is returned obj_from_constant = {name: Object(value, name=name) for name, value in constant_map.items()} streams = parse_stream_pddl(stream_pddl, stream_map, stream_info=stream_info, unit_costs=unit_costs, unit_efforts=unit_efforts) evaluations = evaluations_from_init(init) goal_exp = obj_from_value_expression(goal) return evaluations, goal_exp, domain, streams if len(domain.types) != 1: raise NotImplementedError('Types are not currently supported') if unit_costs: set_unit_costs(domain) obj_from_constant = parse_constants(domain, constant_map) streams = parse_stream_pddl(stream_pddl, stream_map, stream_info=stream_info, unit_costs=unit_costs, unit_efforts=unit_efforts) check_problem(domain, streams, obj_from_constant) evaluations = evaluations_from_init(init) goal_exp = obj_from_value_expression(goal) #normalize_domain_goal(domain, goal_expression) goal_exp = add_plan_constraints(constraints, domain, evaluations, goal_exp) parse_goal(goal_exp, domain) # Just to check that it parses # TODO: refactor the following? compile_to_exogenous(evaluations, domain, streams) enforce_simultaneous(domain, streams) compile_fluent_streams(domain, streams) return evaluations, goal_exp, domain, streams
def parse_problem(problem, stream_info={}, constraints=None, unit_costs=False, unit_efforts=False): # TODO: just return the problem if already written programmatically domain_pddl, constant_map, stream_pddl, stream_map, init, goal = problem domain = parse_domain(domain_pddl) if len(domain.types) != 1: raise NotImplementedError('Types are not currently supported') set_unit_costs(domain, unit_costs) obj_from_constant = parse_constants(domain, constant_map) streams = parse_stream_pddl(stream_pddl, stream_map, stream_info=stream_info, unit_efforts=unit_efforts) check_problem(domain, streams, obj_from_constant) evaluations = evaluations_from_init(init) goal_exp = obj_from_value_expression(goal) #normalize_domain_goal(domain, goal_expression) goal_exp = add_plan_constraints(constraints, domain, evaluations, goal_exp) parse_goal(goal_exp, domain) # Just to check that it parses # TODO: refactor the following? compile_to_exogenous(evaluations, domain, streams) compile_fluent_streams(domain, streams) enforce_simultaneous(domain, streams) return evaluations, goal_exp, domain, streams
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()