Ejemplo n.º 1
0
def _build_equality_set(model):
    """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.

    """
    # Map of variables to their equality set (ComponentSet)
    eq_var_map = ComponentMap()

    # Loop through all the active constraints in the model
    for constraint in model.component_data_objects(
            ctype=Constraint, active=True, descend_into=True):
        eq_linked_vars = _get_equality_linked_variables(constraint)
        if not eq_linked_vars:
            continue  # if we get an empty tuple, skip to next constraint.
        v1, v2 = eq_linked_vars
        set1 = eq_var_map.get(v1, ComponentSet((v1, v2)))
        set2 = eq_var_map.get(v2, (v2,))

        # if set1 and set2 are equivalent, skip to next constraint.
        if set1 is set2:
            continue

        # add all elements of set2 to set 1
        set1.update(set2)
        # Update all elements to point to set 1
        for v in set1:
            eq_var_map[v] = set1

    return eq_var_map
Ejemplo n.º 2
0
    def __init__(self, **kwds):

        # Suffix type information
        self._direction = None
        self._datatype = None
        self._rule = None

        # The suffix direction
        direction = kwds.pop('direction', Suffix.LOCAL)

        # The suffix datatype
        datatype = kwds.pop('datatype', Suffix.FLOAT)

        # The suffix construction rule
        # TODO: deprecate the use of 'rule'
        self._rule = kwds.pop('rule', None)
        self._rule = kwds.pop('initialize', self._rule)

        # Check that keyword values make sense (these function have
        # internal error checking).
        self.set_direction(direction)
        self.set_datatype(datatype)

        # Initialize base classes
        kwds.setdefault('ctype', Suffix)
        ActiveComponent.__init__(self, **kwds)
        ComponentMap.__init__(self)

        if self._rule is None:
            self.construct()
Ejemplo n.º 3
0
 def __init__(self, expression, improved_var_bounds=None):
     super(MCPP_visitor, self).__init__()
     self.mcpp = _MCPP_lib()
     so_file_version = self.mcpp.get_version()
     if six.PY3:
         so_file_version = so_file_version.decode("utf-8")
     if not so_file_version == __version__:
         raise MCPP_Error(
             "Shared object file version %s is out of date with MC++ interface version %s. "
             "Please rebuild the library." % (so_file_version, __version__))
     self.missing_value_warnings = []
     self.expr = expression
     vars = list(identify_variables(expression, include_fixed=False))
     self.num_vars = len(vars)
     # Map expression variables to MC variables
     self.known_vars = ComponentMap()
     # Map expression variables to their index
     self.var_to_idx = ComponentMap()
     # Pre-register all variables
     inf = float('inf')
     for i, var in enumerate(vars):
         self.var_to_idx[var] = i
         # check if improved variable bound is provided
         if improved_var_bounds is not None:
             lb, ub = improved_var_bounds.get(var, (-inf, inf))
         else:
             lb, ub = -inf, inf
         self.known_vars[var] = self.register_var(var, lb, ub)
     self.refs = None
Ejemplo n.º 4
0
 def __setstate__(self, state):
     """
     This method must be defined for deepcopy/pickling because this
     class relies on component ids.
     """
     ActiveComponent.__setstate__(self, state)
     ComponentMap.__setstate__(self, state)
Ejemplo n.º 5
0
def _bilinear_expressions(model):
    # TODO for now, we look for only expressions where the bilinearities are
    # exposed on the root level SumExpression, and thus accessible via
    # generate_standard_repn. This will not detect exp(x*y). We require a
    # factorization transformation to be applied beforehand in order to pick
    # these constraints up.
    pass
    # Bilinear map will be stored in the format:
    # x --> (y --> [constr1, constr2, ...], z --> [constr2, constr3])
    bilinear_map = ComponentMap()
    for constr in model.component_data_objects(
            Constraint, active=True, descend_into=(Block, Disjunct)):
        if constr.body.polynomial_degree() in (1, 0):
            continue  # Skip trivial and linear constraints
        repn = generate_standard_repn(constr.body)
        for pair in repn.quadratic_vars:
            v1, v2 = pair
            v1_pairs = bilinear_map.get(v1, ComponentMap())
            if v2 in v1_pairs:
                # bilinear term has been found before. Simply add constraint to
                # the set associated with the bilinear term.
                v1_pairs[v2].add(constr)
            else:
                # We encounter the bilinear term for the first time.
                bilinear_map[v1] = v1_pairs
                bilinear_map[v2] = bilinear_map.get(v2, ComponentMap())
                constraints_with_bilinear_pair = ComponentSet([constr])
                bilinear_map[v1][v2] = constraints_with_bilinear_pair
                bilinear_map[v2][v1] = constraints_with_bilinear_pair
    return bilinear_map
Ejemplo n.º 6
0
 def __setstate__(self, state):
     """
     This method must be defined for deepcopy/pickling because this
     class relies on component ids.
     """
     ActiveComponent.__setstate__(self, state)
     ComponentMap.__setstate__(self, state)
Ejemplo n.º 7
0
    def _set_instance(self, model, kwds={}):
        self._range_constraints = set()
        DirectOrPersistentSolver._set_instance(self, model, kwds)
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = ComponentMap()
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = ComponentMap()
        try:
            if model.name is not None:
                self._solver_model = self._gurobipy.Model(model.name)
            else:
                self._solver_model = self._gurobipy.Model()
        except Exception:
            e = sys.exc_info()[1]
            msg = (
                'Unable to create Gurobi model. Have you installed the Python bindings for Gurboi?\n\n\t'
                + 'Error message: {0}'.format(e))
            raise Exception(msg)

        self._add_block(model)

        for var, n_ref in self._referenced_variables.items():
            if n_ref != 0:
                if var.fixed:
                    if not self._output_fixed_variable_bounds:
                        raise ValueError(
                            "Encountered a fixed variable (%s) inside an active objective "
                            "or constraint expression on model %s, which is usually indicative of "
                            "a preprocessing error. Use the IO-option 'output_fixed_variable_bounds=True' "
                            "to suppress this error and fix the variable by overwriting its bounds in "
                            "the Gurobi instance." % (
                                var.name,
                                self._pyomo_model.name,
                            ))
Ejemplo n.º 8
0
 def _apply_to(self, instance, **kwds):
     config = self.CONFIG(kwds)
     if config.reversible:
         if any(
                 hasattr(instance, map_name) for map_name in [
                     '_tmp_var_bound_strip_lb', '_tmp_var_bound_strip_ub',
                     '_tmp_var_bound_strip_domain'
                 ]):
             raise RuntimeError(
                 'Variable stripping reversion component maps already '
                 'exist. Did you already apply a temporary transformation '
                 'without a subsequent reversion?')
         # Component maps to store data for reversion.
         instance._tmp_var_bound_strip_lb = ComponentMap()
         instance._tmp_var_bound_strip_ub = ComponentMap()
         instance._tmp_var_bound_strip_domain = ComponentMap()
     for var in instance.component_data_objects(ctype=Var):
         if config.strip_domains and not var.domain == Reals:
             if config.reversible:
                 instance._tmp_var_bound_strip_domain[var] = var.domain
             var.domain = Reals
         if var.has_lb():
             if config.reversible:
                 instance._tmp_var_bound_strip_lb[var] = var.lb
             var.setlb(None)
         if var.has_ub():
             if config.reversible:
                 instance._tmp_var_bound_strip_ub[var] = var.ub
             var.setub(None)
Ejemplo n.º 9
0
 def test_clear(self):
     cmap = ComponentMap()
     self.assertEqual(len(cmap), 0)
     cmap.update(self._components)
     self.assertEqual(len(cmap), len(self._components))
     cmap.clear()
     self.assertEqual(len(cmap), 0)
Ejemplo n.º 10
0
def _build_equality_set(model):
    """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.

    """
    # Map of variables to their equality set (ComponentSet)
    eq_var_map = ComponentMap()

    # Loop through all the active constraints in the model
    for constraint in model.component_data_objects(ctype=Constraint,
                                                   active=True,
                                                   descend_into=True):
        eq_linked_vars = _get_equality_linked_variables(constraint)
        if not eq_linked_vars:
            continue  # if we get an empty tuple, skip to next constraint.
        v1, v2 = eq_linked_vars
        set1 = eq_var_map.get(v1, ComponentSet((v1, v2)))
        set2 = eq_var_map.get(v2, (v2, ))

        # if set1 and set2 are equivalent, skip to next constraint.
        if set1 is set2:
            continue

        # add all elements of set2 to set 1
        set1.update(set2)
        # Update all elements to point to set 1
        for v in set1:
            eq_var_map[v] = set1

    return eq_var_map
Ejemplo n.º 11
0
    def _set_instance(self, model, kwds={}):
        if not isinstance(model, (Model, IBlockStorage)):
            msg = "The problem instance supplied to the {0} plugin " \
                  "'_presolve' method must be of type 'Model'".format(type(self))
            raise ValueError(msg)
        self._pyomo_model = model
        self._symbolic_solver_labels = kwds.pop('symbolic_solver_labels',
                                                self._symbolic_solver_labels)
        self._skip_trivial_constraints = kwds.pop(
            'skip_trivial_constraints', self._skip_trivial_constraints)
        self._output_fixed_variable_bounds = kwds.pop(
            'output_fixed_variable_bounds', self._output_fixed_variable_bounds)
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._pyomo_con_to_solver_con_map = ComponentMap()
        self._vars_referenced_by_con = ComponentMap()
        self._vars_referenced_by_obj = ComponentSet()
        self._referenced_variables = ComponentMap()
        self._objective_label = None
        self._objective = None

        self._symbol_map = SymbolMap()

        if self._symbolic_solver_labels:
            self._labeler = TextLabeler()
        else:
            self._labeler = NumericLabeler('x')
Ejemplo n.º 12
0
 def _get_constraint_map_dict(self, transBlock):
     if not hasattr(transBlock, "_constraintMap"):
         transBlock._constraintMap = {
             'srcConstraints': ComponentMap(),
             'transformedConstraints': ComponentMap()
         }
     return transBlock._constraintMap
