Ejemplo n.º 1
0
    def _transform_constraint(self, obj, disjunct, bigMargs, arg_list,
                              suffix_list):
        # add constraint to the transformation block, we'll transform it there.
        transBlock = disjunct._transformation_block()
        bigm_src = transBlock.bigm_src
        constraintMap = self._get_constraint_map_dict(transBlock)
        
        disjunctionRelaxationBlock = transBlock.parent_block()
        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        cons_name = obj.getname(fully_qualified=True, name_buffer=NAME_BUFFER)
        name = unique_component_name(transBlock, cons_name)

        if obj.is_indexed():
            try:
                newConstraint = Constraint(obj.index_set(),
                                           disjunctionRelaxationBlock.lbub)
            # HACK: We get burned by #191 here... When #1319 is merged we
            # can revist this and I think stop catching the AttributeError.
            except (TypeError, AttributeError):
                # The original constraint may have been indexed by a
                # non-concrete set (like an Any).  We will give up on
                # strict index verification and just blindly proceed.
                newConstraint = Constraint(Any)
        else:
            newConstraint = Constraint(disjunctionRelaxationBlock.lbub)
        transBlock.add_component(name, newConstraint)
        # add mapping of original constraint to transformed constraint
        constraintMap['srcConstraints'][newConstraint] = obj
        constraintMap['transformedConstraints'][obj] = newConstraint

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            # first, we see if an M value was specified in the arguments.
            # (This returns None if not)
            M = self._get_M_from_args(c, bigMargs, arg_list, bigm_src)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "from the BigM argument is %s." % (cons_name,
                                                                str(M)))

            # if we didn't get something from args, try suffixes:
            if M is None:
                M = self._get_M_from_suffixes(c, suffix_list, bigm_src)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after checking suffixes is %s." % (cons_name,
                                                                 str(M)))

            if not isinstance(M, (tuple, list)):
                if M is None:
                    M = (None, None)
                else:
                    try:
                        M = (-M, M)
                    except:
                        logger.error("Error converting scalar M-value %s "
                                     "to (-M,M).  Is %s not a numeric type?"
                                     % (M, type(M)))
                        raise
            if len(M) != 2:
                raise GDP_Error("Big-M %s for constraint %s is not of "
                                "length two. "
                                "Expected either a single value or "
                                "tuple or list of length two for M."
                                % (str(M), name))

            if c.lower is not None and M[0] is None:
                M = (self._estimate_M(c.body, name)[0] - c.lower, M[1])
                bigm_src[c] = M
            if c.upper is not None and M[1] is None:
                M = (M[0], self._estimate_M(c.body, name)[1] - c.upper)
                bigm_src[c] = M

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after estimating (if needed) is %s." %
                             (cons_name, str(M)))

            # Handle indices for both SimpleConstraint and IndexedConstraint
            if i.__class__ is tuple:
                i_lb = i + ('lb',)
                i_ub = i + ('ub',)
            elif obj.is_indexed():
                i_lb = (i, 'lb',)
                i_ub = (i, 'ub',)
            else:
                i_lb = 'lb'
                i_ub = 'ub'

            if c.lower is not None:
                if M[0] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[0] * (1 - disjunct.indicator_var)
                newConstraint.add(i_lb, c.lower <= c. body - M_expr)
            if c.upper is not None:
                if M[1] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[1] * (1 - disjunct.indicator_var)
                newConstraint.add(i_ub, c.body - M_expr <= c.upper)
            # deactivate because we relaxed
            c.deactivate()
Ejemplo n.º 2
0
    def _transform_constraint(self, obj, disjunct, bigMargs, arg_list,
                              disjunct_suffix_list):
        # add constraint to the transformation block, we'll transform it there.
        transBlock = disjunct._transformation_block()
        bigm_src = transBlock.bigm_src
        constraintMap = self._get_constraint_map_dict(transBlock)

        disjunctionRelaxationBlock = transBlock.parent_block()
        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        cons_name = obj.getname(fully_qualified=True, name_buffer=NAME_BUFFER)
        name = unique_component_name(transBlock, cons_name)

        if obj.is_indexed():
            newConstraint = Constraint(obj.index_set(),
                                       disjunctionRelaxationBlock.lbub)
            # we map the container of the original to the container of the
            # transformed constraint. Don't do this if obj is a SimpleConstraint
            # because we will treat that like a _ConstraintData and map to a
            # list of transformed _ConstraintDatas
            constraintMap['transformedConstraints'][obj] = newConstraint
        else:
            newConstraint = Constraint(disjunctionRelaxationBlock.lbub)
        transBlock.add_component(name, newConstraint)
        # add mapping of transformed constraint to original constraint
        constraintMap['srcConstraints'][newConstraint] = obj

        for i in sorted(obj.keys()):
            c = obj[i]
            if not c.active:
                continue

            lower = (None, None, None)
            upper = (None, None, None)

            # first, we see if an M value was specified in the arguments.
            # (This returns None if not)
            lower, upper = self._get_M_from_args(c, bigMargs, arg_list, lower,
                                                 upper)
            M = (lower[0], upper[0])
            
            if self._generate_debug_messages:
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint '%s' "
                             "from the BigM argument is %s." % (cons_name,
                                                                str(M)))

            # if we didn't get something we need from args, try suffixes:
            if (M[0] is None and c.lower is not None) or \
               (M[1] is None and c.upper is not None):
                # first get anything parent to c but below disjunct
                suffix_list = self._get_bigm_suffix_list(c.parent_block(),
                                                         stopping_block=disjunct)
                # prepend that to what we already collected for the disjunct.
                suffix_list.extend(disjunct_suffix_list)
                lower, upper = self._update_M_from_suffixes(c, suffix_list,
                                                            lower, upper)
                M = (lower[0], upper[0])

            if self._generate_debug_messages:
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint '%s' "
                             "after checking suffixes is %s." % (cons_name,
                                                                 str(M)))

            if c.lower is not None and M[0] is None:
                M = (self._estimate_M(c.body, name)[0] - c.lower, M[1])
                lower = (M[0], None, None)
            if c.upper is not None and M[1] is None:
                M = (M[0], self._estimate_M(c.body, name)[1] - c.upper)
                upper = (M[1], None, None)

            if self._generate_debug_messages:
                _name = obj.getname(
                    fully_qualified=True, name_buffer=NAME_BUFFER)
                logger.debug("GDP(BigM): The value for M for constraint '%s' "
                             "after estimating (if needed) is %s." %
                             (cons_name, str(M)))

            # save the source information
            bigm_src[c] = (lower, upper)

            # Handle indices for both SimpleConstraint and IndexedConstraint
            if i.__class__ is tuple:
                i_lb = i + ('lb',)
                i_ub = i + ('ub',)
            elif obj.is_indexed():
                i_lb = (i, 'lb',)
                i_ub = (i, 'ub',)
            else:
                i_lb = 'lb'
                i_ub = 'ub'

            if c.lower is not None:
                if M[0] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint '%s' "
                                    "because M is not defined." % name)
                M_expr = M[0] * (1 - disjunct.indicator_var)
                newConstraint.add(i_lb, c.lower <= c. body - M_expr)
                constraintMap[
                    'transformedConstraints'][c] = [newConstraint[i_lb]]
                constraintMap['srcConstraints'][newConstraint[i_lb]] = c
            if c.upper is not None:
                if M[1] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint '%s' "
                                    "because M is not defined." % name)
                M_expr = M[1] * (1 - disjunct.indicator_var)
                newConstraint.add(i_ub, c.body - M_expr <= c.upper)
                transformed = constraintMap['transformedConstraints'].get(c)
                if transformed is not None:
                    constraintMap['transformedConstraints'][
                        c].append(newConstraint[i_ub])
                else:
                    constraintMap[
                        'transformedConstraints'][c] = [newConstraint[i_ub]]
                constraintMap['srcConstraints'][newConstraint[i_ub]] = c

            # deactivate because we relaxed
            c.deactivate()
