예제 #1
0
    def _write_reference_scenario_lp(self, ph):

        # Make sure the pyomo plugins are loaded
        import pyomo.environ
        lp_file_writer = pyomo.repn.plugins.cpxlp.ProblemWriter_cpxlp()

        # Write the LP file
        print(("Writing LP file to %s" % (self._lpfilename,)))
        scenariotree_vars = \
            self._reference_scenario_instance.\
                _ScenarioTreeSymbolMap.bySymbol
        rootnode_vars = \
            ph._scenario_tree.findRootNode()._standard_variable_ids
        firststage_ids = \
            [id(vardata) for scenariotree_id, vardata \
             in iteritems(scenariotree_vars) \
             if scenariotree_id in rootnode_vars]
        capabilities = lambda x: True
        text_labeler = TextLabeler()
        labeler = lambda x: text_labeler(x) + \
                  (self._firststage_var_postfix \
                   if (id(x) in firststage_ids) else \
                   "")
        output_filename, symbol_map = \
            lp_file_writer(self._reference_scenario_instance,
                           self._lpfilename,
                           capabilities,
                           {'labeler' : labeler})
        assert output_filename == self._lpfilename
        print("\nModel written\n")

        # Return the max symbol length (for siphelp)
        max_name_len = max(len(symbol) for symbol in symbol_map.bySymbol)
        return max_name_len, symbol_map
예제 #2
0
    def _set_instance(self, model, kwds={}):
        if not isinstance(model, (Model, IBlockStorage, Block, _BlockData)):
            msg = "The problem instance supplied to the {0} plugin " \
                  "'_presolve' method must be a Model or a Block".format(type(self))
            raise ValueError(msg)
        self._pyomo_model = model
        self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels', self._symbolic_solver_labels)
        self._skip_trivial_constraints = kwds.pop('skip_trivial_constraints', self._skip_trivial_constraints)
        self._output_fixed_variable_bounds = kwds.pop('output_fixed_variable_bounds',
                                                      self._output_fixed_variable_bounds)
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = dict()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = dict()
        self._vars_referenced_by_con = ComponentMap()
        self._vars_referenced_by_obj = ComponentSet()
        self._referenced_variables = ComponentMap()
        self._objective_label = None
        self._objective = None

        self._symbol_map = SymbolMap()

        if self._symbolic_solver_labels:
            self._labeler = TextLabeler()
        else:
            self._labeler = NumericLabeler('x')
예제 #3
0
    def set_instance(self, model):
        saved_config = self.config
        saved_update_config = self.update_config
        self.__init__()
        self.config = saved_config
        self.update_config = saved_update_config
        self._model = model

        if self.config.symbolic_solver_labels:
            self._var_labeler = TextLabeler()
            self._con_labeler = TextLabeler()
            self._param_labeler = TextLabeler()
        else:
            self._var_labeler = NumericLabeler('x')
            self._con_labeler = NumericLabeler('c')
            self._param_labeler = NumericLabeler('p')

        self._writer = cmodel.NLWriter()

        self.add_block(model)
        if self._objective is None:
            self.set_objective(None)
예제 #4
0
    def set_instance(self,
                     model,
                     symbolic_solver_labels: Optional[bool] = None):
        saved_config = self.config
        saved_update_config = self.update_config

        self.__init__()
        self.config = saved_config
        self.update_config = saved_update_config
        self._expr_types = cmodel.PyomoExprTypes()

        if symbolic_solver_labels is not None:
            self._symbolic_solver_labels = symbolic_solver_labels
        if self._symbolic_solver_labels:
            self._var_labeler = TextLabeler()
            self._con_labeler = TextLabeler()
            self._param_labeler = TextLabeler()
            self._obj_labeler = TextLabeler()

        self._model = model
        self._cmodel = cmodel.FBBTModel()
        self.add_block(model)
        if self._objective is None:
            self.set_objective(None)
