Beispiel #1
0
def set_up_mip_solver(solve_data, config, regularization_problem):
    """Set up the MIP solver.

    Args:
        solve_data (MindtPySolveData): data container that holds solve-instance data.
        config (ConfigBlock): the specific configurations for MindtPy.
        regularization_problem (bool): whether it is solving a regularization problem.

    Returns:
        mainopt (SolverFactory): the customized MIP solver.
    """
    # Deactivate extraneous IMPORT/EXPORT suffixes
    if config.nlp_solver == 'ipopt':
        getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate()
        getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate()
    if regularization_problem:
        mainopt = SolverFactory(config.mip_regularization_solver)
    else:
        if config.mip_solver == 'gurobi_persistent' and config.single_tree:
            mainopt = GurobiPersistent4MindtPy()
            mainopt.solve_data = solve_data
            mainopt.config = config
        else:
            mainopt = SolverFactory(config.mip_solver)

    # determine if persistent solver is called.
    if isinstance(mainopt, PersistentSolver):
        mainopt.set_instance(solve_data.mip, symbolic_solver_labels=True)
    if config.single_tree and not regularization_problem:
        # Configuration of cplex lazy callback
        if config.mip_solver == 'cplex_persistent':
            lazyoa = mainopt._solver_model.register_callback(
                single_tree.LazyOACallback_cplex)
            # pass necessary data and parameters to lazyoa
            lazyoa.main_mip = solve_data.mip
            lazyoa.solve_data = solve_data
            lazyoa.config = config
            lazyoa.opt = mainopt
            mainopt._solver_model.set_warning_stream(None)
            mainopt._solver_model.set_log_stream(None)
            mainopt._solver_model.set_error_stream(None)
        if config.mip_solver == 'gurobi_persistent':
            mainopt.set_callback(single_tree.LazyOACallback_gurobi)
    if config.use_tabu_list:
        tabulist = mainopt._solver_model.register_callback(
            tabu_list.IncumbentCallback_cplex)
        tabulist.solve_data = solve_data
        tabulist.opt = mainopt
        tabulist.config = config
        mainopt._solver_model.parameters.preprocessing.reduce.set(1)
        # If the callback is used to reject incumbents, the user must set the
        # parameter c.parameters.preprocessing.reduce either to the value 1 (one)
        # to restrict presolve to primal reductions only or to 0 (zero) to disable all presolve reductions
        mainopt._solver_model.set_warning_stream(None)
        mainopt._solver_model.set_log_stream(None)
        mainopt._solver_model.set_error_stream(None)
    return mainopt
Beispiel #2
0
def init_max_binaries(solve_data, config):
    """Initialize by turning on as many binary variables as possible.

    The user would usually want to call _solve_NLP_subproblem after an
    invocation of this function.

    """
    m = solve_data.working_model.clone()
    m.dual.deactivate()
    MindtPy = m.MindtPy_utils
    solve_data.mip_subiter += 1
    config.logger.info(
        "MILP %s: maximize value of binaries" %
        (solve_data.mip_iter))
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()
    objective = next(m.component_data_objects(Objective, active=True))
    objective.deactivate()
    binary_vars = (
        v for v in m.component_data_objects(ctype=Var)
        if v.is_binary() and not v.fixed)
    MindtPy.MindtPy_max_binary_obj = Objective(
        expr=sum(v for v in binary_vars), sense=maximize)

    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    opt = SolverFactory(config.mip_solver)
    if isinstance(opt, PersistentSolver):
        opt.set_instance(m)
    mip_args = dict(config.mip_solver_args)
    if config.mip_solver == 'gams':
        mip_args['add_options'] = mip_args.get('add_options', [])
        mip_args['add_options'].append('option optcr=0.0;')
    results = opt.solve(m, **mip_args)

    solve_terminate_cond = results.solver.termination_condition
    if solve_terminate_cond is tc.optimal:
        copy_var_list_values(
            MindtPy.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list,
            config)

        pass  # good
    elif solve_terminate_cond is tc.infeasible:
        raise ValueError(
            'MILP master problem is infeasible. '
            'Problem may have no more feasible '
            'binary configurations.')
    else:
        raise ValueError(
            'MindtPy unable to handle MILP master termination condition '
            'of %s. Solver message: %s' %
            (solve_terminate_cond, results.solver.message))
Beispiel #3
0
def init_max_binaries(solve_data, config):
    """Initialize by turning on as many binary variables as possible.

    The user would usually want to call _solve_NLP_subproblem after an
    invocation of this function.

    """
    m = solve_data.working_model.clone()
    MindtPy = m.MindtPy_utils
    solve_data.mip_subiter += 1
    config.logger.info(
        "MILP %s: maximize value of binaries" %
        (solve_data.mip_iter))
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()
    objective = next(m.component_data_objects(Objective, active=True))
    objective.deactivate()
    binary_vars = (
        v for v in m.component_data_objects(ctype=Var)
        if v.is_binary() and not v.fixed)
    MindtPy.MindtPy_max_binary_obj = Objective(
        expr=sum(v for v in binary_vars), sense=maximize)

    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    results = SolverFactory(config.mip_solver).solve(m, options=config.mip_solver_args)

    solve_terminate_cond = results.solver.termination_condition
    if solve_terminate_cond is tc.optimal:
        copy_var_list_values(
            MindtPy.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list,
            config)
        pass  # good
    elif solve_terminate_cond is tc.infeasible:
        raise ValueError(
            'MILP master problem is infeasible. '
            'Problem may have no more feasible '
            'binary configurations.')
    else:
        raise ValueError(
            'MindtPy unable to handle MILP master termination condition '
            'of %s. Solver message: %s' %
            (solve_terminate_cond, results.solver.message))
Beispiel #4
0
def solve_OA_master(solve_data, config):
    solve_data.mip_iter += 1
    master_mip = solve_data.mip.clone()
    MindtPy = master_mip.MindtPy_utils
    config.logger.info('MIP %s: Solve master problem.' %
                       (solve_data.mip_iter, ))
    # Set up MILP
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()

    MindtPy.MindtPy_linear_cuts.activate()
    main_objective = next(
        master_mip.component_data_objects(Objective, active=True))
    main_objective.deactivate()

    sign_adjust = 1 if main_objective.sense == minimize else -1
    MindtPy.MindtPy_penalty_expr = Expression(
        expr=sign_adjust * config.OA_penalty_factor *
        sum(v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...]))

    MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr +
                                       MindtPy.MindtPy_penalty_expr,
                                       sense=main_objective.sense)

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(master_mip, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(master_mip, 'ipopt_zU_out', _DoNothing()).deactivate()

    # master_mip.pprint() #print oa master problem for debugging
    with SuppressInfeasibleWarning():
        master_mip_results = SolverFactory(config.mip_solver).solve(
            master_mip, **config.mip_solver_args)
    if master_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        master_mip_results, _ = distinguish_mip_infeasible_or_unbounded(
            master_mip, config)

    return master_mip, master_mip_results
Beispiel #5
0
def setup_mip_solver(solve_data, config, regularization_problem):
    # Deactivate extraneous IMPORT/EXPORT suffixes
    if config.nlp_solver == 'ipopt':
        getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate()
        getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate()
    if regularization_problem:
        mainopt = SolverFactory(config.mip_regularization_solver)
    else:
        mainopt = SolverFactory(config.mip_solver)
    # determine if persistent solver is called.
    if isinstance(mainopt, PersistentSolver):
        mainopt.set_instance(solve_data.mip, symbolic_solver_labels=True)
    if config.single_tree and not regularization_problem:
        # Configuration of lazy callback
        lazyoa = mainopt._solver_model.register_callback(
            single_tree.LazyOACallback_cplex)
        # pass necessary data and parameters to lazyoa
        lazyoa.main_mip = solve_data.mip
        lazyoa.solve_data = solve_data
        lazyoa.config = config
        lazyoa.opt = mainopt
        mainopt._solver_model.set_warning_stream(None)
        mainopt._solver_model.set_log_stream(None)
        mainopt._solver_model.set_error_stream(None)
    if config.use_tabu_list:
        tabulist = mainopt._solver_model.register_callback(
            tabu_list.IncumbentCallback_cplex)
        tabulist.solve_data = solve_data
        tabulist.opt = mainopt
        tabulist.config = config
        mainopt._solver_model.parameters.preprocessing.reduce.set(1)
        # If the callback is used to reject incumbents, the user must set the
        # parameter c.parameters.preprocessing.reduce either to the value 1 (one)
        # to restrict presolve to primal reductions only or to 0 (zero) to disable all presolve reductions
        mainopt._solver_model.set_warning_stream(None)
        mainopt._solver_model.set_log_stream(None)
        mainopt._solver_model.set_error_stream(None)
    return mainopt
Beispiel #6
0
def solve_OA_master(solve_data, config):
    solve_data.mip_iter += 1
    MindtPy = solve_data.mip.MindtPy_utils
    config.logger.info('MIP %s: Solve master problem.' %
                       (solve_data.mip_iter, ))
    # Set up MILP
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()

    MindtPy.MindtPy_linear_cuts.activate()
    main_objective = next(
        solve_data.mip.component_data_objects(Objective, active=True))
    main_objective.deactivate()

    sign_adjust = 1 if main_objective.sense == minimize else -1
    MindtPy.del_component('MindtPy_oa_obj')

    if config.add_slack:
        MindtPy.del_component('MindtPy_penalty_expr')

        MindtPy.MindtPy_penalty_expr = Expression(
            expr=sign_adjust * config.OA_penalty_factor *
            sum(v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...]))

        MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr +
                                           MindtPy.MindtPy_penalty_expr,
                                           sense=main_objective.sense)
    else:
        MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr,
                                           sense=main_objective.sense)
    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate()

    masteropt = SolverFactory(config.mip_solver)
    # determine if persistent solver is called.
    if isinstance(masteropt, PersistentSolver):
        masteropt.set_instance(solve_data.mip, symbolic_solver_labels=True)
    if config.single_tree:
        # Configuration of lazy callback
        lazyoa = masteropt._solver_model.register_callback(
            single_tree.LazyOACallback_cplex)
        # pass necessary data and parameters to lazyoa
        lazyoa.master_mip = solve_data.mip
        lazyoa.solve_data = solve_data
        lazyoa.config = config
        lazyoa.opt = masteropt
        masteropt._solver_model.set_warning_stream(None)
        masteropt._solver_model.set_log_stream(None)
        masteropt._solver_model.set_error_stream(None)
        masteropt.options['timelimit'] = config.time_limit
    master_mip_results = masteropt.solve(
        solve_data.mip, **config.mip_solver_args)  # , tee=True)

    if master_mip_results.solver.termination_condition is tc.optimal:
        if config.single_tree:
            if main_objective.sense == minimize:
                solve_data.LB = max(master_mip_results.problem.lower_bound,
                                    solve_data.LB)
                solve_data.LB_progress.append(solve_data.LB)

                solve_data.UB = min(master_mip_results.problem.upper_bound,
                                    solve_data.UB)
                solve_data.UB_progress.append(solve_data.UB)

    elif master_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        master_mip_results, _ = distinguish_mip_infeasible_or_unbounded(
            solve_data.mip, config)

    return solve_data.mip, master_mip_results