Ejemplo n.º 3
0
    def _transform_disjunct(self, obj, transBlock, varSet, localVars):
        if hasattr(obj, "_gdp_transformation_info"):
            infodict = obj._gdp_transformation_info
            # If the user has something with our name that is not a dict, we
            # scream. If they have a dict with this name then we are just going
            # to use it...
            if type(infodict) is not dict:
                raise GDP_Error(
                    "Disjunct %s contains an attribute named "
                    "_gdp_transformation_info. The transformation requires "
                    "that it can create this attribute!" % obj.name)
        else:
            infodict = obj._gdp_transformation_info = {}
        # deactivated means either we've already transformed or user deactivated
        if not obj.active:
            if obj.indicator_var.is_fixed():
                if value(obj.indicator_var) == 0:
                    # The user cleanly deactivated the disjunct: there
                    # is nothing for us to do here.
                    return
                else:
                    raise GDP_Error(
                        "The disjunct %s is deactivated, but the "
                        "indicator_var is fixed to %s. This makes no sense." %
                        (obj.name, value(obj.indicator_var)))
            if not infodict.get('relaxed', False):
                raise GDP_Error(
                    "The disjunct %s is deactivated, but the "
                    "indicator_var is not fixed and the disjunct does not "
                    "appear to have been relaxed. This makes no sense." %
                    (obj.name, ))

        if 'chull' in infodict:
            # we've transformed it (with CHull), so don't do it again.
            return

        # add reference to original disjunct to info dict on
        # transformation block
        relaxedDisjuncts = transBlock.relaxedDisjuncts
        relaxationBlock = relaxedDisjuncts[len(relaxedDisjuncts)]
        relaxationBlockInfo = relaxationBlock._gdp_transformation_info = {
            'src': obj,
            'srcVars': ComponentMap(),
            'srcConstraints': ComponentMap(),
            'boundConstraintToSrcVar': ComponentMap(),
        }
        infodict['chull'] = chull = {
            'relaxationBlock': relaxationBlock,
            'relaxedConstraints': ComponentMap(),
            'disaggregatedVars': ComponentMap(),
            'bigmConstraints': ComponentMap(),
        }

        # if this is a disjunctData from an indexed disjunct, we are
        # going to want to check at the end that the container is
        # deactivated if everything in it is. So we save it in our
        # dictionary of things to check if it isn't there already.
        disjParent = obj.parent_component()
        if disjParent.is_indexed() and \
           disjParent not in transBlock.disjContainers:
            transBlock.disjContainers.add(disjParent)

        # add the disaggregated variables and their bigm constraints
        # to the relaxationBlock
        for var in varSet:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the chull "
                                "transformation! Missing bound for %s." %
                                (var.name))

            disaggregatedVar = Var(within=Reals,
                                   bounds=(min(0, lb), max(0, ub)),
                                   initialize=var.value)
            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            disaggregatedVarName = unique_component_name(
                relaxationBlock, var.local_name)
            relaxationBlock.add_component(disaggregatedVarName,
                                          disaggregatedVar)
            chull['disaggregatedVars'][var] = disaggregatedVar
            relaxationBlockInfo['srcVars'][disaggregatedVar] = var

            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(disaggregatedVarName + "_bounds",
                                          bigmConstraint)
            if lb:
                bigmConstraint.add('lb',
                                   obj.indicator_var * lb <= disaggregatedVar)
            if ub:
                bigmConstraint.add('ub',
                                   disaggregatedVar <= obj.indicator_var * ub)
            chull['bigmConstraints'][var] = bigmConstraint
            relaxationBlockInfo['boundConstraintToSrcVar'][
                bigmConstraint] = var

        for var in localVars:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the chull "
                                "transformation! Missing bound for %s." %
                                (var.name))
            if value(lb) > 0:
                var.setlb(0)
            if value(ub) < 0:
                var.setub(0)

            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            conName = unique_component_name(relaxationBlock,
                                            var.local_name + "_bounds")
            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(conName, bigmConstraint)
            bigmConstraint.add('lb', obj.indicator_var * lb <= var)
            bigmConstraint.add('ub', var <= obj.indicator_var * ub)
            chull['bigmConstraints'][var] = bigmConstraint
            relaxationBlockInfo['boundConstraintToSrcVar'][
                bigmConstraint] = var

        var_substitute_map = dict(
            (id(v), newV) for v, newV in iteritems(chull['disaggregatedVars']))
        zero_substitute_map = dict(
            (id(v), NumericConstant(0))
            for v, newV in iteritems(chull['disaggregatedVars']))
        zero_substitute_map.update(
            (id(v), NumericConstant(0)) for v in localVars)

        # Transform each component within this disjunct
        self._transform_block_components(obj, obj, infodict,
                                         var_substitute_map,
                                         zero_substitute_map)

        # deactivate disjunct so we know we've relaxed it
        obj._deactivate_without_fixing_indicator()
        infodict['relaxed'] = True