Ejemplo n.º 13
0
 def test_update(self):
     cmap = ComponentMap()
     self.assertEqual(len(cmap), 0)
     cmap.update(self._components)
     self.assertEqual(len(cmap), len(self._components))
     for c, val in self._components:
         self.assertEqual(cmap[c], val)
Ejemplo n.º 14
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.º 15
0
    def __init__(self, **kwds):

        # Suffix type information
        self._direction = None
        self._datatype = None
        self._rule = None

        # The suffix direction
        direction = kwds.pop('direction', Suffix.LOCAL)

        # The suffix datatype
        datatype = kwds.pop('datatype', Suffix.FLOAT)

        # The suffix construction rule
        # TODO: deprecate the use of 'rule'
        self._rule = kwds.pop('rule', None)
        self._rule = kwds.pop('initialize', self._rule)

        # Check that keyword values make sense (these function have
        # internal error checking).
        self.set_direction(direction)
        self.set_datatype(datatype)

        # Initialize base classes
        kwds.setdefault('ctype', Suffix)
        ActiveComponent.__init__(self, **kwds)
        ComponentMap.__init__(self)

        if self._rule is None:
            self.construct()
Ejemplo n.º 16
0
 def test_update(self):
     cmap = ComponentMap()
     self.assertEqual(len(cmap), 0)
     cmap.update(self._components)
     self.assertEqual(len(cmap), len(self._components))
     for c, val in self._components:
         self.assertEqual(cmap[c], val)
