Пример #1
0
def unique_equalityset(C, D, b, af, bf, abs_tol=1e-7, verbose=0):
    """Return equality set E with the following property:

    P_E = {x | af x = bf} intersection P

    where P is the polytope C x + D y < b

    The inequalities have to be satisfied with equality everywhere on
    the face defined by af and bf.
    """
    if D is not None:
        A = np.hstack([C, D])
        a = np.hstack([af, np.zeros(D.shape[1])])
    else:
        A = C
        a = af
    E = []
    for i in range(A.shape[0]):
        A_i = np.array(A[i, :])
        b_i = b[i]
        sol = solvers._solve_lp_using_cvxopt(c=A_i, G=A, h=b, A=a.T, b=bf)
        if sol['status'] != "optimal":
            raise Exception("unique_equalityset: LP returned status " +
                            str(sol['status']))
        if np.abs(sol['primal objective'] - b_i) < abs_tol:
            # Constraint is active everywhere
            E.append(i)
    if len(E) == 0:
        raise Exception("unique_equalityset: empty E")
    return np.array(E)
Пример #2
0
def unique_equalityset(C, D, b, af, bf, abs_tol=1e-7, verbose=0):
    """Return equality set E with the following property:

    P_E = {x | af x = bf} intersection P

    where P is the polytope C x + D y < b

    The inequalities have to be satisfied with equality everywhere on
    the face defined by af and bf.
    """
    if D is not None:
        A = np.hstack([C, D])
        a = np.hstack([af, np.zeros(D.shape[1])])
    else:
        A = C
        a = af
    E = []
    for i in range(A.shape[0]):
        A_i = np.array(A[i, :])
        b_i = b[i]
        sol = solvers._solve_lp_using_cvxopt(
            c=A_i, G=A, h=b,
            A=a.T, b=bf)
        if sol['status'] != "optimal":
            raise Exception(
                "unique_equalityset: LP returned status " +
                str(sol['status']))
        if np.abs(sol['primal objective'] - b_i) < abs_tol:
            # Constraint is active everywhere
            E.append(i)
    if len(E) == 0:
        raise Exception("unique_equalityset: empty E")
    return np.array(E)
Пример #3
0
def is_dual_degenerate(c, G, h, A, b, x_opt, z_opt, abs_tol=1e-7):
    """Return `True` if pair of dual problems is dual degenerate.

    Checks if the pair of dual problems::

      (P): min c'x        (D): max h'z + b'y
           s.t Gx <= h         s.t G'z + A'y = c
               Ax = b                z <= 0

    is dual degenerate, i.e. if (P) has several optimal solutions.
    Optimal solutions x* and z* are required.

    Input:

    `G,h,A,b`: Parameters of (P)
    `x_opt`: One optimal solution to (P)
    `z_opt`: The optimal solution to (D) corresponding to
        _inequality constraints_ in (P)

    Output:
    `dual`: Boolean indicating whether (P) has many optimal solutions.
    """
    D = -G
    d = -h.flatten()
    mu = -z_opt.flatten()  # mu >= 0
    # Active constraints
    I = np.nonzero(np.abs(np.dot(D, x_opt).flatten() - d) < abs_tol)[0]
    # Positive elements in dual opt
    J = np.nonzero(mu > abs_tol)[0]
    # i, j
    i = mu < abs_tol  # Zero elements in dual opt
    i = i.astype(int)
    j = np.zeros(len(mu), dtype=int)
    j[I] = 1  # 1 if active
    # Indices where active constraints have 0 dual opt
    L = np.nonzero(i + j == 2)[0]
    # sizes
    nI = len(I)
    nJ = len(J)
    nL = len(L)
    # constraints
    DI = D[I, :]  # Active constraints
    DJ = D[J, :]  # Constraints with positive lagrange mult
    DL = D[L, :]  # Active constraints with zero dual opt
    dual = 0
    if A is None:
        test = DI
    else:
        test = np.vstack([DI, A])
    if rank(test) < np.amin(DI.shape):
        return True
    else:
        if len(L) > 0:
            if A is None:
                Ae = DJ
            else:
                Ae = np.vstack([DJ, A])
            be = np.zeros(Ae.shape[0])
            Ai = -DL
            bi = np.zeros(nL)
            sol = solvers._solve_lp_using_cvxopt(c=-np.sum(DL, axis=0),
                                                 G=Ai,
                                                 h=bi,
                                                 A=Ae,
                                                 b=be)
            if sol['status'] == "dual infeasible":
                # Dual infeasible -> primal unbounded -> value>epsilon
                return True
            if sol['primal objective'] > abs_tol:
                return True
    return False
