Ejemplo n.º 1
0
    def _apply_to(self, instance):
        """Apply the transformation.

        Args:
            instance (Block): the block on which to search for x == sum(var)
                constraints. Note that variables may be located anywhere in
                the model.

        Returns:
            None

        """
        for constr in instance.component_data_objects(ctype=Constraint,
                                                      active=True,
                                                      descend_into=True):
            if not constr.body.polynomial_degree() == 1:
                continue  # constraint not linear. Skip.

            repn = generate_canonical_repn(constr.body)
            if (constr.has_ub() and (
                (repn.constant is None and value(constr.upper) == 0) or
                repn.constant == value(constr.upper)
                    )):
                # term1 + term2 + term3 + ... <= 0
                # all var terms need to be non-negative
                if all(
                    # variable has 0 coefficient
                    coef == 0 or
                    # variable is non-negative and has non-negative coefficient
                    (repn.variables[i].has_lb() and
                     value(repn.variables[i].lb) >= 0 and
                     coef >= 0) or
                    # variable is non-positive and has non-positive coefficient
                    (repn.variables[i].has_ub() and
                     value(repn.variables[i].ub) <= 0 and
                     coef <= 0) for i, coef in enumerate(repn.linear)):
                    for i, coef in enumerate(repn.linear):
                        if not coef == 0:
                            repn.variables[i].fix(0)
                    continue
            if (constr.has_lb() and (
                (repn.constant is None and value(constr.lower) == 0) or
                repn.constant == value(constr.lower)
                    )):
                # term1 + term2 + term3 + ... >= 0
                # all var terms need to be non-positive
                if all(
                    # variable has 0 coefficient
                    coef == 0 or
                    # variable is non-negative and has non-positive coefficient
                    (repn.variables[i].has_lb() and
                     value(repn.variables[i].lb) >= 0 and
                     coef <= 0) or
                    # variable is non-positive and has non-negative coefficient
                    (repn.variables[i].has_ub() and
                     value(repn.variables[i].ub) <= 0 and
                     coef >= 0) for i, coef in enumerate(repn.linear)):
                    for i, coef in enumerate(repn.linear):
                        if not coef == 0:
                            repn.variables[i].fix(0)
Ejemplo n.º 2
0
    def _apply_to(self, model):
        """Apply the transformation."""
        m = model

        for constr in m.component_data_objects(ctype=Constraint,
                                               active=True,
                                               descend_into=True):
            if not constr.body.polynomial_degree() == 1:
                continue  # we currently only process linear constraints
            repn = generate_canonical_repn(constr.body)

            # get the index of all nonzero coefficient variables
            nonzero_vars_indx = [
                i for i, _ in enumerate(repn.variables)
                if not repn.linear[i] == 0
            ]
            const = repn.constant if repn.constant is not None else 0

            # reconstitute the constraint, including only variable terms with
            # nonzero coefficients
            constr_body = sum(repn.linear[i] * repn.variables[i]
                              for i in nonzero_vars_indx) + const
            if constr.equality:
                constr.set_value(constr_body == constr.upper)
            elif constr.has_lb() and not constr.has_ub():
                constr.set_value(constr_body >= constr.lower)
            elif constr.has_ub() and not constr.has_lb():
                constr.set_value(constr_body <= constr.upper)
            else:
                # constraint is a bounded inequality of form a <= x <= b.
                # I don't think this is a great idea, but ¯\_(ツ)_/¯
                constr.set_value(constr.lower <= constr_body <= constr.upper)
Ejemplo n.º 3
0
def _build_equality_set(m):
    """Construct an equality set map.

    Maps all variables to the set of variables that are linked to them by
    equality. Mapping takes place using id(). That is, if you have x = y, then
    you would have id(x) -> ComponentSet([x, y]) and id(y) -> ComponentSet([x,
    y]) in the mapping.

    """
    #: dict: map of var UID to the set of all equality-linked var UIDs
    eq_var_map = ComponentMap()
    relevant_vars = ComponentSet()
    for constr in m.component_data_objects(ctype=Constraint,
                                           active=True,
                                           descend_into=True):
        # Check to make sure the constraint is of form v1 - v2 == 0
        if (value(constr.lower) == 0 and value(constr.upper) == 0
                and constr.body.polynomial_degree() == 1):
            repn = generate_canonical_repn(constr.body)
            # only take the variables with nonzero coefficients
            vars_ = [v for i, v in enumerate(repn.variables) if repn.linear[i]]
            if (len(vars_) == 2
                    and sorted(l for l in repn.linear if l) == [-1, 1]):
                # this is an a == b constraint.
                v1 = vars_[0]
                v2 = vars_[1]
                set1 = eq_var_map.get(v1, ComponentSet([v1]))
                set2 = eq_var_map.get(v2, ComponentSet([v2]))
                relevant_vars.update([v1, v2])
                set1.update(set2)  # set1 is now the union
                for v in set1:
                    eq_var_map[v] = set1

    return eq_var_map, relevant_vars
