Example #1
0
def Pyomo2FuncDesigner(instance):
    if not FD_available:
        return None

    ipoint = {}
    vars = {}
    sense = None
    nobj = 0
    smap = SymbolMap()

    _f_name = []
    _f = []
    _c = []
    for con in instance.component_data_objects(Constraint, active=True):
        body = Pyomo2FD_expression(con.body, ipoint, vars, smap)
        if not con.lower is None:
            lower = Pyomo2FD_expression(con.lower, ipoint, vars, smap)
            _c.append( body > lower )
        if not con.upper is None:
            upper = Pyomo2FD_expression(con.upper, ipoint, vars, smap)
            _c.append( body < upper )

    for var in instance.component_data_objects(Var, active=True):
        body = Pyomo2FD_expression(var, ipoint, vars, smap)
        if not var.lb is None:
            lower = Pyomo2FD_expression(var.lb, ipoint, vars, smap)
            _c.append( body > lower )
        if not var.ub is None:
            upper = Pyomo2FD_expression(var.ub, ipoint, vars, smap)
            _c.append( body < upper )


    for obj in instance.component_data_objects(Objective, active=True):
        nobj += 1
        if obj.is_minimizing():
            _f.append( Pyomo2FD_expression(obj.expr, ipoint, vars, smap) )
        else:
            _f.append( - Pyomo2FD_expression(obj.expr, ipoint, vars, smap) )
        _f_name.append(obj.name)
        smap.getSymbol(obj, lambda objective: objective.name)

    # TODO - use 0.0 for default values???
    # TODO - create results map
    S = FuncDesigner.oosystem()
    S._symbol_map = smap
    S.f = _f[0]
    S._f_name = _f_name
    S.constraints.update(_c)
    S.initial_point = ipoint
    S.sense = sense
    return S
Example #2
0
def Pyomo2FuncDesigner(instance):
    if not FD_available:
        return None

    ipoint = {}
    vars = {}
    sense = None
    nobj = 0
    smap = SymbolMap()

    _f_name = []
    _f = []
    _c = []
    for con in instance.component_data_objects(Constraint, active=True):
        body = Pyomo2FD_expression(con.body, ipoint, vars, smap)
        if not con.lower is None:
            lower = Pyomo2FD_expression(con.lower, ipoint, vars, smap)
            _c.append( body > lower )
        if not con.upper is None:
            upper = Pyomo2FD_expression(con.upper, ipoint, vars, smap)
            _c.append( body < upper )

    for var in instance.component_data_objects(Var, active=True):
        body = Pyomo2FD_expression(var, ipoint, vars, smap)
        if not var.lb is None:
            lower = Pyomo2FD_expression(var.lb, ipoint, vars, smap)
            _c.append( body > lower )
        if not var.ub is None:
            upper = Pyomo2FD_expression(var.ub, ipoint, vars, smap)
            _c.append( body < upper )


    for obj in instance.component_data_objects(Objective, active=True):
        nobj += 1
        if obj.is_minimizing():
            _f.append( Pyomo2FD_expression(obj.expr, ipoint, vars, smap) )
        else:
            _f.append( - Pyomo2FD_expression(obj.expr, ipoint, vars, smap) )
        _f_name.append( obj.cname(True) )
        smap.getSymbol(obj, lambda objective: objective.cname(True))

    # TODO - use 0.0 for default values???
    # TODO - create results map
    S = FuncDesigner.oosystem()
    S._symbol_map = smap
    S.f = _f[0]
    S._f_name = _f_name
    S.constraints.update(_c)
    S.initial_point = ipoint
    S.sense = sense
    return S
Example #3
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)

        # NOTE: io_options is a simple dictionary of keyword-value
        #       pairs specific to this writer.
        symbolic_solver_labels = \
            io_options.pop("symbolic_solver_labels", 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)

        sorter = SortComponents.unsorted
        if file_determinism >= 1:
            sorter = sorter | SortComponents.indices
            if file_determinism >= 2:
                sorter = sorter | SortComponents.alphabetical

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

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

        # Note: Baron does not allow specification of runtime
        #       option outside of this file, so we add support
        #       for them here
        solver_options = io_options.pop("solver_options", {})

        if len(io_options):
            raise ValueError(
                "ProblemWriter_baron_writer 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("Baron problem writer: Using both the "
                             "'symbolic_solver_labels' and 'labeler' "
                             "I/O options is forbidden")

        # Make sure there are no strange ActiveComponents. The expression
        # walker will handle strange things in constraints later.
        model_ctypes = model.collect_ctypes(active=True)
        invalids = set()
        for t in (model_ctypes - valid_active_ctypes_minlp):
            if issubclass(t, ActiveComponent):
                invalids.add(t)
        if len(invalids):
            invalids = [t.__name__ for t in invalids]
            raise RuntimeError(
                "Unallowable active component(s) %s.\nThe BARON writer cannot "
                "export models with this component type." %
                ", ".join(invalids))

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

        output_file = open(output_filename, "w")

        # Process the options. Rely on baron to catch
        # and reset bad option values
        output_file.write("OPTIONS {\n")
        summary_found = False
        if len(solver_options):
            for key, val in iteritems(solver_options):
                if (key.lower() == 'summary'):
                    summary_found = True
                if key.endswith("Name"):
                    output_file.write(key + ": \"" + str(val) + "\";\n")
                else:
                    output_file.write(key + ": " + str(val) + ";\n")
        if not summary_found:
            # The 'summary option is defaulted to 0, so that no
            # summary file is generated in the directory where the
            # user calls baron. Check if a user explicitly asked for
            # a summary file.
            output_file.write("Summary: 0;\n")
        output_file.write("}\n\n")

        if symbolic_solver_labels:
            # Note that the Var and Constraint labelers must use the
            # same labeler, so that we can correctly detect name
            # collisions (which can arise when we truncate the labels to
            # the max allowable length.  BARON requires all identifiers
            # to start with a letter.  We will (randomly) choose "s_"
            # (for 'shortened')
            v_labeler = c_labeler = ShortNameLabeler(15,
                                                     prefix='s_',
                                                     suffix='_',
                                                     caseInsensitive=True,
                                                     legalRegex='^[a-zA-Z]')
        elif labeler is None:
            v_labeler = NumericLabeler('x')
            c_labeler = NumericLabeler('c')
        else:
            v_labeler = c_labeler = labeler

        symbol_map = SymbolMap()
        symbol_map.default_labeler = v_labeler
        #sm_bySymbol = symbol_map.bySymbol

        # Cache the list of model blocks so we don't have to call
        # model.block_data_objects() many many times, which is slow
        # for indexed blocks
        all_blocks_list = list(
            model.block_data_objects(active=True,
                                     sort=sorter,
                                     descend_into=True))
        active_components_data_var = {}
        #for block in all_blocks_list:
        #    tmp = active_components_data_var[id(block)] = \
        #          list(obj for obj in block.component_data_objects(Var,
        #                                                           sort=sorter,
        #                                                           descend_into=False))
        #    create_symbols_func(symbol_map, tmp, labeler)

        # GAH: Not sure this is necessary, and also it would break for
        #      non-mutable indexed params so I am commenting out for now.
        #for param_data in active_components_data(block, Param, sort=sorter):
        #instead of checking if param_data._mutable:
        #if not param_data.is_constant():
        #    create_symbol_func(symbol_map, param_data, labeler)

        #symbol_map_variable_ids = set(symbol_map.byObject.keys())
        #object_symbol_dictionary = symbol_map.byObject

        #
        # Go through the objectives and constraints and generate
        # the output so that we can obtain the set of referenced
        # variables.
        #
        equation_section_stream = StringIO()
        referenced_variable_ids, branching_priorities_suffixes = \
            self._write_equations_section(
                model,
                equation_section_stream,
                all_blocks_list,
                active_components_data_var,
                symbol_map,
                c_labeler,
                output_fixed_variable_bounds,
                skip_trivial_constraints,
                sorter)

        #
        # BINARY_VARIABLES, INTEGER_VARIABLES, POSITIVE_VARIABLES, VARIABLES
        #

        BinVars = []
        IntVars = []
        PosVars = []
        Vars = []
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.is_continuous():
                if var_data.has_lb() and (value(var_data.lb) >= 0):
                    TypeList = PosVars
                else:
                    TypeList = Vars
            elif var_data.is_binary():
                TypeList = BinVars
            elif var_data.is_integer():
                TypeList = IntVars
            else:
                assert False
            TypeList.append(name)

        if len(BinVars) > 0:
            BinVars.sort()
            output_file.write('BINARY_VARIABLES ')
            output_file.write(", ".join(BinVars))
            output_file.write(';\n\n')

        if len(IntVars) > 0:
            IntVars.sort()
            output_file.write('INTEGER_VARIABLES ')
            output_file.write(", ".join(IntVars))
            output_file.write(';\n\n')

        PosVars.append('ONE_VAR_CONST__')
        PosVars.sort()
        output_file.write('POSITIVE_VARIABLES ')
        output_file.write(", ".join(PosVars))
        output_file.write(';\n\n')

        if len(Vars) > 0:
            Vars.sort()
            output_file.write('VARIABLES ')
            output_file.write(", ".join(Vars))
            output_file.write(';\n\n')

        #
        # LOWER_BOUNDS
        #

        lbounds = {}
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.fixed:
                if output_fixed_variable_bounds:
                    var_data_lb = ftoa(var_data.value)
                else:
                    var_data_lb = None
            else:
                var_data_lb = None
                if var_data.has_lb():
                    var_data_lb = ftoa(var_data.lb)

            if var_data_lb is not None:
                name_to_output = symbol_map.getSymbol(var_data)
                lbounds[name_to_output] = '%s: %s;\n' % (name_to_output,
                                                         var_data_lb)

        if len(lbounds) > 0:
            output_file.write("LOWER_BOUNDS{\n")
            output_file.write("".join(lbounds[key]
                                      for key in sorted(lbounds.keys())))
            output_file.write("}\n\n")
        lbounds = None

        #
        # UPPER_BOUNDS
        #

        ubounds = {}
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.fixed:
                if output_fixed_variable_bounds:
                    var_data_ub = ftoa(var_data.value)
                else:
                    var_data_ub = None
            else:
                var_data_ub = None
                if var_data.has_ub():
                    var_data_ub = ftoa(var_data.ub)

            if var_data_ub is not None:
                name_to_output = symbol_map.getSymbol(var_data)
                ubounds[name_to_output] = '%s: %s;\n' % (name_to_output,
                                                         var_data_ub)

        if len(ubounds) > 0:
            output_file.write("UPPER_BOUNDS{\n")
            output_file.write("".join(ubounds[key]
                                      for key in sorted(ubounds.keys())))
            output_file.write("}\n\n")
        ubounds = None

        #
        # BRANCHING_PRIORITIES
        #

        # Specifying priorities requires that the pyomo model has established an
        # EXTERNAL, float suffix called 'branching_priorities' on the model
        # object, indexed by the relevant variable
        BranchingPriorityHeader = False
        for suffix in branching_priorities_suffixes:
            for var_data, priority in iteritems(suffix):
                if id(var_data) not in referenced_variable_ids:
                    continue
                if priority is not None:
                    if not BranchingPriorityHeader:
                        output_file.write('BRANCHING_PRIORITIES{\n')
                        BranchingPriorityHeader = True
                    name_to_output = symbol_map.getSymbol(var_data)
                    output_file.write(name_to_output + ': ' + str(priority) +
                                      ';\n')

        if BranchingPriorityHeader:
            output_file.write("}\n\n")

        #
        # Now write the objective and equations section
        #
        output_file.write(equation_section_stream.getvalue())

        #
        # STARTING_POINT
        #
        output_file.write('STARTING_POINT{\nONE_VAR_CONST__: 1;\n')
        tmp = {}
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            starting_point = var_data.value
            if starting_point is not None:
                var_name = symbol_map.getSymbol(var_data)
                tmp[var_name] = "%s: %s;\n" % (var_name, ftoa(starting_point))

        output_file.write("".join(tmp[key] for key in sorted(tmp.keys())))
        output_file.write('}\n\n')

        output_file.close()

        return output_filename, symbol_map
