Beispiel #1
0
def _add_OA_configs(CONFIG):
    CONFIG.declare("init_strategy", ConfigValue(
        default="set_covering", domain=In(valid_init_strategies.keys()),
        description="Initialization strategy to use.",
        doc="Selects the initialization strategy to use when generating "
            "the initial cuts to construct the master problem."
    ))
    CONFIG.declare("custom_init_disjuncts", ConfigList(
        # domain=ComponentSets of Disjuncts,
        default=None,
        description="List of disjunct sets to use for initialization."
    ))
    CONFIG.declare("max_slack", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Upper bound on slack variables for OA"
    ))
    CONFIG.declare("OA_penalty_factor", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Penalty multiplication term for slack variables on the "
                    "objective value."
    ))
    CONFIG.declare("set_cover_iterlim", ConfigValue(
        default=8, domain=NonNegativeInt,
        description="Limit on the number of set covering iterations."
    ))
    CONFIG.declare("call_before_master_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook before calling the master problem solver"
    ))
    CONFIG.declare("call_after_master_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the master problem"
    ))
    CONFIG.declare("call_before_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook before calling the subproblem solver"
    ))
    CONFIG.declare("call_after_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the "
                    "nonlinear subproblem"
    ))
    CONFIG.declare("call_after_subproblem_feasible", ConfigValue(
        default=_DoNothing,
        description="callback hook after feasible solution of "
                    "the nonlinear subproblem"
    ))
    CONFIG.declare("algorithm_stall_after", ConfigValue(
        default=2,
        description="number of non-improving master iterations after which "
                    "the algorithm will stall and exit."
    ))
    CONFIG.declare("round_discrete_vars", ConfigValue(
        default=True,
        description="flag to round subproblem discrete variable values to the "
        "nearest integer. Rounding is done before fixing disjuncts."
    ))
    CONFIG.declare("force_subproblem_nlp", ConfigValue(
        default=False,
        description="Force subproblems to be NLP, even if discrete variables "
        "exist."
    ))
    CONFIG.declare("mip_presolve", ConfigValue(
        default=True,
        description="Flag to enable or diable GDPopt MIP presolve. "
        "Default=True.",
        domain=bool
    ))
    CONFIG.declare("subproblem_presolve", ConfigValue(
        default=True,
        description="Flag to enable or disable subproblem presolve. "
        "Default=True.",
        domain=bool
    ))
    CONFIG.declare("max_fbbt_iterations", ConfigValue(
        default=3,
        description="Maximum number of feasibility-based bounds tightening "
        "iterations to do during NLP subproblem preprocessing.",
        domain=PositiveInt
    ))
    CONFIG.declare("tighten_nlp_var_bounds", ConfigValue(
        default=False,
        description="Whether or not to do feasibility-based bounds tightening "
        "on the variables in the NLP subproblem before solving it.",
        domain=bool
    ))
    CONFIG.declare("calc_disjunctive_bounds", ConfigValue(
        default=False,
        description="Calculate special disjunctive variable bounds for GLOA. "
        "False by default.",
        domain=bool
    ))
    CONFIG.declare("obbt_disjunctive_bounds", ConfigValue(
        default=False,
        description="Use optimality-based bounds tightening rather than "
        "feasibility-based bounds tightening to compute disjunctive variable "
        "bounds. False by default.",
        domain=bool
    ))
    return CONFIG
Beispiel #2
0
def _define_turbine_multistage_config(config):
    config.declare(
        "dynamic",
        ConfigValue(
            domain=In([False]),
            default=False,
            description="Dynamic model flag",
            doc=
            "Only False, in a dynamic flowsheet this is psuedo-steady-state.",
        ),
    )
    config.declare(
        "has_holdup",
        ConfigValue(
            default=False,
            domain=In([False]),
            description="Holdup construction flag",
            doc=
            "Only False, in a dynamic flowsheet this is psuedo-steady-state.",
        ),
    )
    config.declare(
        "property_package",
        ConfigValue(
            default=useDefault,
            domain=is_physical_parameter_block,
            description="Property package to use for control volume",
            doc=
            """Property parameter object used to define property calculations,
**default** - useDefault.
**Valid values:** {
**useDefault** - use default package from parent model or flowsheet,
**PropertyParameterObject** - a PropertyParameterBlock object.}""",
        ),
    )
    config.declare(
        "property_package_args",
        ConfigBlock(
            implicit=True,
            description="Arguments to use for constructing property packages",
            doc=
            """A ConfigBlock with arguments to be passed to a property block(s)
and used when constructing these,
**default** - None.
**Valid values:** {
see property package for documentation.}""",
        ),
    )
    config.declare(
        "num_parallel_inlet_stages",
        ConfigValue(
            default=4,
            domain=int,
            description=
            "Number of parallel inlet stages to simulate partial arc "
            "admission.  Default=4",
        ),
    )
    config.declare(
        "throttle_valve_function",
        ConfigValue(
            default=ValveFunctionType.linear,
            domain=In(ValveFunctionType),
            description=
            "Valve function type, if custom provide an expression rule",
            doc=
            """The type of valve function, if custom provide an expression rule
with the valve_function_rule argument.
**default** - ValveFunctionType.linear
**Valid values** - {
ValveFunctionType.linear,
ValveFunctionType.quick_opening,
ValveFunctionType.equal_percentage,
ValveFunctionType.custom}""",
        ),
    )
    config.declare(
        "throttle_valve_function_callback",
        ConfigValue(
            default=None,
            description="A callback to add a custom valve function to the "
            "throttle valves or None.  If a callback is provided, it should "
            "take the valve block data as an argument and add a "
            "valve_function expressions to it. Default=None",
        ),
    )
    config.declare(
        "num_hp",
        ConfigValue(
            default=2,
            domain=int,
            description=
            "Number of high pressure stages not including inlet stage",
            doc="Number of high pressure stages not including inlet stage",
        ),
    )
    config.declare(
        "num_ip",
        ConfigValue(
            default=10,
            domain=int,
            description="Number of intermediate pressure stages",
            doc="Number of intermediate pressure stages",
        ),
    )
    config.declare(
        "num_lp",
        ConfigValue(
            default=5,
            domain=int,
            description=
            "Number of low pressure stages not including outlet stage",
            doc="Number of low pressure stages not including outlet stage",
        ),
    )
    config.declare(
        "hp_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitters in HP section",
            doc="A list of index locations of splitters in the HP section. The "
            "indexes indicate after which stage to include splitters.  0 is "
            "between the inlet stage and the first regular HP stage.",
        ),
    )
    config.declare(
        "ip_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitters in IP section",
            doc="A list of index locations of splitters in the IP section. The "
            "indexes indicate after which stage to include splitters.",
        ),
    )
    config.declare(
        "lp_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitter in LP section",
            doc="A list of index locations of splitters in the LP section. The "
            "indexes indicate after which stage to include splitters.",
        ),
    )
    config.declare(
        "hp_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="HP Turbine stages to not connect to next with an arc.",
            doc="HP Turbine stages to not connect to next with an arc. This is "
            "usually used to insert additional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "ip_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="IP Turbine stages to not connect to next with an arc.",
            doc="IP Turbine stages to not connect to next with an arc. This is "
            "usually used to insert additional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "lp_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="LP Turbine stages to not connect to next with an arc.",
            doc="LP Turbine stages to not connect to next with an arc. This is "
            "usually used to insert additional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "hp_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description=
            "Dict, hp split index: number of splitter outlets, if not 2",
        ),
    )
    config.declare(
        "ip_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description=
            "Dict, ip split index: number of splitter outlets, if not 2",
        ),
    )
    config.declare(
        "lp_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description=
            "Dict, lp split index: number of splitter outlets, if not 2",
        ),
    )