Ejemplo n.º 4
0
def generate_ampl_repn(exp, idMap=None):
    # We need to do this not at the global scope in case someone changed
    # the mode after importing the environment.
    _using_pyomo4_trees = expr_common.mode == expr_common.Mode.pyomo4_trees

    if idMap is None:
        idMap = {}
    if exp is None:
        return AmplRepn()
    degree = exp.polynomial_degree()
    if (degree is None) or (degree > 1):
        repn = _generate_ampl_repn(exp)
        repn.compress()
    elif degree == 0:
        repn = AmplRepn()
        repn._constant = value(exp)
        # compress
        repn._linear_vars = tuple()
        repn._linear_terms_coef = tuple()
        repn._nonlinear_vars = tuple()
    else:  # degree == 1
        repn = AmplRepn()
        if _using_pyomo4_trees:
            canonical_repn = generate_canonical_repn(exp, idMap=idMap)
            # compress
            repn._nonlinear_vars = tuple()
            repn._constant = value(canonical_repn.constant)
            repn._linear_vars = tuple(canonical_repn.variables)
            repn._linear_terms_coef = tuple(
                value(_v) for _v in canonical_repn.linear)
        else:
            # compress
            repn._linear_vars = tuple()
            repn._linear_terms_coef = tuple()
            repn._nonlinear_vars = tuple()
            coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap)
            if None in coef:
                val = coef.pop(None)
                if val:
                    repn._constant = val
            # the six module is inefficient in terms of wrapping
            # iterkeys and itervalues, in the context of Python
            # 2.7. use the native dictionary methods where
            # possible.
            if using_py3 is False:
                repn._linear_terms_coef = tuple(val
                                                for val in coef.itervalues()
                                                if val)
                repn._linear_vars = tuple(
                    (varmap[var_hash] for var_hash, val in coef.iteritems()
                     if val))
            else:
                repn._linear_terms_coef = tuple(val for val in coef.values()
                                                if val)
                repn._linear_vars = tuple((varmap[var_hash]
                                           for var_hash, val in coef.items()
                                           if val))
    return repn
Ejemplo n.º 5
0
def generate_ampl_repn(exp, idMap=None):
    # We need to do this not at the global scope in case someone changed
    # the mode after importing the environment.
    _using_pyomo4_trees = expr_common.mode == expr_common.Mode.pyomo4_trees
    _using_pyomo5_trees = expr_common.mode == expr_common.Mode.pyomo5_trees

    if idMap is None:
        idMap = {}
    if _using_pyomo5_trees:
        from pyomo.repn.standard_repn import generate_standard_repn
        return generate_standard_repn(exp, quadratic=False)

    if exp is None:
        return AmplRepn()
    degree = exp.polynomial_degree()
    if (degree is None) or (degree > 1):
        repn = _generate_ampl_repn(exp)
        repn.compress()
    elif degree == 0:
        repn = AmplRepn()
        repn._constant = value(exp)
        # compress
        repn._linear_vars = tuple()
        repn._linear_terms_coef = tuple()
        repn._nonlinear_vars = tuple()
    else: # degree == 1
        repn = AmplRepn()
        if _using_pyomo4_trees:
            canonical_repn = generate_canonical_repn(exp, idMap=idMap)
            # compress
            repn._nonlinear_vars = tuple()
            repn._constant = value(canonical_repn.constant)
            repn._linear_vars = tuple(canonical_repn.variables)
            repn._linear_terms_coef = tuple(value(_v) for _v in canonical_repn.linear)
        else:
            # compress
            repn._linear_vars = tuple()
            repn._linear_terms_coef = tuple()
            repn._nonlinear_vars = tuple()
            coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap)
            if None in coef:
                val = coef.pop(None)
                if val:
                    repn._constant = val
            # the six module is inefficient in terms of wrapping
            # iterkeys and itervalues, in the context of Python
            # 2.7. use the native dictionary methods where
            # possible.
            if using_py3 is False:
                repn._linear_terms_coef = tuple(val for val in coef.itervalues() if val)
                repn._linear_vars = tuple((varmap[var_hash]
                                           for var_hash,val in coef.iteritems() if val))
            else:
                repn._linear_terms_coef = tuple(val for val in coef.values() if val)
                repn._linear_vars = tuple((varmap[var_hash]
                                           for var_hash,val in coef.items() if val))
    return repn
Ejemplo n.º 6
0
    def _apply_to(self, model):
        """Apply the transformation to the given model."""
        m = model

        for constr in m.component_data_objects(ctype=Constraint,
                                               active=True,
                                               descend_into=True):
            # Check if the constraint is k * x + c1 <= c2 or c2 <= k * x + c1
            if constr.body.polynomial_degree() == 1:
                repn = generate_canonical_repn(constr.body)
                if repn.variables is not None and len(repn.variables) == 1:
                    var = repn.variables[0]
                    const = repn.constant if repn.constant is not None else 0
                    coef = float(repn.linear[0])
                    if coef == 0:
                        # This can happen when a one element of a bilinear term
                        # is fixed to zero. Obviously, do not divide by zero.
                        pass
                    else:
                        if constr.upper is not None:
                            newbound = (value(constr.upper) - const) / coef
                            if coef > 0:
                                var.setub(
                                    min(var.ub, newbound) if var.
                                    ub is not None else newbound)
                            elif coef < 0:
                                var.setlb(
                                    max(var.lb, newbound) if var.
                                    lb is not None else newbound)
                        if constr.lower is not None:
                            newbound = (value(constr.lower) - const) / coef
                            if coef > 0:
                                var.setlb(
                                    max(var.lb, newbound) if var.
                                    lb is not None else newbound)
                            elif coef < 0:
                                var.setub(
                                    min(var.ub, newbound) if var.
                                    ub is not None else newbound)
                    constr.deactivate()
                    # Sometimes deactivating the constraint will remove a
                    # variable from all active constraints, so that it won't be
                    # updated during the optimization. Therefore, we need to
                    # shift the value of var as necessary in order to keep it
                    # within its implied bounds, as the constraint we are
                    # deactivating is not an invalid constraint, but rather we
                    # are moving its implied bound directly onto the variable.
                    if (var.has_lb() and var.value is not None
                            and var.value < var.lb):
                        var.set_value(var.lb)
                    if (var.has_ub() and var.value is not None
                            and var.value > var.ub):
                        var.set_value(var.ub)