Ejemplo n.º 4
0
    def _xform_constraint(self, obj, disjunct, infodict, var_substitute_map,
                          zero_substitute_map):
        # we will put a new transformed constraint on the relaxation block.
        relaxationBlock = infodict['chull']['relaxationBlock']
        transBlock = relaxationBlock.parent_block()
        varMap = infodict['chull']['disaggregatedVars']

        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        name = unique_component_name(relaxationBlock, obj.name)

        if obj.is_indexed():
            try:
                newConstraint = Constraint(obj.index_set(), transBlock.lbub)
            except:
                # The original constraint may have been indexed by a
                # non-concrete set (like an Any).  We will give up on
                # strict index verification and just blindly proceed.
                newConstraint = Constraint(Any)
        else:
            newConstraint = Constraint(transBlock.lbub)
        relaxationBlock.add_component(name, newConstraint)
        # add mapping of original constraint to transformed constraint
        # in transformation info dictionary
        infodict['chull']['relaxedConstraints'][obj] = newConstraint
        # add mapping of transformed constraint back to original constraint (we
        # know that the info dict is already created because this only got
        # called if we were transforming a disjunct...)
        relaxationBlock._gdp_transformation_info['srcConstraints'][
            newConstraint] = obj

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            NL = c.body.polynomial_degree() not in (0, 1)
            EPS = self._config.EPS
            mode = self._config.perspective_function

            # We need to evaluate the expression at the origin *before*
            # we substitute the expression variables with the
            # disaggregated variables
            if not NL or mode == "FurmanSawayaGrossmann":
                h_0 = clone_without_expression_components(
                    c.body, substitute=zero_substitute_map)

            y = disjunct.indicator_var
            if NL:
                if mode == "LeeGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / y)
                            for var, subs in iteritems(var_substitute_map)))
                    expr = sub_expr * y
                elif mode == "GrossmannLee":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / (y + EPS))
                            for var, subs in iteritems(var_substitute_map)))
                    expr = (y + EPS) * sub_expr
                elif mode == "FurmanSawayaGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / ((1 - EPS) * y + EPS))
                            for var, subs in iteritems(var_substitute_map)))
                    expr = (
                        (1 - EPS) * y + EPS) * sub_expr - EPS * h_0 * (1 - y)
                else:
                    raise RuntimeError("Unknown NL CHull mode")
            else:
                expr = clone_without_expression_components(
                    c.body, substitute=var_substitute_map)

            if c.equality:
                if NL:
                    newConsExpr = expr == c.lower * y
                else:
                    v = list(EXPR.identify_variables(expr))
                    if len(v) == 1 and not c.lower:
                        # Setting a variable to 0 in a disjunct is
                        # *very* common.  We should recognize that in
                        # that structure, the disaggregated variable
                        # will also be fixed to 0.
                        v[0].fix(0)
                        continue
                    newConsExpr = expr - (1 - y) * h_0 == c.lower * y

                if obj.is_indexed():
                    newConstraint.add((i, 'eq'), newConsExpr)
                else:
                    newConstraint.add('eq', newConsExpr)
                continue

            if c.lower is not None:
                # TODO: At the moment there is no reason for this to be in both
                # lower and upper... I think there could be though if I say what
                # the new constraint is going to be or something.
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "GDP(cHull): Transforming constraint " + "'%s'",
                        c.name)
                if NL:
                    newConsExpr = expr >= c.lower * y
                else:
                    newConsExpr = expr - (1 - y) * h_0 >= c.lower * y

                if obj.is_indexed():
                    newConstraint.add((i, 'lb'), newConsExpr)
                else:
                    newConstraint.add('lb', newConsExpr)

            if c.upper is not None:
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "GDP(cHull): Transforming constraint " + "'%s'",
                        c.name)
                if NL:
                    newConsExpr = expr <= c.upper * y
                else:
                    newConsExpr = expr - (1 - y) * h_0 <= c.upper * y

                if obj.is_indexed():
                    newConstraint.add((i, 'ub'), newConsExpr)
                else:
                    newConstraint.add('ub', newConsExpr)