예제 #5
0
파일: ddsip.py 프로젝트: CanLi1/pyomo-1
def _convert_external_setup_without_cleanup(worker, scenario, output_directory,
                                            firststage_var_suffix,
                                            enforce_derived_nonanticipativity,
                                            io_options):
    import pyomo.environ
    assert os.path.exists(output_directory)

    io_options = dict(io_options)
    scenario_tree = worker.scenario_tree
    reference_model = scenario._instance
    rootnode = scenario_tree.findRootNode()
    firststage = scenario_tree.stages[0]
    secondstage = scenario_tree.stages[1]
    constraint_name_buffer = {}
    objective_name_buffer = {}
    variable_name_buffer = {}

    all_constraints = list(con
                           for con in reference_model.component_data_objects(
                               Constraint, active=True, descend_into=True))

    #
    # Check for model annotations
    #
    stochastic_rhs = locate_annotations(reference_model,
                                        StochasticConstraintBoundsAnnotation,
                                        max_allowed=1)
    if len(stochastic_rhs) == 0:
        stochastic_rhs = None
        stochastic_rhs_entries = {}
        empty_rhs_annotation = False
    else:
        assert len(stochastic_rhs) == 1
        stochastic_rhs = stochastic_rhs[0][1]
        if stochastic_rhs.has_declarations:
            empty_rhs_annotation = False
            stochastic_rhs_entries = stochastic_rhs.expand_entries()
            stochastic_rhs_entries.sort(
                key=lambda x: x[0].getname(True, constraint_name_buffer))
            if len(stochastic_rhs_entries) == 0:
                raise RuntimeError(
                    "The %s annotation was declared "
                    "with external entries but no active Constraint "
                    "objects were recovered from those entries." %
                    (StochasticConstraintBoundsAnnotation.__name__))
        else:
            empty_rhs_annotation = True
            stochastic_rhs_entries = tuple(
                (con, stochastic_rhs.default) for con in all_constraints)

    stochastic_matrix = locate_annotations(reference_model,
                                           StochasticConstraintBodyAnnotation,
                                           max_allowed=1)
    if len(stochastic_matrix) == 0:
        stochastic_matrix = None
        stochastic_matrix_entries = {}
        empty_matrix_annotation = False
    else:
        assert len(stochastic_matrix) == 1
        stochastic_matrix = stochastic_matrix[0][1]
        if stochastic_matrix.has_declarations:
            empty_matrix_annotation = False
            stochastic_matrix_entries = stochastic_matrix.expand_entries()
            stochastic_matrix_entries.sort(
                key=lambda x: x[0].getname(True, constraint_name_buffer))
            if len(stochastic_matrix_entries) == 0:
                raise RuntimeError(
                    "The %s annotation was declared "
                    "with external entries but no active Constraint "
                    "objects were recovered from those entries." %
                    (StochasticConstraintBoundsAnnotation.__name__))
        else:
            empty_matrix_annotation = True
            stochastic_matrix_entries = tuple(
                (con, stochastic_matrix.default) for con in all_constraints)

    stochastic_constraint_ids = set()
    stochastic_constraint_ids.update(
        id(con) for con, _ in stochastic_rhs_entries)
    stochastic_constraint_ids.update(
        id(con) for con, _ in stochastic_matrix_entries)

    stochastic_objective = locate_annotations(reference_model,
                                              StochasticObjectiveAnnotation,
                                              max_allowed=1)
    if len(stochastic_objective) == 0:
        stochastic_objective = None
    else:
        assert len(stochastic_objective) == 1
        stochastic_objective = stochastic_objective[0][1]

    stochastic_varbounds = locate_annotations(
        reference_model, StochasticVariableBoundsAnnotation)
    if len(stochastic_varbounds) > 0:
        raise ValueError(
            "The DDSIP writer does not currently support "
            "stochastic variable bounds. Invalid annotation type: %s" %
            (StochasticVariableBoundsAnnotation.__name__))

    if (stochastic_rhs is None) and \
       (stochastic_matrix is None) and \
       (stochastic_objective is None):
        raise RuntimeError("No stochastic annotations found. DDSIP "
                           "conversion requires at least one of the following "
                           "annotation types:\n - %s\n - %s\n - %s" %
                           (StochasticConstraintBoundsAnnotation.__name__,
                            StochasticConstraintBodyAnnotation.__name__,
                            StochasticObjectiveAnnotation.__name__))

    assert not hasattr(reference_model, "_repn")
    repn_cache = build_repns(reference_model)
    assert hasattr(reference_model, "_repn")
    assert not reference_model._gen_obj_repn
    assert not reference_model._gen_con_repn
    # compute values
    for block_repns in repn_cache.values():
        for repn in block_repns.values():
            repn.constant = value(repn.constant)
            repn.linear_coefs = [value(c) for c in repn.linear_coefs]
            repn.quadratic_coefs = [value(c) for c in repn.quadratic_coefs]

    #
    # Write the LP file once to obtain the symbol map
    #
    output_filename = os.path.join(output_directory,
                                   scenario.name + ".lp.setup")
    with WriterFactory("lp") as writer:
        assert 'column_order' not in io_options
        assert 'row_order' not in io_options
        output_fname, symbol_map = writer(reference_model, output_filename,
                                          lambda x: True, io_options)
        assert output_fname == output_filename
    _safe_remove_file(output_filename)

    StageToVariableMap = map_variable_stages(
        scenario,
        scenario_tree,
        symbol_map,
        enforce_derived_nonanticipativity=enforce_derived_nonanticipativity)
    firststage_variable_ids = \
        set(id(var) for symbol, var, scenario_tree_id
            in StageToVariableMap[firststage.name])
    secondstage_variable_ids = \
        set(id(var) for symbol, var, scenario_tree_id
            in StageToVariableMap[secondstage.name])

    StageToConstraintMap = \
        map_constraint_stages(
            scenario,
            scenario_tree,
            symbol_map,
            stochastic_constraint_ids,
            firststage_variable_ids,
            secondstage_variable_ids)
    secondstage_constraint_ids = \
        set(id(con) for symbols, con
            in StageToConstraintMap[secondstage.name])

    assert len(scenario_tree.stages) == 2
    firststage = scenario_tree.stages[0]
    secondstage = scenario_tree.stages[1]

    #
    # Make sure the objective references all first stage variables.
    # We do this by directly modifying the _repn of the
    # objective which the LP/MPS writer will reference next time we call
    # it. In addition, make sure that the first second-stage variable
    # in our column ordering also appears in the objective so that
    # ONE_VAR_CONSTANT does not get identified as the first
    # second-stage variable.
    # ** Just do NOT preprocess again until we call the writer **
    #
    objective_object = scenario._instance_objective
    assert objective_object is not None
    objective_block = objective_object.parent_block()
    objective_repn = repn_cache[id(objective_block)][objective_object]

    #
    # Create column (variable) ordering maps for LP/MPS files
    #
    column_order = ComponentMap()
    firststage_variable_count = 0
    secondstage_variable_count = 0
    # first-stage variables
    for column_index, (symbol, var, scenario_tree_id) \
        in enumerate(StageToVariableMap[firststage.name]):
        column_order[var] = column_index
        firststage_variable_count += 1
    # second-stage variables
    for column_index, (symbol, var, scenario_tree_id) \
        in enumerate(StageToVariableMap[secondstage.name],
                     len(column_order)):
        column_order[var] = column_index
        secondstage_variable_count += 1
    # account for the ONE_VAR_CONSTANT second-stage variable
    # added by the LP writer
    secondstage_variable_count += 1

    #
    # Create row (constraint) ordering maps for LP/MPS files
    #
    firststage_constraint_count = 0
    secondstage_constraint_count = 0
    row_order = ComponentMap()
    # first-stage constraints
    for row_index, (symbols, con) \
        in enumerate(StageToConstraintMap[firststage.name]):
        row_order[con] = row_index
        firststage_constraint_count += len(symbols)
    # second-stage constraints
    for row_index, (symbols, con) \
        in enumerate(StageToConstraintMap[secondstage.name],
                     len(row_order)):
        row_order[con] = row_index
        secondstage_constraint_count += len(symbols)
    # account for the ONE_VAR_CONSTANT = 1 second-stage constraint
    # added by the LP writer
    secondstage_constraint_count += 1

    #
    # Create a custom labeler that allows DDSIP to identify
    # first-stage variables
    #
    if io_options.pop('symbolic_solver_labels', False):
        _labeler = TextLabeler()
    else:
        _labeler = NumericLabeler('x')
    labeler = lambda x: _labeler(x) + \
              (""
               if ((not isinstance(x, _VarData)) or \
                   (id(x) not in firststage_variable_ids)) else \
               firststage_var_suffix)

    #
    # Write the ordered LP/MPS file
    #
    output_filename = os.path.join(output_directory, scenario.name + ".lp")
    symbols_filename = os.path.join(output_directory,
                                    scenario.name + ".lp.symbols")
    with WriterFactory("lp") as writer:
        assert 'column_order' not in io_options
        assert 'row_order' not in io_options
        assert 'labeler' not in io_options
        assert 'force_objective_constant' not in io_options
        io_options['column_order'] = column_order
        io_options['row_order'] = row_order
        io_options['force_objective_constant'] = True
        io_options['labeler'] = labeler
        output_fname, symbol_map = writer(reference_model, output_filename,
                                          lambda x: True, io_options)
        assert output_fname == output_filename
        # write the lp file symbol paired with the scenario
        # tree id for each variable in the root node
        with open(symbols_filename, "w") as f:
            st_symbol_map = reference_model._ScenarioTreeSymbolMap
            lines = []
            for id_ in sorted(rootnode._variable_ids):
                var = st_symbol_map.bySymbol[id_]
                if not var.is_expression_type():
                    lp_label = symbol_map.byObject[id(var)]
                    lines.append("%s %s\n" % (lp_label, id_))
            f.writelines(lines)

    # re-generate these maps as the LP/MPS symbol map
    # is likely different
    StageToVariableMap = map_variable_stages(
        scenario,
        scenario_tree,
        symbol_map,
        enforce_derived_nonanticipativity=enforce_derived_nonanticipativity)

    StageToConstraintMap = map_constraint_stages(scenario, scenario_tree,
                                                 symbol_map,
                                                 stochastic_constraint_ids,
                                                 firststage_variable_ids,
                                                 secondstage_variable_ids)

    # generate a few data structures that are used
    # when writing the .sc files
    constraint_symbols = ComponentMap(
        (con, symbols) for stage_name in StageToConstraintMap
        for symbols, con in StageToConstraintMap[stage_name])

    #
    # Write the body of the .sc files
    #
    modified_constraint_lb = ComponentMap()
    modified_constraint_ub = ComponentMap()

    #
    # Stochastic RHS
    #
    # **NOTE: In the code that follows we assume the LP
    #         writer always moves constraint body
    #         constants to the rhs and that the lower part
    #         of any range constraints are written before
    #         the upper part.
    #
    stochastic_rhs_count = 0
    with open(os.path.join(output_directory, scenario.name + ".rhs.sc.struct"),
              'w') as f_rhs_struct:
        with open(os.path.join(output_directory, scenario.name + ".rhs.sc"),
                  'w') as f_rhs:
            scenario_probability = scenario.probability
            rhs_struct_template = " %s\n"
            rhs_template = "  %.17g\n"
            f_rhs.write("scen\n%.17g\n" %
                        (_no_negative_zero(scenario_probability)))
            if stochastic_rhs is not None:
                for con, include_bound in stochastic_rhs_entries:
                    assert isinstance(con, _ConstraintData)
                    if not empty_rhs_annotation:
                        # verify that this constraint was
                        # flagged by PySP or the user as second-stage
                        if id(con) not in secondstage_constraint_ids:
                            raise RuntimeError(
                                "The constraint %s has been declared "
                                "in the %s annotation but it was not identified as "
                                "a second-stage constraint. To correct this issue, "
                                "remove the constraint from this annotation." %
                                (con.name,
                                 StochasticConstraintBoundsAnnotation.__name__)
                            )

                    constraint_repn = \
                        repn_cache[id(con.parent_block())][con]
                    if not constraint_repn.is_linear():
                        raise RuntimeError(
                            "Only linear constraints are "
                            "accepted for conversion to DDSIP format. "
                            "Constraint %s is not linear." % (con.name))

                    body_constant = constraint_repn.constant
                    # We are going to rewrite the core problem file
                    # with all stochastic values set to zero. This will
                    # allow an easy test for missing user annotations.
                    constraint_repn.constant = 0
                    if body_constant is None:
                        body_constant = 0.0
                    symbols = constraint_symbols[con]
                    assert len(symbols) > 0
                    for con_label in symbols:
                        if con_label.startswith('c_e_') or \
                           con_label.startswith('c_l_'):
                            assert (include_bound is True) or \
                                   (include_bound[0] is True)
                            stochastic_rhs_count += 1
                            f_rhs_struct.write(rhs_struct_template %
                                               (con_label))
                            f_rhs.write(rhs_template %
                                        (_no_negative_zero(
                                            value(con.lower) - \
                                            value(body_constant))))
                            # We are going to rewrite the core problem file
                            # with all stochastic values set to zero. This will
                            # allow an easy test for missing user annotations.
                            modified_constraint_lb[con] = con.lower
                            con._lower = _deterministic_check_constant
                            if con_label.startswith('c_e_'):
                                modified_constraint_ub[con] = con.upper
                                con._upper = _deterministic_check_constant
                        elif con_label.startswith('r_l_'):
                            if (include_bound is True) or \
                               (include_bound[0] is True):
                                stochastic_rhs_count += 1
                                f_rhs_struct.write(rhs_struct_template %
                                                   (con_label))
                                f_rhs.write(rhs_template %
                                             (_no_negative_zero(
                                                 value(con.lower) - \
                                                 value(body_constant))))
                                # We are going to rewrite the core problem file
                                # with all stochastic values set to zero. This will
                                # allow an easy test for missing user annotations.
                                modified_constraint_lb[con] = con.lower
                                con._lower = _deterministic_check_constant
                        elif con_label.startswith('c_u_'):
                            assert (include_bound is True) or \
                                   (include_bound[1] is True)
                            stochastic_rhs_count += 1
                            f_rhs_struct.write(rhs_struct_template %
                                               (con_label))
                            f_rhs.write(rhs_template %
                                        (_no_negative_zero(
                                            value(con.upper) - \
                                            value(body_constant))))
                            # We are going to rewrite the core problem file
                            # with all stochastic values set to zero. This will
                            # allow an easy test for missing user annotations.
                            modified_constraint_ub[con] = con.upper
                            con._upper = _deterministic_check_constant
                        elif con_label.startswith('r_u_'):
                            if (include_bound is True) or \
                               (include_bound[1] is True):
                                stochastic_rhs_count += 1
                                f_rhs_struct.write(rhs_struct_template %
                                                   (con_label))
                                f_rhs.write(rhs_template %
                                            (_no_negative_zero(
                                                value(con.upper) - \
                                                value(body_constant))))
                                # We are going to rewrite the core problem file
                                # with all stochastic values set to zero. This will
                                # allow an easy test for missing user annotations.
                                modified_constraint_ub[con] = con.upper
                                con._upper = _deterministic_check_constant
                        else:
                            assert False

    #
    # Stochastic Matrix
    #
    stochastic_matrix_count = 0
    with open(
            os.path.join(output_directory,
                         scenario.name + ".matrix.sc.struct"),
            'w') as f_mat_struct:
        with open(os.path.join(output_directory, scenario.name + ".matrix.sc"),
                  'w') as f_mat:
            scenario_probability = scenario.probability
            matrix_struct_template = " %s %s\n"
            matrix_template = "  %.17g\n"
            f_mat.write("scen\n")
            if stochastic_matrix is not None:
                for con, var_list in stochastic_matrix_entries:
                    assert isinstance(con, _ConstraintData)
                    if not empty_matrix_annotation:
                        # verify that this constraint was
                        # flagged by PySP or the user as second-stage
                        if id(con) not in secondstage_constraint_ids:
                            raise RuntimeError(
                                "The constraint %s has been declared "
                                "in the %s annotation but it was not identified as "
                                "a second-stage constraint. To correct this issue, "
                                "remove the constraint from this annotation." %
                                (con.name,
                                 StochasticConstraintBodyAnnotation.__name__))
                    constraint_repn = \
                        repn_cache[id(con.parent_block())][con]
                    if not constraint_repn.is_linear():
                        raise RuntimeError(
                            "Only linear constraints are "
                            "accepted for conversion to DDSIP format. "
                            "Constraint %s is not linear." % (con.name))
                    assert len(constraint_repn.linear_vars) > 0
                    if var_list is None:
                        var_list = constraint_repn.linear_vars
                    assert len(var_list) > 0
                    symbols = constraint_symbols[con]
                    # sort the variable list by the column ordering
                    # so that we have deterministic output
                    var_list = list(var_list)
                    var_list.sort(key=lambda _v: column_order[_v])
                    new_coefs = list(constraint_repn.linear_coefs)
                    for var in var_list:
                        assert isinstance(var, _VarData)
                        assert not var.fixed
                        var_coef = None
                        for i, (_var, coef) in enumerate(
                                zip(constraint_repn.linear_vars,
                                    constraint_repn.linear_coefs)):
                            if _var is var:
                                var_coef = coef
                                # We are going to rewrite with core problem file
                                # with all stochastic values set to zero. This will
                                # allow an easy test for missing user annotations.
                                new_coefs[i] = _deterministic_check_value
                                break
                        if var_coef is None:
                            raise RuntimeError(
                                "The coefficient for variable %s has "
                                "been marked as stochastic in constraint %s using "
                                "the %s annotation, but the variable does not appear"
                                " in the canonical constraint expression." %
                                (var.name, con.name,
                                 StochasticConstraintBodyAnnotation.__name__))
                        var_label = symbol_map.byObject[id(var)]
                        for con_label in symbols:
                            stochastic_matrix_count += 1
                            f_mat_struct.write(matrix_struct_template %
                                               (con_label, var_label))
                            f_mat.write(matrix_template %
                                        (_no_negative_zero(value(var_coef))))

                    constraint_repn.linear_coefs = tuple(new_coefs)

    #
    # Stochastic Objective
    #
    stochastic_cost_count = 0
    with open(
            os.path.join(output_directory, scenario.name + ".cost.sc.struct"),
            'w') as f_obj_struct:
        with open(os.path.join(output_directory, scenario.name + ".cost.sc"),
                  'w') as f_obj:
            obj_struct_template = " %s\n"
            obj_template = "  %.17g\n"
            f_obj.write("scen\n")
            if stochastic_objective is not None:
                if stochastic_objective.has_declarations:
                    sorted_values = stochastic_objective.expand_entries()
                    assert len(sorted_values) <= 1
                    if len(sorted_values) == 0:
                        raise RuntimeError(
                            "The %s annotation was declared "
                            "with external entries but no active Objective "
                            "objects were recovered from those entries." %
                            (StochasticObjectiveAnnotation.__name__))
                    obj, (objective_variables, include_constant) = \
                        sorted_values[0]
                    assert obj is objective_object
                else:
                    objective_variables, include_constant = \
                        stochastic_objective.default

                if not objective_repn.is_linear():
                    raise RuntimeError(
                        "Only linear stochastic objectives are "
                        "accepted for conversion to DDSIP format. "
                        "Objective %s is not linear." %
                        (objective_object.name))
                if objective_variables is None:
                    objective_variables = objective_repn.linear_vars
                stochastic_objective_label = symbol_map.byObject[id(
                    objective_object)]
                # sort the variable list by the column ordering
                # so that we have deterministic output
                objective_variables = list(objective_variables)
                objective_variables.sort(key=lambda _v: column_order[_v])
                assert (len(objective_variables) > 0) or include_constant
                new_coefs = list(objective_repn.linear_coefs)
                for var in objective_variables:
                    assert isinstance(var, _VarData)
                    var_coef = None
                    for i, (_var, coef) in enumerate(
                            zip(objective_repn.linear_vars,
                                objective_repn.linear_coefs)):
                        if _var is var:
                            var_coef = coef
                            # We are going to rewrite the core problem file
                            # with all stochastic values set to zero. This will
                            # allow an easy test for missing user annotations.
                            new_coefs[i] = _deterministic_check_value
                            break
                    if var_coef is None:
                        raise RuntimeError(
                            "The coefficient for variable %s has "
                            "been marked as stochastic in objective %s using "
                            "the %s annotation, but the variable does not appear"
                            " in the canonical objective expression." %
                            (var.name, objective_object.name,
                             StochasticObjectiveAnnotation.__name__))
                    var_label = symbol_map.byObject[id(var)]
                    stochastic_cost_count += 1
                    f_obj_struct.write(obj_struct_template % (var_label))
                    f_obj.write(obj_template %
                                (_no_negative_zero(value(var_coef))))

                objective_repn.linear_coefs = tuple(new_coefs)
                if include_constant:
                    obj_constant = objective_repn.constant
                    # We are going to rewrite the core problem file
                    # with all stochastic values set to zero. This will
                    # allow an easy test for missing user annotations.
                    objective_repn.constant = _deterministic_check_value
                    if obj_constant is None:
                        obj_constant = 0.0
                    stochastic_cost_count += 1
                    f_obj_struct.write(obj_struct_template %
                                       ("ONE_VAR_CONSTANT"))
                    f_obj.write(obj_template %
                                (_no_negative_zero(obj_constant)))

    #
    # Write the deterministic part of the LP/MPS-file to its own
    # file for debugging purposes
    #
    reference_model_name = reference_model.name
    reference_model._name = "ZeroStochasticData"
    det_output_filename = os.path.join(output_directory,
                                       scenario.name + ".lp.det")
    with WriterFactory("lp") as writer:
        output_fname, symbol_map = writer(reference_model, det_output_filename,
                                          lambda x: True, io_options)
        assert output_fname == det_output_filename
    reference_model._name = reference_model_name

    # reset bounds on any constraints that were modified
    for con, lower in iteritems(modified_constraint_lb):
        con._lower = as_numeric(lower)
    for con, upper in iteritems(modified_constraint_ub):
        con._upper = as_numeric(upper)

    return (firststage_variable_count, secondstage_variable_count,
            firststage_constraint_count, secondstage_constraint_count,
            stochastic_cost_count, stochastic_rhs_count,
            stochastic_matrix_count)