Ejemplo n.º 7
0
def _detect_fixed_variables(m):
    """Detect fixed variables due to constraints of form var = const."""
    new_fixed_vars = ComponentSet()
    for constr in m.component_data_objects(ctype=Constraint,
                                           active=True,
                                           descend_into=True):
        if constr.equality and constr.body.polynomial_degree() == 1:
            repn = generate_canonical_repn(constr.body)
            if len(repn.variables) == 1 and repn.linear[0]:
                var = repn.variables[0]
                coef = float(repn.linear[0])
                const = repn.constant if repn.constant is not None else 0
                var_val = (value(constr.lower) - value(const)) / coef
                var.fix(var_val)
                new_fixed_vars.add(var)
    return new_fixed_vars
Ejemplo n.º 8
0
def generate_ampl_repn(exp, idMap=None):
    if idMap is None:
        idMap = {}
    degree = exp.polynomial_degree()
    if (degree is None) or (degree > 1):
        repn = _generate_ampl_repn(exp)
        repn.compress()
    elif degree == 0:
        repn = AmplRepn()
        repn._constant = value(exp)
        # compress
        repn._linear_vars = tuple()
        repn._linear_terms_coef = tuple()
        repn._nonlinear_vars = tuple()
    else: # degree == 1
        repn = AmplRepn()
        if _using_pyomo4_trees:
            canonical_repn = generate_canonical_repn(exp, idMap=idMap)
            # compress
            repn._nonlinear_vars = tuple()
            repn._constant = value(canonical_repn.constant)
            repn._linear_vars = tuple(canonical_repn.variables)
            repn._linear_terms_coef = tuple(value(_v) for _v in canonical_repn.linear)
        else:
            # compress
            repn._linear_vars = tuple()
            repn._linear_terms_coef = tuple()
            repn._nonlinear_vars = tuple()
            coef, varmap = collect_linear_canonical_repn(exp, idMap=idMap)
            if None in coef:
                val = coef.pop(None)
                if val:
                    repn._constant = val
            # the six module is inefficient in terms of wrapping
            # iterkeys and itervalues, in the context of Python
            # 2.7. use the native dictionary methods where
            # possible.
            if using_py3 is False:
                repn._linear_terms_coef = tuple(val for val in coef.itervalues() if val)
                repn._linear_vars = tuple((varmap[var_hash]
                                           for var_hash,val in coef.iteritems() if val))
            else:
                repn._linear_terms_coef = tuple(val for val in coef.values() if val)
                repn._linear_vars = tuple((varmap[var_hash]
                                           for var_hash,val in coef.items() if val))
    return repn