Ejemplo n.º 5
0
    def _transform_constraint(self, obj, disjunct, var_substitute_map,
                              zero_substitute_map):
        # we will put a new transformed constraint on the relaxation block.
        relaxationBlock = disjunct._transformation_block()
        transBlock = relaxationBlock.parent_block()
        varMap = relaxationBlock._disaggregatedVarMap['disaggregatedVar']
        constraintMap = relaxationBlock._constraintMap

        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        name = unique_component_name(
            relaxationBlock,
            obj.getname(fully_qualified=True, name_buffer=NAME_BUFFER))

        if obj.is_indexed():
            newConstraint = Constraint(obj.index_set(), transBlock.lbub)
        else:
            newConstraint = Constraint(transBlock.lbub)
        relaxationBlock.add_component(name, newConstraint)
        # map the containers:
        # add mapping of original constraint to transformed constraint
        if obj.is_indexed():
            constraintMap['transformedConstraints'][obj] = newConstraint
        # add mapping of transformed constraint container back to original
        # constraint container (or SimpleConstraint)
        constraintMap['srcConstraints'][newConstraint] = obj

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            NL = c.body.polynomial_degree() not in (0, 1)
            EPS = self._config.EPS
            mode = self._config.perspective_function

            # We need to evaluate the expression at the origin *before*
            # we substitute the expression variables with the
            # disaggregated variables
            if not NL or mode == "FurmanSawayaGrossmann":
                h_0 = clone_without_expression_components(
                    c.body, substitute=zero_substitute_map)

            y = disjunct.indicator_var
            if NL:
                if mode == "LeeGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / y)
                            for var, subs in iteritems(var_substitute_map)))
                    expr = sub_expr * y
                elif mode == "GrossmannLee":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / (y + EPS))
                            for var, subs in iteritems(var_substitute_map)))
                    expr = (y + EPS) * sub_expr
                elif mode == "FurmanSawayaGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs / ((1 - EPS) * y + EPS))
                            for var, subs in iteritems(var_substitute_map)))
                    expr = (
                        (1 - EPS) * y + EPS) * sub_expr - EPS * h_0 * (1 - y)
                else:
                    raise RuntimeError("Unknown NL Hull mode")
            else:
                expr = clone_without_expression_components(
                    c.body, substitute=var_substitute_map)

            if c.equality:
                if NL:
                    # ESJ TODO: This can't happen right? This is the only
                    # obvious case where someone has messed up, but this has to
                    # be nonconvex, right? Shouldn't we tell them?
                    newConsExpr = expr == c.lower * y
                else:
                    v = list(EXPR.identify_variables(expr))
                    if len(v) == 1 and not c.lower:
                        # Setting a variable to 0 in a disjunct is
                        # *very* common.  We should recognize that in
                        # that structure, the disaggregated variable
                        # will also be fixed to 0.
                        v[0].fix(0)
                        # ESJ: If you ask where the transformed constraint is,
                        # the answer is nowhere. Really, it is in the bounds of
                        # this variable, so I'm going to return
                        # it. Alternatively we could return an empty list, but I
                        # think I like this better.
                        constraintMap['transformedConstraints'][c] = [v[0]]
                        # Reverse map also (this is strange)
                        constraintMap['srcConstraints'][v[0]] = c
                        continue
                    newConsExpr = expr - (1 - y) * h_0 == c.lower * y

                if obj.is_indexed():
                    newConstraint.add((i, 'eq'), newConsExpr)
                    # map the _ConstraintDatas (we mapped the container above)
                    constraintMap['transformedConstraints'][c] = [
                        newConstraint[i, 'eq']
                    ]
                    constraintMap['srcConstraints'][newConstraint[i, 'eq']] = c
                else:
                    newConstraint.add('eq', newConsExpr)
                    # map to the _ConstraintData (And yes, for
                    # SimpleConstraints, this is overwriting the map to the
                    # container we made above, and that is what I want to
                    # happen. SimpleConstraints will map to lists. For
                    # IndexedConstraints, we can map the container to the
                    # container, but more importantly, we are mapping the
                    # _ConstraintDatas to each other above)
                    constraintMap['transformedConstraints'][c] = [
                        newConstraint['eq']
                    ]
                    constraintMap['srcConstraints'][newConstraint['eq']] = c

                continue

            if c.lower is not None:
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    _name = c.getname(fully_qualified=True,
                                      name_buffer=NAME_BUFFER)
                    logger.debug(
                        "GDP(Hull): Transforming constraint " + "'%s'", _name)
                if NL:
                    newConsExpr = expr >= c.lower * y
                else:
                    newConsExpr = expr - (1 - y) * h_0 >= c.lower * y

                if obj.is_indexed():
                    newConstraint.add((i, 'lb'), newConsExpr)
                    constraintMap['transformedConstraints'][c] = [
                        newConstraint[i, 'lb']
                    ]
                    constraintMap['srcConstraints'][newConstraint[i, 'lb']] = c
                else:
                    newConstraint.add('lb', newConsExpr)
                    constraintMap['transformedConstraints'][c] = [
                        newConstraint['lb']
                    ]
                    constraintMap['srcConstraints'][newConstraint['lb']] = c

            if c.upper is not None:
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    _name = c.getname(fully_qualified=True,
                                      name_buffer=NAME_BUFFER)
                    logger.debug(
                        "GDP(Hull): Transforming constraint " + "'%s'", _name)
                if NL:
                    newConsExpr = expr <= c.upper * y
                else:
                    newConsExpr = expr - (1 - y) * h_0 <= c.upper * y

                if obj.is_indexed():
                    newConstraint.add((i, 'ub'), newConsExpr)
                    # map (have to account for fact we might have created list
                    # above
                    transformed = constraintMap['transformedConstraints'].get(
                        c)
                    if transformed is not None:
                        transformed.append(newConstraint[i, 'ub'])
                    else:
                        constraintMap['transformedConstraints'][c] = [
                            newConstraint[i, 'ub']
                        ]
                    constraintMap['srcConstraints'][newConstraint[i, 'ub']] = c
                else:
                    newConstraint.add('ub', newConsExpr)
                    transformed = constraintMap['transformedConstraints'].get(
                        c)
                    if transformed is not None:
                        transformed.append(newConstraint['ub'])
                    else:
                        constraintMap['transformedConstraints'][c] = [
                            newConstraint['ub']
                        ]
                    constraintMap['srcConstraints'][newConstraint['ub']] = c

        # deactivate now that we have transformed
        obj.deactivate()