예제 #6
0
    def __call__(self, model, output_filename, solver_capability, io_options):

        # Make sure not to modify the user's dictionary,
        # they may be reusing it outside of this call
        io_options = dict(io_options)

        # Skip writing constraints whose body section is
        # fixed (i.e., no variables)
        skip_trivial_constraints = \
            io_options.pop("skip_trivial_constraints", False)

        # Use full Pyomo component names in the LP file rather
        # than shortened symbols (slower, but useful for debugging).
        symbolic_solver_labels = \
            io_options.pop("symbolic_solver_labels", False)

        output_fixed_variable_bounds = \
            io_options.pop("output_fixed_variable_bounds", False)

        # If False, unused variables will not be included in
        # the LP file. Otherwise, include all variables in
        # the bounds sections.
        include_all_variable_bounds = \
            io_options.pop("include_all_variable_bounds", False)

        labeler = io_options.pop("labeler", None)

        # How much effort do we want to put into ensuring the
        # LP file is written deterministically for a Pyomo model:
        #    0 : None
        #    1 : sort keys of indexed components (default)
        #    2 : sort keys AND sort names (over declaration order)
        file_determinism = io_options.pop("file_determinism", 1)

        # user defined orderings for variable and constraint
        # output
        row_order = io_options.pop("row_order", None)
        column_order = io_options.pop("column_order", None)

        # make sure the ONE_VAR_CONSTANT variable appears in
        # the objective even if the constant part of the
        # objective is zero
        force_objective_constant = \
            io_options.pop("force_objective_constant", False)

        if len(io_options):
            raise ValueError(
                "ProblemWriter_cpxlp passed unrecognized io_options:\n\t" +
                "\n\t".join("%s = %s" % (k, v)
                            for k, v in iteritems(io_options)))

        if symbolic_solver_labels and (labeler is not None):
            raise ValueError("ProblemWriter_cpxlp: Using both the "
                             "'symbolic_solver_labels' and 'labeler' "
                             "I/O options is forbidden")

        if symbolic_solver_labels:
            labeler = TextLabeler()
        elif labeler is None:
            labeler = NumericLabeler('x')

        # clear the collection of referenced variables.
        self._referenced_variable_ids.clear()

        if output_filename is None:
            output_filename = model.name + ".lp"

        # when sorting, there are a non-trivial number of
        # temporary objects created. these all yield
        # non-circular references, so disable GC - the
        # overhead is non-trivial, and because references
        # are non-circular, everything will be collected
        # immediately anyway.
        with PauseGC() as pgc:
            with open(output_filename, "w") as output_file:
                symbol_map = self._print_model_LP(
                    model,
                    output_file,
                    solver_capability,
                    labeler,
                    output_fixed_variable_bounds=output_fixed_variable_bounds,
                    file_determinism=file_determinism,
                    row_order=row_order,
                    column_order=column_order,
                    skip_trivial_constraints=skip_trivial_constraints,
                    force_objective_constant=force_objective_constant,
                    include_all_variable_bounds=include_all_variable_bounds)

        self._referenced_variable_ids.clear()

        return output_filename, symbol_map
