Esempio n. 1
0
def getqconstrmultipliers(cplex, tol):
    qpi = dict()

    x = dict(zip(cplex.variables.get_names(),
                 cplex.solution.get_values()))

    # Helper function to map a variable index to a variable name
    v2name = lambda x: cplex.variables.get_names(x)

    for q in cplex.quadratic_constraints.get_names():
        # 'dense' is the dense slack vector
        dense = dict(zip(cplex.variables.get_names(),
                         [0] * cplex.variables.get_num()))
        grad = dict(zip(cplex.variables.get_names(),
                        [0] * cplex.variables.get_num()))
        qdslack = cplex.solution.get_quadratic_dualslack(q)
        for (var, val) in zip(map(v2name, qdslack.ind), qdslack.val):
            dense[var] = val

        # Compute value of derivative at optimal solution.
        # The derivative of a quadratic constraint x^TQx + a^Tx + b <= 0
        # is Q^Tx + Qx + a.
        conetop = True
        quad = cplex.quadratic_constraints.get_quadratic_components(q)
        for (row, col, val) in zip(map(v2name, quad.ind1),
                                   map(v2name, quad.ind2),
                                   quad.val):
            if fabs(x[row]) > tol or fabs(x[col]) > tol:
                conetop = False
            grad[row] += val * x[col]
            grad[col] += val * x[row]
        l = cplex.quadratic_constraints.get_linear_components(q)
        for (var, val) in zip(map(v2name, l.ind), l.val):
            grad[var] += val
            if fabs(x[var]) > tol:
                conetop = False

        if conetop:
            raise Exception("Cannot compute dual multiplier at cone top!")

        # Compute qpi[q] as slack/gradient.
        # We may have several indices to choose from and use the one
        # with largest absolute value in the denominator.
        ok = False
        maxabs = -1.0
        for v in cplex.variables.get_names():
            if fabs(grad[v]) > tol:
                if fabs(grad[v]) > maxabs:
                    qpi[q] = dense[v] / grad[v]
                    maxabs = fabs(grad[v])
                ok = True

        if not ok:
            qpi[q] = 0

    return qpi
Esempio n. 2
0
def getqconstrmultipliers(cplex, tol):
    qpi = dict()

    x = dict(zip(cplex.variables.get_names(), cplex.solution.get_values()))

    # Helper function to map a variable index to a variable name
    def v2name(x):
        return cplex.variables.get_names(x)

    for q in cplex.quadratic_constraints.get_names():
        # 'dense' is the dense slack vector
        dense = dict(
            zip(cplex.variables.get_names(), [0] * cplex.variables.get_num()))
        grad = dict(
            zip(cplex.variables.get_names(), [0] * cplex.variables.get_num()))
        qdslack = cplex.solution.get_quadratic_dualslack(q)
        for (var, val) in zip(map(v2name, qdslack.ind), qdslack.val):
            dense[var] = val

        # Compute value of derivative at optimal solution.
        # The derivative of a quadratic constraint x^TQx + a^Tx + b <= 0
        # is Q^Tx + Qx + a.
        conetop = True
        quad = cplex.quadratic_constraints.get_quadratic_components(q)
        for (row, col, val) in zip(map(v2name, quad.ind1),
                                   map(v2name, quad.ind2), quad.val):
            if fabs(x[row]) > tol or fabs(x[col]) > tol:
                conetop = False
            grad[row] += val * x[col]
            grad[col] += val * x[row]
        l = cplex.quadratic_constraints.get_linear_components(q)
        for (var, val) in zip(map(v2name, l.ind), l.val):
            grad[var] += val
            if fabs(x[var]) > tol:
                conetop = False

        if conetop:
            raise Exception("Cannot compute dual multiplier at cone top!")

        # Compute qpi[q] as slack/gradient.
        # We may have several indices to choose from and use the one
        # with largest absolute value in the denominator.
        ok = False
        maxabs = -1.0
        for v in cplex.variables.get_names():
            if fabs(grad[v]) > tol:
                if fabs(grad[v]) > maxabs:
                    qpi[q] = dense[v] / grad[v]
                    maxabs = fabs(grad[v])
                ok = True

        if not ok:
            qpi[q] = 0

    return qpi