Ejemplo n.º 6
0
def form_linearized_objective_constraints(instance_name,
                                          instance,
                                          scenario_tree,
                                          linearize_nonbinary_penalty_terms,
                                          breakpoint_strategy,
                                          tolerance):


    # keep track and return what was added to the instance, so
    # it can be cleaned up if necessary.
    new_instance_attributes = []

    linearization_index_set_name = "PH_LINEARIZATION_INDEX_SET"
    linearization_index_set = instance.find_component(linearization_index_set_name)
    if linearization_index_set is None:
        linearization_index_set = Set(initialize=range(0, linearize_nonbinary_penalty_terms*2), dimen=1, name=linearization_index_set_name)
        instance.add_component(linearization_index_set_name, linearization_index_set)

    scenario = scenario_tree.get_scenario(instance_name)

    nodeid_to_vardata_map = instance._ScenarioTreeSymbolMap.bySymbol

    for tree_node in scenario._node_list[:-1]:

        xbar_dict = tree_node._xbars

        # if linearizing, then we have previously defined a variable
        # associated with the result of the linearized approximation
        # of the penalty term - this is simply added to the objective
        # function.
        linearized_cost_variable_name = "PHQUADPENALTY_"+str(tree_node._name)
        linearized_cost_variable = instance.find_component(linearized_cost_variable_name)

        # grab the linearization constraint associated with the
        # linearized cost variable, if it exists. otherwise, create it
        # - but an empty variety. the constraints are stage-specific -
        # we could index by constraint, but we don't know if that is
        # really worth the additional effort.
        linearization_constraint_name = "PH_LINEARIZATION_"+str(tree_node._name)
        linearization_constraint = instance.find_component(linearization_constraint_name)
        if linearization_constraint is not None:
            # clear whatever constraint components are there - there
            # may be fewer breakpoints, due to tolerances, and we
            # don't want to the old pieces laying around.
            linearization_constraint.clear()
        else:
            # this is the first time the constraint is being added -
            # add it to the list of PH-specific constraints for this
            # instance.
            new_instance_attributes.append(linearization_constraint_name)
            nodal_index_set_name = "PHINDEX_"+str(tree_node._name)
            nodal_index_set = instance.find_component(nodal_index_set_name)
            assert nodal_index_set is not None
            linearization_constraint = \
                Constraint(nodal_index_set,
                           linearization_index_set,
                           name=linearization_constraint_name)
            linearization_constraint.construct()
            instance.add_component(linearization_constraint_name, linearization_constraint)

        for variable_id in tree_node._variable_ids:

            # don't add weight terms for derived variables at the tree
            # node.
            if variable_id in tree_node._derived_variable_ids:
                continue

            if variable_id not in tree_node._minimums:
                variable_name, index = tree_node._variable_ids[variable_id]
                raise RuntimeError("No minimum value statistic found for variable=%s "
                                   "on tree node=%s; cannot form linearized PH objective"
                                   % (variable_name+indexToString(index),
                                      tree_node._name))
            if variable_id not in tree_node._maximums:
                variable_name, index = tree_node._variable_ids[variable_id]
                raise RuntimeError("No maximums value statistic found for "
                                   "variable=%s on tree node=%s; cannot "
                                   "form linearized PH objective"
                                   % (variable_name+indexToString(index),
                                      tree_node._name))


            xbar = xbar_dict[variable_id]
            node_min = tree_node._minimums[variable_id]
            node_max = tree_node._maximums[variable_id]

            instance_vardata = nodeid_to_vardata_map[variable_id]

            if (instance_vardata.stale is False) and (instance_vardata.fixed is False):

                # binaries have already been dealt with in the process of PH objective function formation.
                if isinstance(instance_vardata.domain, BooleanSet) is False:

                    x = instance_vardata

                    if x.lb is None or x.ub is None:
                        msg = "Missing bound for variable '%s'\n"         \
                              'Both lower and upper bounds required when' \
                              ' piece-wise approximating quadratic '      \
                              'penalty terms'
                        raise ValueError(msg % instance_vardata.name)
                    lb = value(x.lb)
                    ub = value(x.ub)

                    # compute the breakpoint sequence according to the specified strategy.
                    try:
                        strategy = (compute_uniform_breakpoints,
                                    compute_uniform_between_nodestat_breakpoints,
                                    compute_uniform_between_woodruff_breakpoints,
                                    compute_exponential_from_mean_breakpoints,
                                    )[ breakpoint_strategy ]
                        args = ( lb, node_min, xbar, node_max, ub, \
                                     linearize_nonbinary_penalty_terms, tolerance )
                        breakpoints = strategy( *args )
                    except ValueError:
                        e = sys.exc_info()[1]
                        msg = 'A breakpoint distribution strategy (%s) '  \
                              'is currently not supported within PH!'
                        raise ValueError(msg % breakpoint_strategy)

                    for i in xrange(len(breakpoints)-1):

                        this_lb = breakpoints[i]
                        this_ub = breakpoints[i+1]

                        segment_tuple = create_piecewise_constraint_tuple(this_lb,
                                                                          this_ub,
                                                                          x,
                                                                          xbar,
                                                                          linearized_cost_variable[variable_id],
                                                                          tolerance)

                        linearization_constraint.add((variable_id,i), segment_tuple)

    return new_instance_attributes
Ejemplo n.º 7
0
    def _transform_disjunct(self, obj, transBlock, varSet, localVars):
        # deactivated should only come from the user
        if not obj.active:
            if obj.indicator_var.is_fixed():
                if value(obj.indicator_var) == 0:
                    # The user cleanly deactivated the disjunct: there
                    # is nothing for us to do here.
                    return
                else:
                    raise GDP_Error(
                        "The disjunct '%s' is deactivated, but the "
                        "indicator_var is fixed to %s. This makes no sense." %
                        (obj.name, value(obj.indicator_var)))
            if obj._transformation_block is None:
                raise GDP_Error(
                    "The disjunct '%s' is deactivated, but the "
                    "indicator_var is not fixed and the disjunct does not "
                    "appear to have been relaxed. This makes no sense. "
                    "(If the intent is to deactivate the disjunct, fix its "
                    "indicator_var to 0.)" % (obj.name, ))

        if obj._transformation_block is not None:
            # we've transformed it, which means this is the second time it's
            # appearing in a Disjunction
            raise GDP_Error(
                "The disjunct '%s' has been transformed, but a disjunction "
                "it appears in has not. Putting the same disjunct in "
                "multiple disjunctions is not supported." % obj.name)

        # create a relaxation block for this disjunct
        relaxedDisjuncts = transBlock.relaxedDisjuncts
        relaxationBlock = relaxedDisjuncts[len(relaxedDisjuncts)]

        relaxationBlock.localVarReferences = Block()

        # Put the disaggregated variables all on their own block so that we can
        # isolate the name collisions and still have complete control over the
        # names on this block. (This is for peace of mind now, but will matter
        # in the future for adding the binaries corresponding to Boolean
        # indicator vars.)
        relaxationBlock.disaggregatedVars = Block()

        # add the map that will link back and forth between transformed
        # constraints and their originals.
        relaxationBlock._constraintMap = {
            'srcConstraints': ComponentMap(),
            'transformedConstraints': ComponentMap()
        }
        # Map between disaggregated variables for this disjunct and their
        # originals
        relaxationBlock._disaggregatedVarMap = {
            'srcVar': ComponentMap(),
            'disaggregatedVar': ComponentMap(),
        }
        # Map between disaggregated variables and their lb*indicator <= var <=
        # ub*indicator constraints
        relaxationBlock._bigMConstraintMap = ComponentMap()

        # add mappings to source disjunct (so we'll know we've relaxed)
        obj._transformation_block = weakref_ref(relaxationBlock)
        relaxationBlock._srcDisjunct = weakref_ref(obj)

        # add Suffix to the relaxation block that disaggregated variables are
        # local (in case this is nested in another Disjunct)
        local_var_set = None
        parent_disjunct = obj.parent_block()
        while parent_disjunct is not None:
            if parent_disjunct.ctype is Disjunct:
                break
            parent_disjunct = parent_disjunct.parent_block()
        if parent_disjunct is not None:
            localVarSuffix = relaxationBlock.LocalVars = Suffix(
                direction=Suffix.LOCAL)
            local_var_set = localVarSuffix[parent_disjunct] = ComponentSet()

        # add the disaggregated variables and their bigm constraints
        # to the relaxationBlock
        for var in varSet:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the hull "
                                "transformation! Missing bound for %s." %
                                (var.name))

            disaggregatedVar = Var(within=Reals,
                                   bounds=(min(0, lb), max(0, ub)),
                                   initialize=var.value)
            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            disaggregatedVarName = unique_component_name(
                relaxationBlock.disaggregatedVars,
                var.getname(fully_qualified=False, name_buffer=NAME_BUFFER),
            )
            relaxationBlock.disaggregatedVars.add_component(
                disaggregatedVarName, disaggregatedVar)
            # mark this as local because we won't re-disaggregate if this is a
            # nested disjunction
            if local_var_set is not None:
                local_var_set.add(disaggregatedVar)
            # store the mappings from variables to their disaggregated selves on
            # the transformation block.
            relaxationBlock._disaggregatedVarMap['disaggregatedVar'][
                var] = disaggregatedVar
            relaxationBlock._disaggregatedVarMap['srcVar'][
                disaggregatedVar] = var

            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(disaggregatedVarName + "_bounds",
                                          bigmConstraint)
            if lb:
                bigmConstraint.add('lb',
                                   obj.indicator_var * lb <= disaggregatedVar)
            if ub:
                bigmConstraint.add('ub',
                                   disaggregatedVar <= obj.indicator_var * ub)

            relaxationBlock._bigMConstraintMap[
                disaggregatedVar] = bigmConstraint

        for var in localVars:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the hull "
                                "transformation! Missing bound for %s." %
                                (var.name))
            if value(lb) > 0:
                var.setlb(0)
            if value(ub) < 0:
                var.setub(0)

            # map it to itself
            relaxationBlock._disaggregatedVarMap['disaggregatedVar'][var] = var
            relaxationBlock._disaggregatedVarMap['srcVar'][var] = var

            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            conName = unique_component_name(
                relaxationBlock,
                var.getname(fully_qualified=False, name_buffer=NAME_BUFFER) + \
                "_bounds")
            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(conName, bigmConstraint)
            if lb:
                bigmConstraint.add('lb', obj.indicator_var * lb <= var)
            if ub:
                bigmConstraint.add('ub', var <= obj.indicator_var * ub)
            relaxationBlock._bigMConstraintMap[var] = bigmConstraint

        var_substitute_map = dict((id(v), newV) for v, newV in iteritems(
            relaxationBlock._disaggregatedVarMap['disaggregatedVar']))
        zero_substitute_map = dict((id(v), ZeroConstant) for v, newV in \
                                   iteritems(
                                       relaxationBlock._disaggregatedVarMap[
                                           'disaggregatedVar']))
        zero_substitute_map.update((id(v), ZeroConstant) for v in localVars)

        # Transform each component within this disjunct
        self._transform_block_components(obj, obj, var_substitute_map,
                                         zero_substitute_map)

        # deactivate disjunct so writers can be happy
        obj._deactivate_without_fixing_indicator()