Example #4
0
class CPLEXPersistent(CPLEXDirect, PersistentSolver):
    """The CPLEX LP/MIP solver
    """

    pyomo.util.plugin.alias('_cplex_persistent',
                            doc='Persistent Python interface to the CPLEX LP/MIP solver')

    def __init__(self, **kwds):
        #
        # Call base class constructor
        #
        kwds['type'] = 'cplexpersistent'
        CPLEXDirect.__init__(self, **kwds)

        # maps pyomo var data labels to the corresponding CPLEX variable id.
        self._cplex_variable_ids = {}
        self._cplex_variable_names = None

    #
    # updates all variable bounds in the compiled model - handles
    # fixed variables and related issues.  re-does everything from
    # scratch by default, ignoring whatever was specified
    # previously. if the value associated with the keyword
    # vars_to_update is a non-empty list (assumed to be variable name
    # / index pairs), then only the bounds for those variables are
    # updated.  this function assumes that the variables themselves
    # already exist in the compiled model.
    #
    def compile_variable_bounds(self, pyomo_instance, vars_to_update):

        from pyomo.core.base import Var

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot compile variable bounds - no "
                               "instance is presently compiled")

        # the bound update entries should be name-value pairs
        new_lower_bounds = []
        new_upper_bounds = []

        # operates through side effects on the above lists!
        def update_bounds_lists(var_name):

            var_lb = None
            var_ub = None

            if var_data.fixed and self._output_fixed_variable_bounds:
                var_lb = var_ub = var_data.value
            elif var_data.fixed:
                # if we've been directed to not deal with fixed
                # variables, then skip - they should have been
                # compiled out of any description of the constraints
                return
            else:
                if not var_data.has_lb():
                    var_lb = -CPLEXDirect._cplex_module.infinity
                else:
                    var_lb = value(var_data.lb)

                if not var_data.has_ub():
                    var_ub = CPLEXDirect._cplex_module.infinity
                else:
                    var_ub= value(var_data.ub)

            var_cplex_id = self._cplex_variable_ids[var_name]

            new_lower_bounds.append((var_cplex_id, var_lb))
            new_upper_bounds.append((var_cplex_id, var_ub))

        if len(vars_to_update) == 0:
            for var_data in pyomo_instance.component_data_objects(Var, active=True):
                var_name = self._symbol_map.getSymbol(var_data, self._labeler)
                update_bounds_lists(var_name)
        else:
            for var_name, var_index in vars_to_update:
                var = pyomo_instance.find_component(var_name)
                # TBD - do some error checking!
                var_data = var[var_index]
                var_name = self._symbol_map.getSymbol(var_data, self._labeler)
                update_bounds_lists(var_name)

        self._active_cplex_instance.variables.set_lower_bounds(new_lower_bounds)
        self._active_cplex_instance.variables.set_upper_bounds(new_upper_bounds)

    #
    # method to compile objective of the input pyomo instance.
    # TBD:
    #   it may be smarter just to track the associated pyomo instance,
    #   and re-compile it automatically from a cached local attribute.
    #   this would ensure consistency, among other things!
    #
    def compile_objective(self, pyomo_instance):

        from pyomo.core.base import Objective
        from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot compile objective - no "
                               "instance is presently compiled")

        cplex_instance = self._active_cplex_instance

        self._has_quadratic_objective = False

        cntr = 0
        for block in pyomo_instance.block_data_objects(active=True):
            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_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 obj_data in block.component_data_objects(Objective,
                                                         active=True,
                                                         descend_into=False):

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

                if obj_data.is_minimizing():
                    cplex_instance.objective.set_sense(
                        cplex_instance.objective.sense.minimize)
                else:
                    cplex_instance.objective.set_sense(
                        cplex_instance.objective.sense.maximize)

                cplex_instance.objective.set_name(
                    self._symbol_map.getSymbol(obj_data,
                                               self._labeler))

                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) and \
                    ((obj_repn.linear == None) or \
                     (len(obj_repn.linear) == 0))) or \
                    canonical_is_constant(obj_repn):
                    print("Warning: Constant objective detected, replacing "
                          "with a placeholder to prevent solver failure.")
                    offset = obj_repn.constant
                    if offset is None:
                        offset = 0.0
                    objective_expression = [("ONE_VAR_CONSTANT",offset)]
                    cplex_instance.objective.set_linear(objective_expression)

                else:

                    if isinstance(obj_repn, LinearCanonicalRepn):
                        objective_expression, offset = \
                            self._encode_constraint_body_linear_specialized(
                                    obj_repn,
                                    self._labeler,
                                    use_variable_names=False,
                                    cplex_variable_name_index_map=self._cplex_variable_ids,
                                    as_pairs=True)
                        if offset != 0.0:
                            objective_expression.append((self._cplex_variable_ids["ONE_VAR_CONSTANT"],offset))
                        cplex_instance.objective.set_linear(objective_expression)

                    else:
                        #Linear terms
                        if 1 in obj_repn:
                            objective_expression, offset = \
                                self._encode_constraint_body_linear(
                                    obj_repn,
                                    self._labeler,
                                    as_pairs=True)
                            if offset != 0.0:
                                objective_expression.append(("ONE_VAR_CONSTANT",offset))
                            cplex_instance.objective.set_linear(objective_expression)

                        #Quadratic terms
                        if 2 in obj_repn:
                            self._has_quadratic_objective = True
                            objective_expression = \
                                self._encode_constraint_body_quadratic(obj_repn,
                                                                       self._labeler,
                                                                       as_triples=True,
                                                                       is_obj=2.0)
                            cplex_instance.objective.\
                                set_quadratic_coefficients(objective_expression)

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

    #
    # method to populate the CPLEX problem instance (interface) from
    # the supplied Pyomo problem instance.
    #
    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)

    #
    # simple method to query whether a Pyomo instance has already been
    # compiled.
    #
    def instance_compiled(self):

        return self._active_cplex_instance is not None

    #
    # Override base class method to check for compiled instance
    #
    def _warm_start(self, instance):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot warm start - no instance is "
                               "presently compiled")

        # clear any existing warm starts.
        self._active_cplex_instance.MIP_starts.delete()

        # the iteration order is identical to that used in generating
        # the cplex instance, so all should be well.
        variable_ids = []
        variable_values = []

        # IMPT: the var_data returned is a weak ref!
        for label, var_data in iteritems(self._variable_symbol_map.bySymbol):
            cplex_id = self._cplex_variable_ids[label]
            if var_data().fixed and not self._output_fixed_variable_bounds:
                continue
            elif var_data().value is not None:
                variable_ids.append(cplex_id)
                variable_values.append(var_data().value)

        if len(variable_ids):
            self._active_cplex_instance.MIP_starts.add(
                [variable_ids, variable_values],
                self._active_cplex_instance.MIP_starts.effort_level.auto)

    #
    # Override base class method to check for compiled instance
    #

    def _populate_cplex_instance(self, model):
        assert model == self._instance

    def _presolve(self, *args, **kwds):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin"
                               " cannot presolve - no instance is "
                               "presently compiled")

        # These must be passed in to the compile_instance method,
        # but assert that any values here match those already supplied
        if 'symbolic_solver_labels' in kwds:
            assert self._symbolic_solver_labels == \
                kwds['symbolic_solver_labels']
        if 'output_fixed_variable_bounds' in kwds:
            assert self._output_fixed_variable_bounds == \
                kwds['output_fixed_variable_bounds']
        if 'skip_trivial_constraints' in kwds:
            assert self._skip_trivial_constraints == \
                kwds["skip_trivial_constraints"]

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


        ################################################
        # populate the problem type in the cplex model #
        ################################################

        # This gets rid of the annoying "Freeing MIP data." message.
        def _filter_freeing_mip_data(val):
            if val.strip() == 'Freeing MIP data.':
                return ""
            return val
        self._active_cplex_instance.set_warning_stream(sys.stderr,
                                                       fn=_filter_freeing_mip_data)

        if (self._has_quadratic_objective is True) or \
           (self._has_quadratic_constraints is True):
            if (self._num_integer_variables > 0) or \
               (self._num_binary_variables > 0) or \
               (self._used_sos_constraints):
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQP)
            else:
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QP)
        elif (self._num_integer_variables > 0) or \
             (self._num_binary_variables > 0) or \
             (self._used_sos_constraints):
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.MILP)
        else:
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.LP)

        # restore the warning stream without our filter function
        self._active_cplex_instance.set_warning_stream(sys.stderr)

        CPLEXDirect._presolve(self, *args, **kwds)

        # like other solver plugins, persistent solver plugins can
        # take an instance as an input argument. the only context in
        # which this instance is used, however, is for warm-starting.
        if len(args) > 2:
            raise ValueError("The CPLEXPersistent plugin method "
                             "'_presolve' can be supplied at most "
                             "one problem instance - %s were "
                             "supplied" % len(args))

            # Re-add the symbol map id if it was cleared
            # after a previous solution load
            if id(self._symbol_map) not in args[0].solutions.symbol_map:
                args[0].solutions.add_symbol_map(self._symbol_map)
                self._smap_id = id(self._symbol_map)

    #
    # invoke the solver on the currently compiled instance!!!
    #
    def _apply_solver(self):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin cannot "
                               "apply solver - no instance is presently compiled")

        # NOTE:
        # CPLEX maintains the pool of feasible solutions from the
        # prior solve as the set of mip starts for the next solve.
        # and evaluating multiple mip starts (and there can be many)
        # is expensive. so if the warm_start method is not invoked,
        # there will potentially be a lot of time wasted.

        return CPLEXDirect._apply_solver(self)

    def _postsolve(self):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot postsolve - no instance is "
                               "presently compiled")

        active_cplex_instance = self._active_cplex_instance
        variable_symbol_map = self._variable_symbol_map
        instance = self._instance

        ret = CPLEXDirect._postsolve(self)

        #
        # These get reset to None by the base class method
        #
        self._active_cplex_instance = active_cplex_instance
        self._variable_symbol_map = variable_symbol_map
        self._instance = instance

        return ret