Ejemplo n.º 9
0
def compile_block_linear_constraints(parent_block,
                                     constraint_name,
                                     skip_trivial_constraints=False,
                                     single_precision_storage=False,
                                     verbose=False,
                                     descend_into=True):

    if verbose:
        print("")
        print("Compiling linear constraints on block with name: %s" %
              (parent_block.name))

    if not parent_block.is_constructed():
        raise RuntimeError(
            "Attempting to compile block '%s' with unconstructed "
            "component(s)" % (parent_block.name))

    #
    # Linear MatrixConstraint in CSR format
    #
    SparseMat_pRows = []
    SparseMat_jCols = []
    SparseMat_Vals = []
    Ranges = []
    RangeTypes = []

    def _get_bound(exp):
        if exp is None:
            return None
        if is_fixed(exp):
            return value(exp)
        raise ValueError("non-fixed bound: " + str(exp))

    start_time = time.time()
    if verbose:
        print("Sorting active blocks...")

    sortOrder = SortComponents.indices | SortComponents.alphabetical
    all_blocks = [
        _b for _b in parent_block.block_data_objects(
            active=True, sort=sortOrder, descend_into=descend_into)
    ]

    stop_time = time.time()
    if verbose:
        print("Time to sort active blocks: %.2f seconds" %
              (stop_time - start_time))

    start_time = time.time()
    if verbose:
        print("Collecting variables on active blocks...")

    #
    # First Pass: assign each variable a deterministic id
    #             (an index in a list)
    #
    VarSymbolToVarObject = []
    for block in all_blocks:
        VarSymbolToVarObject.extend(
            block.component_data_objects(Var,
                                         sort=sortOrder,
                                         descend_into=False))
    VarIDToVarSymbol = \
        dict((id(vardata), index)
             for index, vardata in enumerate(VarSymbolToVarObject))

    stop_time = time.time()
    if verbose:
        print("Time to collect variables on active blocks: %.2f seconds" %
              (stop_time - start_time))

    start_time = time.time()
    if verbose:
        print("Compiling active linear constraints...")

    #
    # Second Pass: collect and remove active linear constraints
    #
    constraint_data_to_remove = []
    empty_constraint_containers_to_remove = []
    constraint_containers_to_remove = []
    constraint_containers_to_check = set()
    referenced_variable_symbols = set()
    nnz = 0
    nrows = 0
    SparseMat_pRows = [0]
    for block in all_blocks:

        if hasattr(block, '_canonical_repn'):
            del block._canonical_repn
        if hasattr(block, '_ampl_repn'):
            del block._ampl_repn

        for constraint in block.component_objects(Constraint,
                                                  active=True,
                                                  sort=sortOrder,
                                                  descend_into=False):

            assert not isinstance(constraint, MatrixConstraint)

            if len(constraint) == 0:

                empty_constraint_containers_to_remove.append(
                    (block, constraint))

            else:

                singleton = isinstance(constraint, SimpleConstraint)

                for index, constraint_data in iteritems(constraint):

                    if constraint_data.body.polynomial_degree() <= 1:

                        # collect for removal
                        if singleton:
                            constraint_containers_to_remove.append(
                                (block, constraint))
                        else:
                            constraint_data_to_remove.append(
                                (constraint, index))
                            constraint_containers_to_check.add(
                                (block, constraint))

                        canonical_repn = generate_canonical_repn(
                            constraint_data.body)

                        assert isinstance(canonical_repn, LinearCanonicalRepn)

                        row_variable_symbols = []
                        row_coefficients = []
                        if canonical_repn.variables is None:
                            if skip_trivial_constraints:
                                continue
                        else:
                            row_variable_symbols = \
                                [VarIDToVarSymbol[id(vardata)]
                                 for vardata in canonical_repn.variables]
                            referenced_variable_symbols.update(
                                row_variable_symbols)
                            assert canonical_repn.linear is not None
                            row_coefficients = canonical_repn.linear

                        SparseMat_pRows.append(SparseMat_pRows[-1] + \
                                               len(row_variable_symbols))
                        SparseMat_jCols.extend(row_variable_symbols)
                        SparseMat_Vals.extend(row_coefficients)

                        nnz += len(row_variable_symbols)
                        nrows += 1

                        L = _get_bound(constraint_data.lower)
                        U = _get_bound(constraint_data.upper)
                        constant = value(canonical_repn.constant)
                        if constant is None:
                            constant = 0

                        Ranges.append(L - constant if (L is not None) else 0)
                        Ranges.append(U - constant if (U is not None) else 0)
                        if (L is not None) and \
                           (U is not None) and \
                           (not constraint_data.equality):
                            RangeTypes.append(MatrixConstraint.LowerBound
                                              | MatrixConstraint.UpperBound)
                        elif constraint_data.equality:
                            RangeTypes.append(MatrixConstraint.Equality)
                        elif L is not None:
                            assert U is None
                            RangeTypes.append(MatrixConstraint.LowerBound)
                        else:
                            assert U is not None
                            RangeTypes.append(MatrixConstraint.UpperBound)

                        # Start freeing up memory
                        constraint_data.set_value(None)

    ncols = len(referenced_variable_symbols)

    stop_time = time.time()
    if verbose:
        print("Time to compile active linear constraints: %.2f seconds" %
              (stop_time - start_time))

    start_time = time.time()
    if verbose:
        print("Removing compiled constraint objects...")

    #
    # Remove compiled constraints
    #
    constraints_removed = 0
    constraint_containers_removed = 0
    for block, constraint in empty_constraint_containers_to_remove:
        block.del_component(constraint)
        constraint_containers_removed += 1
    for constraint, index in constraint_data_to_remove:
        del constraint[index]
        constraints_removed += 1
    for block, constraint in constraint_containers_to_remove:
        block.del_component(constraint)
        constraints_removed += 1
        constraint_containers_removed += 1
    for block, constraint in constraint_containers_to_check:
        if len(constraint) == 0:
            block.del_component(constraint)
            constraint_containers_removed += 1

    stop_time = time.time()
    if verbose:
        print("Eliminated %s constraints and %s Constraint container objects" %
              (constraints_removed, constraint_containers_removed))
        print("Time to remove compiled constraint objects: %.2f seconds" %
              (stop_time - start_time))

    start_time = time.time()
    if verbose:
        print("Assigning variable column indices...")

    #
    # Assign a column index to the set of referenced variables
    #
    ColumnIndexToVarSymbol = sorted(referenced_variable_symbols)
    VarSymbolToColumnIndex = dict(
        (symbol, column)
        for column, symbol in enumerate(ColumnIndexToVarSymbol))
    SparseMat_jCols = [
        VarSymbolToColumnIndex[symbol] for symbol in SparseMat_jCols
    ]
    del VarSymbolToColumnIndex
    ColumnIndexToVarObject = [
        VarSymbolToVarObject[var_symbol]
        for var_symbol in ColumnIndexToVarSymbol
    ]

    stop_time = time.time()
    if verbose:
        print("Time to assign variable column indices: %.2f seconds" %
              (stop_time - start_time))

    start_time = time.time()
    if verbose:
        print("Converting compiled constraint data to array storage...")
        print("  - Using %s precision for numeric values" %
              ('single' if single_precision_storage else 'double'))

    #
    # Convert to array storage
    #

    number_storage = 'f' if single_precision_storage else 'd'
    SparseMat_pRows = array.array('L', SparseMat_pRows)
    SparseMat_jCols = array.array('L', SparseMat_jCols)
    SparseMat_Vals = array.array(number_storage, SparseMat_Vals)
    Ranges = array.array(number_storage, Ranges)
    RangeTypes = array.array('B', RangeTypes)

    stop_time = time.time()
    if verbose:
        storage_bytes = \
            SparseMat_pRows.buffer_info()[1] * SparseMat_pRows.itemsize + \
            SparseMat_jCols.buffer_info()[1] * SparseMat_jCols.itemsize + \
            SparseMat_Vals.buffer_info()[1] * SparseMat_Vals.itemsize + \
            Ranges.buffer_info()[1] * Ranges.itemsize + \
            RangeTypes.buffer_info()[1] * RangeTypes.itemsize
        print("Sparse Matrix Dimension:")
        print("  - Rows: " + str(nrows))
        print("  - Cols: " + str(ncols))
        print("  - Nonzeros: " + str(nnz))
        print("Compiled Data Storage: " + str(_label_bytes(storage_bytes)))
        print("Time to convert compiled constraint data to "
              "array storage: %.2f seconds" % (stop_time - start_time))

    parent_block.add_component(
        constraint_name,
        MatrixConstraint(nrows, ncols, nnz, SparseMat_pRows, SparseMat_jCols,
                         SparseMat_Vals, Ranges, RangeTypes,
                         ColumnIndexToVarObject))