Ejemplo n.º 8
0
    def _xform_constraint(self, obj, disjunct, infodict, bigMargs,
                          suffix_list):
        # add constraint to the transformation block, we'll transform it there.

        relaxationBlock = infodict['bigm']['relaxationBlock']
        transBlock = relaxationBlock.parent_block()
        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        name = unique_component_name(relaxationBlock, obj.name)

        if obj.is_indexed():
            try:
                newConstraint = Constraint(obj.index_set(), transBlock.lbub)
            except TypeError:
                # The original constraint may have been indexed by a
                # non-concrete set (like an Any).  We will give up on
                # strict index verification and just blindly proceed.
                newConstraint = Constraint(Any)
        else:
            newConstraint = Constraint(transBlock.lbub)
        relaxationBlock.add_component(name, newConstraint)
        # add mapping of original constraint to transformed constraint
        # in transformation info dictionary
        infodict['bigm']['relaxedConstraints'][obj] = newConstraint
        # add mapping of transformed constraint back to original constraint (we
        # know that the info dict is already created because this only got
        # called if we were transforming a disjunct...)
        relaxationBlock._gdp_transformation_info['srcConstraints'][
            newConstraint] = obj

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            # first, we see if an M value was specified in the arguments.
            # (This returns None if not)
            M = self._get_M_from_args(c, bigMargs)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "from the BigM argument is %s." %
                             (obj.name, str(M)))

            # if we didn't get something from args, try suffixes:
            if M is None:
                M = self._get_M_from_suffixes(c, suffix_list)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after checking suffixes is %s." %
                             (obj.name, str(M)))

            if not isinstance(M, (tuple, list)):
                if M is None:
                    M = (None, None)
                else:
                    try:
                        M = (-M, M)
                    except:
                        logger.error("Error converting scalar M-value %s "
                                     "to (-M,M).  Is %s not a numeric type?" %
                                     (M, type(M)))
                        raise
            if len(M) != 2:
                raise GDP_Error("Big-M %s for constraint %s is not of "
                                "length two. "
                                "Expected either a single value or "
                                "tuple or list of length two for M." %
                                (str(M), name))

            if c.lower is not None and M[0] is None:
                M = (self._estimate_M(c.body, name)[0] - c.lower, M[1])
            if c.upper is not None and M[1] is None:
                M = (M[0], self._estimate_M(c.body, name)[1] - c.upper)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after estimating (if needed) is %s." %
                             (obj.name, str(M)))

            # Handle indices for both SimpleConstraint and IndexedConstraint
            if i.__class__ is tuple:
                i_lb = i + ('lb', )
                i_ub = i + ('ub', )
            elif obj.is_indexed():
                i_lb = (
                    i,
                    'lb',
                )
                i_ub = (
                    i,
                    'ub',
                )
            else:
                i_lb = 'lb'
                i_ub = 'ub'

            if c.lower is not None:
                if M[0] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[0] * (1 - disjunct.indicator_var)
                newConstraint.add(i_lb, c.lower <= c.body - M_expr)
            if c.upper is not None:
                if M[1] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[1] * (1 - disjunct.indicator_var)
                newConstraint.add(i_ub, c.body - M_expr <= c.upper)
