Exemple #1
0
def Aopt_KKT_solver(si2, W):
    '''
    Construct a solver that solves the KKT equations associated with the cone 
    programming for A-optimal:

    / 0   At   Gt  \ / x \   / p \
    | A   0    0   | | y | = | q |
    \ G   0  -Wt W / \ z /   \ s /

    Args:

    si2: symmetric KxK matrix, si2[i,j] = 1/s_{ij}^2
    '''
    K = si2.size[0]

    ds = W['d']
    dis = W['di']  # dis[i] := 1./ds[i]
    rtis = W['rti']
    ris = W['r']

    d2s = ds**2
    di2s = dis**2

    # R_i = r_i^{-t}r_i^{-1}
    Ris = [matrix(0.0, (K + 1, K + 1)) for i in xrange(K)]
    for i in xrange(K):
        blas.gemm(rtis[i], rtis[i], Ris[i], transB='T')

    ddR2 = matrix(0., (K * (K + 1) / 2, K * (K + 1) / 2))
    sumdR2_C(Ris, ddR2, K)

    # upper triangular representation si2ab[(a,b)] := si2[a,b]
    si2ab = matrix(0., (K * (K + 1) / 2, 1))
    p = 0
    for i in xrange(K):
        si2ab[p:p + (K - i)] = si2[i:, i]
        p += (K - i)

    si2q = matrix(0., (K * (K + 1) / 2, K * (K + 1) / 2))
    blas.syr(si2ab, si2q)

    sRVR = cvxopt.mul(si2q, ddR2)

    #  We first solve for K(K+1)/2 n_{ab}, K u_i, 1 y
    nvars = K * (K + 1) / 2 + K  # + 1  We solve y by elimination of n and u.
    Bm = matrix(0.0, (nvars, nvars))

    # The LHS matrix of equations
    #
    # d_{ab}^{-2} n_{ab} + vec(V_{ab})^t . vec( \sum_i R_i* F R_i*)
    # + \sum_i vec(V_{ab})^t . vec( g_i g_i^t) u_i + y
    # = -d_{ab}^{-2}l_{ab} + ( p_{ab} - vec(V_{ab})^t . vec(\sum_i L_i*)
    #

    # Coefficients for n_{ab}
    Bm[:K * (K + 1) / 2, :K * (K + 1) / 2] = cvxopt.mul(si2q, ddR2)

    row = 0
    for a in xrange(K):
        for b in xrange(a, K):
            Bm[row, row] += di2s[row]  # d_{ab}^{-2} n_{ab}
            row += 1
    assert (K * (K + 1) / 2 == row)

    # Coefficients for u_i

    # The LHS of equations
    # g_i^t F g_i + R_{i,K+1,K+1}^2 u_i = pi - L_{i,K+1,K+1}
    dg = matrix(0., (K, K * (K + 1) / 2))
    g = matrix(0., (K, K))
    for i in xrange(K):
        g[i, :] = Ris[i][K, :K]
    # dg[:,(a,b)] = g[a] - g[b] if a!=b else g[a]
    pairwise_diff(g, dg, K)
    dg2 = dg**2
    # dg2 := s[(a,b)]^{-2} dg[(a,b)]^2
    for i in xrange(K):
        dg2[i, :] = cvxopt.mul(si2ab.T, dg2[i, :])

    Bm[K * (K + 1) / 2:K * (K + 1) / 2 + K, :-K] = dg2
    # Diagonal coefficients for u_i.
    uoffset = K * (K + 1) / 2
    for i in xrange(K):
        RiKK = Ris[i][K, K]
        Bm[uoffset + i, uoffset + i] = RiKK**2

    # Compare with the default KKT solver.
    TEST_KKT = False
    if (TEST_KKT):
        Bm0 = matrix(0., Bm.size)
        blas.copy(Bm, Bm0)
        G, h, A = Aopt_GhA(si2)
        dims = dict(l=K * (K + 1) / 2, q=[], s=[K + 1] * K)
        default_solver = misc.kkt_ldl(G, dims, A)(W)

    ipiv = matrix(0, Bm.size)
    lapack.sytrf(Bm, ipiv)
    # TODO: IS THIS A POSITIVE DEFINITE MATRIX?
    # lapack.potrf( Bm)

    # oz := (1, ..., 1, 0, ..., 0)' with K*(K+1)/2 ones and K zeros
    oz = matrix(0., (Bm.size[0], 1))
    oz[:K * (K + 1) / 2] = 1.
    # iB1 := B^{-1} oz
    iB1 = matrix(oz[:], oz.size)
    lapack.sytrs(Bm, ipiv, iB1)

    # lapack.potrs( Bm, iB1)

    #######
    #
    #  The solver
    #
    #######
    def kkt_solver(x, y, z):

        if (TEST_KKT):
            x0 = matrix(0., x.size)
            y0 = matrix(0., y.size)
            z0 = matrix(0., z.size)
            x0[:] = x[:]
            y0[:] = y[:]
            z0[:] = z[:]

            # Get default solver solutions.
            xp = matrix(0., x.size)
            yp = matrix(0., y.size)
            zp = matrix(0., z.size)
            xp[:] = x[:]
            yp[:] = y[:]
            zp[:] = z[:]
            default_solver(xp, yp, zp)
            offset = K * (K + 1) / 2
            for i in xrange(K):
                symmetrize_matrix(zp, K + 1, offset)
                offset += (K + 1) * (K + 1)

        # pab = x[:K*(K+1)/2]  # p_{ab}  1<=a<=b<=K
        # pis = x[K*(K+1)/2:]  # \pi_i   1<=i<=K

        # z_{ab} := d_{ab}^{-1} z_{ab}
        # \mat{z}_i = r_i^{-1} \mat{z}_i r_i^{-t}
        misc.scale(z, W, trans='T', inverse='I')

        l = z[:]

        # l_{ab} := d_{ab}^{-2} z_{ab}
        # \mat{z}_i := r_i^{-t}r_i^{-1} \mat{z}_i r_i^{-t} r_i^{-1}
        misc.scale(l, W, trans='N', inverse='I')

        # The RHS of equations
        #
        # d_{ab}^{-2}n_{ab} + vec(V_{ab})^t . vec( \sum_i R_i* F R_i*)
        # + \sum_i vec(V_{ab})^t . vec( g_i g_i^t) u_i + y
        # = -d_{ab}^{-2} l_{ab} + ( p_{ab} - vec(V_{ab})^t . vec(\sum_i L_i*)
        #
        ###

        # Lsum := \sum_i L_i
        moffset = K * (K + 1) / 2
        Lsum = np.sum(np.array(l[moffset:]).reshape((K, (K + 1) * (K + 1))),
                      axis=0)
        Lsum = matrix(Lsum, (K + 1, K + 1))
        Ls = Lsum[:K, :K]

        x[:K * (K + 1) / 2] -= l[:K * (K + 1) / 2]

        dL = matrix(0., (K * (K + 1) / 2, 1))
        ab = 0
        for a in xrange(K):
            dL[ab] = Ls[a, a]
            ab += 1
            for b in xrange(a + 1, K):
                dL[ab] = Ls[a, a] + Ls[b, b] - 2 * Ls[b, a]
                ab += 1

        x[:K * (K + 1) / 2] -= cvxopt.mul(si2ab, dL)

        # The RHS of equations
        # g_i^t F g_i + R_{i,K+1,K+1}^2 u_i = pi - L_{i,K+1,K+1}
        x[K * (K + 1) / 2:] -= l[K * (K + 1) / 2 + (K + 1) * (K + 1) -
                                 1::(K + 1) * (K + 1)]

        # x := B^{-1} Cv
        lapack.sytrs(Bm, ipiv, x)
        # lapack.potrs( Bm, x)

        # y := (oz'.B^{-1}.Cv[:-1] - y)/(oz'.B^{-1}.oz)
        y[0] = (blas.dotu(oz, x) - y[0]) / blas.dotu(oz, iB1)
        # x := B^{-1} Cv - B^{-1}.oz y
        blas.axpy(iB1, x, -y[0])

        # Solve for -n_{ab} - d_{ab}^2 z_{ab} = l_{ab}
        # We need to return scaled d*z.
        # z := d_{ab} d_{ab}^{-2}(n_{ab} + l_{ab})
        #    = d_{ab}^{-1}n_{ab} + d_{ab}^{-1}l_{ab}
        z[:K * (K + 1) / 2] += cvxopt.mul(dis, x[:K * (K + 1) / 2])
        z[:K * (K + 1) / 2] *= -1.

        # Solve for \mat{z}_i = -R_i (\mat{l}_i + diag(F, u_i)) R_i
        #                     = -L_i - R_i diag(F, u_i) R_i
        # We return
        # r_i^t \mat{z}_i r_i = -r_i^{-1} (\mat{l}_i +  diag(F, u_i)) r_i^{-t}
        ui = x[-K:]
        nab = tri2symm(x, K)

        F = Fisher_matrix(si2, nab)
        offset = K * (K + 1) / 2
        for i in xrange(K):
            start, end = i * (K + 1) * (K + 1), (i + 1) * (K + 1) * (K + 1)
            Fu = matrix(0.0, (K + 1, K + 1))
            Fu[:K, :K] = F
            Fu[K, K] = ui[i]
            Fu = matrix(Fu, ((K + 1) * (K + 1), 1))
            # Fu := -r_i^{-1} diag( F, u_i) r_i^{-t}
            cngrnc(rtis[i], Fu, K + 1, alpha=-1.)
            # Fu := -r_i^{-1} (\mat{l}_i + diag( F, u_i )) r_i^{-t}
            blas.axpy(z[offset + start:offset + end], Fu, alpha=-1.)
            z[offset + start:offset + end] = Fu

        if (TEST_KKT):
            offset = K * (K + 1) / 2
            for i in xrange(K):
                symmetrize_matrix(z, K + 1, offset)
                offset += (K + 1) * (K + 1)
            dz = np.max(np.abs(z - zp))
            dx = np.max(np.abs(x - xp))
            dy = np.max(np.abs(y - yp))
            tol = 1e-5
            if dx > tol:
                print 'dx='
                print dx
                print x
                print xp
            if dy > tol:
                print 'dy='
                print dy
                print y
                print yp
            if dz > tol:
                print 'dz='
                print dz
                print z
                print zp
            if dx > tol or dy > tol or dz > tol:
                for i, (r, rti) in enumerate(zip(ris, rtis)):
                    print 'r[%d]=' % i
                    print r
                    print 'rti[%d]=' % i
                    print rti
                    print 'rti.T*r='
                    print rti.T * r
                for i, d in enumerate(ds):
                    print 'd[%d]=%g' % (i, d)
                print 'x0, y0, z0='
                print x0
                print y0
                print z0
                print Bm0

    ###
    #  END of kkt_solver.
    ###

    return kkt_solver
Exemple #2
0
 def default_kkt_solver(W):
     return misc.kkt_ldl(Gm, dims, Am)(W)
Exemple #3
0
 def default_solver(W):
     return misc.kkt_ldl(G, dims, A)(W)