Example #5
0
class NLWriter(PersistentBase):
    def __init__(self):
        super(NLWriter, self).__init__()
        self._config = WriterConfig()
        self._writer = None
        self._symbol_map = SymbolMap()
        self._var_labeler = None
        self._con_labeler = None
        self._param_labeler = None
        self._pyomo_var_to_solver_var_map = dict()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_var_to_pyomo_var_map = dict()
        self._solver_con_to_pyomo_con_map = dict()
        self._pyomo_param_to_solver_param_map = dict()
        self._walker = PyomoToCModelWalker(
            self._pyomo_var_to_solver_var_map,
            self._pyomo_param_to_solver_param_map)

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

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

    @property
    def symbol_map(self):
        return self._symbol_map

    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)

    def _add_variables(self, variables: List[_GeneralVarData]):
        cvars = cmodel.create_vars(len(variables))
        for ndx, v in enumerate(variables):
            cv = cvars[ndx]
            cv.name = self._symbol_map.getSymbol(v, self._var_labeler)
            if not v.is_continuous():
                raise NotImplementedError(
                    'NLWriter currently only supports continuous variables')
            lb = value(v.lb)
            ub = value(v.ub)
            if lb is not None:
                cv.lb = lb
            if ub is not None:
                cv.ub = ub
            if v.value is not None:
                cv.value = v.value
            if v.is_fixed():
                cv.fixed = True
            self._pyomo_var_to_solver_var_map[id(v)] = cv
            self._solver_var_to_pyomo_var_map[cv] = v

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

    def _add_constraints(self, cons: List[_GeneralConstraintData]):
        for c in cons:
            cname = self._symbol_map.getSymbol(c, self._con_labeler)
            repn = generate_standard_repn(c.body,
                                          compute_values=False,
                                          quadratic=False)
            const = self._walker.dfs_postorder_stack(repn.constant)
            lin_vars = [
                self._pyomo_var_to_solver_var_map[id(i)]
                for i in repn.linear_vars
            ]
            lin_coef = [
                self._walker.dfs_postorder_stack(i) for i in repn.linear_coefs
            ]
            if repn.nonlinear_expr is None:
                nonlin = self._walker.dfs_postorder_stack(0)
            else:
                nonlin = self._walker.dfs_postorder_stack(repn.nonlinear_expr)
            cc = cmodel.NLConstraint(const, lin_coef, lin_vars, nonlin)
            lb = c.lower
            ub = c.upper
            if lb is not None:
                cc.lb = self._walker.dfs_postorder_stack(lb)
            if ub is not None:
                cc.ub = self._walker.dfs_postorder_stack(ub)
            self._writer.add_constraint(cc)
            self._pyomo_con_to_solver_con_map[c] = cc
            self._solver_con_to_pyomo_con_map[cc] = c

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

    def _remove_constraints(self, cons: List[_GeneralConstraintData]):
        for c in cons:
            cc = self._pyomo_con_to_solver_con_map.pop(c)
            self._writer.remove_constraint(cc)
            self._symbol_map.removeSymbol(c)
            self._con_labeler.remove_obj(c)
            del self._solver_con_to_pyomo_con_map[cc]

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

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

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

    def _update_variables(self, variables: List[_GeneralVarData]):
        for v in variables:
            cv = self._pyomo_var_to_solver_var_map[id(v)]
            if not v.is_continuous():
                raise NotImplementedError(
                    'NLWriter currently only supports continuous variables')
            lb = value(v.lb)
            ub = value(v.ub)
            if lb is None:
                cv.lb = -cmodel.inf
            else:
                cv.lb = lb
            if ub is None:
                cv.ub = cmodel.inf
            else:
                cv.ub = ub
            if v.value is not None:
                cv.value = v.value
            if v.is_fixed():
                cv.fixed = True
            else:
                cv.fixed = False

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

    def _set_objective(self, obj: _GeneralObjectiveData):
        if obj is None:
            const = cmodel.Constant(0)
            lin_vars = list()
            lin_coef = list()
            nonlin = cmodel.Constant(0)
            sense = 0
        else:
            repn = generate_standard_repn(obj.expr,
                                          compute_values=False,
                                          quadratic=False)
            const = self._walker.dfs_postorder_stack(repn.constant)
            lin_vars = [
                self._pyomo_var_to_solver_var_map[id(i)]
                for i in repn.linear_vars
            ]
            lin_coef = [
                self._walker.dfs_postorder_stack(i) for i in repn.linear_coefs
            ]
            if repn.nonlinear_expr is None:
                nonlin = cmodel.Constant(0)
            else:
                nonlin = self._walker.dfs_postorder_stack(repn.nonlinear_expr)
            if obj.sense is minimize:
                sense = 0
            else:
                sense = 1
        cobj = cmodel.NLObjective(const, lin_coef, lin_vars, nonlin)
        cobj.sense = sense
        self._writer.objective = cobj

    def write(self,
              model: _BlockData,
              filename: str,
              timer: HierarchicalTimer = None):
        if timer is None:
            timer = HierarchicalTimer()
        if model is not self._model:
            timer.start('set_instance')
            self.set_instance(model)
            timer.stop('set_instance')
        else:
            timer.start('update')
            self.update(timer=timer)
            for cv, v in self._solver_var_to_pyomo_var_map.items():
                if v.value is not None:
                    cv.value = v.value
            timer.stop('update')
        timer.start('write file')
        self._writer.write(filename)
        timer.stop('write file')

    def get_ordered_vars(self):
        return [
            self._solver_var_to_pyomo_var_map[i]
            for i in self._writer.get_solve_vars()
        ]

    def get_ordered_cons(self):
        return [
            self._solver_con_to_pyomo_con_map[i]
            for i in self._writer.get_solve_cons()
        ]

    def get_active_objective(self):
        return self._objective