Beispiel #3
0
class GDPoptSolver(object):
    """Decomposition solver for Generalized Disjunctive Programming (GDP) problems.

    The GDPopt (Generalized Disjunctive Programming optimizer) solver applies a
    variety of decomposition-based approaches to solve Generalized Disjunctive
    Programming (GDP) problems. GDP models can include nonlinear, continuous
    variables and constraints, as well as logical conditions.

    These approaches include:

    - Outer approximation
    - Partial surrogate cuts [pending]
    - Generalized Bender decomposition [pending]

    This solver implementation was developed by Carnegie Mellon University in the
    research group of Ignacio Grossmann.

    For nonconvex problems, the bounds self.LB and self.UB may not be rigorous.

    Questions: Please make a post at StackOverflow and/or contact Qi Chen
    <https://github.com/qtothec>.

    Keyword arguments below are specified for the :code:`solve` function.

    """

    _metasolver = False

    CONFIG = ConfigBlock("GDPopt")
    CONFIG.declare("iterlim", ConfigValue(
        default=30, domain=NonNegativeInt,
        description="Iteration limit."
    ))
    CONFIG.declare("strategy", ConfigValue(
        default="LOA", domain=In(["LOA", "GLOA"]),
        description="Decomposition strategy to use."
    ))
    CONFIG.declare("init_strategy", ConfigValue(
        default="set_covering", domain=In(valid_init_strategies.keys()),
        description="Initialization strategy to use.",
        doc="""Selects the initialization strategy to use when generating
        the initial cuts to construct the master problem."""
    ))
    CONFIG.declare("custom_init_disjuncts", ConfigList(
        # domain=ComponentSets of Disjuncts,
        default=None,
        description="List of disjunct sets to use for initialization."
    ))
    CONFIG.declare("max_slack", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Upper bound on slack variables for OA"
    ))
    CONFIG.declare("OA_penalty_factor", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Penalty multiplication term for slack variables on the "
        "objective value."
    ))
    CONFIG.declare("set_cover_iterlim", ConfigValue(
        default=8, domain=NonNegativeInt,
        description="Limit on the number of set covering iterations."
    ))
    CONFIG.declare("mip_solver", ConfigValue(
        default="gurobi",
        description="Mixed integer linear solver to use."
    ))
    CONFIG.declare("mip_presolve", ConfigValue(
        default=True,
        description="Flag to enable or diable Pyomo MIP presolve. Default=True.",
        domain=bool
    ))
    mip_solver_args = CONFIG.declare(
        "mip_solver_args", ConfigBlock(implicit=True))
    CONFIG.declare("nlp_solver", ConfigValue(
        default="ipopt",
        description="Nonlinear solver to use"))
    nlp_solver_args = CONFIG.declare(
        "nlp_solver_args", ConfigBlock(implicit=True))
    CONFIG.declare("subproblem_presolve", ConfigValue(
        default=True,
        description="Flag to enable or disable subproblem presolve. Default=True.",
        domain=bool
    ))
    CONFIG.declare("minlp_solver", ConfigValue(
        default="baron",
        description="MINLP solver to use"
    ))
    minlp_solver_args = CONFIG.declare(
        "minlp_solver_args", ConfigBlock(implicit=True))
    CONFIG.declare("call_before_master_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook before calling the master problem solver"
    ))

    CONFIG.declare("call_after_master_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the master problem"
    ))
    CONFIG.declare("call_before_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook before calling the subproblem solver"
    ))
    CONFIG.declare("call_after_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the "
        "nonlinear subproblem"
    ))
    CONFIG.declare("call_after_subproblem_feasible", ConfigValue(
        default=_DoNothing,
        description="callback hook after feasible solution of "
        "the nonlinear subproblem"
    ))
    CONFIG.declare("algorithm_stall_after", ConfigValue(
        default=2,
        description="number of non-improving master iterations after which "
        "the algorithm will stall and exit."
    ))
    CONFIG.declare("tee", ConfigValue(
        default=False,
        description="Stream output to terminal.",
        domain=bool
    ))
    CONFIG.declare("logger", ConfigValue(
        default='pyomo.contrib.gdpopt',
        description="The logger object or name to use for reporting.",
        domain=a_logger
    ))
    CONFIG.declare("calc_disjunctive_bounds", ConfigValue(
        default=False,
        description="Calculate special disjunctive variable bounds for GLOA. False by default.",
        domain=bool
    ))
    CONFIG.declare("obbt_disjunctive_bounds", ConfigValue(
        default=False,
        description="Use optimality-based bounds tightening rather than feasibility-based bounds tightening "
        "to compute disjunctive variable bounds. False by default.",
        domain=bool
    ))
    CONFIG.declare("bound_tolerance", ConfigValue(
        default=1E-6, domain=NonNegativeFloat,
        description="Tolerance for bound convergence."
    ))
    CONFIG.declare("small_dual_tolerance", ConfigValue(
        default=1E-8,
        description="When generating cuts, small duals multiplied "
        "by expressions can cause problems. Exclude all duals "
        "smaller in absolue value than the following."
    ))
    CONFIG.declare("integer_tolerance", ConfigValue(
        default=1E-5,
        description="Tolerance on integral values."
    ))
    CONFIG.declare("constraint_tolerance", ConfigValue(
        default=1E-6,
        description="Tolerance on constraint satisfaction."
    ))
    CONFIG.declare("variable_tolerance", ConfigValue(
        default=1E-8,
        description="Tolerance on variable bounds."
    ))
    CONFIG.declare("zero_tolerance", ConfigValue(
        default=1E-15,
        description="Tolerance on variable equal to zero."))
    CONFIG.declare("round_discrete_vars", ConfigValue(
        default=True,
        description="flag to round subproblem discrete variable values to the nearest integer. "
        "Rounding is done before fixing disjuncts."
    ))
    CONFIG.declare("force_subproblem_nlp", ConfigValue(
        default=False,
        description="Force subproblems to be NLP, even if discrete variables exist."
    ))

    __doc__ = add_docstring_list(__doc__, CONFIG)

    def available(self, exception_flag=True):
        """Check if solver is available.

        TODO: For now, it is always available. However, sub-solvers may not
        always be available, and so this should reflect that possibility.

        """
        return True

    def version(self):
        """Return a 3-tuple describing the solver version."""
        return __version__

    def solve(self, model, **kwds):
        """Solve the model.

        Warning: this solver is still in beta. Keyword arguments subject to
        change. Undocumented keyword arguments definitely subject to change.

        This function performs all of the GDPopt solver setup and problem
        validation. It then calls upon helper functions to construct the
        initial master approximation and iteration loop.

        Args:
            model (Block): a Pyomo model or block to be solved

        """
        config = self.CONFIG(kwds.pop('options', {}))
        config.set_value(kwds)
        solve_data = GDPoptSolveData()
        solve_data.results = SolverResults()
        solve_data.timing = Container()

        old_logger_level = config.logger.getEffectiveLevel()
        with time_code(solve_data.timing, 'total'), \
                restore_logger_level(config.logger), \
                create_utility_block(model, 'GDPopt_utils', solve_data):
            if config.tee and old_logger_level > logging.INFO:
                # If the logger does not already include INFO, include it.
                config.logger.setLevel(logging.INFO)
            config.logger.info(
                "Starting GDPopt version %s using %s algorithm"
                % (".".join(map(str, self.version())), config.strategy)
            )
            config.logger.info(
                """
If you use this software, you may cite the following:
- Implementation:
    Chen, Q; Johnson, ES; Siirola, JD; Grossmann, IE.
    Pyomo.GDP: Disjunctive Models in Python. 
    Proc. of the 13th Intl. Symposium on Process Systems Eng.
    San Diego, 2018.
- LOA algorithm:
    Türkay, M; Grossmann, IE.
    Logic-based MINLP algorithms for the optimal synthesis of process networks.
    Comp. and Chem. Eng. 1996, 20(8), 959–978.
    DOI: 10.1016/0098-1354(95)00219-7.
- GLOA algorithm:
    Lee, S; Grossmann, IE.
    A Global Optimization Algorithm for Nonconvex Generalized Disjunctive Programming and Applications to Process Systems
    Comp. and Chem. Eng. 2001, 25, 1675-1697.
    DOI: 10.1016/S0098-1354(01)00732-3
                """.strip()
            )
            solve_data.results.solver.name = 'GDPopt %s - %s' % (
                str(self.version()), config.strategy)

            solve_data.original_model = model
            solve_data.working_model = model.clone()
            GDPopt = solve_data.working_model.GDPopt_utils
            setup_results_object(solve_data, config)

            solve_data.current_strategy = config.strategy

            # Verify that objective has correct form
            process_objective(solve_data, config)

            # Save model initial values. These are used later to initialize NLP
            # subproblems.
            solve_data.initial_var_values = list(
                v.value for v in GDPopt.variable_list)
            solve_data.best_solution_found = None

            # Validate the model to ensure that GDPopt is able to solve it.
            if not model_is_valid(solve_data, config):
                return

            # Integer cuts exclude particular discrete decisions
            GDPopt.integer_cuts = ConstraintList(doc='integer cuts')

            # Feasible integer cuts exclude discrete realizations that have
            # been explored via an NLP subproblem. Depending on model
            # characteristics, the user may wish to revisit NLP subproblems
            # (with a different initialization, for example). Therefore, these
            # cuts are not enabled by default, unless the initial model has no
            # discrete decisions.

            # Note: these cuts will only exclude integer realizations that are
            # not already in the primary GDPopt_integer_cuts ConstraintList.
            GDPopt.no_backtracking = ConstraintList(
                doc='explored integer cuts')

            # Set up iteration counters
            solve_data.master_iteration = 0
            solve_data.mip_iteration = 0
            solve_data.nlp_iteration = 0

            # set up bounds
            solve_data.LB = float('-inf')
            solve_data.UB = float('inf')
            solve_data.iteration_log = {}

            # Flag indicating whether the solution improved in the past
            # iteration or not
            solve_data.feasible_solution_improved = False

            # Initialize the master problem
            with time_code(solve_data.timing, 'initialization'):
                GDPopt_initialize_master(solve_data, config)

            # Algorithm main loop
            with time_code(solve_data.timing, 'main loop'):
                GDPopt_iteration_loop(solve_data, config)

            if solve_data.best_solution_found is not None:
                # Update values in working model
                copy_var_list_values(
                    from_list=solve_data.best_solution_found.GDPopt_utils.variable_list,
                    to_list=GDPopt.variable_list,
                    config=config)
                # Update values in original model
                copy_var_list_values(
                    GDPopt.variable_list,
                    solve_data.original_model.GDPopt_utils.variable_list,
                    config)

            solve_data.results.problem.lower_bound = solve_data.LB
            solve_data.results.problem.upper_bound = solve_data.UB

        solve_data.results.solver.timing = solve_data.timing
        solve_data.results.solver.user_time = solve_data.timing.total
        solve_data.results.solver.wallclock_time = solve_data.timing.total

        solve_data.results.solver.iterations = solve_data.master_iteration

        return solve_data.results

    #
    # Support "with" statements.
    #
    def __enter__(self):
        return self

    def __exit__(self, t, v, traceback):
        pass
