예제 #1
0
파일: base.py 프로젝트: qtothec/pyomo
    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
예제 #2
0
파일: base.py 프로젝트: jay1yun/pyomo
    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
예제 #3
0
파일: ef.py 프로젝트: Pyomo/pyomo
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
예제 #4
0
파일: ef.py 프로젝트: CanLi1/pyomo-1
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
예제 #5
0
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
예제 #6
0
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