Example #6
0
class LPWriter(PersistentBase):
    def __init__(self):
        super(LPWriter, self).__init__()
        self._config = WriterConfig()
        self._writer = None
        self._symbol_map = SymbolMap()
        self._var_labeler = None
        self._con_labeler = None
        self._param_labeler = None
        self._obj_labeler = None
        self._pyomo_var_to_solver_var_map = dict()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_var_to_pyomo_var_map = dict()
        self._solver_con_to_pyomo_con_map = dict()
        self._pyomo_param_to_solver_param_map = dict()
        self._walker = PyomoToCModelWalker(
            self._pyomo_var_to_solver_var_map,
            self._pyomo_param_to_solver_param_map)

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

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

    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()
            self._obj_labeler = TextLabeler()
        else:
            self._var_labeler = NumericLabeler('x')
            self._con_labeler = NumericLabeler('c')
            self._param_labeler = NumericLabeler('p')
            self._obj_labeler = NumericLabeler('obj')

        self._writer = cmodel.LPWriter()

        self.add_block(model)
        if self._objective is None:
            self.set_objective(None)

    def _add_variables(self, variables: List[_GeneralVarData]):
        cvars = cmodel.create_vars(len(variables))
        for ndx, v in enumerate(variables):
            cv = cvars[ndx]
            cv.name = self._symbol_map.getSymbol(v, self._var_labeler)
            if v.is_binary():
                cv.domain = 'binary'
            elif v.is_integer():
                cv.domain = 'integer'
            else:
                assert v.is_continuous(
                ), 'LP writer only supports continuous, binary, and integer variables'
                cv.domain = 'continuous'
            _, lb, ub, v_is_fixed, v_domain, v_value = self._vars[id(v)]
            if lb is not None:
                cv.lb = lb
            if ub is not None:
                cv.ub = ub
            if v_value is not None:
                cv.value = v_value
            if v_is_fixed:
                cv.fixed = True
            self._pyomo_var_to_solver_var_map[id(v)] = cv
            self._solver_var_to_pyomo_var_map[cv] = v

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

    def _add_constraints(self, cons: List[_GeneralConstraintData]):
        cmodel.process_lp_constraints(cons, self)

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

    def _remove_constraints(self, cons: List[_GeneralConstraintData]):
        for c in cons:
            cc = self._pyomo_con_to_solver_con_map.pop(c)
            self._writer.remove_constraint(cc)
            self._symbol_map.removeSymbol(c)
            self._con_labeler.remove_obj(c)
            del self._solver_con_to_pyomo_con_map[cc]

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

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

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

    def update_variables(self, variables: List[_GeneralVarData]):
        for v in variables:
            cv = self._pyomo_var_to_solver_var_map[id(v)]
            if v.is_binary():
                cv.domain = 'binary'
            elif v.is_integer():
                cv.domain = 'integer'
            else:
                assert v.is_continuous(
                ), 'LP writer only supports continuous, binary, and integer variables'
                cv.domain = 'continuous'
            lb = value(v.lb)
            ub = value(v.ub)
            if lb is None:
                cv.lb = -cmodel.inf
            else:
                cv.lb = lb
            if ub is None:
                cv.ub = cmodel.inf
            else:
                cv.ub = ub
            if v.value is not None:
                cv.value = v.value
            if v.is_fixed():
                cv.fixed = True
            else:
                cv.fixed = False

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

    def _set_objective(self, obj: _GeneralObjectiveData):
        if obj is None:
            const = cmodel.Constant(0)
            lin_coef = list()
            lin_vars = list()
            quad_coef = list()
            quad_vars_1 = list()
            quad_vars_2 = list()
            sense = 0
        else:
            repn = generate_standard_repn(obj.expr,
                                          compute_values=False,
                                          quadratic=True)
            const = self._walker.dfs_postorder_stack(repn.constant)
            lin_coef = [
                self._walker.dfs_postorder_stack(i) for i in repn.linear_coefs
            ]
            lin_vars = [
                self._pyomo_var_to_solver_var_map[id(i)]
                for i in repn.linear_vars
            ]
            quad_coef = [
                self._walker.dfs_postorder_stack(i)
                for i in repn.quadratic_coefs
            ]
            quad_vars_1 = [
                self._pyomo_var_to_solver_var_map[id(i[0])]
                for i in repn.quadratic_vars
            ]
            quad_vars_2 = [
                self._pyomo_var_to_solver_var_map[id(i[1])]
                for i in repn.quadratic_vars
            ]
            if obj.sense is minimize:
                sense = 0
            else:
                sense = 1
        cobj = cmodel.LPObjective(const, lin_coef, lin_vars, quad_coef,
                                  quad_vars_1, quad_vars_2)
        cobj.sense = sense
        if obj is None:
            cname = 'objective'
        else:
            cname = self._symbol_map.getSymbol(obj, self._obj_labeler)
        cobj.name = cname
        self._writer.objective = cobj

    def write(self,
              model: _BlockData,
              filename: str,
              timer: HierarchicalTimer = None):
        if timer is None:
            timer = HierarchicalTimer()
        if model is not self._model:
            timer.start('set_instance')
            self.set_instance(model)
            timer.stop('set_instance')
        else:
            timer.start('update')
            self.update(timer=timer)
            timer.stop('update')
        timer.start('write file')
        self._writer.write(filename)
        timer.stop('write file')

    def get_vars(self):
        return [
            self._solver_var_to_pyomo_var_map[i]
            for i in self._writer.get_solve_vars()
        ]

    def get_ordered_cons(self):
        return [
            self._solver_con_to_pyomo_con_map[i]
            for i in self._writer.get_solve_cons()
        ]

    def get_active_objective(self):
        return self._objective

    @property
    def symbol_map(self):
        return self._symbol_map
