Beispiel #1
0
    def test_create_abstract_from_rule(self):
        def make_invalid(m):
            m.I = RangeSet(3)
            m.x = Var(m.I)
            m.c = Constraint(expr=sum(m.x[i] for i in m.I) >= 0)

        def make(m):
            m.I = RangeSet(3)
            m.x = Var(m.I)

            def c(b):
                return sum(m.x[i] for i in m.I) >= 0

            m.c = Constraint(rule=c)

        model = AbstractModel(rule=make_invalid)
        self.assertRaises(RuntimeError, model.create_instance)

        model = AbstractModel(rule=make)
        instance = model.create_instance()
        self.assertEqual([x.local_name for x in model.component_objects()], [])
        self.assertEqual([x.local_name for x in instance.component_objects()],
                         ['I', 'x', 'c'])
        self.assertEqual(len(list(identify_variables(instance.c.body))), 3)

        model = AbstractModel(rule=make)
        model.y = Var()
        instance = model.create_instance()
        self.assertEqual([x.local_name for x in instance.component_objects()],
                         ['y', 'I', 'x', 'c'])
        self.assertEqual(len(list(identify_variables(instance.c.body))), 3)
Beispiel #2
0
    def test_create_abstract_from_rule(self):
        def make_invalid(m):
            m.I = RangeSet(3)
            m.x = Var(m.I)
            m.c = Constraint( expr=sum(m.x[i] for i in m.I) >= 0 )

        def make(m):
            m.I = RangeSet(3)
            m.x = Var(m.I)
            def c(b):
                return sum(m.x[i] for i in m.I) >= 0
            m.c = Constraint( rule=c )

        model = AbstractModel(rule=make_invalid)
        self.assertRaises(RuntimeError, model.create_instance)

        model = AbstractModel(rule=make)
        instance = model.create_instance()
        self.assertEqual( [x.cname() for x in model.component_objects()],
                          [] )
        self.assertEqual( [x.cname() for x in instance.component_objects()],
                          ['I','x','c'] )
        self.assertEqual( len(list(identify_variables(instance.c.body))), 3 )

        model = AbstractModel(rule=make)
        model.y = Var()
        instance = model.create_instance()
        self.assertEqual( [x.cname() for x in instance.component_objects()],
                          ['y','I','x','c'] )
        self.assertEqual( len(list(identify_variables(instance.c.body))), 3 )
Beispiel #3
0
    def test_1(self):
        '''
        The simplest case that the black box has only two inputs and there is only one black block involved
        '''
        def blackbox(a, b):
            return sin(a - b)

        m = self.m
        bb = ExternalFunction(blackbox)
        m.eflist = [bb]
        m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + bb(m.x[0], m.x[1]) == 2 *
                          sqrt(2.0))
        pI = PyomoInterface(m, [bb])
        self.assertEqual(pI.lx, 2)
        self.assertEqual(pI.ly, 1)
        self.assertEqual(pI.lz, 3)
        self.assertEqual(len(list(identify_variables(m.c1.body))), 3)
        self.assertEqual(len(list(identify_variables(m.c2.body))), 2)
Beispiel #4
0
 def test_create_concrete_from_rule(self):
     def make(m):
         m.I = RangeSet(3)
         m.x = Var(m.I)
         m.c = Constraint( expr=sum(m.x[i] for i in m.I) >= 0 )
     model = ConcreteModel(rule=make)
     self.assertEqual( [x.cname() for x in model.component_objects()],
                       ['I','x','c'] )
     self.assertEqual( len(list(identify_variables(model.c.body))), 3 )
Beispiel #5
0
    def test_2(self):
        '''
        The simplest case that the black box has only one inputs and there is only a formula
        '''
        def blackbox(a):
            return sin(a)

        m = self.m
        bb = ExternalFunction(blackbox)
        m.eflist = [bb]
        m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + bb(m.x[0] - m.x[1]) == 2 *
                          sqrt(2.0))
        pI = PyomoInterface(m, [bb])
        self.assertEqual(pI.lx, 1)
        self.assertEqual(pI.ly, 1)
        self.assertEqual(pI.lz, 5)
        self.assertEqual(len(list(identify_variables(m.c1.body))), 3)
        self.assertEqual(len(list(identify_variables(m.c2.body))), 2)
        self.assertEqual(len(m.tR.conset), 1)
        self.assertEqual(len(list(identify_variables(m.tR.conset[1].body))), 3)
