def solve(self, solver, io, io_options, solver_options, symbolic_labels, load_solutions): """ Optimize the model """ assert self.model is not None opt = SolverFactory(solver, solver_io=io) opt.options.update(solver_options) if io == 'nl': assert opt.problem_format() == ProblemFormat.nl elif io == 'lp': assert opt.problem_format() == ProblemFormat.cpxlp elif io == 'mps': assert opt.problem_format() == ProblemFormat.mps #elif io == 'python': # print opt.problem_format() # assert opt.problem_format() is None try: if isinstance(opt, PersistentSolver): opt.compile_instance(self.model, symbolic_solver_labels=symbolic_labels) if opt.warm_start_capable(): results = opt.solve( self.model, symbolic_solver_labels=symbolic_labels, warmstart=True, load_solutions=load_solutions, **io_options) else: results = opt.solve( self.model, symbolic_solver_labels=symbolic_labels, load_solutions=load_solutions, **io_options) return opt, results finally: opt.deactivate() del opt return None, None
def solve(self, solver, io, io_options, solver_options, symbolic_labels, load_solutions): """ Optimize the model """ assert self.model is not None opt = SolverFactory(solver, solver_io=io) opt.options.update(solver_options) if io == 'nl': assert opt.problem_format() == ProblemFormat.nl elif io == 'lp': assert opt.problem_format() == ProblemFormat.cpxlp elif io == 'mps': assert opt.problem_format() == ProblemFormat.mps #elif io == 'python': # print opt.problem_format() # assert opt.problem_format() is None try: if isinstance(opt, PersistentSolver): opt.compile_instance(self.model, symbolic_solver_labels=symbolic_labels) if opt.warm_start_capable(): results = opt.solve(self.model, symbolic_solver_labels=symbolic_labels, warmstart=True, load_solutions=load_solutions, **io_options) else: results = opt.solve(self.model, symbolic_solver_labels=symbolic_labels, load_solutions=load_solutions, **io_options) return opt, results finally: opt.deactivate() del opt return None, None
class ExtensiveFormAlgorithm(PySPConfiguredObject): @classmethod def _declare_options(cls, options=None): if options is None: options = PySPConfigBlock() safe_declare_unique_option( options, "cvar_weight", PySPConfigValue( 1.0, domain=_domain_nonnegative, description=( "The weight associated with the CVaR term in " "the risk-weighted objective " "formulation. If the weight is 0, then " "*only* a non-weighted CVaR cost will appear " "in the EF objective - the expected cost " "component will be dropped. Default is 1.0." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "generate_weighted_cvar", PySPConfigValue( False, domain=bool, description=( "Add a weighted CVaR term to the " "primary objective. Default is False." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "risk_alpha", PySPConfigValue( 0.95, domain=_domain_unit_interval, description=( "The probability threshold associated with " "CVaR (or any future) risk-oriented " "performance metrics. Default is 0.95." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "cc_alpha", PySPConfigValue( 0.0, domain=_domain_unit_interval, description=( "The probability threshold associated with a " "chance constraint. The RHS will be one " "minus this value. Default is 0." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "cc_indicator_var", PySPConfigValue( None, domain=_domain_must_be_str, description=( "The name of the binary variable to be used " "to construct a chance constraint. Default " "is None, which indicates no chance " "constraint." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_common_option(options, "solver") safe_declare_common_option(options, "solver_io") safe_declare_common_option(options, "solver_manager") safe_declare_common_option(options, "solver_options") safe_declare_common_option(options, "disable_warmstart") safe_declare_common_option(options, "solver_manager_pyro_host") safe_declare_common_option(options, "solver_manager_pyro_port") safe_declare_common_option(options, "solver_manager_pyro_shutdown") safe_declare_common_option(options, "verbose", ap_group=_ef_group_label) safe_declare_common_option(options, "output_times", ap_group=_ef_group_label) safe_declare_common_option(options, "output_solver_results", ap_group=_ef_group_label) return options def __enter__(self): return self def __exit__(self, *args): self.close() def close(self): self.destroy_ef() if self._solver_manager is not None: if isinstance(self._solver_manager, pyomo.solvers.plugins.smanager.\ pyro.SolverManager_Pyro): if self.get_option("pyro_shutdown_workers"): self._solver_manager.shutdown_workers() self._solver_manager = None self._manager = None def __init__(self, manager, *args, **kwds): import pyomo.solvers.plugins.smanager.pyro super(ExtensiveFormAlgorithm, self).__init__(*args, **kwds) # TODO: after PH moves over to the new code #if not isinstance(manager, ScenarioTreeManager): # raise TypeError("ExtensiveFormAlgorithm requires an instance of the " # "ScenarioTreeManager interface as the " # "second argument") if not manager.initialized: raise ValueError("ExtensiveFormAlgorithm requires a scenario tree " "manager that has been fully initialized") self._manager = manager self.instance = None self._solver_manager = None self._solver = None # The following attributes will be modified by the # solve() method. For users that are scripting, these # can be accessed after the solve() method returns. # They will be reset each time solve() is called. ############################################ self.objective = None self.gap = None self.termination_condition = None self.termination_message = None self.solver_status = None self.solution_status = None self.solver_results = None self.time = None self.pyomo_time = None ############################################ # apparently the SolverFactory does not have sane # behavior when the solver name is None if self.get_option("solver") is None: raise ValueError("The 'solver' option can not be None") self._solver = SolverFactory(self.get_option("solver"), solver_io=self.get_option("solver_io")) if isinstance(self._solver, UnknownSolver): raise ValueError("Failed to create solver of type="+ self.get_option("solver")+ " for use in extensive form solve") solver_manager_type = self.get_option("solver_manager") if solver_manager_type == "phpyro": print("*** WARNING ***: PHPyro is not a supported solver " "manager type for the extensive-form solver. " "Falling back to serial.") solver_manager_type = 'serial' self._solver_manager = SolverManagerFactory( solver_manager_type, host=self.get_option("solver_manager_pyro_host"), port=self.get_option("solver_manager_pyro_port")) if self._solver_manager is None: raise ValueError("Failed to create solver manager of type=" +self.get_option("solver")+ " for use in extensive form solve") def build_ef(self): self.destroy_ef() if self.get_option("verbose"): print("Creating extensive form instance") start_time = time.time() # then validate the associated parameters. generate_weighted_cvar = False cvar_weight = None risk_alpha = None if self.get_option("generate_weighted_cvar"): generate_weighted_cvar = True cvar_weight = self.get_option("cvar_weight") risk_alpha = self.get_option("risk_alpha") self.instance = create_ef_instance( self._manager.scenario_tree, verbose_output=self.get_option("verbose"), generate_weighted_cvar=generate_weighted_cvar, cvar_weight=cvar_weight, risk_alpha=risk_alpha, cc_indicator_var_name=self.get_option("cc_indicator_var"), cc_alpha=self.get_option("cc_alpha")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to construct extensive form instance=%.2f seconds" %(time.time() - start_time)) def destroy_ef(self): if self.instance is not None: for scenario in self._manager.scenario_tree.scenarios: self.instance.del_component(scenario.name) scenario._instance_objective.activate() self.instance = None def write(self, filename): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") suf = os.path.splitext(filename)[1] if suf not in ['.nl','.lp','.mps']: if self._solver.problem_format() == ProblemFormat.cpxlp: filename += '.lp' elif self._solver.problem_format() == ProblemFormat.nl: filename += '.nl' elif self._solver.problem_format() == ProblemFormat.mps: filename += '.mps' else: raise ValueError("Could not determine output file format. " "No recognized ending suffix was provided " "and no format was indicated was by the " "--solver-io option.") start_time = time.time() print("Writing extensive form to file="+filename) smap_id = write_ef(self.instance, filename, self.get_option("symbolic_solver_labels")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to write output file=%.2f seconds" % (time.time() - start_time)) return filename, smap_id def solve(self, check_status=True, exception_on_failure=True, output_solver_log=False, symbolic_solver_labels=False, keep_solver_files=False, io_options=None): # TODO: Does this import need to be delayed because # it is in a plugins subdirectory from pyomo.solvers.plugins.solvers.persistent_solver import \ PersistentSolver if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") start_time = time.time() if self.get_option("verbose"): print("Queuing extensive form solve") self.objective = None self.gap = None self.bound = None self.termination_condition = None self.termination_message = None self.solver_status = None self.solution_status = None self.solver_results = None self.time = None self.pyomo_time = None if isinstance(self._solver, PersistentSolver): self._solver.compile_instance( self.instance, symbolic_solver_labels=symbolic_solver_labels) solve_kwds = {} solve_kwds['load_solutions'] = False if keep_solver_files: solve_kwds['keepfiles'] = True if symbolic_solver_labels: solve_kwds['symbolic_solver_labels'] = True if output_solver_log: solve_kwds['tee'] = True solver_options = self.get_option("solver_options") if len(solver_options) > 0: if type(solver_options) is tuple: solve_kwds["options"] = {} for name_val in solver_options: assert "=" in name_val name, val = name_val.split("=") solve_kwds["options"][name.strip()] = val.strip() else: solve_kwds["options"] = solver_options if io_options is not None: solve_kwds.update(io_options) self.objective_sense = \ find_active_objective(self.instance).sense if (not self.get_option("disable_warmstart")) and \ (self._solver.warm_start_capable()): action_handle = self._solver_manager.queue(self.instance, opt=self._solver, warmstart=True, **solve_kwds) else: action_handle = self._solver_manager.queue(self.instance, opt=self._solver, **solve_kwds) if self.get_option("verbose"): print("Waiting for extensive form solve") results = self._solver_manager.wait_for(action_handle) if self.get_option("verbose"): print("Done with extensive form solve - loading results") if self.get_option("output_solver_results"): print("Results for ef:") results.write(num=1) self.solver_results = results if hasattr(results.solver,"user_time") and \ (not isinstance(results.solver.user_time, UndefinedData)) and \ (results.solver.user_time is not None): # the solve time might be a string, or might # not be - we eventually would like more # consistency on this front from the solver # plugins. self.time = \ float(results.solver.user_time) elif hasattr(results.solver,"time"): self.time = \ float(results.solver.time) else: self.time = None if hasattr(results,"pyomo_solve_time"): self.pyomo_time = \ results.pyomo_solve_time else: self.pyomo_time = None self.termination_condition = \ results.solver.termination_condition self.termination_message = None if hasattr(results.solver,"termination_message"): self.termination_message = results.solver.termination_message elif hasattr(results.solver,"message"): self.termination_message = results.solver.message self.solver_status = \ results.solver.status if len(results.solution) > 0: assert len(results.solution) == 1 results_sm = results._smap self.instance.solutions.load_from(results) solution0 = results.solution(0) if hasattr(solution0, "gap") and \ (solution0.gap is not None) and \ (not isinstance(solution0.gap, UndefinedData)): self.gap = float(solution0.gap) else: self.gap = None self.solution_status = solution0.status if self.get_option("verbose"): print("Storing solution in scenario tree") for scenario in self._manager.scenario_tree.scenarios: scenario.update_solution_from_instance() self._manager.scenario_tree.snapshotSolutionFromScenarios() self.objective = self._manager.scenario_tree.\ findRootNode().\ computeExpectedNodeCost() if self.gap is not None: if self.objective_sense == pyomo.core.base.minimize: self.bound = self.objective - self.gap else: self.bound = self.objective + self.gap else: self.objective = None self.gap = None self.bound = None self.solution_status = None failure = False if check_status: if not ((self.solution_status == SolutionStatus.optimal) or \ (self.solution_status == SolutionStatus.feasible)): failure = True if self.get_option("verbose") or \ exception_on_failure: msg = ("EF solve failed solution status check:\n" "Solver Status: %s\n" "Termination Condition: %s\n" "Solution Status: %s\n" % (self.solver_status, self.termination_condition, self.solution_status)) if self.get_option("verbose"): print(msg) if exception_on_failure: raise RuntimeError(msg) else: if self.get_option("verbose"): print("EF solve completed. Skipping status check.") if self.get_option("verbose") or self.get_option("output_times"): print("Time to solve and load results for the " "extensive form=%.2f seconds" % (time.time()-start_time)) return failure
class ExtensiveFormAlgorithm(PySPConfiguredObject): @classmethod def _declare_options(cls, options=None): if options is None: options = PySPConfigBlock() safe_declare_unique_option( options, "cvar_weight", PySPConfigValue( 1.0, domain=_domain_nonnegative, description=("The weight associated with the CVaR term in " "the risk-weighted objective " "formulation. If the weight is 0, then " "*only* a non-weighted CVaR cost will appear " "in the EF objective - the expected cost " "component will be dropped. Default is 1.0."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "generate_weighted_cvar", PySPConfigValue( False, domain=bool, description=("Add a weighted CVaR term to the " "primary objective. Default is False."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "risk_alpha", PySPConfigValue( 0.95, domain=_domain_unit_interval, description=("The probability threshold associated with " "CVaR (or any future) risk-oriented " "performance metrics. Default is 0.95."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "cc_alpha", PySPConfigValue( 0.0, domain=_domain_unit_interval, description=("The probability threshold associated with a " "chance constraint. The RHS will be one " "minus this value. Default is 0."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( options, "cc_indicator_var", PySPConfigValue( None, domain=_domain_must_be_str, description=("The name of the binary variable to be used " "to construct a chance constraint. Default " "is None, which indicates no chance " "constraint."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_common_option(options, "solver") safe_declare_common_option(options, "solver_io") safe_declare_common_option(options, "solver_manager") safe_declare_common_option(options, "solver_options") safe_declare_common_option(options, "disable_warmstart") safe_declare_common_option(options, "solver_manager_pyro_host") safe_declare_common_option(options, "solver_manager_pyro_port") safe_declare_common_option(options, "solver_manager_pyro_shutdown") safe_declare_common_option(options, "verbose", ap_group=_ef_group_label) safe_declare_common_option(options, "output_times", ap_group=_ef_group_label) safe_declare_common_option(options, "output_solver_results", ap_group=_ef_group_label) return options def __enter__(self): return self def __exit__(self, *args): self.close() def close(self): self.destroy_ef() if self._solver_manager is not None: if isinstance(self._solver_manager, pyomo.solvers.plugins.smanager.\ pyro.SolverManager_Pyro): if self.get_option("pyro_shutdown_workers"): self._solver_manager.shutdown_workers() self._solver_manager = None self._manager = None def __init__(self, manager, *args, **kwds): import pyomo.solvers.plugins.smanager.pyro super(ExtensiveFormAlgorithm, self).__init__(*args, **kwds) # TODO: after PH moves over to the new code #if not isinstance(manager, ScenarioTreeManager): # raise TypeError("ExtensiveFormAlgorithm requires an instance of the " # "ScenarioTreeManager interface as the " # "second argument") if not manager.initialized: raise ValueError("ExtensiveFormAlgorithm requires a scenario tree " "manager that has been fully initialized") self._manager = manager self.instance = None self._solver_manager = None self._solver = None # The following attributes will be modified by the # solve() method. For users that are scripting, these # can be accessed after the solve() method returns. # They will be reset each time solve() is called. ############################################ self.objective = None self.gap = None self.termination_condition = None self.termination_message = None self.solver_status = None self.solution_status = None self.solver_results = None self.time = None self.pyomo_time = None ############################################ # apparently the SolverFactory does not have sane # behavior when the solver name is None if self.get_option("solver") is None: raise ValueError("The 'solver' option can not be None") self._solver = SolverFactory(self.get_option("solver"), solver_io=self.get_option("solver_io")) if isinstance(self._solver, UnknownSolver): raise ValueError("Failed to create solver of type=" + self.get_option("solver") + " for use in extensive form solve") solver_manager_type = self.get_option("solver_manager") if solver_manager_type == "phpyro": print("*** WARNING ***: PHPyro is not a supported solver " "manager type for the extensive-form solver. " "Falling back to serial.") solver_manager_type = 'serial' self._solver_manager = SolverManagerFactory( solver_manager_type, host=self.get_option("solver_manager_pyro_host"), port=self.get_option("solver_manager_pyro_port")) if self._solver_manager is None: raise ValueError("Failed to create solver manager of type=" + self.get_option("solver") + " for use in extensive form solve") def build_ef(self): self.destroy_ef() if self.get_option("verbose"): print("Creating extensive form instance") start_time = time.time() # then validate the associated parameters. generate_weighted_cvar = False cvar_weight = None risk_alpha = None if self.get_option("generate_weighted_cvar"): generate_weighted_cvar = True cvar_weight = self.get_option("cvar_weight") risk_alpha = self.get_option("risk_alpha") self.instance = create_ef_instance( self._manager.scenario_tree, verbose_output=self.get_option("verbose"), generate_weighted_cvar=generate_weighted_cvar, cvar_weight=cvar_weight, risk_alpha=risk_alpha, cc_indicator_var_name=self.get_option("cc_indicator_var"), cc_alpha=self.get_option("cc_alpha")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to construct extensive form instance=%.2f seconds" % (time.time() - start_time)) def destroy_ef(self): if self.instance is not None: for scenario in self._manager.scenario_tree.scenarios: self.instance.del_component(scenario.name) scenario._instance_objective.activate() self.instance = None def write(self, filename): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") suf = os.path.splitext(filename)[1] if suf not in ['.nl', '.lp', '.mps']: if self._solver.problem_format() == ProblemFormat.cpxlp: filename += '.lp' elif self._solver.problem_format() == ProblemFormat.nl: filename += '.nl' elif self._solver.problem_format() == ProblemFormat.mps: filename += '.mps' else: raise ValueError("Could not determine output file format. " "No recognized ending suffix was provided " "and no format was indicated was by the " "--solver-io option.") start_time = time.time() print("Writing extensive form to file=" + filename) smap_id = write_ef(self.instance, filename, self.get_option("symbolic_solver_labels")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to write output file=%.2f seconds" % (time.time() - start_time)) return filename, smap_id def solve(self, check_status=True, exception_on_failure=True, output_solver_log=False, symbolic_solver_labels=False, keep_solver_files=False, io_options=None): # TODO: Does this import need to be delayed because # it is in a plugins subdirectory from pyomo.solvers.plugins.solvers.persistent_solver import \ PersistentSolver if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") start_time = time.time() if self.get_option("verbose"): print("Queuing extensive form solve") self.objective = None self.gap = None self.bound = None self.termination_condition = None self.termination_message = None self.solver_status = None self.solution_status = None self.solver_results = None self.time = None self.pyomo_time = None if isinstance(self._solver, PersistentSolver): self._solver.compile_instance( self.instance, symbolic_solver_labels=symbolic_solver_labels) solve_kwds = {} solve_kwds['load_solutions'] = False if keep_solver_files: solve_kwds['keepfiles'] = True if symbolic_solver_labels: solve_kwds['symbolic_solver_labels'] = True if output_solver_log: solve_kwds['tee'] = True solver_options = self.get_option("solver_options") if len(solver_options) > 0: if type(solver_options) is tuple: solve_kwds["options"] = {} for name_val in solver_options: assert "=" in name_val name, val = name_val.split("=") solve_kwds["options"][name.strip()] = val.strip() else: solve_kwds["options"] = solver_options if io_options is not None: solve_kwds.update(io_options) self.objective_sense = \ find_active_objective(self.instance).sense if (not self.get_option("disable_warmstart")) and \ (self._solver.warm_start_capable()): action_handle = self._solver_manager.queue(self.instance, opt=self._solver, warmstart=True, **solve_kwds) else: action_handle = self._solver_manager.queue(self.instance, opt=self._solver, **solve_kwds) if self.get_option("verbose"): print("Waiting for extensive form solve") results = self._solver_manager.wait_for(action_handle) if self.get_option("verbose"): print("Done with extensive form solve - loading results") if self.get_option("output_solver_results"): print("Results for ef:") results.write(num=1) self.solver_results = results if hasattr(results.solver,"user_time") and \ (not isinstance(results.solver.user_time, UndefinedData)) and \ (results.solver.user_time is not None): # the solve time might be a string, or might # not be - we eventually would like more # consistency on this front from the solver # plugins. self.time = \ float(results.solver.user_time) elif hasattr(results.solver, "time"): self.time = \ float(results.solver.time) else: self.time = None if hasattr(results, "pyomo_solve_time"): self.pyomo_time = \ results.pyomo_solve_time else: self.pyomo_time = None self.termination_condition = \ results.solver.termination_condition self.termination_message = None if hasattr(results.solver, "termination_message"): self.termination_message = results.solver.termination_message elif hasattr(results.solver, "message"): self.termination_message = results.solver.message self.solver_status = \ results.solver.status if len(results.solution) > 0: assert len(results.solution) == 1 results_sm = results._smap self.instance.solutions.load_from(results) solution0 = results.solution(0) if hasattr(solution0, "gap") and \ (solution0.gap is not None) and \ (not isinstance(solution0.gap, UndefinedData)): self.gap = float(solution0.gap) else: self.gap = None self.solution_status = solution0.status if self.get_option("verbose"): print("Storing solution in scenario tree") for scenario in self._manager.scenario_tree.scenarios: scenario.update_solution_from_instance() self._manager.scenario_tree.snapshotSolutionFromScenarios() self.objective = self._manager.scenario_tree.\ findRootNode().\ computeExpectedNodeCost() if self.gap is not None: if self.objective_sense == pyomo.core.base.minimize: self.bound = self.objective - self.gap else: self.bound = self.objective + self.gap else: self.objective = None self.gap = None self.bound = None self.solution_status = None failure = False if check_status: if not ((self.solution_status == SolutionStatus.optimal) or \ (self.solution_status == SolutionStatus.feasible)): failure = True if self.get_option("verbose") or \ exception_on_failure: msg = ("EF solve failed solution status check:\n" "Solver Status: %s\n" "Termination Condition: %s\n" "Solution Status: %s\n" % (self.solver_status, self.termination_condition, self.solution_status)) if self.get_option("verbose"): print(msg) if exception_on_failure: raise RuntimeError(msg) else: if self.get_option("verbose"): print("EF solve completed. Skipping status check.") if self.get_option("verbose") or self.get_option("output_times"): print("Time to solve and load results for the " "extensive form=%.2f seconds" % (time.time() - start_time)) return failure
class ExtensiveFormAlgorithm(PySPConfiguredObject): _declared_options = \ PySPConfigBlock("Options declared for the " "ExtensiveFormAlgorithm class") safe_declare_unique_option( _declared_options, "cvar_weight", PySPConfigValue( 1.0, domain=_domain_nonnegative, description=( "The weight associated with the CVaR term in " "the risk-weighted objective " "formulation. If the weight is 0, then " "*only* a non-weighted CVaR cost will appear " "in the EF objective - the expected cost " "component will be dropped. Default is 1.0." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "generate_weighted_cvar", PySPConfigValue( False, domain=bool, description=( "Add a weighted CVaR term to the " "primary objective. Default is False." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "risk_alpha", PySPConfigValue( 0.95, domain=_domain_unit_interval, description=( "The probability threshold associated with " "CVaR (or any future) risk-oriented " "performance metrics. Default is 0.95." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "cc_alpha", PySPConfigValue( 0.0, domain=_domain_unit_interval, description=( "The probability threshold associated with a " "chance constraint. The RHS will be one " "minus this value. Default is 0." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "cc_indicator_var", PySPConfigValue( None, domain=_domain_must_be_str, description=( "The name of the binary variable to be used " "to construct a chance constraint. Default " "is None, which indicates no chance " "constraint." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "mipgap", PySPConfigValue( None, domain=_domain_unit_interval, description=( "Specifies the mipgap for the EF solve." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver", PySPConfigValue( "cplex", domain=_domain_must_be_str, description=( "Specifies the solver used to solve the " "extensive form model. Default is cplex." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_io", PySPConfigValue( None, domain=_domain_must_be_str, description=( "The type of IO used to execute the " "solver. Different solvers support different " "types of IO, but the following are common " "options: lp - generate LP files, nl - " "generate NL files, python - direct Python " "interface, os - generate OSiL XML files." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_manager", PySPConfigValue( 'serial', domain=_domain_must_be_str, description=( "The type of solver manager used to " "coordinate solves. Default is serial." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_options", PySPConfigValue( (), domain=_domain_tuple_of_str_or_dict, description=( "Persistent solver options used when " "solving the extensive form model. This " "option can be used multiple times from " "the command line to specify more than " "one solver option." ), doc=None, visibility=0), ap_kwds={'action': 'append'}, ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "disable_warmstart", PySPConfigValue( False, domain=bool, description=( "Disable warm-start of EF solves. " "Default is False." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_host", PySPConfigValue( None, domain=_domain_must_be_str, description=( "The hostname to bind on when searching " "for a Pyro nameserver." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_port", PySPConfigValue( None, domain=_domain_nonnegative_integer, description=( "The port to bind on when searching for " "a Pyro nameserver." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_shutdown", PySPConfigValue( False, domain=bool, description=( "Attempt to shut down all Pyro-related components " "associated with the Pyro name server used by any scenario " "tree manager or solver manager. Components to shutdown " "include the name server, dispatch server, and any " "scenariotreeserver or pyro_mip_server processes. Note " "that if Pyro4 is in use the nameserver will always " "ignore this request." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_shutdown_workers", PySPConfigValue( False, domain=bool, description=( "Upon exit, send shutdown requests to all worker " "processes that were acquired through the dispatcher. " "This typically includes scenariotreeserver processes " "(used by the Pyro scenario tree manager) and pyro_mip_server " "processes (used by the Pyro solver manager). This leaves " "any dispatchers and namservers running as well as any " "processes registered with the dispather that were not " "acquired for work by this client." ), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "symbolic_solver_labels", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_solver_log", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "verbose", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_times", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "keep_solver_files", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_solver_results", ap_group=_ef_group_label) def __enter__(self): return self def __exit__(self, *args): self.close() def close(self): self.destroy_ef() if self._solver is not None: self._solver.deactivate() if self._solver_manager is not None: if isinstance(self._solver_manager, pyomo.solvers.plugins.smanager.\ pyro.SolverManager_Pyro): if self.get_option("pyro_shutdown_workers"): self._solver_manager.shutdown_workers() self._solver_manager.deactivate() self._solver_manager = None self._manager = None self.objective = undefined self.objective_sense = undefined self.gap = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined self.pyomo_solve_time = undefined self.solve_time = undefined def __init__(self, manager, *args, **kwds): import pyomo.solvers.plugins.smanager.pyro super(ExtensiveFormAlgorithm, self).__init__(*args, **kwds) # TODO: after PH moves over to the new code #if not isinstance(manager, ScenarioTreeManager): # raise TypeError("ExtensiveFormAlgorithm requires an instance of the " # "ScenarioTreeManager interface as the " # "second argument") if not manager.initialized: raise ValueError("ExtensiveFormAlgorithm requires a scenario tree " "manager that has been fully initialized") self._manager = manager self.instance = None self._solver_manager = None self._solver = None # The following attributes will be modified by the # solve() method. For users that are scripting, these # can be accessed after the solve() method returns. # They will be reset each time solve() is called. ############################################ self.objective = undefined self.gap = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined self.pyomo_solve_time = undefined self.solve_time = undefined ############################################ self._solver = SolverFactory(self.get_option("solver"), solver_io=self.get_option("solver_io")) if isinstance(self._solver, UnknownSolver): raise ValueError("Failed to create solver of type="+ self.get_option("solver")+ " for use in extensive form solve") if len(self.get_option("solver_options")) > 0: if self.get_option("verbose"): print("Initializing ef solver with options=" +str(list(self.get_option("solver_options")))) self._solver.set_options("".join(self.get_option("solver_options"))) if self.get_option("mipgap") is not None: if (self.get_option("mipgap") < 0.0) or \ (self.get_option("mipgap") > 1.0): raise ValueError("Value of the mipgap parameter for the EF " "solve must be on the unit interval; " "value specified="+str(self.get_option("mipgap"))) self._solver.options.mipgap = float(self.get_option("mipgap")) solver_manager_type = self.get_option("solver_manager") if solver_manager_type == "phpyro": print("*** WARNING ***: PHPyro is not a supported solver " "manager type for the extensive-form solver. " "Falling back to serial.") solver_manager_type = 'serial' self._solver_manager = SolverManagerFactory( solver_manager_type, host=self.get_option("pyro_host"), port=self.get_option("pyro_port")) if self._solver_manager is None: raise ValueError("Failed to create solver manager of type=" +self.get_option("solver")+ " for use in extensive form solve") def build_ef(self): self.destroy_ef() if self.get_option("verbose"): print("Creating extensive form instance") start_time = time.time() # then validate the associated parameters. generate_weighted_cvar = False cvar_weight = None risk_alpha = None if self.get_option("generate_weighted_cvar"): generate_weighted_cvar = True cvar_weight = self.get_option("cvar_weight") risk_alpha = self.get_option("risk_alpha") self.instance = create_ef_instance( self._manager.scenario_tree, verbose_output=self.get_option("verbose"), generate_weighted_cvar=generate_weighted_cvar, cvar_weight=cvar_weight, risk_alpha=risk_alpha, cc_indicator_var_name=self.get_option("cc_indicator_var"), cc_alpha=self.get_option("cc_alpha")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to construct extensive form instance=%.2f seconds" %(time.time() - start_time)) def destroy_ef(self): if self.instance is not None: for scenario in self._manager.scenario_tree.scenarios: self.instance.del_component(scenario.name) scenario._instance_objective.activate() self.instance = None def write(self, filename): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") suf = os.path.splitext(filename)[1] if suf not in ['.nl','.lp','.mps']: if self._solver.problem_format() == ProblemFormat.cpxlp: filename += '.lp' elif self._solver.problem_format() == ProblemFormat.nl: filename += '.nl' elif self._solver.problem_format() == ProblemFormat.mps: filename += '.mps' else: raise ValueError("Could not determine output file format. " "No recognized ending suffix was provided " "and no format was indicated was by the " "--solver-io option.") start_time = time.time() if self.get_option("verbose"): print("Starting to write extensive form") smap_id = write_ef(self.instance, filename, self.get_option("symbolic_solver_labels")) print("Extensive form written to file="+filename) if self.get_option("verbose") or self.get_option("output_times"): print("Time to write output file=%.2f seconds" % (time.time() - start_time)) return filename, smap_id def solve(self, check_status=True, exception_on_failure=True, io_options=None): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") start_time = time.time() if self.get_option("verbose"): print("Queuing extensive form solve") self.objective = undefined self.gap = undefined self.bound = undefined self.pyomo_solve_time = undefined self.solve_time = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined if isinstance(self._solver, PersistentSolver): self._solver.compile_instance( self.instance, symbolic_solver_labels=self.get_option("symbolic_solver_labels")) solve_kwds = {} solve_kwds['load_solutions'] = False if self.get_option("keep_solver_files"): solve_kwds['keepfiles'] = True if self.get_option("symbolic_solver_labels"): solve_kwds['symbolic_solver_labels'] = True if self.get_option("output_solver_log"): solve_kwds['tee'] = True if io_options is not None: solve_kwds.update(io_options) self.objective_sense = \ find_active_objective(self.instance).sense if (not self.get_option("disable_warmstart")) and \ (self._solver.warm_start_capable()): action_handle = self._solver_manager.queue(self.instance, opt=self._solver, warmstart=True, **solve_kwds) else: action_handle = self._solver_manager.queue(self.instance, opt=self._solver, **solve_kwds) if self.get_option("verbose"): print("Waiting for extensive form solve") results = self._solver_manager.wait_for(action_handle) if self.get_option("verbose"): print("Done with extensive form solve - loading results") if self.get_option("output_solver_results"): print("Results for ef:") results.write(num=1) self.solver_results = results if hasattr(results.solver,"user_time") and \ (not isinstance(results.solver.user_time, UndefinedData)) and \ (results.solver.user_time is not None): # the solve time might be a string, or might # not be - we eventually would like more # consistency on this front from the solver # plugins. self.solve_time = \ float(results.solver.user_time) elif hasattr(results.solver,"time"): self.solve_time = \ float(results.solver.time) else: self.solve_time = undefined if hasattr(results,"pyomo_solve_time"): self.pyomo_solve_time = \ results.pyomo_solve_time else: self.pyomo_solve_times = undefined self.termination_condition = \ results.solver.termination_condition self.solver_status = \ results.solver.status if len(results.solution) > 0: assert len(results.solution) == 1 results_sm = results._smap self.instance.solutions.load_from(results) solution0 = results.solution(0) if hasattr(solution0, "gap") and \ (solution0.gap is not None): self.gap = solution0.gap else: self.gap = undefined self.solution_status = solution0.status if self.get_option("verbose"): print("Storing solution in scenario tree") for scenario in self._manager.scenario_tree.scenarios: scenario.update_solution_from_instance() self._manager.scenario_tree.snapshotSolutionFromScenarios() self.objective = self._manager.scenario_tree.\ findRootNode().\ computeExpectedNodeCost() if self.gap is not undefined: if self.objective_sense == pyomo.core.base.minimize: self.bound = self.objective - self.gap else: self.bound = self.objective + self.gap else: self.objective = undefined self.gap = undefined self.bound = undefined self.solution_status = undefined failure = False if check_status: if not ((self.solution_status == SolutionStatus.optimal) or \ (self.solution_status == SolutionStatus.feasible)): failure = True if self.get_option("verbose") or \ exception_on_failure: msg = ("EF solve failed solution status check:\n" "Solver Status: %s\n" "Termination Condition: %s\n" "Solution Status: %s\n" % (self.solver_status, self.termination_condition, self.solution_status)) if self.get_option("verbose"): print(msg) if exception_on_failure: raise RuntimeError(msg) else: if self.get_option("verbose"): print("EF solve completed. Skipping status check.") if self.get_option("verbose") or self.get_option("output_times"): print("Time to solve and load results for the " "extensive form=%.2f seconds" % (time.time()-start_time)) return failure
class ExtensiveFormAlgorithm(PySPConfiguredObject): _declared_options = \ PySPConfigBlock("Options declared for the " "ExtensiveFormAlgorithm class") safe_declare_unique_option( _declared_options, "cvar_weight", PySPConfigValue( 1.0, domain=_domain_nonnegative, description=("The weight associated with the CVaR term in " "the risk-weighted objective " "formulation. If the weight is 0, then " "*only* a non-weighted CVaR cost will appear " "in the EF objective - the expected cost " "component will be dropped. Default is 1.0."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "generate_weighted_cvar", PySPConfigValue(False, domain=bool, description=("Add a weighted CVaR term to the " "primary objective. Default is False."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "risk_alpha", PySPConfigValue( 0.95, domain=_domain_unit_interval, description=("The probability threshold associated with " "CVaR (or any future) risk-oriented " "performance metrics. Default is 0.95."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "cc_alpha", PySPConfigValue( 0.0, domain=_domain_unit_interval, description=("The probability threshold associated with a " "chance constraint. The RHS will be one " "minus this value. Default is 0."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "cc_indicator_var", PySPConfigValue( None, domain=_domain_must_be_str, description=("The name of the binary variable to be used " "to construct a chance constraint. Default " "is None, which indicates no chance " "constraint."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "mipgap", PySPConfigValue(None, domain=_domain_unit_interval, description=("Specifies the mipgap for the EF solve."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver", PySPConfigValue( "cplex", domain=_domain_must_be_str, description=("Specifies the solver used to solve the " "extensive form model. Default is cplex."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_io", PySPConfigValue( None, domain=_domain_must_be_str, description=("The type of IO used to execute the " "solver. Different solvers support different " "types of IO, but the following are common " "options: lp - generate LP files, nl - " "generate NL files, python - direct Python " "interface, os - generate OSiL XML files."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_manager", PySPConfigValue('serial', domain=_domain_must_be_str, description=("The type of solver manager used to " "coordinate solves. Default is serial."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "solver_options", PySPConfigValue((), domain=_domain_tuple_of_str_or_dict, description=("Persistent solver options used when " "solving the extensive form model. This " "option can be used multiple times from " "the command line to specify more than " "one solver option."), doc=None, visibility=0), ap_kwds={'action': 'append'}, ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "disable_warmstart", PySPConfigValue(False, domain=bool, description=("Disable warm-start of EF solves. " "Default is False."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_host", PySPConfigValue(None, domain=_domain_must_be_str, description=("The hostname to bind on when searching " "for a Pyro nameserver."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_port", PySPConfigValue(None, domain=_domain_nonnegative_integer, description=("The port to bind on when searching for " "a Pyro nameserver."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_shutdown", PySPConfigValue( False, domain=bool, description=( "Attempt to shut down all Pyro-related components " "associated with the Pyro name server used by any scenario " "tree manager or solver manager. Components to shutdown " "include the name server, dispatch server, and any " "scenariotreeserver or pyro_mip_server processes. Note " "that if Pyro4 is in use the nameserver will always " "ignore this request."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_unique_option( _declared_options, "pyro_shutdown_workers", PySPConfigValue( False, domain=bool, description=( "Upon exit, send shutdown requests to all worker " "processes that were acquired through the dispatcher. " "This typically includes scenariotreeserver processes " "(used by the Pyro scenario tree manager) and pyro_mip_server " "processes (used by the Pyro solver manager). This leaves " "any dispatchers and namservers running as well as any " "processes registered with the dispather that were not " "acquired for work by this client."), doc=None, visibility=0), ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "symbolic_solver_labels", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_solver_log", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "verbose", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_times", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "keep_solver_files", ap_group=_ef_group_label) safe_declare_common_option(_declared_options, "output_solver_results", ap_group=_ef_group_label) def __enter__(self): return self def __exit__(self, *args): self.close() def close(self): self.destroy_ef() if self._solver is not None: self._solver.deactivate() if self._solver_manager is not None: if isinstance(self._solver_manager, pyomo.solvers.plugins.smanager.\ pyro.SolverManager_Pyro): if self.get_option("pyro_shutdown_workers"): self._solver_manager.shutdown_workers() self._solver_manager.deactivate() self._solver_manager = None self._manager = None self.objective = undefined self.objective_sense = undefined self.gap = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined self.pyomo_solve_time = undefined self.solve_time = undefined def __init__(self, manager, *args, **kwds): import pyomo.solvers.plugins.smanager.pyro super(ExtensiveFormAlgorithm, self).__init__(*args, **kwds) # TODO: after PH moves over to the new code #if not isinstance(manager, ScenarioTreeManager): # raise TypeError("ExtensiveFormAlgorithm requires an instance of the " # "ScenarioTreeManager interface as the " # "second argument") if not manager.initialized: raise ValueError("ExtensiveFormAlgorithm requires a scenario tree " "manager that has been fully initialized") self._manager = manager self.instance = None self._solver_manager = None self._solver = None # The following attributes will be modified by the # solve() method. For users that are scripting, these # can be accessed after the solve() method returns. # They will be reset each time solve() is called. ############################################ self.objective = undefined self.gap = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined self.pyomo_solve_time = undefined self.solve_time = undefined ############################################ self._solver = SolverFactory(self.get_option("solver"), solver_io=self.get_option("solver_io")) if isinstance(self._solver, UnknownSolver): raise ValueError("Failed to create solver of type=" + self.get_option("solver") + " for use in extensive form solve") if len(self.get_option("solver_options")) > 0: if self.get_option("verbose"): print("Initializing ef solver with options=" + str(list(self.get_option("solver_options")))) self._solver.set_options("".join( self.get_option("solver_options"))) if self.get_option("mipgap") is not None: if (self.get_option("mipgap") < 0.0) or \ (self.get_option("mipgap") > 1.0): raise ValueError("Value of the mipgap parameter for the EF " "solve must be on the unit interval; " "value specified=" + str(self.get_option("mipgap"))) self._solver.options.mipgap = float(self.get_option("mipgap")) solver_manager_type = self.get_option("solver_manager") if solver_manager_type == "phpyro": print("*** WARNING ***: PHPyro is not a supported solver " "manager type for the extensive-form solver. " "Falling back to serial.") solver_manager_type = 'serial' self._solver_manager = SolverManagerFactory( solver_manager_type, host=self.get_option("pyro_host"), port=self.get_option("pyro_port")) if self._solver_manager is None: raise ValueError("Failed to create solver manager of type=" + self.get_option("solver") + " for use in extensive form solve") def build_ef(self): self.destroy_ef() if self.get_option("verbose"): print("Creating extensive form instance") start_time = time.time() # then validate the associated parameters. generate_weighted_cvar = False cvar_weight = None risk_alpha = None if self.get_option("generate_weighted_cvar"): generate_weighted_cvar = True cvar_weight = self.get_option("cvar_weight") risk_alpha = self.get_option("risk_alpha") self.instance = create_ef_instance( self._manager.scenario_tree, verbose_output=self.get_option("verbose"), generate_weighted_cvar=generate_weighted_cvar, cvar_weight=cvar_weight, risk_alpha=risk_alpha, cc_indicator_var_name=self.get_option("cc_indicator_var"), cc_alpha=self.get_option("cc_alpha")) if self.get_option("verbose") or self.get_option("output_times"): print("Time to construct extensive form instance=%.2f seconds" % (time.time() - start_time)) def destroy_ef(self): if self.instance is not None: for scenario in self._manager.scenario_tree.scenarios: self.instance.del_component(scenario.name) scenario._instance_objective.activate() self.instance = None def write(self, filename): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") suf = os.path.splitext(filename)[1] if suf not in ['.nl', '.lp', '.mps']: if self._solver.problem_format() == ProblemFormat.cpxlp: filename += '.lp' elif self._solver.problem_format() == ProblemFormat.nl: filename += '.nl' elif self._solver.problem_format() == ProblemFormat.mps: filename += '.mps' else: raise ValueError("Could not determine output file format. " "No recognized ending suffix was provided " "and no format was indicated was by the " "--solver-io option.") start_time = time.time() if self.get_option("verbose"): print("Starting to write extensive form") smap_id = write_ef(self.instance, filename, self.get_option("symbolic_solver_labels")) print("Extensive form written to file=" + filename) if self.get_option("verbose") or self.get_option("output_times"): print("Time to write output file=%.2f seconds" % (time.time() - start_time)) return filename, smap_id def solve(self, check_status=True, exception_on_failure=True, io_options=None): if self.instance is None: raise RuntimeError( "The extensive form instance has not been constructed." "Call the build_ef() method to construct it.") start_time = time.time() if self.get_option("verbose"): print("Queuing extensive form solve") self.objective = undefined self.gap = undefined self.bound = undefined self.pyomo_solve_time = undefined self.solve_time = undefined self.termination_condition = undefined self.solver_status = undefined self.solution_status = undefined self.solver_results = undefined if isinstance(self._solver, PersistentSolver): self._solver.compile_instance( self.instance, symbolic_solver_labels=self.get_option( "symbolic_solver_labels")) solve_kwds = {} solve_kwds['load_solutions'] = False if self.get_option("keep_solver_files"): solve_kwds['keepfiles'] = True if self.get_option("symbolic_solver_labels"): solve_kwds['symbolic_solver_labels'] = True if self.get_option("output_solver_log"): solve_kwds['tee'] = True if io_options is not None: solve_kwds.update(io_options) self.objective_sense = \ find_active_objective(self.instance).sense if (not self.get_option("disable_warmstart")) and \ (self._solver.warm_start_capable()): action_handle = self._solver_manager.queue(self.instance, opt=self._solver, warmstart=True, **solve_kwds) else: action_handle = self._solver_manager.queue(self.instance, opt=self._solver, **solve_kwds) if self.get_option("verbose"): print("Waiting for extensive form solve") results = self._solver_manager.wait_for(action_handle) if self.get_option("verbose"): print("Done with extensive form solve - loading results") if self.get_option("output_solver_results"): print("Results for ef:") results.write(num=1) self.solver_results = results if hasattr(results.solver,"user_time") and \ (not isinstance(results.solver.user_time, UndefinedData)) and \ (results.solver.user_time is not None): # the solve time might be a string, or might # not be - we eventually would like more # consistency on this front from the solver # plugins. self.solve_time = \ float(results.solver.user_time) elif hasattr(results.solver, "time"): self.solve_time = \ float(results.solver.time) else: self.solve_time = undefined if hasattr(results, "pyomo_solve_time"): self.pyomo_solve_time = \ results.pyomo_solve_time else: self.pyomo_solve_times = undefined self.termination_condition = \ results.solver.termination_condition self.solver_status = \ results.solver.status if len(results.solution) > 0: assert len(results.solution) == 1 results_sm = results._smap self.instance.solutions.load_from(results) solution0 = results.solution(0) if hasattr(solution0, "gap") and \ (solution0.gap is not None): self.gap = solution0.gap else: self.gap = undefined self.solution_status = solution0.status if self.get_option("verbose"): print("Storing solution in scenario tree") for scenario in self._manager.scenario_tree.scenarios: scenario.update_solution_from_instance() self._manager.scenario_tree.snapshotSolutionFromScenarios() self.objective = self._manager.scenario_tree.\ findRootNode().\ computeExpectedNodeCost() if self.gap is not undefined: if self.objective_sense == pyomo.core.base.minimize: self.bound = self.objective - self.gap else: self.bound = self.objective + self.gap else: self.objective = undefined self.gap = undefined self.bound = undefined self.solution_status = undefined failure = False if check_status: if not ((self.solution_status == SolutionStatus.optimal) or \ (self.solution_status == SolutionStatus.feasible)): failure = True if self.get_option("verbose") or \ exception_on_failure: msg = ("EF solve failed solution status check:\n" "Solver Status: %s\n" "Termination Condition: %s\n" "Solution Status: %s\n" % (self.solver_status, self.termination_condition, self.solution_status)) if self.get_option("verbose"): print(msg) if exception_on_failure: raise RuntimeError(msg) else: if self.get_option("verbose"): print("EF solve completed. Skipping status check.") if self.get_option("verbose") or self.get_option("output_times"): print("Time to solve and load results for the " "extensive form=%.2f seconds" % (time.time() - start_time)) return failure