Example #7
0
class NLWriter(PersistentBase):
    def __init__(self):
        super(NLWriter, self).__init__()
        self._config = WriterConfig()
        self._writer = None
        self._symbol_map = SymbolMap()
        self._var_labeler = None
        self._con_labeler = None
        self._param_labeler = None
        self._pyomo_var_to_solver_var_map = dict()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_var_to_pyomo_var_map = dict()
        self._solver_con_to_pyomo_con_map = dict()
        self._pyomo_param_to_solver_param_map = dict()
        self._expr_types = None

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

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

    @property
    def symbol_map(self):
        return self._symbol_map

    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
        self._expr_types = cmodel.PyomoExprTypes()

        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)
        self._set_pyomo_amplfunc_env()

    def _add_variables(self, variables: List[_GeneralVarData]):
        cmodel.process_pyomo_vars(self._expr_types, variables,
                                  self._pyomo_var_to_solver_var_map,
                                  self._pyomo_param_to_solver_param_map,
                                  self._vars,
                                  self._solver_var_to_pyomo_var_map, False,
                                  None, None, False)

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

    def _add_constraints(self, cons: List[_GeneralConstraintData]):
        cmodel.process_nl_constraints(self._writer, self._expr_types, cons,
                                      self._pyomo_var_to_solver_var_map,
                                      self._pyomo_param_to_solver_param_map,
                                      self._active_constraints,
                                      self._pyomo_con_to_solver_con_map,
                                      self._solver_con_to_pyomo_con_map)

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

    def _remove_constraints(self, cons: List[_GeneralConstraintData]):
        for c in cons:
            cc = self._pyomo_con_to_solver_con_map.pop(c)
            self._writer.remove_constraint(cc)
            self._con_labeler.remove_obj(c)
            del self._solver_con_to_pyomo_con_map[cc]

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

    def _remove_variables(self, variables: List[_GeneralVarData]):
        for v in variables:
            cvar = self._pyomo_var_to_solver_var_map.pop(id(v))
            del self._solver_var_to_pyomo_var_map[cvar]
            # self._symbol_map.removeSymbol(v)
            self._var_labeler.remove_obj(v)

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

    def _update_variables(self, variables: List[_GeneralVarData]):
        cmodel.process_pyomo_vars(self._expr_types, variables,
                                  self._pyomo_var_to_solver_var_map,
                                  self._pyomo_param_to_solver_param_map,
                                  self._vars,
                                  self._solver_var_to_pyomo_var_map, False,
                                  None, None, True)

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

    def _set_objective(self, obj: _GeneralObjectiveData):
        if obj is None:
            const = cmodel.Constant(0)
            lin_vars = list()
            lin_coef = list()
            nonlin = cmodel.Constant(0)
            sense = 0
        else:
            pyomo_expr_types = cmodel.PyomoExprTypes()
            repn = generate_standard_repn(obj.expr,
                                          compute_values=False,
                                          quadratic=False)
            const = cmodel.appsi_expr_from_pyomo_expr(
                repn.constant, self._pyomo_var_to_solver_var_map,
                self._pyomo_param_to_solver_param_map, pyomo_expr_types)
            lin_vars = [
                self._pyomo_var_to_solver_var_map[id(i)]
                for i in repn.linear_vars
            ]
            lin_coef = [
                cmodel.appsi_expr_from_pyomo_expr(
                    i, self._pyomo_var_to_solver_var_map,
                    self._pyomo_param_to_solver_param_map, pyomo_expr_types)
                for i in repn.linear_coefs
            ]
            if repn.nonlinear_expr is None:
                nonlin = cmodel.appsi_expr_from_pyomo_expr(
                    0, self._pyomo_var_to_solver_var_map,
                    self._pyomo_param_to_solver_param_map, pyomo_expr_types)
            else:
                nonlin = cmodel.appsi_expr_from_pyomo_expr(
                    repn.nonlinear_expr, self._pyomo_var_to_solver_var_map,
                    self._pyomo_param_to_solver_param_map, pyomo_expr_types)
            if obj.sense is minimize:
                sense = 0
            else:
                sense = 1
        cobj = cmodel.NLObjective(const, lin_coef, lin_vars, nonlin)
        cobj.sense = sense
        self._writer.objective = cobj

    def write(self,
              model: _BlockData,
              filename: str,
              timer: HierarchicalTimer = None):
        if timer is None:
            timer = HierarchicalTimer()
        if model is not self._model:
            timer.start('set_instance')
            self.set_instance(model)
            timer.stop('set_instance')
        else:
            timer.start('update')
            self.update(timer=timer)
            for cv, v in self._solver_var_to_pyomo_var_map.items():
                if v.value is not None:
                    cv.value = v.value
            timer.stop('update')
        timer.start('write file')
        self._writer.write(filename)
        timer.stop('write file')

    def update(self, timer: HierarchicalTimer = None):
        super(NLWriter, self).update(timer=timer)
        self._set_pyomo_amplfunc_env()

    def get_ordered_vars(self):
        return [
            self._solver_var_to_pyomo_var_map[i]
            for i in self._writer.get_solve_vars()
        ]

    def get_ordered_cons(self):
        return [
            self._solver_con_to_pyomo_con_map[i]
            for i in self._writer.get_solve_cons()
        ]

    def get_active_objective(self):
        return self._objective

    def _set_pyomo_amplfunc_env(self):
        if self._external_functions:
            external_Libs = OrderedSet()
            for con, ext_funcs in self._external_functions.items():
                external_Libs.update([i._fcn._library for i in ext_funcs])
            set_pyomo_amplfunc_env(external_Libs)
        elif "PYOMO_AMPLFUNC" in os.environ:
            del os.environ["PYOMO_AMPLFUNC"]
Example #8
0
    def solve(self,
              model: _BlockData,
              tee: bool = False,
              load_solutions: bool = True,
              logfile: Optional[str] = None,
              solnfile: Optional[str] = None,
              timelimit: Optional[float] = None,
              report_timing: bool = False,
              solver_io: Optional[str] = None,
              suffixes: Optional[Sequence] = None,
              options: Optional[Dict] = None,
              keepfiles: bool = False,
              symbolic_solver_labels: bool = False):
        original_config = self.config
        self.config = self.config()
        self.config.stream_solver = tee
        self.config.load_solution = load_solutions
        self.config.symbolic_solver_labels = symbolic_solver_labels
        self.config.time_limit = timelimit
        self.config.report_timing = report_timing
        if solver_io is not None:
            raise NotImplementedError('Still working on this')
        if suffixes is not None:
            raise NotImplementedError('Still working on this')
        if logfile is not None:
            raise NotImplementedError('Still working on this')
        if 'keepfiles' in self.config:
            self.config.keepfiles = keepfiles
        if solnfile is not None:
            if 'filename' in self.config:
                filename = os.path.splitext(solnfile)[0]
                self.config.filename = filename
        original_options = self.options
        if options is not None:
            self.options = options

        results: Results = super(LegacySolverInterface, self).solve(model)

        legacy_results = LegacySolverResults()
        legacy_soln = LegacySolution()
        legacy_results.solver.status = legacy_solver_status_map[results.termination_condition]
        legacy_results.solver.termination_condition = legacy_termination_condition_map[results.termination_condition]
        legacy_soln.status = legacy_solution_status_map[results.termination_condition]
        legacy_results.solver.termination_message = str(results.termination_condition)

        obj = get_objective(model)
        legacy_results.problem.sense = obj.sense

        if obj.sense == minimize:
            legacy_results.problem.lower_bound = results.best_objective_bound
            legacy_results.problem.upper_bound = results.best_feasible_objective
        else:
            legacy_results.problem.upper_bound = results.best_objective_bound
            legacy_results.problem.lower_bound = results.best_feasible_objective
        if results.best_feasible_objective is not None and results.best_objective_bound is not None:
            legacy_soln.gap = abs(results.best_feasible_objective - results.best_objective_bound)
        else:
            legacy_soln.gap = None

        symbol_map = SymbolMap()
        symbol_map.byObject = dict(self.symbol_map.byObject)
        symbol_map.bySymbol = {symb: weakref.ref(obj()) for symb, obj in self.symbol_map.bySymbol.items()}
        symbol_map.aliases = {symb: weakref.ref(obj()) for symb, obj in self.symbol_map.aliases.items()}
        symbol_map.default_labeler = self.symbol_map.default_labeler
        model.solutions.add_symbol_map(symbol_map)
        legacy_results._smap_id = id(symbol_map)

        delete_legacy_soln = True
        if load_solutions:
            if hasattr(model, 'dual') and model.dual.import_enabled():
                for c, val in results.solution_loader.get_duals().items():
                    model.dual[c] = val
            if hasattr(model, 'slack') and model.slack.import_enabled():
                for c, val in results.solution_loader.get_slacks().items():
                    model.slack[c] = val
            if hasattr(model, 'rc') and model.rc.import_enabled():
                for v, val in results.solution_loader.get_reduced_costs().items():
                    model.rc[v] = val
        elif results.best_feasible_objective is not None:
            delete_legacy_soln = False
            for v, val in results.solution_loader.get_primals().items():
                legacy_soln.variable[symbol_map.getSymbol(v)] = {'Value': val}
            if hasattr(model, 'dual') and model.dual.import_enabled():
                for c, val in results.solution_loader.get_duals().items():
                    legacy_soln.constraint[symbol_map.getSymbol(c)] = {'Dual': val}
            if hasattr(model, 'slack') and model.slack.import_enabled():
                for c, val in results.solution_loader.get_slacks().items():
                    symbol = symbol_map.getSymbol(c)
                    if symbol in legacy_soln.constraint:
                        legacy_soln.constraint[symbol]['Slack'] = val
            if hasattr(model, 'rc') and model.rc.import_enabled():
                for v, val in results.solution_loader.get_reduced_costs().items():
                    legacy_soln.variable['Rc'] = val

        legacy_results.solution.insert(legacy_soln)
        if delete_legacy_soln:
            legacy_results.solution.delete(0)

        self.config = original_config
        self.options = original_options

        return legacy_results
