コード例 #1
0
ファイル: induced_linearity.py プロジェクト: CanLi1/pyomo-1
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
コード例 #2
0
ファイル: var_aggregator.py プロジェクト: Pyomo/pyomo
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
コード例 #3
0
ファイル: induced_linearity.py プロジェクト: Pyomo/pyomo
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
コード例 #4
0
ファイル: var_aggregator.py プロジェクト: jotaigna/pyomo
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
コード例 #5
0
ファイル: test_component_map.py プロジェクト: pazochoa/pyomo
 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)
コード例 #6
0
ファイル: test_component_map.py プロジェクト: Pyomo/pyomo
 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)
コード例 #7
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
コード例 #8
0
ファイル: equality_propagate.py プロジェクト: Pyomo/pyomo
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
コード例 #9
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)
コード例 #10
0
ファイル: induced_linearity.py プロジェクト: CanLi1/pyomo-1
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
コード例 #11
0
ファイル: induced_linearity.py プロジェクト: Pyomo/pyomo
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
コード例 #12
0
ファイル: quadratic.py プロジェクト: michaelbynum/suspect
def _collect_expression_types(quadratic, linear):
    """Collect different expression types from quadratic and linear expression.

    Given an expression

    a0 x0^2 + a1 x1^2  + ...+ b0 x0 + b1 x1 + b2 x2 + ...

    Returns the quadratic univariate expressions like `a0 x0^2`, the bilinear
    expressions `c x1 x2`, and the linear expressions `bn xn`.

    :param quadratic: the quadratic expression
    :param linear: the linear expression
    :return:
    A tuple (univariate_expr, bilinear_terms, linear_terms)
    """
    variables_coef = ComponentMap()

    for coef, var in zip(linear.linear_coefs, linear.linear_vars):
        variables_coef[var] = \
            _VariableCoefficients(quadratic=0.0, linear=coef)

    non_univariate_terms = []
    for term in quadratic.terms:
        if term.var1 is term.var2:
            var_coef = variables_coef.get(term.var1, None)
            if var_coef is None:
                non_univariate_terms.append(term)
            else:
                linear_coef = var_coef.linear
                var_coef = _VariableCoefficients(quadratic=term.coefficient,
                                                 linear=linear_coef)
                variables_coef[term.var1] = var_coef
        else:
            non_univariate_terms.append(term)

    linear_terms = [(v, vc.linear) for v, vc in variables_coef.items()
                    if almosteq(vc.quadratic, 0.0)]
    univariate_terms = [(v, vc.quadratic, vc.linear)
                        for v, vc in variables_coef.items()
                        if not almosteq(vc.quadratic, 0.0)]

    return univariate_terms, non_univariate_terms, linear_terms