Beispiel #6
0
def differentiate(expr, wrt=None, wrt_list=None):
    if not _sympy_available:
        raise RuntimeError(
            "The sympy module is not available.  "
            "Cannot perform automatic symbolic differentiation.")

    if not (( wrt is None ) ^ ( wrt_list is None )):
        raise ValueError(
            "differentiate(): Must specify exactly one of wrt and wrt_list")
    if wrt is not None:
        wrt_list = [ wrt ]
    else:
        # Copy the list because we will normalize things in place below
        wrt_list = list(wrt_list)

    pyomo_vars = list(EXPR.identify_variables(expr))
    sympy_vars = [sympy.var('x%s'% i) for i in range(len(pyomo_vars))]
    sympy2pyomo = dict( zip(sympy_vars, pyomo_vars) )
    pyomo2sympy = dict( (id(pyomo_vars[i]), sympy_vars[i])
                         for i in range(len(pyomo_vars)) )

    ans = []
    for i, target in enumerate(wrt_list):
        if target.__class__ is not tuple:
            wrt_list[i] = target = (target,)
        mismatch_target = False
        for var in target:
            if id(var) not in pyomo2sympy:
                mismatch_target = True
                break
        wrt_list[i] = tuple( pyomo2sympy.get(id(var),None) for var in target )
        ans.append(0 if mismatch_target else None)

    # If there is nothing to do, do nothing
    if all(i is not None for i in ans):
        return ans if wrt is None else ans[0]

    tmp_expr = EXPR.clone_expression( expr, substitute=pyomo2sympy )
    tmp_expr = _map_intrinsic_functions(tmp_expr, sympy2pyomo)
    tmp_expr = str(tmp_expr)

    sympy_expr = sympy.sympify(
        tmp_expr, locals=dict((str(x), x) for x in sympy_vars) )

    for i, target in enumerate(wrt_list):
        if ans[i] is None:
            sympy_ans = sympy_expr.diff(*target)
            ans[i] = _map_sympy2pyomo(sympy_ans, sympy2pyomo)

    return ans if wrt is None else ans[0]