Example #9
0
class CPLEXPersistent(CPLEXDirect, PersistentSolver):
    """The CPLEX LP/MIP solver
    """

    pyomo.util.plugin.alias('_cplex_persistent',
                            doc='Persistent Python interface to the CPLEX LP/MIP solver')

    def __init__(self, **kwds):
        #
        # Call base class constructor
        #
        kwds['type'] = 'cplexpersistent'
        CPLEXDirect.__init__(self, **kwds)

        # maps pyomo var data labels to the corresponding CPLEX variable id.
        self._cplex_variable_ids = {}
        self._cplex_variable_names = None

    #
    # updates all variable bounds in the compiled model - handles
    # fixed variables and related issues.  re-does everything from
    # scratch by default, ignoring whatever was specified
    # previously. if the value associated with the keyword
    # vars_to_update is a non-empty list (assumed to be variable name
    # / index pairs), then only the bounds for those variables are
    # updated.  this function assumes that the variables themselves
    # already exist in the compiled model.
    #
    def compile_variable_bounds(self, pyomo_instance, vars_to_update):

        from pyomo.core.base import Var

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot compile variable bounds - no "
                               "instance is presently compiled")

        # the bound update entries should be name-value pairs
        new_lower_bounds = []
        new_upper_bounds = []

        # operates through side effects on the above lists!
        def update_bounds_lists(var_name):

            var_lb = None
            var_ub = None

            if var_data.fixed and self._output_fixed_variable_bounds:
                var_lb = var_ub = var_data.value
            elif var_data.fixed:
                # if we've been directed to not deal with fixed
                # variables, then skip - they should have been
                # compiled out of any description of the constraints
                return
            else:
                if var_data.lb is None:
                    var_lb = -cplex.infinity
                else:
                    var_lb = value(var_data.lb)

                if var_data.ub is None:
                    var_ub = cplex.infinity
                else:
                    var_ub= value(var_data.ub)

            var_cplex_id = self._cplex_variable_ids[var_name]

            new_lower_bounds.append((var_cplex_id, var_lb))
            new_upper_bounds.append((var_cplex_id, var_ub))

        if len(vars_to_update) == 0:
            for var_data in pyomo_instance.component_data_objects(Var, active=True):
                var_name = self._symbol_map.getSymbol(var_data, self._labeler)
                update_bounds_lists(var_name)
        else:
            for var_name, var_index in vars_to_update:
                var = pyomo_instance.find_component(var_name)
                # TBD - do some error checking!
                var_data = var[var_index]
                var_name = self._symbol_map.getSymbol(var_data, self._labeler)
                update_bounds_lists(var_name)

        self._active_cplex_instance.variables.set_lower_bounds(new_lower_bounds)
        self._active_cplex_instance.variables.set_upper_bounds(new_upper_bounds)

    #
    # method to compile objective of the input pyomo instance.
    # TBD:
    #   it may be smarter just to track the associated pyomo instance,
    #   and re-compile it automatically from a cached local attribute.
    #   this would ensure consistency, among other things!
    #
    def compile_objective(self, pyomo_instance):

        from pyomo.core.base import Objective
        from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot compile objective - no "
                               "instance is presently compiled")

        cplex_instance = self._active_cplex_instance

        cntr = 0
        for block in pyomo_instance.block_data_objects(active=True):
            gen_obj_canonical_repn = \
                getattr(block, "_gen_obj_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 obj_data in block.component_data_objects(Objective,
                                                         active=True,
                                                         descend_into=False):

                cntr += 1
                if 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))

                if obj_data.is_minimizing():
                    cplex_instance.objective.set_sense(
                        cplex_instance.objective.sense.minimize)
                else:
                    cplex_instance.objective.set_sense(
                        cplex_instance.objective.sense.maximize)

                cplex_instance.objective.set_name(
                    self._symbol_map.getSymbol(obj_data,
                                               self._labeler))

                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) and \
                    (obj_repn.linear == None)) or \
                    canonical_is_constant(obj_repn):
                    print("Warning: Constant objective detected, replacing "
                          "with a placeholder to prevent solver failure.")
                    offset = obj_repn.constant
                    if offset is None:
                        offset = 0.0
                    objective_expression = [("ONE_VAR_CONSTANT",offset)]
                    cplex_instance.objective.set_linear(objective_expression)

                else:

                    if isinstance(obj_repn, LinearCanonicalRepn):
                        objective_expression, offset = \
                            self._encode_constraint_body_linear_specialized(
                                    obj_repn,
                                    self._labeler,
                                    use_variable_names=False,
                                    cplex_variable_name_index_map=self._cplex_variable_ids,
                                    as_pairs=True)
                        if offset != 0.0:
                            objective_expression.append((self._cplex_variable_ids["ONE_VAR_CONSTANT"],offset))
                        cplex_instance.objective.set_linear(objective_expression)

                    else:
                        #Linear terms
                        if 1 in obj_repn:
                            objective_expression, offset = \
                                self._encode_constraint_body_linear(
                                    obj_repn,
                                    self._labeler,
                                    as_pairs=True)
                            if offset != 0.0:
                                objective_expression.append(("ONE_VAR_CONSTANT",offset))
                            cplex_instance.objective.set_linear(objective_expression)

                        #Quadratic terms
                        if 2 in obj_repn:
                            self._has_quadratic_objective = True
                            objective_expression = \
                                self._encode_constraint_body_quadratic(obj_repn,
                                                                       self._labeler,
                                                                       as_triples=True,
                                                                       is_obj=2.0)
                            cplex_instance.objective.\
                                set_quadratic_coefficients(objective_expression)

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

    #
    # method to populate the CPLEX problem instance (interface) from
    # the supplied Pyomo problem instance.
    #
    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
        used_sos_constraints = False

        self._active_cplex_instance = cplex.Cplex()

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

        self._symbol_map = SymbolMap()
        self._instance = pyomo_instance
        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.
        num_binary_variables = 0
        num_integer_variables = 0
        num_continuous_variables = 0

        #############################################
        # 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 (var_data.lb is None) or (var_data.lb == -infinity):
                var_lbs.append(-cplex.infinity)
            else:
                var_lbs.append(value(var_data.lb))

            if (var_data.ub is None) or (var_data.ub == infinity):
                var_ubs.append(cplex.infinity)
            else:
                var_ubs.append(value(var_data.ub))

            if var_data.is_integer():
                var_types.append(self._active_cplex_instance.variables.type.integer)
                num_integer_variables += 1
            elif var_data.is_binary():
                var_types.append(self._active_cplex_instance.variables.type.binary)
                num_binary_variables += 1
            elif var_data.is_continuous():
                var_types.append(self._active_cplex_instance.variables.type.continuous)
                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 (con.lower is None) and \
                   (con.upper is None):
                    continue  # not binding at all, don't bother

                con_repn = None
                if 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 (con_repn.linear is None) and \
                       self._skip_trivial_constraints:
                       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.cname(True)))
                    expr, offset = self._encode_constraint_body_linear(con_repn,
                                                                       labeler)

                if quadratic:
                    if expr is None:
                        expr = cplex.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.lower is not None) and (con.upper is not None):
                        raise RuntimeError(
                            "The CPLEXDirect plugin can not translate range "
                            "constraints containing quadratic expressions.")

                    elif con.lower is not None:
                        assert con.upper is None
                        qsenses.append('G')
                        qrhss.append(self._get_bound(con.lower) - offset)

                    else:
                        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.lower is not None) and (con.upper is not None):
                        # 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.lower is not None:
                        senses.append('G')
                        rhss.append(self._get_bound(con.lower) - offset)
                        range_values.append(0.0)

                    else:
                        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])
            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)

        ################################################
        # populate the problem type in the cplex model #
        ################################################

        # This gets rid of the annoying "Freeing MIP data." message.
        def _filter_freeing_mip_data(val):
            if val.strip() == 'Freeing MIP data.':
                return ""
            return val
        self._active_cplex_instance.set_warning_stream(sys.stderr,
                                                       fn=_filter_freeing_mip_data)

        if (self._has_quadratic_objective is True) or \
           (self._has_quadratic_constraints is True):
            if (num_integer_variables > 0) or \
               (num_binary_variables > 0) or \
               (used_sos_constraints):
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.MIQP)
            else:
                if self._has_quadratic_constraints is True:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QCP)
                else:
                    self._active_cplex_instance.set_problem_type(
                        self._active_cplex_instance.problem_type.QP)
        elif (num_integer_variables > 0) or \
             (num_binary_variables > 0) or \
             (used_sos_constraints):
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.MILP)
        else:
            self._active_cplex_instance.set_problem_type(
                self._active_cplex_instance.problem_type.LP)

        # restore the warning stream without our filter function
        self._active_cplex_instance.set_warning_stream(sys.stderr)


    #
    # simple method to query whether a Pyomo instance has already been
    # compiled.
    #
    def instance_compiled(self):

        return self._active_cplex_instance is not None

    #
    # Override base class method to check for compiled instance
    #
    def _warm_start(self, instance):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot warm start - no instance is "
                               "presently compiled")

        # clear any existing warm starts.
        self._active_cplex_instance.MIP_starts.delete()

        # the iteration order is identical to that used in generating
        # the cplex instance, so all should be well.
        variable_ids = []
        variable_values = []

        # IMPT: the var_data returned is a weak ref!
        for label, var_data in iteritems(self._variable_symbol_map.bySymbol):
            cplex_id = self._cplex_variable_ids[label]
            if var_data().fixed and not self._output_fixed_variable_bounds:
                continue
            elif var_data().value is not None:
                variable_ids.append(cplex_id)
                variable_values.append(var_data().value)

        if len(variable_ids):
            self._active_cplex_instance.MIP_starts.add(
                [variable_ids, variable_values],
                self._active_cplex_instance.MIP_starts.effort_level.auto)

    #
    # Override base class method to check for compiled instance
    #

    def _populate_cplex_instance(self, model):
        assert model == self._instance

    def _presolve(self, *args, **kwds):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin"
                               " cannot presolve - no instance is "
                               "presently compiled")

        # These must be passed in to the compile_instance method,
        # but assert that any values here match those already supplied
        if 'symbolic_solver_labels' in kwds:
            assert self._symbolic_solver_labels == \
                kwds['symbolic_solver_labels']
        if 'output_fixed_variable_bounds' in kwds:
            assert self._output_fixed_variable_bounds == \
                kwds['output_fixed_variable_bounds']
        if 'skip_trivial_constraints' in kwds:
            assert self._skip_trivial_constraints == \
                kwds["skip_trivial_constraints"]

        if self._smap_id not in self._instance.solutions.symbol_map:
            self._instance.solutions.add_symbol_map(self._symbol_map)

        CPLEXDirect._presolve(self, *args, **kwds)

        # like other solver plugins, persistent solver plugins can
        # take an instance as an input argument. the only context in
        # which this instance is used, however, is for warm-starting.
        if len(args) > 2:
            raise ValueError("The CPLEXPersistent plugin method "
                             "'_presolve' can be supplied at most "
                             "one problem instance - %s were "
                             "supplied" % len(args))

            # Re-add the symbol map id if it was cleared
            # after a previous solution load
            if id(self._symbol_map) not in args[0].solutions.symbol_map:
                args[0].solutions.add_symbol_map(self._symbol_map)
                self._smap_id = id(self._symbol_map)

    #
    # invoke the solver on the currently compiled instance!!!
    #
    def _apply_solver(self):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin cannot "
                               "apply solver - no instance is presently compiled")

        # NOTE:
        # CPLEX maintains the pool of feasible solutions from the
        # prior solve as the set of mip starts for the next solve.
        # and evaluating multiple mip starts (and there can be many)
        # is expensive. so if the warm_start method is not invoked,
        # there will potentially be a lot of time wasted.

        return CPLEXDirect._apply_solver(self)

    def _postsolve(self):

        if self._active_cplex_instance is None:
            raise RuntimeError("***The CPLEXPersistent solver plugin "
                               "cannot postsolve - no instance is "
                               "presently compiled")

        active_cplex_instance = self._active_cplex_instance
        variable_symbol_map = self._variable_symbol_map
        instance = self._instance

        ret = CPLEXDirect._postsolve(self)

        #
        # These get reset to None by the base class method
        #
        self._active_cplex_instance = active_cplex_instance
        self._variable_symbol_map = variable_symbol_map
        self._instance = instance

        return ret