Ejemplo n.º 17
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_standard_repn(constr.body)
            # only take the variables with nonzero coefficients
            vars_ = [v for i, v in enumerate(repn.linear_vars)
                     if repn.linear_coefs[i]]
            if (len(vars_) == 2 and
                    sorted(l for l in repn.linear_coefs 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.º 18
0
def reverse_sd(expr):
    """
    First order reverse ad

    Parameters
    ----------
    expr: pyomo.core.expr.numeric_expr.ExpressionBase
        expression to differentiate

    Returns
    -------
    pyomo.core.kernel.component_map.ComponentMap
        component_map mapping variables to derivatives with respect
        to the corresponding variable
    """
    val_dict = ComponentMap()
    der_dict = ComponentMap()

    visitorA = _ReverseSDVisitorLeafToRoot(val_dict, der_dict)
    visitorA.dfs_postorder_stack(expr)
    der_dict[expr] = 1
    visitorB = _ReverseSDVisitorRootToLeaf(val_dict, der_dict)
    visitorB.dfs_postorder_stack(expr)

    return der_dict
Ejemplo n.º 19
0
 def test_items(self):
     cmap = ComponentMap(self._components)
     for x in cmap.items():
         self.assertEqual(type(x), tuple)
         self.assertEqual(len(x), 2)
     self.assertEqual(
         sorted(cmap.items(), key=lambda _x: (id(_x[0]), _x[1])),
         sorted(self._components, key=lambda _x: (id(_x[0]), _x[1])))
Ejemplo n.º 20
0
 def test_len(self):
     cmap = ComponentMap()
     self.assertEqual(len(cmap), 0)
     cmap.update(self._components)
     self.assertEqual(len(cmap), len(self._components))
     cmap = ComponentMap(self._components)
     self.assertEqual(len(cmap), len(self._components))
     self.assertTrue(len(self._components) > 0)
Ejemplo n.º 21
0
    def __init__(self, component):
        BaseRelaxationData.__init__(self, component)

        self._partitions = ComponentMap()
        """ComponentMap: var: list of float"""

        self._saved_partitions = []
        """list of CompnentMap"""
Ejemplo n.º 22
0
 def test_iter(self):
     cmap = ComponentMap()
     self.assertEqual(list(iter(cmap)), [])
     cmap.update(self._components)
     ids_seen = set()
     for c in cmap:
         ids_seen.add(id(c))
     self.assertEqual(ids_seen, set(id(c) for c, val in self._components))
Ejemplo n.º 23
0
 def __init__(self, **kwds):
     kwds['type'] = 'gurobi_direct'
     DirectSolver.__init__(self, **kwds)
     self._pyomo_var_to_solver_var_map = ComponentMap()
     self._solver_var_to_pyomo_var_map = ComponentMap()
     self._pyomo_con_to_solver_con_map = dict()
     self._solver_con_to_pyomo_con_map = ComponentMap()
     self._init()
Ejemplo n.º 24
0
 def test_product_with_itself(self, visitor, f, cvx_f, bounds_f, expected):
     convexity = ComponentMap()
     convexity[f] = cvx_f
     bounds = ComponentMap()
     bounds[f] = bounds_f
     expr = f * f
     matched, result = visitor.visit_expression(expr, convexity, None, bounds)
     assert matched
     assert result == expected
Ejemplo n.º 25
0
 def _result_with_mono_bounds(self, visitor, g, mono_g, bounds_g):
     mono = ComponentMap()
     mono[g] = mono_g
     bounds = ComponentMap()
     bounds[g] = bounds_g
     expr = pe.cos(g)
     matched, result = visitor.visit_expression(expr, mono, bounds)
     assert matched
     return result
Ejemplo n.º 26
0
 def test_items(self):
     cmap = ComponentMap(self._components)
     for x in cmap.items():
         self.assertEqual(type(x), tuple)
         self.assertEqual(len(x), 2)
     self.assertEqual(sorted(cmap.items(),
                             key=lambda _x: (id(_x[0]), _x[1])),
                      sorted(self._components,
                             key=lambda _x: (id(_x[0]), _x[1])))
Ejemplo n.º 27
0
 def test_iter(self):
     cmap = ComponentMap()
     self.assertEqual(list(iter(cmap)), [])
     cmap.update(self._components)
     ids_seen = set()
     for c in cmap:
         ids_seen.add(id(c))
     self.assertEqual(ids_seen,
                      set(id(c) for c,val in self._components))
Ejemplo n.º 28
0
def test_abs(visitor, g, mono_g, bounds_g, expected):
    bounds = ComponentMap()
    bounds[g] = bounds_g
    mono = ComponentMap()
    mono[g] = mono_g
    expr = abs(g)
    matched, result = visitor.visit_expression(expr, mono, bounds)
    assert matched
    assert result == expected
Ejemplo n.º 29
0
def test_reciprocal(visitor, g, mono_g, bound_g, expected):
    bounds = ComponentMap()
    bounds[g] = bound_g
    mono = ComponentMap()
    mono[g] = mono_g

    expr = ReciprocalExpression([g])
    matched, result = visitor.visit_expression(expr, mono, bounds)
    assert matched
    assert result == expected
Ejemplo n.º 30
0
def test_nonincreasing_function(visitor, g, func_name, mono_g, bounds_g):
    mono = ComponentMap()
    mono[g] = mono_g
    bounds = ComponentMap()
    bounds[g] = bounds_g
    func = getattr(pe, func_name)
    expr = func(g)
    matched, result = visitor.visit_expression(expr, mono, bounds)
    assert matched
    assert result == mono_g.negate()
Ejemplo n.º 31
0
def test_negation(visitor, g, mono_g, bounds_g):
    mono = ComponentMap()
    mono[g] = mono_g
    bounds = ComponentMap()
    bounds[g] = bounds_g
    expr = -g
    assume(isinstance(expr, NegationExpression))
    matched, result = visitor.visit_expression(expr, mono, bounds)
    assert matched
    assert result == mono_g.negate()
Ejemplo n.º 32
0
 def __init__(self):
     self.pyomo2sympy = ComponentMap()
     self.parent_symbol = ComponentMap()
     self.sympy2pyomo = {}
     self.sympy2latex = {}
     self.used = {}
     self.i_var = 0
     self.i_expr = 0
     self.i_func = 0
     self.i = 0
Ejemplo n.º 33
0
    def generate_names(self,
                       active=None,
                       descend_into=True,
                       convert=str,
                       prefix=""):
        """
        Generate a container of fully qualified names (up to
        this container) for objects stored under this
        container.

        Args:
            active (:const:`True`/:const:`None`): Set to
                :const:`True` to indicate that only active
                components should be included. The default
                value of :const:`None` indicates that all
                components (including those that have been
                deactivated) should be included. *Note*:
                This flag is ignored for any objects that do
                not have an active flag.
            descend_into (bool): Indicates whether or not to
                include subcomponents of any container
                objects that are not components. Default is
                :const:`True`.
            convert (function): A function that converts a
                storage key into a string
                representation. Default is str.
            prefix (str): A string to prefix names with.

        Returns:
            A component map that behaves as a dictionary
            mapping component objects to names.
        """
        assert active in (None, True)
        from pyomo.core.kernel.component_map import ComponentMap
        names = ComponentMap()

        # if not active, then no children can be active
        if (active is not None) and \
           not getattr(self, _active_flag_name, True):
            return names

        name_template = (prefix + self._child_storage_delimiter_string +
                         self._child_storage_entry_string)
        for child in self.children():
            if (active is None) or \
               getattr(child, _active_flag_name, True):
                names[child] = (name_template % convert(child.storage_key))
                if descend_into and child._is_container and \
                   (not child._is_component):
                    names.update(
                        child.generate_names(active=active,
                                             descend_into=True,
                                             convert=convert,
                                             prefix=names[child]))
        return names
Ejemplo n.º 34
0
 def _result_with_base_expo(self, visitor, base, mono_base, bounds_base, expo):
     mono = ComponentMap()
     mono[base] = mono_base
     mono[expo] = M.Constant
     bounds = ComponentMap()
     bounds[base] = bounds_base
     bounds[expo] = I(expo, expo)
     expr = PowExpression([base, expo])
     matched, result = visitor.visit_expression(expr, mono, bounds)
     assert matched
     return result
Ejemplo n.º 35
0
def test_pow(visitor, base, expo, mono_base, bounds_base, mono_expo, bounds_expo):
    mono = ComponentMap()
    mono[base] = mono_base
    mono[expo] = mono_expo
    bounds = ComponentMap()
    bounds[base] = bounds_base
    bounds[expo] = bounds_expo
    expr = PowExpression([base, expo])
    matched, result = visitor.visit_expression(expr, mono, bounds)
    assert matched
    assert result == M.Unknown
Ejemplo n.º 36
0
    def __init__(self, **kwds):
        if 'type' not in kwds:
            kwds['type'] = 'gurobi_direct'
        super(GurobiDirect, self).__init__(**kwds)
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = ComponentMap()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = ComponentMap()
        self._needs_updated = True  # flag that indicates if solver_model.update() needs called before getting variable and constraint attributes
        self._callback = None
        self._callback_func = None

        self._name = None
        try:
            import gurobipy
            self._gurobipy = gurobipy
            self._python_api_exists = True
            self._version = self._gurobipy.gurobi.version()
            self._name = "Gurobi %s.%s%s" % self._version
            while len(self._version) < 4:
                self._version += (0, )
            self._version = self._version[:4]
            self._version_major = self._version[0]
        except ImportError:
            self._python_api_exists = False
        except Exception as e:
            # other forms of exceptions can be thrown by the gurobi python
            # import. for example, a gurobipy.GurobiError exception is thrown
            # if all tokens for Gurobi are already in use. assuming, of
            # course, the license is a token license. unfortunately, you can't
            # import without a license, which means we can't test for the
            # exception above!
            print("Import of gurobipy failed - gurobi message=" + str(e) +
                  "\n")
            self._python_api_exists = False

        self._range_constraints = set()

        self._max_obj_degree = 2
        self._max_constraint_degree = 2

        # Note: Undefined capabilites default to None
        self._capabilities.linear = True
        self._capabilities.quadratic_objective = True
        self._capabilities.quadratic_constraint = True
        self._capabilities.integer = True
        self._capabilities.sos1 = True
        self._capabilities.sos2 = True

        # fix for compatibility with pre-5.0 Gurobi
        if self._python_api_exists and \
           (self._version_major < 5):
            self._max_constraint_degree = 1
            self._capabilities.quadratic_constraint = False
Ejemplo n.º 37
0
    def _rule_result(self, visitor, g, cvx, mono_g, bounds_g, func):
        convexity = ComponentMap()
        convexity[g] = cvx
        mono = ComponentMap()
        mono[g] = mono_g
        bounds = ComponentMap()
        bounds[g] = bounds_g

        expr = func(g)
        matched, result = visitor.visit_expression(expr, convexity, mono, bounds)
        assert matched
        return result
Ejemplo n.º 38
0
def test_visitor_tightens_new_bounds(visitor, expr):
    bounds = ComponentMap()
    assert bounds.get(expr, None) is None

    assert visitor.handle_result(expr, Interval(0, 2), bounds)
    assert bounds[expr] == Interval(0, 2)

    assert not visitor.handle_result(expr, Interval(0, 2), bounds)
    assert bounds[expr] == Interval(0, 2)

    assert visitor.handle_result(expr, Interval(0, 1), bounds)
    assert bounds[expr] == Interval(0, 1)
Ejemplo n.º 39
0
Archivo: util.py Proyecto: CanLi1/pyomo
def calc_jacobians(solve_data, config):
    """Generate a map of jacobians."""
    # Map nonlinear_constraint --> Map(
    #     variable --> jacobian of constraint wrt. variable)
    solve_data.jacobians = ComponentMap()
    for c in solve_data.mip.MindtPy_utils.constraint_list:
        if c.body.polynomial_degree() in (1, 0):
            continue  # skip linear constraints
        vars_in_constr = list(EXPR.identify_variables(c.body))
        jac_list = differentiate(c.body, wrt_list=vars_in_constr)
        solve_data.jacobians[c] = ComponentMap(
            (var, jac_wrt_var)
            for var, jac_wrt_var in zip(vars_in_constr, jac_list))
Ejemplo n.º 40
0
 def _result_with_base_expo(self, visitor, base, expo, mono_expo, bounds_expo):
     rule = PowerRule()
     mono = ComponentMap()
     mono[base] = M.Constant
     mono[expo] = mono_expo
     bounds = ComponentMap()
     bounds[base] = I(base, base)
     bounds[expo] = bounds_expo
     expr = base ** expo
     assume(isinstance(expr, PowExpression))
     matched, result = visitor.visit_expression(expr, mono, bounds)
     assert matched
     return result
Ejemplo n.º 41
0
 def test_getsetdelitem(self):
     cmap = ComponentMap()
     for c, val in self._components:
         self.assertTrue(c not in cmap)
     for c, val in self._components:
         cmap[c] = val
         self.assertEqual(cmap[c], val)
         self.assertEqual(cmap.get(c), val)
         del cmap[c]
         with self.assertRaises(KeyError):
             cmap[c]
         with self.assertRaises(KeyError):
             del cmap[c]
         self.assertEqual(cmap.get(c), None)
Ejemplo n.º 42
0
def determine_valid_values(block, discr_var_to_constrs_map, config):
    """Calculate valid values for each effectively discrete variable.

    We need the set of possible values for the effectively discrete variable in
    order to do the reformulations.

    Right now, we select a naive approach where we look for variables in the
    discreteness-inducing constraints. We then adjust their values and see if
    things are stil feasible. Based on their coefficient values, we can infer a
    set of allowable values for the effectively discrete variable.

    Args:
        block: The model or a disjunct on the model.

    """
    possible_values = ComponentMap()

    for eff_discr_var, constrs in discr_var_to_constrs_map.items():
        # get the superset of possible values by looking through the
        # constraints
        for constr in constrs:
            repn = generate_standard_repn(constr.body)
            var_coef = sum(coef for i, coef in enumerate(repn.linear_coefs)
                           if repn.linear_vars[i] is eff_discr_var)
            const = -(repn.constant - constr.upper) / var_coef
            possible_vals = set((const,))
            for i, var in enumerate(repn.linear_vars):
                if var is eff_discr_var:
                    continue
                coef = -repn.linear_coefs[i] / var_coef
                if var.is_binary():
                    var_values = (0, coef)
                elif var.is_integer():
                    var_values = [v * coef for v in range(var.lb, var.ub + 1)]
                else:
                    raise ValueError(
                        '%s has unacceptable variable domain: %s' %
                        (var.name, var.domain))
                possible_vals = set(
                    (v1 + v2 for v1 in possible_vals for v2 in var_values))
            old_possible_vals = possible_values.get(eff_discr_var, None)
            if old_possible_vals is not None:
                possible_values[eff_discr_var] = old_possible_vals & possible_vals
            else:
                possible_values[eff_discr_var] = possible_vals

    possible_values = prune_possible_values(block, possible_values, config)

    return possible_values
Ejemplo n.º 43
0
 def test_setdefault(self):
     cmap = ComponentMap()
     for c,_ in self._components:
         with self.assertRaises(KeyError):
             cmap[c]
         self.assertTrue(c not in cmap)
         cmap.setdefault(c, []).append(1)
         self.assertEqual(cmap[c], [1])
         del cmap[c]
         with self.assertRaises(KeyError):
             cmap[c]
         self.assertTrue(c not in cmap)
         cmap[c] = []
         cmap.setdefault(c, []).append(1)
         self.assertEqual(cmap[c], [1])
Ejemplo n.º 44
0
    def _set_instance(self, model, kwds={}):
        self._range_constraints = set()
        DirectOrPersistentSolver._set_instance(self, model, kwds)
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = ComponentMap()
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = ComponentMap()
        try:
            if model.name is not None:
                self._solver_model = self._gurobipy.Model(model.name)
            else:
                self._solver_model = self._gurobipy.Model()
        except Exception:
            e = sys.exc_info()[1]
            msg = ("Unable to create Gurobi model. "
                   "Have you installed the Python "
                   "bindings for Gurboi?\n\n\t"+
                   "Error message: {0}".format(e))
            raise Exception(msg)

        self._add_block(model)

        for var, n_ref in self._referenced_variables.items():
            if n_ref != 0:
                if var.fixed:
                    if not self._output_fixed_variable_bounds:
                        raise ValueError(
                            "Encountered a fixed variable (%s) inside "
                            "an active objective or constraint "
                            "expression on model %s, which is usually "
                            "indicative of a preprocessing error. Use "
                            "the IO-option 'output_fixed_variable_bounds=True' "
                            "to suppress this error and fix the variable "
                            "by overwriting its bounds in the Gurobi instance."
                            % (var.name, self._pyomo_model.name,))
Ejemplo n.º 45
0
 def __init__(self, **kwds):
     kwds['type'] = 'gurobi_direct'
     DirectSolver.__init__(self, **kwds)
     self._pyomo_var_to_solver_var_map = ComponentMap()
     self._solver_var_to_pyomo_var_map = ComponentMap()
     self._pyomo_con_to_solver_con_map = dict()
     self._solver_con_to_pyomo_con_map = ComponentMap()
     self._init()
Ejemplo n.º 46
0
    def test_eq(self):
        cmap1 = ComponentMap()
        self.assertNotEqual(cmap1, set())
        self.assertFalse(cmap1 == set())
        self.assertNotEqual(cmap1, list())
        self.assertFalse(cmap1 == list())
        self.assertNotEqual(cmap1, tuple())
        self.assertFalse(cmap1 == tuple())
        self.assertEqual(cmap1, dict())
        self.assertTrue(cmap1 == dict())

        cmap1.update(self._components)
        self.assertNotEqual(cmap1, set())
        self.assertFalse(cmap1 == set())
        self.assertNotEqual(cmap1, list())
        self.assertFalse(cmap1 == list())
        self.assertNotEqual(cmap1, tuple())
        self.assertFalse(cmap1 == tuple())
        self.assertNotEqual(cmap1, dict())
        self.assertFalse(cmap1 == dict())

        self.assertTrue(cmap1 == cmap1)
        self.assertEqual(cmap1, cmap1)

        cmap2 = ComponentMap(self._components)
        self.assertTrue(cmap2 == cmap1)
        self.assertFalse(cmap2 != cmap1)
        self.assertEqual(cmap2, cmap1)
        self.assertTrue(cmap1 == cmap2)
        self.assertFalse(cmap1 != cmap2)
        self.assertEqual(cmap1, cmap2)

        del cmap2[self._components[0][0]]
        self.assertFalse(cmap2 == cmap1)
        self.assertTrue(cmap2 != cmap1)
        self.assertNotEqual(cmap2, cmap1)
        self.assertFalse(cmap1 == cmap2)
        self.assertTrue(cmap1 != cmap2)
        self.assertNotEqual(cmap1, cmap2)
Ejemplo n.º 47
0
def detect_effectively_discrete_vars(block, equality_tolerance):
    """Detect effectively discrete variables.

    These continuous variables are the sum of discrete variables.

    """
    # Map of effectively_discrete var --> inducing constraints
    effectively_discrete = ComponentMap()

    for constr in block.component_data_objects(Constraint, active=True):
        if constr.lower is None or constr.upper is None:
            continue  # skip inequality constraints
        if fabs(value(constr.lower) - value(constr.upper)
                ) > equality_tolerance:
            continue  # not equality constriant. Skip.
        if constr.body.polynomial_degree() not in (1, 0):
            continue  # skip nonlinear expressions
        repn = generate_standard_repn(constr.body)
        if len(repn.linear_vars) < 2:
            # TODO should this be < 2 or < 1?
            # TODO we should make sure that trivial equality relations are
            # preprocessed before this, or we will end up reformulating
            # expressions that we do not need to here.
            continue
        non_discrete_vars = list(v for v in repn.linear_vars
                                 if v.is_continuous())
        if len(non_discrete_vars) == 1:
            # We know that this is an effectively discrete continuous
            # variable. Add it to our identified variable list.
            var = non_discrete_vars[0]
            inducing_constraints = effectively_discrete.get(var, [])
            inducing_constraints.append(constr)
            effectively_discrete[var] = inducing_constraints
        # TODO we should eventually also look at cases where all other
        # non_discrete_vars are effectively_discrete_vars

    return effectively_discrete
Ejemplo n.º 48
0
Archivo: port.py Proyecto: Pyomo/pyomo
    def __init__(self, component=None):
        #
        # These lines represent in-lining of the
        # following constructors:
        #   - ComponentData
        #   - NumericValue
        self._component = weakref_ref(component) if (component is not None) \
                          else None

        self.vars = {}
        self._arcs = []
        self._sources = []
        self._dests = []
        self._rules = {}
        self._splitfracs = ComponentMap()
Ejemplo n.º 49
0
def fbbt(comp, deactivate_satisfied_constraints=False, integer_tol=1e-5, infeasible_tol=1e-8):
    """
    Perform FBBT on a constraint, block, or model. For more control,
    use fbbt_con and fbbt_block. For detailed documentation, see
    the docstrings for fbbt_con and fbbt_block.

    Parameters
    ----------
    comp: pyomo.core.base.constraint.Constraint or pyomo.core.base.block.Block or pyomo.core.base.PyomoModel.ConcreteModel
    deactivate_satisfied_constraints: bool
        If deactivate_satisfied_constraints is True and a constraint is always satisfied, then the constranit
        will be deactivated
    integer_tol: float
        If the lower bound computed on a binary variable is less than or equal to integer_tol, then the
        lower bound is left at 0. Otherwise, the lower bound is increased to 1. If the upper bound computed
        on a binary variable is greater than or equal to 1-integer_tol, then the upper bound is left at 1.
        Otherwise the upper bound is decreased to 0.
    infeasible_tol: float
        If the bounds computed on the body of a constraint violate the bounds of the constraint by more than
        infeasible_tol, then the constraint is considered infeasible and an exception is raised.

    Returns
    -------
    new_var_bounds: ComponentMap
        A ComponentMap mapping from variables a tuple containing the lower and upper bounds, respectively, computed
        from FBBT.
    """
    new_var_bounds = ComponentMap()
    if comp.type() == Constraint:
        if comp.is_indexed():
            for _c in comp.values():
                _new_var_bounds = fbbt_con(comp, deactivate_satisfied_constraints=deactivate_satisfied_constraints,
                                           integer_tol=integer_tol, infeasible_tol=infeasible_tol)
                new_var_bounds.update(_new_var_bounds)
        else:
            _new_var_bounds = fbbt_con(comp, deactivate_satisfied_constraints=deactivate_satisfied_constraints,
                                       integer_tol=integer_tol, infeasible_tol=infeasible_tol)
            new_var_bounds.update(_new_var_bounds)
    elif comp.type() == Block:
        _new_var_bounds = fbbt_block(comp, deactivate_satisfied_constraints=deactivate_satisfied_constraints,
                                     integer_tol=integer_tol, infeasible_tol=infeasible_tol)
        new_var_bounds.update(_new_var_bounds)
    else:
        raise FBBTException('Cannot perform FBBT on objects of type {0}'.format(type(comp)))

    return new_var_bounds
Ejemplo n.º 50
0
class GurobiDirect(DirectSolver):

    def __init__(self, **kwds):
        kwds['type'] = 'gurobi_direct'
        DirectSolver.__init__(self, **kwds)
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = ComponentMap()
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = ComponentMap()
        self._init()

    def _init(self):
        self._name = None
        try:
            import gurobipy
            self._gurobipy = gurobipy
            self._python_api_exists = True
            self._version = self._gurobipy.gurobi.version()
            self._name = "Gurobi %s.%s%s" % self._version
            while len(self._version) < 4:
                self._version += (0,)
            self._version = self._version[:4]
            self._version_major = self._version[0]
        except ImportError:
            self._python_api_exists = False
        except Exception as e:
            # other forms of exceptions can be thrown by the gurobi python
            # import. for example, a gurobipy.GurobiError exception is thrown
            # if all tokens for Gurobi are already in use. assuming, of
            # course, the license is a token license. unfortunately, you can't
            # import without a license, which means we can't test for the
            # exception above!
            print("Import of gurobipy failed - gurobi message=" + str(e) + "\n")
            self._python_api_exists = False

        self._range_constraints = set()

        self._max_obj_degree = 2
        self._max_constraint_degree = 2

        # Note: Undefined capabilites default to None
        self._capabilities.linear = True
        self._capabilities.quadratic_objective = True
        self._capabilities.quadratic_constraint = True
        self._capabilities.integer = True
        self._capabilities.sos1 = True
        self._capabilities.sos2 = True

        # fix for compatibility with pre-5.0 Gurobi
        if self._python_api_exists and \
           (self._version_major < 5):
            self._max_constraint_degree = 1
            self._capabilities.quadratic_constraint = False

    def _apply_solver(self):
        if not self._save_results:
            for block in self._pyomo_model.block_data_objects(descend_into=True,
                                                              active=True):
                for var in block.component_data_objects(ctype=pyomo.core.base.var.Var,
                                                        descend_into=False,
                                                        active=True,
                                                        sort=False):
                    var.stale = True
        if self._tee:
            self._solver_model.setParam('OutputFlag', 1)
        else:
            self._solver_model.setParam('OutputFlag', 0)

        self._solver_model.setParam('LogFile', self._log_file)

        if self._keepfiles:
            print("Solver log file: "+self._log_file)

        # Options accepted by gurobi (case insensitive):
        # ['Cutoff', 'IterationLimit', 'NodeLimit', 'SolutionLimit', 'TimeLimit',
        #  'FeasibilityTol', 'IntFeasTol', 'MarkowitzTol', 'MIPGap', 'MIPGapAbs',
        #  'OptimalityTol', 'PSDTol', 'Method', 'PerturbValue', 'ObjScale', 'ScaleFlag',
        #  'SimplexPricing', 'Quad', 'NormAdjust', 'BarIterLimit', 'BarConvTol',
        #  'BarCorrectors', 'BarOrder', 'Crossover', 'CrossoverBasis', 'BranchDir',
        #  'Heuristics', 'MinRelNodes', 'MIPFocus', 'NodefileStart', 'NodefileDir',
        #  'NodeMethod', 'PumpPasses', 'RINS', 'SolutionNumber', 'SubMIPNodes', 'Symmetry',
        #  'VarBranch', 'Cuts', 'CutPasses', 'CliqueCuts', 'CoverCuts', 'CutAggPasses',
        #  'FlowCoverCuts', 'FlowPathCuts', 'GomoryPasses', 'GUBCoverCuts', 'ImpliedCuts',
        #  'MIPSepCuts', 'MIRCuts', 'NetworkCuts', 'SubMIPCuts', 'ZeroHalfCuts', 'ModKCuts',
        #  'Aggregate', 'AggFill', 'PreDual', 'DisplayInterval', 'IISMethod', 'InfUnbdInfo',
        #  'LogFile', 'PreCrush', 'PreDepRow', 'PreMIQPMethod', 'PrePasses', 'Presolve',
        #  'ResultFile', 'ImproveStartTime', 'ImproveStartGap', 'Threads', 'Dummy', 'OutputFlag']
        for key, option in self.options.items():
            # When options come from the pyomo command, all
            # values are string types, so we try to cast
            # them to a numeric value in the event that
            # setting the parameter fails.
            try:
                self._solver_model.setParam(key, option)
            except TypeError:
                # we place the exception handling for
                # checking the cast of option to a float in
                # another function so that we can simply
                # call raise here instead of except
                # TypeError as e / raise e, because the
                # latter does not preserve the Gurobi stack
                # trace
                if not _is_numeric(option):
                    raise
                self._solver_model.setParam(key, float(option))

        if self._version_major >= 5:
            for suffix in self._suffixes:
                if re.match(suffix, "dual"):
                    self._solver_model.setParam(self._gurobipy.GRB.Param.QCPDual, 1)

        self._solver_model.optimize()

        self._solver_model.setParam('LogFile', 'default')

        # FIXME: can we get a return code indicating if Gurobi had a significant failure?
        return Bunch(rc=None, log=None)

    def _get_expr_from_pyomo_repn(self, repn, max_degree=2):
        referenced_vars = ComponentSet()

        degree = repn.polynomial_degree()
        if (degree is None) or (degree > max_degree):
            raise DegreeError('GurobiDirect does not support expressions of degree {0}.'.format(degree))

        if len(repn.linear_vars) > 0:
            referenced_vars.update(repn.linear_vars)
            new_expr = self._gurobipy.LinExpr(repn.linear_coefs, [self._pyomo_var_to_solver_var_map[i] for i in repn.linear_vars])
        else:
            new_expr = 0.0

        for i,v in enumerate(repn.quadratic_vars):
            x,y = v
            new_expr += repn.quadratic_coefs[i] * self._pyomo_var_to_solver_var_map[x] * self._pyomo_var_to_solver_var_map[y]
            referenced_vars.add(x)
            referenced_vars.add(y)

        new_expr += repn.constant

        return new_expr, referenced_vars

    def _get_expr_from_pyomo_expr(self, expr, max_degree=2):
        if max_degree == 2:
            repn = generate_standard_repn(expr, quadratic=True)
        else:
            repn = generate_standard_repn(expr, quadratic=False)

        try:
            gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn(repn, max_degree)
        except DegreeError as e:
            msg = e.args[0]
            msg += '\nexpr: {0}'.format(expr)
            raise DegreeError(msg)

        return gurobi_expr, referenced_vars

    def _add_var(self, var):
        varname = self._symbol_map.getSymbol(var, self._labeler)
        vtype = self._gurobi_vtype_from_var(var)
        if var.has_lb():
            lb = value(var.lb)
        else:
            lb = -self._gurobipy.GRB.INFINITY
        if var.has_ub():
            ub = value(var.ub)
        else:
            ub = self._gurobipy.GRB.INFINITY

        gurobipy_var = self._solver_model.addVar(lb=lb, ub=ub, vtype=vtype, name=varname)

        self._pyomo_var_to_solver_var_map[var] = gurobipy_var
        self._solver_var_to_pyomo_var_map[gurobipy_var] = var
        self._referenced_variables[var] = 0

        if var.is_fixed():
            gurobipy_var.setAttr('lb', var.value)
            gurobipy_var.setAttr('ub', var.value)

    def _set_instance(self, model, kwds={}):
        self._range_constraints = set()
        DirectOrPersistentSolver._set_instance(self, model, kwds)
        self._pyomo_con_to_solver_con_map = dict()
        self._solver_con_to_pyomo_con_map = ComponentMap()
        self._pyomo_var_to_solver_var_map = ComponentMap()
        self._solver_var_to_pyomo_var_map = ComponentMap()
        try:
            if model.name is not None:
                self._solver_model = self._gurobipy.Model(model.name)
            else:
                self._solver_model = self._gurobipy.Model()
        except Exception:
            e = sys.exc_info()[1]
            msg = ("Unable to create Gurobi model. "
                   "Have you installed the Python "
                   "bindings for Gurboi?\n\n\t"+
                   "Error message: {0}".format(e))
            raise Exception(msg)

        self._add_block(model)

        for var, n_ref in self._referenced_variables.items():
            if n_ref != 0:
                if var.fixed:
                    if not self._output_fixed_variable_bounds:
                        raise ValueError(
                            "Encountered a fixed variable (%s) inside "
                            "an active objective or constraint "
                            "expression on model %s, which is usually "
                            "indicative of a preprocessing error. Use "
                            "the IO-option 'output_fixed_variable_bounds=True' "
                            "to suppress this error and fix the variable "
                            "by overwriting its bounds in the Gurobi instance."
                            % (var.name, self._pyomo_model.name,))

    def _add_block(self, block):
        DirectOrPersistentSolver._add_block(self, block)
        self._solver_model.update()

    def _add_constraint(self, con):
        if not con.active:
            return None

        if is_fixed(con.body):
            if self._skip_trivial_constraints:
                return None

        conname = self._symbol_map.getSymbol(con, self._labeler)

        if con._linear_canonical_form:
            gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn(
                con.canonical_form(),
                self._max_constraint_degree)
        #elif isinstance(con, LinearCanonicalRepn):
        #    gurobi_expr, referenced_vars = self._get_expr_from_pyomo_repn(
        #        con,
        #        self._max_constraint_degree)
        else:
            gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(
                con.body,
                self._max_constraint_degree)

        if con.has_lb():
            if not is_fixed(con.lower):
                raise ValueError("Lower bound of constraint {0} "
                                 "is not constant.".format(con))
        if con.has_ub():
            if not is_fixed(con.upper):
                raise ValueError("Upper bound of constraint {0} "
                                 "is not constant.".format(con))

        if con.equality:
            gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr,
                                                        sense=self._gurobipy.GRB.EQUAL,
                                                        rhs=value(con.lower),
                                                        name=conname)
        elif con.has_lb() and con.has_ub():
            gurobipy_con = self._solver_model.addRange(gurobi_expr,
                                                       value(con.lower),
                                                       value(con.upper),
                                                       name=conname)
            self._range_constraints.add(con)
        elif con.has_lb():
            gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr,
                                                        sense=self._gurobipy.GRB.GREATER_EQUAL,
                                                        rhs=value(con.lower),
                                                        name=conname)
        elif con.has_ub():
            gurobipy_con = self._solver_model.addConstr(lhs=gurobi_expr,
                                                        sense=self._gurobipy.GRB.LESS_EQUAL,
                                                        rhs=value(con.upper),
                                                        name=conname)
        else:
            raise ValueError("Constraint does not have a lower "
                             "or an upper bound: {0} \n".format(con))

        for var in referenced_vars:
            self._referenced_variables[var] += 1
        self._vars_referenced_by_con[con] = referenced_vars
        self._pyomo_con_to_solver_con_map[con] = gurobipy_con
        self._solver_con_to_pyomo_con_map[gurobipy_con] = con

    def _add_sos_constraint(self, con):
        if not con.active:
            return None

        conname = self._symbol_map.getSymbol(con, self._labeler)
        level = con.level
        if level == 1:
            sos_type = self._gurobipy.GRB.SOS_TYPE1
        elif level == 2:
            sos_type = self._gurobipy.GRB.SOS_TYPE2
        else:
            raise ValueError("Solver does not support SOS "
                             "level {0} constraints".format(level))

        gurobi_vars = []
        weights = []

        self._vars_referenced_by_con[con] = ComponentSet()

        if hasattr(con, 'get_items'):
            # aml sos constraint
            sos_items = list(con.get_items())
        else:
            # kernel sos constraint
            sos_items = list(con.items())

        for v, w in sos_items:
            self._vars_referenced_by_con[con].add(v)
            gurobi_vars.append(self._pyomo_var_to_solver_var_map[v])
            self._referenced_variables[v] += 1
            weights.append(w)

        gurobipy_con = self._solver_model.addSOS(sos_type, gurobi_vars, weights)
        self._pyomo_con_to_solver_con_map[con] = gurobipy_con
        self._solver_con_to_pyomo_con_map[gurobipy_con] = con

    def _gurobi_vtype_from_var(self, var):
        """
        This function takes a pyomo variable and returns the appropriate gurobi variable type
        :param var: pyomo.core.base.var.Var
        :return: gurobipy.GRB.CONTINUOUS or gurobipy.GRB.BINARY or gurobipy.GRB.INTEGER
        """
        if var.is_binary():
            vtype = self._gurobipy.GRB.BINARY
        elif var.is_integer():
            vtype = self._gurobipy.GRB.INTEGER
        elif var.is_continuous():
            vtype = self._gurobipy.GRB.CONTINUOUS
        else:
            raise ValueError('Variable domain type is not recognized for {0}'.format(var.domain))
        return vtype

    def _set_objective(self, obj):
        if self._objective is not None:
            for var in self._vars_referenced_by_obj:
                self._referenced_variables[var] -= 1
            self._vars_referenced_by_obj = ComponentSet()
            self._objective = None

        if obj.active is False:
            raise ValueError('Cannot add inactive objective to solver.')

        if obj.sense == minimize:
            sense = self._gurobipy.GRB.MINIMIZE
        elif obj.sense == maximize:
            sense = self._gurobipy.GRB.MAXIMIZE
        else:
            raise ValueError('Objective sense is not recognized: {0}'.format(obj.sense))

        gurobi_expr, referenced_vars = self._get_expr_from_pyomo_expr(obj.expr, self._max_obj_degree)

        for var in referenced_vars:
            self._referenced_variables[var] += 1

        self._solver_model.setObjective(gurobi_expr, sense=sense)
        self._objective = obj
        self._vars_referenced_by_obj = referenced_vars

    def _postsolve(self):
        # the only suffixes that we extract from GUROBI are
        # constraint duals, constraint slacks, and variable
        # reduced-costs. scan through the solver suffix list
        # and throw an exception if the user has specified
        # any others.
        extract_duals = False
        extract_slacks = False
        extract_reduced_costs = False
        for suffix in self._suffixes:
            flag = False
            if re.match(suffix, "dual"):
                extract_duals = True
                flag = True
            if re.match(suffix, "slack"):
                extract_slacks = True
                flag = True
            if re.match(suffix, "rc"):
                extract_reduced_costs = True
                flag = True
            if not flag:
                raise RuntimeError("***The gurobi_direct solver plugin cannot extract solution suffix="+suffix)

        gprob = self._solver_model
        grb = self._gurobipy.GRB
        status = gprob.Status

        if gprob.getAttr(self._gurobipy.GRB.Attr.IsMIP):
            if extract_reduced_costs:
                logger.warning("Cannot get reduced costs for MIP.")
            if extract_duals:
                logger.warning("Cannot get duals for MIP.")
            extract_reduced_costs = False
            extract_duals = False

        self.results = SolverResults()
        soln = Solution()

        self.results.solver.name = self._name
        self.results.solver.wallclock_time = gprob.Runtime

        if status == grb.LOADED:  # problem is loaded, but no solution
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Model is loaded, but no solution information is available."
            self.results.solver.termination_condition = TerminationCondition.error
            soln.status = SolutionStatus.unknown
        elif status == grb.OPTIMAL:  # optimal
            self.results.solver.status = SolverStatus.ok
            self.results.solver.termination_message = "Model was solved to optimality (subject to tolerances), " \
                                                      "and an optimal solution is available."
            self.results.solver.termination_condition = TerminationCondition.optimal
            soln.status = SolutionStatus.optimal
        elif status == grb.INFEASIBLE:
            self.results.solver.status = SolverStatus.warning
            self.results.solver.termination_message = "Model was proven to be infeasible"
            self.results.solver.termination_condition = TerminationCondition.infeasible
            soln.status = SolutionStatus.infeasible
        elif status == grb.INF_OR_UNBD:
            self.results.solver.status = SolverStatus.warning
            self.results.solver.termination_message = "Problem proven to be infeasible or unbounded."
            self.results.solver.termination_condition = TerminationCondition.infeasibleOrUnbounded
            soln.status = SolutionStatus.unsure
        elif status == grb.UNBOUNDED:
            self.results.solver.status = SolverStatus.warning
            self.results.solver.termination_message = "Model was proven to be unbounded."
            self.results.solver.termination_condition = TerminationCondition.unbounded
            soln.status = SolutionStatus.unbounded
        elif status == grb.CUTOFF:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimal objective for model was proven to be worse than the " \
                                                      "value specified in the Cutoff parameter. No solution " \
                                                      "information is available."
            self.results.solver.termination_condition = TerminationCondition.minFunctionValue
            soln.status = SolutionStatus.unknown
        elif status == grb.ITERATION_LIMIT:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimization terminated because the total number of simplex " \
                                                      "iterations performed exceeded the value specified in the " \
                                                      "IterationLimit parameter."
            self.results.solver.termination_condition = TerminationCondition.maxIterations
            soln.status = SolutionStatus.stoppedByLimit
        elif status == grb.NODE_LIMIT:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimization terminated because the total number of " \
                                                      "branch-and-cut nodes explored exceeded the value specified " \
                                                      "in the NodeLimit parameter"
            self.results.solver.termination_condition = TerminationCondition.maxEvaluations
            soln.status = SolutionStatus.stoppedByLimit
        elif status == grb.TIME_LIMIT:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimization terminated because the time expended exceeded " \
                                                      "the value specified in the TimeLimit parameter."
            self.results.solver.termination_condition = TerminationCondition.maxTimeLimit
            soln.status = SolutionStatus.stoppedByLimit
        elif status == grb.SOLUTION_LIMIT:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimization terminated because the number of solutions found " \
                                                      "reached the value specified in the SolutionLimit parameter."
            self.results.solver.termination_condition = TerminationCondition.unknown
            soln.status = SolutionStatus.stoppedByLimit
        elif status == grb.INTERRUPTED:
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "Optimization was terminated by the user."
            self.results.solver.termination_condition = TerminationCondition.error
            soln.status = SolutionStatus.error
        elif status == grb.NUMERIC:
            self.results.solver.status = SolverStatus.error
            self.results.solver.termination_message = "Optimization was terminated due to unrecoverable numerical " \
                                                      "difficulties."
            self.results.solver.termination_condition = TerminationCondition.error
            soln.status = SolutionStatus.error
        elif status == grb.SUBOPTIMAL:
            self.results.solver.status = SolverStatus.warning
            self.results.solver.termination_message = "Unable to satisfy optimality tolerances; a sub-optimal " \
                                                      "solution is available."
            self.results.solver.termination_condition = TerminationCondition.other
            soln.status = SolutionStatus.feasible
        # note that USER_OBJ_LIMIT was added in Gurobi 7.0, so it may not be present
        elif (status is not None) and \
             (status == getattr(grb,'USER_OBJ_LIMIT',None)):
            self.results.solver.status = SolverStatus.aborted
            self.results.solver.termination_message = "User specified an objective limit " \
                                                      "(a bound on either the best objective " \
                                                      "or the best bound), and that limit has " \
                                                      "been reached. Solution is available."
            self.results.solver.termination_condition = TerminationCondition.other
            soln.status = SolutionStatus.stoppedByLimit
        else:
            self.results.solver.status = SolverStatus.error
            self.results.solver.termination_message = \
                ("Unhandled Gurobi solve status "
                 "("+str(status)+")")
            self.results.solver.termination_condition = TerminationCondition.error
            soln.status = SolutionStatus.error

        self.results.problem.name = gprob.ModelName

        if gprob.ModelSense == 1:
            self.results.problem.sense = minimize
        elif gprob.ModelSense == -1:
            self.results.problem.sense = maximize
        else:
            raise RuntimeError('Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense))

        self.results.problem.upper_bound = None
        self.results.problem.lower_bound = None
        if (gprob.NumBinVars + gprob.NumIntVars) == 0:
            try:
                self.results.problem.upper_bound = gprob.ObjVal
                self.results.problem.lower_bound = gprob.ObjVal
            except (self._gurobipy.GurobiError, AttributeError):
                pass
        elif gprob.ModelSense == 1:  # minimizing
            try:
                self.results.problem.upper_bound = gprob.ObjVal
            except (self._gurobipy.GurobiError, AttributeError):
                pass
            try:
                self.results.problem.lower_bound = gprob.ObjBound
            except (self._gurobipy.GurobiError, AttributeError):
                pass
        elif gprob.ModelSense == -1:  # maximizing
            try:
                self.results.problem.upper_bound = gprob.ObjBound
            except (self._gurobipy.GurobiError, AttributeError):
                pass
            try:
                self.results.problem.lower_bound = gprob.ObjVal
            except (self._gurobipy.GurobiError, AttributeError):
                pass
        else:
            raise RuntimeError('Unrecognized gurobi objective sense: {0}'.format(gprob.ModelSense))

        try:
            soln.gap = self.results.problem.upper_bound - self.results.problem.lower_bound
        except TypeError:
            soln.gap = None

        self.results.problem.number_of_constraints = gprob.NumConstrs + gprob.NumQConstrs + gprob.NumSOS
        self.results.problem.number_of_nonzeros = gprob.NumNZs
        self.results.problem.number_of_variables = gprob.NumVars
        self.results.problem.number_of_binary_variables = gprob.NumBinVars
        self.results.problem.number_of_integer_variables = gprob.NumIntVars
        self.results.problem.number_of_continuous_variables = gprob.NumVars - gprob.NumIntVars - gprob.NumBinVars
        self.results.problem.number_of_objectives = 1
        self.results.problem.number_of_solutions = gprob.SolCount

        # if a solve was stopped by a limit, we still need to check to
        # see if there is a solution available - this may not always
        # be the case, both in LP and MIP contexts.
        if self._save_results:
            """
            This code in this if statement is only needed for backwards compatability. It is more efficient to set
            _save_results to False and use load_vars, load_duals, etc.
            """
            if gprob.SolCount > 0:
                soln_variables = soln.variable
                soln_constraints = soln.constraint

                gurobi_vars = self._solver_model.getVars()
                gurobi_vars = list(set(gurobi_vars).intersection(set(self._pyomo_var_to_solver_var_map.values())))
                var_vals = self._solver_model.getAttr("X", gurobi_vars)
                names = self._solver_model.getAttr("VarName", gurobi_vars)
                for gurobi_var, val, name in zip(gurobi_vars, var_vals, names):
                    pyomo_var = self._solver_var_to_pyomo_var_map[gurobi_var]
                    if self._referenced_variables[pyomo_var] > 0:
                        pyomo_var.stale = False
                        soln_variables[name] = {"Value": val}

                if extract_reduced_costs:
                    vals = self._solver_model.getAttr("Rc", gurobi_vars)
                    for gurobi_var, val, name in zip(gurobi_vars, vals, names):
                        pyomo_var = self._solver_var_to_pyomo_var_map[gurobi_var]
                        if self._referenced_variables[pyomo_var] > 0:
                            soln_variables[name]["Rc"] = val

                if extract_duals or extract_slacks:
                    gurobi_cons = self._solver_model.getConstrs()
                    con_names = self._solver_model.getAttr("ConstrName", gurobi_cons)
                    for name in con_names:
                        soln_constraints[name] = {}
                    if self._version_major >= 5:
                        gurobi_q_cons = self._solver_model.getQConstrs()
                        q_con_names = self._solver_model.getAttr("QCName", gurobi_q_cons)
                        for name in q_con_names:
                            soln_constraints[name] = {}

                if extract_duals:
                    vals = self._solver_model.getAttr("Pi", gurobi_cons)
                    for val, name in zip(vals, con_names):
                        soln_constraints[name]["Dual"] = val
                    if self._version_major >= 5:
                        q_vals = self._solver_model.getAttr("QCPi", gurobi_q_cons)
                        for val, name in zip(q_vals, q_con_names):
                            soln_constraints[name]["Dual"] = val

                if extract_slacks:
                    gurobi_range_con_vars = set(self._solver_model.getVars()) - set(self._pyomo_var_to_solver_var_map.values())
                    vals = self._solver_model.getAttr("Slack", gurobi_cons)
                    for gurobi_con, val, name in zip(gurobi_cons, vals, con_names):
                        pyomo_con = self._solver_con_to_pyomo_con_map[gurobi_con]
                        if pyomo_con in self._range_constraints:
                            lin_expr = self._solver_model.getRow(gurobi_con)
                            for i in reversed(range(lin_expr.size())):
                                v = lin_expr.getVar(i)
                                if v in gurobi_range_con_vars:
                                    Us_ = v.X
                                    Ls_ = v.UB - v.X
                                    if Us_ > Ls_:
                                        soln_constraints[name]["Slack"] = Us_
                                    else:
                                        soln_constraints[name]["Slack"] = -Ls_
                                    break
                        else:
                            soln_constraints[name]["Slack"] = val
                    if self._version_major >= 5:
                        q_vals = self._solver_model.getAttr("QCSlack", gurobi_q_cons)
                        for val, name in zip(q_vals, q_con_names):
                            soln_constraints[name]["Slack"] = val
        elif self._load_solutions:
            if gprob.SolCount > 0:

                self._load_vars()

                if extract_reduced_costs:
                    self._load_rc()

                if extract_duals:
                    self._load_duals()

                if extract_slacks:
                    self._load_slacks()

        self.results.solution.insert(soln)

        # finally, clean any temporary files registered with the temp file
        # manager, created populated *directly* by this plugin.
        TempfileManager.pop(remove=not self._keepfiles)

        return DirectOrPersistentSolver._postsolve(self)

    def warm_start_capable(self):
        return True

    def _warm_start(self):
        for pyomo_var, gurobipy_var in self._pyomo_var_to_solver_var_map.items():
            if pyomo_var.value is not None:
                gurobipy_var.setAttr(self._gurobipy.GRB.Attr.Start, value(pyomo_var))

    def _load_vars(self, vars_to_load=None):
        var_map = self._pyomo_var_to_solver_var_map
        ref_vars = self._referenced_variables
        if vars_to_load is None:
            vars_to_load = var_map.keys()

        gurobi_vars_to_load = [var_map[pyomo_var] for pyomo_var in vars_to_load]
        vals = self._solver_model.getAttr("X", gurobi_vars_to_load)

        for var, val in zip(vars_to_load, vals):
            if ref_vars[var] > 0:
                var.stale = False
                var.value = val

    def _load_rc(self, vars_to_load=None):
        if not hasattr(self._pyomo_model, 'rc'):
            self._pyomo_model.rc = Suffix(direction=Suffix.IMPORT)
        var_map = self._pyomo_var_to_solver_var_map
        ref_vars = self._referenced_variables
        rc = self._pyomo_model.rc
        if vars_to_load is None:
            vars_to_load = var_map.keys()

        gurobi_vars_to_load = [var_map[pyomo_var] for pyomo_var in vars_to_load]
        vals = self._solver_model.getAttr("Rc", gurobi_vars_to_load)

        for var, val in zip(vars_to_load, vals):
            if ref_vars[var] > 0:
                rc[var] = val

    def _load_duals(self, cons_to_load=None):
        if not hasattr(self._pyomo_model, 'dual'):
            self._pyomo_model.dual = Suffix(direction=Suffix.IMPORT)
        con_map = self._pyomo_con_to_solver_con_map
        reverse_con_map = self._solver_con_to_pyomo_con_map
        dual = self._pyomo_model.dual

        if cons_to_load is None:
            linear_cons_to_load = self._solver_model.getConstrs()
            if self._version_major >= 5:
                quadratic_cons_to_load = self._solver_model.getQConstrs()
        else:
            gurobi_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load])
            linear_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getConstrs()))
            if self._version_major >= 5:
                quadratic_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getQConstrs()))
        linear_vals = self._solver_model.getAttr("Pi", linear_cons_to_load)
        if self._version_major >= 5:
            quadratic_vals = self._solver_model.getAttr("QCPi", quadratic_cons_to_load)

        for gurobi_con, val in zip(linear_cons_to_load, linear_vals):
            pyomo_con = reverse_con_map[gurobi_con]
            dual[pyomo_con] = val
        if self._version_major >= 5:
            for gurobi_con, val in zip(quadratic_cons_to_load, quadratic_vals):
                pyomo_con = reverse_con_map[gurobi_con]
                dual[pyomo_con] = val

    def _load_slacks(self, cons_to_load=None):
        if not hasattr(self._pyomo_model, 'slack'):
            self._pyomo_model.slack = Suffix(direction=Suffix.IMPORT)
        con_map = self._pyomo_con_to_solver_con_map
        reverse_con_map = self._solver_con_to_pyomo_con_map
        slack = self._pyomo_model.slack

        gurobi_range_con_vars = set(self._solver_model.getVars()) - set(self._pyomo_var_to_solver_var_map.values())

        if cons_to_load is None:
            linear_cons_to_load = self._solver_model.getConstrs()
            if self._version_major >= 5:
                quadratic_cons_to_load = self._solver_model.getQConstrs()
        else:
            gurobi_cons_to_load = set([con_map[pyomo_con] for pyomo_con in cons_to_load])
            linear_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getConstrs()))
            if self._version_major >= 5:
                quadratic_cons_to_load = gurobi_cons_to_load.intersection(set(self._solver_model.getQConstrs()))
        linear_vals = self._solver_model.getAttr("Slack", linear_cons_to_load)
        if self._version_major >= 5:
            quadratic_vals = self._solver_model.getAttr("QCSlack", quadratic_cons_to_load)

        for gurobi_con, val in zip(linear_cons_to_load, linear_vals):
            pyomo_con = reverse_con_map[gurobi_con]
            if pyomo_con in self._range_constraints:
                lin_expr = self._solver_model.getRow(gurobi_con)
                for i in reversed(range(lin_expr.size())):
                    v = lin_expr.getVar(i)
                    if v in gurobi_range_con_vars:
                        Us_ = v.X
                        Ls_ = v.UB - v.X
                        if Us_ > Ls_:
                            slack[pyomo_con] = Us_
                        else:
                            slack[pyomo_con] = -Ls_
                        break
            else:
                slack[pyomo_con] = val
        if self._version_major >= 5:
            for gurobi_con, val in zip(quadratic_cons_to_load, quadratic_vals):
                pyomo_con = reverse_con_map[gurobi_con]
                slack[pyomo_con] = val

    def load_duals(self, cons_to_load=None):
        """
        Load the duals into the 'dual' suffix. The 'dual' suffix must live on the parent model.

        Parameters
        ----------
        cons_to_load: list of Constraint
        """
        self._load_duals(cons_to_load)

    def load_rc(self, vars_to_load):
        """
        Load the reduced costs into the 'rc' suffix. The 'rc' suffix must live on the parent model.

        Parameters
        ----------
        vars_to_load: list of Var
        """
        self._load_rc(vars_to_load)

    def load_slacks(self, cons_to_load=None):
        """
        Load the values of the slack variables into the 'slack' suffix. The 'slack' suffix must live on the parent
        model.

        Parameters
        ----------
        cons_to_load: list of Constraint
        """
        self._load_slacks(cons_to_load)