Ejemplo n.º 10
0
def compile_block_linear_constraints(parent_block,
                                     constraint_name,
                                     skip_trivial_constraints=False,
                                     single_precision_storage=False,
                                     verbose=False,
                                     descend_into=True):

    if verbose:
        print("")
        print("Compiling linear constraints on block with name: %s"
              % (parent_block.name))

    if not parent_block.is_constructed():
        raise RuntimeError(
            "Attempting to compile block '%s' with unconstructed "
            "component(s)" % (parent_block.name))

    #
    # Linear MatrixConstraint in CSR format
    #
    SparseMat_pRows = []
    SparseMat_jCols = []
    SparseMat_Vals = []
    Ranges = []
    RangeTypes = []

    def _get_bound(exp):
        if exp is None:
            return None
        if is_fixed(exp):
            return value(exp)
        raise ValueError("non-fixed bound: " + str(exp))

    start_time = time.time()
    if verbose:
        print("Sorting active blocks...")

    sortOrder = SortComponents.indices | SortComponents.alphabetical
    all_blocks = [_b for _b in parent_block.block_data_objects(
        active=True,
        sort=sortOrder,
        descend_into=descend_into)]

    stop_time = time.time()
    if verbose:
        print("Time to sort active blocks: %.2f seconds"
              % (stop_time-start_time))

    start_time = time.time()
    if verbose:
        print("Collecting variables on active blocks...")

    #
    # First Pass: assign each variable a deterministic id
    #             (an index in a list)
    #
    VarSymbolToVarObject = []
    for block in all_blocks:
        VarSymbolToVarObject.extend(
            block.component_data_objects(Var,
                                         sort=sortOrder,
                                         descend_into=False))
    VarIDToVarSymbol = \
        dict((id(vardata), index)
             for index, vardata in enumerate(VarSymbolToVarObject))

    stop_time = time.time()
    if verbose:
        print("Time to collect variables on active blocks: %.2f seconds"
              % (stop_time-start_time))

    start_time = time.time()
    if verbose:
        print("Compiling active linear constraints...")

    #
    # Second Pass: collect and remove active linear constraints
    #
    constraint_data_to_remove = []
    empty_constraint_containers_to_remove = []
    constraint_containers_to_remove = []
    constraint_containers_to_check = set()
    referenced_variable_symbols = set()
    nnz = 0
    nrows = 0
    SparseMat_pRows = [0]
    for block in all_blocks:

        if hasattr(block, '_canonical_repn'):
            del block._canonical_repn
        if hasattr(block, '_ampl_repn'):
            del block._ampl_repn

        for constraint in block.component_objects(Constraint,
                                                  active=True,
                                                  sort=sortOrder,
                                                  descend_into=False):

            assert not isinstance(constraint, MatrixConstraint)

            if len(constraint) == 0:

                empty_constraint_containers_to_remove.append((block, constraint))

            else:

                singleton = isinstance(constraint, SimpleConstraint)

                for index, constraint_data in iteritems(constraint):

                    if constraint_data.body.polynomial_degree() <= 1:

                        # collect for removal
                        if singleton:
                            constraint_containers_to_remove.append((block, constraint))
                        else:
                            constraint_data_to_remove.append((constraint, index))
                            constraint_containers_to_check.add((block, constraint))

                        canonical_repn = generate_canonical_repn(constraint_data.body)

                        assert isinstance(canonical_repn, LinearCanonicalRepn)

                        row_variable_symbols = []
                        row_coefficients = []
                        if canonical_repn.variables is None:
                            if skip_trivial_constraints:
                                continue
                        else:
                            row_variable_symbols = \
                                [VarIDToVarSymbol[id(vardata)]
                                 for vardata in canonical_repn.variables]
                            referenced_variable_symbols.update(
                                row_variable_symbols)
                            assert canonical_repn.linear is not None
                            row_coefficients = canonical_repn.linear

                        SparseMat_pRows.append(SparseMat_pRows[-1] + \
                                               len(row_variable_symbols))
                        SparseMat_jCols.extend(row_variable_symbols)
                        SparseMat_Vals.extend(row_coefficients)

                        nnz += len(row_variable_symbols)
                        nrows += 1

                        L = _get_bound(constraint_data.lower)
                        U = _get_bound(constraint_data.upper)
                        constant = value(canonical_repn.constant)
                        if constant is None:
                            constant = 0

                        Ranges.append(L - constant if (L is not None) else 0)
                        Ranges.append(U - constant if (U is not None) else 0)
                        if (L is not None) and \
                           (U is not None) and \
                           (not constraint_data.equality):
                            RangeTypes.append(MatrixConstraint.LowerBound |
                                              MatrixConstraint.UpperBound)
                        elif constraint_data.equality:
                            RangeTypes.append(MatrixConstraint.Equality)
                        elif L is not None:
                            assert U is None
                            RangeTypes.append(MatrixConstraint.LowerBound)
                        else:
                            assert U is not None
                            RangeTypes.append(MatrixConstraint.UpperBound)

                        # Start freeing up memory
                        constraint_data.set_value(None)

    ncols = len(referenced_variable_symbols)

    stop_time = time.time()
    if verbose:
        print("Time to compile active linear constraints: %.2f seconds"
              % (stop_time-start_time))

    start_time = time.time()
    if verbose:
        print("Removing compiled constraint objects...")

    #
    # Remove compiled constraints
    #
    constraints_removed = 0
    constraint_containers_removed = 0
    for block, constraint in empty_constraint_containers_to_remove:
        block.del_component(constraint)
        constraint_containers_removed += 1
    for constraint, index in constraint_data_to_remove:
        del constraint[index]
        constraints_removed += 1
    for block, constraint in constraint_containers_to_remove:
        block.del_component(constraint)
        constraints_removed += 1
        constraint_containers_removed += 1
    for block, constraint in constraint_containers_to_check:
        if len(constraint) == 0:
            block.del_component(constraint)
            constraint_containers_removed += 1

    stop_time = time.time()
    if verbose:
        print("Eliminated %s constraints and %s Constraint container objects"
              % (constraints_removed, constraint_containers_removed))
        print("Time to remove compiled constraint objects: %.2f seconds"
              % (stop_time-start_time))

    start_time = time.time()
    if verbose:
        print("Assigning variable column indices...")

    #
    # Assign a column index to the set of referenced variables
    #
    ColumnIndexToVarSymbol = sorted(referenced_variable_symbols)
    VarSymbolToColumnIndex = dict((symbol, column)
                                  for column, symbol in enumerate(ColumnIndexToVarSymbol))
    SparseMat_jCols = [VarSymbolToColumnIndex[symbol] for symbol in SparseMat_jCols]
    del VarSymbolToColumnIndex
    ColumnIndexToVarObject = [VarSymbolToVarObject[var_symbol]
                              for var_symbol in ColumnIndexToVarSymbol]

    stop_time = time.time()
    if verbose:
        print("Time to assign variable column indices: %.2f seconds"
              % (stop_time-start_time))

    start_time = time.time()
    if verbose:
        print("Converting compiled constraint data to array storage...")
        print("  - Using %s precision for numeric values"
              % ('single' if single_precision_storage else 'double'))

    #
    # Convert to array storage
    #

    number_storage = 'f' if single_precision_storage else 'd'
    SparseMat_pRows = array.array('L', SparseMat_pRows)
    SparseMat_jCols = array.array('L', SparseMat_jCols)
    SparseMat_Vals = array.array(number_storage, SparseMat_Vals)
    Ranges = array.array(number_storage, Ranges)
    RangeTypes = array.array('B', RangeTypes)

    stop_time = time.time()
    if verbose:
        storage_bytes = \
            SparseMat_pRows.buffer_info()[1] * SparseMat_pRows.itemsize + \
            SparseMat_jCols.buffer_info()[1] * SparseMat_jCols.itemsize + \
            SparseMat_Vals.buffer_info()[1] * SparseMat_Vals.itemsize + \
            Ranges.buffer_info()[1] * Ranges.itemsize + \
            RangeTypes.buffer_info()[1] * RangeTypes.itemsize
        print("Sparse Matrix Dimension:")
        print("  - Rows: "+str(nrows))
        print("  - Cols: "+str(ncols))
        print("  - Nonzeros: "+str(nnz))
        print("Compiled Data Storage: "+str(_label_bytes(storage_bytes)))
        print("Time to convert compiled constraint data to "
              "array storage: %.2f seconds" % (stop_time-start_time))

    parent_block.add_component(constraint_name,
                               MatrixConstraint(nrows, ncols, nnz,
                                                SparseMat_pRows,
                                                SparseMat_jCols,
                                                SparseMat_Vals,
                                                Ranges,
                                                RangeTypes,
                                                ColumnIndexToVarObject))
