def solve_restart(problem, max_time=INF, max_restarts=0, iteration_time=INF, abort=True, **kwargs): # TODO: iteratively lower the cost bound # TODO: a sequence of different planner configurations # TODO: reset objects and/or streams if (max_restarts >= 1) and (iteration_time == INF): iteration_time = min(2 * 60, iteration_time) assert (max_restarts == 0) or (iteration_time != INF) assert max_restarts >= 0 start_time = time.time() for attempt in irange(1+max_restarts): iteration_start_time = time.time() if elapsed_time(start_time) > max_time: break if attempt >= 1: print(SEPARATOR) #solution = planner_fn(problem) # Or include the problem in the lambda remaining_time = min(iteration_time, max_time-elapsed_time(start_time)) solution = solve(problem, max_time=remaining_time, **kwargs) plan, cost, certificate = solution if is_plan(plan): # TODO: INFEASIBLE return solution if abort and (elapsed_time(iteration_start_time) < remaining_time): break # TODO: return the cause of failure certificate = Certificate(all_facts=[], preimage_facts=[]) # TODO: aggregate return Solution(None, INF, certificate)
def process_until_new(self, complexity_limit): # TODO: process the entire queue once instead print('Sampling until new output values') is_new = False attempts = 0 print_frequency = 1.0 last_time = time.time() standy = [] while self.is_active() and (not is_new): attempts += 1 key, binding = heappop(self.queue) readd, is_new = self._process_binding(binding) if readd is True: heappush(self.queue, binding.get_element()) elif readd is None: standy.append(binding) #is_new |= self._process_root() self.greedily_process(complexity_limit) if print_frequency <= elapsed_time(last_time): print('Queue: {} | Attempts: {} | Time: {:.3f}'.format( len(self.queue), attempts, elapsed_time(last_time))) last_time = time.time() if is_new: for binding in standy: heappush(self.queue, binding.get_element()) return is_new
def process_instance(instantiator, store, instance, verbose=False): #, **complexity_args): if instance.enumerated: return [] start_time = time.time() new_results, new_facts = instance.next_results(verbose=verbose) store.sample_time += elapsed_time(start_time) evaluations = store.evaluations #remove_blocked(evaluations, instance, new_results) for result in new_results: complexity = result.compute_complexity(evaluations) #complexity = instantiator.compute_complexity(instance) for evaluation in add_certified(evaluations, result): instantiator.add_atom(evaluation, complexity) fact_complexity = 0 # TODO: record the instance or treat as initial? for evaluation in add_facts(evaluations, new_facts, result=UNKNOWN_EVALUATION, complexity=fact_complexity): instantiator.add_atom(evaluation, fact_complexity) if not instance.enumerated: instantiator.push_instance(instance) return new_results
def instantiate_task(task, check_infeasible=True, use_fd=FD_INSTANTIATE, **kwargs): start_time = time() print() normalize.normalize(task) if use_fd: relaxed_reachable, atoms, actions, axioms, reachable_action_params = instantiate.explore( task) else: relaxed_reachable, atoms, actions, axioms = instantiate_domain( task, **kwargs) reachable_action_params = get_reachable_action_params(actions) #for atom in sorted(filter(lambda a: isinstance(a, pddl.Literal), set(task.init) | set(atoms)), # key=lambda a: a.predicate): # print(fact_from_fd(atom)) #print(axioms) #for i, action in enumerate(sorted(actions, key=lambda a: a.name)): # print(i, transform_action_args(pddl_from_instance(action), obj_from_pddl)) print('Infeasible:', not relaxed_reachable) print('Instantiation time:', elapsed_time(start_time)) if check_infeasible and not relaxed_reachable: return None goal_list = instantiate_goal(task.goal) return InstantiatedTask(task, atoms, actions, axioms, reachable_action_params, goal_list)
def greedily_process_queue(queue, evaluations, store, max_time): # TODO: search until new disabled or new evaluation? start_time = time.time() while queue and not store.is_terminated(): key, skeleton = queue[0] if (key.attempts != 0) and (max_time <= elapsed_time(start_time)): break heappop(queue) proccess_stream_plan(key, skeleton, queue, evaluations, store)
def timed_process(self, max_time=INF, max_iterations=INF): # TODO: combine process methods into process_until iterations = num_new = 0 if not self.is_active(): return num_new print('Sampling for up to {:.3f} seconds'.format(max_time)) #, max_iterations)) start_time = time.time() # TODO: instead use sample_time while self.is_active() and (elapsed_time(start_time) < max_time) and (iterations < max_iterations): iterations += 1 num_new += self.process_root() #print('Iterations: {} | New: {} | Time: {:.3f}'.format(iterations, num_new, elapsed_time(start_time))) return num_new + self.greedily_process()
def new_list_gen_fn(*inputs): generator = list_gen_fn(*inputs) terminated = False while not terminated: start_time = time.time() elements = [] for i in range(max_attempts): if terminated or (num_elements <= len(elements)) or (max_time <= elapsed_time(start_time)): break new_elements, terminated = get_next(generator) elements.extend(new_elements) yield elements
def process(self, stream_plan, action_plan, cost, complexity_limit, max_time=0): # TODO: manually add stream_plans for synthesizers/optimizers start_time = time.time() if is_plan(stream_plan): #print([result for result in stream_plan if result.optimistic]) #raw_input('New skeleton') self.new_skeleton(stream_plan, action_plan, cost) self.greedily_process() elif stream_plan is INFEASIBLE: # TODO: use complexity_limit self.process_until_new() self.timed_process(max_time - elapsed_time(start_time)) self.accelerate_best_bindings()
def process_skeleton_queue(store, queue, stream_plan, action_plan, cost, max_sample_time): start_time = time.time() if stream_plan is None: if not queue: return False queue.process_until_success() elif not stream_plan: store.add_plan(action_plan, cost) else: queue.new_skeleton(stream_plan, action_plan, cost) queue.timed_process(max_sample_time - elapsed_time(start_time)) return True
def solve_tfd(domain_pddl, problem_pddl, max_time=INF, debug=False): if PLANNER == 'tfd': root, template = TFD_PATH, TFD_COMMAND elif PLANNER == 'cerberus': root, template = CERB_PATH, CERB_COMMAND elif PLANNER == 'tflap': root, template = TFLAP_PATH, TFLAP_COMMAND elif PLANNER == 'optic': root, template = OPTIC_PATH, OPTIC_COMMAND elif PLANNER == 'tpshe': root, template = TPSHE_PATH, TPSHE_COMMAND else: raise ValueError(PLANNER) start_time = time.time() domain_path, problem_path = write_pddl(domain_pddl, problem_pddl) plan_path = os.path.join(TEMP_DIR, PLAN_FILE) #assert not actions, "There shouldn't be any actions - just temporal actions" paths = [ os.path.join(os.getcwd(), p) for p in (domain_path, problem_path, plan_path) ] command = os.path.join(root, template.format(*paths)) print(command) if debug: stdout, stderr = None, None else: stdout, stderr = open(os.devnull, 'w'), open(os.devnull, 'w') proc = subprocess.call(command, shell=True, cwd=root, stdout=stdout, stderr=stderr) # timeout=None (python3) error = proc != 0 print('Error:', error) # TODO: close any opened resources temp_path = os.path.join(os.getcwd(), TEMP_DIR) plan_files = sorted(f for f in os.listdir(temp_path) if f.startswith(PLAN_FILE)) print('Plans:', plan_files) best_plan, best_makespan = parse_plans(temp_path, plan_files) #if not debug: # safe_rm_dir(TEMP_DIR) print('Makespan: ', best_makespan) print('Time:', elapsed_time(start_time)) sequential_plan = sequential_from_temporal_plan(best_plan) return sequential_plan, best_makespan
def process_until_new(self, print_frequency=1.): # TODO: process the entire queue for one pass instead num_new = 0 if not self.is_active(): return num_new print('Sampling until new output values') iterations = 0 last_time = time.time() while self.is_active() and (not num_new): iterations += 1 _, binding = self.pop_binding() readd, is_new = self._process_binding(binding) if readd is True: self.push_binding(binding) elif readd is STANDBY: self.standby.append(binding) # TODO: test for deciding whether to standby num_new += is_new if print_frequency <= elapsed_time(last_time): print('Queue: {} | Iterations: {} | Time: {:.3f}'.format( len(self.queue), iterations, elapsed_time(last_time))) last_time = time.time() self.readd_standby() return num_new + self.greedily_process()
def abstrips_solve_from_task_sequential(sas_task, temp_dir=TEMP_DIR, clean=False, debug=False, hierarchy=[], subgoal_horizon=1, **kwargs): # TODO: version that plans for each goal individually # TODO: can reduce to goal serialization if binary flag for each subgoal if not hierarchy: return solve_from_task(sas_task, temp_dir=temp_dir, clean=clean, debug=debug, **kwargs) start_time = time() plan, cost = None, INF with Verbose(debug): last_plan = None for level in range(len(hierarchy) + 1): local_sas_task = deepcopy(sas_task) prune_hierarchy_pre_eff( local_sas_task, hierarchy[level:]) # TODO: break if no pruned # The goal itself is effectively a subgoal # Handle this subgoal horizon subgoal_plan = [local_sas_task.goal.pairs[:]] # TODO: do I want to consider the "subgoal action" as a real action? if last_plan is not None: subgoal_var = add_subgoals(local_sas_task, last_plan) subgoal_plan = [[(subgoal_var, val)] for val in range( 1, local_sas_task.variables.ranges[subgoal_var], subgoal_horizon)] + subgoal_plan hierarchy_horizon = min(hierarchy[level - 1].horizon, len(subgoal_plan)) subgoal_plan = subgoal_plan[:hierarchy_horizon] plan, cost = plan_subgoals(local_sas_task, subgoal_plan, temp_dir, **kwargs) if (level == len(hierarchy)) or (plan is None): # TODO: fall back on normal # TODO: search in space of subgoals break last_plan = [ name_from_action(action, args) for action, args in plan ] if clean: safe_rm_dir(temp_dir) print('Total runtime: {:.3f}'.format(elapsed_time(start_time))) # TODO: record which level of abstraction each operator is at when returning # TODO: return instantiated actions here rather than names (including pruned pre/eff) return plan, cost
def instantiate_task(task, check_infeasible=True, **kwargs): start_time = time() print() normalize.normalize(task) if FD_INSTANTIATE: relaxed_reachable, atoms, actions, axioms, reachable_action_params = instantiate.explore(task) else: relaxed_reachable, atoms, actions, axioms = instantiate_domain(task, **kwargs) reachable_action_params = get_reachable_action_params(actions) print('Infeasible:', not relaxed_reachable) print('Instantiation time:', elapsed_time(start_time)) if check_infeasible and not relaxed_reachable: return None goal_list = instantiate_goal(task.goal) return InstantiatedTask(task, atoms, actions, axioms, reachable_action_params, goal_list)
def sample_solutions(model, variables, num_samples=INF, norm=2, closest=True): from gurobipy import GRB, quicksum, abs_ start_time = time.time() objective = model.getObjective() #sprint(objective, objective.getValue()) # model = model.feasibility() # model.setObjective(0.0) if norm == 2: model.setParam(GRB.Param.NonConvex, 2) # PSDTol objective_terms = [] if closest: min_distance = model.addVar(lb=0., ub=GRB.INFINITY) objective_terms.append(min_distance) solutions = [] # TODO: sample initial solution from this set while len(solutions) < num_samples: terms = [] for var in variables: for index, coord in enumerate(var): value = coord.X set_guess(coord, value, hard=True) delta = model.addVar(lb=-GRB.INFINITY, ub=GRB.INFINITY) model.addConstr(delta == coord - value) if norm == 1: term = model.addVar(lb=0., ub=GRB.INFINITY) model.addConstr(term == abs_(delta)) elif norm == 2: term = delta * delta else: raise NotImplementedError(norm) terms.append(term) # TODO: scale distance = quicksum(terms) if closest: model.addConstr(min_distance <= distance) else: objective_terms.append(distance) # TODO: weight model.setObjective(quicksum(objective_terms), sense=GRB.MAXIMIZE) # MINIMIZE | MAXIMIZE model.optimize() print( '# {} | objective: {:.3f} | cost: {:.3f} | runtime: {:.3f}'.format( len(solutions), model.ObjVal, objective.getValue(), elapsed_time(start_time))) # min_distance.X solution = [value_from_var(var) for var in variables] solutions.append(solution) yield solution
def process(self, stream_plan, action_plan, cost, complexity_limit, max_time=0, accelerate=False): start_time = time.time() if is_plan(stream_plan): self.new_skeleton(stream_plan, action_plan, cost) self.greedily_process() elif (stream_plan is INFEASIBLE) and not self.process_until_new(): # Move this after process_complexity return INFEASIBLE if not self.queue: return FAILED # TODO: add and process self.timed_process(max_time=(max_time - elapsed_time(start_time))) self.process_complexity(complexity_limit) if accelerate: self.accelerate_best_bindings() return FAILED
def solve_from_pddl(domain_pddl, problem_pddl, temp_dir=TEMP_DIR, clean=False, debug=False, **search_kwargs): # TODO: combine with solve_from_task #return solve_tfd(domain_pddl, problem_pddl) start_time = time() with Verbose(debug): write_pddl(domain_pddl, problem_pddl, temp_dir) #run_translate(temp_dir, verbose) translate_and_write_pddl(domain_pddl, problem_pddl, temp_dir, debug) solution = run_search(temp_dir, debug=debug, **search_kwargs) if clean: safe_rm_dir(temp_dir) print('Total runtime: {:.3f}'.format(elapsed_time(start_time))) return solution
def abstrips_solve_from_task(sas_task, temp_dir=TEMP_DIR, clean=False, debug=False, hierarchy=[], **kwargs): # Like partial order planning in terms of precondition order # TODO: add achieve subgoal actions # TODO: most generic would be a heuristic on each state if hierarchy == SERIALIZE: return serialized_solve_from_task(sas_task, temp_dir=temp_dir, clean=clean, debug=debug, **kwargs) if not hierarchy: return solve_from_task(sas_task, temp_dir=temp_dir, clean=clean, debug=debug, **kwargs) start_time = time() plan, cost = None, INF with Verbose(debug): print('\n' + 50 * '-' + '\n') last_plan = [] for level in range(len(hierarchy) + 1): local_sas_task = deepcopy(sas_task) prune_hierarchy_pre_eff( local_sas_task, hierarchy[level:]) # TODO: break if no pruned add_subgoals(local_sas_task, last_plan) write_sas_task(local_sas_task, temp_dir) plan, cost = run_search(temp_dir, debug=True, **kwargs) if (level == len(hierarchy)) or (plan is None): # TODO: fall back on standard search break last_plan = [ name_from_action(action, args) for action, args in plan ] if clean: safe_rm_dir(temp_dir) print('Total runtime: {:.3f}'.format(elapsed_time(start_time))) return plan, cost
def process(self, stream_plan, action_plan, cost, complexity_limit, max_time=0): # TODO: detect infeasibility when an intermediate stream fails start_time = time.time() if is_plan(stream_plan): self.new_skeleton(stream_plan, action_plan, cost) self.greedily_process() elif stream_plan is INFEASIBLE: # Move this after process_complexity self.process_until_new() #if not is_plan(stream_plan): # print('Complexity:', complexity_limit) # self.process_complexity(complexity_limit) remaining_time = max_time - elapsed_time(start_time) print('Time:', remaining_time) self.timed_process(remaining_time)
def iterative_plan_streams(all_evaluations, externals, optimistic_solve_fn, complexity_limit, **effort_args): # Previously didn't have unique optimistic objects that could be constructed at arbitrary depths start_time = time.time() complexity_evals = {e: n for e, n in all_evaluations.items() if n.complexity <= complexity_limit} num_iterations = 0 while True: num_iterations += 1 results, exhausted = optimistic_process_streams(complexity_evals, externals, complexity_limit, **effort_args) opt_solution, final_depth = hierarchical_plan_streams( complexity_evals, externals, results, optimistic_solve_fn, complexity_limit, depth=0, constraints=None, **effort_args) stream_plan, action_plan, cost = opt_solution print('Attempt: {} | Results: {} | Depth: {} | Success: {} | Time: {:.3f}'.format( num_iterations, len(results), final_depth, is_plan(action_plan), elapsed_time(start_time))) if is_plan(action_plan): return OptSolution(stream_plan, action_plan, cost) if final_depth == 0: status = INFEASIBLE if exhausted else FAILED return OptSolution(status, status, cost)
def serialized_solve_from_task(sas_task, temp_dir=TEMP_DIR, clean=False, debug=False, hierarchy=[], **kwargs): # TODO: specify goal grouping / group by predicate & objects # TODO: version that solves for all disjuctive subgoals at once start_time = time() with Verbose(debug): print('\n' + 50 * '-' + '\n') subgoal_plan = [ sas_task.goal.pairs[:i + 1] for i in range(len(sas_task.goal.pairs)) ] plan, cost = plan_subgoals(sas_task, subgoal_plan, temp_dir, **kwargs) if clean: safe_rm_dir(temp_dir) print('Total runtime: {:.3f}'.format(elapsed_time(start_time))) return plan, cost
def solve_exhaustive(problem, max_time=300, verbose=True, **search_kwargs): """ Solves a PDDLStream problem by applying all possible streams and searching once Requires a finite max_time when infinitely many stream instances :param problem: a PDDLStream problem :param max_time: the maximum amount of time to apply streams :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 """ start_time = time.time() evaluations, goal_expression, domain, externals = parse_problem(problem) ensure_no_fluent_streams(externals) instantiator = Instantiator(evaluations, externals) while instantiator.stream_queue and (elapsed_time(start_time) < max_time): process_stream_queue(instantiator, evaluations, verbose=verbose) plan, cost = solve_finite(evaluations, goal_expression, domain, **search_kwargs) return revert_solution(plan, cost, evaluations)
def solve_exhaustive(problem, constraints=PlanConstraints(), unit_costs=False, max_time=300, verbose=False, **search_args): """ Solves a PDDLStream problem by applying all possible streams and searching once Requires a finite max_time when infinitely many stream instances :param problem: a PDDLStream problem :param constraints: PlanConstraints on the available solutions :param unit_costs: use unit action costs rather than numeric costs :param max_time: the maximum amount of time to apply streams :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 """ start_time = time.time() evaluations, goal_expression, domain, externals = parse_problem( problem, constraints=constraints, unit_costs=unit_costs) ensure_no_fluent_streams(externals) if UPDATE_STATISTICS: load_stream_statistics(externals) instantiator = Instantiator(externals, evaluations) while instantiator.stream_queue and (elapsed_time(start_time) < max_time): process_instance(instantiator, evaluations, instantiator.pop_stream(), verbose=verbose) process_function_queue(instantiator, evaluations, verbose=verbose) plan, cost = solve_finite(evaluations, goal_expression, domain, max_cost=constraints.max_cost, **search_args) if UPDATE_STATISTICS: write_stream_statistics(externals, verbose) return revert_solution(plan, cost, evaluations)
def solve_from_task(sas_task, temp_dir=TEMP_DIR, clean=False, debug=False, hierarchy=[], **search_args): # TODO: can solve using another planner and then still translate using FastDownward # Can apply plan constraints (skeleton constraints) here as well start_time = time() with Verbose(debug): print('\n' + 50 * '-' + '\n') write_sas_task(sas_task, temp_dir) solution = run_search(temp_dir, debug=True, **search_args) if clean: safe_rm_dir(temp_dir) print('Total runtime: {:.3f}'.format(elapsed_time(start_time))) #for axiom in sas_task.axioms: # # TODO: return the set of axioms here as well # var, value = axiom.effect # print(sas_task.variables.value_names[var]) # axiom.dump() return solution
def process_instance(store, domain, instance, disable=False): if instance.enumerated: return [], [] start_time = time.time() new_results, new_facts = instance.next_results(verbose=store.verbose) store.sample_time += elapsed_time(start_time) evaluations = store.evaluations if disable: instance.disable(evaluations, domain) for result in new_results: #add_certified(evaluations, result) # TODO: only add if the fact is actually new? complexity = INF if (not disable or result.external.is_special) else \ result.compute_complexity(evaluations) add_facts(evaluations, result.get_certified(), result=result, complexity=complexity) if disable: remove_blocked(evaluations, domain, instance, new_results) add_facts(evaluations, new_facts, result=UNKNOWN_EVALUATION, complexity=0) # TODO: record the instance return new_results, new_facts
def process(self, stream_plan, action_plan, cost, complexity_limit, max_time=0): # TODO: detect infeasibility when an intermediate stream fails start_time = time.time() if is_plan(stream_plan): self.new_skeleton(stream_plan, action_plan, cost) self.greedily_process() elif stream_plan is INFEASIBLE and not self.process_until_new( complexity_limit): # Move this after process_complexity return False #if not is_plan(stream_plan): # print('Complexity:', complexity_limit) # self.process_complexity(complexity_limit) remaining_time = max_time - elapsed_time(start_time) print('Remaining sampling time: {:.3f} seconds'.format(remaining_time)) self.timed_process(complexity_limit, remaining_time) # TODO: accelerate the best bindings #self.accelerate_best_bindings() return True
def get_sample_result(sample_strategy, learner, context, sim_world, collector, task, feature, evalfunc, saver, n_samples): learner.sample_strategy = sample_strategy learner.reset_sample() learner.set_world_feature(sim_world, feature) start_time = time.time() for i in range(n_samples): learner.sample(context) sample_time = elapsed_time(start_time) div = diversity(learner.sampled_xx, learner.func.param_idx) print('len samples = {} | diversity = {}'.format( len(learner.sampled_xx), div)) scores, plan_results = evaluate_samples(sim_world, collector, task, feature, learner.func, learner.sampled_xx, evalfunc, saver) good_samples = learner.sampled_xx[scores > 0] if good_samples.shape[0] >= 5: div_5 = diversity(good_samples[:5], learner.func.param_idx) num_samples_5 = 0 # find number of samples to get first good 5 samples cnt = 0 for s in scores: if s > 0: cnt += 1 num_samples_5 += 1 if cnt >= 5: break else: div_5 = None num_samples_5 = None precision = sum(scores > 0) / len(scores) return Result(learner.sampled_xx, sample_time, div, div_5, num_samples_5, scores, plan_results, precision, context, n_samples, learner.beta, learner.best_beta)
def solve_focused(problem, max_time=INF, stream_info={}, effort_weight=None, eager_iterations=1, visualize=False, verbose=True, **kwargs): # TODO: eager, negative, context, costs, bindings start_time = time.time() num_iterations = 0 best_plan = None best_cost = INF evaluations, goal_expression, domain, externals = parse_problem(problem) update_stream_info(externals, stream_info) eager_externals = filter(lambda e: e.info.eager, externals) constraint_solver = ConstraintSolver(problem[3]) disabled = [] if visualize: clear_visualizations() while elapsed_time(start_time) < max_time: num_iterations += 1 print('\nIteration: {} | Evaluations: {} | Cost: {} | Time: {:.3f}'. format(num_iterations, len(evaluations), best_cost, elapsed_time(start_time))) eagerly_evaluate(evaluations, eager_externals, eager_iterations, max_time - elapsed_time(start_time), verbose) # TODO: version that just calls one of the incremental algorithms instantiator = Instantiator(evaluations, externals) stream_results = [] while instantiator.stream_queue and (elapsed_time(start_time) < max_time): stream_results += optimistic_process_stream_queue(instantiator) # exhaustive_stream_plan | incremental_stream_plan | simultaneous_stream_plan | sequential_stream_plan | relaxed_stream_plan solve_stream_plan = sequential_stream_plan if effort_weight is None else simultaneous_stream_plan #solve_stream_plan = simultaneous_stream_plan stream_plan, action_plan, cost = solve_stream_plan( evaluations, goal_expression, domain, stream_results, **kwargs) print('Stream plan: {}\n' 'Action plan: {}'.format(stream_plan, action_plan)) if stream_plan is None: if not disabled: break reset_disabled(disabled) elif (len(stream_plan) == 0) and (cost < best_cost): best_plan = action_plan best_cost = cost break else: if visualize: create_visualizations(evaluations, stream_plan, num_iterations) constraint_facts = constraint_solver.solve( get_optimistic_constraints(evaluations, stream_plan), verbose=verbose) if constraint_facts: evaluations.update(map(evaluation_from_fact, constraint_facts)) else: #process_stream_plan(evaluations, stream_plan, disabled, verbose) process_immediate_stream_plan(evaluations, stream_plan, disabled, verbose) return revert_solution(best_plan, best_cost, evaluations)
def elapsed_time(self): return elapsed_time(self.start_time)
def update_statistics(self, start_time, results): overhead = elapsed_time(start_time) successes = len([r.is_successful() for r in results]) self.external.update_statistics(overhead, bool(successes)) self.results_history.append(results) self.successes += successes
def add_plan(self, plan, cost): # TODO: double-check that plan is a solution if is_plan(plan) and (cost < self.best_cost): self.solutions.append( Solution(plan, cost, elapsed_time(self.start_time)))