Ejemplo n.º 9
0
Archivo: bigm.py Proyecto: Pyomo/pyomo
    def _xform_constraint(self, obj, disjunct, infodict,
                          bigMargs, suffix_list):
        # add constraint to the transformation block, we'll transform it there.

        relaxationBlock = infodict['bigm']['relaxationBlock']
        transBlock = relaxationBlock.parent_block()
        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        name = unique_component_name(relaxationBlock, obj.name)

        if obj.is_indexed():
            try:
                newConstraint = Constraint(obj.index_set(), transBlock.lbub)
            except TypeError:
                # The original constraint may have been indexed by a
                # non-concrete set (like an Any).  We will give up on
                # strict index verification and just blindly proceed.
                newConstraint = Constraint(Any)
        else:
            newConstraint = Constraint(transBlock.lbub)
        relaxationBlock.add_component(name, newConstraint)
        # add mapping of original constraint to transformed constraint
        # in transformation info dictionary
        infodict['bigm']['relaxedConstraints'][obj] = newConstraint
        # add mapping of transformed constraint back to original constraint (we
        # know that the info dict is already created because this only got
        # called if we were transforming a disjunct...)
        relaxationBlock._gdp_transformation_info['srcConstraints'][
            newConstraint] = obj

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            # first, we see if an M value was specified in the arguments.
            # (This returns None if not)
            M = self._get_M_from_args(c, bigMargs)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "from the BigM argument is %s." % (obj.name,
                                                                str(M)))

            # if we didn't get something from args, try suffixes:
            if M is None:
                M = self._get_M_from_suffixes(c, suffix_list)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after checking suffixes is %s." % (obj.name,
                                                                 str(M)))

            if not isinstance(M, (tuple, list)):
                if M is None:
                    M = (None, None)
                else:
                    try:
                        M = (-M, M)
                    except:
                        logger.error("Error converting scalar M-value %s "
                                     "to (-M,M).  Is %s not a numeric type?"
                                     % (M, type(M)))
                        raise
            if len(M) != 2:
                raise GDP_Error("Big-M %s for constraint %s is not of "
                                "length two. "
                                "Expected either a single value or "
                                "tuple or list of length two for M."
                                % (str(M), name))

            if c.lower is not None and M[0] is None:
                M = (self._estimate_M(c.body, name)[0] - c.lower, M[1])
            if c.upper is not None and M[1] is None:
                M = (M[0], self._estimate_M(c.body, name)[1] - c.upper)

            if __debug__ and logger.isEnabledFor(logging.DEBUG):
                logger.debug("GDP(BigM): The value for M for constraint %s "
                             "after estimating (if needed) is %s." %
                             (obj.name, str(M)))

            # Handle indices for both SimpleConstraint and IndexedConstraint
            if i.__class__ is tuple:
                i_lb = i + ('lb',)
                i_ub = i + ('ub',)
            elif obj.is_indexed():
                i_lb = (i, 'lb',)
                i_ub = (i, 'ub',)
            else:
                i_lb = 'lb'
                i_ub = 'ub'

            if c.lower is not None:
                if M[0] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[0] * (1 - disjunct.indicator_var)
                newConstraint.add(i_lb, c.lower <= c. body - M_expr)
            if c.upper is not None:
                if M[1] is None:
                    raise GDP_Error("Cannot relax disjunctive constraint %s "
                                    "because M is not defined." % name)
                M_expr = M[1] * (1 - disjunct.indicator_var)
                newConstraint.add(i_ub, c.body - M_expr <= c.upper)
Ejemplo n.º 10
0
Archivo: chull.py Proyecto: Pyomo/pyomo
    def _xform_constraint(self, obj, disjunct, infodict, var_substitute_map,
                          zero_substitute_map):
        # we will put a new transformed constraint on the relaxation block.
        relaxationBlock = infodict['chull']['relaxationBlock']
        transBlock = relaxationBlock.parent_block()
        varMap = infodict['chull']['disaggregatedVars']

        # Though rare, it is possible to get naming conflicts here
        # since constraints from all blocks are getting moved onto the
        # same block. So we get a unique name
        name = unique_component_name(relaxationBlock, obj.name)

        if obj.is_indexed():
            try:
                newConstraint = Constraint(obj.index_set(), transBlock.lbub)
            except:
                # The original constraint may have been indexed by a
                # non-concrete set (like an Any).  We will give up on
                # strict index verification and just blindly proceed.
                newConstraint = Constraint(Any)
        else:
            newConstraint = Constraint(transBlock.lbub)
        relaxationBlock.add_component(name, newConstraint)
        # add mapping of original constraint to transformed constraint
        # in transformation info dictionary
        infodict['chull']['relaxedConstraints'][obj] = newConstraint
        # add mapping of transformed constraint back to original constraint (we
        # know that the info dict is already created because this only got
        # called if we were transforming a disjunct...)
        relaxationBlock._gdp_transformation_info['srcConstraints'][
            newConstraint] = obj

        for i in sorted(iterkeys(obj)):
            c = obj[i]
            if not c.active:
                continue

            NL = c.body.polynomial_degree() not in (0,1)
            EPS = self._config.EPS
            mode = self._config.perspective_function

            # We need to evaluate the expression at the origin *before*
            # we substitute the expression variables with the
            # disaggregated variables
            if not NL or mode == "FurmanSawayaGrossmann":
                h_0 = clone_without_expression_components(
                    c.body, substitute=zero_substitute_map)

            y = disjunct.indicator_var
            if NL:
                if mode == "LeeGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var,  subs/y)
                            for var, subs in iteritems(var_substitute_map) )
                    )
                    expr = sub_expr * y
                elif mode == "GrossmannLee":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs/(y + EPS))
                            for var, subs in iteritems(var_substitute_map) )
                    )
                    expr = (y + EPS) * sub_expr
                elif mode == "FurmanSawayaGrossmann":
                    sub_expr = clone_without_expression_components(
                        c.body,
                        substitute=dict(
                            (var, subs/((1 - EPS)*y + EPS))
                            for var, subs in iteritems(var_substitute_map) )
                    )
                    expr = ((1-EPS)*y + EPS)*sub_expr - EPS*h_0*(1-y)
                else:
                    raise RuntimeError("Unknown NL CHull mode")
            else:
                expr = clone_without_expression_components(
                    c.body, substitute=var_substitute_map)

            if c.equality:
                if NL:
                    newConsExpr = expr == c.lower*y
                else:
                    v = list(EXPR.identify_variables(expr))
                    if len(v) == 1 and not c.lower:
                        # Setting a variable to 0 in a disjunct is
                        # *very* common.  We should recognize that in
                        # that structure, the disaggregated variable
                        # will also be fixed to 0.
                        v[0].fix(0)
                        continue
                    newConsExpr = expr - (1-y)*h_0 == c.lower*y

                if obj.is_indexed():
                    newConstraint.add((i, 'eq'), newConsExpr)
                else:
                    newConstraint.add('eq', newConsExpr)
                continue

            if c.lower is not None:
                # TODO: At the moment there is no reason for this to be in both
                # lower and upper... I think there could be though if I say what
                # the new constraint is going to be or something.
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    logger.debug("GDP(cHull): Transforming constraint " +
                                 "'%s'", c.name)
                if NL:
                    newConsExpr = expr >= c.lower*y
                else:
                    newConsExpr = expr - (1-y)*h_0 >= c.lower*y

                if obj.is_indexed():
                    newConstraint.add((i, 'lb'), newConsExpr)
                else:
                    newConstraint.add('lb', newConsExpr)

            if c.upper is not None:
                if __debug__ and logger.isEnabledFor(logging.DEBUG):
                    logger.debug("GDP(cHull): Transforming constraint " +
                                 "'%s'", c.name)
                if NL:
                    newConsExpr = expr <= c.upper*y
                else:
                    newConsExpr = expr - (1-y)*h_0 <= c.upper*y

                if obj.is_indexed():
                    newConstraint.add((i, 'ub'), newConsExpr)
                else:
                    newConstraint.add('ub', newConsExpr)