Beispiel #4
0
def default_config_block(solver, init=False):
    config, blocks = ProblemConfigFactory('default').config_block(init)

    #
    # Solver
    #
    solver = ConfigBlock()
    solver.declare('solver name', ConfigValue(
                'glpk',
                str,
                'Solver name',
                None) )
    solver.declare('solver executable', ConfigValue(
        default=None,
        domain=str,
        description="The solver executable used by the solver interface.",
        doc=("The solver executable used by the solver interface. "
             "This option is only valid for those solver interfaces that "
             "interact with a local executable through the shell. If unset, "
             "the solver interface will attempt to find an executable within "
             "the search path of the shell's environment that matches a name "
             "commonly associated with the solver interface.")))
    solver.declare('io format', ConfigValue(
                None,
                str,
                '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.',
                None) )
    solver.declare('manager', ConfigValue(
                'serial',
                str,
                'The technique that is used to manage solver executions.',
                None) )
    solver.declare('options', ConfigBlock(
                implicit=True,
                implicit_domain=ConfigValue(
                    None,
                    str,
                    'Solver option',
                    None),
                description="Options passed into the solver") )
    solver.declare('options string', ConfigValue(
                None,
                str,
                'String describing solver options',
                None) )
    solver.declare('suffixes', ConfigList(
                [],
                ConfigValue(None, str, 'Suffix', None),
                'Solution suffixes that will be extracted by the solver (e.g., rc, dual, or slack). The use of this option is not required when a suffix has been declared on the model using Pyomo\'s Suffix component.',
                None) )
    blocks['solver'] = solver
    #
    solver_list = config.declare('solvers', ConfigList(
                [],
                solver, #ConfigValue(None, str, 'Solver', None),
                'List of solvers.  The first solver in this list is the master solver.',
                None) )
    #
    # Make sure that there is one solver in the list.
    #
    # This will be the solver into which we dump command line options.
    # Note that we CANNOT declare the argparse options on the base block
    # definition above, as we use that definition as the DOMAIN TYPE for
    # the list of solvers.  As that information is NOT copied to
    # derivative blocks, the initial solver entry we are creating would
    # be missing all argparse information. Plus, if we were to have more
    # than one solver defined, we wouldn't want command line options
    # going to both.
    solver_list.append()
    solver_list[0].get('solver name').\
        declare_as_argument('--solver', dest='solver')
    solver_list[0].get('solver executable').\
        declare_as_argument('--solver-executable',
                            dest="solver_executable", metavar="FILE")
    solver_list[0].get('io format').\
        declare_as_argument('--solver-io', dest='io_format', metavar="FORMAT")
    solver_list[0].get('manager').\
        declare_as_argument('--solver-manager', dest="smanager_type",
                            metavar="TYPE")
    solver_list[0].get('options string').\
        declare_as_argument('--solver-options', dest='options_string',
                            metavar="STRING")
    solver_list[0].get('suffixes').\
        declare_as_argument('--solver-suffix', dest="solver_suffixes")

    #
    # Postprocess
    #
    config.declare('postprocess', ConfigList(
                [],
                ConfigValue(None, str, 'Module', None),
                'Specify a Python module that gets executed after optimization.',
                None) ).declare_as_argument(dest='postprocess')

    #
    # Postsolve
    #
    postsolve = config.declare('postsolve', ConfigBlock())
    postsolve.declare('print logfile', ConfigValue(
                False,
                bool,
                'Print the solver logfile after performing optimization.',
                None) ).declare_as_argument('-l', '--log', dest="log")
    postsolve.declare('save results', ConfigValue(
                None,
                str,
                'Specify the filename to which the results are saved.',
                None) ).declare_as_argument('--save-results', dest="save_results", metavar="FILE")
    postsolve.declare('show results', ConfigValue(
                False,
                bool,
                'Print the results object after optimization.',
                None) ).declare_as_argument(dest="show_results")
    postsolve.declare('results format', ConfigValue(
        None,
        str,
        'Specify the results format:  json or yaml.',
        None)
    ).declare_as_argument(
        '--results-format', dest="results_format", metavar="FORMAT"
    ).declare_as_argument(
        '--json', dest="results_format", action="store_const",
        const="json", help="Store results in JSON format")
    postsolve.declare('summary', ConfigValue(
                False,
                bool,
                'Summarize the final solution after performing optimization.',
                None) ).declare_as_argument(dest="summary")
    blocks['postsolve'] = postsolve

    #
    # Runtime
    #
    runtime = blocks['runtime']
    runtime.declare('only instance', ConfigValue(
                False,
                bool,
                "Generate a model instance, and then exit",
                None) ).declare_as_argument('--instance-only', dest='only_instance')
    runtime.declare('stream output', ConfigValue(
                False,
                bool,
                "Stream the solver output to provide information about the solver's progress.",
                None) ).declare_as_argument('--stream-output', '--stream-solver', dest="tee")
    #
    return config, blocks