Esempio n. 3
0
def qcpdual():
    # ###################################################################### #
    #                                                                        #
    #    S E T U P   P R O B L E M                                           #
    #                                                                        #
    # ###################################################################### #
    c = cplex.Cplex()
    c.variables.add(obj=Data.obj, lb=Data.lb, ub=Data.ub, names=Data.cname)
    c.linear_constraints.add(lin_expr=Data.rows,
                             senses=Data.sense,
                             rhs=Data.rhs,
                             names=Data.rname)
    for q in range(0, len(Data.qname)):
        c.quadratic_constraints.add(lin_expr=Data.qlin[q],
                                    quad_expr=Data.quad[q],
                                    sense=Data.qsense[q],
                                    rhs=Data.qrhs[q],
                                    name=Data.qname[q])

    # ###################################################################### #
    #                                                                        #
    #    O P T I M I Z E   P R O B L E M                                     #
    #                                                                        #
    # ###################################################################### #
    c.parameters.barrier.qcpconvergetol.set(1e-10)
    c.solve()
    if not c.solution.get_status() == c.solution.status.optimal:
        raise Exception("No optimal solution found")

    # ###################################################################### #
    #                                                                        #
    #    Q U E R Y   S O L U T I O N                                         #
    #                                                                        #
    # ###################################################################### #
    # We store all results in a dictionary so that we can easily access
    # them by name.
    # Optimal solution and slacks for linear and quadratic constraints.
    x = dict(zip(c.variables.get_names(), c.solution.get_values()))
    slack = dict(
        zip(c.linear_constraints.get_names(), c.solution.get_linear_slacks()))
    qslack = dict(
        zip(c.quadratic_constraints.get_names(),
            c.solution.get_quadratic_slacks()))
    # Dual multipliers for constraints.
    cpi = dict(zip(c.variables.get_names(), c.solution.get_reduced_costs()))
    rpi = dict(
        zip(c.linear_constraints.get_names(), c.solution.get_dual_values()))
    qpi = getqconstrmultipliers(c, ZEROTOL)

    # Some CPLEX functions return results by index instead of by name.
    # Define a function that translates from index to name.
    v2name = lambda x: c.variables.get_names(x)

    # ###################################################################### #
    #                                                                        #
    #    C H E C K   K K T   C O N D I T I O N S                             #
    #                                                                        #
    #    Here we verify that the optimal solution computed by CPLEX (and     #
    #    the qpi[] values computed above) satisfy the KKT conditions.        #
    #                                                                        #
    # ###################################################################### #

    # Primal feasibility: This example is about duals so we skip this test. #

    # Dual feasibility: We must verify
    # - for <= constraints (linear or quadratic) the dual
    #   multiplier is non-positive.
    # - for >= constraints (linear or quadratic) the dual
    #   multiplier is non-negative.
    for r in c.linear_constraints.get_names():
        if c.linear_constraints.get_senses(r) == 'E':
            pass
        elif c.linear_constraints.get_senses(r) == 'R':
            pass
        elif c.linear_constraints.get_senses(r) == 'L':
            if rpi[r] > ZEROTOL:
                raise Exception("Dual feasibility test failed")
        elif c.linear_constraints.get_senses(r) == 'G':
            if rpi[r] < -ZEROTOL:
                raise Exception("Dual feasibility test failed")
    for q in c.quadratic_constraints.get_names():
        if c.quadratic_constraints.get_senses(q) == 'E':
            pass
        elif c.quadratic_constraints.get_senses(q) == 'L':
            if qpi[q] > ZEROTOL:
                raise Exception("Dual feasibility test failed")
        elif c.quadratic_constraints.get_senses(q) == 'G':
            if qpi[q] < -ZEROTOL:
                raise Exception("Dual feasibility test failed")

    # Complementary slackness.
    # For any constraint the product of primal slack and dual multiplier
    # must be 0.
    for r in c.linear_constraints.get_names():
        if ((not c.linear_constraints.get_senses(r) == 'E')
                and fabs(slack[r] * rpi[r]) > ZEROTOL):
            raise Exception("Complementary slackness test failed")
    for q in c.quadratic_constraints.get_names():
        if ((not c.quadratic_constraints.get_senses(q) == 'E')
                and fabs(qslack[q] * qpi[q]) > ZEROTOL):
            raise Exception("Complementary slackness test failed")
    for j in c.variables.get_names():
        if c.variables.get_upper_bounds(j) < cplex.infinity:
            slk = c.variables.get_upper_bounds(j) - x[j]
            if cpi[j] < -ZEROTOL:
                dual = cpi[j]
            else:
                dual = 0.0
            if fabs(slk * dual) > ZEROTOL:
                raise Exception("Complementary slackness test failed")
        if c.variables.get_lower_bounds(j) > -cplex.infinity:
            slk = x[j] - c.variables.get_lower_bounds(j)
            if cpi[j] > ZEROTOL:
                dual = cpi[j]
            else:
                dual = 0.0
            if fabs(slk * dual) > ZEROTOL:
                raise Exception("Complementary slackness test failed")

    # Stationarity.
    # The difference between objective function and gradient at optimal
    # solution multiplied by dual multipliers must be 0, i.e., for the
    # optimal solution x
    # 0 == c
    #      - sum(r in rows)  r'(x)*rpi[r]
    #      - sum(q in quads) q'(x)*qpi[q]
    #      - sum(c in cols)  b'(x)*cpi[c]
    # where r' and q' are the derivatives of a row or quadratic constraint,
    # x is the optimal solution and rpi[r] and qpi[q] are the dual
    # multipliers for row r and quadratic constraint q.
    # b' is the derivative of a bound constraint and cpi[c] the dual bound
    # multiplier for column c.

    # Objective function
    kktsum = dict(zip(c.variables.get_names(), c.objective.get_linear()))

    # Linear constraints.
    # The derivative of a linear constraint ax - b (<)= 0 is just a.
    for r in c.linear_constraints.get_names():
        row = c.linear_constraints.get_rows(r)
        for (var, val) in zip(map(v2name, row.ind), row.val):
            kktsum[var] -= rpi[r] * val
    # Quadratic constraints.
    # The derivative of a constraint xQx + ax - b <= 0 is
    # Qx + Q'x + a.
    for q in c.quadratic_constraints.get_names():
        lin = c.quadratic_constraints.get_linear_components(q)
        for (var, val) in zip(map(v2name, lin.ind), lin.val):
            kktsum[var] -= qpi[q] * val
        quad = c.quadratic_constraints.get_quadratic_components(q)
        for (row, col, val) in zip(map(v2name, quad.ind1),
                                   map(v2name, quad.ind2), quad.val):
            kktsum[row] -= qpi[q] * x[col] * val
            kktsum[col] -= qpi[q] * x[row] * val

    # Bounds.
    # The derivative for lower bounds is -1 and that for upper bounds
    # is 1.
    # CPLEX already returns dj with the appropriate sign so there is
    # no need to distinguish between different bound types here.
    for v in c.variables.get_names():
        kktsum[v] -= cpi[v]

    for v in c.variables.get_names():
        if fabs(kktsum[v]) > ZEROTOL:
            raise Exception("Stationarity test failed")

    # KKT conditions satisfied. Dump out solution and dual values.
    print("Optimal solution satisfies KKT conditions.")
    print("  x[] = [", end=' ')
    for n in c.variables.get_names():
        print(" %7.3f" % x[n], end=' ')
    print(" ]")
    print("cpi[] = [", end=' ')
    for n in c.variables.get_names():
        print(" %7.3f" % cpi[n], end=' ')
    print(" ]")
    print("rpi[] = [", end=' ')
    for n in c.linear_constraints.get_names():
        print(" %7.3f" % rpi[n], end=' ')
    print(" ]")
    print("qpi[] = [", end=' ')
    for n in c.quadratic_constraints.get_names():
        print(" %7.3f" % qpi[n], end=' ')
    print(" ]")