Beispiel #7
0
def differentiate(expr, wrt=None, wrt_list=None):
    if not _sympy_available:
        raise RuntimeError(
            "The sympy module is not available.  "
            "Cannot perform automatic symbolic differentiation.")

    if not ((wrt is None) ^ (wrt_list is None)):
        raise ValueError(
            "differentiate(): Must specify exactly one of wrt and wrt_list")
    if wrt is not None:
        wrt_list = [wrt]
    else:
        # Copy the list because we will normalize things in place below
        wrt_list = list(wrt_list)

    pyomo_vars = list(EXPR.identify_variables(expr))
    sympy_vars = [sympy.var('x%s' % i) for i in range(len(pyomo_vars))]
    sympy2pyomo = dict(zip(sympy_vars, pyomo_vars))
    pyomo2sympy = dict(
        (id(pyomo_vars[i]), sympy_vars[i]) for i in range(len(pyomo_vars)))

    ans = []
    for i, target in enumerate(wrt_list):
        if target.__class__ is not tuple:
            wrt_list[i] = target = (target, )
        mismatch_target = False
        for var in target:
            if id(var) not in pyomo2sympy:
                mismatch_target = True
                break
        wrt_list[i] = tuple(pyomo2sympy.get(id(var), None) for var in target)
        ans.append(0 if mismatch_target else None)

    # If there is nothing to do, do nothing
    if all(i is not None for i in ans):
        return ans if wrt is None else ans[0]

    tmp_expr = EXPR.clone_expression(expr, substitute=pyomo2sympy)
    tmp_expr = _map_intrinsic_functions(tmp_expr, sympy2pyomo)
    tmp_expr = str(tmp_expr)

    sympy_expr = sympy.sympify(tmp_expr,
                               locals=dict((str(x), x) for x in sympy_vars))

    for i, target in enumerate(wrt_list):
        if ans[i] is None:
            sympy_ans = sympy_expr.diff(*target)
            ans[i] = _map_sympy2pyomo(sympy_ans, sympy2pyomo)

    return ans if wrt is None else ans[0]
    def test_zero_term_removal(self):
        """Test for removing zero terms from linear constraints."""
        m = ConcreteModel()
        m.v0 = Var()
        m.v1 = Var()
        m.v2 = Var()
        m.v3 = Var()
        m.c = Constraint(expr=m.v0 == m.v1 * m.v2 + m.v3)
        m.c2 = Constraint(expr=m.v1 * m.v2 + m.v3 <= m.v0)
        m.c3 = Constraint(expr=m.v0 <= m.v1 * m.v2 + m.v3)
        m.c4 = Constraint(expr=1 <= m.v1 * m.v2 + m.v3 <= 3)
        m.v1.fix(0)

        TransformationFactory('contrib.remove_zero_terms').apply_to(m)
        m.v1.unfix()
        # Check that the term no longer exists
        self.assertFalse(
            any(id(m.v1) == id(v) for v in identify_variables(m.c.body)))
        self.assertFalse(
            any(id(m.v1) == id(v) for v in identify_variables(m.c2.body)))
        self.assertFalse(
            any(id(m.v1) == id(v) for v in identify_variables(m.c3.body)))
        self.assertFalse(
            any(id(m.v1) == id(v) for v in identify_variables(m.c4.body)))