Ejemplo n.º 51
0
Archivo: port.py Proyecto: Pyomo/pyomo
class _PortData(ComponentData):
    """
    This class defines the data for a single Port

    Attributes
    ----------
        vars:`dict`
            A dictionary mapping added names to variables
    """

    __slots__ = ('vars', '_arcs', '_sources', '_dests', '_rules', '_splitfracs')

    def __init__(self, component=None):
        #
        # These lines represent in-lining of the
        # following constructors:
        #   - ComponentData
        #   - NumericValue
        self._component = weakref_ref(component) if (component is not None) \
                          else None

        self.vars = {}
        self._arcs = []
        self._sources = []
        self._dests = []
        self._rules = {}
        self._splitfracs = ComponentMap()

    def __getstate__(self):
        state = super(_PortData, self).__getstate__()
        for i in _PortData.__slots__:
            state[i] = getattr(self, i)
        return state

    # Note: None of the slots on this class need to be edited, so we
    # don't need to implement a specialized __setstate__ method, and
    # can quietly rely on the super() class's implementation.

    def __getattr__(self, name):
        """Returns `self.vars[name]` if it exists"""
        if name in self.vars:
            return self.vars[name]
        # Since the base classes don't support getattr, we can just
        # throw the "normal" AttributeError
        raise AttributeError("'%s' object has no attribute '%s'"
                             % (self.__class__.__name__, name))

    def arcs(self, active=None):
        """A list of Arcs in which this Port is a member"""
        return self._collect_ports(active, self._arcs)

    def sources(self, active=None):
        """A list of Arcs in which this Port is a destination"""
        return self._collect_ports(active, self._sources)

    def dests(self, active=None):
        """A list of Arcs in which this Port is a source"""
        return self._collect_ports(active, self._dests)

    def _collect_ports(self, active, port_list):
        # need to call the weakrefs
        if active is None:
            return [_a() for _a in port_list]
        tmp = []
        for _a in port_list:
            a = _a()
            if a.active == active:
                tmp.append(a)
        return tmp

    def set_value(self, value):
        """Cannot specify the value of a port"""
        raise ValueError("Cannot specify the value of a port: '%s'" % self.name)

    def polynomial_degree(self):
        """Returns the maximum polynomial degree of all port members"""
        ans = 0
        for v in self.iter_vars():
            tmp = v.polynomial_degree()
            if tmp is None:
                return None
            ans = max(ans, tmp)
        return ans

    def is_fixed(self):
        """Return True if all vars/expressions in the Port are fixed"""
        return all(v.is_fixed() for v in self.iter_vars())

    def is_potentially_variable(self):
        """Return True as ports may (should!) contain variables"""
        return True

    def is_binary(self):
        """Return True if all variables in the Port are binary"""
        return len(self) and all(
            v.is_binary() for v in self.iter_vars(expr_vars=True))

    def is_integer(self):
        """Return True if all variables in the Port are integer"""
        return len(self) and all(
            v.is_integer() for v in self.iter_vars(expr_vars=True))

    def is_continuous(self):
        """Return True if all variables in the Port are continuous"""
        return len(self) and all(
            v.is_continuous() for v in self.iter_vars(expr_vars=True))

    def add(self, var, name=None, rule=None, **kwds):
        """
        Add `var` to this Port, casting it to a Pyomo numeric if necessary

        Arguments
        ---------
            var
                A variable or some `NumericValue` like an expression
            name: `str`
                Name to associate with this member of the Port
            rule: `function`
                Function implementing the desired expansion procedure
                for this member. `Port.Equality` by default, other
                options include `Port.Extensive`. Customs are allowed.
            kwds
                Keyword arguments that will be passed to rule
        """
        if var is not None:
            try:
                # indexed components are ok, but as_numeric will error on them
                # make sure they have this attribute
                var.is_indexed()
            except AttributeError:
                var = as_numeric(var)
        if name is None:
            name = var.local_name
        if name in self.vars and self.vars[name] is not None:
            # don't throw warning if replacing an implicit (None) var
            logger.warning("Implicitly replacing variable '%s' in Port '%s'.\n"
                           "To avoid this warning, use Port.remove() first."
                           % (name, self.name))
        self.vars[name] = var
        if rule is None:
            rule = Port.Equality
        if rule is Port.Extensive:
            # avoid name collisions
            if (name.endswith("_split") or name.endswith("_equality") or
                    name == "splitfrac"):
                raise ValueError(
                    "Extensive variable '%s' on Port '%s' may not end "
                    "with '_split' or '_equality'" % (name, self.name))
        self._rules[name] = (rule, kwds)

    def remove(self, name):
        """Remove this member from the port"""
        if name not in self.vars:
            raise ValueError("Cannot remove member '%s' not in Port '%s'"
                             % (name, self.name))
        self.vars.pop(name)
        self._rules.pop(name)

    def rule_for(self, name):
        """Return the rule associated with the given port member"""
        return self._rules[name][0]

    def is_equality(self, name):
        """Return True if the rule for this port member is Port.Equality"""
        return self.rule_for(name) is Port.Equality

    def is_extensive(self, name):
        """Return True if the rule for this port member is Port.Extensive"""
        return self.rule_for(name) is Port.Extensive

    def fix(self):
        """
        Fix all variables in the port at their current values.
        For expressions, fix every variable in the expression.
        """
        for v in self.iter_vars(expr_vars=True, fixed=False):
            v.fix()

    def unfix(self):
        """
        Unfix all variables in the port.
        For expressions, unfix every variable in the expression.
        """
        for v in self.iter_vars(expr_vars=True, fixed=True):
            v.unfix()

    free = unfix

    def iter_vars(self, expr_vars=False, fixed=None, names=False):
        """
        Iterate through every member of the port, going through
        the indices of indexed members.

        Arguments
        ---------
            expr_vars: `bool`
                If True, call `identify_variables` on expression type members
            fixed: `bool`
                Only include variables/expressions with this type of fixed
            names: `bool`
                If True, yield (name, var/expr) pairs
        """
        for name, mem in iteritems(self.vars):
            if not mem.is_indexed():
                itr = (mem,)
            else:
                itr = itervalues(mem)
            for v in itr:
                if fixed is not None and v.is_fixed() != fixed:
                    continue
                if expr_vars and v.is_expression_type():
                    for var in identify_variables(v):
                        if fixed is not None and var.is_fixed() != fixed:
                            continue
                        if names:
                            yield name, var
                        else:
                            yield var
                else:
                    if names:
                        yield name, v
                    else:
                        yield v

    def set_split_fraction(self, arc, val, fix=True):
        """
        Set the split fraction value to be used for an arc during
        arc expansion when using `Port.Extensive`.
        """
        if arc not in self.dests():
            raise ValueError("Port '%s' is not a source of Arc '%s', cannot "
                             "set split fraction" % (self.name, arc.name))
        self._splitfracs[arc] = (val, fix)

    def get_split_fraction(self, arc):
        """
        Returns a tuple (val, fix) for the split fraction of this arc that
        was set via `set_split_fraction` if it exists, and otherwise None.
        """
        res = self._splitfracs.get(arc, None)
        if res is None:
            return None
        else:
            return res