Beispiel #5
0
class GDPoptSolver(pyomo.common.plugin.Plugin):
    """A decomposition-based GDP solver."""

    pyomo.common.plugin.implements(IOptSolver)
    pyomo.common.plugin.alias(
        'gdpopt',
        doc='The GDPopt decomposition-based '
        'Generalized Disjunctive Programming (GDP) solver')

    _metasolver = False

    CONFIG = ConfigBlock("GDPopt")
    CONFIG.declare("iterlim", ConfigValue(
        default=30, domain=NonNegativeInt,
        description="Iteration limit."
    ))
    CONFIG.declare("strategy", ConfigValue(
        default="LOA", domain=In(["LOA", "GLOA"]),
        description="Decomposition strategy to use."
    ))
    CONFIG.declare("init_strategy", ConfigValue(
        default="set_covering", domain=In(valid_init_strategies.keys()),
        description="Initialization strategy to use.",
        doc="""Selects the initialization strategy to use when generating
        the initial cuts to construct the master problem."""
    ))
    CONFIG.declare("custom_init_disjuncts", ConfigList(
        # domain=ComponentSets of Disjuncts,
        default=None,
        description="List of disjunct sets to use for initialization."
    ))
    CONFIG.declare("max_slack", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Upper bound on slack variables for OA"
    ))
    CONFIG.declare("OA_penalty_factor", ConfigValue(
        default=1000, domain=NonNegativeFloat,
        description="Penalty multiplication term for slack variables on the "
        "objective value."
    ))
    CONFIG.declare("set_cover_iterlim", ConfigValue(
        default=8, domain=NonNegativeInt,
        description="Limit on the number of set covering iterations."
    ))
    CONFIG.declare("mip_solver", ConfigValue(
        default="gurobi",
        description="Mixed integer linear solver to use."
    ))
    mip_solver_args = CONFIG.declare(
        "mip_solver_args", ConfigBlock(implicit=True))
    CONFIG.declare("nlp_solver", ConfigValue(
        default="ipopt",
        description="Nonlinear solver to use"))
    nlp_solver_args = CONFIG.declare(
        "nlp_solver_args", ConfigBlock(implicit=True))
    CONFIG.declare("call_after_master_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the master problem"
    ))
    CONFIG.declare("call_before_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook before calling the subproblem solver"
    ))
    CONFIG.declare("call_after_subproblem_solve", ConfigValue(
        default=_DoNothing,
        description="callback hook after a solution of the "
        "nonlinear subproblem"
    ))
    CONFIG.declare("call_after_subproblem_feasible", ConfigValue(
        default=_DoNothing,
        description="callback hook after feasible solution of "
        "the nonlinear subproblem"
    ))
    CONFIG.declare("algorithm_stall_after", ConfigValue(
        default=2,
        description="number of non-improving master iterations after which "
        "the algorithm will stall and exit."
    ))
    CONFIG.declare("tee", ConfigValue(
        default=False,
        description="Stream output to terminal.",
        domain=bool
    ))
    CONFIG.declare("logger", ConfigValue(
        default='pyomo.contrib.gdpopt',
        description="The logger object or name to use for reporting.",
        domain=a_logger
    ))
    CONFIG.declare("bound_tolerance", ConfigValue(
        default=1E-6, domain=NonNegativeFloat,
        description="Tolerance for bound convergence."
    ))
    CONFIG.declare("small_dual_tolerance", ConfigValue(
        default=1E-8,
        description="When generating cuts, small duals multiplied "
        "by expressions can cause problems. Exclude all duals "
        "smaller in absolue value than the following."
    ))
    CONFIG.declare("integer_tolerance", ConfigValue(
        default=1E-5,
        description="Tolerance on integral values."
    ))
    CONFIG.declare("constraint_tolerance", ConfigValue(
        default=1E-6,
        description="Tolerance on constraint satisfaction."
    ))
    CONFIG.declare("variable_tolerance", ConfigValue(
        default=1E-8,
        description="Tolerance on variable bounds."
    ))
    CONFIG.declare("round_NLP_binaries", ConfigValue(
        default=True,
        description="flag to round binary values to exactly 0 or 1. "
        "Rounding is done before fixing disjuncts."
    ))
    CONFIG.declare("reformulate_integer_vars_using", ConfigValue(
        default=None,
        description="The method to use for reformulating integer variables "
        "into binary for this solver."
    ))

    def available(self, exception_flag=True):
        """Check if solver is available.

        TODO: For now, it is always available. However, sub-solvers may not
        always be available, and so this should reflect that possibility.

        """
        return True

    def version(self):
        """Return a 3-tuple describing the solver version."""
        return __version__

    def solve(self, model, **kwds):
        """Solve the model.

        Warning: this solver is still in beta. Keyword arguments subject to
        change. Undocumented keyword arguments definitely subject to change.

        This function performs all of the GDPopt solver setup and problem
        validation. It then calls upon helper functions to construct the
        initial master approximation and iteration loop.

        Args:
            model (Block): a Pyomo model or block to be solved

        """
        config = self.CONFIG(kwds.pop('options', {}))
        config.set_value(kwds)
        solve_data = GDPoptSolveData()
        created_GDPopt_block = False

        old_logger_level = config.logger.getEffectiveLevel()
        try:
            if config.tee and old_logger_level > logging.INFO:
                # If the logger does not already include INFO, include it.
                config.logger.setLevel(logging.INFO)
            config.logger.info("---Starting GDPopt---")

            # Create a model block on which to store GDPopt-specific utility
            # modeling objects.
            if hasattr(model, 'GDPopt_utils'):
                raise RuntimeError(
                    "GDPopt needs to create a Block named GDPopt_utils "
                    "on the model object, but an attribute with that name "
                    "already exists.")
            else:
                created_GDPopt_block = True
                model.GDPopt_utils = Block(
                    doc="Container for GDPopt solver utility modeling objects")

            solve_data.original_model = model

            solve_data.working_model = clone_orig_model_with_lists(model)
            GDPopt = solve_data.working_model.GDPopt_utils
            record_original_model_statistics(solve_data, config)

            solve_data.current_strategy = config.strategy

            # Reformulate integer variables to binary
            reformulate_integer_variables(solve_data.working_model, config)

            # Save ordered lists of main modeling components, so that data can
            # be easily transferred between future model clones.
            build_ordered_component_lists(solve_data.working_model)
            record_working_model_statistics(solve_data, config)
            solve_data.results.solver.name = 'GDPopt ' + str(self.version())

            # Save model initial values. These are used later to initialize NLP
            # subproblems.
            solve_data.initial_var_values = list(
                v.value for v in GDPopt.working_var_list)

            # Store the initial model state as the best solution found. If we
            # find no better solution, then we will restore from this copy.
            solve_data.best_solution_found = solve_data.initial_var_values

            # Validate the model to ensure that GDPopt is able to solve it.
            if not model_is_valid(solve_data, config):
                return

            # Maps in order to keep track of certain generated constraints
            GDPopt.oa_cut_map = ComponentMap()

            # Integer cuts exclude particular discrete decisions
            GDPopt.integer_cuts = ConstraintList(doc='integer cuts')

            # Feasible integer cuts exclude discrete realizations that have
            # been explored via an NLP subproblem. Depending on model
            # characteristics, the user may wish to revisit NLP subproblems
            # (with a different initialization, for example). Therefore, these
            # cuts are not enabled by default, unless the initial model has no
            # discrete decisions.

            # Note: these cuts will only exclude integer realizations that are
            # not already in the primary GDPopt_integer_cuts ConstraintList.
            GDPopt.no_backtracking = ConstraintList(
                doc='explored integer cuts')

            # Set up iteration counters
            solve_data.master_iteration = 0
            solve_data.mip_iteration = 0
            solve_data.nlp_iteration = 0

            # set up bounds
            solve_data.LB = float('-inf')
            solve_data.UB = float('inf')
            solve_data.iteration_log = {}

            # Flag indicating whether the solution improved in the past
            # iteration or not
            solve_data.feasible_solution_improved = False

            # Initialize the master problem
            GDPopt_initialize_master(solve_data, config)

            # Algorithm main loop
            GDPopt_iteration_loop(solve_data, config)

            # Update values in working model
            copy_var_list_values(
                from_list=solve_data.best_solution_found,
                to_list=GDPopt.working_var_list,
                config=config)
            GDPopt.objective_value.set_value(
                value(solve_data.working_objective_expr, exception=False))

            # Update values in original model
            copy_var_list_values(
                GDPopt.orig_var_list,
                solve_data.original_model.GDPopt_utils.orig_var_list,
                config)

            solve_data.results.problem.lower_bound = solve_data.LB
            solve_data.results.problem.upper_bound = solve_data.UB

        finally:
            config.logger.setLevel(old_logger_level)
            if created_GDPopt_block:
                model.del_component('GDPopt_utils')