Ejemplo n.º 11
0
def to_matrix_form(model):
    """
    Converts a concrete Pyomo model with a linear objective
    and linear constraints into matrix form.

    Args:
        model: A concrete Pyomo model.

    Returns:
        Objects that define the following LP representation:

        min(max)    c0 + c^T x
        s.t.     bL <= Ax <= bU
                 xL <=  x <= xU

        where,

          c0: scalar representing the aggregation of all
              constants found in the objective expression
          c:  nvars-length list of objective coefficients.
          bL: ncons-length list of constraint upper bounds
          bU: ncons-length list of constraint lower bounds
          A:  3-tuple consisting of list objects (data,
              indices, indptr) defining a sparse matrix in
              Compressed Sparse Row format
          xL: nvars-length list of variable lower bounds
          xU: nvars-length list of variable upper bounds

        In addition, the following mapping objects are
        returned:

          vartocol: maps model variable objects to their
                    integer column index in the A     
                    matrix. E.g.,
                       vartocol[model.x[5]] # -> 0
          contorow: maps model constraint objects to their
                    integer row index in the A matrix. E.g.,
                       contorow[model.c] # -> 19

        All variable and constraint bound vectors will
        contain values of float('-inf') and float('inf')
        where the corresponding bound does not exist.
    """

    # Assign each variable a deterministic symbol (an index
    # in a list) so that we can guarantee the same matrix
    # ordering for a given Pyomo model. We can not assign a
    # column index until after collecting the list of
    # variables that are actually used.
    sortOrder = (SortComponents.indices |
                 SortComponents.alphabetical)
    all_blocks = [_b for _b in
                  model.block_data_objects(
                      active=True,
                      sort=sortOrder)]
    VarSymbolToVarObject = []
    for block in all_blocks:
        VarSymbolToVarObject.extend(
            block.component_data_objects(
                Var,
                sort=sortOrder,
                descend_into=False))
    VarIDToVarSymbol = \
        dict((id(var), index) for index, var in
             enumerate(VarSymbolToVarObject))

    # Loop over objective and constraints to generate the
    # cost vector and matrix rows. Raise an exception if any
    # nonlinear expressions are encountered.
    negative_infinity = float('-inf')
    positive_infinity = float('inf')
    nobjs = 0
    referenced_var_symbols = set()
    A_indptr = [0]
    A_indices = []
    A_data = []
    bL = []
    bU = []
    c_sparse = {}
    c0 = 0.0
    RowIndexToConstraintObject = []
    for block in all_blocks:

        for objective in block.component_data_objects(
                Objective,
                active=True,
                sort=sortOrder,
                descend_into=False):

            nobjs += 1
            if nobjs > 1:
                raise ValueError(
                    "This function does not support "
                    "multiple objectives")

            polynomial_degree = \
                objective.expr.polynomial_degree()
            if (polynomial_degree != 0) and \
               (polynomial_degree != 1):
                raise ValueError(
                    "This function does not support "
                    "nonlinear objectives")

            canonical_repn = \
                generate_canonical_repn(objective.expr)
            variables = canonical_repn.variables
            coefficients = canonical_repn.linear
            if variables is not None:
                for var, coef in zip(variables,
                                     coefficients):
                    var_symbol = VarIDToVarSymbol[id(var)]
                    c_sparse[var_symbol] = coef
                    referenced_var_symbols.add(var_symbol)

            if canonical_repn.constant is not None:
                c0 = value(canonical_repn.constant)

        for sosconstraint in block.component_data_objects(
                SOSConstraint,
                active=True,
                sort=sortOrder,
                descend_into=False):
            raise ValueError("This function does not "
                             "support SOSConstraints")

        for constraint in block.component_data_objects(
                Constraint,
                active=True,
                sort=sortOrder,
                descend_into=False):

            polynomial_degree = \
                constraint.body.polynomial_degree()
            if (polynomial_degree != 0) and \
               (polynomial_degree != 1):
                raise ValueError(
                    "This function does not support "
                    "nonlinear constraints")

            RowIndexToConstraintObject.append(constraint)

            canonical_repn = \
                generate_canonical_repn(constraint.body)
            variables = canonical_repn.variables
            coefficients = canonical_repn.linear

            row_variable_symbols = []
            row_coefficients = []
            if variables is not None:
                row_variable_symbols = \
                    [VarIDToVarSymbol[id(var)]
                     for var in variables]
                referenced_var_symbols.\
                    update(row_variable_symbols)
                row_coefficients = coefficients

            A_indptr.append(A_indptr[-1] +
                            len(row_variable_symbols))
            A_indices.extend(row_variable_symbols)
            A_data.extend(row_coefficients)

            L = negative_infinity
            U = positive_infinity
            constant = 0.0

            if constraint.lower is not None:
                L = value(constraint.lower)
            if constraint.upper is not None:
                U = value(constraint.upper)
            if canonical_repn.constant is not None:
                constant = value(canonical_repn.constant)

            bL.append(L - constant)
            bU.append(U - constant)

    ncols = len(referenced_var_symbols)

    # Assign a column index to the set of referenced
    # variables
    ColumnIndexToVarSymbol = sorted(referenced_var_symbols)
    VarSymbolToColumnIndex = \
        dict((symbol, col) for col, symbol in
             enumerate(ColumnIndexToVarSymbol))

    A_indices = [VarSymbolToColumnIndex[symbol]
                 for symbol in A_indices]

    ColumnIndexToVarObject = \
        [VarSymbolToVarObject[var_symbol]
         for var_symbol in ColumnIndexToVarSymbol]

    # Convert the sparse cost vector into a dense list based
    # on the variable column id assignments.
    c = [0.0 for j in range(ncols)]
    for var_symbol, coef in c_sparse.items():
        c[VarSymbolToColumnIndex[var_symbol]] = coef

    # Generate dense xL and xU variable bound lists based on
    # the variable column id assignments
    xL = [negative_infinity for j in range(ncols)]
    xU = [positive_infinity for j in range(ncols)]
    for j, var in enumerate(ColumnIndexToVarObject):
        if var.lb is not None:
            xL[j] = value(var.lb)
        if var.ub is not None:
            xU[j] = value(var.ub)

    # Generate the component maps that allow one to recover
    # the row/column index from a constraint/variable
    # object. The reverse maps are easy enough to generate
    # from these two maps if needed.
    vartocol = ComponentMap(
        (var, j) for j, var in
        enumerate(ColumnIndexToVarObject))
    contorow = ComponentMap(
        (con, i)
        for i, con in enumerate(RowIndexToConstraintObject))
    return (c0, c,
            bL, bU,
            (A_data, A_indices, A_indptr),
            xL, xU,
            vartocol, contorow)