Пример #4
0
def adjacent(C, D, b, rid_fac, abs_tol=1e-7):
    """Compute the (unique) adjacent facet.

    @param rid_fac: A Ridge_Facet object containing the parameters for
        a facet and one of its ridges.

    @return: (E_adj,a_adj,b_adj): The equality set and parameters for
        the adjacent facet such that::

            P_{E_adj} = P intersection {x | a_adj x = b_adj}
    """
    E = rid_fac.E_0
    af = rid_fac.af
    bf = rid_fac.bf
    #
    E_r = rid_fac.E_r
    ar = rid_fac.ar
    br = rid_fac.br
    # shape
    d = C.shape[1]
    k = D.shape[1]
    # E_r slices
    C_er = C[E_r, :]
    D_er = D[E_r, :]
    b_er = b[E_r]
    # stack
    c = -np.hstack([ar, np.zeros(k)])
    G = np.hstack([C_er, D_er])
    h = b_er
    A = np.hstack([af, np.zeros(k)])
    sol = solvers._solve_lp_using_cvxopt(c, G, h, A=A.T, b=bf * (1 - 0.01))
    if sol['status'] != "optimal":
        print(G)
        print(h)
        print(af)
        print(bf)
        print(ar)
        print(br)
        print(np.dot(af, ar))
        data = {}
        data["C"] = C
        data["D"] = D
        data["b"] = b
        sio.savemat("matlabdata", data)
        pickle.dump(data, open("polytope.p", "wb"))
        raise Exception("adjacent: Lp returned status " + str(sol['status']))
    opt_sol = np.array(sol['x']).flatten()
    dual_opt_sol = np.array(sol['z']).flatten()
    x_opt = opt_sol[range(0, d)]
    y_opt = opt_sol[range(d, d + k)]
    if is_dual_degenerate(c.flatten(),
                          G,
                          h,
                          A,
                          bf * (1 - 0.01),
                          opt_sol,
                          dual_opt_sol,
                          abs_tol=abs_tol):
        # If degenerate, compute affine hull and take preimage
        E_temp = np.nonzero(np.abs(np.dot(G, opt_sol) - h) < abs_tol)[0]
        a_temp, b_temp = proj_aff(C_er[E_temp, :],
                                  D_er[E_temp, :],
                                  b_er[E_temp],
                                  expected_dim=1,
                                  abs_tol=abs_tol)
        E_adj = unique_equalityset(C, D, b, a_temp, b_temp, abs_tol=abs_tol)
        if len(E_adj) == 0:
            data = {}
            data["C"] = C
            data["D"] = D
            data["b"] = b
            data["Er"] = E_r + 1
            data["ar"] = ar
            data["br"] = br
            data["Ef"] = E + 1
            data["af"] = af
            data["bf"] = bf
            sio.savemat("matlabdata", data)
            raise Exception(
                "adjacent: equality set computation returned empty set")
    else:
        r = np.abs(np.dot(C, x_opt) + np.dot(D, y_opt) - b) < abs_tol
        E_adj = np.nonzero(r)[0]
    C_eadj = C[E_adj, :]
    D_eadj = D[E_adj, :]
    b_eadj = b[E_adj]
    af_adj, bf_adj = proj_aff(C_eadj, D_eadj, b_eadj, abs_tol=abs_tol)
    return E_adj, af_adj, bf_adj