Beispiel #7
0
def init_max_binaries(solve_data, config):
    """Modifies model by maximizing the number of activated binary variables.

    Note - The user would usually want to call solve_subproblem after an invocation 
    of this function.

    Parameters
    ----------
    solve_data : MindtPySolveData
        Data container that holds solve-instance data.
    config : ConfigBlock
        The specific configurations for MindtPy.

    Raises
    ------
    ValueError
        MILP main problem is infeasible.
    ValueError
        MindtPy unable to handle the termination condition of the MILP main problem.
    """
    m = solve_data.working_model.clone()
    if config.calculate_dual:
        m.dual.deactivate()
    MindtPy = m.MindtPy_utils
    solve_data.mip_subiter += 1
    config.logger.debug('Initialization: maximize value of binaries')
    for c in MindtPy.nonlinear_constraint_list:
        c.deactivate()
    objective = next(m.component_data_objects(Objective, active=True))
    objective.deactivate()
    binary_vars = (v for v in m.MindtPy_utils.discrete_variable_list
                   if v.is_binary() and not v.fixed)
    MindtPy.max_binary_obj = Objective(expr=sum(v for v in binary_vars),
                                       sense=maximize)

    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    mipopt = SolverFactory(config.mip_solver)
    if isinstance(mipopt, PersistentSolver):
        mipopt.set_instance(m)
    mip_args = dict(config.mip_solver_args)
    set_solver_options(mipopt, solve_data, config, solver_type='mip')
    results = mipopt.solve(m, tee=config.mip_solver_tee, **mip_args)

    solve_terminate_cond = results.solver.termination_condition
    if solve_terminate_cond is tc.optimal:
        copy_var_list_values(
            MindtPy.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list, config)
        config.logger.info(
            solve_data.log_formatter.format(
                '-', 'Max binary MILP', value(MindtPy.max_binary_obj.expr),
                solve_data.LB, solve_data.UB, solve_data.rel_gap,
                get_main_elapsed_time(solve_data.timing)))
    elif solve_terminate_cond is tc.infeasible:
        raise ValueError('MILP main problem is infeasible. '
                         'Problem may have no more feasible '
                         'binary configurations.')
    elif solve_terminate_cond is tc.maxTimeLimit:
        config.logger.info(
            'NLP subproblem failed to converge within time limit.')
        solve_data.results.solver.termination_condition = tc.maxTimeLimit
    elif solve_terminate_cond is tc.maxIterations:
        config.logger.info(
            'NLP subproblem failed to converge within iteration limit.')
    else:
        raise ValueError(
            'MindtPy unable to handle MILP main termination condition '
            'of %s. Solver message: %s' %
            (solve_terminate_cond, results.solver.message))
Beispiel #8
0
def solve_linear_GDP(linear_GDP_model, solve_data, config):
    """Solves the linear GDP model and attempts to resolve solution issues."""
    m = linear_GDP_model
    GDPopt = m.GDPopt_utils
    # Transform disjunctions
    _bigm = TransformationFactory('gdp.bigm')
    _bigm.handlers[Port] = False
    _bigm.apply_to(m)

    preprocessing_transformations = [
        # # Propagate variable bounds
        # 'contrib.propagate_eq_var_bounds',
        # # Detect fixed variables
        # 'contrib.detect_fixed_vars',
        # # Propagate fixed variables
        # 'contrib.propagate_fixed_vars',
        # # Remove zero terms in linear expressions
        # 'contrib.remove_zero_terms',
        # # Remove terms in equal to zero summations
        # 'contrib.propagate_zero_sum',
        # # Transform bound constraints
        # 'contrib.constraints_to_var_bounds',
        # # Detect fixed variables
        # 'contrib.detect_fixed_vars',
        # # Remove terms in equal to zero summations
        # 'contrib.propagate_zero_sum',
        # Remove trivial constraints
        'contrib.deactivate_trivial_constraints',
    ]
    if config.mip_presolve:
        try:
            fbbt(m, integer_tol=config.integer_tolerance)
            for xfrm in preprocessing_transformations:
                TransformationFactory(xfrm).apply_to(m)
        except InfeasibleConstraintException:
            config.logger.debug("MIP preprocessing detected infeasibility.")
            mip_result = MasterProblemResult()
            mip_result.feasible = False
            mip_result.var_values = list(v.value for v in GDPopt.variable_list)
            mip_result.pyomo_results = SolverResults()
            mip_result.pyomo_results.solver.termination_condition = tc.error
            mip_result.disjunct_values = list(disj.indicator_var.value
                                              for disj in GDPopt.disjunct_list)
            return mip_result

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    # Create solver, check availability
    if not SolverFactory(config.mip_solver).available():
        raise RuntimeError("MIP solver %s is not available." %
                           config.mip_solver)

    # Callback immediately before solving MIP master problem
    config.call_before_master_solve(m, solve_data)

    try:
        with SuppressInfeasibleWarning():
            mip_args = dict(config.mip_solver_args)
            elapsed = get_main_elapsed_time(solve_data.timing)
            remaining = max(config.time_limit - elapsed, 1)
            if config.mip_solver == 'gams':
                mip_args['add_options'] = mip_args.get('add_options', [])
                mip_args['add_options'].append('option reslim=%s;' % remaining)
            elif config.mip_solver == 'multisolve':
                mip_args['time_limit'] = min(
                    mip_args.get('time_limit', float('inf')), remaining)
            results = SolverFactory(config.mip_solver).solve(m, **mip_args)
    except RuntimeError as e:
        if 'GAMS encountered an error during solve.' in str(e):
            config.logger.warning(
                "GAMS encountered an error in solve. Treating as infeasible.")
            mip_result = MasterProblemResult()
            mip_result.feasible = False
            mip_result.var_values = list(v.value for v in GDPopt.variable_list)
            mip_result.pyomo_results = SolverResults()
            mip_result.pyomo_results.solver.termination_condition = tc.error
            mip_result.disjunct_values = list(disj.indicator_var.value
                                              for disj in GDPopt.disjunct_list)
            return mip_result
        else:
            raise
    terminate_cond = results.solver.termination_condition
    if terminate_cond is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        results, terminate_cond = distinguish_mip_infeasible_or_unbounded(
            m, config)
    if terminate_cond is tc.unbounded:
        # Solution is unbounded. Add an arbitrary bound to the objective and resolve.
        # This occurs when the objective is nonlinear. The nonlinear objective is moved
        # to the constraints, and deactivated for the linear master problem.
        obj_bound = 1E15
        config.logger.warning(
            'Linear GDP was unbounded. '
            'Resolving with arbitrary bound values of (-{0:.10g}, {0:.10g}) on the objective. '
            'Check your initialization routine.'.format(obj_bound))
        main_objective = next(m.component_data_objects(Objective, active=True))
        GDPopt.objective_bound = Constraint(expr=(-obj_bound,
                                                  main_objective.expr,
                                                  obj_bound))
        with SuppressInfeasibleWarning():
            results = SolverFactory(config.mip_solver).solve(
                m, **config.mip_solver_args)
        terminate_cond = results.solver.termination_condition

    # Build and return results object
    mip_result = MasterProblemResult()
    mip_result.feasible = True
    mip_result.var_values = list(v.value for v in GDPopt.variable_list)
    mip_result.pyomo_results = results
    mip_result.disjunct_values = list(disj.indicator_var.value
                                      for disj in GDPopt.disjunct_list)

    if terminate_cond in {tc.optimal, tc.locallyOptimal, tc.feasible}:
        pass
    elif terminate_cond is tc.infeasible:
        config.logger.info(
            'Linear GDP is now infeasible. '
            'GDPopt has finished exploring feasible discrete configurations.')
        mip_result.feasible = False
    elif terminate_cond is tc.maxTimeLimit:
        # TODO check that status is actually ok and everything is feasible
        config.logger.info(
            'Unable to optimize linear GDP problem within time limit. '
            'Using current solver feasible solution.')
    elif (terminate_cond is tc.other
          and results.solution.status is SolutionStatus.feasible):
        # load the solution and suppress the warning message by setting
        # solver status to ok.
        config.logger.info('Linear GDP solver reported feasible solution, '
                           'but not guaranteed to be optimal.')
    else:
        raise ValueError('GDPopt unable to handle linear GDP '
                         'termination condition '
                         'of %s. Solver message: %s' %
                         (terminate_cond, results.solver.message))

    return mip_result
