def _solve_impl(self, sp, output_solver_log=False, keep_solver_files=False, symbolic_solver_labels=False): """ Solve a stochastic program by building the extensive form and calling and a Pyomo solver. See the 'solve' method on the base class for additional keyword documentation. Args: sp: The stochastic program to solve. Pyro based managers are not accepted. All scenario models must be managed locally. output_solver_log (bool): Stream the solver output during the solve. keep_solver_files (bool): Retain temporary solver input and output files after the solve completes. symbolic_solver_labels (bool): Generate solver input files using human-readable symbols (makes debugging easier). Returns: A results object with information about the solution. """ if isinstance(sp, (ScenarioTreeManagerClientPyro, ScenarioTreeManagerSolverClientPyro)): raise TypeError("The EF solver does not handle " "Pyro-based scenario tree managers") orig_parents = {} if sp.scenario_tree.contains_bundles(): for scenario in sp.scenario_tree.scenarios: if scenario._instance._parent is not None: orig_parents[scenario] = scenario._instance._parent scenario._instance._parent = None assert not scenario._instance_objective.active scenario._instance_objective.activate() try: with ExtensiveFormAlgorithm(sp, self._options) as ef: ef.build_ef() ef.solve( output_solver_log=output_solver_log, keep_solver_files=keep_solver_files, symbolic_solver_labels=symbolic_solver_labels, check_status=False) finally: for scenario, parent in orig_parents.items(): scenario._instance._parent = parent assert scenario._instance_objective.active scenario._instance_objective.deactivate() results = SPSolverResults() results.objective = ef.objective results.bound = ef.bound results.status = ef.solution_status results.solver.status = ef.solver_status results.solver.termination_condition = ef.termination_condition results.solver.message = ef.termination_message results.solver.time = ef.time results.solver.pyomo_time = ef.pyomo_time results.xhat = None if ef.solution_status is not None: xhat = results.xhat = {} for stage in sp.scenario_tree.stages[:-1]: for node in stage.nodes: node_xhat = xhat[node.name] = {} reference_scenario = node.scenarios[0] node_x = reference_scenario._x[node.name] for id_ in node._variable_ids: node_xhat[id_] = node_x[id_] # TODO: stage costs #for stagenum, stage in enumerate(sp.scenario_tree.stages[:-1]): # cost_variable_name, cost_variable_index = \ # stage._cost_variable # stage_cost_obj = \ # instance.find_component(cost_variable_name)[cost_variable_index] # if not stage_cost_obj.is_expression_type(): # refvar = ComponentUID(stage_cost_obj,cuid_buffer=tmp).\ # find_component(reference_model) # refvar.value = stage_cost_obj.value # refvar.stale = stage_cost_obj.stale return results
def _solve_impl(self, sp, output_solver_log=False, verbose=False, logfile=None, **kwds): """ Solve a stochastic program with the SchurIpopt solver. See the 'solve' method on the base class for additional keyword documentation. Args: sp: The stochastic program to solve. output_solver_log (bool): Stream the solver output during the solve. logfile: The name of the logfile to save the solver output into. verbose: Report verbose status information to aid debugging. **kwds: Passed to the DDSIP file writer as I/O options (e.g., symbolic_solver_labels=True). Returns: A results object with information about the solution. """ # # Setup the SchurIpopt working directory # problem_list_filename = "PySP_Subproblems.txt" working_directory = self._create_tempdir("workdir", dir=os.getcwd()) if logfile is None: logfile = os.path.join(working_directory, "schuripopt.log") self._add_tempfile("logfile", logfile) else: self._files["logfile"] = logfile if verbose: print("Schuripopt solver working directory: %s" % (working_directory)) print("Schuripopt solver logfile: %s" % (logfile)) # # Launch SchurIpopt from the worker processes # (assumed to be launched together using mpirun) # status = self._launch_solver( sp, working_directory, logfile=logfile, output_solver_log=output_solver_log, verbose=verbose, io_options=kwds) objective = 0.0 solver_status = set() solver_message = set() termination_condition = set() solution_status = set() if status.solve_type == "bundles": assert sp.scenario_tree.contains_bundles() assert len(status.objective) == \ len(sp.scenario_tree.bundles) for bundle in sp.scenario_tree.bundles: if objective is not None: if isinstance(status.objective[bundle.name], UndefinedData): objective = None else: objective += bundle.probability * \ status.objective[bundle.name] solver_status.add(status.solver_status[bundle.name]) solver_message.add(status.solver_message[bundle.name]) termination_condition.add(status.termination_condition[bundle.name]) if isinstance(status.solution_status[bundle.name], UndefinedData): solution_status.add(None) else: solution_status.add(status.solution_status[bundle.name]) else: assert status.solve_type == "scenarios" assert len(status.objective) == \ len(sp.scenario_tree.scenarios) for scenario in sp.scenario_tree.scenarios: if objective is not None: if isinstance(status.objective[scenario.name], UndefinedData): objective = None else: objective += scenario.probability * \ status.objective[scenario.name] solver_status.add(status.solver_status[scenario.name]) solver_message.add(status.solver_message[scenario.name]) termination_condition.add(status.termination_condition[scenario.name]) if isinstance(status.solution_status[scenario.name], UndefinedData): solution_status.add(None) else: solution_status.add(status.solution_status[scenario.name]) assert len(solver_status) == 1 assert len(solver_message) == 1 assert len(termination_condition) == 1 assert len(solution_status) == 1 results = SPSolverResults() results.objective = None results.bound = None results.status = solution_status.pop() results.solver.status = solver_status.pop() results.solver.termination_condition = termination_condition.pop() results.solver.message = solver_message.pop() results.solver.time = max(status.solve_time.values()) results.solver.pyomo_time = \ max(status.pyomo_solve_time.values()) results.xhat = None if str(results.solver.status) == "ok" and \ str(results.solver.termination_condition) == "optimal": results.objective = objective xhat = results.xhat = {} for stage in sp.scenario_tree.stages[:-1]: for node in stage.nodes: worker_name = sp.get_worker_for_scenario( node.scenarios[0].name) node_solution = sp.invoke_function_on_worker( worker_name, "EXTERNAL_collect_solution", thisfile, invocation_type=InvocationType.Single, function_args=(node.name,)) node_xhat = xhat[node.name] = {} for id_ in node_solution: node_xhat[repr(id_)] = node_solution[id_][0] return results
def _solve_impl(self, sp, output_solver_log=False, keep_solver_files=False, symbolic_solver_labels=False): """ Solve a stochastic program by building the extensive form and calling and a Pyomo solver. See the 'solve' method on the base class for additional keyword documentation. Args: sp: The stochastic program to solve. Pyro based managers are not accepted. All scenario models must be managed locally. output_solver_log (bool): Stream the solver output during the solve. keep_solver_files (bool): Retain temporary solver input and output files after the solve completes. symbolic_solver_labels (bool): Generate solver input files using human-readable symbols (makes debugging easier). Returns: A results object with information about the solution. """ if isinstance(sp, (ScenarioTreeManagerClientPyro, ScenarioTreeManagerSolverClientPyro)): raise TypeError("The EF solver does not handle " "Pyro-based scenario tree managers") orig_parents = {} if sp.scenario_tree.contains_bundles(): for scenario in sp.scenario_tree.scenarios: if scenario._instance._parent is not None: orig_parents[scenario] = scenario._instance._parent scenario._instance._parent = None assert not scenario._instance_objective.active scenario._instance_objective.activate() try: with ExtensiveFormAlgorithm(sp, self._options) as ef: ef.build_ef() ef.solve(output_solver_log=output_solver_log, keep_solver_files=keep_solver_files, symbolic_solver_labels=symbolic_solver_labels, check_status=False) finally: for scenario, parent in orig_parents.items(): scenario._instance._parent = parent assert scenario._instance_objective.active scenario._instance_objective.deactivate() results = SPSolverResults() results.objective = ef.objective results.bound = ef.bound results.status = ef.solution_status results.solver.status = ef.solver_status results.solver.termination_condition = ef.termination_condition results.solver.message = ef.termination_message results.solver.time = ef.time results.solver.pyomo_time = ef.pyomo_time results.xhat = None if ef.solution_status is not None: xhat = results.xhat = {} for stage in sp.scenario_tree.stages[:-1]: for node in stage.nodes: node_xhat = xhat[node.name] = {} reference_scenario = node.scenarios[0] node_x = reference_scenario._x[node.name] for id_ in node._variable_ids: node_xhat[id_] = node_x[id_] # TODO: stage costs #for stagenum, stage in enumerate(sp.scenario_tree.stages[:-1]): # cost_variable_name, cost_variable_index = \ # stage._cost_variable # stage_cost_obj = \ # instance.find_component(cost_variable_name)[cost_variable_index] # if not stage_cost_obj.is_expression_type(): # refvar = ComponentUID(stage_cost_obj,cuid_buffer=tmp).\ # find_component(reference_model) # refvar.value = stage_cost_obj.value # refvar.stale = stage_cost_obj.stale return results
def _solve_impl(self, sp, rho=1.0, y_init=0.0, z_init=0.0, output_solver_log=False): if len(sp.scenario_tree.stages) > 2: raise ValueError("ADMM solver does not yet handle more " "than 2 time-stages") start_time = time.time() scenario_tree = sp.scenario_tree num_scenarios = len(scenario_tree.scenarios) num_stages = len(scenario_tree.stages) num_na_nodes = 0 num_na_variables = 0 num_na_continuous_variables = 0 num_na_binary_variables = 0 num_na_integer_variables = 0 for stage in sp.scenario_tree.stages[:-1]: for tree_node in stage.nodes: num_na_nodes += 1 num_na_variables += len(tree_node._standard_variable_ids) for id_ in tree_node._standard_variable_ids: if tree_node.is_variable_binary(id_): num_na_binary_variables += 1 elif tree_node.is_variable_integer(id_): num_na_integer_variables += 1 else: num_na_continuous_variables += 1 # print("-"*20) # print("Problem Statistics".center(20)) # print("-"*20) # print("Total number of scenarios.................: %10s" # % (num_scenarios)) # print("Total number of time stages...............: %10s" # % (num_stages)) # print("Total number of non-anticipative nodes....: %10s" # % (num_na_nodes)) # print("Total number of non-anticipative variables: %10s\n#" # " continuous: %10s\n#" # " binary: %10s\n#" # " integer: %10s" # % (num_na_variables, # num_na_continuous_variables, # num_na_binary_variables, # num_na_integer_variables)) rel_tol_primal = \ self.get_option("primal_residual_relative_tolerance") rel_tol_dual = \ self.get_option("dual_residual_relative_tolerance") max_iterations = \ self.get_option("max_iterations") self.objective_history = OrderedDict() self.primal_residual_history = OrderedDict() self.dual_residual_history = OrderedDict() self.iterations = 0 if output_solver_log: print("") label_cols = ("{0:^4} {1:>16} {2:>8} {3:>8} {4:>12}".format( "iter", "objective", "pr_res", "du_res", "lg(||rho||)")) with ADMMAlgorithm(sp, self._options) as admm: rho, x, y, z = admm.initialize_algorithm_data(rho_init=rho, y_init=y_init, z_init=z_init) rho_strategy = RhoStrategyFactory(self.get_option("rho_strategy"), self._options) rho_strategy.initialize(sp, x, y, z, rho) for i in xrange(max_iterations): objective = \ admm.run_x_update(x, y, z, rho) (unscaled_primal_residual, unscaled_dual_residual, x_scale, z_scale) = \ admm.run_z_update(x, y, z, rho) y_scale = \ admm.run_y_update(x, y, z, rho) # we've completed another iteration self.iterations += 1 # check for convergence primal_rel_scale = max(1.0, x_scale, z_scale) dual_rel_scale = max(1.0, y_scale) primal_residual = unscaled_primal_residual / \ math.sqrt(num_scenarios) / \ primal_rel_scale dual_residual = unscaled_dual_residual / \ math.sqrt(num_na_variables) / \ dual_rel_scale self.objective_history[i] = \ objective self.primal_residual_history[i] = \ primal_residual self.dual_residual_history[i] = \ dual_residual if output_solver_log: if (i % 10) == 0: print(label_cols) print("%4d %16.7e %8.2e %8.2e %12.2e" % (i, objective, primal_residual, dual_residual, math.log(admm.compute_nodevector_norm(rho)))) if (primal_residual < rel_tol_primal) and \ (dual_residual < rel_tol_dual): if output_solver_log: print("\nNumber of Iterations....: %s" % (self.iterations)) break else: rho_strategy.update_rho(sp, x, y, z, rho) else: if output_solver_log: print("\nMaximum number of iterations reached: %s" % (max_iterations)) if output_solver_log: print("") print(" {0:^24} {1:^24}".\ format("(scaled)", "(unscaled)")) print("Objective..........: {0:^24} {1:^24.16e}".\ format("-", objective)) print("Primal residual....: {0:^24.16e} {1:^24.16e}".\ format(primal_residual, unscaled_primal_residual)) print("Dual residual......: {0:^24.16e} {1:^24.16e}".\ format(dual_residual, unscaled_dual_residual)) unscaled_err = unscaled_primal_residual + \ unscaled_dual_residual err = primal_residual + dual_residual print("Overall error......: {0:^24.16e} {1:^24.16e}".\ format(err, unscaled_err)) results = SPSolverResults() results.objective = objective results.xhat = z return results
def _solve_impl(self, sp, rho=1.0, y_init=0.0, z_init=0.0, output_solver_log=False): if len(sp.scenario_tree.stages) > 2: raise ValueError( "ADMM solver does not yet handle more " "than 2 time-stages") start_time = time.time() scenario_tree = sp.scenario_tree num_scenarios = len(scenario_tree.scenarios) num_stages = len(scenario_tree.stages) num_na_nodes = 0 num_na_variables = 0 num_na_continuous_variables = 0 num_na_binary_variables = 0 num_na_integer_variables = 0 for stage in sp.scenario_tree.stages[:-1]: for tree_node in stage.nodes: num_na_nodes += 1 num_na_variables += len(tree_node._standard_variable_ids) for id_ in tree_node._standard_variable_ids: if tree_node.is_variable_binary(id_): num_na_binary_variables += 1 elif tree_node.is_variable_integer(id_): num_na_integer_variables += 1 else: num_na_continuous_variables += 1 # print("-"*20) # print("Problem Statistics".center(20)) # print("-"*20) # print("Total number of scenarios.................: %10s" # % (num_scenarios)) # print("Total number of time stages...............: %10s" # % (num_stages)) # print("Total number of non-anticipative nodes....: %10s" # % (num_na_nodes)) # print("Total number of non-anticipative variables: %10s\n#" # " continuous: %10s\n#" # " binary: %10s\n#" # " integer: %10s" # % (num_na_variables, # num_na_continuous_variables, # num_na_binary_variables, # num_na_integer_variables)) rel_tol_primal = \ self.get_option("primal_residual_relative_tolerance") rel_tol_dual = \ self.get_option("dual_residual_relative_tolerance") max_iterations = \ self.get_option("max_iterations") self.objective_history = OrderedDict() self.primal_residual_history = OrderedDict() self.dual_residual_history = OrderedDict() self.iterations = 0 if output_solver_log: print("") label_cols = ("{0:^4} {1:>16} {2:>8} {3:>8} {4:>12}".format( "iter","objective","pr_res","du_res","lg(||rho||)")) with ADMMAlgorithm(sp, self._options) as admm: rho, x, y, z = admm.initialize_algorithm_data(rho_init=rho, y_init=y_init, z_init=z_init) rho_strategy = RhoStrategyFactory( self.get_option("rho_strategy"), self._options) rho_strategy.initialize(sp, x, y, z, rho) for i in xrange(max_iterations): objective = \ admm.run_x_update(x, y, z, rho) (unscaled_primal_residual, unscaled_dual_residual, x_scale, z_scale) = \ admm.run_z_update(x, y, z, rho) y_scale = \ admm.run_y_update(x, y, z, rho) # we've completed another iteration self.iterations += 1 # check for convergence primal_rel_scale = max(1.0, x_scale, z_scale) dual_rel_scale = max(1.0, y_scale) primal_residual = unscaled_primal_residual / \ math.sqrt(num_scenarios) / \ primal_rel_scale dual_residual = unscaled_dual_residual / \ math.sqrt(num_na_variables) / \ dual_rel_scale self.objective_history[i] = \ objective self.primal_residual_history[i] = \ primal_residual self.dual_residual_history[i] = \ dual_residual if output_solver_log: if (i % 10) == 0: print(label_cols) print("%4d %16.7e %8.2e %8.2e %12.2e" % (i, objective, primal_residual, dual_residual, math.log(admm.compute_nodevector_norm(rho)))) if (primal_residual < rel_tol_primal) and \ (dual_residual < rel_tol_dual): if output_solver_log: print("\nNumber of Iterations....: %s" % (self.iterations)) break else: rho_strategy.update_rho(sp, x, y, z, rho) else: if output_solver_log: print("\nMaximum number of iterations reached: %s" % (max_iterations)) if output_solver_log: print("") print(" {0:^24} {1:^24}".\ format("(scaled)", "(unscaled)")) print("Objective..........: {0:^24} {1:^24.16e}".\ format("-", objective)) print("Primal residual....: {0:^24.16e} {1:^24.16e}".\ format(primal_residual, unscaled_primal_residual)) print("Dual residual......: {0:^24.16e} {1:^24.16e}".\ format(dual_residual, unscaled_dual_residual)) unscaled_err = unscaled_primal_residual + \ unscaled_dual_residual err = primal_residual + dual_residual print("Overall error......: {0:^24.16e} {1:^24.16e}".\ format(err, unscaled_err)) results = SPSolverResults() results.objective = objective results.xhat = z return results
def _solve_impl(self, sp, output_solver_log=False, verbose=False, logfile=None, **kwds): """ Solve a stochastic program with the SchurIpopt solver. See the 'solve' method on the base class for additional keyword documentation. Args: sp: The stochastic program to solve. output_solver_log (bool): Stream the solver output during the solve. logfile: The name of the logfile to save the solver output into. verbose: Report verbose status information to aid debugging. **kwds: Passed to the DDSIP file writer as I/O options (e.g., symbolic_solver_labels=True). Returns: A results object with information about the solution. """ # # Setup the SchurIpopt working directory # problem_list_filename = "PySP_Subproblems.txt" working_directory = self._create_tempdir("workdir", dir=os.getcwd()) if logfile is None: logfile = os.path.join(working_directory, "schuripopt.log") self._add_tempfile("logfile", logfile) else: self._files["logfile"] = logfile if verbose: print("Schuripopt solver working directory: %s" % (working_directory)) print("Schuripopt solver logfile: %s" % (logfile)) # # Launch SchurIpopt from the worker processes # (assumed to be launched together using mpirun) # status = self._launch_solver( sp, working_directory, logfile=logfile, output_solver_log=output_solver_log, verbose=verbose, io_options=kwds) objective = 0.0 solver_status = set() solver_message = set() termination_condition = set() solution_status = set() if status.solve_type == "bundles": assert sp.scenario_tree.contains_bundles() assert len(status.objective) == \ len(sp.scenario_tree.bundles) for bundle in sp.scenario_tree.bundles: if objective is not None: if isinstance(status.objective[bundle.name], UndefinedData): objective = None else: objective += bundle.probability * \ status.objective[bundle.name] solver_status.add(status.solver_status[bundle.name]) solver_message.add(status.solver_message[bundle.name]) termination_condition.add(status.termination_condition[bundle.name]) if isinstance(status.solution_status[bundle.name], UndefinedData): solution_status.add(None) else: solution_status.add(status.solution_status[bundle.name]) else: assert status.solve_type == "scenarios" assert len(status.objective) == \ len(sp.scenario_tree.scenarios) for scenario in sp.scenario_tree.scenarios: if objective is not None: if isinstance(status.objective[scenario.name], UndefinedData): objective = None else: objective += scenario.probability * \ status.objective[scenario.name] solver_status.add(status.solver_status[scenario.name]) solver_message.add(status.solver_message[scenario.name]) termination_condition.add(status.termination_condition[scenario.name]) if isinstance(status.solution_status[scenario.name], UndefinedData): solution_status.add(None) else: solution_status.add(status.solution_status[scenario.name]) assert len(solver_status) == 1 assert len(solver_message) == 1 assert len(termination_condition) == 1 assert len(solution_status) == 1 results = SPSolverResults() results.objective = None results.bound = None results.status = solution_status.pop() results.solver.status = solver_status.pop() results.solver.termination_condition = termination_condition.pop() results.solver.message = solver_message.pop() results.solver.time = max(status.solve_time.values()) results.solver.pyomo_time = \ max(status.pyomo_solve_time.values()) results.xhat = None if str(results.solver.status) == "ok" and \ str(results.solver.termination_condition) == "optimal": results.objective = objective xhat = results.xhat = {} for stage in sp.scenario_tree.stages[:-1]: for node in stage.nodes: worker_name = sp.get_worker_for_scenario( node.scenarios[0].name) node_solution = sp.invoke_function_on_worker( worker_name, "EXTERNAL_collect_solution", thisfile, invocation_type=InvocationType.Single, function_args=(node.name,)) node_xhat = xhat[node.name] = {} for id_ in node_solution: node_xhat[repr(id_)] = node_solution[id_][0] return results