Пример #5
0
def ridge(C, D, b, E, af, bf, abs_tol=1e-7, verbose=0):
    """Compute all ridges of a facet in the projection.

    Input:
    `C,D,b`: Original polytope data
    `E,af,bf`: Equality set and affine hull of a facet in the projection

    Output:
    `ridge_list`: A list containing all the ridges of
        the facet as Ridge objects
    """
    d = C.shape[1]
    k = D.shape[1]
    Er_list = []
    q = C.shape[0]
    E_c = np.setdiff1d(range(q), E)
    # E slices
    C_E = C[E, :]
    D_E = D[E, :]
    b_E = b[E, :]
    # E_c slices
    C_Ec = C[E_c, :]
    D_Ec = D[E_c, :]
    b_Ec = b[E_c]
    # dots
    S = C_Ec - np.dot(np.dot(D_Ec, linalg.pinv(D_E)), C_E)
    L = np.dot(D_Ec, null_space(D_E))
    t = b_Ec - np.dot(D_Ec, np.dot(linalg.pinv(D_E), b_E))
    if rank(np.hstack([C_E, D_E])) < k + 1:
        if verbose > 1:
            print("Doing recursive ESP call")
        u, s, v = linalg.svd(np.array([af]), full_matrices=1)
        sigma = s[0]
        v = v.T * u[0, 0]  # Correct sign
        V_hat = v[:, [0]]
        V_tilde = v[:, range(1, v.shape[1])]
        Cnew = np.dot(S, V_tilde)
        Dnew = L
        bnew = t - np.dot(S, V_hat).flatten() * bf / sigma
        Anew = np.hstack([Cnew, Dnew])
        xc2, yc2, cen2 = cheby_center(Cnew, Dnew, bnew)
        bnew = bnew - np.dot(Cnew, xc2).flatten() - np.dot(Dnew, yc2).flatten()
        Gt, gt, E_t = esp(Cnew,
                          Dnew,
                          bnew,
                          centered=True,
                          abs_tol=abs_tol,
                          verbose=0)
        if (len(E_t[0]) == 0) or (len(E_t[1]) == 0):
            raise Exception(
                "ridge: recursive call did not return any equality sets")
        for i in range(len(E_t)):
            E_f = E_t[i]
            er = np.sort(np.hstack([E, E_c[E_f]]))
            ar = np.dot(Gt[i, :], V_tilde.T).flatten()
            br0 = gt[i].flatten()
            # Make orthogonal to facet
            ar = ar - af * np.dot(af.flatten(), ar.flatten())
            br = br0 - bf * np.dot(af.flatten(), ar.flatten())
            # Normalize and make ridge equation point outwards
            norm = np.sqrt(np.sum(ar * ar))
            ar = ar * np.sign(br) / norm
            br = br * np.sign(br) / norm
            # Restore center
            br = br + np.dot(Gt[i, :], xc2) / norm
            if len(ar) > d:
                raise Exception("ridge: wrong length of new ridge!")
            Er_list.append(Ridge(er, ar, br))
    else:
        if verbose > 0:
            print("Doing direct calculation of ridges")
        X = np.arange(S.shape[0])
        while len(X) > 0:
            i = X[0]
            X = np.setdiff1d(X, i)
            if np.linalg.norm(S[i, :]) < abs_tol:
                continue
            Si = S[i, :]
            Si = Si / np.linalg.norm(Si)
            if np.linalg.norm(af - np.dot(Si, af) * Si) > abs_tol:
                test1 = null_space(np.vstack(
                    [np.hstack([af, bf]),
                     np.hstack([S[i, :], t[i]])]),
                                   nonempty=True)
                test2 = np.hstack([S, np.array([t]).T])
                test = np.dot(test1.T, test2.T)
                test = np.sum(np.abs(test), 0)
                Q_i = np.nonzero(test > abs_tol)[0]
                Q = np.nonzero(test < abs_tol)[0]
                X = np.setdiff1d(X, Q)
                # Have Q_i
                Sq = S[Q_i, :]
                tq = t[Q_i]
                c = np.zeros(d + 1)
                c[0] = 1
                Gup = np.hstack([-np.ones([Sq.shape[0], 1]), Sq])
                Gdo = np.hstack([-1, np.zeros(Sq.shape[1])])
                G = np.vstack([Gup, Gdo])
                h = np.hstack([tq, 1])
                Al = np.zeros([2, 1])
                Ar = np.vstack([af, S[i, :]])
                A = np.hstack([Al, Ar])
                bb = np.hstack([bf, t[i]])
                sol = solvers._solve_lp_using_cvxopt(c, G, h, A=A, b=bb)
                if sol['status'] == 'optimal':
                    tau = sol['x'][0]
                    if tau < -abs_tol:
                        ar = np.array([S[i, :]]).flatten()
                        br = t[i].flatten()
                        # Make orthogonal to facet
                        ar = ar - af * np.dot(af.flatten(), ar.flatten())
                        br = br - bf * np.dot(af.flatten(), ar.flatten())
                        # Normalize and make ridge equation point outwards
                        norm = np.sqrt(np.sum(ar * ar))
                        ar = ar / norm
                        br = br / norm
                        # accumulate
                        Er_list.append(
                            Ridge(np.sort(np.hstack([E, E_c[Q]])), ar, br))
    return Er_list
