示例#1
0
    def _f(m: Block):
        m.lmbda = Var(vertices, domain=NonNegativeReals)  # 非负
        m.y = Var(simplices, domain=Binary)  # 二进制

        m.a0 = Constraint(dimensions, rule=lambda m, d: sum(m.lmbda[v] * pointsT[d][v] for v in vertices) == input[d])
        if bound == 'eq':
            m.a1 = Constraint(expr=output == sum(m.lmbda[v] * values[v] for v in vertices))
        elif bound == 'lb':
            m.a1 = Constraint(expr=output <= sum(m.lmbda[v] * values[v] for v in vertices))
        elif bound == 'ub':
            m.a1 = Constraint(expr=output >= sum(m.lmbda[v] * values[v] for v in vertices))
        else:
            raise RuntimeError("bound值错误!bound=" + bound)

        m.b = Constraint(expr=sum(m.lmbda[v] for v in vertices) == 1)

        # generate a map from vertex index to simplex index,
        # which avoids an n^2 lookup when generating the
        # constraint
        vertex_to_simplex = [[] for _ in vertices]
        for s, simplex in enumerate(tri.simplices):
            for v in simplex:
                vertex_to_simplex[v].append(s)
        m.c0 = Constraint(vertices, rule=lambda m, v: m.lmbda[v] <= sum(m.y[s] for s in vertex_to_simplex[v]))
        m.c1 = Constraint(expr=sum(m.y[s] for s in simplices) == 1)
        return m
示例#2
0
def dlog(m: Block,
         tri: qhull.Delaunay,
         values: List[float],
         input: List[SimpleVar] = None,
         output: SimpleVar = None,
         bound: str = 'eq',
         **kw):
    values = np.array(values).tolist()
    ndim = len(input)
    nsimplices = len(tri.simplices)
    npoints = len(tri.points)
    pointsT = list(zip(*tri.points))
    # create index objects
    dimensions = list(range(ndim))
    simplices = list(range(nsimplices))  # 跟单纯形 数量一致
    vertices = list(range(npoints))
    bound = bound.lower()
    L = int(math.ceil(math.log2(nsimplices)))
    L_Range = list(range(L))
    vp = [0, 1, 2]
    #
    m.lmbda = Var(simplices, vp, domain=NonNegativeReals)  # 非负
    m.a0 = Constraint(
        dimensions,
        rule=lambda m, d: sum(m.lmbda[s, v] * pointsT[d][tri.simplices[s][v]]
                              for s in simplices for v in vp) == input[d])
    if bound == 'eq':
        m.a1 = Constraint(expr=output == sum(
            m.lmbda[s, v] * values[tri.simplices[s][v]] for s in simplices
            for v in vp))
    elif bound == 'lb':
        m.a1 = Constraint(
            expr=output <= sum(m.lmbda[s, v] * values[tri.simplices[s][v]]
                               for s in simplices for v in vp))
    elif bound == 'ub':
        m.a1 = Constraint(
            expr=output >= sum(m.lmbda[s, v] * values[tri.simplices[s][v]]
                               for s in simplices for v in vp))
    else:
        raise RuntimeError("bound值错误!bound=" + bound)

    m.b1 = Constraint(expr=sum(m.lmbda[s, v] for s in simplices
                               for v in vp) == 1)

    m.y = Var(L_Range, domain=Binary)  # 二进制

    m.c0 = Constraint(L_Range,
                      rule=lambda m, l: sum(m.lmbda[s, v] for s in simplices
                                            if bin(s)[2:].zfill(L)[l] == '1'
                                            for v in vp) <= m.y[l])
    m.c1 = Constraint(L_Range,
                      rule=lambda m, l: sum(m.lmbda[s, v] for s in simplices
                                            if bin(s)[2:].zfill(L)[l] == '0'
                                            for v in vp) <= 1 - m.y[l])
    return m