Ejemplo n.º 52
0
 def test_keys(self):
     cmap = ComponentMap(self._components)
     self.assertEqual(sorted(cmap.keys(), key=id),
                      sorted(list(c for c,val in self._components),
                             key=id))
Ejemplo n.º 53
0
def fbbt_block(m, tol=1e-4, deactivate_satisfied_constraints=False, integer_tol=1e-5, infeasible_tol=1e-8):
    """
    Feasibility based bounds tightening (FBBT) for a block or model. This
    loops through all of the constraints in the block and performs
    FBBT on each constraint (see the docstring for fbbt_con()).
    Through this processes, any variables whose bounds improve
    by more than tol are collected, and FBBT is
    performed again on all constraints involving those variables.
    This process is continued until no variable bounds are improved
    by more than tol.

    Parameters
    ----------
    m: pyomo.core.base.block.Block or pyomo.core.base.PyomoModel.ConcreteModel
    tol: float
    deactivate_satisfied_constraints: bool
        If deactivate_satisfied_constraints is True and a constraint is always satisfied, then the constranit
        will be deactivated
    integer_tol: float
        If the lower bound computed on a binary variable is less than or equal to integer_tol, then the
        lower bound is left at 0. Otherwise, the lower bound is increased to 1. If the upper bound computed
        on a binary variable is greater than or equal to 1-integer_tol, then the upper bound is left at 1.
        Otherwise the upper bound is decreased to 0.
    infeasible_tol: float
        If the bounds computed on the body of a constraint violate the bounds of the constraint by more than
        infeasible_tol, then the constraint is considered infeasible and an exception is raised.

    Returns
    -------
    new_var_bounds: ComponentMap
        A ComponentMap mapping from variables a tuple containing the lower and upper bounds, respectively, computed
        from FBBT.
    """
    new_var_bounds = ComponentMap()
    var_to_con_map = ComponentMap()
    var_lbs = ComponentMap()
    var_ubs = ComponentMap()
    for c in m.component_data_objects(ctype=Constraint, active=True,
                                      descend_into=True, sort=True):
        for v in identify_variables(c.body):
            if v not in var_to_con_map:
                var_to_con_map[v] = list()
            if v.lb is None:
                var_lbs[v] = -math.inf
            else:
                var_lbs[v] = value(v.lb)
            if v.ub is None:
                var_ubs[v] = math.inf
            else:
                var_ubs[v] = value(v.ub)
            var_to_con_map[v].append(c)

    for _v in m.component_data_objects(ctype=Var, active=True, descend_into=True, sort=True):
        if _v.is_fixed():
            _v.setlb(_v.value)
            _v.setub(_v.value)
            new_var_bounds[_v] = (_v.value, _v.value)

    improved_vars = ComponentSet()
    for c in m.component_data_objects(ctype=Constraint, active=True,
                                      descend_into=True, sort=True):
        _new_var_bounds = fbbt_con(c, deactivate_satisfied_constraints=deactivate_satisfied_constraints,
                                   integer_tol=integer_tol, infeasible_tol=infeasible_tol)
        new_var_bounds.update(_new_var_bounds)
        for v, bnds in _new_var_bounds.items():
            vlb, vub = bnds
            if vlb is not None:
                if vlb > var_lbs[v] + tol:
                    improved_vars.add(v)
                    var_lbs[v] = vlb
            if vub is not None:
                if vub < var_ubs[v] - tol:
                    improved_vars.add(v)
                    var_ubs[v] = vub

    while len(improved_vars) > 0:
        v = improved_vars.pop()
        for c in var_to_con_map[v]:
            _new_var_bounds = fbbt_con(c, deactivate_satisfied_constraints=deactivate_satisfied_constraints,
                                       integer_tol=integer_tol, infeasible_tol=infeasible_tol)
            new_var_bounds.update(_new_var_bounds)
            for _v, bnds in _new_var_bounds.items():
                _vlb, _vub = bnds
                if _vlb is not None:
                    if _vlb > var_lbs[_v] + tol:
                        improved_vars.add(_v)
                        var_lbs[_v] = _vlb
                if _vub is not None:
                    if _vub < var_ubs[_v] - tol:
                        improved_vars.add(_v)
                        var_ubs[_v] = _vub

    return new_var_bounds