Пример #6
0
def is_dual_degenerate(c, G, h, A, b, x_opt, z_opt, abs_tol=1e-7):
    """Return `True` if pair of dual problems is dual degenerate.

    Checks if the pair of dual problems::

      (P): min c'x        (D): max h'z + b'y
           s.t Gx <= h         s.t G'z + A'y = c
               Ax = b                z <= 0

    is dual degenerate, i.e. if (P) has several optimal solutions.
    Optimal solutions x* and z* are required.

    Input:

    `G,h,A,b`: Parameters of (P)
    `x_opt`: One optimal solution to (P)
    `z_opt`: The optimal solution to (D) corresponding to
        _inequality constraints_ in (P)

    Output:
    `dual`: Boolean indicating whether (P) has many optimal solutions.
    """
    D = - G
    d = - h.flatten()
    mu = - z_opt.flatten()  # mu >= 0
    # Active constraints
    I = np.nonzero(np.abs(np.dot(D, x_opt).flatten() - d) < abs_tol)[0]
    # Positive elements in dual opt
    J = np.nonzero(mu > abs_tol)[0]
    # i, j
    i = mu < abs_tol  # Zero elements in dual opt
    i = i.astype(int)
    j = np.zeros(len(mu), dtype=int)
    j[I] = 1  # 1 if active
    # Indices where active constraints have 0 dual opt
    L = np.nonzero(i + j == 2)[0]
    # sizes
    nI = len(I)
    nJ = len(J)
    nL = len(L)
    # constraints
    DI = D[I, :]  # Active constraints
    DJ = D[J, :]  # Constraints with positive lagrange mult
    DL = D[L, :]  # Active constraints with zero dual opt
    dual = 0
    if A is None:
        test = DI
    else:
        test = np.vstack([DI, A])
    if rank(test) < np.amin(DI.shape):
        return True
    else:
        if len(L) > 0:
            if A is None:
                Ae = DJ
            else:
                Ae = np.vstack([DJ, A])
            be = np.zeros(Ae.shape[0])
            Ai = - DL
            bi = np.zeros(nL)
            sol = solvers._solve_lp_using_cvxopt(
                c= - np.sum(DL, axis=0), G=Ai,
                h=bi, A=Ae, b=be)
            if sol['status'] == "dual infeasible":
                # Dual infeasible -> primal unbounded -> value>epsilon
                return True
            if sol['primal objective'] > abs_tol:
                return True
    return False