Ejemplo n.º 11
0
Archivo: chull.py Proyecto: Pyomo/pyomo
    def _transform_disjunct(self, obj, transBlock, varSet, localVars):
        if hasattr(obj, "_gdp_transformation_info"):
            infodict = obj._gdp_transformation_info
            # If the user has something with our name that is not a dict, we
            # scream. If they have a dict with this name then we are just going
            # to use it...
            if type(infodict) is not dict:
                raise GDP_Error(
                    "Disjunct %s contains an attribute named "
                    "_gdp_transformation_info. The transformation requires "
                    "that it can create this attribute!" % obj.name)
        else:
            infodict = obj._gdp_transformation_info = {}
        # deactivated means either we've already transformed or user deactivated
        if not obj.active:
            if obj.indicator_var.is_fixed():
                if value(obj.indicator_var) == 0:
                    # The user cleanly deactivated the disjunct: there
                    # is nothing for us to do here.
                    return
                else:
                    raise GDP_Error(
                        "The disjunct %s is deactivated, but the "
                        "indicator_var is fixed to %s. This makes no sense."
                        % ( obj.name, value(obj.indicator_var) ))
            if not infodict.get('relaxed', False):
                raise GDP_Error(
                    "The disjunct %s is deactivated, but the "
                    "indicator_var is not fixed and the disjunct does not "
                    "appear to have been relaxed. This makes no sense."
                    % ( obj.name, ))

        if 'chull' in infodict:
            # we've transformed it (with CHull), so don't do it again.
            return

        # add reference to original disjunct to info dict on
        # transformation block
        relaxedDisjuncts = transBlock.relaxedDisjuncts
        relaxationBlock = relaxedDisjuncts[len(relaxedDisjuncts)]
        relaxationBlockInfo = relaxationBlock._gdp_transformation_info = {
            'src': obj,
            'srcVars': ComponentMap(),
            'srcConstraints': ComponentMap(),
            'boundConstraintToSrcVar': ComponentMap(),
        }
        infodict['chull'] = chull = {
            'relaxationBlock': relaxationBlock,
            'relaxedConstraints': ComponentMap(),
            'disaggregatedVars': ComponentMap(),
            'bigmConstraints': ComponentMap(),
        }

        # if this is a disjunctData from an indexed disjunct, we are
        # going to want to check at the end that the container is
        # deactivated if everything in it is. So we save it in our
        # dictionary of things to check if it isn't there already.
        disjParent = obj.parent_component()
        if disjParent.is_indexed() and \
           disjParent not in transBlock.disjContainers:
            transBlock.disjContainers.add(disjParent)

        # add the disaggregated variables and their bigm constraints
        # to the relaxationBlock
        for var in varSet:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the chull "
                                "transformation! Missing bound for %s."
                                % (var.name))

            disaggregatedVar = Var(within=Reals,
                                   bounds=(min(0, lb), max(0, ub)),
                                   initialize=var.value)
            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            disaggregatedVarName = unique_component_name(
                relaxationBlock, var.local_name)
            relaxationBlock.add_component(
                disaggregatedVarName, disaggregatedVar)
            chull['disaggregatedVars'][var] = disaggregatedVar
            relaxationBlockInfo['srcVars'][disaggregatedVar] = var

            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(
                disaggregatedVarName + "_bounds", bigmConstraint)
            if lb:
                bigmConstraint.add(
                    'lb', obj.indicator_var*lb <= disaggregatedVar)
            if ub:
                bigmConstraint.add(
                    'ub', disaggregatedVar <= obj.indicator_var*ub)
            chull['bigmConstraints'][var] = bigmConstraint
            relaxationBlockInfo['boundConstraintToSrcVar'][bigmConstraint] = var

        for var in localVars:
            lb = var.lb
            ub = var.ub
            if lb is None or ub is None:
                raise GDP_Error("Variables that appear in disjuncts must be "
                                "bounded in order to use the chull "
                                "transformation! Missing bound for %s."
                                % (var.name))
            if value(lb) > 0:
                var.setlb(0)
            if value(ub) < 0:
                var.setub(0)

            # naming conflicts are possible here since this is a bunch
            # of variables from different blocks coming together, so we
            # get a unique name
            conName = unique_component_name(
                relaxationBlock, var.local_name+"_bounds")
            bigmConstraint = Constraint(transBlock.lbub)
            relaxationBlock.add_component(conName, bigmConstraint)
            bigmConstraint.add('lb', obj.indicator_var*lb <= var)
            bigmConstraint.add('ub', var <= obj.indicator_var*ub)
            chull['bigmConstraints'][var] = bigmConstraint
            relaxationBlockInfo['boundConstraintToSrcVar'][bigmConstraint] = var

        var_substitute_map = dict((id(v), newV) for v, newV in
                                  iteritems(chull['disaggregatedVars']))
        zero_substitute_map = dict((id(v), ZeroConstant) for v, newV in
                                   iteritems(chull['disaggregatedVars']))
        zero_substitute_map.update((id(v), ZeroConstant)
                                   for v in localVars)

        # Transform each component within this disjunct
        self._transform_block_components(obj, obj, infodict, var_substitute_map,
                                         zero_substitute_map)

        # deactivate disjunct so we know we've relaxed it
        obj._deactivate_without_fixing_indicator()
        infodict['relaxed'] = True