def _define_turbine_multistage_config(config):
    config.declare(
        "dynamic",
        ConfigValue(
            domain=In([True, False]),
            default=False,
            description="Dynamic model flag",
            doc="Indicates whether the model is dynamic.",
        ),
    )
    config.declare(
        "has_holdup",
        ConfigValue(
            default=useDefault,
            domain=In([useDefault, True, False]),
            description="Holdup construction flag",
            doc="""Indicates whether holdup terms should be constructed or not.
Must be True if dynamic = True,
**default** - False.
**Valid values:** {
**True** - construct holdup terms,
**False** - do not construct holdup terms}""",
        ),
    )
    config.declare(
        "has_phase_equilibrium",
        ConfigValue(
            default=False,
            domain=In([True, False]),
            description="Calculate phase equilibrium in mixed stream",
            doc="""Argument indicating whether phase equilibrium should be
calculated for the resulting mixed stream,
**default** - False.
**Valid values:** {
**True** - calculate phase equilibrium in mixed stream,
**False** - do not calculate equilibrium in mixed stream.}""",
        ),
    )
    config.declare(
        "material_balance_type",
        ConfigValue(
            default=MaterialBalanceType.componentTotal,
            domain=In(MaterialBalanceType),
            description="Material balance construction flag",
            doc="""Indicates what type of mass balance should be constructed,
**default** - MaterialBalanceType.componentTotal`.
**Valid values:** {
**MaterialBalanceType.none** - exclude material balances,
**MaterialBalanceType.componentPhase** - use phase component balances,
**MaterialBalanceType.componentTotal** - use total component balances,
**MaterialBalanceType.elementTotal** - use total element balances,
**MaterialBalanceType.total** - use total material balance.}""",
        ),
    )
    config.declare(
        "property_package",
        ConfigValue(
            default=useDefault,
            domain=is_physical_parameter_block,
            description="Property package to use for control volume",
            doc="""Property parameter object used to define property
calculations, **default** - useDefault.
**Valid values:** {
**useDefault** - use default package from parent model or flowsheet,
**PropertyParameterObject** - a PropertyParameterBlock object.}""",
        ),
    )
    config.declare(
        "property_package_args",
        ConfigBlock(
            implicit=True,
            description="Arguments to use for constructing property packages",
            doc="""A ConfigBlock with arguments to be passed to a property
block(s) and used when constructing these,
**default** - None.
**Valid values:** {
see property package for documentation.}""",
        ),
    )
    config.declare(
        "num_parallel_inlet_stages",
        ConfigValue(
            default=4,
            domain=int,
            description="Number of parallel inlet stages to simulate partial "
            "arc admission.  Default=4",
        ),
    )
    config.declare(
        "num_hp",
        ConfigValue(
            default=2,
            domain=int,
            description="Number of high pressure stages not including inlet stage",
            doc="Number of high pressure stages not including inlet stage",
        ),
    )
    config.declare(
        "num_ip",
        ConfigValue(
            default=10,
            domain=int,
            description="Number of intermediate pressure stages",
            doc="Number of intermediate pressure stages",
        ),
    )
    config.declare(
        "num_lp",
        ConfigValue(
            default=5,
            domain=int,
            description="Number of low pressure stages not including outlet stage",
            doc="Number of low pressure stages not including outlet stage",
        ),
    )
    config.declare(
        "hp_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitters in HP section",
            doc="A list of index locations of splitters in the HP section. The"
            " indexes indicate after which stage to include splitters.  0 is "
            "between the inlet stage and the first regular HP stage.",
        ),
    )
    config.declare(
        "ip_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitters in IP section",
            doc="A list of index locations of splitters in the IP section. The"
            " indexes indicate after which stage to include splitters.",
        ),
    )
    config.declare(
        "lp_split_locations",
        ConfigList(
            default=[],
            domain=int,
            description="Locations of splitter in LP section",
            doc="A list of index locations of splitters in the LP section. The"
            " indexes indicate after which stage to include splitters.",
        ),
    )
    config.declare(
        "hp_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="HP Turbine stages to not connect to next with an arc.",
            doc="HP Turbine stages to not connect to next with an arc. This is"
            "usually used to insert addtional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "ip_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="IP Turbine stages to not connect to next with an arc.",
            doc="IP Turbine stages to not connect to next with an arc. This is"
            " usually used to insert addtional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "lp_disconnect",
        ConfigList(
            default=[],
            domain=int,
            description="LP Turbine stages to not connect to next with an arc.",
            doc="LP Turbine stages to not connect to next with an arc. This is"
            " usually used to insert addtional units between stages on a "
            "flowsheet, such as a reheater",
        ),
    )
    config.declare(
        "hp_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description="Dict, hp split index: number of splitter outlets, if not 2",
        ),
    )
    config.declare(
        "ip_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description="Dict, ip split index: number of splitter outlets, if not 2",
        ),
    )
    config.declare(
        "lp_split_num_outlets",
        ConfigValue(
            default={},
            domain=dict,
            description="Dict, lp split index: number of splitter outlets, if not 2",
        ),
    )