Пример #7
0
def adjacent(C, D, b, rid_fac, abs_tol=1e-7):
    """Compute the (unique) adjacent facet.

    @param rid_fac: A Ridge_Facet object containing the parameters for
        a facet and one of its ridges.

    @return: (E_adj,a_adj,b_adj): The equality set and parameters for
        the adjacent facet such that::

            P_{E_adj} = P intersection {x | a_adj x = b_adj}
    """
    E = rid_fac.E_0
    af = rid_fac.af
    bf = rid_fac.bf
    #
    E_r = rid_fac.E_r
    ar = rid_fac.ar
    br = rid_fac.br
    # shape
    d = C.shape[1]
    k = D.shape[1]
    # E_r slices
    C_er = C[E_r, :]
    D_er = D[E_r, :]
    b_er = b[E_r]
    # stack
    c = -np.hstack([ar, np.zeros(k)])
    G = np.hstack([C_er, D_er])
    h = b_er
    A = np.hstack([af, np.zeros(k)])
    sol = solvers._solve_lp_using_cvxopt(
        c, G, h, A=A.T, b=bf * (1 - 0.01))
    if sol['status'] != "optimal":
        print(G)
        print(h)
        print(af)
        print(bf)
        print(ar)
        print(br)
        print(np.dot(af, ar))
        data = {}
        data["C"] = C
        data["D"] = D
        data["b"] = b
        sio.savemat("matlabdata", data)
        pickle.dump(data, open("polytope.p", "wb"))
        raise Exception(
            "adjacent: Lp returned status " + str(sol['status']))
    opt_sol = np.array(sol['x']).flatten()
    dual_opt_sol = np.array(sol['z']).flatten()
    x_opt = opt_sol[range(0, d)]
    y_opt = opt_sol[range(d, d + k)]
    if is_dual_degenerate(
            c.flatten(), G, h, A, bf * (1 - 0.01),
            opt_sol, dual_opt_sol, abs_tol=abs_tol):
        # If degenerate, compute affine hull and take preimage
        E_temp = np.nonzero(np.abs(np.dot(G, opt_sol) - h) < abs_tol)[0]
        a_temp, b_temp = proj_aff(
            C_er[E_temp, :], D_er[E_temp, :], b_er[E_temp],
            expected_dim=1, abs_tol=abs_tol)
        E_adj = unique_equalityset(C, D, b, a_temp, b_temp, abs_tol=abs_tol)
        if len(E_adj) == 0:
            data = {}
            data["C"] = C
            data["D"] = D
            data["b"] = b
            data["Er"] = E_r + 1
            data["ar"] = ar
            data["br"] = br
            data["Ef"] = E + 1
            data["af"] = af
            data["bf"] = bf
            sio.savemat("matlabdata", data)
            raise Exception(
                "adjacent: equality set computation returned empty set")
    else:
        r = np.abs(np.dot(C, x_opt) + np.dot(D, y_opt) - b) < abs_tol
        E_adj = np.nonzero(r)[0]
    C_eadj = C[E_adj, :]
    D_eadj = D[E_adj, :]
    b_eadj = b[E_adj]
    af_adj, bf_adj = proj_aff(C_eadj, D_eadj, b_eadj, abs_tol=abs_tol)
    return E_adj, af_adj, bf_adj