Ejemplo n.º 12
0
def to_matrix_form(model):
    """
    Converts a concrete Pyomo model with a linear objective
    and linear constraints into matrix form.

    Args:
        model: A concrete Pyomo model.

    Returns:
        Objects that define the following LP representation:

        min(max)    c0 + c^T x
        s.t.     bL <= Ax <= bU
                 xL <=  x <= xU

        where,

          c0: scalar representing the aggregation of all
              constants found in the objective expression
          c:  nvars-length list of objective coefficients.
          bL: ncons-length list of constraint upper bounds
          bU: ncons-length list of constraint lower bounds
          A:  3-tuple consisting of list objects (data,
              indices, indptr) defining a sparse matrix in
              Compressed Sparse Row format
          xL: nvars-length list of variable lower bounds
          xU: nvars-length list of variable upper bounds

        In addition, the following mapping objects are
        returned:

          vartocol: maps model variable objects to their
                    integer column index in the A     
                    matrix. E.g.,
                       vartocol[model.x[5]] # -> 0
          contorow: maps model constraint objects to their
                    integer row index in the A matrix. E.g.,
                       contorow[model.c] # -> 19

        All variable and constraint bound vectors will
        contain values of float('-inf') and float('inf')
        where the corresponding bound does not exist.
    """

    # Assign each variable a deterministic symbol (an index
    # in a list) so that we can guarantee the same matrix
    # ordering for a given Pyomo model. We can not assign a
    # column index until after collecting the list of
    # variables that are actually used.
    sortOrder = (SortComponents.indices | SortComponents.alphabetical)
    all_blocks = [
        _b for _b in model.block_data_objects(active=True, sort=sortOrder)
    ]
    VarSymbolToVarObject = []
    for block in all_blocks:
        VarSymbolToVarObject.extend(
            block.component_data_objects(Var,
                                         sort=sortOrder,
                                         descend_into=False))
    VarIDToVarSymbol = \
        dict((id(var), index) for index, var in
             enumerate(VarSymbolToVarObject))

    # Loop over objective and constraints to generate the
    # cost vector and matrix rows. Raise an exception if any
    # nonlinear expressions are encountered.
    negative_infinity = float('-inf')
    positive_infinity = float('inf')
    nobjs = 0
    referenced_var_symbols = set()
    A_indptr = [0]
    A_indices = []
    A_data = []
    bL = []
    bU = []
    c_sparse = {}
    c0 = 0.0
    RowIndexToConstraintObject = []
    for block in all_blocks:

        for objective in block.component_data_objects(Objective,
                                                      active=True,
                                                      sort=sortOrder,
                                                      descend_into=False):

            nobjs += 1
            if nobjs > 1:
                raise ValueError("This function does not support "
                                 "multiple objectives")

            polynomial_degree = \
                objective.expr.polynomial_degree()
            if (polynomial_degree != 0) and \
               (polynomial_degree != 1):
                raise ValueError("This function does not support "
                                 "nonlinear objectives")

            canonical_repn = \
                generate_canonical_repn(objective.expr)
            variables = canonical_repn.variables
            coefficients = canonical_repn.linear
            if variables is not None:
                for var, coef in zip(variables, coefficients):
                    var_symbol = VarIDToVarSymbol[id(var)]
                    c_sparse[var_symbol] = coef
                    referenced_var_symbols.add(var_symbol)

            if canonical_repn.constant is not None:
                c0 = value(canonical_repn.constant)

        for sosconstraint in block.component_data_objects(SOSConstraint,
                                                          active=True,
                                                          sort=sortOrder,
                                                          descend_into=False):
            raise ValueError("This function does not "
                             "support SOSConstraints")

        for constraint in block.component_data_objects(Constraint,
                                                       active=True,
                                                       sort=sortOrder,
                                                       descend_into=False):

            polynomial_degree = \
                constraint.body.polynomial_degree()
            if (polynomial_degree != 0) and \
               (polynomial_degree != 1):
                raise ValueError("This function does not support "
                                 "nonlinear constraints")

            RowIndexToConstraintObject.append(constraint)

            canonical_repn = \
                generate_canonical_repn(constraint.body)
            variables = canonical_repn.variables
            coefficients = canonical_repn.linear

            row_variable_symbols = []
            row_coefficients = []
            if variables is not None:
                row_variable_symbols = \
                    [VarIDToVarSymbol[id(var)]
                     for var in variables]
                referenced_var_symbols.\
                    update(row_variable_symbols)
                row_coefficients = coefficients

            A_indptr.append(A_indptr[-1] + len(row_variable_symbols))
            A_indices.extend(row_variable_symbols)
            A_data.extend(row_coefficients)

            L = negative_infinity
            U = positive_infinity
            constant = 0.0

            if constraint.lower is not None:
                L = value(constraint.lower)
            if constraint.upper is not None:
                U = value(constraint.upper)
            if canonical_repn.constant is not None:
                constant = value(canonical_repn.constant)

            bL.append(L - constant)
            bU.append(U - constant)

    ncols = len(referenced_var_symbols)

    # Assign a column index to the set of referenced
    # variables
    ColumnIndexToVarSymbol = sorted(referenced_var_symbols)
    VarSymbolToColumnIndex = \
        dict((symbol, col) for col, symbol in
             enumerate(ColumnIndexToVarSymbol))

    A_indices = [VarSymbolToColumnIndex[symbol] for symbol in A_indices]

    ColumnIndexToVarObject = \
        [VarSymbolToVarObject[var_symbol]
         for var_symbol in ColumnIndexToVarSymbol]

    # Convert the sparse cost vector into a dense list based
    # on the variable column id assignments.
    c = [0.0 for j in range(ncols)]
    for var_symbol, coef in c_sparse.items():
        c[VarSymbolToColumnIndex[var_symbol]] = coef

    # Generate dense xL and xU variable bound lists based on
    # the variable column id assignments
    xL = [negative_infinity for j in range(ncols)]
    xU = [positive_infinity for j in range(ncols)]
    for j, var in enumerate(ColumnIndexToVarObject):
        if var.lb is not None:
            xL[j] = value(var.lb)
        if var.ub is not None:
            xU[j] = value(var.ub)

    # Generate the component maps that allow one to recover
    # the row/column index from a constraint/variable
    # object. The reverse maps are easy enough to generate
    # from these two maps if needed.
    vartocol = ComponentMap(
        (var, j) for j, var in enumerate(ColumnIndexToVarObject))
    contorow = ComponentMap(
        (con, i) for i, con in enumerate(RowIndexToConstraintObject))
    return (c0, c, bL, bU, (A_data, A_indices, A_indptr), xL, xU, vartocol,
            contorow)