Ejemplo n.º 54
0
def fbbt_con(con, deactivate_satisfied_constraints=False, integer_tol=1e-5, infeasible_tol=1e-8):
    """
    Feasibility based bounds tightening for a constraint. This function attempts to improve the bounds of each variable
    in the constraint based on the bounds of the constraint and the bounds of the other variables in the constraint.
    For example:

    >>> import pyomo.environ as pe
    >>> from pyomo.contrib.fbbt.fbbt import fbbt
    >>> m = pe.ConcreteModel()
    >>> m.x = pe.Var(bounds=(-1,1))
    >>> m.y = pe.Var(bounds=(-2,2))
    >>> m.z = pe.Var()
    >>> m.c = pe.Constraint(expr=m.x*m.y + m.z == 1)
    >>> fbbt(m.c)
    >>> print(m.z.lb, m.z.ub)
    -1.0 3.0

    Parameters
    ----------
    con: pyomo.core.base.constraint.Constraint
        constraint on which to perform fbbt
    deactivate_satisfied_constraints: bool
        If deactivate_satisfied_constraints is True and the constraint is always satisfied, then the constranit
        will be deactivated
    integer_tol: float
        If the lower bound computed on a binary variable is less than or equal to integer_tol, then the
        lower bound is left at 0. Otherwise, the lower bound is increased to 1. If the upper bound computed
        on a binary variable is greater than or equal to 1-integer_tol, then the upper bound is left at 1.
        Otherwise the upper bound is decreased to 0.
    infeasible_tol: float
        If the bounds computed on the body of a constraint violate the bounds of the constraint by more than
        infeasible_tol, then the constraint is considered infeasible and an exception is raised.

    Returns
    -------
    new_var_bounds: ComponentMap
        A ComponentMap mapping from variables a tuple containing the lower and upper bounds, respectively, computed
        from FBBT.
    """
    if not con.active:
        return ComponentMap()

    bnds_dict = ComponentMap()  # a dictionary to store the bounds of every node in the tree

    # a walker to propagate bounds from the variables to the root
    visitorA = _FBBTVisitorLeafToRoot(bnds_dict)
    visitorA.dfs_postorder_stack(con.body)

    # Now we need to replace the bounds in bnds_dict for the root
    # node with the bounds on the constraint (if those bounds are
    # better).
    _lb = value(con.lower)
    _ub = value(con.upper)
    if _lb is None:
        _lb = -math.inf
    if _ub is None:
        _ub = math.inf

    lb, ub = bnds_dict[con.body]

    # check if the constraint is infeasible
    if lb > _ub + infeasible_tol or ub < _lb - infeasible_tol:
        raise InfeasibleConstraintException('Detected an infeasible constraint during FBBT: {0}'.format(str(con)))

    # check if the constraint is always satisfied
    if deactivate_satisfied_constraints:
        if lb >= _lb and ub <= _ub:
            con.deactivate()

    if _lb > lb:
        lb = _lb
    if _ub < ub:
        ub = _ub
    bnds_dict[con.body] = (lb, ub)

    # Now, propagate bounds back from the root to the variables
    visitorB = _FBBTVisitorRootToLeaf(bnds_dict, integer_tol=integer_tol)
    visitorB.dfs_postorder_stack(con.body)

    new_var_bounds = ComponentMap()
    for _node, _bnds in bnds_dict.items():
        if _node.__class__ in nonpyomo_leaf_types:
            continue
        if _node.is_variable_type():
            lb, ub = bnds_dict[_node]
            if lb == -math.inf:
                lb = None
            if ub == math.inf:
                ub = None
            new_var_bounds[_node] = (lb, ub)
    return new_var_bounds
Ejemplo n.º 55
0
 def test_str(self):
     cmap = ComponentMap()
     self.assertEqual(str(cmap), "ComponentMap({})")
     cmap.update(self._components)
     str(cmap)
Ejemplo n.º 56
0
 def test_values(self):
     cmap = ComponentMap(self._components)
     self.assertEqual(sorted(cmap.values()),
                      sorted(list(val for c,val in self._components)))