Beispiel #9
0
    def _apply_to(self, instance, **kwds):
        if __debug__ and logger.isEnabledFor(logging.DEBUG):   #pragma:nocover
            logger.debug("Calling ConnectorExpander")

        connectorsFound = False
        for c in instance.component_data_objects(Connector):
            connectorsFound = True
            break
        if not connectorsFound:
            return

        if __debug__ and logger.isEnabledFor(logging.DEBUG):   #pragma:nocover
            logger.debug("   Connectors found!")

        #
        # At this point, there are connectors in the model, so we must
        # look for constraints that involve connectors and expand them.
        #
        connector_types = set([SimpleConnector, _ConnectorData])
        constraint_list = []
        connector_list = []
        matched_connectors = {}
        found = dict()
        for constraint in instance.component_data_objects(Constraint):
            for c in expr.identify_variables(
                    constraint.body, include_potentially_variable=True):
                if c.__class__ in connector_types:
                    found[id(c)] = c
            if not found:
                continue

            # Note that it is important to copy the set of found
            # connectors, since the matching routine below will
            # manipulate sets in place.
            found_this_constraint = dict(found)
            constraint_list.append( (constraint, found_this_constraint) )

            # Find all the connectors that are used in the constraint,
            # so we know which connectors to validate against each
            # other.  Note that the validation must be transitive (that
            # is, if con1 has a & b and con2 has b & c, then a,b, and c
            # must all validate against each other.
            for cId, c in iteritems(found_this_constraint):
                if cId in matched_connectors:
                    oldSet = matched_connectors[cId]
                    found.update( oldSet )
                    for _cId in oldSet:
                        matched_connectors[_cId] = found
                else:
                    connector_list.append(c)
                matched_connectors[cId] = found

            # Reset found back to empty (this is more efficient as the
            # bulk of the constraints in the model will not have
            # connectors - so if we did this at the top of the loop, we
            # would spend a lot of time clearing empty sets
            found = {}

        # Validate all connector sets and expand the empty ones
        known_conn_sets = {}
        for connector in connector_list:
            conn_set = matched_connectors[id(connector)]
            if id(conn_set) in known_conn_sets:
                continue
            known_conn_sets[id(conn_set)] \
                = self._validate_and_expand_connector_set(conn_set)

        # Expand each constraint
        for constraint, conn_set in constraint_list:
            cList = ConstraintList()
            constraint.parent_block().add_component(
                '%s.expanded' % ( constraint.local_name, ), cList )
            connId = next(iterkeys(conn_set))
            ref = known_conn_sets[id(matched_connectors[connId])]
            for k,v in sorted(iteritems(ref)):
                if v[1] >= 0:
                    _iter = v[0]
                else:
                    _iter = (v[0],)
                for idx in _iter:
                    substitution = {}
                    for c in itervalues(conn_set):
                        if v[1] >= 0:
                            new_v = c.vars[k][idx]
                        elif k in c.aggregators:
                            new_v = c.vars[k].add()
                        else:
                            new_v = c.vars[k]
                        substitution[id(c)] = new_v
                    cList.add((
                        constraint.lower,
                        expr.clone_expression( constraint.body, substitution ),
                        constraint.upper ))
            constraint.deactivate()

        # Now, go back and implement VarList aggregators
        for conn in connector_list:
            block = conn.parent_block()
            for var, aggregator in iteritems(conn.aggregators):
                c = Constraint(expr=aggregator(block, conn.vars[var]))
                block.add_component(
                    '%s.%s.aggregate' % (conn.local_name, var), c )
    def _apply_to(self, instance, **kwds):
        if __debug__ and logger.isEnabledFor(logging.DEBUG):  #pragma:nocover
            logger.debug("Calling ConnectorExpander")

        connectorsFound = False
        for c in instance.component_data_objects(Connector):
            connectorsFound = True
            break
        if not connectorsFound:
            return

        if __debug__ and logger.isEnabledFor(logging.DEBUG):  #pragma:nocover
            logger.debug("   Connectors found!")

        #
        # At this point, there are connectors in the model, so we must
        # look for constraints that involve connectors and expand them.
        #
        connector_types = set([SimpleConnector, _ConnectorData])
        constraint_list = []
        connector_list = []
        matched_connectors = {}
        found = dict()
        for constraint in instance.component_data_objects(Constraint):
            for c in expr.identify_variables(
                    constraint.body, include_potentially_variable=True):
                if c.__class__ in connector_types:
                    found[id(c)] = c
            if not found:
                continue

            # Note that it is important to copy the set of found
            # connectors, since the matching routine below will
            # manipulate sets in place.
            found_this_constraint = dict(found)
            constraint_list.append((constraint, found_this_constraint))

            # Find all the connectors that are used in the constraint,
            # so we know which connectors to validate against each
            # other.  Note that the validation must be transitive (that
            # is, if con1 has a & b and con2 has b & c, then a,b, and c
            # must all validate against each other.
            for cId, c in iteritems(found_this_constraint):
                if cId in matched_connectors:
                    oldSet = matched_connectors[cId]
                    found.update(oldSet)
                    for _cId in oldSet:
                        matched_connectors[_cId] = found
                else:
                    connector_list.append(c)
                matched_connectors[cId] = found

            # Reset found back to empty (this is more efficient as the
            # bulk of the constraints in the model will not have
            # connectors - so if we did this at the top of the loop, we
            # would spend a lot of time clearing empty sets
            found = {}

        # Validate all connector sets and expand the empty ones
        known_conn_sets = {}
        for connector in connector_list:
            conn_set = matched_connectors[id(connector)]
            if id(conn_set) in known_conn_sets:
                continue
            known_conn_sets[id(conn_set)] \
                = self._validate_and_expand_connector_set(conn_set)

        # Expand each constraint
        for constraint, conn_set in constraint_list:
            cList = ConstraintList()
            constraint.parent_block().add_component(
                '%s.expanded' % (constraint.local_name, ), cList)
            connId = next(iterkeys(conn_set))
            ref = known_conn_sets[id(matched_connectors[connId])]
            for k, v in sorted(iteritems(ref)):
                if v[1] >= 0:
                    _iter = v[0]
                else:
                    _iter = (v[0], )
                for idx in _iter:
                    substitution = {}
                    for c in itervalues(conn_set):
                        if v[1] >= 0:
                            new_v = c.vars[k][idx]
                        elif k in c.aggregators:
                            new_v = c.vars[k].add()
                        else:
                            new_v = c.vars[k]
                        substitution[id(c)] = new_v
                    cList.add((constraint.lower,
                               expr.clone_expression(constraint.body,
                                                     substitution),
                               constraint.upper))
            constraint.deactivate()

        # Now, go back and implement VarList aggregators
        for conn in connector_list:
            block = conn.parent_block()
            for var, aggregator in iteritems(conn.aggregators):
                c = Constraint(expr=aggregator(block, conn.vars[var]))
                block.add_component('%s.%s.aggregate' % (conn.local_name, var),
                                    c)