Beispiel #9
0
def solve_OA_master(solve_data, config):
    """
    This function solves the MIP master problem for the OA algorithm

    Parameters
    ----------
    solve_data: MindtPy Data Container
        data container that holds solve-instance data
    config: ConfigBlock
        contains the specific configurations for the algorithm

    Returns
    -------
    solve_data.mip: Pyomo model
        the MIP stored in solve_data
    master_mip_results: Pyomo results object
        result from solving the master MIP
    """
    solve_data.mip_iter += 1
    MindtPy = solve_data.mip.MindtPy_utils
    config.logger.info('MIP %s: Solve master problem.' %
                       (solve_data.mip_iter, ))
    # Set up MILP
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()

    MindtPy.MindtPy_linear_cuts.activate()
    main_objective = next(
        solve_data.mip.component_data_objects(Objective, active=True))
    main_objective.deactivate()

    sign_adjust = 1 if main_objective.sense == minimize else -1
    MindtPy.del_component('MindtPy_oa_obj')

    if config.add_slack:
        MindtPy.del_component('MindtPy_penalty_expr')

        MindtPy.MindtPy_penalty_expr = Expression(
            expr=sign_adjust * config.OA_penalty_factor *
            sum(v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...]))

    MindtPy.MindtPy_oa_obj = Objective(
        expr=main_objective.expr +
        (MindtPy.MindtPy_penalty_expr if config.add_slack else 0),
        sense=main_objective.sense)

    if config.use_dual_bound:
        # Delete previously added dual bound constraint
        if MindtPy.MindtPy_linear_cuts.find_component(
                'dual_bound') is not None:
            MindtPy.MindtPy_linear_cuts.del_component('dual_bound')
        if main_objective.sense == minimize:
            MindtPy.MindtPy_linear_cuts.dual_bound = Constraint(
                expr=main_objective.expr +
                (MindtPy.MindtPy_penalty_expr if config.add_slack else 0) >=
                solve_data.LB,
                doc=
                'Objective function expression should improve on the best found dual bound'
            )
        else:
            MindtPy.MindtPy_linear_cuts.dual_bound = Constraint(
                expr=main_objective.expr +
                (MindtPy.MindtPy_penalty_expr if config.add_slack else 0) <=
                solve_data.UB,
                doc=
                'Objective function expression should improve on the best found dual bound'
            )

    # Deactivate extraneous IMPORT/EXPORT suffixes
    if config.nlp_solver == 'ipopt':
        getattr(solve_data.mip, 'ipopt_zL_out', _DoNothing()).deactivate()
        getattr(solve_data.mip, 'ipopt_zU_out', _DoNothing()).deactivate()

    masteropt = SolverFactory(config.mip_solver)
    # determine if persistent solver is called.
    if isinstance(masteropt, PersistentSolver):
        masteropt.set_instance(solve_data.mip, symbolic_solver_labels=True)
    if config.single_tree:
        # Configuration of lazy callback
        lazyoa = masteropt._solver_model.register_callback(
            single_tree.LazyOACallback_cplex)
        # pass necessary data and parameters to lazyoa
        lazyoa.master_mip = solve_data.mip
        lazyoa.solve_data = solve_data
        lazyoa.config = config
        lazyoa.opt = masteropt
        masteropt._solver_model.set_warning_stream(None)
        masteropt._solver_model.set_log_stream(None)
        masteropt._solver_model.set_error_stream(None)
        masteropt.options['timelimit'] = config.time_limit
    if config.threads > 0:
        masteropt.options["threads"] = config.threads
    mip_args = dict(config.mip_solver_args)
    elapsed = get_main_elapsed_time(solve_data.timing)
    remaining = int(max(config.time_limit - elapsed, 1))
    if config.mip_solver == 'gams':
        mip_args['add_options'] = mip_args.get('add_options', [])
        mip_args['add_options'].append('option optcr=0.001;')
        mip_args['add_options'].append('option reslim=%s;' % remaining)
    # elif config.mip_solver == 'glpk':
    #     masteropt.options['timelimit'] = remaining
    master_mip_results = masteropt.solve(solve_data.mip,
                                         tee=config.solver_tee,
                                         **mip_args)

    # if config.single_tree is False and config.add_nogood_cuts is False:

    if master_mip_results.solver.termination_condition is tc.optimal:
        if config.single_tree and config.add_nogood_cuts is False:
            if main_objective.sense == minimize:
                solve_data.LB = max(master_mip_results.problem.lower_bound,
                                    solve_data.LB)
                solve_data.LB_progress.append(solve_data.LB)
            else:
                solve_data.UB = min(master_mip_results.problem.upper_bound,
                                    solve_data.UB)
                solve_data.UB_progress.append(solve_data.UB)

    elif master_mip_results.solver.termination_condition is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        master_mip_results, _ = distinguish_mip_infeasible_or_unbounded(
            solve_data.mip, config)

    return solve_data.mip, master_mip_results