Beispiel #7
0
class GDPoptSolver(object):
    """Decomposition solver for Generalized Disjunctive Programming (GDP) problems.

    The GDPopt (Generalized Disjunctive Programming optimizer) solver applies a
    variety of decomposition-based approaches to solve Generalized Disjunctive
    Programming (GDP) problems. GDP models can include nonlinear, continuous
    variables and constraints, as well as logical conditions.

    These approaches include:

    - Outer approximation
    - Partial surrogate cuts [pending]
    - Generalized Bender decomposition [pending]

    This solver implementation was developed by Carnegie Mellon University in the
    research group of Ignacio Grossmann.

    For nonconvex problems, the bounds self.LB and self.UB may not be rigorous.

    Questions: Please make a post at StackOverflow and/or contact Qi Chen
    <https://github.com/qtothec>.

    Keyword arguments below are specified for the :code:`solve` function.

    """

    _metasolver = False

    CONFIG = ConfigBlock("GDPopt")
    CONFIG.declare(
        "iterlim",
        ConfigValue(default=30,
                    domain=NonNegativeInt,
                    description="Iteration limit."))
    CONFIG.declare(
        "strategy",
        ConfigValue(default="LOA",
                    domain=In(["LOA", "GLOA"]),
                    description="Decomposition strategy to use."))
    CONFIG.declare(
        "init_strategy",
        ConfigValue(
            default="set_covering",
            domain=In(valid_init_strategies.keys()),
            description="Initialization strategy to use.",
            doc="""Selects the initialization strategy to use when generating
        the initial cuts to construct the master problem."""))
    CONFIG.declare(
        "custom_init_disjuncts",
        ConfigList(
            # domain=ComponentSets of Disjuncts,
            default=None,
            description="List of disjunct sets to use for initialization."))
    CONFIG.declare(
        "max_slack",
        ConfigValue(default=1000,
                    domain=NonNegativeFloat,
                    description="Upper bound on slack variables for OA"))
    CONFIG.declare(
        "OA_penalty_factor",
        ConfigValue(
            default=1000,
            domain=NonNegativeFloat,
            description="Penalty multiplication term for slack variables on the "
            "objective value."))
    CONFIG.declare(
        "set_cover_iterlim",
        ConfigValue(
            default=8,
            domain=NonNegativeInt,
            description="Limit on the number of set covering iterations."))
    CONFIG.declare(
        "mip_solver",
        ConfigValue(default="gurobi",
                    description="Mixed integer linear solver to use."))
    CONFIG.declare(
        "mip_presolve",
        ConfigValue(
            default="true",
            description=
            "Flag to enable or diable Pyomo MIP presolve. Default=True.",
            domain=bool))
    mip_solver_args = CONFIG.declare("mip_solver_args",
                                     ConfigBlock(implicit=True))
    CONFIG.declare(
        "nlp_solver",
        ConfigValue(default="ipopt", description="Nonlinear solver to use"))
    nlp_solver_args = CONFIG.declare("nlp_solver_args",
                                     ConfigBlock(implicit=True))
    CONFIG.declare(
        "nlp_presolve",
        ConfigValue(
            default=True,
            description="Flag to enable or disable NLP presolve. Default=True.",
            domain=bool))
    CONFIG.declare(
        "call_before_master_solve",
        ConfigValue(
            default=_DoNothing,
            description="callback hook before calling the master problem solver"
        ))
    CONFIG.declare(
        "call_after_master_solve",
        ConfigValue(
            default=_DoNothing,
            description="callback hook after a solution of the master problem")
    )
    CONFIG.declare(
        "call_before_subproblem_solve",
        ConfigValue(
            default=_DoNothing,
            description="callback hook before calling the subproblem solver"))
    CONFIG.declare(
        "call_after_subproblem_solve",
        ConfigValue(default=_DoNothing,
                    description="callback hook after a solution of the "
                    "nonlinear subproblem"))
    CONFIG.declare(
        "call_after_subproblem_feasible",
        ConfigValue(default=_DoNothing,
                    description="callback hook after feasible solution of "
                    "the nonlinear subproblem"))
    CONFIG.declare(
        "algorithm_stall_after",
        ConfigValue(
            default=2,
            description="number of non-improving master iterations after which "
            "the algorithm will stall and exit."))
    CONFIG.declare(
        "tee",
        ConfigValue(default=False,
                    description="Stream output to terminal.",
                    domain=bool))
    CONFIG.declare(
        "logger",
        ConfigValue(
            default='pyomo.contrib.gdpopt',
            description="The logger object or name to use for reporting.",
            domain=a_logger))
    CONFIG.declare(
        "bound_tolerance",
        ConfigValue(default=1E-6,
                    domain=NonNegativeFloat,
                    description="Tolerance for bound convergence."))
    CONFIG.declare(
        "small_dual_tolerance",
        ConfigValue(default=1E-8,
                    description="When generating cuts, small duals multiplied "
                    "by expressions can cause problems. Exclude all duals "
                    "smaller in absolue value than the following."))
    CONFIG.declare(
        "integer_tolerance",
        ConfigValue(default=1E-5, description="Tolerance on integral values."))
    CONFIG.declare(
        "constraint_tolerance",
        ConfigValue(default=1E-6,
                    description="Tolerance on constraint satisfaction."))
    CONFIG.declare(
        "variable_tolerance",
        ConfigValue(default=1E-8, description="Tolerance on variable bounds."))
    CONFIG.declare(
        "zero_tolerance",
        ConfigValue(default=1E-15,
                    description="Tolerance on variable equal to zero."))
    CONFIG.declare(
        "round_NLP_binaries",
        ConfigValue(
            default=True,
            description="flag to round binary values to exactly 0 or 1. "
            "Rounding is done before fixing disjuncts."))
    CONFIG.declare(
        "reformulate_integer_vars_using",
        ConfigValue(
            default=None,
            description="The method to use for reformulating integer variables "
            "into binary for this solver."))

    __doc__ = add_docstring_list(__doc__, CONFIG)

    def available(self, exception_flag=True):
        """Check if solver is available.

        TODO: For now, it is always available. However, sub-solvers may not
        always be available, and so this should reflect that possibility.

        """
        return True

    def version(self):
        """Return a 3-tuple describing the solver version."""
        return __version__

    def solve(self, model, **kwds):
        """Solve the model.

        Warning: this solver is still in beta. Keyword arguments subject to
        change. Undocumented keyword arguments definitely subject to change.

        This function performs all of the GDPopt solver setup and problem
        validation. It then calls upon helper functions to construct the
        initial master approximation and iteration loop.

        Args:
            model (Block): a Pyomo model or block to be solved

        """
        config = self.CONFIG(kwds.pop('options', {}))
        config.set_value(kwds)
        solve_data = GDPoptSolveData()
        solve_data.results = SolverResults()
        solve_data.timing = Container()

        old_logger_level = config.logger.getEffectiveLevel()
        with time_code(solve_data.timing, 'total'), \
                restore_logger_level(config.logger), \
                create_utility_block(model, 'GDPopt_utils', solve_data):
            if config.tee and old_logger_level > logging.INFO:
                # If the logger does not already include INFO, include it.
                config.logger.setLevel(logging.INFO)
            config.logger.info("---Starting GDPopt---")

            solve_data.original_model = model

            build_ordered_component_lists(model, solve_data, prefix='orig')
            solve_data.working_model = model.clone()
            GDPopt = solve_data.working_model.GDPopt_utils
            record_original_model_statistics(solve_data, config)

            solve_data.current_strategy = config.strategy

            # Reformulate integer variables to binary
            reformulate_integer_variables(solve_data.working_model, config)
            process_objective(solve_data, config)

            # Save ordered lists of main modeling components, so that data can
            # be easily transferred between future model clones.
            build_ordered_component_lists(solve_data.working_model,
                                          solve_data,
                                          prefix='working')
            record_working_model_statistics(solve_data, config)
            solve_data.results.solver.name = 'GDPopt %s - %s' % (str(
                self.version()), config.strategy)

            # Save model initial values. These are used later to initialize NLP
            # subproblems.
            solve_data.initial_var_values = list(
                v.value for v in GDPopt.working_var_list)

            # Store the initial model state as the best solution found. If we
            # find no better solution, then we will restore from this copy.
            solve_data.best_solution_found = solve_data.initial_var_values

            # Validate the model to ensure that GDPopt is able to solve it.
            if not model_is_valid(solve_data, config):
                return

            # Maps in order to keep track of certain generated constraints
            GDPopt.oa_cut_map = ComponentMap()

            # Integer cuts exclude particular discrete decisions
            GDPopt.integer_cuts = ConstraintList(doc='integer cuts')

            # Feasible integer cuts exclude discrete realizations that have
            # been explored via an NLP subproblem. Depending on model
            # characteristics, the user may wish to revisit NLP subproblems
            # (with a different initialization, for example). Therefore, these
            # cuts are not enabled by default, unless the initial model has no
            # discrete decisions.

            # Note: these cuts will only exclude integer realizations that are
            # not already in the primary GDPopt_integer_cuts ConstraintList.
            GDPopt.no_backtracking = ConstraintList(
                doc='explored integer cuts')

            # Set up iteration counters
            solve_data.master_iteration = 0
            solve_data.mip_iteration = 0
            solve_data.nlp_iteration = 0

            # set up bounds
            solve_data.LB = float('-inf')
            solve_data.UB = float('inf')
            solve_data.iteration_log = {}

            # Flag indicating whether the solution improved in the past
            # iteration or not
            solve_data.feasible_solution_improved = False

            # Initialize the master problem
            with time_code(solve_data.timing, 'initialization'):
                GDPopt_initialize_master(solve_data, config)

            # Algorithm main loop
            with time_code(solve_data.timing, 'main loop'):
                GDPopt_iteration_loop(solve_data, config)

            # Update values in working model
            copy_var_list_values(from_list=solve_data.best_solution_found,
                                 to_list=GDPopt.working_var_list,
                                 config=config)
            GDPopt.objective_value.set_value(
                value(solve_data.working_objective_expr, exception=False))

            # Update values in original model
            copy_var_list_values(
                GDPopt.orig_var_list,
                solve_data.original_model.GDPopt_utils.orig_var_list, config)

            solve_data.results.problem.lower_bound = solve_data.LB
            solve_data.results.problem.upper_bound = solve_data.UB

        solve_data.results.solver.timing = solve_data.timing

        return solve_data.results

    #
    # Support "with" statements.
    #
    def __enter__(self):
        return self

    def __exit__(self, t, v, traceback):
        pass