Beispiel #11
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(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)
Beispiel #12
0
    def _transformDisjunctionData(self, obj, transBlock, index):
        # Convex hull 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 convex hull transformation for "
                            "disjunction %s with or constraint. Must be an xor!"
                            % obj.name)

        parent_component = obj.parent_component()
        transBlock.disjContainers.add(parent_component)
        orConstraint, disaggregationConstraint \
            = self._getDisjunctionConstraints(parent_component)

        # We first go through and collect all the variables that we
        # are going to disaggregate.
        varOrder_set = ComponentSet()
        varOrder = []
        varsByDisjunct = ComponentMap()
        for disjunct in obj.disjuncts:
            # This is crazy, but if the disjunct has been previously
            # relaxed, the disjunct *could* be deactivated.
            not_active = not disjunct.active
            if not_active:
                disjunct._activate_without_unfixing_indicator()
            try:
                disjunctVars = varsByDisjunct[disjunct] = ComponentSet()
                for cons in disjunct.component_data_objects(
                        Constraint,
                        active = True,
                        sort=SortComponents.deterministic,
                        descend_into=Block):
                    # we aren't going to disaggregate fixed
                    # variables. This means there is trouble if they are
                    # unfixed later...
                    for var in identify_variables(
                            cons.body, include_fixed=False):
                        # 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 var not in varOrder_set:
                            varOrder.append(var)
                            varOrder_set.add(var)
            finally:
                if not_active:
                    disjunct._deactivate_without_fixing_indicator()

        # We will only disaggregate variables that
        #  1) appear in multiple disjuncts, or
        #  2) are not contained in this disjunct, or
        #  3) are not themselves disaggregated variables
        varSet = []
        localVars = ComponentMap((d,[]) for d in obj.disjuncts)
        for var in varOrder:
            disjuncts = [d for d in varsByDisjunct if var in varsByDisjunct[d]]
            if len(disjuncts) > 1:
                varSet.append(var)
            elif self._contained_in(var, disjuncts[0]):
                localVars[disjuncts[0]].append(var)
            elif self._contained_in(var, transBlock):
                # There is nothing to do here: these are already
                # disaggregated vars that can/will be forced to 0 when
                # their disjunct is not active.
                pass
            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[disjunct])
        orConstraint.add(index, (or_expr, 1))

        for i, var in enumerate(varSet):
            disaggregatedExpr = 0
            for disjunct in obj.disjuncts:
                if 'chull' not in disjunct._gdp_transformation_info:
                    if not disjunct.indicator_var.is_fixed() \
                            or value(disjunct.indicator_var) != 0:
                        raise RuntimeError(
                            "GDP chull: disjunct was not relaxed, but "
                            "does not appear to be correctly deactivated.")
                    continue
                disaggregatedVar = disjunct._gdp_transformation_info['chull'][
                    'disaggregatedVars'][var]
                disaggregatedExpr += disaggregatedVar
            if type(index) is tuple:
                consIdx = index + (i,)
            elif parent_component.is_indexed():
                consIdx = (index,) + (i,)
            else:
                consIdx = i

            disaggregationConstraint.add(
                consIdx,
                var == disaggregatedExpr)