示例#3
0
def cc(m: Block,
       tri: qhull.Delaunay,
       values: List[float],
       input: List[SimpleVar] = None,
       output: SimpleVar = None,
       bound: str = 'eq',
       **kw):
    values = np.array(values).tolist()
    ndim = len(input)
    nsimplices = len(tri.simplices)
    npoints = len(tri.points)
    pointsT = list(zip(*tri.points))
    # create index objects
    dimensions = list(range(ndim))
    simplices = list(range(nsimplices))  # 跟单纯形 数量一致
    vertices = list(range(npoints))
    bound = bound.lower()

    m.lmbda = Var(vertices, domain=NonNegativeReals)  # 非负
    m.y = Var(simplices, domain=Binary)  # 二进制
    # m.y = Var(simplices, domain=NonNegativeReals, bounds=(0, 1))  # 二进制

    m.a0 = Constraint(dimensions,
                      rule=lambda m, d: sum(m.lmbda[v] * pointsT[d][v]
                                            for v in vertices) == input[d])
    if bound == 'eq':
        m.a1 = Constraint(expr=output == sum(m.lmbda[v] * values[v]
                                             for v in vertices))
    elif bound == 'lb':
        m.a1 = Constraint(expr=output <= sum(m.lmbda[v] * values[v]
                                             for v in vertices))
    elif bound == 'ub':
        m.a1 = Constraint(expr=output >= sum(m.lmbda[v] * values[v]
                                             for v in vertices))
    else:
        raise RuntimeError("bound值错误!bound=" + bound)

    m.b = Constraint(expr=sum(m.lmbda[v] for v in vertices) == 1)

    # generate a map from vertex index to simplex index,
    # which avoids an n^2 lookup when generating the
    # constraint
    vertex_to_simplex = [[] for _ in vertices]
    for s, simplex in enumerate(tri.simplices):
        for v in simplex:
            vertex_to_simplex[v].append(s)
    m.c0 = Constraint(
        vertices,
        rule=lambda m, v: m.lmbda[v] <= sum(m.y[s]
                                            for s in vertex_to_simplex[v]))
    m.c1 = Constraint(expr=sum(m.y[s] for s in simplices) == 1)
    return m