예제 #7
0
    def compile_instance(self,
                         pyomo_instance,
                         symbolic_solver_labels=False,
                         output_fixed_variable_bounds=False,
                         skip_trivial_constraints=False):

        from pyomo.core.base import Var, Constraint, SOSConstraint
        from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree

        self._symbolic_solver_labels = symbolic_solver_labels
        self._output_fixed_variable_bounds = output_fixed_variable_bounds
        self._skip_trivial_constraints = skip_trivial_constraints

        self._has_quadratic_constraints = False
        self._has_quadratic_objective = False

        self._active_cplex_instance = CPLEXDirect._cplex_module.Cplex()

        if self._symbolic_solver_labels:
            labeler = self._labeler = TextLabeler()
        else:
            labeler = self._labeler = NumericLabeler('x')

        self._symbol_map = SymbolMap()
        self._instance = pyomo_instance
        if isinstance(pyomo_instance, IBlockStorage):
            # BIG HACK
            if not hasattr(pyomo_instance, "._symbol_maps"):
                setattr(pyomo_instance, "._symbol_maps", {})
            getattr(pyomo_instance, "._symbol_maps")[id(self._symbol_map)] = \
                self._symbol_map
        else:
            pyomo_instance.solutions.add_symbol_map(self._symbol_map)
        self._smap_id = id(self._symbol_map)

        # we use this when iterating over the constraints because it
        # will have a much smaller hash table, we also use this for
        # the warm start code after it is cleaned to only contain
        # variables referenced in the constraints
        self._variable_symbol_map = SymbolMap()

        # cplex wants the caller to set the problem type, which is (for
        # current purposes) strictly based on variable type counts.
        self._num_binary_variables = 0
        self._num_integer_variables = 0
        self._num_continuous_variables = 0
        self._used_sos_constraints = False

        #############################################
        # populate the variables in the cplex model #
        #############################################

        var_names = []
        var_lbs = []
        var_ubs = []
        var_types = []

        self._referenced_variable_ids.clear()

        # maps pyomo var data labels to the corresponding CPLEX variable id.
        self._cplex_variable_ids.clear()

        # cached in the loop below - used to update the symbol map
        # immediately following loop termination.
        var_label_pairs = []

        for var_data in pyomo_instance.component_data_objects(Var, active=True):

            if var_data.fixed and not self._output_fixed_variable_bounds:
                # if a variable is fixed, and we're preprocessing
                # fixed variables (as in not outputting them), there
                # is no need to add them to the compiled model.
                continue

            var_name = self._symbol_map.getSymbol(var_data, labeler)
            var_names.append(var_name)
            var_label_pairs.append((var_data, var_name))

            self._cplex_variable_ids[var_name] = len(self._cplex_variable_ids)

            if not var_data.has_lb():
                var_lbs.append(-CPLEXDirect._cplex_module.infinity)
            else:
                var_lbs.append(value(var_data.lb))

            if not var_data.has_ub():
                var_ubs.append(CPLEXDirect._cplex_module.infinity)
            else:
                var_ubs.append(value(var_data.ub))

            if var_data.is_integer():
                var_types.append(self._active_cplex_instance.variables.type.integer)
                self._num_integer_variables += 1
            elif var_data.is_binary():
                var_types.append(self._active_cplex_instance.variables.type.binary)
                self._num_binary_variables += 1
            elif var_data.is_continuous():
                var_types.append(self._active_cplex_instance.variables.type.continuous)
                self._num_continuous_variables += 1
            else:
                raise TypeError("Invalid domain type for variable with name '%s'. "
                                "Variable is not continuous, integer, or binary.")

        self._active_cplex_instance.variables.add(names=var_names,
                                                  lb=var_lbs,
                                                  ub=var_ubs,
                                                  types=var_types)

        self._active_cplex_instance.variables.add(lb=[1],
                                                  ub=[1],
                                                  names=["ONE_VAR_CONSTANT"])

        self._cplex_variable_ids["ONE_VAR_CONSTANT"] = len(self._cplex_variable_ids)

        self._variable_symbol_map.addSymbols(var_label_pairs)
        self._cplex_variable_names = self._active_cplex_instance.variables.get_names()

        ########################################################
        # populate the standard constraints in the cplex model #
        ########################################################

        expressions = []
        senses = []
        rhss = []
        range_values = []
        names = []

        qexpressions = []
        qlinears = []
        qsenses = []
        qrhss = []
        qnames = []

        for block in pyomo_instance.block_data_objects(active=True):

            gen_con_canonical_repn = \
                getattr(block, "_gen_con_canonical_repn", True)
            # Get/Create the ComponentMap for the repn
            if not hasattr(block,'_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn

            for con in block.component_data_objects(Constraint,
                                                    active=True,
                                                    descend_into=False):

                if (not con.has_lb()) and \
                   (not con.has_ub()):
                    assert not con.equality
                    continue  # not binding at all, don't bother

                con_repn = None
                if con._linear_canonical_form:
                    con_repn = con.canonical_form()
                elif isinstance(con, LinearCanonicalRepn):
                    con_repn = con
                else:
                    if gen_con_canonical_repn:
                        con_repn = generate_canonical_repn(con.body)
                        block_canonical_repn[con] = con_repn
                    else:
                        con_repn = block_canonical_repn[con]

                # There are conditions, e.g., when fixing variables, under which
                # a constraint block might be empty.  Ignore these, for both
                # practical reasons and the fact that the CPLEX LP format
                # requires a variable in the constraint body.  It is also
                # possible that the body of the constraint consists of only a
                # constant, in which case the "variable" of
                if isinstance(con_repn, LinearCanonicalRepn):
                    if self._skip_trivial_constraints and \
                       ((con_repn.linear is None) or \
                        (len(con_repn.linear) == 0)):
                       continue
                else:
                    # we shouldn't come across a constant canonical repn
                    # that is not LinearCanonicalRepn
                    assert not canonical_is_constant(con_repn)

                name = self._symbol_map.getSymbol(con, labeler)
                expr = None
                qexpr = None
                quadratic = False
                if isinstance(con_repn, LinearCanonicalRepn):
                    expr, offset = \
                        self._encode_constraint_body_linear_specialized(con_repn,
                                                                        labeler,
                                                                        use_variable_names=False,
                                                                        cplex_variable_name_index_map=self._cplex_variable_ids)
                else:
                    degree = canonical_degree(con_repn)
                    if degree == 2:
                        quadratic = True
                    elif (degree != 0) or (degree != 1):
                        raise ValueError(
                            "CPLEXPersistent plugin does not support general nonlinear "
                            "constraint expression (only linear or quadratic).\n"
                            "Constraint: %s" % (con.name))
                    expr, offset = self._encode_constraint_body_linear(con_repn,
                                                                       labeler)

                if quadratic:
                    if expr is None:
                        expr = CPLEXDirect._cplex_module.SparsePair(ind=[0],val=[0.0])
                    self._has_quadratic_constraints = True

                    qexpr = self._encode_constraint_body_quadratic(con_repn,labeler)
                    qnames.append(name)

                    if con.equality:
                        # equality constraint.
                        qsenses.append('E')
                        qrhss.append(self._get_bound(con.lower) - offset)

                    elif con.has_lb() and con.has_ub():

                        raise RuntimeError(
                            "The CPLEXDirect plugin can not translate range "
                            "constraints containing quadratic expressions.")

                    elif con.has_lb():
                        assert not con.has_ub()
                        qsenses.append('G')
                        qrhss.append(self._get_bound(con.lower) - offset)

                    else:
                        assert con.has_ub()
                        qsenses.append('L')
                        qrhss.append(self._get_bound(con.upper) - offset)

                    qlinears.append(expr)
                    qexpressions.append(qexpr)

                else:
                    names.append(name)
                    expressions.append(expr)

                    if con.equality:
                        # equality constraint.
                        senses.append('E')
                        rhss.append(self._get_bound(con.lower) - offset)
                        range_values.append(0.0)

                    elif con.has_lb() and con.has_ub():
                        # ranged constraint.
                        senses.append('R')
                        lower_bound = self._get_bound(con.lower) - offset
                        upper_bound = self._get_bound(con.upper) - offset
                        rhss.append(lower_bound)
                        range_values.append(upper_bound - lower_bound)

                    elif con.has_lb():
                        senses.append('G')
                        rhss.append(self._get_bound(con.lower) - offset)
                        range_values.append(0.0)

                    else:
                        assert con.has_ub()
                        senses.append('L')
                        rhss.append(self._get_bound(con.upper) - offset)
                        range_values.append(0.0)

        ###################################################
        # populate the SOS constraints in the cplex model #
        ###################################################

        # SOS constraints - largely taken from cpxlp.py so updates there,
        # should be applied here
        # TODO: Allow users to specify the variables coefficients for custom
        # branching/set orders - refer to cpxlp.py
        sosn = self._capabilities.sosn
        sos1 = self._capabilities.sos1
        sos2 = self._capabilities.sos2
        modelSOS = ModelSOS()
        for soscondata in pyomo_instance.component_data_objects(SOSConstraint,
                                                                active=True):
            level = soscondata.level
            if (level == 1 and not sos1) or \
               (level == 2 and not sos2) or \
               (level > 2 and not sosn):
                raise Exception("Solver does not support SOS level %s constraints"
                                % (level,))
            modelSOS.count_constraint(self._symbol_map,
                                      labeler,
                                      self._variable_symbol_map,
                                      soscondata)

        if modelSOS.sosType:
            for key in modelSOS.sosType:
                self._active_cplex_instance.SOS.add(type = modelSOS.sosType[key],
                                       name = modelSOS.sosName[key],
                                       SOS = [modelSOS.varnames[key],
                                              modelSOS.weights[key]])
                self._referenced_variable_ids.update(modelSOS.varids[key])
            self._used_sos_constraints = True

        self._active_cplex_instance.linear_constraints.add(
            lin_expr=expressions,
            senses=senses,
            rhs=rhss,
            range_values=range_values,
            names=names)

        for index in xrange(len(qexpressions)):
            self._active_cplex_instance.quadratic_constraints.add(
                lin_expr=qlinears[index],
                quad_expr=qexpressions[index],
                sense=qsenses[index],
                rhs=qrhss[index],
                name=qnames[index])

        #############################################
        # populate the objective in the cplex model #
        #############################################

        self.compile_objective(pyomo_instance)
예제 #8
0
    def _populate_gurobi_instance(self, pyomo_instance):

        from pyomo.core.base import Var, Objective, Constraint, SOSConstraint
        from pyomo.repn import LinearCanonicalRepn, canonical_degree

        try:
            grbmodel = Model(name=pyomo_instance.name)
        except Exception:
            e = sys.exc_info()[1]
            msg = 'Unable to create Gurobi model.  Have you installed the Python'\
            '\n       bindings for Gurobi?\n\n\tError message: %s'
            raise Exception(msg % e)

        if self._symbolic_solver_labels:
            labeler = TextLabeler()
        else:
            labeler = NumericLabeler('x')
        # cache to avoid dictionary getitem calls in the loops below.
        self_symbol_map = self._symbol_map = SymbolMap()
        pyomo_instance.solutions.add_symbol_map(self_symbol_map)
        self._smap_id = id(self_symbol_map)

        # we use this when iterating over the constraints because it
        # will have a much smaller hash table, we also use this for
        # the warm start code after it is cleaned to only contain
        # variables referenced in the constraints
        self_variable_symbol_map = self._variable_symbol_map = SymbolMap()
        var_symbol_pairs = []

        # maps _VarData labels to the corresponding Gurobi variable object
        pyomo_gurobi_variable_map = {}

        self._referenced_variable_ids.clear()

        # cache to avoid dictionary getitem calls in the loop below.
        grb_infinity = GRB.INFINITY

        for var_value in pyomo_instance.component_data_objects(Var,
                                                               active=True):

            lb = -grb_infinity
            ub = grb_infinity

            if (var_value.lb is not None) and (var_value.lb != -infinity):
                lb = value(var_value.lb)
            if (var_value.ub is not None) and (var_value.ub != infinity):
                ub = value(var_value.ub)

            # _VarValue objects will not be in the symbol map yet, so
            # avoid some checks.
            var_value_label = self_symbol_map.createSymbol(var_value, labeler)
            var_symbol_pairs.append((var_value, var_value_label))

            # be sure to impart the integer and binary nature of any variables
            if var_value.is_integer():
                var_type = GRB.INTEGER
            elif var_value.is_binary():
                var_type = GRB.BINARY
            elif var_value.is_continuous():
                var_type = GRB.CONTINUOUS
            else:
                raise TypeError(
                    "Invalid domain type for variable with name '%s'. "
                    "Variable is not continuous, integer, or binary.")

            pyomo_gurobi_variable_map[var_value_label] = \
                grbmodel.addVar(lb=lb, \
                                ub=ub, \
                                vtype=var_type, \
                                name=var_value_label)

        self_variable_symbol_map.addSymbols(var_symbol_pairs)

        grbmodel.update()

        # The next loop collects the following component types from the model:
        #  - SOSConstraint
        #  - Objective
        #  - Constraint
        sos1 = self._capabilities.sos1
        sos2 = self._capabilities.sos2
        modelSOS = ModelSOS()
        objective_cntr = 0
        # Track the range constraints and their associated variables added by gurobi
        self._last_native_var_idx = grbmodel.NumVars - 1
        range_var_idx = grbmodel.NumVars
        _self_range_con_var_pairs = self._range_con_var_pairs = []
        for block in pyomo_instance.block_data_objects(active=True):

            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_canonical_repn", True)
            gen_con_canonical_repn = \
                getattr(block, "_gen_con_canonical_repn", True)
            # Get/Create the ComponentMap for the repn
            if not hasattr(block, '_canonical_repn'):
                block._canonical_repn = ComponentMap()
            block_canonical_repn = block._canonical_repn

            # SOSConstraints
            for soscondata in block.component_data_objects(SOSConstraint,
                                                           active=True,
                                                           descend_into=False):
                level = soscondata.level
                if (level == 1 and not sos1) or \
                   (level == 2 and not sos2) or \
                   (level > 2):
                    raise RuntimeError(
                        "Solver does not support SOS level %s constraints" %
                        (level, ))
                modelSOS.count_constraint(self_symbol_map, labeler,
                                          self_variable_symbol_map,
                                          pyomo_gurobi_variable_map,
                                          soscondata)

            # Objective
            for obj_data in block.component_data_objects(Objective,
                                                         active=True,
                                                         descend_into=False):

                if objective_cntr > 1:
                    raise ValueError(
                        "Multiple active objectives found on Pyomo instance '%s'. "
                        "Solver '%s' will only handle a single active objective" \
                        % (pyomo_instance.cname(True), self.type))

                sense = GRB_MIN if (obj_data.is_minimizing()) else GRB_MAX
                grbmodel.ModelSense = sense
                obj_expr = LinExpr()

                if gen_obj_canonical_repn:
                    obj_repn = generate_canonical_repn(obj_data.expr)
                    block_canonical_repn[obj_data] = obj_repn
                else:
                    obj_repn = block_canonical_repn[obj_data]

                if isinstance(obj_repn, LinearCanonicalRepn):

                    if obj_repn.constant != None:
                        obj_expr.addConstant(obj_repn.constant)

                    if obj_repn.linear != None:

                        for i in xrange(len(obj_repn.linear)):
                            var_coefficient = obj_repn.linear[i]
                            var_value = obj_repn.variables[i]
                            self._referenced_variable_ids.add(id(var_value))
                            label = self_variable_symbol_map.getSymbol(
                                var_value)
                            obj_expr.addTerms(var_coefficient,
                                              pyomo_gurobi_variable_map[label])
                else:

                    if 0 in obj_repn:  # constant term
                        obj_expr.addConstant(obj_repn[0][None])

                    if 1 in obj_repn:  # first-order terms
                        hash_to_variable_map = obj_repn[-1]
                        for var_hash, var_coefficient in iteritems(
                                obj_repn[1]):
                            vardata = hash_to_variable_map[var_hash]
                            self._referenced_variable_ids.add(id(vardata))
                            label = self_variable_symbol_map.getSymbol(vardata)
                            obj_expr.addTerms(var_coefficient,
                                              pyomo_gurobi_variable_map[label])

                    if 2 in obj_repn:
                        obj_expr = QuadExpr(obj_expr)
                        hash_to_variable_map = obj_repn[-1]
                        for quad_repn, coef in iteritems(obj_repn[2]):
                            gurobi_expr = QuadExpr(coef)
                            for var_hash, exponent in iteritems(quad_repn):
                                vardata = hash_to_variable_map[var_hash]
                                self._referenced_variable_ids.add(id(vardata))
                                gurobi_var = pyomo_gurobi_variable_map\
                                             [self_variable_symbol_map.\
                                              getSymbol(vardata)]
                                gurobi_expr *= gurobi_var
                                if exponent == 2:
                                    gurobi_expr *= gurobi_var
                            obj_expr += gurobi_expr

                    degree = canonical_degree(obj_repn)
                    if (degree is None) or (degree > 2):
                        raise ValueError(
                            "gurobi_direct plugin does not support general nonlinear "
                            "objective expressions (only linear or quadratic).\n"
                            "Objective: %s" % (obj_data.cname(True)))

                # need to cache the objective label, because the
                # GUROBI python interface doesn't track this.
                # _ObjectiveData objects will not be in the symbol map
                # yet, so avoid some checks.
                self._objective_label = \
                    self_symbol_map.createSymbol(obj_data, labeler)

                grbmodel.setObjective(obj_expr, sense=sense)

            # Constraint
            for constraint_data in block.component_data_objects(
                    Constraint, active=True, descend_into=False):

                if (constraint_data.lower is None) and \
                   (constraint_data.upper is None):
                    continue  # not binding at all, don't bother

                con_repn = None
                if isinstance(constraint_data, LinearCanonicalRepn):
                    con_repn = constraint_data
                else:
                    if gen_con_canonical_repn:
                        con_repn = generate_canonical_repn(
                            constraint_data.body)
                        block_canonical_repn[constraint_data] = con_repn
                    else:
                        con_repn = block_canonical_repn[constraint_data]

                offset = 0.0
                # _ConstraintData objects will not be in the symbol
                # map yet, so avoid some checks.
                constraint_label = \
                    self_symbol_map.createSymbol(constraint_data, labeler)

                trivial = False
                if isinstance(con_repn, LinearCanonicalRepn):

                    #
                    # optimization (these might be generated on the fly)
                    #
                    constant = con_repn.constant
                    coefficients = con_repn.linear
                    variables = con_repn.variables

                    if constant is not None:
                        offset = constant
                    expr = LinExpr() + offset

                    if coefficients is not None:

                        linear_coefs = list()
                        linear_vars = list()

                        for i in xrange(len(coefficients)):

                            var_coefficient = coefficients[i]
                            var_value = variables[i]
                            self._referenced_variable_ids.add(id(var_value))
                            label = self_variable_symbol_map.getSymbol(
                                var_value)
                            linear_coefs.append(var_coefficient)
                            linear_vars.append(
                                pyomo_gurobi_variable_map[label])

                        expr += LinExpr(linear_coefs, linear_vars)

                    else:

                        trivial = True

                else:

                    if 0 in con_repn:
                        offset = con_repn[0][None]
                    expr = LinExpr() + offset

                    if 1 in con_repn:  # first-order terms

                        linear_coefs = list()
                        linear_vars = list()

                        hash_to_variable_map = con_repn[-1]
                        for var_hash, var_coefficient in iteritems(
                                con_repn[1]):
                            var = hash_to_variable_map[var_hash]
                            self._referenced_variable_ids.add(id(var))
                            label = self_variable_symbol_map.getSymbol(var)
                            linear_coefs.append(var_coefficient)
                            linear_vars.append(
                                pyomo_gurobi_variable_map[label])

                        expr += LinExpr(linear_coefs, linear_vars)

                    if 2 in con_repn:  # quadratic constraint
                        if _GUROBI_VERSION_MAJOR < 5:
                            raise ValueError(
                                "The gurobi_direct plugin does not handle quadratic "
                                "constraint expressions for Gurobi major versions "
                                "< 5. Current version: Gurobi %s.%s%s" %
                                (gurobi.version()))

                        expr = QuadExpr(expr)
                        hash_to_variable_map = con_repn[-1]
                        for quad_repn, coef in iteritems(con_repn[2]):
                            gurobi_expr = QuadExpr(coef)
                            for var_hash, exponent in iteritems(quad_repn):
                                vardata = hash_to_variable_map[var_hash]
                                self._referenced_variable_ids.add(id(vardata))
                                gurobi_var = pyomo_gurobi_variable_map\
                                             [self_variable_symbol_map.\
                                              getSymbol(vardata)]
                                gurobi_expr *= gurobi_var
                                if exponent == 2:
                                    gurobi_expr *= gurobi_var
                            expr += gurobi_expr

                    degree = canonical_degree(con_repn)
                    if (degree is None) or (degree > 2):
                        raise ValueError(
                            "gurobi_direct plugin does not support general nonlinear "
                            "constraint expressions (only linear or quadratic).\n"
                            "Constraint: %s" % (constraint_data.cname(True)))

                if (not trivial) or (not self._skip_trivial_constraints):

                    if constraint_data.equality:
                        sense = GRB.EQUAL
                        bound = self._get_bound(constraint_data.lower)
                        grbmodel.addConstr(lhs=expr,
                                           sense=sense,
                                           rhs=bound,
                                           name=constraint_label)
                    else:
                        # L <= body <= U
                        if (constraint_data.upper is not None) and \
                           (constraint_data.lower is not None):
                            grb_con = grbmodel.addRange(
                                expr, self._get_bound(constraint_data.lower),
                                self._get_bound(constraint_data.upper),
                                constraint_label)
                            _self_range_con_var_pairs.append(
                                (grb_con, range_var_idx))
                            range_var_idx += 1
                        # body <= U
                        elif constraint_data.upper is not None:
                            bound = self._get_bound(constraint_data.upper)
                            if bound < float('inf'):
                                grbmodel.addConstr(lhs=expr,
                                                   sense=GRB.LESS_EQUAL,
                                                   rhs=bound,
                                                   name=constraint_label)
                        # L <= body
                        else:
                            bound = self._get_bound(constraint_data.lower)
                            if bound > -float('inf'):
                                grbmodel.addConstr(lhs=expr,
                                                   sense=GRB.GREATER_EQUAL,
                                                   rhs=bound,
                                                   name=constraint_label)

        if modelSOS.sosType:
            for key in modelSOS.sosType:
                grbmodel.addSOS(modelSOS.sosType[key], \
                                modelSOS.varnames[key], \
                                modelSOS.weights[key] )
                self._referenced_variable_ids.update(modelSOS.varids[key])

        for var_id in self._referenced_variable_ids:
            varname = self._variable_symbol_map.byObject[var_id]
            vardata = self._variable_symbol_map.bySymbol[varname]()
            if vardata.fixed:
                if not self._output_fixed_variable_bounds:
                    raise ValueError(
                        "Encountered a fixed variable (%s) inside an active objective "
                        "or constraint expression on model %s, which is usually indicative of "
                        "a preprocessing error. Use the IO-option 'output_fixed_variable_bounds=True' "
                        "to suppress this error and fix the variable by overwriting its bounds in "
                        "the Gurobi instance." % (
                            vardata.cname(True),
                            pyomo_instance.cname(True),
                        ))

                grbvar = pyomo_gurobi_variable_map[varname]
                grbvar.setAttr(GRB.Attr.UB, vardata.value)
                grbvar.setAttr(GRB.Attr.LB, vardata.value)

        grbmodel.update()

        self._gurobi_instance = grbmodel
        self._pyomo_gurobi_variable_map = pyomo_gurobi_variable_map
예제 #9
0
class IntervalTightener(PersistentBase):
    def __init__(self):
        super(IntervalTightener, self).__init__()
        self._config = IntervalConfig()
        self._cmodel = None
        self._var_map = dict()
        self._con_map = dict()
        self._param_map = dict()
        self._rvar_map = dict()
        self._rcon_map = dict()
        self._pyomo_expr_types = cmodel.PyomoExprTypes()
        self._symbolic_solver_labels: bool = False
        self._symbol_map = SymbolMap()
        self._var_labeler = None
        self._con_labeler = None
        self._param_labeler = None
        self._obj_labeler = None
        self._objective = None

    @property
    def config(self):
        return self._config

    @config.setter
    def config(self, val: IntervalConfig):
        self._config = val

    def set_instance(self,
                     model,
                     symbolic_solver_labels: Optional[bool] = None):
        saved_config = self.config
        saved_update_config = self.update_config

        self.__init__()
        self.config = saved_config
        self.update_config = saved_update_config
        self._expr_types = cmodel.PyomoExprTypes()

        if symbolic_solver_labels is not None:
            self._symbolic_solver_labels = symbolic_solver_labels
        if self._symbolic_solver_labels:
            self._var_labeler = TextLabeler()
            self._con_labeler = TextLabeler()
            self._param_labeler = TextLabeler()
            self._obj_labeler = TextLabeler()

        self._model = model
        self._cmodel = cmodel.FBBTModel()
        self.add_block(model)
        if self._objective is None:
            self.set_objective(None)

    def _add_variables(self, variables: List[_GeneralVarData]):
        if self._symbolic_solver_labels:
            set_name = True
            symbol_map = self._symbol_map
            labeler = self._var_labeler
        else:
            set_name = False
            symbol_map = None
            labeler = None
        cmodel.process_pyomo_vars(self._pyomo_expr_types, variables,
                                  self._var_map, self._param_map, self._vars,
                                  self._rvar_map, set_name, symbol_map,
                                  labeler, False)

    def _add_params(self, params: List[_ParamData]):
        cparams = cmodel.create_params(len(params))
        for ndx, p in enumerate(params):
            cp = cparams[ndx]
            cp.value = p.value
            self._param_map[id(p)] = cp
        if self._symbolic_solver_labels:
            for ndx, p in enumerate(params):
                cp = cparams[ndx]
                cp.name = self._symbol_map.getSymbol(p, self._param_labeler)

    def _add_constraints(self, cons: List[_GeneralConstraintData]):
        cmodel.process_fbbt_constraints(self._cmodel, self._pyomo_expr_types,
                                        cons, self._var_map, self._param_map,
                                        self._active_constraints,
                                        self._con_map, self._rcon_map)
        if self._symbolic_solver_labels:
            for c, cc in self._con_map.items():
                cc.name = self._symbol_map.getSymbol(c, self._con_labeler)

    def _add_sos_constraints(self, cons: List[_SOSConstraintData]):
        if len(cons) != 0:
            raise NotImplementedError(
                'IntervalTightener does not support SOS constraints')

    def _remove_constraints(self, cons: List[_GeneralConstraintData]):
        if self._symbolic_solver_labels:
            for c in cons:
                self._symbol_map.removeSymbol(c)
                self._con_labeler.remove_obj(c)
        for c in cons:
            cc = self._con_map.pop(c)
            self._cmodel.remove_constraint(cc)
            del self._rcon_map[cc]

    def _remove_sos_constraints(self, cons: List[_SOSConstraintData]):
        if len(cons) != 0:
            raise NotImplementedError(
                'IntervalTightener does not support SOS constraints')

    def _remove_variables(self, variables: List[_GeneralVarData]):
        if self._symbolic_solver_labels:
            for v in variables:
                self._symbol_map.removeSymbol(v)
                self._var_labeler.remove_obj(v)
        for v in variables:
            cvar = self._var_map.pop(id(v))
            del self._rvar_map[cvar]

    def _remove_params(self, params: List[_ParamData]):
        if self._symbolic_solver_labels:
            for p in params:
                self._symbol_map.removeSymbol(p)
                self._param_labeler.remove_obj(p)
        for p in params:
            del self._param_map[id(p)]

    def _update_variables(self, variables: List[_GeneralVarData]):
        cmodel.process_pyomo_vars(self._pyomo_expr_types, variables,
                                  self._var_map, self._param_map, self._vars,
                                  self._rvar_map, False, None, None, True)

    def update_params(self):
        for p_id, p in self._params.items():
            cp = self._param_map[p_id]
            cp.value = p.value

    def set_objective(self, obj: _GeneralObjectiveData):
        if self._symbolic_solver_labels:
            if self._objective is not None:
                self._symbol_map.removeSymbol(self._objective)
                self._obj_labeler.remove_obj(self._objective)
        super().set_objective(obj)

    def _set_objective(self, obj: _GeneralObjectiveData):
        if obj is None:
            ce = cmodel.Constant(0)
            sense = 0
        else:
            ce = cmodel.appsi_expr_from_pyomo_expr(obj.expr, self._var_map,
                                                   self._param_map,
                                                   self._pyomo_expr_types)
            if obj.sense is minimize:
                sense = 0
            else:
                sense = 1
        cobj = cmodel.FBBTObjective(ce)
        cobj.sense = sense
        self._cmodel.objective = cobj
        self._objective = obj
        if self._symbolic_solver_labels and obj is not None:
            cobj.name = self._symbol_map.getSymbol(obj, self._obj_labeler)

    def _update_pyomo_var_bounds(self):
        for cv, v in self._rvar_map.items():
            cv_lb = cv.get_lb()
            cv_ub = cv.get_ub()
            if -cmodel.inf < cv_lb:
                v.setlb(cv_lb)
                v_id = id(v)
                _v, _lb, _ub, _fixed, _domain, _value = self._vars[v_id]
                self._vars[v_id] = (_v, cv_lb, _ub, _fixed, _domain, _value)
            if cv_ub < cmodel.inf:
                v.setub(cv_ub)
                v_id = id(v)
                _v, _lb, _ub, _fixed, _domain, _value = self._vars[v_id]
                self._vars[v_id] = (_v, _lb, cv_ub, _fixed, _domain, _value)

    def _deactivate_satisfied_cons(self):
        if self.config.deactivate_satisfied_constraints:
            for c, cc in self._con_map.items():
                if not cc.active:
                    c.deactivate()

    def perform_fbbt(self,
                     model: _BlockData,
                     symbolic_solver_labels: Optional[bool] = None):
        if model is not self._model:
            self.set_instance(model,
                              symbolic_solver_labels=symbolic_solver_labels)
        else:
            if symbolic_solver_labels is not None and symbolic_solver_labels != self._symbolic_solver_labels:
                raise RuntimeError(
                    'symbolic_solver_labels can only be changed through the set_instance method. '
                    'Please either use set_instance or create a new instance of IntervalTightener.'
                )
            self.update()
        try:
            n_iter = self._cmodel.perform_fbbt(
                self.config.feasibility_tol, self.config.integer_tol,
                self.config.improvement_tol, self.config.max_iter,
                self.config.deactivate_satisfied_constraints)
        finally:
            # we want to make sure the pyomo model and cmodel stay in sync
            # even if an exception is raised and caught
            self._update_pyomo_var_bounds()
            self._deactivate_satisfied_cons()
        return n_iter

    def perform_fbbt_with_seed(self, model: _BlockData,
                               seed_var: _GeneralVarData):
        if model is not self._model:
            self.set_instance(model)
        else:
            self.update()
        try:
            n_iter = self._cmodel.perform_fbbt_with_seed(
                self._var_map[id(seed_var)], self.config.feasibility_tol,
                self.config.integer_tol, self.config.improvement_tol,
                self.config.max_iter,
                self.config.deactivate_satisfied_constraints)
        finally:
            # we want to make sure the pyomo model and cmodel stay in sync
            # even if an exception is raised and caught
            self._update_pyomo_var_bounds()
            self._deactivate_satisfied_cons()
        return n_iter