Beispiel #8
0
def minlp_config_block(init=False):
    config = ConfigBlock(
        "Configuration for a canonical model construction and optimization sequence"
    )
    blocks = {}

    #
    # Data
    #
    data = config.declare('data', ConfigBlock())
    data.declare(
        'files',
        ConfigList([], ConfigValue(None, str, 'Filename', None),
                   'Model data files', None))
    data.declare(
        'namespaces',
        ConfigList(
            [], ConfigValue(None, str, 'Namespace', None),
            'A namespace that is used to select data in Pyomo data files.',
            None)).declare_as_argument('--namespace',
                                       dest='namespaces',
                                       action='append')
    blocks['data'] = data

    #
    # Model
    #
    model = config.declare('model', ConfigBlock())
    model.declare(
        'filename',
        ConfigValue(None, str, 'The Python module that specifies the model',
                    None))
    model.declare(
        'object name',
        ConfigValue(
            None, str,
            'The name of the model object that is created in the specified Pyomo module',
            None)).declare_as_argument('--model-name', dest='model_name')
    model.declare('type', ConfigValue(None, str, 'The problem type', None))
    model.declare(
        'options',
        ConfigBlock(implicit=True,
                    description='Options used to construct the model'))
    model.declare(
        'linearize expressions',
        ConfigValue(
            False, bool,
            'An option intended for use on linear or mixed-integer models in which expression trees in a model (constraints or objectives) are compacted into a more memory-efficient and concise form.',
            None))
    model.declare(
        'save file',
        ConfigValue(
            None, str,
            "The filename to which the model is saved. The suffix of this filename specifies the file format.",
            None))
    model.declare(
        'save format',
        ConfigValue(
            None, str,
            "The format that the model is saved. When specified, this overrides the format implied by the 'save file' option.",
            None))
    model.declare(
        'symbolic solver labels',
        ConfigValue(
            False, bool,
            'When interfacing with the solver, use symbol names derived from the model. For example, \"my_special_variable[1_2_3]\" instead of \"v1\". Useful for debugging. When using the ASL interface (--solver-io=nl), generates corresponding .row (constraints) and .col (variables) files. The ordering in these files provides a mapping from ASL index to symbolic model names.',
            None)).declare_as_argument(dest='symbolic_solver_labels')
    model.declare(
        'file determinism',
        ConfigValue(
            1, int,
            'When interfacing with a solver using file based I/O, set the effort level for ensuring the file creation process is determistic. The default (1) sorts the index of components when transforming the model. Anything less than 1 disables index sorting. Anything greater than 1 additionaly sorts by component name to override declartion order.',
            None)).declare_as_argument(dest='file_determinism')
    blocks['model'] = model

    #
    # Transform
    #
    transform = ConfigBlock()
    transform.declare(
        'name', ConfigValue(None, str, 'Name of the model transformation',
                            None))
    transform.declare(
        'options',
        ConfigBlock(implicit=True, description='Transformation options'))
    blocks['transform'] = transform
    #
    transform_list = config.declare(
        'transform',
        ConfigList([], ConfigValue(None, str, 'Transformation',
                                   None), 'List of model transformations',
                   None)).declare_as_argument(dest='transformations',
                                              action='append')
    if init:
        transform_list.append()

    #
    # Preprocess
    #
    config.declare(
        'preprocess',
        ConfigList([], ConfigValue(
            None, str, 'Module', None
        ), 'Specify a Python module that gets immediately executed (before the optimization model is setup).',
                   None)).declare_as_argument(dest='preprocess')

    #
    # Runtime
    #
    runtime = config.declare('runtime', ConfigBlock())
    runtime.declare(
        'logging',
        ConfigValue(None, str,
                    'Logging level:  quiet, warning, info, verbose, debug',
                    None)).declare_as_argument(dest="logging", metavar="LEVEL")
    runtime.declare(
        'logfile',
        ConfigValue(None, str, 'Redirect output to the specified file.',
                    None)).declare_as_argument(dest="output", metavar="FILE")
    runtime.declare(
        'catch errors',
        ConfigValue(
            False, bool,
            'Trigger failures for exceptions to print the program stack.',
            None)).declare_as_argument('-c', '--catch-errors', dest="catch")
    runtime.declare(
        'disable gc',
        ConfigValue(False, bool, 'Disable the garbage collecter.',
                    None)).declare_as_argument('--disable-gc',
                                               dest='disable_gc')
    runtime.declare(
        'interactive',
        ConfigValue(
            False, bool,
            'After executing Pyomo, launch an interactive Python shell. If IPython is installed, this shell is an IPython shell.',
            None))
    runtime.declare('keep files',
                    ConfigValue(False, bool, 'Keep temporary files',
                                None)).declare_as_argument('-k',
                                                           '--keepfiles',
                                                           dest='keepfiles')
    runtime.declare(
        'paths',
        ConfigList([], ConfigValue(None, str, 'Path', None),
                   'Give a path that is used to find the Pyomo python files.',
                   None)).declare_as_argument('--path', dest='path')
    runtime.declare(
        'profile count',
        ConfigValue(
            0, int,
            'Enable profiling of Python code. The value of this option is the number of functions that are summarized.',
            None)).declare_as_argument(dest='profile_count', metavar='COUNT')
    runtime.declare(
        'profile memory',
        ConfigValue(
            0, int,
            "Report memory usage statistics for the generated instance and any associated processing steps. A value of 0 indicates disabled. A value of 1 forces the print of the total memory after major stages of the pyomo script. A value of 2 forces summary memory statistics after major stages of the pyomo script. A value of 3 forces detailed memory statistics during instance creation and various steps of preprocessing. Values equal to 4 and higher currently provide no additional information. Higher values automatically enable all functionality associated with lower values, e.g., 3 turns on detailed and summary statistics.",
            None))
    runtime.declare(
        'report timing',
        ConfigValue(
            False, bool,
            'Report various timing statistics during model construction.',
            None)).declare_as_argument(dest='report_timing')
    runtime.declare(
        'tempdir',
        ConfigValue(
            None, str,
            'Specify the directory where temporary files are generated.',
            None)).declare_as_argument(dest='tempdir')
    blocks['runtime'] = runtime
    #
    return config, blocks
Beispiel #9
0
    def test_config_integration(self):
        c = ConfigList()
        c.add(1)
        c.add(3)
        c.add(5)
        a = Initializer(c)
        self.assertIs(type(a), ItemInitializer)
        self.assertTrue(a.contains_indices())
        self.assertEqual(list(a.indices()), [0, 1, 2])
        self.assertEqual(a(None, 0), 1)
        self.assertEqual(a(None, 1), 3)
        self.assertEqual(a(None, 2), 5)

        c = ConfigDict()
        c.declare('opt_1', ConfigValue(default=1))
        c.declare('opt_3', ConfigValue(default=3))
        c.declare('opt_5', ConfigValue(default=5))
        a = Initializer(c)
        self.assertIs(type(a), ItemInitializer)
        self.assertTrue(a.contains_indices())
        self.assertEqual(list(a.indices()), ['opt_1', 'opt_3', 'opt_5'])
        self.assertEqual(a(None, 'opt_1'), 1)
        self.assertEqual(a(None, 'opt_3'), 3)
        self.assertEqual(a(None, 'opt_5'), 5)