Esempio n. 4
0
def qcpdual():
    # ###################################################################### #
    #                                                                        #
    #    S E T U P   P R O B L E M                                           #
    #                                                                        #
    # ###################################################################### #
    c = cplex.Cplex()
    c.variables.add(obj=Data.obj,
                    lb=Data.lb, ub=Data.ub, names=Data.cname)
    c.linear_constraints.add(lin_expr=Data.rows, senses=Data.sense,
                             rhs=Data.rhs, names=Data.rname)
    for q in range(0, len(Data.qname)):
        c.quadratic_constraints.add(lin_expr=Data.qlin[q],
                                    quad_expr=Data.quad[q],
                                    sense=Data.qsense[q],
                                    rhs=Data.qrhs[q],
                                    name=Data.qname[q])

    # ###################################################################### #
    #                                                                        #
    #    O P T I M I Z E   P R O B L E M                                     #
    #                                                                        #
    # ###################################################################### #
    c.parameters.barrier.qcpconvergetol.set(1e-10)
    c.solve()
    if not c.solution.get_status() == c.solution.status.optimal:
        raise Exception("No optimal solution found")

    # ###################################################################### #
    #                                                                        #
    #    Q U E R Y   S O L U T I O N                                         #
    #                                                                        #
    # ###################################################################### #
    # We store all results in a dictionary so that we can easily access
    # them by name.
    # Optimal solution and slacks for linear and quadratic constraints.
    x = dict(zip(c.variables.get_names(),
                 c.solution.get_values()))
    slack = dict(zip(c.linear_constraints.get_names(),
                     c.solution.get_linear_slacks()))
    qslack = dict(zip(c.quadratic_constraints.get_names(),
                      c.solution.get_quadratic_slacks()))
    # Dual multipliers for constraints.
    cpi = dict(zip(c.variables.get_names(),
                   c.solution.get_reduced_costs()))
    rpi = dict(zip(c.linear_constraints.get_names(),
                   c.solution.get_dual_values()))
    qpi = getqconstrmultipliers(c, ZEROTOL)

    # Some CPLEX functions return results by index instead of by name.
    # Define a function that translates from index to name.
    v2name = lambda x: c.variables.get_names(x)

    # ###################################################################### #
    #                                                                        #
    #    C H E C K   K K T   C O N D I T I O N S                             #
    #                                                                        #
    #    Here we verify that the optimal solution computed by CPLEX (and     #
    #    the qpi[] values computed above) satisfy the KKT conditions.        #
    #                                                                        #
    # ###################################################################### #

    # Primal feasibility: This example is about duals so we skip this test. #

    # Dual feasibility: We must verify
    # - for <= constraints (linear or quadratic) the dual
    #   multiplier is non-positive.
    # - for >= constraints (linear or quadratic) the dual
    #   multiplier is non-negative.
    for r in c.linear_constraints.get_names():
        if c.linear_constraints.get_senses(r) == 'E':
            pass
        elif c.linear_constraints.get_senses(r) == 'R':
            pass
        elif c.linear_constraints.get_senses(r) == 'L':
            if rpi[r] > ZEROTOL:
                raise Exception("Dual feasibility test failed")
        elif c.linear_constraints.get_senses(r) == 'G':
            if rpi[r] < -ZEROTOL:
                raise Exception("Dual feasibility test failed")
    for q in c.quadratic_constraints.get_names():
        if c.quadratic_constraints.get_senses(q) == 'E':
            pass
        elif c.quadratic_constraints.get_senses(q) == 'L':
            if qpi[q] > ZEROTOL:
                raise Exception("Dual feasibility test failed")
        elif c.quadratic_constraints.get_senses(q) == 'G':
            if qpi[q] < -ZEROTOL:
                raise Exception("Dual feasibility test failed")

    # Complementary slackness.
    # For any constraint the product of primal slack and dual multiplier
    # must be 0.
    for r in c.linear_constraints.get_names():
        if ((not c.linear_constraints.get_senses(r) == 'E') and
                fabs(slack[r] * rpi[r]) > ZEROTOL):
            raise Exception("Complementary slackness test failed")
    for q in c.quadratic_constraints.get_names():
        if ((not c.quadratic_constraints.get_senses(q) == 'E') and
                fabs(qslack[q] * qpi[q]) > ZEROTOL):
            raise Exception("Complementary slackness test failed")
    for j in c.variables.get_names():
        if c.variables.get_upper_bounds(j) < cplex.infinity:
            slk = c.variables.get_upper_bounds(j) - x[j]
            if cpi[j] < -ZEROTOL:
                dual = cpi[j]
            else:
                dual = 0.0
            if fabs(slk * dual) > ZEROTOL:
                raise Exception("Complementary slackness test failed")
        if c.variables.get_lower_bounds(j) > -cplex.infinity:
            slk = x[j] - c.variables.get_lower_bounds(j)
            if cpi[j] > ZEROTOL:
                dual = cpi[j]
            else:
                dual = 0.0
            if fabs(slk * dual) > ZEROTOL:
                raise Exception("Complementary slackness test failed")

    # Stationarity.
    # The difference between objective function and gradient at optimal
    # solution multiplied by dual multipliers must be 0, i.e., for the
    # optimal solution x
    # 0 == c
    #      - sum(r in rows)  r'(x)*rpi[r]
    #      - sum(q in quads) q'(x)*qpi[q]
    #      - sum(c in cols)  b'(x)*cpi[c]
    # where r' and q' are the derivatives of a row or quadratic constraint,
    # x is the optimal solution and rpi[r] and qpi[q] are the dual
    # multipliers for row r and quadratic constraint q.
    # b' is the derivative of a bound constraint and cpi[c] the dual bound
    # multiplier for column c.

    # Objective function
    kktsum = dict(zip(c.variables.get_names(), c.objective.get_linear()))

    # Linear constraints.
    # The derivative of a linear constraint ax - b (<)= 0 is just a.
    for r in c.linear_constraints.get_names():
        row = c.linear_constraints.get_rows(r)
        for (var, val) in zip(map(v2name, row.ind), row.val):
            kktsum[var] -= rpi[r] * val
    # Quadratic constraints.
    # The derivative of a constraint xQx + ax - b <= 0 is
    # Qx + Q'x + a.
    for q in c.quadratic_constraints.get_names():
        lin = c.quadratic_constraints.get_linear_components(q)
        for (var, val) in zip(map(v2name, lin.ind), lin.val):
            kktsum[var] -= qpi[q] * val
        quad = c.quadratic_constraints.get_quadratic_components(q)
        for (row, col, val) in zip(map(v2name, quad.ind1),
                                   map(v2name, quad.ind2), quad.val):
            kktsum[row] -= qpi[q] * x[col] * val
            kktsum[col] -= qpi[q] * x[row] * val

    # Bounds.
    # The derivative for lower bounds is -1 and that for upper bounds
    # is 1.
    # CPLEX already returns dj with the appropriate sign so there is
    # no need to distinguish between different bound types here.
    for v in c.variables.get_names():
        kktsum[v] -= cpi[v]

    for v in c.variables.get_names():
        if fabs(kktsum[v]) > ZEROTOL:
            raise Exception("Stationarity test failed")

    # KKT conditions satisfied. Dump out solution and dual values.
    print("Optimal solution satisfies KKT conditions.")
    print("  x[] = [", end=' ')
    for n in c.variables.get_names():
        print(" %7.3f" % x[n], end=' ')
    print(" ]")
    print("cpi[] = [", end=' ')
    for n in c.variables.get_names():
        print(" %7.3f" % cpi[n], end=' ')
    print(" ]")
    print("rpi[] = [", end=' ')
    for n in c.linear_constraints.get_names():
        print(" %7.3f" % rpi[n], end=' ')
    print(" ]")
    print("qpi[] = [", end=' ')
    for n in c.quadratic_constraints.get_names():
        print(" %7.3f" % qpi[n], end=' ')
    print(" ]")