Beispiel #10
0
class MindtPySolver(object):
    """A decomposition-based MINLP solver.
    """

    CONFIG = ConfigBlock("MindtPy")
    CONFIG.declare(
        "bound_tolerance",
        ConfigValue(default=1E-5,
                    domain=PositiveFloat,
                    description="Bound tolerance",
                    doc="Relative tolerance for bound feasibility checks"))
    CONFIG.declare(
        "iteration_limit",
        ConfigValue(
            default=30,
            domain=PositiveInt,
            description="Iteration limit",
            doc="Number of maximum iterations in the decomposition methods"))
    CONFIG.declare(
        "time_limit",
        ConfigValue(
            default=600,
            domain=PositiveInt,
            description="Time limit (seconds, default=600)",
            doc="Seconds allowed until terminated. Note that the time limit can"
            "currently only be enforced between subsolver invocations. You may"
            "need to set subsolver time limits as well."))
    CONFIG.declare(
        "strategy",
        ConfigValue(
            default="OA",
            domain=In(["OA", "GBD", "ECP", "PSC"]),
            description="Decomposition strategy",
            doc="MINLP Decomposition strategy to be applied to the method. "
            "Currently available Outer Approximation (OA), Extended Cutting "
            "Plane (ECP), Partial Surrogate Cuts (PSC), and Generalized "
            "Benders Decomposition (GBD)"))
    CONFIG.declare(
        "init_strategy",
        ConfigValue(
            default="rNLP",
            domain=In(["rNLP", "initial_binary", "max_binary"]),
            description="Initialization strategy",
            doc="Initialization strategy used by any method. Currently the "
            "continuous relaxation of the MINLP (rNLP), solve a maximal "
            "covering problem (max_binary), and fix the initial value for "
            "the integer variables (initial_binary)"))
    CONFIG.declare(
        "integer_cuts",
        ConfigValue(
            default=True,
            domain=bool,
            description="Integer cuts",
            doc=
            "Add integer cuts after finding a feasible solution to the MINLP"))
    CONFIG.declare(
        "max_slack",
        ConfigValue(
            default=1000.0,
            domain=PositiveFloat,
            description="Maximum slack variable",
            doc=
            "Maximum slack variable value allowed for the Outer Approximation "
            "cuts"))
    CONFIG.declare(
        "OA_penalty_factor",
        ConfigValue(
            default=1000.0,
            domain=PositiveFloat,
            description="Outer Approximation slack penalty factor",
            doc=
            "In the objective function of the Outer Approximation method, the "
            "slack variables corresponding to all the constraints get "
            "multiplied by this number and added to the objective"))
    CONFIG.declare(
        "ECP_tolerance",
        ConfigValue(
            default=1E-4,
            domain=PositiveFloat,
            description="ECP tolerance",
            doc=
            "Feasibility tolerance used to determine the stopping criterion in"
            "the ECP method. As long as nonlinear constraint are violated for "
            "more than this tolerance, the method will keep iterating"))
    CONFIG.declare(
        "nlp_solver",
        ConfigValue(
            default="ipopt",
            domain=In(["ipopt"]),
            description="NLP subsolver name",
            doc=
            "Which NLP subsolver is going to be used for solving the nonlinear"
            "subproblems"))
    CONFIG.declare(
        "nlp_solver_args",
        ConfigBlock(
            implicit=True,
            description="NLP subsolver options",
            doc="Which NLP subsolver options to be passed to the solver while "
            "solving the nonlinear subproblems"))
    CONFIG.declare(
        "mip_solver",
        ConfigValue(
            default="gurobi",
            domain=In(["gurobi", "cplex", "cbc", "glpk", "gams"]),
            description="MIP subsolver name",
            doc="Which MIP subsolver is going to be used for solving the mixed-"
            "integer master problems"))
    CONFIG.declare(
        "mip_solver_args",
        ConfigBlock(
            implicit=True,
            description="MIP subsolver options",
            doc="Which MIP subsolver options to be passed to the solver while "
            "solving the mixed-integer master problems"))
    CONFIG.declare(
        "call_after_master_solve",
        ConfigValue(
            default=_DoNothing(),
            domain=None,
            description="Function to be executed after every master problem",
            doc="Callback hook after a solution of the master problem."))
    CONFIG.declare(
        "call_after_subproblem_solve",
        ConfigValue(
            default=_DoNothing(),
            domain=None,
            description="Function to be executed after every subproblem",
            doc="Callback hook after a solution of the nonlinear subproblem."))
    CONFIG.declare(
        "call_after_subproblem_feasible",
        ConfigValue(default=_DoNothing(),
                    domain=None,
                    description=
                    "Function to be executed after every feasible subproblem",
                    doc="Callback hook after a feasible solution"
                    " of the nonlinear subproblem."))
    CONFIG.declare(
        "tee",
        ConfigValue(default=False,
                    description="Stream output to terminal.",
                    domain=bool))
    CONFIG.declare(
        "logger",
        ConfigValue(
            default='pyomo.contrib.mindtpy',
            description="The logger object or name to use for reporting.",
            domain=a_logger))
    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 absolute 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(
        "initial_feas",
        ConfigValue(default=True,
                    description="Apply an initial feasibility step.",
                    domain=bool))
    CONFIG.declare(
        "obj_bound",
        ConfigValue(
            default=1E15,
            domain=PositiveFloat,
            description=
            "Bound applied to the linearization of the objective function if master MILP is unbounded."
        ))
    CONFIG.declare(
        "integer_to_binary",
        ConfigValue(default=False,
                    description=
                    "Convert integer variables to binaries (for integer cuts)",
                    domain=bool))
    CONFIG.declare(
        "add_integer_cuts",
        ConfigValue(
            default=False,
            description=
            "Add integer cuts (no-good cuts) to binary variables to disallow same integer solution again."
            "Note that 'integer_to_binary' flag needs to be used to apply it to actual integers and not just binaries.",
            domain=bool))

    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.
        Warning: at this point in time, if you try to use PSC or GBD with
        anything other than IPOPT as the NLP solver, bad things will happen.
        This is because the suffixes are not in place to extract dual values
        from the variable bounds for any other solver.
        TODO: fix needed with the GBD implementation.
        Args:
            model (Block): a Pyomo model or block to be solved
        """
        config = self.CONFIG(kwds.pop('options', {}))
        config.set_value(kwds)
        solve_data = MindtPySolveData()
        solve_data.results = SolverResults()
        solve_data.timing = Container()

        solve_data.original_model = model
        solve_data.working_model = model.clone()
        if config.integer_to_binary:
            TransformationFactory('contrib.integer_to_binary'). \
                apply_to(solve_data.working_model)

        new_logging_level = logging.INFO if config.tee else None
        with time_code(solve_data.timing, 'total', is_main_timer=True), \
             lower_logger_level_to(config.logger, new_logging_level), \
             create_utility_block(solve_data.working_model, 'MindtPy_utils', solve_data):
            config.logger.info("---Starting MindtPy---")

            MindtPy = solve_data.working_model.MindtPy_utils
            setup_results_object(solve_data, config)
            process_objective(solve_data, config)

            # Save model initial values.
            solve_data.initial_var_values = list(
                v.value for v in MindtPy.variable_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 = None

            # Record solver name
            solve_data.results.solver.name = 'MindtPy' + str(config.strategy)

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

            # Create a model block in which to store the generated feasibility
            # slack constraints. Do not leave the constraints on by default.
            feas = MindtPy.MindtPy_feas = Block()
            feas.deactivate()
            feas.feas_constraints = ConstraintList(
                doc='Feasibility Problem Constraints')

            # Create a model block in which to store the generated linear
            # constraints. Do not leave the constraints on by default.
            lin = MindtPy.MindtPy_linear_cuts = Block()
            lin.deactivate()

            # Integer cuts exclude particular discrete decisions
            lin.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.
            #
            # Note: these cuts will only exclude integer realizations that are
            # not already in the primary integer_cuts ConstraintList.
            lin.feasible_integer_cuts = ConstraintList(
                doc='explored integer cuts')
            lin.feasible_integer_cuts.deactivate()

            # Set up iteration counters
            solve_data.nlp_iter = 0
            solve_data.mip_iter = 0
            solve_data.mip_subiter = 0

            # set up bounds
            solve_data.LB = float('-inf')
            solve_data.UB = float('inf')
            solve_data.LB_progress = [solve_data.LB]
            solve_data.UB_progress = [solve_data.UB]

            # Set of NLP iterations for which cuts were generated
            lin.nlp_iters = Set(dimen=1)

            # Set of MIP iterations for which cuts were generated in ECP
            lin.mip_iters = Set(dimen=1)

            nonlinear_constraints = [
                c for c in MindtPy.constraint_list
                if c.body.polynomial_degree() not in (1, 0)
            ]
            lin.nl_constraint_set = RangeSet(
                len(nonlinear_constraints),
                doc="Integer index set over the nonlinear constraints")
            feas.constraint_set = RangeSet(
                len(MindtPy.constraint_list),
                doc="integer index set over the constraints")

            # # Mapping Constraint -> integer index
            # MindtPy.feas_map = {}
            # # Mapping integer index -> Constraint
            # MindtPy.feas_inverse_map = {}
            # # Generate the two maps. These maps may be helpful for later
            # # interpreting indices on the slack variables or generated cuts.
            # for c, n in zip(MindtPy.constraint_list, feas.constraint_set):
            #     MindtPy.feas_map[c] = n
            #     MindtPy.feas_inverse_map[n] = c

            # Create slack variables for OA cuts
            lin.slack_vars = VarList(bounds=(0, config.max_slack),
                                     initialize=0,
                                     domain=NonNegativeReals)
            # Create slack variables for feasibility problem
            feas.slack_var = Var(feas.constraint_set,
                                 domain=NonNegativeReals,
                                 initialize=1)

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

            if not hasattr(solve_data.working_model, 'ipopt_zL_out'):
                solve_data.working_model.ipopt_zL_out = Suffix(
                    direction=Suffix.IMPORT)
            if not hasattr(solve_data.working_model, 'ipopt_zU_out'):
                solve_data.working_model.ipopt_zU_out = Suffix(
                    direction=Suffix.IMPORT)

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

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

            if solve_data.best_solution_found is not None:
                # Update values in original model
                copy_var_list_values(from_list=solve_data.best_solution_found.
                                     MindtPy_utils.variable_list,
                                     to_list=MindtPy.variable_list,
                                     config=config)
                # MindtPy.objective_value.set_value(
                #     value(solve_data.working_objective_expr, exception=False))
                copy_var_list_values(
                    MindtPy.variable_list,
                    solve_data.original_model.component_data_objects(Var),
                    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.mip_iter

        return solve_data.results

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

    def __exit__(self, t, v, traceback):
        pass
Beispiel #11
0
def solve_OA_master(solve_data, config):
    solve_data.mip_iter += 1
    m = solve_data.mip.clone()
    MindtPy = m.MindtPy_utils
    config.logger.info('MIP %s: Solve master problem.' %
                       (solve_data.mip_iter, ))
    # Set up MILP
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()

    MindtPy.MindtPy_linear_cuts.activate()
    main_objective = next(m.component_data_objects(Objective, active=True))
    main_objective.deactivate()

    sign_adjust = 1 if main_objective.sense == minimize else -1
    MindtPy.MindtPy_penalty_expr = Expression(
        expr=sign_adjust * config.OA_penalty_factor *
        sum(v for v in MindtPy.MindtPy_linear_cuts.slack_vars[...]))

    MindtPy.MindtPy_oa_obj = Objective(expr=main_objective.expr +
                                       MindtPy.MindtPy_penalty_expr,
                                       sense=main_objective.sense)

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    # m.pprint() #print oa master problem for debugging
    with SuppressInfeasibleWarning():
        results = SolverFactory(config.mip_solver).solve(
            m, **config.mip_solver_args)
    master_terminate_cond = results.solver.termination_condition
    if master_terminate_cond is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        results, master_terminate_cond = distinguish_mip_infeasible_or_unbounded(
            m, config)

    # Process master problem result
    if master_terminate_cond is tc.optimal:
        # proceed. Just need integer values
        copy_var_list_values(
            m.MindtPy_utils.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list, config)

        if main_objective.sense == minimize:
            solve_data.LB = max(value(MindtPy.MindtPy_oa_obj.expr),
                                solve_data.LB)
            solve_data.LB_progress.append(solve_data.LB)
        else:
            solve_data.UB = min(value(MindtPy.MindtPy_oa_obj.expr),
                                solve_data.UB)
            solve_data.UB_progress.append(solve_data.UB)
        config.logger.info(
            'MIP %s: OBJ: %s  LB: %s  UB: %s' %
            (solve_data.mip_iter, value(
                MindtPy.MindtPy_oa_obj.expr), solve_data.LB, solve_data.UB))
    elif master_terminate_cond is tc.infeasible:
        config.logger.info(
            'MILP master problem is infeasible. '
            'Problem may have no more feasible binary combinations.')
        if solve_data.mip_iter == 1:
            config.logger.info(
                'MindtPy initialization may have generated poor '
                'quality cuts.')
    elif master_terminate_cond is tc.maxTimeLimit:
        # TODO check that status is actually ok and everything is feasible
        config.logger.info('Unable to optimize MILP master problem '
                           'within time limit. '
                           'Using current solver feasible solution.')
        copy_var_list_values(
            m.MindtPy_utils.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list, config)
        if MindtPy.obj.sense == minimize:
            solve_data.LB = max(value(MindtPy.obj.expr), solve_data.LB)
            solve_data.LB_progress.append(solve_data.LB)
        else:
            solve_data.UB = min(value(MindtPy.obj.expr), solve_data.UB)
            solve_data.UB_progress.append(solve_data.UB)
        config.logger.info('MIP %s: OBJ: %s  LB: %s  UB: %s' %
                           (solve_data.mip_iter, value(MindtPy.obj.expr),
                            solve_data.LB, solve_data.UB))
    elif (master_terminate_cond is tc.other
          and results.solution.status is SolutionStatus.feasible):
        # load the solution and suppress the warning message by setting
        # solver status to ok.
        config.logger.info('MILP solver reported feasible solution, '
                           'but not guaranteed to be optimal.')
        copy_var_list_values(
            m.MindtPy_utils.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list, config)
        if MindtPy.obj.sense == minimize:
            solve_data.LB = max(value(MindtPy.MindtPy_oa_obj.expr),
                                solve_data.LB)
            solve_data.LB_progress.append(solve_data.LB)
        else:
            solve_data.UB = min(value(MindtPy.MindtPy_oa_obj.expr),
                                solve_data.UB)
            solve_data.UB_progress.append(solve_data.UB)
        config.logger.info(
            'MIP %s: OBJ: %s  LB: %s  UB: %s' %
            (solve_data.mip_iter, value(
                MindtPy.MindtPy_oa_obj.expr), solve_data.LB, solve_data.UB))
    elif master_terminate_cond is tc.infeasible:
        config.logger.info('MILP master problem is infeasible. '
                           'Problem may have no more feasible '
                           'binary configurations.')
        if solve_data.mip_iter == 1:
            config.logger.warn(
                'MindtPy initialization may have generated poor '
                'quality cuts.')
        # set optimistic bound to infinity
        if main_objective.sense == minimize:
            solve_data.LB = float('inf')
            solve_data.LB_progress.append(solve_data.UB)
        else:
            solve_data.UB = float('-inf')
            solve_data.UB_progress.append(solve_data.UB)
    elif master_terminate_cond is tc.unbounded:
        # Solution is unbounded. Add an arbitrary bound to the objective and resolve.
        # This occurs when the objective is nonlinear. The nonlinear objective is moved
        # to the constraints, and deactivated for the linear master problem.
        config.logger.warning(
            'Master MILP was unbounded. '
            'Resolving with arbitrary bound values of (-{0:.10g}, {0:.10g}) on the objective. '
            'You can change this bound with the option obj_bound.'.format(
                config.obj_bound))
        main_objective = next(m.component_data_objects(Objective, active=True))
        MindtPy.objective_bound = Constraint(expr=(-config.obj_bound,
                                                   main_objective.expr,
                                                   config.obj_bound))
        with SuppressInfeasibleWarning():
            results = SolverFactory(config.mip_solver).solve(
                m, **config.mip_solver_args)

    else:
        raise ValueError(
            'MindtPy unable to handle MILP master termination condition '
            'of %s. Solver message: %s' %
            (master_terminate_cond, results.solver.message))

    # Call the MILP post-solve callback
    config.call_after_master_solve(m, solve_data)
Beispiel #12
0
def solve_linear_GDP(linear_GDP_model, solve_data, config):
    m = linear_GDP_model
    GDPopt = m.GDPopt_utils
    # Transform disjunctions
    TransformationFactory('gdp.bigm').apply_to(m)

    preprocessing_transformations = [
        # Propagate variable bounds
        'contrib.propagate_eq_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Propagate fixed variables
        'contrib.propagate_fixed_vars',
        # Remove zero terms in linear expressions
        'contrib.remove_zero_terms',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Transform bound constraints
        'contrib.constraints_to_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Remove trivial constraints
        'contrib.deactivate_trivial_constraints'
    ]
    for xfrm in preprocessing_transformations:
        TransformationFactory(xfrm).apply_to(m)

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    # Load solutions is false because otherwise an annoying error message
    # appears for infeasible models.
    mip_solver = SolverFactory(config.mip)
    if not mip_solver.available():
        raise RuntimeError("MIP solver %s is not available." % config.mip)
    results = mip_solver.solve(m, load_solutions=False, **config.mip_options)
    terminate_cond = results.solver.termination_condition
    if terminate_cond is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        tmp_options = deepcopy(config.mip_options)
        # TODO This solver option is specific to Gurobi.
        tmp_options['DualReductions'] = 0
        results = mip_solver.solve(m, load_solutions=False, **tmp_options)
        terminate_cond = results.solver.termination_condition

    if terminate_cond is tc.optimal:
        m.solutions.load_from(results)
        return True, list(v.value for v in GDPopt.working_var_list)
    elif terminate_cond is tc.infeasible:
        config.logger.info(
            'Linear GDP is infeasible. '
            'Problem may have no more feasible discrete configurations.')
        return False
    elif terminate_cond is tc.maxTimeLimit:
        # TODO check that status is actually ok and everything is feasible
        config.logger.info(
            'Unable to optimize linear GDP problem within time limit. '
            'Using current solver feasible solution.')
        results.solver.status = SolverStatus.ok
        m.solutions.load_from(results)
        return True, list(v.value for v in GDPopt.working_var_list)
    elif (terminate_cond is tc.other
          and results.solution.status is SolutionStatus.feasible):
        # load the solution and suppress the warning message by setting
        # solver status to ok.
        config.logger.info('Linear GDP solver reported feasible solution, '
                           'but not guaranteed to be optimal.')
        results.solver.status = SolverStatus.ok
        m.solutions.load_from(results)
        return True, list(v.value for v in GDPopt.working_var_list)
    else:
        raise ValueError('GDPopt unable to handle linear GDP '
                         'termination condition '
                         'of %s. Solver message: %s' %
                         (terminate_cond, results.solver.message))
Beispiel #13
0
def solve_linear_GDP(linear_GDP_model, solve_data, config):
    """Solves the linear GDP model and attempts to resolve solution issues."""
    m = linear_GDP_model
    GDPopt = m.GDPopt_utils
    # Transform disjunctions
    TransformationFactory('gdp.bigm').apply_to(m)

    preprocessing_transformations = [
        # Propagate variable bounds
        'contrib.propagate_eq_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Propagate fixed variables
        'contrib.propagate_fixed_vars',
        # Remove zero terms in linear expressions
        'contrib.remove_zero_terms',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Transform bound constraints
        'contrib.constraints_to_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Remove trivial constraints
        'contrib.deactivate_trivial_constraints',
    ]
    if config.mip_presolve:
        for xfrm in preprocessing_transformations:
            TransformationFactory(xfrm).apply_to(m)

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    # Create solver, check availability
    if not SolverFactory(config.mip_solver).available():
        raise RuntimeError(
            "MIP solver %s is not available." % config.mip_solver)

    # Callback immediately before solving NLP subproblem
    config.call_before_master_solve(m, solve_data)

    # We use LoggingIntercept in order to suppress the stupid "Loading a
    # SolverResults object with a warning status" warning message.
    with SuppressInfeasibleWarning():
        results = SolverFactory(config.mip_solver).solve(
            m, **config.mip_solver_args)
    terminate_cond = results.solver.termination_condition
    if terminate_cond is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        results, terminate_cond = distinguish_mip_infeasible_or_unbounded(
            m, config)

    # Build and return results object
    mip_result = MasterProblemResult()
    mip_result.feasible = True
    mip_result.var_values = list(v.value for v in GDPopt.working_var_list)
    mip_result.pyomo_results = results
    mip_result.disjunct_values = list(
        disj.indicator_var.value for disj in GDPopt.working_disjuncts_list)

    if terminate_cond is tc.optimal or terminate_cond is tc.locallyOptimal:
        pass
    elif terminate_cond is tc.infeasible:
        config.logger.info(
            'Linear GDP is now infeasible. '
            'GDPopt has finished exploring feasible discrete configurations.')
        mip_result.feasible = False
    elif terminate_cond is tc.maxTimeLimit:
        # TODO check that status is actually ok and everything is feasible
        config.logger.info(
            'Unable to optimize linear GDP problem within time limit. '
            'Using current solver feasible solution.')
    elif (terminate_cond is tc.other and
          results.solution.status is SolutionStatus.feasible):
        # load the solution and suppress the warning message by setting
        # solver status to ok.
        config.logger.info(
            'Linear GDP solver reported feasible solution, '
            'but not guaranteed to be optimal.')
    else:
        raise ValueError(
            'GDPopt unable to handle linear GDP '
            'termination condition '
            'of %s. Solver message: %s' %
            (terminate_cond, results.solver.message))

    return mip_result
Beispiel #14
0
def _get_GDPopt_config():
    CONFIG = ConfigBlock("MindtPy")
    CONFIG.declare("bound_tolerance", ConfigValue(
        default=1E-4,
        domain=PositiveFloat,
        description="Bound tolerance",
        doc="Relative tolerance for bound feasibility checks."
    ))
    CONFIG.declare("iteration_limit", ConfigValue(
        default=50,
        domain=PositiveInt,
        description="Iteration limit",
        doc="Number of maximum iterations in the decomposition methods."
    ))
    CONFIG.declare("stalling_limit", ConfigValue(
        default=15,
        domain=PositiveInt,
        description="Stalling limit",
        doc="Stalling limit for progress in the decomposition methods."
    ))
    CONFIG.declare("time_limit", ConfigValue(
        default=600,
        domain=PositiveInt,
        description="Time limit (seconds, default=600)",
        doc="Seconds allowed until terminated. Note that the time limit can"
            "currently only be enforced between subsolver invocations. You may"
            "need to set subsolver time limits as well."
    ))
    CONFIG.declare("strategy", ConfigValue(
        default="OA",
        domain=In(["OA", "GBD", "ECP", "PSC", "GOA"]),
        description="Decomposition strategy",
        doc="MINLP Decomposition strategy to be applied to the method. "
            "Currently available Outer Approximation (OA), Extended Cutting "
            "Plane (ECP), Partial Surrogate Cuts (PSC), and Generalized "
            "Benders Decomposition (GBD)."
    ))
    CONFIG.declare("init_strategy", ConfigValue(
        default=None,
        domain=In(["rNLP", "initial_binary", "max_binary"]),
        description="Initialization strategy",
        doc="Initialization strategy used by any method. Currently the "
            "continuous relaxation of the MINLP (rNLP), solve a maximal "
            "covering problem (max_binary), and fix the initial value for "
            "the integer variables (initial_binary)."
    ))
    CONFIG.declare("max_slack", ConfigValue(
        default=1000.0,
        domain=PositiveFloat,
        description="Maximum slack variable",
        doc="Maximum slack variable value allowed for the Outer Approximation "
            "cuts."
    ))
    CONFIG.declare("OA_penalty_factor", ConfigValue(
        default=1000.0,
        domain=PositiveFloat,
        description="Outer Approximation slack penalty factor",
        doc="In the objective function of the Outer Approximation method, the "
            "slack variables corresponding to all the constraints get "
            "multiplied by this number and added to the objective."
    ))
    CONFIG.declare("ecp_tolerance", ConfigValue(
        default=None,
        domain=PositiveFloat,
        description="ECP tolerance",
        doc="Feasibility tolerance used to determine the stopping criterion in"
            "the ECP method. As long as nonlinear constraint are violated for "
            "more than this tolerance, the method will keep iterating."
    ))
    CONFIG.declare("nlp_solver", ConfigValue(
        default="ipopt",
        domain=In(["ipopt", "gams", "baron"]),
        description="NLP subsolver name",
        doc="Which NLP subsolver is going to be used for solving the nonlinear"
            "subproblems."
    ))
    CONFIG.declare("nlp_solver_args", ConfigBlock(
        implicit=True,
        description="NLP subsolver options",
        doc="Which NLP subsolver options to be passed to the solver while "
            "solving the nonlinear subproblems."
    ))
    CONFIG.declare("mip_solver", ConfigValue(
        default="glpk",
        domain=In(["gurobi", "cplex", "cbc", "glpk", "gams",
                   "gurobi_persistent", "cplex_persistent"]),
        description="MIP subsolver name",
        doc="Which MIP subsolver is going to be used for solving the mixed-"
            "integer master problems."
    ))
    CONFIG.declare("mip_solver_args", ConfigBlock(
        implicit=True,
        description="MIP subsolver options",
        doc="Which MIP subsolver options to be passed to the solver while "
            "solving the mixed-integer master problems."
    ))
    CONFIG.declare("call_after_master_solve", ConfigValue(
        default=_DoNothing(),
        domain=None,
        description="Function to be executed after every master problem",
        doc="Callback hook after a solution of the master problem."
    ))
    CONFIG.declare("call_after_subproblem_solve", ConfigValue(
        default=_DoNothing(),
        domain=None,
        description="Function to be executed after every subproblem",
        doc="Callback hook after a solution of the nonlinear subproblem."
    ))
    CONFIG.declare("call_after_subproblem_feasible", ConfigValue(
        default=_DoNothing(),
        domain=None,
        description="Function to be executed after every feasible subproblem",
        doc="Callback hook after a feasible solution"
            " of the nonlinear subproblem."
    ))
    CONFIG.declare("tee", ConfigValue(
        default=False,
        description="Stream output to terminal.",
        domain=bool
    ))
    CONFIG.declare("solver_tee", ConfigValue(
        default=False,
        description="Stream the output of mip solver and nlp solver to terminal.",
        domain=bool
    ))
    CONFIG.declare("logger", ConfigValue(
        default='pyomo.contrib.mindtpy',
        description="The logger object or name to use for reporting.",
        domain=a_logger
    ))
    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 absolute 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-7,
        description="Tolerance on variable equal to zero."
    ))
    CONFIG.declare("initial_feas", ConfigValue(
        default=True,
        description="Apply an initial feasibility step.",
        domain=bool
    ))
    CONFIG.declare("obj_bound", ConfigValue(
        default=1E15,
        domain=PositiveFloat,
        description="Bound applied to the linearization of the objective function if master MILP is unbounded."
    ))
    CONFIG.declare("integer_to_binary", ConfigValue(
        default=False,
        description="Convert integer variables to binaries (for integer cuts).",
        domain=bool
    ))
    CONFIG.declare("add_nogood_cuts", ConfigValue(
        default=False,
        description="Add integer cuts (no-good cuts) to binary variables to disallow same integer solution again."
                    "Note that 'integer_to_binary' flag needs to be used to apply it to actual integers and not just binaries.",
        domain=bool
    ))
    CONFIG.declare("single_tree", ConfigValue(
        default=False,
        description="Use single tree implementation in solving the MILP master problem.",
        domain=bool
    ))
    CONFIG.declare("solution_pool", ConfigValue(
        default=False,
        description="Use solution pool in solving the MILP master problem.",
        domain=bool
    ))
    CONFIG.declare("add_slack", ConfigValue(
        default=False,
        description="whether add slack variable here."
                    "slack variables here are used to deal with nonconvex MINLP.",
        domain=bool
    ))
    CONFIG.declare("continuous_var_bound", ConfigValue(
        default=1e10,
        description="default bound added to unbounded continuous variables in nonlinear constraint if single tree is activated.",
        domain=PositiveFloat
    ))
    CONFIG.declare("integer_var_bound", ConfigValue(
        default=1e9,
        description="default bound added to unbounded integral variables in nonlinear constraint if single tree is activated.",
        domain=PositiveFloat
    ))
    CONFIG.declare("cycling_check", ConfigValue(
        default=True,
        description="check if OA algorithm is stalled in a cycle and terminate.",
        domain=bool
    ))
    CONFIG.declare("feasibility_norm", ConfigValue(
        default="L_infinity",
        domain=In(["L1", "L2", "L_infinity"]),
        description="different forms of objective function in feasibility subproblem."
    ))
    CONFIG.declare("differentiate_mode", ConfigValue(
        default="reverse_symbolic",
        domain=In(["reverse_symbolic", "sympy"]),
        description="differentiate mode to calculate jacobian."
    ))
    CONFIG.declare("linearize_inactive", ConfigValue(
        default=False,
        description="Add OA cuts for inactive constraints.",
        domain=bool
    ))
    CONFIG.declare("use_mcpp", ConfigValue(
        default=False,
        description="use package MC++ to set a bound for variable 'objective_value', which is introduced when the original problem's objective function is nonlinear.",
        domain=bool
    ))
    CONFIG.declare("use_dual", ConfigValue(
        default=True,
        description="use dual solution from the nlp solver to add OA cuts for equality constraints.",
        domain=bool
    ))
    CONFIG.declare("use_fbbt", ConfigValue(
        default=False,
        description="use fbbt to tighten the feasible region of the problem",
        domain=bool
    ))
    CONFIG.declare("threads", ConfigValue(
        default=0,
        domain=NonNegativeInt,
        description="Threads",
        doc="Threads used by milp solver and nlp solver."
    ))
    CONFIG.declare("use_dual_bound", ConfigValue(
        default=True,
        description="add dual bound constraint to enforce the objective function should improve on the best found dual bound",
        domain=bool
    ))
    return CONFIG
Beispiel #15
0
def solve_linear_GDP(linear_GDP_model, solve_data, config):
    """Solves the linear GDP model and attempts to resolve solution issues."""
    m = linear_GDP_model
    GDPopt = m.GDPopt_utils
    # Transform disjunctions
    TransformationFactory('gdp.bigm').apply_to(m)

    preprocessing_transformations = [
        # Propagate variable bounds
        'contrib.propagate_eq_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Propagate fixed variables
        'contrib.propagate_fixed_vars',
        # Remove zero terms in linear expressions
        'contrib.remove_zero_terms',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Transform bound constraints
        'contrib.constraints_to_var_bounds',
        # Detect fixed variables
        'contrib.detect_fixed_vars',
        # Remove terms in equal to zero summations
        'contrib.propagate_zero_sum',
        # Remove trivial constraints
        'contrib.deactivate_trivial_constraints',
    ]
    if config.mip_presolve:
        for xfrm in preprocessing_transformations:
            TransformationFactory(xfrm).apply_to(m)

    # Deactivate extraneous IMPORT/EXPORT suffixes
    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    # Create solver, check availability
    if not SolverFactory(config.mip_solver).available():
        raise RuntimeError(
            "MIP solver %s is not available." % config.mip_solver)

    # Callback immediately before solving MIP master problem
    config.call_before_master_solve(m, solve_data)

    with SuppressInfeasibleWarning():
        results = SolverFactory(config.mip_solver).solve(
            m, **config.mip_solver_args)
    terminate_cond = results.solver.termination_condition
    if terminate_cond is tc.infeasibleOrUnbounded:
        # Linear solvers will sometimes tell me that it's infeasible or
        # unbounded during presolve, but fails to distinguish. We need to
        # resolve with a solver option flag on.
        results, terminate_cond = distinguish_mip_infeasible_or_unbounded(
            m, config)
    if terminate_cond is tc.unbounded:
        # Solution is unbounded. Add an arbitrary bound to the objective and resolve.
        # This occurs when the objective is nonlinear. The nonlinear objective is moved
        # to the constraints, and deactivated for the linear master problem.
        obj_bound = 1E15
        config.logger.warning(
            'Linear GDP was unbounded. '
            'Resolving with arbitrary bound values of (-{0:.10g}, {0:.10g}) on the objective. '
            'Check your initialization routine.'.format(obj_bound))
        main_objective = next(m.component_data_objects(Objective, active=True))
        GDPopt.objective_bound = Constraint(expr=(-obj_bound, main_objective.expr, obj_bound))
        with SuppressInfeasibleWarning():
            results = SolverFactory(config.mip_solver).solve(
                m, **config.mip_solver_args)
        terminate_cond = results.solver.termination_condition

    # Build and return results object
    mip_result = MasterProblemResult()
    mip_result.feasible = True
    mip_result.var_values = list(v.value for v in GDPopt.variable_list)
    mip_result.pyomo_results = results
    mip_result.disjunct_values = list(
        disj.indicator_var.value for disj in GDPopt.disjunct_list)

    if terminate_cond is tc.optimal or terminate_cond is tc.locallyOptimal:
        pass
    elif terminate_cond is tc.infeasible:
        config.logger.info(
            'Linear GDP is now infeasible. '
            'GDPopt has finished exploring feasible discrete configurations.')
        mip_result.feasible = False
    elif terminate_cond is tc.maxTimeLimit:
        # TODO check that status is actually ok and everything is feasible
        config.logger.info(
            'Unable to optimize linear GDP problem within time limit. '
            'Using current solver feasible solution.')
    elif (terminate_cond is tc.other and
          results.solution.status is SolutionStatus.feasible):
        # load the solution and suppress the warning message by setting
        # solver status to ok.
        config.logger.info(
            'Linear GDP solver reported feasible solution, '
            'but not guaranteed to be optimal.')
    else:
        raise ValueError(
            'GDPopt unable to handle linear GDP '
            'termination condition '
            'of %s. Solver message: %s' %
            (terminate_cond, results.solver.message))

    return mip_result
Beispiel #16
0
def _get_MindtPy_config():
    """Set up the configurations for MindtPy.

    Returns
    -------
    CONFIG : ConfigBlock
        The specific configurations for MindtPy
    """
    CONFIG = ConfigBlock('MindtPy')

    CONFIG.declare(
        'iteration_limit',
        ConfigValue(
            default=50,
            domain=NonNegativeInt,
            description='Iteration limit',
            doc='Number of maximum iterations in the decomposition methods.'))
    CONFIG.declare(
        'stalling_limit',
        ConfigValue(
            default=15,
            domain=PositiveInt,
            description='Stalling limit',
            doc=
            'Stalling limit for primal bound progress in the decomposition methods.'
        ))
    CONFIG.declare(
        'time_limit',
        ConfigValue(
            default=600,
            domain=PositiveInt,
            description='Time limit (seconds, default=600)',
            doc='Seconds allowed until terminated. Note that the time limit can'
            'currently only be enforced between subsolver invocations. You may'
            'need to set subsolver time limits as well.'))
    CONFIG.declare(
        'strategy',
        ConfigValue(
            default='OA',
            domain=In(['OA', 'ECP', 'GOA', 'FP']),
            description='Decomposition strategy',
            doc='MINLP Decomposition strategy to be applied to the method. '
            'Currently available Outer Approximation (OA), Extended Cutting '
            'Plane (ECP), Global Outer Approximation (GOA) and Feasibility Pump (FP).'
        ))
    CONFIG.declare(
        'add_regularization',
        ConfigValue(
            default=None,
            domain=In([
                'level_L1', 'level_L2', 'level_L_infinity', 'grad_lag',
                'hess_lag', 'hess_only_lag', 'sqp_lag'
            ]),
            description='add regularization',
            doc=
            'Solving a regularization problem before solve the fixed subproblem'
            'the objective function of the regularization problem.'))
    CONFIG.declare(
        'init_strategy',
        ConfigValue(
            default=None,
            domain=In(['rNLP', 'initial_binary', 'max_binary', 'FP']),
            description='Initialization strategy',
            doc='Initialization strategy used by any method. Currently the '
            'continuous relaxation of the MINLP (rNLP), solve a maximal '
            'covering problem (max_binary), and fix the initial value for '
            'the integer variables (initial_binary).'))
    CONFIG.declare(
        'max_slack',
        ConfigValue(
            default=1000.0,
            domain=PositiveFloat,
            description='Maximum slack variable',
            doc=
            'Maximum slack variable value allowed for the Outer Approximation '
            'cuts.'))
    CONFIG.declare(
        'OA_penalty_factor',
        ConfigValue(
            default=1000.0,
            domain=PositiveFloat,
            description='Outer Approximation slack penalty factor',
            doc=
            'In the objective function of the Outer Approximation method, the '
            'slack variables corresponding to all the constraints get '
            'multiplied by this number and added to the objective.'))
    CONFIG.declare(
        'call_after_main_solve',
        ConfigValue(
            default=_DoNothing(),
            domain=None,
            description='Function to be executed after every main problem',
            doc='Callback hook after a solution of the main problem.'))
    CONFIG.declare(
        'call_after_subproblem_solve',
        ConfigValue(
            default=_DoNothing(),
            domain=None,
            description='Function to be executed after every subproblem',
            doc='Callback hook after a solution of the nonlinear subproblem.'))
    CONFIG.declare(
        'call_after_subproblem_feasible',
        ConfigValue(default=_DoNothing(),
                    domain=None,
                    description=
                    'Function to be executed after every feasible subproblem',
                    doc='Callback hook after a feasible solution'
                    ' of the nonlinear subproblem.'))
    CONFIG.declare(
        'tee',
        ConfigValue(default=False,
                    description='Stream output to terminal.',
                    domain=bool))
    CONFIG.declare(
        'logger',
        ConfigValue(
            default='pyomo.contrib.mindtpy',
            description='The logger object or name to use for reporting.',
            domain=a_logger))
    CONFIG.declare(
        'logging_level',
        ConfigValue(
            default=logging.INFO,
            domain=NonNegativeInt,
            description='The logging level for MindtPy.'
            'CRITICAL = 50, ERROR = 40, WARNING = 30, INFO = 20, DEBUG = 10, NOTSET = 0',
        ))
    CONFIG.declare(
        'integer_to_binary',
        ConfigValue(
            default=False,
            description=
            'Convert integer variables to binaries (for no-good cuts).',
            domain=bool))
    CONFIG.declare(
        'add_no_good_cuts',
        ConfigValue(
            default=False,
            description=
            'Add no-good cuts (no-good cuts) to binary variables to disallow same integer solution again.'
            'Note that integer_to_binary flag needs to be used to apply it to actual integers and not just binaries.',
            domain=bool))
    CONFIG.declare(
        'use_tabu_list',
        ConfigValue(
            default=False,
            description=
            'Use tabu list and incumbent callback to disallow same integer solution again.',
            domain=bool))
    CONFIG.declare(
        'add_affine_cuts',
        ConfigValue(default=False,
                    description='Add affine cuts drive from MC++.',
                    domain=bool))
    CONFIG.declare(
        'single_tree',
        ConfigValue(
            default=False,
            description=
            'Use single tree implementation in solving the MIP main problem.',
            domain=bool))
    CONFIG.declare(
        'solution_pool',
        ConfigValue(
            default=False,
            description='Use solution pool in solving the MIP main problem.',
            domain=bool))
    CONFIG.declare(
        'num_solution_iteration',
        ConfigValue(
            default=5,
            description=
            'The number of MIP solutions (from the solution pool) used to generate the fixed NLP subproblem in each iteration.',
            domain=PositiveInt))
    CONFIG.declare(
        'add_slack',
        ConfigValue(
            default=False,
            description='Whether add slack variable here.'
            'slack variables here are used to deal with nonconvex MINLP.',
            domain=bool))
    CONFIG.declare(
        'cycling_check',
        ConfigValue(
            default=True,
            description=
            'Check if OA algorithm is stalled in a cycle and terminate.',
            domain=bool))
    CONFIG.declare(
        'feasibility_norm',
        ConfigValue(
            default='L_infinity',
            domain=In(['L1', 'L2', 'L_infinity']),
            description=
            'Different forms of objective function in feasibility subproblem.')
    )
    CONFIG.declare(
        'differentiate_mode',
        ConfigValue(default='reverse_symbolic',
                    domain=In(['reverse_symbolic', 'sympy']),
                    description='Differentiate mode to calculate jacobian.'))
    CONFIG.declare(
        'linearize_inactive',
        ConfigValue(default=False,
                    description='Add OA cuts for inactive constraints.',
                    domain=bool))
    CONFIG.declare(
        'use_mcpp',
        ConfigValue(
            default=False,
            description=
            "Use package MC++ to set a bound for variable 'objective_value', which is introduced when the original problem's objective function is nonlinear.",
            domain=bool))
    CONFIG.declare(
        'equality_relaxation',
        ConfigValue(
            default=False,
            description=
            'Use dual solution from the NLP solver to add OA cuts for equality constraints.',
            domain=bool))
    CONFIG.declare(
        'calculate_dual',
        ConfigValue(default=False,
                    description='Calculate duals of the NLP subproblem.',
                    domain=bool))
    CONFIG.declare(
        'use_fbbt',
        ConfigValue(default=False,
                    description=
                    'Use fbbt to tighten the feasible region of the problem.',
                    domain=bool))
    CONFIG.declare(
        'use_dual_bound',
        ConfigValue(
            default=True,
            description=
            'Add dual bound constraint to enforce the objective satisfies best-found dual bound.',
            domain=bool))
    CONFIG.declare(
        'heuristic_nonconvex',
        ConfigValue(
            default=False,
            description=
            'Use dual solution from the NLP solver and slack variables to add OA cuts for equality constraints (Equality relaxation)'
            'and minimize the sum of the slack variables (Augmented Penalty).',
            domain=bool))
    CONFIG.declare(
        'partition_obj_nonlinear_terms',
        ConfigValue(
            default=True,
            description=
            'Partition objective with the sum of nonlinear terms using epigraph reformulation.',
            domain=bool))

    _add_subsolver_configs(CONFIG)
    _add_tolerance_configs(CONFIG)
    _add_fp_configs(CONFIG)
    _add_bound_configs(CONFIG)
    _add_loa_configs(CONFIG)
    return CONFIG
Beispiel #17
0
def init_max_binaries(solve_data, config):
    """
    Modifies model by maximizing the number of activated binary variables

    Note - The user would usually want to call solve_NLP_subproblem after an
    invocation of this function.

    Parameters
    ----------
    solve_data: MindtPy Data Container
        data container that holds solve-instance data
    config: ConfigBlock
        contains the specific configurations for the algorithm
    """
    m = solve_data.working_model.clone()
    if config.use_dual:
        m.dual.deactivate()
    MindtPy = m.MindtPy_utils
    solve_data.mip_subiter += 1
    config.logger.info(
        "MILP %s: maximize value of binaries" %
        (solve_data.mip_iter))
    for c in MindtPy.constraint_list:
        if c.body.polynomial_degree() not in (1, 0):
            c.deactivate()
    objective = next(m.component_data_objects(Objective, active=True))
    objective.deactivate()
    binary_vars = (
        v for v in m.component_data_objects(ctype=Var)
        if v.is_binary() and not v.fixed)
    MindtPy.MindtPy_max_binary_obj = Objective(
        expr=sum(v for v in binary_vars), sense=maximize)

    getattr(m, 'ipopt_zL_out', _DoNothing()).deactivate()
    getattr(m, 'ipopt_zU_out', _DoNothing()).deactivate()

    opt = SolverFactory(config.mip_solver)
    if isinstance(opt, PersistentSolver):
        opt.set_instance(m)
    mip_args = dict(config.mip_solver_args)
    elapsed = get_main_elapsed_time(solve_data.timing)
    remaining = int(max(config.time_limit - elapsed, 1))
    if config.mip_solver == 'gams':
        mip_args['add_options'] = mip_args.get('add_options', [])
        mip_args['add_options'].append('option optcr=0.001;')
    results = opt.solve(m, tee=config.solver_tee, **mip_args)

    solve_terminate_cond = results.solver.termination_condition
    if solve_terminate_cond is tc.optimal:
        copy_var_list_values(
            MindtPy.variable_list,
            solve_data.working_model.MindtPy_utils.variable_list,
            config)

        pass  # good
    elif solve_terminate_cond is tc.infeasible:
        raise ValueError(
            'MILP master problem is infeasible. '
            'Problem may have no more feasible '
            'binary configurations.')
    elif solve_terminate_cond is tc.maxTimeLimit:
        config.logger.info(
            'NLP subproblem failed to converge within time limit.')
    elif solve_terminate_cond is tc.maxIterations:
        config.logger.info(
            'NLP subproblem failed to converge within iteration limit.')
    else:
        raise ValueError(
            'MindtPy unable to handle MILP master termination condition '
            'of %s. Solver message: %s' %
            (solve_terminate_cond, results.solver.message))