Example #10
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)
        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)
        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)
        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)
        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):
        cons_to_deactivate = list()
        if self.config.deactivate_satisfied_constraints:
            for c, cc in self._con_map.items():
                if not cc.active:
                    cons_to_deactivate.append(c)
        self.remove_constraints(cons_to_deactivate)
        for c in cons_to_deactivate:
            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
Example #11
0
class LPWriter(PersistentBase):
    def __init__(self):
        super(LPWriter, self).__init__()
        self._config = WriterConfig()
        self._writer = None
        self._symbol_map = SymbolMap()
        self._var_labeler = None
        self._con_labeler = None
        self._param_labeler = None
        self._obj_labeler = None
        self._pyomo_var_to_solver_var_map = dict()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_var_to_pyomo_var_map = dict()
        self._solver_con_to_pyomo_con_map = dict()
        self._pyomo_param_to_solver_param_map = dict()
        self._expr_types = None

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

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

    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
        self._expr_types = cmodel.PyomoExprTypes()

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

        self._writer = cmodel.LPWriter()

        self.add_block(model)
        if self._objective is None:
            self.set_objective(None)

    def _add_variables(self, variables: List[_GeneralVarData]):
        cmodel.process_pyomo_vars(self._expr_types, variables, self._pyomo_var_to_solver_var_map,
                                  self._pyomo_param_to_solver_param_map, self._vars,
                                  self._solver_var_to_pyomo_var_map, True, self._symbol_map,
                                  self._var_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.name = self._symbol_map.getSymbol(p, self._param_labeler)
            cp.value = p.value
            self._pyomo_param_to_solver_param_map[id(p)] = cp

    def _add_constraints(self, cons: List[_GeneralConstraintData]):
        cmodel.process_lp_constraints(cons, self)

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

    def _remove_constraints(self, cons: List[_GeneralConstraintData]):
        for c in cons:
            cc = self._pyomo_con_to_solver_con_map.pop(c)
            self._writer.remove_constraint(cc)
            self._symbol_map.removeSymbol(c)
            self._con_labeler.remove_obj(c)
            del self._solver_con_to_pyomo_con_map[cc]

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

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

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

    def _update_variables(self, variables: List[_GeneralVarData]):
        cmodel.process_pyomo_vars(self._expr_types, variables, self._pyomo_var_to_solver_var_map,
                                  self._pyomo_param_to_solver_param_map, self._vars,
                                  self._solver_var_to_pyomo_var_map, False, None,
                                  None, True)

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

    def _set_objective(self, obj: _GeneralObjectiveData):
        cobj = cmodel.process_lp_objective(self._expr_types, obj, self._pyomo_var_to_solver_var_map,
                                           self._pyomo_param_to_solver_param_map)
        if obj is None:
            sense = 0
            cname = 'objective'
        else:
            cname = self._symbol_map.getSymbol(obj, self._obj_labeler)
            if obj.sense is minimize:
                sense = 0
            else:
                sense = 1
        cobj.sense = sense
        cobj.name = cname
        self._writer.objective = cobj

    def write(self, model: _BlockData, filename: str, timer: HierarchicalTimer = None):
        if timer is None:
            timer = HierarchicalTimer()
        if model is not self._model:
            timer.start('set_instance')
            self.set_instance(model)
            timer.stop('set_instance')
        else:
            timer.start('update')
            self.update(timer=timer)
            timer.stop('update')
        timer.start('write file')
        self._writer.write(filename)
        timer.stop('write file')

    def get_vars(self):
        return [self._solver_var_to_pyomo_var_map[i] for i in self._writer.get_solve_vars()]

    def get_ordered_cons(self):
        return [self._solver_con_to_pyomo_con_map[i] for i in self._writer.get_solve_cons()]

    def get_active_objective(self):
        return self._objective

    @property
    def symbol_map(self):
        return self._symbol_map
Example #12
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)

        # NOTE: io_options is a simple dictionary of keyword-value
        #       pairs specific to this writer.
        symbolic_solver_labels = \
            io_options.pop("symbolic_solver_labels", 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)

        sorter = SortComponents.unsorted
        if file_determinism >= 1:
            sorter = sorter | SortComponents.indices
            if file_determinism >= 2:
                sorter = sorter | SortComponents.alphabetical

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

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

        # Note: Baron does not allow specification of runtime
        #       option outside of this file, so we add support
        #       for them here
        solver_options = io_options.pop("solver_options", {})

        if len(io_options):
            raise ValueError(
                "ProblemWriter_baron_writer 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("Baron problem writer: Using both the "
                             "'symbolic_solver_labels' and 'labeler' "
                             "I/O options is forbidden")

        # Make sure there are no strange ActiveComponents. The expression
        # walker will handle strange things in constraints later.
        model_ctypes = model.collect_ctypes(active=True)
        invalids = set()
        for t in (model_ctypes - valid_active_ctypes_minlp):
            if issubclass(t, ActiveComponent):
                invalids.add(t)
        if len(invalids):
            invalids = [t.__name__ for t in invalids]
            raise RuntimeError(
                "Unallowable active component(s) %s.\nThe BARON writer cannot "
                "export models with this component type." %
                ", ".join(invalids))

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

        output_file=open(output_filename, "w")

        # Process the options. Rely on baron to catch
        # and reset bad option values
        output_file.write("OPTIONS {\n")
        summary_found = False
        if len(solver_options):
            for key, val in iteritems(solver_options):
                if (key.lower() == 'summary'):
                    summary_found = True
                if key.endswith("Name"):
                    output_file.write(key+": \""+str(val)+"\";\n")
                else:
                    output_file.write(key+": "+str(val)+";\n")
        if not summary_found:
            # The 'summary option is defaulted to 0, so that no
            # summary file is generated in the directory where the
            # user calls baron. Check if a user explicitly asked for
            # a summary file.
            output_file.write("Summary: 0;\n")
        output_file.write("}\n\n")

        if symbolic_solver_labels:
            v_labeler = AlphaNumericTextLabeler()
            c_labeler = AlphaNumericTextLabeler()
        elif labeler is None:
            v_labeler = NumericLabeler('x')
            c_labeler = NumericLabeler('c')

        symbol_map = SymbolMap()
        symbol_map.default_labeler = v_labeler
        #sm_bySymbol = symbol_map.bySymbol

        # Cache the list of model blocks so we don't have to call
        # model.block_data_objects() many many times, which is slow
        # for indexed blocks
        all_blocks_list = list(model.block_data_objects(active=True,
                                                        sort=sorter,
                                                        descend_into=True))
        active_components_data_var = {}
        #for block in all_blocks_list:
        #    tmp = active_components_data_var[id(block)] = \
        #          list(obj for obj in block.component_data_objects(Var,
        #                                                           sort=sorter,
        #                                                           descend_into=False))
        #    create_symbols_func(symbol_map, tmp, labeler)

            # GAH: Not sure this is necessary, and also it would break for
            #      non-mutable indexed params so I am commenting out for now.
            #for param_data in active_components_data(block, Param, sort=sorter):
                #instead of checking if param_data._mutable:
                #if not param_data.is_constant():
                #    create_symbol_func(symbol_map, param_data, labeler)

        #symbol_map_variable_ids = set(symbol_map.byObject.keys())
        #object_symbol_dictionary = symbol_map.byObject

        #
        # Go through the objectives and constraints and generate
        # the output so that we can obtain the set of referenced
        # variables.
        #
        equation_section_stream = StringIO()
        referenced_variable_ids, branching_priorities_suffixes = \
            self._write_equations_section(
                model,
                equation_section_stream,
                all_blocks_list,
                active_components_data_var,
                symbol_map,
                c_labeler,
                output_fixed_variable_bounds,
                skip_trivial_constraints,
                sorter)

        #
        # BINARY_VARIABLES, INTEGER_VARIABLES, POSITIVE_VARIABLES, VARIABLES
        #

        BinVars = []
        IntVars = []
        PosVars = []
        Vars = []
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.is_continuous():
                if var_data.has_lb() and \
                   (self._get_bound(var_data.lb) >= 0):
                    TypeList = PosVars
                else:
                    TypeList = Vars
            elif var_data.is_binary():
                TypeList = BinVars
            elif var_data.is_integer():
                TypeList = IntVars
            else:
                assert False
            TypeList.append(name)

        if len(BinVars) > 0:
            BinVars.sort()
            output_file.write('BINARY_VARIABLES ')
            output_file.write(", ".join(BinVars))
            output_file.write(';\n\n')

        if len(IntVars) > 0:
            IntVars.sort()
            output_file.write('INTEGER_VARIABLES ')
            output_file.write(", ".join(IntVars))
            output_file.write(';\n\n')

        PosVars.append('ONE_VAR_CONST__')
        PosVars.sort()
        output_file.write('POSITIVE_VARIABLES ')
        output_file.write(", ".join(PosVars))
        output_file.write(';\n\n')

        if len(Vars) > 0:
            Vars.sort()
            output_file.write('VARIABLES ')
            output_file.write(", ".join(Vars))
            output_file.write(';\n\n')

        #
        # LOWER_BOUNDS
        #

        lbounds = {}
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.fixed:
                if output_fixed_variable_bounds:
                    var_data_lb = var_data.value
                else:
                    var_data_lb = None
            else:
                var_data_lb = None
                if var_data.has_lb():
                    var_data_lb = self._get_bound(var_data.lb)

            if var_data_lb is not None:
                name_to_output = symbol_map.getSymbol(var_data)
                lb_string_template = '%s: %'+self._precision_string+';\n'
                lbounds[name_to_output] = lb_string_template % (name_to_output, var_data_lb)

        if len(lbounds) > 0:
            output_file.write("LOWER_BOUNDS{\n")
            output_file.write("".join( lbounds[key] for key in sorted(lbounds.keys()) ) )
            output_file.write("}\n\n")
        lbounds = None

        #
        # UPPER_BOUNDS
        #

        ubounds = {}
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            if var_data.fixed:
                if output_fixed_variable_bounds:
                    var_data_ub = var_data.value
                else:
                    var_data_ub = None
            else:
                var_data_ub = None
                if var_data.has_ub():
                    var_data_ub = self._get_bound(var_data.ub)

            if var_data_ub is not None:
                name_to_output = symbol_map.getSymbol(var_data)
                ub_string_template = '%s: %'+self._precision_string+';\n'
                ubounds[name_to_output] = ub_string_template % (name_to_output, var_data_ub)

        if len(ubounds) > 0:
            output_file.write("UPPER_BOUNDS{\n")
            output_file.write("".join( ubounds[key] for key in sorted(ubounds.keys()) ) )
            output_file.write("}\n\n")
        ubounds = None

        #
        # BRANCHING_PRIORITIES
        #

        # Specifying priorities requires that the pyomo model has established an
        # EXTERNAL, float suffix called 'branching_priorities' on the model
        # object, indexed by the relevant variable
        BranchingPriorityHeader = False
        for suffix in branching_priorities_suffixes:
            for var_data, priority in iteritems(suffix):
                if id(var_data) not in referenced_variable_ids:
                    continue
                if priority is not None:
                    if not BranchingPriorityHeader:
                        output_file.write('BRANCHING_PRIORITIES{\n')
                        BranchingPriorityHeader = True
                    name_to_output = symbol_map.getSymbol(var_data)
                    output_file.write(name_to_output+': '+str(priority)+';\n')

        if BranchingPriorityHeader:
            output_file.write("}\n\n")

        #
        # Now write the objective and equations section
        #
        output_file.write(equation_section_stream.getvalue())

        #
        # STARTING_POINT
        #
        output_file.write('STARTING_POINT{\nONE_VAR_CONST__: 1;\n')
        tmp = {}
        string_template = '%s: %'+self._precision_string+';\n'
        for vid in referenced_variable_ids:
            name = symbol_map.byObject[vid]
            var_data = symbol_map.bySymbol[name]()

            starting_point = var_data.value
            if starting_point is not None:
                var_name = symbol_map.getSymbol(var_data)
                tmp[var_name] = string_template % (var_name, starting_point)

        output_file.write("".join( tmp[key] for key in sorted(tmp.keys()) ))
        output_file.write('}\n\n')

        output_file.close()

        return output_filename, symbol_map