Пример #8
0
def ridge(C, D, b, E, af, bf, abs_tol=1e-7, verbose=0):
    """Compute all ridges of a facet in the projection.

    Input:
    `C,D,b`: Original polytope data
    `E,af,bf`: Equality set and affine hull of a facet in the projection

    Output:
    `ridge_list`: A list containing all the ridges of
        the facet as Ridge objects
    """
    d = C.shape[1]
    k = D.shape[1]
    Er_list = []
    q = C.shape[0]
    E_c = np.setdiff1d(range(q), E)
    # E slices
    C_E = C[E, :]
    D_E = D[E, :]
    b_E = b[E, :]
    # E_c slices
    C_Ec = C[E_c, :]
    D_Ec = D[E_c, :]
    b_Ec = b[E_c]
    # dots
    S = C_Ec - np.dot(np.dot(D_Ec, linalg.pinv(D_E)), C_E)
    L = np.dot(D_Ec, null_space(D_E))
    t = b_Ec - np.dot(D_Ec, np.dot(linalg.pinv(D_E), b_E))
    if rank(np.hstack([C_E, D_E])) < k + 1:
        if verbose > 1:
            print("Doing recursive ESP call")
        u, s, v = linalg.svd(np.array([af]), full_matrices=1)
        sigma = s[0]
        v = v.T * u[0, 0]  # Correct sign
        V_hat = v[:, [0]]
        V_tilde = v[:, range(1, v.shape[1])]
        Cnew = np.dot(S, V_tilde)
        Dnew = L
        bnew = t - np.dot(S, V_hat).flatten() * bf / sigma
        Anew = np.hstack([Cnew, Dnew])
        xc2, yc2, cen2 = cheby_center(Cnew, Dnew, bnew)
        bnew = bnew - np.dot(Cnew, xc2).flatten() - np.dot(Dnew, yc2).flatten()
        Gt, gt, E_t = esp(
            Cnew, Dnew, bnew,
            centered=True, abs_tol=abs_tol, verbose=0)
        if (len(E_t[0]) == 0) or (len(E_t[1]) == 0):
            raise Exception(
                "ridge: recursive call did not return any equality sets")
        for i in range(len(E_t)):
            E_f = E_t[i]
            er = np.sort(np.hstack([E, E_c[E_f]]))
            ar = np.dot(Gt[i, :], V_tilde.T).flatten()
            br0 = gt[i].flatten()
            # Make orthogonal to facet
            ar = ar - af * np.dot(af.flatten(), ar.flatten())
            br = br0 - bf * np.dot(af.flatten(), ar.flatten())
            # Normalize and make ridge equation point outwards
            norm = np.sqrt(np.sum(ar * ar))
            ar = ar * np.sign(br) / norm
            br = br * np.sign(br) / norm
            # Restore center
            br = br + np.dot(Gt[i, :], xc2) / norm
            if len(ar) > d:
                raise Exception("ridge: wrong length of new ridge!")
            Er_list.append(Ridge(er, ar, br))
    else:
        if verbose > 0:
            print("Doing direct calculation of ridges")
        X = np.arange(S.shape[0])
        while len(X) > 0:
            i = X[0]
            X = np.setdiff1d(X, i)
            if np.linalg.norm(S[i, :]) < abs_tol:
                continue
            Si = S[i, :]
            Si = Si / np.linalg.norm(Si)
            if np.linalg.norm(af - np.dot(Si, af) * Si) > abs_tol:
                test1 = null_space(
                    np.vstack([
                        np.hstack([af, bf]),
                        np.hstack([S[i, :], t[i]])]),
                    nonempty=True)
                test2 = np.hstack([S, np.array([t]).T])
                test = np.dot(test1.T, test2.T)
                test = np.sum(np.abs(test), 0)
                Q_i = np.nonzero(test > abs_tol)[0]
                Q = np.nonzero(test < abs_tol)[0]
                X = np.setdiff1d(X, Q)
                # Have Q_i
                Sq = S[Q_i, :]
                tq = t[Q_i]
                c = np.zeros(d + 1)
                c[0] = 1
                Gup = np.hstack([-np.ones([Sq.shape[0], 1]), Sq])
                Gdo = np.hstack([-1, np.zeros(Sq.shape[1])])
                G = np.vstack([Gup, Gdo])
                h = np.hstack([tq, 1])
                Al = np.zeros([2, 1])
                Ar = np.vstack([af, S[i, :]])
                A = np.hstack([Al, Ar])
                bb = np.hstack([bf, t[i]])
                sol = solvers._solve_lp_using_cvxopt(
                    c, G, h, A=A, b=bb)
                if sol['status'] == 'optimal':
                    tau = sol['x'][0]
                    if tau < -abs_tol:
                        ar = np.array([S[i, :]]).flatten()
                        br = t[i].flatten()
                        # Make orthogonal to facet
                        ar = ar - af * np.dot(af.flatten(), ar.flatten())
                        br = br - bf * np.dot(af.flatten(), ar.flatten())
                        # Normalize and make ridge equation point outwards
                        norm = np.sqrt(np.sum(ar * ar))
                        ar = ar / norm
                        br = br / norm
                        # accumulate
                        Er_list.append(
                            Ridge(np.sort(np.hstack([E, E_c[Q]])), ar, br))
    return Er_list