コード例 #13
0
ファイル: induced_linearity.py プロジェクト: Pyomo/pyomo
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
コード例 #14
0
ファイル: induced_linearity.py プロジェクト: CanLi1/pyomo-1
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
コード例 #15
0
ファイル: port.py プロジェクト: zy09838/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
コード例 #16
0
    def _transform_disjunctionData(self, obj, index, transBlock=None):
        if not obj.active:
            return
        # Hull reformulation doesn't work if this is an OR constraint. So if
        # xor is false, give up
        if not obj.xor:
            raise GDP_Error("Cannot do hull reformulation for "
                            "Disjunction '%s' with OR constraint.  "
                            "Must be an XOR!" % obj.name)

        if transBlock is None:
            # It's possible that we have already created a transformation block
            # for another disjunctionData from this same container. If that's
            # the case, let's use the same transformation block. (Else it will
            # be really confusing that the XOR constraint goes to that old block
            # but we create a new one here.)
            if obj.parent_component()._algebraic_constraint is not None:
                transBlock = obj.parent_component()._algebraic_constraint().\
                             parent_block()
            else:
                transBlock = self._add_transformation_block(obj.parent_block())

        parent_component = obj.parent_component()

        orConstraint = self._add_xor_constraint(parent_component, transBlock)
        disaggregationConstraint = transBlock.disaggregationConstraints
        disaggregationConstraintMap = transBlock._disaggregationConstraintMap

        # Just because it's unlikely this is what someone meant to do...
        if len(obj.disjuncts) == 0:
            raise GDP_Error(
                "Disjunction '%s' is empty. This is "
                "likely indicative of a modeling error." %
                obj.getname(fully_qualified=True, name_buffer=NAME_BUFFER))

        # We first go through and collect all the variables that we
        # are going to disaggregate.
        varOrder_set = ComponentSet()
        varOrder = []
        varsByDisjunct = ComponentMap()
        localVarsByDisjunct = ComponentMap()
        include_fixed_vars = not self._config.assume_fixed_vars_permanent
        for disjunct in obj.disjuncts:
            disjunctVars = varsByDisjunct[disjunct] = ComponentSet()
            for cons in disjunct.component_data_objects(
                    Constraint,
                    active=True,
                    sort=SortComponents.deterministic,
                    descend_into=Block):
                # [ESJ 02/14/2020] By default, we disaggregate fixed variables
                # on the philosophy that fixing is not a promise for the future
                # and we are mathematically wrong if we don't transform these
                # correctly and someone later unfixes them and keeps playing
                # with their transformed model. However, the user may have set
                # assume_fixed_vars_permanent to True in which case we will skip
                # them
                for var in EXPR.identify_variables(
                        cons.body, include_fixed=include_fixed_vars):
                    # Note the use of a list so that we will
                    # eventually disaggregate the vars in a
                    # deterministic order (the order that we found
                    # them)
                    disjunctVars.add(var)
                    if not var in varOrder_set:
                        varOrder.append(var)
                        varOrder_set.add(var)

            # check for LocalVars Suffix
            localVarsByDisjunct = self._get_local_var_suffixes(
                disjunct, localVarsByDisjunct)

        # We will disaggregate all variables which are not explicitly declared
        # as being local. Note however, that we do declare our own disaggregated
        # variables as local, so they will not be re-disaggregated.
        varSet = []
        # values of localVarsByDisjunct are ComponentSets, so we need this for
        # determinism (we iterate through the localVars later)
        localVars = []
        for var in varOrder:
            disjuncts = [d for d in varsByDisjunct if var in varsByDisjunct[d]]
            # clearly not local if used in more than one disjunct
            if len(disjuncts) > 1:
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    logger.debug("Assuming '%s' is not a local var since it is"
                                 "used in multiple disjuncts." %
                                 var.getname(fully_qualified=True,
                                             name_buffer=NAME_BUFFER))
                varSet.append(var)
            elif localVarsByDisjunct.get(disjuncts[0]) is not None:
                if var in localVarsByDisjunct[disjuncts[0]]:
                    localVars.append(var)
                else:
                    varSet.append(var)
            else:
                varSet.append(var)

        # Now that we know who we need to disaggregate, we will do it
        # while we also transform the disjuncts.
        or_expr = 0
        for disjunct in obj.disjuncts:
            or_expr += disjunct.indicator_var
            self._transform_disjunct(disjunct, transBlock, varSet, localVars)
        orConstraint.add(index, (or_expr, 1))
        # map the DisjunctionData to its XOR constraint to mark it as
        # transformed
        obj._algebraic_constraint = weakref_ref(orConstraint[index])

        for i, var in enumerate(varSet):
            disaggregatedExpr = 0
            for disjunct in obj.disjuncts:
                if disjunct._transformation_block is None:
                    # Because we called _transform_disjunct in the loop above,
                    # we know that if this isn't transformed it is because it
                    # was cleanly deactivated, and we can just skip it.
                    continue

                disaggregatedVar = disjunct._transformation_block().\
                                   _disaggregatedVarMap['disaggregatedVar'][var]
                disaggregatedExpr += disaggregatedVar

            disaggregationConstraint.add((i, index), var == disaggregatedExpr)
            # and update the map so that we can find this later. We index by
            # variable and the particular disjunction because there is a
            # different one for each disjunction
            if disaggregationConstraintMap.get(var) is not None:
                disaggregationConstraintMap[var][
                    obj] = disaggregationConstraint[(i, index)]
            else:
                thismap = disaggregationConstraintMap[var] = ComponentMap()
                thismap[obj] = disaggregationConstraint[(i, index)]

        # deactivate for the writers
        obj.deactivate()
コード例 #17
0
class _PortData(ComponentData):
    """This class defines the data for a single Port."""

    __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):
        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 len(self) and all(v.is_binary()
                                 for v in self.iter_vars(expr_vars=True))

    def is_integer(self):
        return len(self) and all(v.is_integer()
                                 for v in self.iter_vars(expr_vars=True))

    def is_continuous(self):
        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 a variable to this Port.

        Arguments:
            var         A variable or some NumericValue like an expression
            name        Name to associate with this member of the Port
            rule        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 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 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 iter_vars(self, expr_vars=False, fixed=True, with_names=False):
        """
        Iterate through every member of the port, going through
        the indices of indexed members.

        If expr_vars, call identify_variables on expression type members.
        If not fixed, exclude fixed variables/expressions.
        """
        for name, mem in iteritems(self.vars):
            if not mem.is_indexed():
                itr = (mem, )
            else:
                itr = itervalues(mem)
            for v in itr:
                if not fixed and v.is_fixed():
                    continue
                if v.is_expression_type() and expr_vars:
                    for var in identify_variables(v, include_fixed=fixed):
                        if with_names:
                            yield name, var
                        else:
                            yield var
                else:
                    if with_names:
                        yield name, v
                    else:
                        yield v

    def set_split_fraction(self, arc, val, fix=True):
        """
        Set the split fraction value for an arc 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] = dict(val=val, fix=fix)

    def get_split_fraction(self, arc):
        """
        Returns a tuple (val, fix) for the split fraction of
        this arc if it exists, and otherwise None
        """
        d = self._splitfracs.get(arc, None)
        if d is None:
            return None
        else:
            return d["val"], d["fix"]