示例#4
0
def create_submodel_kkt_block(instance, submodel, deterministic,
                              fixed_upper_vars):
    """
    Add optimality conditions for the submodel

    This assumes that the original model has the form:

        min c1*x + d1*y
            A3*x <= b3
            A1*x + B1*y <= b1
            min c2*x + d2*y + x'*Q*y
                A2*x + B2*y + x'*E2*y <= b2
                y >= 0

    NOTE THE VARIABLE BOUNDS!
    """
    fixed_vars = {id(v) for v in fixed_upper_vars}
    #
    # Populate the block with the linear constraints.
    # Note that we don't simply clone the current block.
    # We need to collect a single set of equations that
    # can be easily expressed.
    #
    d2 = {}
    B2 = {}
    vtmp = {}
    utmp = {}
    sids_set = set()
    sids_list = []
    #
    block = Block(concrete=True)
    block.u = VarList(
    )  # Note: Dual variables associated to bounds in primal problem
    block.v = VarList(
    )  # Note: Dual variables associated to constraints in primal problem
    block.c1 = ConstraintList()
    block.c2 = ComplementarityList()
    block.c3 = ComplementarityList()
    #
    # Collect submodel objective terms
    #
    # TODO: detect fixed variables
    #
    for odata in submodel.component_data_objects(Objective, active=True):
        if odata.sense == maximize:
            d_sense = -1
        else:
            d_sense = 1
        #
        # Iterate through the variables in the representation
        #
        o_terms = generate_standard_repn(odata.expr, compute_values=False)
        #
        # Linear terms
        #
        for i, var in enumerate(o_terms.linear_vars):
            if id(var) in fixed_vars:
                #
                # Skip fixed upper variables
                #
                continue
            #
            # Store the coefficient for the variable.  The coefficient is
            # negated if the objective is maximized.
            #
            id_ = id(var)
            d2[id_] = d_sense * o_terms.linear_coefs[i]
            if not id_ in sids_set:
                sids_set.add(id_)
                sids_list.append(id_)
        #
        # Quadratic terms
        #
        for i, var in enumerate(o_terms.quadratic_vars):
            if id(var[0]) in fixed_vars:
                if id(var[1]) in fixed_vars:
                    #
                    # Skip fixed upper variables
                    #
                    continue
                #
                # Add the linear term
                #
                id_ = id(var[1])
                d2[id_] = d2.get(
                    id_, 0) + d_sense * o_terms.quadratic_coefs[i] * var[0]
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
            elif id(var[1]) in fixed_vars:
                #
                # Add the linear term
                #
                id_ = id(var[0])
                d2[id_] = d2.get(
                    id_, 0) + d_sense * o_terms.quadratic_coefs[i] * var[1]
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
            else:
                raise RuntimeError(
                    "Cannot apply this transformation to a problem with \
quadratic terms where both variables are in the lower level.")
        #
        # Stop after the first objective
        #
        break
    #
    # Iterate through all lower level variables, adding dual variables
    # and complementarity slackness conditions for y bound constraints
    #
    for vcomponent in instance.component_objects(Var, active=True):
        for ndx in vcomponent:
            if id(vcomponent[ndx]) in fixed_vars:
                #
                # Skip fixed upper variables
                #
                continue
            #
            # For each index, get the bounds for the variable
            #
            lb, ub = vcomponent[ndx].bounds
            if not lb is None:
                #
                # Add the complementarity slackness condition for a lower bound
                #
                v = block.v.add()
                block.c3.add(complements(vcomponent[ndx] >= lb, v >= 0))
            else:
                v = None
            if not ub is None:
                #
                # Add the complementarity slackness condition for an upper bound
                #
                w = block.v.add()
                vtmp[id(vcomponent[ndx])] = w
                block.c3.add(complements(vcomponent[ndx] <= ub, w >= 0))
            else:
                w = None
            if not (v is None and w is None):
                #
                # Record the variables for which complementarity slackness conditions
                # were created.
                #
                id_ = id(vcomponent[ndx])
                vtmp[id_] = (v, w)
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
    #
    # Iterate through all constraints, adding dual variables and
    # complementary slackness conditions (for inequality constraints)
    #
    for cdata in submodel.component_data_objects(Constraint, active=True):
        if cdata.equality:
            # Don't add a complementary slackness condition for an equality constraint
            u = block.u.add()
            utmp[id(cdata)] = (None, u)
        else:
            if not cdata.lower is None:
                #
                # Add the complementarity slackness condition for a greater-than inequality
                #
                u = block.u.add()
                block.c2.add(complements(-cdata.body <= -cdata.lower, u >= 0))
            else:
                u = None
            if not cdata.upper is None:
                #
                # Add the complementarity slackness condition for a less-than inequality
                #
                w = block.u.add()
                block.c2.add(complements(cdata.body <= cdata.upper, w >= 0))
            else:
                w = None
            if not (u is None and w is None):
                utmp[id(cdata)] = (u, w)
        #
        # Store the coefficients for the constraint variables that are not fixed
        #
        c_terms = generate_standard_repn(cdata.body, compute_values=False)
        #
        # Linear terms
        #
        for i, var in enumerate(c_terms.linear_vars):
            if id(var) in fixed_vars:
                continue
            id_ = id(var)
            B2.setdefault(id_, {}).setdefault(id(cdata),
                                              c_terms.linear_coefs[i])
            if not id_ in sids_set:
                sids_set.add(id_)
                sids_list.append(id_)
        #
        # Quadratic terms
        #
        for i, var in enumerate(c_terms.quadratic_vars):
            if id(var[0]) in fixed_vars:
                if id(var[1]) in fixed_vars:
                    continue
                id_ = id(var[1])
                if id_ in B2:
                    B2[id_][id(cdata)] = c_terms.quadratic_coefs[i] * var[0]
                else:
                    B2.setdefault(id_, {}).setdefault(
                        id(cdata), c_terms.quadratic_coefs[i] * var[0])
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
            elif id(var[1]) in fixed_vars:
                id_ = id(var[0])
                if id_ in B2:
                    B2[id_][id(cdata)] = c_terms.quadratic_coefs[i] * var[1]
                else:
                    B2.setdefault(id_, {}).setdefault(
                        id(cdata), c_terms.quadratic_coefs[i] * var[1])
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
            else:
                raise RuntimeError(
                    "Cannot apply this transformation to a problem with \
quadratic terms where both variables are in the lower level.")
    #
    # Generate stationarity equations
    #
    tmp__ = (None, None)
    for vid in sids_list:
        exp = d2.get(vid, 0)
        #
        lb_dual, ub_dual = vtmp.get(vid, tmp__)
        if vid in vtmp:
            if not lb_dual is None:
                exp -= lb_dual  # dual for variable lower bound
            if not ub_dual is None:
                exp += ub_dual  # dual for variable upper bound
        #
        B2_ = B2.get(vid, {})
        utmp_keys = list(utmp.keys())
        if deterministic:
            utmp_keys.sort(key=lambda x: utmp[x][0].local_name\
                           if utmp[x][1] is None else utmp[x][1].local_name)
        for uid in utmp_keys:
            if uid in B2_:
                lb_dual, ub_dual = utmp[uid]
                if not lb_dual is None:
                    exp -= B2_[uid] * lb_dual
                if not ub_dual is None:
                    exp += B2_[uid] * ub_dual
        if type(exp) in six.integer_types or type(exp) is float:
            # TODO: Annotate the model as unbounded
            raise IOError("Unbounded variable without side constraints")
        block.c1.add(exp == 0)
    return block