Esempio n. 1
0
    def l1regls_mosek(A, b):
        """

        Returns the solution of l1-norm regularized least-squares problem

            minimize    || A*x - b ||_2^2  + e'*u

            subject to  -u <= x <= u

        """

        m, n = A.size

        env = mosek.Env()
        task = env.Task(0, 0)
        task.set_Stream(mosek.streamtype.log, lambda x: sys.stdout.write(x))

        task.appendvars(2 * n)  # number of variables
        task.appendcons(2 * n)  # number of constraints

        # input quadratic objective
        Q = matrix(0.0, (n, n))
        blas.syrk(A, Q, alpha=2.0, trans='T')

        I = []
        for i in range(n):
            I.extend(list(range(i, n)))

        J = []
        for i in range(n):
            J.extend((n - i) * [i])

        task.putqobj(I, J, list(Q[matrix(I) + matrix(J) * n]))
        task.putclist(list(range(2 * n)),
                      list(-2 * A.T * b) + n * [1.0])  # setup linear objective

        # input constraint matrix row by row
        for i in range(n):
            task.putarow(i, [i, n + i], [1.0, -1.0])
            task.putarow(n + i, [i, n + i], [1.0, 1.0])

        # setup bounds on constraints
        task.putboundslice(mosek.accmode.con, 0, n, n * [mosek.boundkey.up],
                           n * [0.0], n * [0.0])
        task.putboundslice(mosek.accmode.con, n, 2 * n,
                           n * [mosek.boundkey.lo], n * [0.0], n * [0.0])

        # setup variable bounds
        task.putboundslice(mosek.accmode.var, 0, 2 * n,
                           2 * n * [mosek.boundkey.fr], 2 * n * [0.0],
                           2 * n * [0.0])

        # optimize the task
        task.putobjsense(mosek.objsense.minimize)
        task.optimize()
        task.solutionsummary(mosek.streamtype.log)
        x = n * [0.0]
        task.getsolutionslice(mosek.soltype.itr, mosek.solitem.xx, 0, n, x)

        return matrix(x)
Esempio n. 2
0
def llt(L):
    """
    Supernodal multifrontal Cholesky product:

    .. math::
         X = LL^T

    where :math:`L` is lower-triangular. On exit, the argument `L`
    contains the product :math:`X`.

    :param L:    :py:class:`cspmatrix` (factor)
    """

    assert isinstance(L, cspmatrix) and L.is_factor is True, "L must be a cspmatrix factor"

    n = L.symb.n
    snpost = L.symb.snpost
    snptr = L.symb.snptr
    chptr = L.symb.chptr
    chidx = L.symb.chidx

    relptr = L.symb.relptr
    relidx = L.symb.relidx
    blkptr = L.symb.blkptr
    blkval = L.blkval

    stack = []

    for k in snpost:

        nn = snptr[k+1]-snptr[k]       # |Nk|
        na = relptr[k+1]-relptr[k]     # |Ak|
        nj = na + nn

        # compute [I; L_{Ak,Nk}]*D_k*[I;L_{Ak,Nk}]'; store in F
        blas.trmm(blkval, blkval, side = "R", m = na, n = nn, ldA = nj,\
                  ldB = nj, offsetA = blkptr[k], offsetB = blkptr[k]+nn)
        F = matrix(0.0, (nj, nj))
        blas.syrk(blkval, F, n = nj, k = nn, offsetA = blkptr[k], ldA = nj)

        # if supernode k has any children, subtract update matrices 
        for _ in range(chptr[k],chptr[k+1]):
            Ui, i = stack.pop()
            frontal_add_update(F, Ui, relidx, relptr, i)

        # if supernode k is not a root node, push update matrix onto stack
        if na > 0:
            Uk = matrix(0.0,(na,na))
            lapack.lacpy(F, Uk, m = na, n = na, uplo = 'L', offsetA = nn*nj+nn, ldA = nj)
            stack.append((Uk,k))

        # copy leading Nk columns of F to blkval
        lapack.lacpy(F, blkval, m = nj, n = nn, ldA = nj, uplo = 'L',\
                     ldB = nj, offsetB = blkptr[k])

    L._is_factor = False

    return 
Esempio n. 3
0
    def l1regls_mosek(A, b):
        """

        Returns the solution of l1-norm regularized least-squares problem

            minimize    || A*x - b ||_2^2  + e'*u

            subject to  -u <= x <= u

        """

        m, n = A.size

        env  = mosek.Env()
        task = env.Task(0,0)
        task.set_Stream(mosek.streamtype.log, lambda x: sys.stdout.write(x))

        task.appendvars( 2*n)            # number of variables
        task.appendcons( 2*n)            # number of constraints

        # input quadratic objective
        Q = matrix(0.0, (n,n)) 
        blas.syrk(A, Q, alpha = 2.0, trans='T')

        I = []
        for i in range(n):
            I.extend(range(i,n))

        J = []
        for i in range(n):
            J.extend((n-i)*[i])

        task.putqobj(I, J, list(Q[matrix(I) + matrix(J)*n]))
        task.putclist(range(2*n), list(-2*A.T*b) + n*[1.0])  # setup linear objective

        # input constraint matrix row by row
        for i in range(n):
            task.putarow(   i, [i, n+i], [1.0, -1.0])
            task.putarow( n+i, [i, n+i], [1.0,  1.0])

        # setup bounds on constraints
        task.putboundslice(mosek.accmode.con,
                           0, n, n*[mosek.boundkey.up], n*[0.0], n*[0.0])
        task.putboundslice(mosek.accmode.con,
                           n, 2*n, n*[mosek.boundkey.lo], n*[0.0], n*[0.0])

        # setup variable bounds
        task.putboundslice(mosek.accmode.var,
                           0, 2*n, 2*n*[mosek.boundkey.fr], 2*n*[0.0], 2*n*[0.0])

        # optimize the task
        task.putobjsense(mosek.objsense.minimize)
        task.optimize()
        task.solutionsummary(mosek.streamtype.log)
        x = n*[0.0]
        task.getsolutionslice(mosek.soltype.itr, mosek.solitem.xx, 0, n, x)

        return matrix(x)
def Fkkt(W):

    # Factor
    #
    #     S = A*D^-1*A' + I
    #
    # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**2, D2 = d[n:]**2.

    d1, d2 = W['di'][:n]**2, W['di'][n:]**2

    # ds is square root of diagonal of D
    ds = sqrt(2.0) * div(mul(W['di'][:n], W['di'][n:]), sqrt(d1 + d2))
    d3 = div(d2 - d1, d1 + d2)

    # Asc = A*diag(d)^-1/2
    blas.copy(A, Asc)
    for k in range(m):
        blas.tbsv(ds, Asc, n=n, k=0, ldA=1, incx=m, offsetx=k)

    # S = I + A * D^-1 * A'
    blas.syrk(Asc, S)
    S[::m + 1] += 1.0
    lapack.potrf(S)

    def g(x, y, z):

        x[:n] = 0.5 * ( x[:n] - mul(d3, x[n:]) + \
                mul(d1, z[:n] + mul(d3, z[:n])) - \
                mul(d2, z[n:] - mul(d3, z[n:])) )
        x[:n] = div(x[:n], ds)

        # Solve
        #
        #     S * v = 0.5 * A * D^-1 * ( bx[:n]
        #             - (D2-D1)*(D1+D2)^-1 * bx[n:]
        #             + D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bz[:n]
        #             - D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bz[n:] )

        blas.gemv(Asc, x, v)
        lapack.potrs(S, v)

        # x[:n] = D^-1 * ( rhs - A'*v ).
        blas.gemv(Asc, v, x, alpha=-1.0, beta=1.0, trans='T')
        x[:n] = div(x[:n], ds)

        # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bz[:n]  - D2*bz[n:] )
        #         - (D2-D1)*(D1+D2)^-1 * x[:n]
        x[n:] = div( x[n:] - mul(d1, z[:n]) - mul(d2, z[n:]), d1+d2 )\
                - mul( d3, x[:n] )

        # z[:n] = D1^1/2 * (  x[:n] - x[n:] - bz[:n] )
        # z[n:] = D2^1/2 * ( -x[:n] - x[n:] - bz[n:] ).
        z[:n] = mul(W['di'][:n], x[:n] - x[n:] - z[:n])
        z[n:] = mul(W['di'][n:], -x[:n] - x[n:] - z[n:])

    return g
Esempio n. 5
0
def Fkkt(W):

    # Factor 
    #
    #     S = A*D^-1*A' + I 
    #
    # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**2, D2 = d[n:]**2.

    d1, d2 = W['di'][:n]**2, W['di'][n:]**2    

    # ds is square root of diagonal of D
    ds = sqrt(2.0) * div( mul( W['di'][:n], W['di'][n:]), sqrt(d1+d2) )
    d3 =  div(d2 - d1, d1 + d2)
 
    # Asc = A*diag(d)^-1/2
    blas.copy(A, Asc)
    for k in range(m):
        blas.tbsv(ds, Asc, n=n, k=0, ldA=1, incx=m, offsetx=k)

    # S = I + A * D^-1 * A'
    blas.syrk(Asc, S)
    S[::m+1] += 1.0 
    lapack.potrf(S)

    def g(x, y, z):

        x[:n] = 0.5 * ( x[:n] - mul(d3, x[n:]) + \
                mul(d1, z[:n] + mul(d3, z[:n])) - \
                mul(d2, z[n:] - mul(d3, z[n:])) )
        x[:n] = div( x[:n], ds) 

        # Solve
        #
        #     S * v = 0.5 * A * D^-1 * ( bx[:n] 
        #             - (D2-D1)*(D1+D2)^-1 * bx[n:] 
        #             + D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bz[:n]
        #             - D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bz[n:] )
	    
        blas.gemv(Asc, x, v)
        lapack.potrs(S, v)
	
        # x[:n] = D^-1 * ( rhs - A'*v ).
        blas.gemv(Asc, v, x, alpha=-1.0, beta=1.0, trans='T')
        x[:n] = div(x[:n], ds)

        # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bz[:n]  - D2*bz[n:] )
        #         - (D2-D1)*(D1+D2)^-1 * x[:n]         
        x[n:] = div( x[n:] - mul(d1, z[:n]) - mul(d2, z[n:]), d1+d2 )\
                - mul( d3, x[:n] )
	    
        # z[:n] = D1^1/2 * (  x[:n] - x[n:] - bz[:n] )
        # z[n:] = D2^1/2 * ( -x[:n] - x[n:] - bz[n:] ).
        z[:n] = mul( W['di'][:n],  x[:n] - x[n:] - z[:n] ) 
        z[n:] = mul( W['di'][n:], -x[:n] - x[n:] - z[n:] ) 

    return g
Esempio n. 6
0
    def Fkkt(W):

        # Factor
        #
        #     S = A*D^-1*A' + I
        #
        # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**-2, D2 = d[n:]**-2.

        d1, d2 = W['di'][:n]**2, W['di'][n:]**2
        print 'printing: ', W['di']

        # ds is square root of diagonal of D
        ds = math.sqrt(2.0) * div(mul(W['di'][:n], W['di'][n:]), sqrt(d1 + d2))
        d3 = div(d2 - d1, d1 + d2)

        Asc = matrix(0.0, (m, n))
        # Asc = A*diag(d)^-1/2
        Asc = A * spdiag(ds**-1)

        # S = I + A * D^-1 * A'
        blas.syrk(Asc, S)
        S[::m + 1] += 1.0
        lapack.potrf(S)

        def g(x, y, z):

            x[:n] = 0.5 * (x[:n] - mul(d3, x[n:]) + mul(
                d1, z[:n] + mul(d3, z[:n])) - mul(d2, z[n:] - mul(d3, z[n:])))
            x[:n] = div(x[:n], ds)

            # Solve
            #
            #     S * v = 0.5 * A * D^-1 * ( bx[:n] -
            #         (D2-D1)*(D1+D2)^-1 * bx[n:] +
            #         D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bzl[:n] -
            #         D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bzl[n:] )

            blas.gemv(Asc, x, v)
            lapack.potrs(S, v)

            # x[:n] = D^-1 * ( rhs - A'*v ).
            blas.gemv(Asc, v, x, alpha=-1.0, beta=1.0, trans='T')
            x[:n] = div(x[:n], ds)

            # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bzl[:n]  - D2*bzl[n:] )
            #         - (D2-D1)*(D1+D2)^-1 * x[:n]
            x[n:] = div( x[n:] - mul(d1, z[:n]) - mul(d2, z[n:]), d1+d2 )\
                - mul( d3, x[:n] )

            # zl[:n] = D1^1/2 * (  x[:n] - x[n:] - bzl[:n] )
            # zl[n:] = D2^1/2 * ( -x[:n] - x[n:] - bzl[n:] ).
            z[:n] = mul(W['di'][:n], x[:n] - x[n:] - z[:n])
            z[n:] = mul(W['di'][n:], -x[:n] - x[n:] - z[n:])

        return g
Esempio n. 7
0
    def Fkkt(W):

        # Factor 
        #
        #     S = A*D^-1*A' + I 
        #
        # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**-2, D2 = d[n:]**-2.

        d1, d2 = W['di'][:n]**2, W['di'][n:]**2

        # ds is square root of diagonal of D
        ds = math.sqrt(2.0) * div( mul( W['di'][:n], W['di'][n:]), 
            sqrt(d1+d2) )
        d3 =  div(d2 - d1, d1 + d2)
     
        # Asc = A*diag(d)^-1/2
        Asc = A * spdiag(ds**-1)

        # S = I + A * D^-1 * A'
        blas.syrk(Asc, S)
        S[::m+1] += 1.0 
        lapack.potrf(S)

        def g(x, y, z):

            x[:n] = 0.5 * ( x[:n] - mul(d3, x[n:]) + 
                mul(d1, z[:n] + mul(d3, z[:n])) - mul(d2, z[n:] - 
                mul(d3, z[n:])) )
            x[:n] = div( x[:n], ds) 

            # Solve
            #
            #     S * v = 0.5 * A * D^-1 * ( bx[:n] - 
            #         (D2-D1)*(D1+D2)^-1 * bx[n:] + 
            #         D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bzl[:n] - 
            #         D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bzl[n:] )
                
            blas.gemv(Asc, x, v)
            lapack.potrs(S, v)
            
            # x[:n] = D^-1 * ( rhs - A'*v ).
            blas.gemv(Asc, v, x, alpha=-1.0, beta=1.0, trans='T')
            x[:n] = div(x[:n], ds)

            # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bzl[:n]  - D2*bzl[n:] ) 
            #         - (D2-D1)*(D1+D2)^-1 * x[:n]         
            x[n:] = div( x[n:] - mul(d1, z[:n]) - mul(d2, z[n:]), d1+d2 )\
                - mul( d3, x[:n] )
                
            # zl[:n] = D1^1/2 * (  x[:n] - x[n:] - bzl[:n] )
            # zl[n:] = D2^1/2 * ( -x[:n] - x[n:] - bzl[n:] ).
            z[:n] = mul( W['di'][:n],  x[:n] - x[n:] - z[:n] ) 
            z[n:] = mul( W['di'][n:], -x[:n] - x[n:] - z[n:] ) 

        return g
Esempio n. 8
0
    def Fgp(x = None, z = None):

        if x is None: return mnl, matrix(0.0, (n,1))

        f = matrix(0.0, (mnl+1,1))
        Df = matrix(0.0, (mnl+1,n))

        # y = F*x+g
        blas.copy(g, y)
        base.gemv(F, x, y, beta=1.0)

        if z is not None: H = matrix(0.0, (n,n))

        for i, start, stop in ind:

            # yi := exp(yi) = exp(Fi*x+gi)
            ymax = max(y[start:stop])
            y[start:stop] = base.exp(y[start:stop] - ymax)

            # fi = log sum yi = log sum exp(Fi*x+gi)
            ysum = blas.asum(y, n=stop-start, offset=start)
            f[i] = ymax + math.log(ysum)

            # yi := yi / sum(yi) = exp(Fi*x+gi) / sum(exp(Fi*x+gi))
            blas.scal(1.0/ysum, y, n=stop-start, offset=start)

            # gradfi := Fi' * yi
            #        = Fi' * exp(Fi*x+gi) / sum(exp(Fi*x+gi))
            base.gemv(F, y, Df, trans='T', m=stop-start, incy=mnl+1,
                offsetA=start, offsetx=start, offsety=i)

            if z is not None:

                # Hi = Fi' * (diag(yi) - yi*yi') * Fi
                #    = Fisc' * Fisc
                # where
                # Fisc = diag(yi)^1/2 * (I - 1*yi') * Fi
                #      = diag(yi)^1/2 * (Fi - 1*gradfi')

                Fsc[:K[i], :] = F[start:stop, :]
                for k in range(start,stop):
                   blas.axpy(Df, Fsc, n=n, alpha=-1.0, incx=mnl+1,
                       incy=Fsc.size[0], offsetx=i, offsety=k-start)
                   blas.scal(math.sqrt(y[k]), Fsc, inc=Fsc.size[0],
                       offset=k-start)

                # H += z[i]*Hi = z[i] * Fisc' * Fisc
                blas.syrk(Fsc, H, trans='T', k=stop-start, alpha=z[i],
                    beta=1.0)

        if z is None:
            return f, Df
        return f, Df, H
Esempio n. 9
0
    def Fkkt(W):

        # Returns a function f(x, y, z) that solves
        #
        # [ 0  0  P'      -P'      ] [ x[:n] ]   [ bx[:n] ]
        # [ 0  0 -I       -I       ] [ x[n:] ]   [ bx[n:] ]
        # [ P -I -D1^{-1}  0       ] [ z[:m] ] = [ bz[:m] ]
        # [-P -I  0       -D2^{-1} ] [ z[m:] ]   [ bz[m:] ]
        #
        # where D1 = diag(di[:m])^2, D2 = diag(di[m:])^2 and di = W['di'].
        #
        # On entry bx, bz are stored in x, z.
        # On exit x, z contain the solution, with z scaled (di .* z is
        # returned instead of z).

        # Factor A = 4*P'*D*P where D = d1.*d2 ./(d1+d2) and
        # d1 = d[:m].^2, d2 = d[m:].^2.

        di = W['di']
        d1, d2 = di[:m]**2, di[m:]**2
        D = div(mul(d1, d2), d1 + d2)
        Ds = spdiag(2 * sqrt(D))
        base.gemm(Ds, P, Ps)
        blas.syrk(Ps, A, trans='T')
        lapack.potrf(A)

        def f(x, y, z):

            # Solve for x[:n]:
            #
            #    A*x[:n] = bx[:n] + P' * ( ((D1-D2)*(D1+D2)^{-1})*bx[n:]
            #        + (2*D1*D2*(D1+D2)^{-1}) * (bz[:m] - bz[m:]) ).

            blas.copy((mul(div(d1 - d2, d1 + d2), x[n:]) +
                       mul(2 * D, z[:m] - z[m:])), u)
            blas.gemv(P, u, x, beta=1.0, trans='T')
            lapack.potrs(A, x)

            # x[n:] := (D1+D2)^{-1} * (bx[n:] - D1*bz[:m] - D2*bz[m:]
            #     + (D1-D2)*P*x[:n])

            base.gemv(P, x, u)
            x[n:] = div(
                x[n:] - mul(d1, z[:m]) - mul(d2, z[m:]) + mul(d1 - d2, u),
                d1 + d2)

            # z[:m] := d1[:m] .* ( P*x[:n] - x[n:] - bz[:m])
            # z[m:] := d2[m:] .* (-P*x[:n] - x[n:] - bz[m:])

            z[:m] = mul(di[:m], u - x[n:] - z[:m])
            z[m:] = mul(di[m:], -u - x[n:] - z[m:])

        return f
Esempio n. 10
0
    def Fkkt(W): 

        # Returns a function f(x, y, z) that solves
        #
        # [ 0  0  P'      -P'      ] [ x[:n] ]   [ bx[:n] ]
        # [ 0  0 -I       -I       ] [ x[n:] ]   [ bx[n:] ]
        # [ P -I -D1^{-1}  0       ] [ z[:m] ] = [ bz[:m] ]
        # [-P -I  0       -D2^{-1} ] [ z[m:] ]   [ bz[m:] ]
        #
        # where D1 = diag(di[:m])^2, D2 = diag(di[m:])^2 and di = W['di'].
        #
        # On entry bx, bz are stored in x, z.
        # On exit x, z contain the solution, with z scaled (di .* z is
        # returned instead of z). 

        # Factor A = 4*P'*D*P where D = d1.*d2 ./(d1+d2) and
        # d1 = d[:m].^2, d2 = d[m:].^2.

        di = W['di']
        d1, d2 = di[:m]**2, di[m:]**2
        D = div( mul(d1,d2), d1+d2 )  
        Ds = spdiag(2 * sqrt(D))
        base.gemm(Ds, P, Ps)
        blas.syrk(Ps, A, trans = 'T')
        lapack.potrf(A)

        def f(x, y, z):

            # Solve for x[:n]:
            #
            #    A*x[:n] = bx[:n] + P' * ( ((D1-D2)*(D1+D2)^{-1})*bx[n:]
            #        + (2*D1*D2*(D1+D2)^{-1}) * (bz[:m] - bz[m:]) ).

            blas.copy(( mul( div(d1-d2, d1+d2), x[n:]) + 
                mul( 2*D, z[:m]-z[m:] ) ), u)
            blas.gemv(P, u, x, beta = 1.0, trans = 'T')
            lapack.potrs(A, x)

            # x[n:] := (D1+D2)^{-1} * (bx[n:] - D1*bz[:m] - D2*bz[m:]
            #     + (D1-D2)*P*x[:n])

            base.gemv(P, x, u)
            x[n:] =  div( x[n:] - mul(d1, z[:m]) - mul(d2, z[m:]) + 
                mul(d1-d2, u), d1+d2 )

            # z[:m] := d1[:m] .* ( P*x[:n] - x[n:] - bz[:m])
            # z[m:] := d2[m:] .* (-P*x[:n] - x[n:] - bz[m:]) 

            z[:m] = mul(di[:m],  u-x[n:]-z[:m])
            z[m:] = mul(di[m:], -u-x[n:]-z[m:])

        return f
Esempio n. 11
0
 def Fkkt(x, z, W):
     ds = (2.0 * div(1 + x**2, (1 - x**2)**2))**-0.5
     Asc = A * spdiag(ds)
     blas.syrk(Asc, S)
     S[::m+1] += 1.0 
     lapack.potrf(S)
     a = z[0]
     def g(x, y, z):
         x[:] = mul(x, ds) / a
         blas.gemv(Asc, x, v)
         lapack.potrs(S, v)
         blas.gemv(Asc, v, x, alpha = -1.0, beta = 1.0, trans = 'T')
         x[:] = mul(x, ds)  
     return g
def F(x=None, z=None):
    if x is None: return 0, matrix(1.0, (n,1))
    X = V * spdiag(x) * V.T
    L = +X
    try: lapack.potrf(L)
    except ArithmeticError: return None
    f = - 2.0 * (log(L[0,0])  + log(L[1,1]))
    W = +V
    blas.trsm(L, W)    
    gradf = matrix(-1.0, (1,2)) * W**2
    if z is None: return f, gradf
    H = matrix(0.0, (n,n))
    blas.syrk(W, H, trans='T')
    return f, gradf, z[0] * H**2
Esempio n. 13
0
 def Fkkt(x, z, W):
     ds = (2.0 * div(1 + x**2, (1 - x**2)**2))**-0.5
     Asc = A * spdiag(ds)
     blas.syrk(Asc, S)
     S[::m+1] += 1.0 
     lapack.potrf(S)
     a = z[0]
     def g(x, y, z):
         x[:] = mul(x, ds) / a
         blas.gemv(Asc, x, v)
         lapack.potrs(S, v)
         blas.gemv(Asc, v, x, alpha = -1.0, beta = 1.0, trans = 'T')
         x[:] = mul(x, ds)  
     return g
def barrier():

    # variables kept same from cvxopt example
    MAXITERS = 100
    ALPHA = 0.01
    BETA = 0.5

    x = matrix(0.0, (n, 1))
    H = matrix(0.0, (n, n))  # Symmetrix matrix

    for iter in range(MAXITERS):
        # get the gradient of the function
        d = (b - A * x)**-1
        g = A.T * d

        # print(d[:, n*[0]].size)
        # print(A.size)
        """bug here: won't multiply of two matrix of same dimension. code is looking into another dimension?"""
        # get Hessian
        # lower diaganol multiplied to constraint matrix, n*[0] is first center x^t(0)
        h = np.zeros(shape=(m, n))
        np.matmul(d[:, n * [0]], A, h)
        # use the BLAS solver to get the symmetric matrix and get roots
        blas.syrk(h, H, trans='T')

        # do Newton's step
        v = -g  # g is our gradient
        # LAPACK solves the matrix and gives us the tep value to transverse with
        lapack.posv(H, v)

        # Stop condition if exceeding tolerance
        lam = blas.dot(g, v)
        if sqrt(-lam) < mu:
            return x  # return the orignal value if we're above tolerance

        # Line search to go to optimal using ALPHA and BETA
        y = mul(A * v, d)
        step = 1.0
        while 1 - step * max(y) < 0:
            step *= BETA
        while True:
            if -sum(log(1 - step * y)) < (ALPHA * step * lam):
                break
            step *= BETA

        # increment x by the step times the negative gradient otherwise
        x += step * v
Esempio n. 15
0
def acent(A, b):
    """  
    Computes analytic center of A*x <= b with A m by n of rank n. 
    We assume that b > 0 and the feasible set is bounded.
    """

    MAXITERS = 100
    ALPHA = 0.01
    BETA = 0.5
    TOL = 1e-8

    ntdecrs = []
    m, n = A.size
    x = matrix(0.0, (n, 1))
    H = matrix(0.0, (n, n))

    for iter in range(MAXITERS):

        # Gradient is g = A^T * (1./(b-A*x)).
        d = (b - A * x) ** -1
        g = A.T * d

        # Hessian is H = A^T * diag(1./(b-A*x))^2 * A.
        Asc = mul(d[:, n * [0]], A)
        blas.syrk(Asc, H, trans="T")

        # Newton step is v = H^-1 * g.
        v = -g
        lapack.posv(H, v)

        # Directional derivative and Newton decrement.
        lam = blas.dot(g, v)
        ntdecrs += [sqrt(-lam)]
        print("%2d.  Newton decr. = %3.3e" % (iter, ntdecrs[-1]))
        if ntdecrs[-1] < TOL:
            return x, ntdecrs

        # Backtracking line search.
        y = mul(A * v, d)
        step = 1.0
        while 1 - step * max(y) < 0:
            step *= BETA
        while True:
            if -sum(log(1 - step * y)) < ALPHA * step * lam:
                break
            step *= BETA
        x += step * v
Esempio n. 16
0
def acent(A, b):
    """  
    Computes analytic center of A*x <= b with A m by n of rank n. 
    We assume that b > 0 and the feasible set is bounded.
    """

    MAXITERS = 100
    ALPHA = 0.01
    BETA = 0.5
    TOL = 1e-8

    ntdecrs = []
    m, n = A.size
    x = matrix(0.0, (n, 1))
    H = matrix(0.0, (n, n))

    for iter in range(MAXITERS):

        # Gradient is g = A^T * (1./(b-A*x)).
        d = (b - A * x)**-1
        g = A.T * d

        # Hessian is H = A^T * diag(1./(b-A*x))^2 * A.
        Asc = mul(d[:, n * [0]], A)
        blas.syrk(Asc, H, trans='T')

        # Newton step is v = H^-1 * g.
        v = -g
        lapack.posv(H, v)

        # Directional derivative and Newton decrement.
        lam = blas.dot(g, v)
        ntdecrs += [sqrt(-lam)]
        print("%2d.  Newton decr. = %3.3e" % (iter, ntdecrs[-1]))
        if ntdecrs[-1] < TOL: return x, ntdecrs

        # Backtracking line search.
        y = mul(A * v, d)
        step = 1.0
        while 1 - step * max(y) < 0:
            step *= BETA
        while True:
            if -sum(log(1 - step * y)) < ALPHA * step * lam: break
            step *= BETA
        x += step * v
Esempio n. 17
0
    def F(W):
        """
        Create a solver for the linear equations

                                C * ux + G' * uzl - 2*A'(uzs21) = bx
                                                         -uzs11 = bX1
                                                         -uzs22 = bX2
                                            G * ux - Dl^2 * uzl = bzl
            [ -uX1   -A(ux)' ]          [ uzs11 uzs21' ]     
            [                ] - r*r' * [              ] * r*r' = bzs
            [ -A(ux) -uX2    ]          [ uzs21 uzs22  ]

        where Dl = diag(W['l']), r = W['r'][0].  

        On entry, x = (bx, bX1, bX2) and z = [ bzl; bzs[:] ].
        On exit, x = (ux, uX1, uX2) and z = [ Dl*uzl; (r'*uzs*r)[:] ].


        1. Compute matrices V1, V2 such that (with T = r*r')
        
               [ V1   0   ] [ T11  T21' ] [ V1'  0  ]   [ I  S' ]
               [          ] [           ] [         ] = [       ]
               [ 0    V2' ] [ T21  T22  ] [ 0    V2 ]   [ S  I  ]
        
           and S = [ diag(s); 0 ], s a positive q-vector.

        2. Factor the mapping X -> X + S * X' * S:

               X + S * X' * S = L( L'( X )). 

        3. Compute scaled mappings: a matrix As with as its columns the 
           coefficients of the scaled mapping 

               L^-1( V2' * A() * V1' ) 

           and the matrix Gs = Dl^-1 * G.

        4. Cholesky factorization of H = C + Gs'*Gs + 2*As'*As.

        """

        # 1. Compute V1, V2, s.

        r = W['r'][0]

        # LQ factorization R[:q, :] = L1 * Q1.
        lapack.lacpy(r, Q1, m=q)
        lapack.gelqf(Q1, tau1)
        lapack.lacpy(Q1, L1, n=q, uplo='L')
        lapack.orglq(Q1, tau1)

        # LQ factorization R[q:, :] = L2 * Q2.
        lapack.lacpy(r, Q2, m=p, offsetA=q)
        lapack.gelqf(Q2, tau2)
        lapack.lacpy(Q2, L2, n=p, uplo='L')
        lapack.orglq(Q2, tau2)

        # V2, V1, s are computed from an SVD: if
        #
        #     Q2 * Q1' = U * diag(s) * V',
        #
        # then V1 = V' * L1^-1 and V2 = L2^-T * U.

        # T21 = Q2 * Q1.T
        blas.gemm(Q2, Q1, T21, transB='T')

        # SVD T21 = U * diag(s) * V'.  Store U in V2 and V' in V1.
        lapack.gesvd(T21, s, jobu='A', jobvt='A', U=V2, Vt=V1)

        #        # Q2 := Q2 * Q1' without extracting Q1; store T21 in Q2
        #        this will requires lapack.ormlq or lapack.unmlq

        # V2 = L2^-T * U
        blas.trsm(L2, V2, transA='T')

        # V1 = V' * L1^-1
        blas.trsm(L1, V1, side='R')

        # 2. Factorization X + S * X' * S = L( L'( X )).
        #
        # The factor L is stored as a diagonal matrix D and a sparse lower
        # triangular matrix P, such that
        #
        #     L(X)[:] = D**-1 * (I + P) * X[:]
        #     L^-1(X)[:] = D * (I - P) * X[:].

        # SS is q x q with SS[i,j] = si*sj.
        blas.scal(0.0, SS)
        blas.syr(s, SS)

        # For a p x q matrix X, P*X[:] is Y[:] where
        #
        #     Yij = si * sj * Xji  if i < j
        #         = 0              otherwise.
        #
        P.V = SS[Itril2]

        # For a p x q matrix X, D*X[:] is Y[:] where
        #
        #     Yij = Xij / sqrt( 1 - si^2 * sj^2 )  if i < j
        #         = Xii / sqrt( 1 + si^2 )         if i = j
        #         = Xij                            otherwise.
        #
        DV[Idiag] = sqrt(1.0 + SS[::q + 1])
        DV[Itriu] = sqrt(1.0 - SS[Itril3]**2)
        D.V = DV**-1

        # 3. Scaled linear mappings

        # Ask :=  V2' * Ask * V1'
        blas.scal(0.0, As)
        base.axpy(A, As)
        for i in xrange(n):
            # tmp := V2' * As[i, :]
            blas.gemm(V2,
                      As,
                      tmp,
                      transA='T',
                      m=p,
                      n=q,
                      k=p,
                      ldB=p,
                      offsetB=i * p * q)
            # As[:,i] := tmp * V1'
            blas.gemm(tmp,
                      V1,
                      As,
                      transB='T',
                      m=p,
                      n=q,
                      k=q,
                      ldC=p,
                      offsetC=i * p * q)

        # As := D * (I - P) * As
        #     = L^-1 * As.
        blas.copy(As, As2)
        base.gemm(P, As, As2, alpha=-1.0, beta=1.0)
        base.gemm(D, As2, As)

        # Gs := Dl^-1 * G
        blas.scal(0.0, Gs)
        base.axpy(G, Gs)
        for k in xrange(n):
            blas.tbmv(W['di'], Gs, n=m, k=0, ldA=1, offsetx=k * m)

        # 4. Cholesky factorization of H = C + Gs' * Gs + 2 * As' * As.

        blas.syrk(As, H, trans='T', alpha=2.0)
        blas.syrk(Gs, H, trans='T', beta=1.0)
        base.axpy(C, H)
        lapack.potrf(H)

        def f(x, y, z):
            """

            Solve 

                              C * ux + G' * uzl - 2*A'(uzs21) = bx
                                                       -uzs11 = bX1
                                                       -uzs22 = bX2
                                           G * ux - D^2 * uzl = bzl
                [ -uX1   -A(ux)' ]       [ uzs11 uzs21' ]     
                [                ] - T * [              ] * T = bzs.
                [ -A(ux) -uX2    ]       [ uzs21 uzs22  ]

            On entry, x = (bx, bX1, bX2) and z = [ bzl; bzs[:] ].
            On exit, x = (ux, uX1, uX2) and z = [ D*uzl; (r'*uzs*r)[:] ].

            Define X = uzs21, Z = T * uzs * T:   
 
                      C * ux + G' * uzl - 2*A'(X) = bx
                                [ 0  X' ]               [ bX1 0   ]
                            T * [       ] * T - Z = T * [         ] * T
                                [ X  0  ]               [ 0   bX2 ]
                               G * ux - D^2 * uzl = bzl
                [ -uX1   -A(ux)' ]   [ Z11 Z21' ]     
                [                ] - [          ] = bzs
                [ -A(ux) -uX2    ]   [ Z21 Z22  ]

            Return x = (ux, uX1, uX2), z = [ D*uzl; (rti'*Z*rti)[:] ].

            We use the congruence transformation 

                [ V1   0   ] [ T11  T21' ] [ V1'  0  ]   [ I  S' ]
                [          ] [           ] [         ] = [       ]
                [ 0    V2' ] [ T21  T22  ] [ 0    V2 ]   [ S  I  ]

            and the factorization 

                X + S * X' * S = L( L'(X) ) 

            to write this as

                                  C * ux + G' * uzl - 2*A'(X) = bx
                L'(V2^-1 * X * V1^-1) - L^-1(V2' * Z21 * V1') = bX
                                           G * ux - D^2 * uzl = bzl
                            [ -uX1   -A(ux)' ]   [ Z11 Z21' ]     
                            [                ] - [          ] = bzs,
                            [ -A(ux) -uX2    ]   [ Z21 Z22  ]

            or

                C * ux + Gs' * uuzl - 2*As'(XX) = bx
                                      XX - ZZ21 = bX
                                 Gs * ux - uuzl = D^-1 * bzl
                                 -As(ux) - ZZ21 = bbzs_21
                                     -uX1 - Z11 = bzs_11
                                     -uX2 - Z22 = bzs_22

            if we introduce scaled variables

                uuzl = D * uzl
                  XX = L'(V2^-1 * X * V1^-1) 
                     = L'(V2^-1 * uzs21 * V1^-1)
                ZZ21 = L^-1(V2' * Z21 * V1') 

            and define

                bbzs_21 = L^-1(V2' * bzs_21 * V1')
                                           [ bX1  0   ]
                     bX = L^-1( V2' * (T * [          ] * T)_21 * V1').
                                           [ 0    bX2 ]           
 
            Eliminating Z21 gives 

                C * ux + Gs' * uuzl - 2*As'(XX) = bx
                                 Gs * ux - uuzl = D^-1 * bzl
                                   -As(ux) - XX = bbzs_21 - bX
                                     -uX1 - Z11 = bzs_11
                                     -uX2 - Z22 = bzs_22 

            and eliminating uuzl and XX gives

                        H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bX - bbzs_21)
                Gs * ux - uuzl = D^-1 * bzl
                  -As(ux) - XX = bbzs_21 - bX
                    -uX1 - Z11 = bzs_11
                    -uX2 - Z22 = bzs_22.


            In summary, we can use the following algorithm: 

            1. bXX := bX - bbzs21
                                        [ bX1 0   ]
                    = L^-1( V2' * ((T * [         ] * T)_21 - bzs_21) * V1')
                                        [ 0   bX2 ]

            2. Solve H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bXX).

            3. From ux, compute 

                   uuzl = Gs*ux - D^-1 * bzl and 
                      X = V2 * L^-T(-As(ux) + bXX) * V1.

            4. Return ux, uuzl, 

                   rti' * Z * rti = r' * [ -bX1, X'; X, -bX2 ] * r
 
               and uX1 = -Z11 - bzs_11,  uX2 = -Z22 - bzs_22.

            """

            # Save bzs_11, bzs_22, bzs_21.
            lapack.lacpy(z, bz11, uplo='L', m=q, n=q, ldA=p + q, offsetA=m)
            lapack.lacpy(z, bz21, m=p, n=q, ldA=p + q, offsetA=m + q)
            lapack.lacpy(z,
                         bz22,
                         uplo='L',
                         m=p,
                         n=p,
                         ldA=p + q,
                         offsetA=m + (p + q + 1) * q)

            # zl := D^-1 * zl
            #     = D^-1 * bzl
            blas.tbmv(W['di'], z, n=m, k=0, ldA=1)

            # zs := r' * [ bX1, 0; 0, bX2 ] * r.

            # zs := [ bX1, 0; 0, bX2 ]
            blas.scal(0.0, z, offset=m)
            lapack.lacpy(x[1], z, uplo='L', m=q, n=q, ldB=p + q, offsetB=m)
            lapack.lacpy(x[2],
                         z,
                         uplo='L',
                         m=p,
                         n=p,
                         ldB=p + q,
                         offsetB=m + (p + q + 1) * q)

            # scale diagonal of zs by 1/2
            blas.scal(0.5, z, inc=p + q + 1, offset=m)

            # a := tril(zs)*r
            blas.copy(r, a)
            blas.trmm(z,
                      a,
                      side='L',
                      m=p + q,
                      n=p + q,
                      ldA=p + q,
                      ldB=p + q,
                      offsetA=m)

            # zs := a'*r + r'*a
            blas.syr2k(r,
                       a,
                       z,
                       trans='T',
                       n=p + q,
                       k=p + q,
                       ldB=p + q,
                       ldC=p + q,
                       offsetC=m)

            # bz21 := L^-1( V2' * ((r * zs * r')_21 - bz21) * V1')
            #
            #                           [ bX1 0   ]
            #       = L^-1( V2' * ((T * [         ] * T)_21 - bz21) * V1').
            #                           [ 0   bX2 ]

            # a = [ r21 r22 ] * z
            #   = [ r21 r22 ] * r' * [ bX1, 0; 0, bX2 ] * r
            #   = [ T21  T22 ] * [ bX1, 0; 0, bX2 ] * r
            blas.symm(z,
                      r,
                      a,
                      side='R',
                      m=p,
                      n=p + q,
                      ldA=p + q,
                      ldC=p + q,
                      offsetB=q)

            # bz21 := -bz21 + a * [ r11, r12 ]'
            #       = -bz21 + (T * [ bX1, 0; 0, bX2 ] * T)_21
            blas.gemm(a,
                      r,
                      bz21,
                      transB='T',
                      m=p,
                      n=q,
                      k=p + q,
                      beta=-1.0,
                      ldA=p + q,
                      ldC=p)

            # bz21 := V2' * bz21 * V1'
            #       = V2' * (-bz21 + (T*[bX1, 0; 0, bX2]*T)_21) * V1'
            blas.gemm(V2, bz21, tmp, transA='T', m=p, n=q, k=p, ldB=p)
            blas.gemm(tmp, V1, bz21, transB='T', m=p, n=q, k=q, ldC=p)

            # bz21[:] := D * (I-P) * bz21[:]
            #       = L^-1 * bz21[:]
            #       = bXX[:]
            blas.copy(bz21, tmp)
            base.gemv(P, bz21, tmp, alpha=-1.0, beta=1.0)
            base.gemv(D, tmp, bz21)

            # Solve H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bXX).

            # x[0] := x[0] + Gs'*zl + 2*As'(bz21)
            #       = bx + G' * D^-1 * bzl + 2 * As'(bXX)
            blas.gemv(Gs, z, x[0], trans='T', alpha=1.0, beta=1.0)
            blas.gemv(As, bz21, x[0], trans='T', alpha=2.0, beta=1.0)

            # x[0] := H \ x[0]
            #      = ux
            lapack.potrs(H, x[0])

            # uuzl = Gs*ux - D^-1 * bzl
            blas.gemv(Gs, x[0], z, alpha=1.0, beta=-1.0)

            # bz21 := V2 * L^-T(-As(ux) + bz21) * V1
            #       = X
            blas.gemv(As, x[0], bz21, alpha=-1.0, beta=1.0)
            blas.tbsv(DV, bz21, n=p * q, k=0, ldA=1)
            blas.copy(bz21, tmp)
            base.gemv(P, tmp, bz21, alpha=-1.0, beta=1.0, trans='T')
            blas.gemm(V2, bz21, tmp)
            blas.gemm(tmp, V1, bz21)

            # zs := -zs + r' * [ 0, X'; X, 0 ] * r
            #     = r' * [ -bX1, X'; X, -bX2 ] * r.

            # a := bz21 * [ r11, r12 ]
            #   =  X * [ r11, r12 ]
            blas.gemm(bz21, r, a, m=p, n=p + q, k=q, ldA=p, ldC=p + q)

            # z := -z + [ r21, r22 ]' * a + a' * [ r21, r22 ]
            #    = rti' * uzs * rti
            blas.syr2k(r,
                       a,
                       z,
                       trans='T',
                       beta=-1.0,
                       n=p + q,
                       k=p,
                       offsetA=q,
                       offsetC=m,
                       ldB=p + q,
                       ldC=p + q)

            # uX1 = -Z11 - bzs_11
            #     = -(r*zs*r')_11 - bzs_11
            # uX2 = -Z22 - bzs_22
            #     = -(r*zs*r')_22 - bzs_22

            blas.copy(bz11, x[1])
            blas.copy(bz22, x[2])

            # scale diagonal of zs by 1/2
            blas.scal(0.5, z, inc=p + q + 1, offset=m)

            # a := r*tril(zs)
            blas.copy(r, a)
            blas.trmm(z,
                      a,
                      side='R',
                      m=p + q,
                      n=p + q,
                      ldA=p + q,
                      ldB=p + q,
                      offsetA=m)

            # x[1] := -x[1] - a[:q,:] * r[:q, :]' - r[:q,:] * a[:q,:]'
            #       = -bzs_11 - (r*zs*r')_11
            blas.syr2k(a, r, x[1], n=q, alpha=-1.0, beta=-1.0)

            # x[2] := -x[2] - a[q:,:] * r[q:, :]' - r[q:,:] * a[q:,:]'
            #       = -bzs_22 - (r*zs*r')_22
            blas.syr2k(a,
                       r,
                       x[2],
                       n=p,
                       alpha=-1.0,
                       beta=-1.0,
                       offsetA=q,
                       offsetB=q)

            # scale diagonal of zs by 1/2
            blas.scal(2.0, z, inc=p + q + 1, offset=m)

        return f
Esempio n. 18
0
    def F(W):
        """
        Create a solver for the linear equations

                                C * ux + G' * uzl - 2*A'(uzs21) = bx
                                                         -uzs11 = bX1
                                                         -uzs22 = bX2
                                            G * ux - Dl^2 * uzl = bzl
            [ -uX1   -A(ux)' ]          [ uzs11 uzs21' ]     
            [                ] - r*r' * [              ] * r*r' = bzs
            [ -A(ux) -uX2    ]          [ uzs21 uzs22  ]

        where Dl = diag(W['l']), r = W['r'][0].  

        On entry, x = (bx, bX1, bX2) and z = [ bzl; bzs[:] ].
        On exit, x = (ux, uX1, uX2) and z = [ Dl*uzl; (r'*uzs*r)[:] ].


        1. Compute matrices V1, V2 such that (with T = r*r')
        
               [ V1   0   ] [ T11  T21' ] [ V1'  0  ]   [ I  S' ]
               [          ] [           ] [         ] = [       ]
               [ 0    V2' ] [ T21  T22  ] [ 0    V2 ]   [ S  I  ]
        
           and S = [ diag(s); 0 ], s a positive q-vector.

        2. Factor the mapping X -> X + S * X' * S:

               X + S * X' * S = L( L'( X )). 

        3. Compute scaled mappings: a matrix As with as its columns the 
           coefficients of the scaled mapping 

               L^-1( V2' * A() * V1' ) 

           and the matrix Gs = Dl^-1 * G.

        4. Cholesky factorization of H = C + Gs'*Gs + 2*As'*As.

        """


        # 1. Compute V1, V2, s.  

        r = W['r'][0]

        # LQ factorization R[:q, :] = L1 * Q1.
        lapack.lacpy(r, Q1, m = q)
        lapack.gelqf(Q1, tau1)
        lapack.lacpy(Q1, L1, n = q, uplo = 'L')
        lapack.orglq(Q1, tau1)

        # LQ factorization R[q:, :] = L2 * Q2.
        lapack.lacpy(r, Q2, m = p, offsetA = q)
	lapack.gelqf(Q2, tau2)
        lapack.lacpy(Q2, L2, n = p, uplo = 'L')
        lapack.orglq(Q2, tau2)


        # V2, V1, s are computed from an SVD: if
        # 
        #     Q2 * Q1' = U * diag(s) * V',
        #
        # then V1 = V' * L1^-1 and V2 = L2^-T * U.
    
        # T21 = Q2 * Q1.T  
        blas.gemm(Q2, Q1, T21, transB = 'T')

        # SVD T21 = U * diag(s) * V'.  Store U in V2 and V' in V1.
        lapack.gesvd(T21, s, jobu = 'A', jobvt = 'A', U = V2, Vt = V1) 

#        # Q2 := Q2 * Q1' without extracting Q1; store T21 in Q2
#        this will requires lapack.ormlq or lapack.unmlq

        # V2 = L2^-T * U   
        blas.trsm(L2, V2, transA = 'T') 

        # V1 = V' * L1^-1 
        blas.trsm(L1, V1, side = 'R') 


        # 2. Factorization X + S * X' * S = L( L'( X )).  
        #
        # The factor L is stored as a diagonal matrix D and a sparse lower 
        # triangular matrix P, such that  
        #
        #     L(X)[:] = D**-1 * (I + P) * X[:] 
        #     L^-1(X)[:] = D * (I - P) * X[:].

        # SS is q x q with SS[i,j] = si*sj.
        blas.scal(0.0, SS)
        blas.syr(s, SS)    
        
        # For a p x q matrix X, P*X[:] is Y[:] where 
        #
        #     Yij = si * sj * Xji  if i < j
        #         = 0              otherwise.
        # 
        P.V = SS[Itril2]

        # For a p x q matrix X, D*X[:] is Y[:] where 
        #
        #     Yij = Xij / sqrt( 1 - si^2 * sj^2 )  if i < j
        #         = Xii / sqrt( 1 + si^2 )         if i = j
        #         = Xij                            otherwise.
        # 
        DV[Idiag] = sqrt(1.0 + SS[::q+1])
        DV[Itriu] = sqrt(1.0 - SS[Itril3]**2)
        D.V = DV**-1


        # 3. Scaled linear mappings 
         
        # Ask :=  V2' * Ask * V1' 
        blas.scal(0.0, As)
        base.axpy(A, As)
        for i in xrange(n):
            # tmp := V2' * As[i, :]
            blas.gemm(V2, As, tmp, transA = 'T', m = p, n = q, k = p,
                ldB = p, offsetB = i*p*q)
            # As[:,i] := tmp * V1'
            blas.gemm(tmp, V1, As, transB = 'T', m = p, n = q, k = q,
                ldC = p, offsetC = i*p*q)

        # As := D * (I - P) * As 
        #     = L^-1 * As.
        blas.copy(As, As2)
        base.gemm(P, As, As2, alpha = -1.0, beta = 1.0)
        base.gemm(D, As2, As)

        # Gs := Dl^-1 * G 
        blas.scal(0.0, Gs)
        base.axpy(G, Gs)
        for k in xrange(n):
            blas.tbmv(W['di'], Gs, n = m, k = 0, ldA = 1, offsetx = k*m)


        # 4. Cholesky factorization of H = C + Gs' * Gs + 2 * As' * As.

        blas.syrk(As, H, trans = 'T', alpha = 2.0)
        blas.syrk(Gs, H, trans = 'T', beta = 1.0)
        base.axpy(C, H)   
        lapack.potrf(H)


        def f(x, y, z):
            """

            Solve 

                              C * ux + G' * uzl - 2*A'(uzs21) = bx
                                                       -uzs11 = bX1
                                                       -uzs22 = bX2
                                           G * ux - D^2 * uzl = bzl
                [ -uX1   -A(ux)' ]       [ uzs11 uzs21' ]     
                [                ] - T * [              ] * T = bzs.
                [ -A(ux) -uX2    ]       [ uzs21 uzs22  ]

            On entry, x = (bx, bX1, bX2) and z = [ bzl; bzs[:] ].
            On exit, x = (ux, uX1, uX2) and z = [ D*uzl; (r'*uzs*r)[:] ].

            Define X = uzs21, Z = T * uzs * T:   
 
                      C * ux + G' * uzl - 2*A'(X) = bx
                                [ 0  X' ]               [ bX1 0   ]
                            T * [       ] * T - Z = T * [         ] * T
                                [ X  0  ]               [ 0   bX2 ]
                               G * ux - D^2 * uzl = bzl
                [ -uX1   -A(ux)' ]   [ Z11 Z21' ]     
                [                ] - [          ] = bzs
                [ -A(ux) -uX2    ]   [ Z21 Z22  ]

            Return x = (ux, uX1, uX2), z = [ D*uzl; (rti'*Z*rti)[:] ].

            We use the congruence transformation 

                [ V1   0   ] [ T11  T21' ] [ V1'  0  ]   [ I  S' ]
                [          ] [           ] [         ] = [       ]
                [ 0    V2' ] [ T21  T22  ] [ 0    V2 ]   [ S  I  ]

            and the factorization 

                X + S * X' * S = L( L'(X) ) 

            to write this as

                                  C * ux + G' * uzl - 2*A'(X) = bx
                L'(V2^-1 * X * V1^-1) - L^-1(V2' * Z21 * V1') = bX
                                           G * ux - D^2 * uzl = bzl
                            [ -uX1   -A(ux)' ]   [ Z11 Z21' ]     
                            [                ] - [          ] = bzs,
                            [ -A(ux) -uX2    ]   [ Z21 Z22  ]

            or

                C * ux + Gs' * uuzl - 2*As'(XX) = bx
                                      XX - ZZ21 = bX
                                 Gs * ux - uuzl = D^-1 * bzl
                                 -As(ux) - ZZ21 = bbzs_21
                                     -uX1 - Z11 = bzs_11
                                     -uX2 - Z22 = bzs_22

            if we introduce scaled variables

                uuzl = D * uzl
                  XX = L'(V2^-1 * X * V1^-1) 
                     = L'(V2^-1 * uzs21 * V1^-1)
                ZZ21 = L^-1(V2' * Z21 * V1') 

            and define

                bbzs_21 = L^-1(V2' * bzs_21 * V1')
                                           [ bX1  0   ]
                     bX = L^-1( V2' * (T * [          ] * T)_21 * V1').
                                           [ 0    bX2 ]           
 
            Eliminating Z21 gives 

                C * ux + Gs' * uuzl - 2*As'(XX) = bx
                                 Gs * ux - uuzl = D^-1 * bzl
                                   -As(ux) - XX = bbzs_21 - bX
                                     -uX1 - Z11 = bzs_11
                                     -uX2 - Z22 = bzs_22 

            and eliminating uuzl and XX gives

                        H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bX - bbzs_21)
                Gs * ux - uuzl = D^-1 * bzl
                  -As(ux) - XX = bbzs_21 - bX
                    -uX1 - Z11 = bzs_11
                    -uX2 - Z22 = bzs_22.


            In summary, we can use the following algorithm: 

            1. bXX := bX - bbzs21
                                        [ bX1 0   ]
                    = L^-1( V2' * ((T * [         ] * T)_21 - bzs_21) * V1')
                                        [ 0   bX2 ]

            2. Solve H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bXX).

            3. From ux, compute 

                   uuzl = Gs*ux - D^-1 * bzl and 
                      X = V2 * L^-T(-As(ux) + bXX) * V1.

            4. Return ux, uuzl, 

                   rti' * Z * rti = r' * [ -bX1, X'; X, -bX2 ] * r
 
               and uX1 = -Z11 - bzs_11,  uX2 = -Z22 - bzs_22.

            """

            # Save bzs_11, bzs_22, bzs_21.
            lapack.lacpy(z, bz11, uplo = 'L', m = q, n = q, ldA = p+q,
                offsetA = m)
            lapack.lacpy(z, bz21, m = p, n = q, ldA = p+q, offsetA = m+q)
            lapack.lacpy(z, bz22, uplo = 'L', m = p, n = p, ldA = p+q,
                offsetA = m + (p+q+1)*q)


            # zl := D^-1 * zl
            #     = D^-1 * bzl
            blas.tbmv(W['di'], z, n = m, k = 0, ldA = 1)


            # zs := r' * [ bX1, 0; 0, bX2 ] * r.

            # zs := [ bX1, 0; 0, bX2 ]
            blas.scal(0.0, z, offset = m)
            lapack.lacpy(x[1], z, uplo = 'L', m = q, n = q, ldB = p+q,
                offsetB = m)
            lapack.lacpy(x[2], z, uplo = 'L', m = p, n = p, ldB = p+q,
                offsetB = m + (p+q+1)*q)

            # scale diagonal of zs by 1/2
            blas.scal(0.5, z, inc = p+q+1, offset = m)

            # a := tril(zs)*r  
            blas.copy(r, a)
            blas.trmm(z, a, side = 'L', m = p+q, n = p+q, ldA = p+q, ldB = 
                p+q, offsetA = m)

            # zs := a'*r + r'*a 
            blas.syr2k(r, a, z, trans = 'T', n = p+q, k = p+q, ldB = p+q,
                ldC = p+q, offsetC = m)



            # bz21 := L^-1( V2' * ((r * zs * r')_21 - bz21) * V1')
            #
            #                           [ bX1 0   ]
            #       = L^-1( V2' * ((T * [         ] * T)_21 - bz21) * V1').
            #                           [ 0   bX2 ]

            # a = [ r21 r22 ] * z
            #   = [ r21 r22 ] * r' * [ bX1, 0; 0, bX2 ] * r
            #   = [ T21  T22 ] * [ bX1, 0; 0, bX2 ] * r
            blas.symm(z, r, a, side = 'R', m = p, n = p+q, ldA = p+q, 
                ldC = p+q, offsetB = q)
    
            # bz21 := -bz21 + a * [ r11, r12 ]'
            #       = -bz21 + (T * [ bX1, 0; 0, bX2 ] * T)_21
            blas.gemm(a, r, bz21, transB = 'T', m = p, n = q, k = p+q, 
                beta = -1.0, ldA = p+q, ldC = p)

            # bz21 := V2' * bz21 * V1'
            #       = V2' * (-bz21 + (T*[bX1, 0; 0, bX2]*T)_21) * V1'
            blas.gemm(V2, bz21, tmp, transA = 'T', m = p, n = q, k = p, 
                ldB = p)
            blas.gemm(tmp, V1, bz21, transB = 'T', m = p, n = q, k = q, 
                ldC = p)

            # bz21[:] := D * (I-P) * bz21[:] 
            #       = L^-1 * bz21[:]
            #       = bXX[:]
            blas.copy(bz21, tmp)
            base.gemv(P, bz21, tmp, alpha = -1.0, beta = 1.0)
            base.gemv(D, tmp, bz21)


            # Solve H * ux = bx + Gs' * D^-1 * bzl + 2*As'(bXX).

            # x[0] := x[0] + Gs'*zl + 2*As'(bz21) 
            #       = bx + G' * D^-1 * bzl + 2 * As'(bXX)
            blas.gemv(Gs, z, x[0], trans = 'T', alpha = 1.0, beta = 1.0)
            blas.gemv(As, bz21, x[0], trans = 'T', alpha = 2.0, beta = 1.0) 

            # x[0] := H \ x[0] 
            #      = ux
            lapack.potrs(H, x[0])


            # uuzl = Gs*ux - D^-1 * bzl
            blas.gemv(Gs, x[0], z, alpha = 1.0, beta = -1.0)

            
            # bz21 := V2 * L^-T(-As(ux) + bz21) * V1
            #       = X
            blas.gemv(As, x[0], bz21, alpha = -1.0, beta = 1.0)
            blas.tbsv(DV, bz21, n = p*q, k = 0, ldA = 1)
            blas.copy(bz21, tmp)
            base.gemv(P, tmp, bz21, alpha = -1.0, beta = 1.0, trans = 'T')
            blas.gemm(V2, bz21, tmp)
            blas.gemm(tmp, V1, bz21)


            # zs := -zs + r' * [ 0, X'; X, 0 ] * r
            #     = r' * [ -bX1, X'; X, -bX2 ] * r.

            # a := bz21 * [ r11, r12 ]
            #   =  X * [ r11, r12 ]
            blas.gemm(bz21, r, a, m = p, n = p+q, k = q, ldA = p, ldC = p+q)
            
            # z := -z + [ r21, r22 ]' * a + a' * [ r21, r22 ]
            #    = rti' * uzs * rti
            blas.syr2k(r, a, z, trans = 'T', beta = -1.0, n = p+q, k = p,
                offsetA = q, offsetC = m, ldB = p+q, ldC = p+q)  



            # uX1 = -Z11 - bzs_11 
            #     = -(r*zs*r')_11 - bzs_11
            # uX2 = -Z22 - bzs_22 
            #     = -(r*zs*r')_22 - bzs_22


            blas.copy(bz11, x[1])
            blas.copy(bz22, x[2])

            # scale diagonal of zs by 1/2
            blas.scal(0.5, z, inc = p+q+1, offset = m)

            # a := r*tril(zs)  
            blas.copy(r, a)
            blas.trmm(z, a, side = 'R', m = p+q, n = p+q, ldA = p+q, ldB = 
                p+q, offsetA = m)

            # x[1] := -x[1] - a[:q,:] * r[:q, :]' - r[:q,:] * a[:q,:]'
            #       = -bzs_11 - (r*zs*r')_11
            blas.syr2k(a, r, x[1], n = q, alpha = -1.0, beta = -1.0) 

            # x[2] := -x[2] - a[q:,:] * r[q:, :]' - r[q:,:] * a[q:,:]'
            #       = -bzs_22 - (r*zs*r')_22
            blas.syr2k(a, r, x[2], n = p, alpha = -1.0, beta = -1.0, 
                offsetA = q, offsetB = q)

            # scale diagonal of zs by 1/2
            blas.scal(2.0, z, inc = p+q+1, offset = m)


        return f
Esempio n. 19
0
def custom_kkt(W):
    """
    Custom KKT solver for the following conic LP
    formulation of the Schur relaxation of the
    balance-constrained min/max cut problem
    
        maximize     Tr(C,X)
        subject to 
            X_{ii} = 1, i=1...n
            sum(X) + x = const
            x >= 0, X psd
    """
    r = W['rti'][0]
    N = r.size[0]
    e = matrix(1.0, (N, 1))

    # Form and factorize reduced KKT system
    H = matrix(0.0, (N + 1, N + 1))
    blas.syrk(r, H, n=N, ldC=N + 1)
    blas.symv(H, e, H, n=N, ldA=N + 1, offsety=N, incy=N + 1)
    H[N, N] = blas.dot(H, e, n=N, offsetx=N, incx=N + 1)
    rr = H[:N, :N]  # Extract and symmetrize (1,1) block
    misc.symm(rr, N)  #
    q = H[N, :N].T  # Extract q = rr*e
    H = mul(H, H)
    H[N, N] += W['di'][0]**2
    lapack.potrf(H)

    def fsolve(x, y, z):
        """
        Solves the system of equations

            [ 0  G'*W^{-1} ] [ ux ] = [ bx ]
            [ G  -W'       ] [ uz ]   [ bz ]
        
        """
        #  Compute bx := bx + G'*W^{-1}*W^{-T}*bz
        v = matrix(0., (N, 1))
        for i in range(N):
            blas.symv(z, rr, v, ldA=N, offsetA=1, n=N, offsetx=N * i)
            x[i] += blas.dot(rr, v, n=N, offsetx=N * i)
        blas.symv(z, q, v, ldA=N, offsetA=1, n=N)
        x[N] += blas.dot(q, v) + z[0] * W['di'][0]**2
        #  Solve G'*W^{-1}*W^{-T}*G*ux = bx
        lapack.potrs(H, x)

        # Compute bz := -W^{-T}*(bz-G*ux)
        # z -= G*x
        z[1::N + 1] -= x[:-1]
        z -= x[-1]
        # Apply scaling
        z[0] *= -W['di'][0]
        blas.scal(0.5, z, n=N, offset=1, inc=N + 1)
        tmp = +r
        blas.trmm(z, tmp, ldA=N, offsetA=1, n=N, m=N)
        blas.syr2k(r,
                   tmp,
                   z,
                   trans='T',
                   offsetC=1,
                   ldC=N,
                   n=N,
                   k=N,
                   alpha=-1.0)

    return fsolve
Esempio n. 20
0
def Fkkt(W):
    """
        Custom solver:

          v := alpha * 2*A'*A * u + beta * v
    """

    global mmS
    mmS = matrix(0.0, (iR, iR))

    global vvV
    vvV = matrix(0.0, (iR, 1))

    # Factor
    #
    #     S = A*D^-1*A' + I
    #
    # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**2, D2 = d[n:]**2.
    mmAsc = matrix(0.0, (iR, iC))

    d1, d2 = W['di'][:iC]**2, W['di'][iC:]**2

    # ds is square root of diagonal of D
    ds = sqrt(2.0) * div(mul(W['di'][:iC], W['di'][iC:]), sqrt(d1 + d2))
    d3 = div(d2 - d1, d1 + d2)

    # Asc = A*diag(d)^-1/2
    blas.copy(mmTh, mmAsc)
    for k in range(iR):
        blas.tbsv(ds, mmAsc, n=iC, k=0, ldA=1, incx=iR, offsetx=k)

    # S = I + A * D^-1 * A'
    blas.syrk(mmAsc, mmS)
    mmS[::iR + 1] += 1.0
    lapack.potrf(mmS)

    def g(x, y, z):

        x[:iC] = 0.5 * ( x[:iC] - mul(d3, x[iC:]) + \
                mul(d1, z[:iC] + mul(d3, z[:iC])) - \
                mul(d2, z[iC:] - mul(d3, z[iC:])) )
        x[:iC] = div(x[:iC], ds)

        # Solve
        #
        #     S * v = 0.5 * A * D^-1 * ( bx[:n]
        #             - (D2-D1)*(D1+D2)^-1 * bx[n:]
        #             + D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bz[:n]
        #             - D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bz[n:] )

        blas.gemv(mmAsc, x, vvV)
        lapack.potrs(mmS, vvV)

        # x[:n] = D^-1 * ( rhs - A'*v ).
        blas.gemv(mmAsc, vvV, x, alpha=-1.0, beta=1.0, trans='T')
        x[:iC] = div(x[:iC], ds)

        # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bz[:n]  - D2*bz[n:] )
        #         - (D2-D1)*(D1+D2)^-1 * x[:n]
        x[iC:] = div( x[iC:] - mul(d1, z[:iC]) - mul(d2, z[iC:]), d1+d2 )\
                - mul( d3, x[:iC] )

        # z[:n] = D1^1/2 * (  x[:n] - x[n:] - bz[:n] )
        # z[n:] = D2^1/2 * ( -x[:n] - x[n:] - bz[n:] ).
        z[:iC] = mul(W['di'][:iC], x[:iC] - x[iC:] - z[:iC])
        z[iC:] = mul(W['di'][iC:], -x[:iC] - x[iC:] - z[iC:])

    return g
Esempio n. 21
0
    def F(W):
        """
        Generate a solver for

                                             A'(uz0) = bx[0]
                                          -uz0 - uz1 = bx[1] 
            A(ux[0]) - ux[1] - r0*r0' * uz0 * r0*r0' = bz0 
                     - ux[1] - r1*r1' * uz1 * r1*r1' = bz1.

        uz0, uz1, bz0, bz1 are symmetric m x m-matrices.
        ux[0], bx[0] are n-vectors.
        ux[1], bx[1] are symmetric m x m-matrices.

        We first calculate a congruence that diagonalizes r0*r0' and r1*r1':
 
            U' * r0 * r0' * U = I,  U' * r1 * r1' * U = S.

        We then make a change of variables

            usx[0] = ux[0],  
            usx[1] = U' * ux[1] * U  
              usz0 = U^-1 * uz0 * U^-T  
              usz1 = U^-1 * uz1 * U^-T 

        and define 

              As() = U' * A() * U'  
            bsx[1] = U^-1 * bx[1] * U^-T
              bsz0 = U' * bz0 * U  
              bsz1 = U' * bz1 * U.  

        This gives

                             As'(usz0) = bx[0]
                          -usz0 - usz1 = bsx[1] 
            As(usx[0]) - usx[1] - usz0 = bsz0 
                -usx[1] - S * usz1 * S = bsz1.


        1. Eliminate usz0, usz1 using equations 3 and 4,

               usz0 = As(usx[0]) - usx[1] - bsz0
               usz1 = -S^-1 * (usx[1] + bsz1) * S^-1.

           This gives two equations in usx[0] an usx[1].

               As'(As(usx[0]) - usx[1]) = bx[0] + As'(bsz0)

               -As(usx[0]) + usx[1] + S^-1 * usx[1] * S^-1
                   = bsx[1] - bsz0 - S^-1 * bsz1 * S^-1.


        2. Eliminate usx[1] using equation 2:

               usx[1] + S * usx[1] * S 
                   = S * ( As(usx[0]) + bsx[1] - bsz0 ) * S - bsz1

           i.e., with Gamma[i,j] = 1.0 + S[i,i] * S[j,j],
 
               usx[1] = ( S * As(usx[0]) * S ) ./ Gamma 
                        + ( S * ( bsx[1] - bsz0 ) * S - bsz1 ) ./ Gamma.

           This gives an equation in usx[0].

               As'( As(usx[0]) ./ Gamma ) 
                   = bx0 + As'(bsz0) + 
                     As'( (S * ( bsx[1] - bsz0 ) * S - bsz1) ./ Gamma )
                   = bx0 + As'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ Gamma ).

        """

        # Calculate U s.t.
        #
        #     U' * r0*r0' * U = I,   U' * r1*r1' * U = diag(s).

        # Cholesky factorization r0 * r0' = L * L'
        blas.syrk(W['r'][0], L)
        lapack.potrf(L)

        # SVD L^-1 * r1 = U * diag(s) * V'
        blas.copy(W['r'][1], U)
        blas.trsm(L, U)
        lapack.gesvd(U, s, jobu='O')

        # s := s**2
        s[:] = s**2

        # Uti := U
        blas.copy(U, Uti)

        # U := L^-T * U
        blas.trsm(L, U, transA='T')

        # Uti := L * Uti = U^-T
        blas.trmm(L, Uti)

        # Us := U * diag(s)^-1
        blas.copy(U, Us)
        for i in range(m):
            blas.tbsv(s, Us, n=m, k=0, ldA=1, incx=m, offsetx=i)

        # S is m x m with lower triangular entries s[i] * s[j]
        # sqrtG is m x m with lower triangular entries sqrt(1.0 + s[i]*s[j])
        # Upper triangular entries are undefined but nonzero.

        blas.scal(0.0, S)
        blas.syrk(s, S)
        Gamma = 1.0 + S
        sqrtG = sqrt(Gamma)

        # Asc[i] = (U' * Ai * * U ) ./  sqrtG,  for i = 1, ..., n
        #        = Asi ./ sqrt(Gamma)
        blas.copy(A, Asc)
        misc.scale(
            Asc,  # only 'r' part of the dictionary is used   
            {
                'dnl': matrix(0.0, (0, 1)),
                'dnli': matrix(0.0, (0, 1)),
                'd': matrix(0.0, (0, 1)),
                'di': matrix(0.0, (0, 1)),
                'v': [],
                'beta': [],
                'r': [U],
                'rti': [U]
            })
        for i in range(n):
            blas.tbsv(sqrtG, Asc, n=msq, k=0, ldA=1, offsetx=i * msq)

        # Convert columns of Asc to packed storage
        misc.pack2(Asc, {'l': 0, 'q': [], 's': [m]})

        # Cholesky factorization of Asc' * Asc.
        H = matrix(0.0, (n, n))
        blas.syrk(Asc, H, trans='T', k=mpckd)
        lapack.potrf(H)

        def solve(x, y, z):
            """

            1. Solve for usx[0]:

               Asc'(Asc(usx[0]))
                   = bx0 + Asc'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ sqrtG)
                   = bx0 + Asc'( ( bsz0 + S * ( bsx[1] - bssz1) S ) 
                     ./ sqrtG)

               where bsx[1] = U^-1 * bx[1] * U^-T, bsz0 = U' * bz0 * U, 
               bsz1 = U' * bz1 * U, bssz1 = S^-1 * bsz1 * S^-1 

            2. Solve for usx[1]:

               usx[1] + S * usx[1] * S 
                   = S * ( As(usx[0]) + bsx[1] - bsz0 ) * S - bsz1 

               usx[1] 
                   = ( S * (As(usx[0]) + bsx[1] - bsz0) * S - bsz1) ./ Gamma
                   = -bsz0 + (S * As(usx[0]) * S) ./ Gamma
                     + (bsz0 - bsz1 + S * bsx[1] * S ) . / Gamma
                   = -bsz0 + (S * As(usx[0]) * S) ./ Gamma
                     + (bsz0 + S * ( bsx[1] - bssz1 ) * S ) . / Gamma

               Unscale ux[1] = Uti * usx[1] * Uti'

            3. Compute usz0, usz1

               r0' * uz0 * r0 = r0^-1 * ( A(ux[0]) - ux[1] - bz0 ) * r0^-T
               r1' * uz1 * r1 = r1^-1 * ( -ux[1] - bz1 ) * r1^-T

            """

            # z0 := U' * z0 * U
            #     = bsz0
            __cngrnc(U, z, trans='T')

            # z1 := Us' * bz1 * Us
            #     = S^-1 * U' * bz1 * U * S^-1
            #     = S^-1 * bsz1 * S^-1
            __cngrnc(Us, z, trans='T', offsetx=msq)

            # x[1] := Uti' * x[1] * Uti
            #       = bsx[1]
            __cngrnc(Uti, x[1], trans='T')

            # x[1] := x[1] - z[msq:]
            #       = bsx[1] - S^-1 * bsz1 * S^-1
            blas.axpy(z, x[1], alpha=-1.0, offsetx=msq)

            # x1 = (S * x[1] * S + z[:msq] ) ./ sqrtG
            #    = (S * ( bsx[1] - S^-1 * bsz1 * S^-1) * S + bsz0 ) ./ sqrtG
            #    = (S * bsx[1] * S - bsz1 + bsz0 ) ./ sqrtG
            # in packed storage
            blas.copy(x[1], x1)
            blas.tbmv(S, x1, n=msq, k=0, ldA=1)
            blas.axpy(z, x1, n=msq)
            blas.tbsv(sqrtG, x1, n=msq, k=0, ldA=1)
            misc.pack2(x1, {'l': 0, 'q': [], 's': [m]})

            # x[0] := x[0] + Asc'*x1
            #       = bx0 + Asc'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ sqrtG)
            #       = bx0 + As'( ( bz0 - bz1 + S * bx[1] * S ) ./ Gamma )
            blas.gemv(Asc, x1, x[0], m=mpckd, trans='T', beta=1.0)

            # x[0] := H^-1 * x[0]
            #       = ux[0]
            lapack.potrs(H, x[0])

            # x1 = Asc(x[0]) .* sqrtG  (unpacked)
            #    = As(x[0])
            blas.gemv(Asc, x[0], tmp, m=mpckd)
            misc.unpack(tmp, x1, {'l': 0, 'q': [], 's': [m]})
            blas.tbmv(sqrtG, x1, n=msq, k=0, ldA=1)

            # usx[1] = (x1 + (x[1] - z[:msq])) ./ sqrtG**2
            #        = (As(ux[0]) + bsx[1] - bsz0 - S^-1 * bsz1 * S^-1)
            #           ./ Gamma

            # x[1] := x[1] - z[:msq]
            #       = bsx[1] - bsz0 - S^-1 * bsz1 * S^-1
            blas.axpy(z, x[1], -1.0, n=msq)

            # x[1] := x[1] + x1
            #       = As(ux) + bsx[1] - bsz0 - S^-1 * bsz1 * S^-1
            blas.axpy(x1, x[1])

            # x[1] := x[1] / Gammma
            #       = (As(ux) + bsx[1] - bsz0 + S^-1 * bsz1 * S^-1 ) / Gamma
            #       = S^-1 * usx[1] * S^-1
            blas.tbsv(Gamma, x[1], n=msq, k=0, ldA=1)

            # z[msq:] := r1' * U * (-z[msq:] - x[1]) * U * r1
            #         := -r1' * U * S^-1 * (bsz1 + ux[1]) * S^-1 *  U * r1
            #         := -r1' * uz1 * r1
            blas.axpy(x[1], z, n=msq, offsety=msq)
            blas.scal(-1.0, z, offset=msq)
            __cngrnc(U, z, offsetx=msq)
            __cngrnc(W['r'][1], z, trans='T', offsetx=msq)

            # x[1] :=  S * x[1] * S
            #       =  usx1
            blas.tbmv(S, x[1], n=msq, k=0, ldA=1)

            # z[:msq] = r0' * U' * ( x1 - x[1] - z[:msq] ) * U * r0
            #         = r0' * U' * ( As(ux) - usx1 - bsz0 ) * U * r0
            #         = r0' * U' *  usz0 * U * r0
            #         = r0' * uz0 * r0
            blas.axpy(x1, z, -1.0, n=msq)
            blas.scal(-1.0, z, n=msq)
            blas.axpy(x[1], z, -1.0, n=msq)
            __cngrnc(U, z)
            __cngrnc(W['r'][0], z, trans='T')

            # x[1] := Uti * x[1] * Uti'
            #       = ux[1]
            __cngrnc(Uti, x[1])

        return solve
Esempio n. 22
0
def Fkkt(W):
    """
        Custom solver:

          v := alpha * 2*A'*A * u + beta * v
    """

    global mmS
    mmS = matrix(0.0, (iR, iR))

    global vvV
    vvV = matrix(0.0, (iR, 1))

    # Factor
    #
    #     S = A*D^-1*A' + I
    #
    # where D = 2*D1*D2*(D1+D2)^-1, D1 = d[:n]**2, D2 = d[n:]**2.
    mmAsc = matrix(0.0, (iR, iC))

    d1, d2 = W["di"][:iC] ** 2, W["di"][iC:] ** 2

    # ds is square root of diagonal of D
    ds = sqrt(2.0) * div(mul(W["di"][:iC], W["di"][iC:]), sqrt(d1 + d2))
    d3 = div(d2 - d1, d1 + d2)

    # Asc = A*diag(d)^-1/2
    blas.copy(mmTh, mmAsc)
    for k in range(iR):
        blas.tbsv(ds, mmAsc, n=iC, k=0, ldA=1, incx=iR, offsetx=k)

    # S = I + A * D^-1 * A'
    blas.syrk(mmAsc, mmS)
    mmS[:: iR + 1] += 1.0
    lapack.potrf(mmS)

    def g(x, y, z):

        x[:iC] = 0.5 * (
            x[:iC] - mul(d3, x[iC:]) + mul(d1, z[:iC] + mul(d3, z[:iC])) - mul(d2, z[iC:] - mul(d3, z[iC:]))
        )
        x[:iC] = div(x[:iC], ds)

        # Solve
        #
        #     S * v = 0.5 * A * D^-1 * ( bx[:n]
        #             - (D2-D1)*(D1+D2)^-1 * bx[n:]
        #             + D1 * ( I + (D2-D1)*(D1+D2)^-1 ) * bz[:n]
        #             - D2 * ( I - (D2-D1)*(D1+D2)^-1 ) * bz[n:] )

        blas.gemv(mmAsc, x, vvV)
        lapack.potrs(mmS, vvV)

        # x[:n] = D^-1 * ( rhs - A'*v ).
        blas.gemv(mmAsc, vvV, x, alpha=-1.0, beta=1.0, trans="T")
        x[:iC] = div(x[:iC], ds)

        # x[n:] = (D1+D2)^-1 * ( bx[n:] - D1*bz[:n]  - D2*bz[n:] )
        #         - (D2-D1)*(D1+D2)^-1 * x[:n]
        x[iC:] = div(x[iC:] - mul(d1, z[:iC]) - mul(d2, z[iC:]), d1 + d2) - mul(d3, x[:iC])

        # z[:n] = D1^1/2 * (  x[:n] - x[n:] - bz[:n] )
        # z[n:] = D2^1/2 * ( -x[:n] - x[n:] - bz[n:] ).
        z[:iC] = mul(W["di"][:iC], x[:iC] - x[iC:] - z[:iC])
        z[iC:] = mul(W["di"][iC:], -x[:iC] - x[iC:] - z[iC:])

    return g
Esempio n. 23
0
    def F(W): 
        """
        Custom solver for the system

        [  It  0   0    Xt'     0     At1' ...  Atk' ][ dwt  ]   [ rwt ]
        [  0   0   0    -d'     0      0   ...   0   ][ db   ]   [ rb  ]
        [  0   0   0    -I     -I      0   ...   0   ][ dv   ]   [ rv  ]
        [  Xt -d  -I  -Wl1^-2                        ][ dzl1 ]   [ rl1 ]
        [  0   0  -I         -Wl2^-2                 ][ dzl2 ] = [ rl2 ]
        [ At1  0   0                -W1^-2           ][ dz1  ]   [ r1  ] 
        [  |   |   |                       .         ][  |   ]   [  |  ]
        [ Atk  0   0                          -Wk^-2 ][ dzk  ]   [ rk  ]

        where

        It = [ I 0 ]  Xt = [ -D*X E ]  Ati = [ 0   -e_i' ]  
             [ 0 0 ]                         [ -Pi   0   ] 

        dwt = [ dw ]  rwt = [ rw ]
              [ dt ]        [ rt ].

        """

        # scalings and 'intermediate' vectors
        # db = inv(Wl1)^2 + inv(Wl2)^2
        db = W['di'][:m]**2 + W['di'][m:2*m]**2
        dbi = div(1.0,db)
        
        # dt = I - inv(Wl1)*Dbi*inv(Wl1)
        dt = 1.0 - mul(W['di'][:m]**2,dbi)
        dtsqrt = sqrt(dt)

        # lam = Dt*inv(Wl1)*d
        lam = mul(dt,mul(W['di'][:m],d))

        # lt = E'*inv(Wl1)*lam
        lt = matrix(0.0,(k,1))
        base.gemv(E, mul(W['di'][:m],lam), lt, trans = 'T')

        # Xs = sqrt(Dt)*inv(Wl1)*X
        tmp = mul(dtsqrt,W['di'][:m])
        Xs = spmatrix(tmp,range(m),range(m))*X

        # Es = D*sqrt(Dt)*inv(Wl1)*E
        Es = spmatrix(mul(d,tmp),range(m),range(m))*E

        # form Ab = I + sum((1/bi)^2*(Pi'*Pi + 4*(v'*v + 1)*Pi'*y*y'*Pi)) + Xs'*Xs
        #  and Bb = -sum((1/bi)^2*(4*ui*v'*v*Pi'*y*ei')) - Xs'*Es
        #  and D2 = Es'*Es + sum((1/bi)^2*(1+4*ui^2*(v'*v - 1))
        Ab = matrix(0.0,(n,n))
        Ab[::n+1] = 1.0
        base.syrk(Xs,Ab,trans = 'T', beta = 1.0)
        Bb = matrix(0.0,(n,k))
        Bb = -Xs.T*Es # inefficient!?
        D2 = spmatrix(0.0,range(k),range(k))
        base.syrk(Es,D2,trans = 'T', partial = True)
        d2 = +D2.V
        del D2
        py = matrix(0.0,(n,1))
        for i in range(k):
            binvsq = (1.0/W['beta'][i])**2
            Ab += binvsq*Pt[i]
            dvv = blas.dot(W['v'][i],W['v'][i])
            blas.gemv(P[i], W['v'][i][1:], py, trans = 'T', alpha = 1.0, beta = 0.0)
            blas.syrk(py, Ab, alpha = 4*binvsq*(dvv+1), beta = 1.0)
            Bb[:,i] -= 4*binvsq*W['v'][i][0]*dvv*py
            d2[i] += binvsq*(1+4*(W['v'][i][0]**2)*(dvv-1))
        
        d2i = div(1.0,d2)
        d2isqrt = sqrt(d2i)

        # compute a = alpha - lam'*inv(Wl1)*E*inv(D2)*E'*inv(Wl1)*lam
        alpha = blas.dot(lam,mul(W['di'][:m],d))
        tmp = matrix(0.0,(k,1))
        base.gemv(E,mul(W['di'][:m],lam), tmp, trans = 'T')
        tmp = mul(tmp, d2isqrt) #tmp = inv(D2)^(1/2)*E'*inv(Wl1)*lam
        a = alpha - blas.dot(tmp,tmp)

        # compute M12 = X'*D*inv(Wl1)*lam + Bb*inv(D2)*E'*inv(Wl1)*lam
        tmp = mul(tmp, d2isqrt)
        M12 = matrix(0.0,(n,1))
        blas.gemv(Bb,tmp,M12, alpha = 1.0)
        tmp = mul(d,mul(W['di'][:m],lam))
        blas.gemv(X,tmp,M12, trans = 'T', alpha = 1.0, beta = 1.0)

        # form and factor M
        sBb = Bb * spmatrix(d2isqrt,range(k), range(k)) 
        base.syrk(sBb, Ab, alpha = -1.0, beta = 1.0)
        M = matrix([[Ab, M12.T],[M12, a]])
        lapack.potrf(M)
        
        def f(x,y,z):
            
            # residuals
            rwt = x[:n+k]
            rb = x[n+k]
            rv = x[n+k+1:n+k+1+m]
            iw_rl1 = mul(W['di'][:m],z[:m])
            iw_rl2 = mul(W['di'][m:2*m],z[m:2*m])
            ri = [z[2*m+i*(n+1):2*m+(i+1)*(n+1)] for i in range(k)]
            
            # compute 'derived' residuals 
            # rbwt = rwt + sum(Ai'*inv(Wi)^2*ri) + [-X'*D; E']*inv(Wl1)^2*rl1
            rbwt = +rwt
            for i in range(k):
                tmp = +ri[i]
                qscal(tmp,W['beta'][i],W['v'][i],inv=True)
                qscal(tmp,W['beta'][i],W['v'][i],inv=True)
                rbwt[n+i] -= tmp[0]
                blas.gemv(P[i], tmp[1:], rbwt, trans = 'T', alpha = -1.0, beta = 1.0)
            tmp = mul(W['di'][:m],iw_rl1)
            tmp2 = matrix(0.0,(k,1))
            base.gemv(E,tmp,tmp2,trans='T')
            rbwt[n:] += tmp2
            tmp = mul(d,tmp) # tmp = D*inv(Wl1)^2*rl1
            blas.gemv(X,tmp,rbwt,trans='T', alpha = -1.0, beta = 1.0)
            
            # rbb = rb - d'*inv(Wl1)^2*rl1
            rbb = rb - sum(tmp)

            # rbv = rv - inv(Wl2)*rl2 - inv(Wl1)^2*rl1
            rbv = rv - mul(W['di'][m:2*m],iw_rl2) - mul(W['di'][:m],iw_rl1) 
            
            # [rtw;rtt] = rbwt + [-X'*D; E']*inv(Wl1)^2*inv(Db)*rbv 
            tmp = mul(W['di'][:m]**2, mul(dbi,rbv))
            rtt = +rbwt[n:] 
            base.gemv(E, tmp, rtt, trans = 'T', alpha = 1.0, beta = 1.0)
            rtw = +rbwt[:n]
            tmp = mul(d,tmp)
            blas.gemv(X, tmp, rtw, trans = 'T', alpha = -1.0, beta = 1.0)

            # rtb = rbb - d'*inv(Wl1)^2*inv(Db)*rbv
            rtb = rbb - sum(tmp)
            
            # solve M*[dw;db] = [rtw - Bb*inv(D2)*rtt; rtb + lt'*inv(D2)*rtt]
            tmp = mul(d2i,rtt)
            tmp2 = matrix(0.0,(n,1))
            blas.gemv(Bb,tmp,tmp2)
            dwdb = matrix([rtw - tmp2,rtb + blas.dot(mul(d2i,lt),rtt)]) 
            lapack.potrs(M,dwdb)

            # compute dt = inv(D2)*(rtt - Bb'*dw + lt*db)
            tmp2 = matrix(0.0,(k,1))
            blas.gemv(Bb, dwdb[:n], tmp2, trans='T')
            dt = mul(d2i, rtt - tmp2 + lt*dwdb[-1])

            # compute dv = inv(Db)*(rbv + inv(Wl1)^2*(E*dt - D*X*dw - d*db))
            dv = matrix(0.0,(m,1))
            blas.gemv(X,dwdb[:n],dv,alpha = -1.0)
            dv = mul(d,dv) - d*dwdb[-1]
            base.gemv(E, dt, dv, beta = 1.0)
            tmp = +dv  # tmp = E*dt - D*X*dw - d*db
            dv = mul(dbi, rbv + mul(W['di'][:m]**2,dv))

            # compute wdz1 = inv(Wl1)*(E*dt - D*X*dw - d*db - dv - rl1)
            wdz1 = mul(W['di'][:m], tmp - dv) - iw_rl1

            # compute wdz2 = - inv(Wl2)*(dv + rl2)
            wdz2 = - mul(W['di'][m:2*m],dv) - iw_rl2

            # compute wdzi = inv(Wi)*([-ei'*dt; -Pi*dw] - ri)
            wdzi = []
            tmp = matrix(0.0,(n,1))
            for i in range(k):
                blas.gemv(P[i],dwdb[:n],tmp, alpha = -1.0, beta = 0.0) 
                tmp1 = matrix([-dt[i],tmp])
                blas.axpy(ri[i],tmp1,alpha = -1.0)
                qscal(tmp1,W['beta'][i],W['v'][i],inv=True)
                wdzi.append(tmp1)

            # solution
            x[:n] = dwdb[:n]
            x[n:n+k] = dt
            x[n+k] = dwdb[-1]
            x[n+k+1:] = dv
            z[:m] = wdz1 
            z[m:2*m] = wdz2
            for i in range(k):
                z[2*m+i*(n+1):2*m+(i+1)*(n+1)] = wdzi[i]

        return f
Esempio n. 24
0
def kernel_matrix(X,
                  kernel,
                  sigma=1.0,
                  theta=1.0,
                  degree=1,
                  V=None,
                  width=None):
    """
    Computes the kernel matrix or a partial kernel matrix.

    Input arguments.

        X is an N x n matrix.

        kernel is a string with values 'linear', 'rfb', 'poly', or 'tanh'.
        'linear': k(u,v) = u'*v/sigma.
        'rbf':    k(u,v) = exp(-||u - v||^2 / (2*sigma)).
        'poly':   k(u,v) = (u'*v/sigma)**degree.
        'tanh':   k(u,v) = tanh(u'*v/sigma - theta).        kernel is a

        sigma and theta are positive numbers.

        degree is a positive integer.

        V is an N x N sparse matrix (default is None).

        width is a positive integer (default is None).

    Output.

        Q, an N x N matrix or sparse matrix.
        If V is a sparse matrix, a partial kernel matrix with the sparsity
        pattern V is returned.
        If width is specified and V = 'band', a partial kernel matrix
        with band sparsity is returned (width is the half-bandwidth).

        a, an N x 1 matrix with the products <xi,xi>/sigma.

    """
    N, n = X.size

    #### dense (full) kernel matrix
    if V is None:
        if verbose: print("building kernel matrix ..")

        # Qij = xi'*xj / sigma
        Q = matrix(0.0, (N, N))
        blas.syrk(X, Q, alpha=1.0 / sigma)
        a = Q[::N + 1]  # ai = ||xi||**2 / sigma

        if kernel == 'linear':
            pass

        elif kernel == 'rbf':
            # Qij := Qij - 0.5 * ( ai + aj )
            #      = -||xi - xj||^2 / (2*sigma)
            ones = matrix(1.0, (N, 1))
            blas.syr2(a, ones, Q, alpha=-0.5)

            Q = exp(Q)

        elif kernel == 'tanh':
            Q = exp(Q - theta)
            Q = div(Q - Q**-1, Q + Q**-1)

        elif kernel == 'poly':
            Q = Q**degree

        else:
            raise ValueError('invalid kernel type')

    #### general sparse partial kernel matrix
    elif type(V) is cvxopt.base.spmatrix:

        if verbose: print("building projected kernel matrix ...")
        Q = +V
        base.syrk(X, Q, partial=True, alpha=1.0 / sigma)

        # ai = ||xi||**2 / sigma
        a = matrix(Q[::N + 1], (N, 1))

        if kernel == 'linear':
            pass

        elif kernel == 'rbf':

            ones = matrix(1.0, (N, 1))

            # Qij := Qij - 0.5 * ( ai + aj )
            #      = -||xi - xj||^2 / (2*sigma)
            p = chompack.maxcardsearch(V)
            symb = chompack.symbolic(Q, p)
            Qc = chompack.cspmatrix(symb) + Q
            chompack.syr2(Qc, a, ones, alpha=-0.5)
            Q = Qc.spmatrix(reordered=False)
            Q.V = exp(Q.V)

        elif kernel == 'tanh':

            v = +Q.V
            v = exp(v - theta)
            v = div(v - v**-1, v + v**-1)
            Q.V = v

        elif kernel == 'poly':

            Q.V = Q.V**degree

        else:
            raise ValueError('invalid kernel type')

    #### banded partial kernel matrix
    elif V == 'band' and width is not None:

        # Lower triangular part of band matrix with bandwidth 2*w+1.
        if verbose: print("building projected kernel matrix ...")
        I = [i for k in range(N) for i in range(k, min(width + k + 1, N))]
        J = [k for k in range(N) for i in range(min(width + 1, N - k))]
        V = matrix(0.0, (len(I), 1))
        oy = 0
        for k in range(N):  # V[:,k] = Xtrain[k:k+w, :] * Xtrain[k,:].T
            m = min(width + 1, N - k)
            blas.gemv(X,
                      X,
                      V,
                      m=m,
                      ldA=N,
                      incx=N,
                      offsetA=k,
                      offsetx=k,
                      offsety=oy)
            oy += m
        blas.scal(1.0 / sigma, V)

        # ai = ||xi||**2 / sigma
        a = matrix(V[[i for i in range(len(I)) if I[i] == J[i]]], (N, 1))

        if kernel == 'linear':

            Q = spmatrix(V, I, J, (N, N))

        elif kernel == 'rbf':

            Q = spmatrix(V, I, J, (N, N))

            ones = matrix(1.0, (N, 1))

            # Qij := Qij - 0.5 * ( ai + aj )
            #      = -||xi - xj||^2 / (2*sigma)
            symb = chompack.symbolic(Q)
            Qc = chompack.cspmatrix(symb) + Q
            chompack.syr2(Qc, a, ones, alpha=-0.5)
            Q = Qc.spmatrix(reordered=False)
            Q.V = exp(Q.V)

        elif kernel == 'tanh':

            V = exp(V - theta)
            V = div(V - V**-1, V + V**-1)
            Q = spmatrix(V, I, J, (N, N))

        elif kernel == 'poly':

            Q = spmatrix(V**degree, I, J, (N, N))

        else:
            raise ValueError('invalid kernel type')
    else:
        raise TypeError('invalid type V')

    return Q, a
Esempio n. 25
0
def robsvm(X, d, gamma, P, e):
    """
    Solves the following robust SVM training problem:

       minimize    (1/2) w'*w + gamma*sum(v)
       subject to  diag(d)*(X*w + b*1) >= 1 - v + E*u
                   || S_j*w ||_2 <= u_j,  j = 1...t
                   v >= 0

    The variables are w, b, v, and u. The matrix E is a selector
    matrix with zeros and one '1' per row.  E_ij = 1 means that the
    i'th training vector is associated with the j'th uncertainty
    ellipsoid.

    A custom KKT solver that exploits low-rank structure is used, and
    a positive definite system of equations of order n is
    formed and solved at each iteration.

    ARGUMENTS

    X             m-by-n matrix with training vectors as rows

    d             m-vector with training labels (-1,+1)

    P             list of t symmetric matrices of order n

    e             m-vector where e[i] is the index of the uncertainty
                  ellipsoid associated with the i'th training vector

    RETURNS

    w        n-vector

    b        scalar

    u        t-vector

    v        m-vector

    iters    number of interior-point iterations

    """

    m,n = X.size
    assert type(P) is list, "P must be a list of t symmtric positive definite matrices of order n."
    k = len(P)
    if k > 0:
        assert e.size == (m,1), "e must be an m-vector."
        assert max(e) < k and min(e) >= 0, "e[i] must be in {0,1,...,k-1}."

    E = spmatrix(1.,e,range(m),(k,m)).T
    d = matrix(d,tc='d')
    q = matrix(0.0, (n+k+1+m,1))
    q[n+k+1:] = gamma
    h = matrix(0.0,(2*m+k*(n+1),1))
    h[:m] = -1.0

    # linear operators Q and G
    def Q(x, y, alpha = 1.0, beta = 0.0, trans = 'N'):
        y[:n] = alpha * x[:n] + beta * y[:n]

    def G(x, y, alpha = 1.0, beta = 0.0, trans = 'N'):
        """
        Implements the linear operator

               [ -DX    E   -d   -I ]  
               [  0     0    0   -I ]  
               [  0   -e_1'  0    0 ]
          G =  [ -P_1'  0    0    0 ]     
               [  .     .    .    . ]    
               [  0   -e_k'  0    0 ]        
               [ -P_k'  0    0    0 ]       

        and its adjoint G'.

        """
        if trans == 'N':
            tmp = +y[:m]
            # y[:m] = alpha*(-DXw + Et - d*b - v) + beta*y[:m]
            base.gemv(E, x[n:n+k], tmp, alpha = alpha, beta = beta)
            blas.axpy(x[n+k+1:], tmp, alpha = -alpha)
            blas.axpy(d, tmp, alpha = -alpha*x[n+k])
            y[:m] = tmp

            base.gemv(X, x[:n], tmp, alpha = alpha, beta = 0.0)
            tmp = mul(d,tmp)
            y[:m] -= tmp
            
            # y[m:2*m] = -v
            y[m:2*m] = -alpha * x[n+k+1:] + beta * y[m:2*m]

            # SOC 1,...,k
            for i in range(k):
                l = 2*m+i*(n+1)
                y[l] = -alpha * x[n+i] + beta * y[l]
                y[l+1:l+1+n] = -alpha * P[i] * x[:n] + beta * y[l+1:l+1+n];

        else:
            tmp1 = mul(d,x[:m])
            tmp2 = y[:n]
            blas.gemv(X, tmp1, tmp2, trans = 'T', alpha = -alpha, beta = beta)
            for i in range(k):
                l = 2*m+1+i*(n+1)
                blas.gemv(P[i], x[l:l+n], tmp2, trans = 'T', alpha = -alpha, beta = 1.0)
            y[:n] = tmp2

            tmp2 = y[n:n+k]
            base.gemv(E, x[:m], tmp2, trans = 'T', alpha = alpha, beta = beta)
            blas.axpy(x[2*m:2*m+k*(1+n):n+1], tmp2, alpha = -alpha)
            y[n:n+k] = tmp2

            y[n+k] = -alpha * blas.dot(d,x[:m]) + beta * y[n+k]
            y[n+k+1:] = -alpha * (x[:m] + x[m:2*m]) + beta * y[n+k+1:]

    # precompute products Pi'*Pi
    Pt = []
    for p in P:
        y = matrix(0.0, (n,n))
        blas.syrk(p, y, trans = 'T')
        Pt.append(y)

    # scaled hyperbolic Householder transformations
    def qscal(u, beta, v, inv = False):
        """
        Transforms the vector u as
           u := beta * (2*v*v' - J) * u
        if 'inv' is False and as
           u := (1/beta) * (2*J*v*v'*J - J) * u
        if 'inv' is True.
        """
        if not inv:
            tmp = blas.dot(u,v)
            u[0] *= -1
            u += 2 * v * tmp
            u *= beta
        else:
            u[0] *= -1.0
            tmp = blas.dot(v,u)
            u[0] -= 2*v[0] * tmp 
            u[1:] += 2*v[1:] * tmp
            u /= beta

    # custom KKT solver
    def F(W): 
        """
        Custom solver for the system

        [  It  0   0    Xt'     0     At1' ...  Atk' ][ dwt  ]   [ rwt ]
        [  0   0   0    -d'     0      0   ...   0   ][ db   ]   [ rb  ]
        [  0   0   0    -I     -I      0   ...   0   ][ dv   ]   [ rv  ]
        [  Xt -d  -I  -Wl1^-2                        ][ dzl1 ]   [ rl1 ]
        [  0   0  -I         -Wl2^-2                 ][ dzl2 ] = [ rl2 ]
        [ At1  0   0                -W1^-2           ][ dz1  ]   [ r1  ] 
        [  |   |   |                       .         ][  |   ]   [  |  ]
        [ Atk  0   0                          -Wk^-2 ][ dzk  ]   [ rk  ]

        where

        It = [ I 0 ]  Xt = [ -D*X E ]  Ati = [ 0   -e_i' ]  
             [ 0 0 ]                         [ -Pi   0   ] 

        dwt = [ dw ]  rwt = [ rw ]
              [ dt ]        [ rt ].

        """

        # scalings and 'intermediate' vectors
        # db = inv(Wl1)^2 + inv(Wl2)^2
        db = W['di'][:m]**2 + W['di'][m:2*m]**2
        dbi = div(1.0,db)
        
        # dt = I - inv(Wl1)*Dbi*inv(Wl1)
        dt = 1.0 - mul(W['di'][:m]**2,dbi)
        dtsqrt = sqrt(dt)

        # lam = Dt*inv(Wl1)*d
        lam = mul(dt,mul(W['di'][:m],d))

        # lt = E'*inv(Wl1)*lam
        lt = matrix(0.0,(k,1))
        base.gemv(E, mul(W['di'][:m],lam), lt, trans = 'T')

        # Xs = sqrt(Dt)*inv(Wl1)*X
        tmp = mul(dtsqrt,W['di'][:m])
        Xs = spmatrix(tmp,range(m),range(m))*X

        # Es = D*sqrt(Dt)*inv(Wl1)*E
        Es = spmatrix(mul(d,tmp),range(m),range(m))*E

        # form Ab = I + sum((1/bi)^2*(Pi'*Pi + 4*(v'*v + 1)*Pi'*y*y'*Pi)) + Xs'*Xs
        #  and Bb = -sum((1/bi)^2*(4*ui*v'*v*Pi'*y*ei')) - Xs'*Es
        #  and D2 = Es'*Es + sum((1/bi)^2*(1+4*ui^2*(v'*v - 1))
        Ab = matrix(0.0,(n,n))
        Ab[::n+1] = 1.0
        base.syrk(Xs,Ab,trans = 'T', beta = 1.0)
        Bb = matrix(0.0,(n,k))
        Bb = -Xs.T*Es # inefficient!?
        D2 = spmatrix(0.0,range(k),range(k))
        base.syrk(Es,D2,trans = 'T', partial = True)
        d2 = +D2.V
        del D2
        py = matrix(0.0,(n,1))
        for i in range(k):
            binvsq = (1.0/W['beta'][i])**2
            Ab += binvsq*Pt[i]
            dvv = blas.dot(W['v'][i],W['v'][i])
            blas.gemv(P[i], W['v'][i][1:], py, trans = 'T', alpha = 1.0, beta = 0.0)
            blas.syrk(py, Ab, alpha = 4*binvsq*(dvv+1), beta = 1.0)
            Bb[:,i] -= 4*binvsq*W['v'][i][0]*dvv*py
            d2[i] += binvsq*(1+4*(W['v'][i][0]**2)*(dvv-1))
        
        d2i = div(1.0,d2)
        d2isqrt = sqrt(d2i)

        # compute a = alpha - lam'*inv(Wl1)*E*inv(D2)*E'*inv(Wl1)*lam
        alpha = blas.dot(lam,mul(W['di'][:m],d))
        tmp = matrix(0.0,(k,1))
        base.gemv(E,mul(W['di'][:m],lam), tmp, trans = 'T')
        tmp = mul(tmp, d2isqrt) #tmp = inv(D2)^(1/2)*E'*inv(Wl1)*lam
        a = alpha - blas.dot(tmp,tmp)

        # compute M12 = X'*D*inv(Wl1)*lam + Bb*inv(D2)*E'*inv(Wl1)*lam
        tmp = mul(tmp, d2isqrt)
        M12 = matrix(0.0,(n,1))
        blas.gemv(Bb,tmp,M12, alpha = 1.0)
        tmp = mul(d,mul(W['di'][:m],lam))
        blas.gemv(X,tmp,M12, trans = 'T', alpha = 1.0, beta = 1.0)

        # form and factor M
        sBb = Bb * spmatrix(d2isqrt,range(k), range(k)) 
        base.syrk(sBb, Ab, alpha = -1.0, beta = 1.0)
        M = matrix([[Ab, M12.T],[M12, a]])
        lapack.potrf(M)
        
        def f(x,y,z):
            
            # residuals
            rwt = x[:n+k]
            rb = x[n+k]
            rv = x[n+k+1:n+k+1+m]
            iw_rl1 = mul(W['di'][:m],z[:m])
            iw_rl2 = mul(W['di'][m:2*m],z[m:2*m])
            ri = [z[2*m+i*(n+1):2*m+(i+1)*(n+1)] for i in range(k)]
            
            # compute 'derived' residuals 
            # rbwt = rwt + sum(Ai'*inv(Wi)^2*ri) + [-X'*D; E']*inv(Wl1)^2*rl1
            rbwt = +rwt
            for i in range(k):
                tmp = +ri[i]
                qscal(tmp,W['beta'][i],W['v'][i],inv=True)
                qscal(tmp,W['beta'][i],W['v'][i],inv=True)
                rbwt[n+i] -= tmp[0]
                blas.gemv(P[i], tmp[1:], rbwt, trans = 'T', alpha = -1.0, beta = 1.0)
            tmp = mul(W['di'][:m],iw_rl1)
            tmp2 = matrix(0.0,(k,1))
            base.gemv(E,tmp,tmp2,trans='T')
            rbwt[n:] += tmp2
            tmp = mul(d,tmp) # tmp = D*inv(Wl1)^2*rl1
            blas.gemv(X,tmp,rbwt,trans='T', alpha = -1.0, beta = 1.0)
            
            # rbb = rb - d'*inv(Wl1)^2*rl1
            rbb = rb - sum(tmp)

            # rbv = rv - inv(Wl2)*rl2 - inv(Wl1)^2*rl1
            rbv = rv - mul(W['di'][m:2*m],iw_rl2) - mul(W['di'][:m],iw_rl1) 
            
            # [rtw;rtt] = rbwt + [-X'*D; E']*inv(Wl1)^2*inv(Db)*rbv 
            tmp = mul(W['di'][:m]**2, mul(dbi,rbv))
            rtt = +rbwt[n:] 
            base.gemv(E, tmp, rtt, trans = 'T', alpha = 1.0, beta = 1.0)
            rtw = +rbwt[:n]
            tmp = mul(d,tmp)
            blas.gemv(X, tmp, rtw, trans = 'T', alpha = -1.0, beta = 1.0)

            # rtb = rbb - d'*inv(Wl1)^2*inv(Db)*rbv
            rtb = rbb - sum(tmp)
            
            # solve M*[dw;db] = [rtw - Bb*inv(D2)*rtt; rtb + lt'*inv(D2)*rtt]
            tmp = mul(d2i,rtt)
            tmp2 = matrix(0.0,(n,1))
            blas.gemv(Bb,tmp,tmp2)
            dwdb = matrix([rtw - tmp2,rtb + blas.dot(mul(d2i,lt),rtt)]) 
            lapack.potrs(M,dwdb)

            # compute dt = inv(D2)*(rtt - Bb'*dw + lt*db)
            tmp2 = matrix(0.0,(k,1))
            blas.gemv(Bb, dwdb[:n], tmp2, trans='T')
            dt = mul(d2i, rtt - tmp2 + lt*dwdb[-1])

            # compute dv = inv(Db)*(rbv + inv(Wl1)^2*(E*dt - D*X*dw - d*db))
            dv = matrix(0.0,(m,1))
            blas.gemv(X,dwdb[:n],dv,alpha = -1.0)
            dv = mul(d,dv) - d*dwdb[-1]
            base.gemv(E, dt, dv, beta = 1.0)
            tmp = +dv  # tmp = E*dt - D*X*dw - d*db
            dv = mul(dbi, rbv + mul(W['di'][:m]**2,dv))

            # compute wdz1 = inv(Wl1)*(E*dt - D*X*dw - d*db - dv - rl1)
            wdz1 = mul(W['di'][:m], tmp - dv) - iw_rl1

            # compute wdz2 = - inv(Wl2)*(dv + rl2)
            wdz2 = - mul(W['di'][m:2*m],dv) - iw_rl2

            # compute wdzi = inv(Wi)*([-ei'*dt; -Pi*dw] - ri)
            wdzi = []
            tmp = matrix(0.0,(n,1))
            for i in range(k):
                blas.gemv(P[i],dwdb[:n],tmp, alpha = -1.0, beta = 0.0) 
                tmp1 = matrix([-dt[i],tmp])
                blas.axpy(ri[i],tmp1,alpha = -1.0)
                qscal(tmp1,W['beta'][i],W['v'][i],inv=True)
                wdzi.append(tmp1)

            # solution
            x[:n] = dwdb[:n]
            x[n:n+k] = dt
            x[n+k] = dwdb[-1]
            x[n+k+1:] = dv
            z[:m] = wdz1 
            z[m:2*m] = wdz2
            for i in range(k):
                z[2*m+i*(n+1):2*m+(i+1)*(n+1)] = wdzi[i]

        return f

    # solve cone QP and return solution
    sol = solvers.coneqp(Q, q, G, h, dims = {'l':2*m,'q':[n+1 for i in range(k)],'s':[]}, kktsolver = F)
    return sol['x'][:n], sol['x'][n+k], sol['x'][n:n+k], sol['x'][n+k+1:], sol['iterations']
Esempio n. 26
0
    def factor(W, H=None, Df=None):

        if F['firstcall']:
            if type(G) is matrix:
                F['Gs'] = matrix(0.0, G.size)
            else:
                F['Gs'] = spmatrix(0.0, G.I, G.J, G.size)
            if mnl:
                if type(Df) is matrix:
                    F['Dfs'] = matrix(0.0, Df.size)
                else:
                    F['Dfs'] = spmatrix(0.0, Df.I, Df.J, Df.size)
            if (mnl and type(Df) is matrix) or type(G) is matrix or \
                    type(H) is matrix:
                F['S'] = matrix(0.0, (n, n))
                F['K'] = matrix(0.0, (p, p))
            else:
                F['S'] = spmatrix([], [], [], (n, n), 'd')
                F['Sf'] = None
                if type(A) is matrix:
                    F['K'] = matrix(0.0, (p, p))
                else:
                    F['K'] = spmatrix([], [], [], (p, p), 'd')

        # Dfs = Wnl^{-1} * Df
        if mnl:
            base.gemm(spmatrix(W['dnli'], list(range(mnl)),
                               list(range(mnl))), Df, F['Dfs'], partial=True)

        # Gs = Wl^{-1} * G.
        base.gemm(spmatrix(W['di'], list(range(ml)), list(range(ml))),
                  G, F['Gs'], partial=True)

        if F['firstcall']:
            base.syrk(F['Gs'], F['S'], trans='T')
            if mnl:
                base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0)
            if H is not None:
                F['S'] += H
            try:
                if type(F['S']) is matrix:
                    lapack.potrf(F['S'])
                else:
                    F['Sf'] = cholmod.symbolic(F['S'])
                    cholmod.numeric(F['S'], F['Sf'])
            except ArithmeticError:
                F['singular'] = True
                if type(A) is matrix and type(F['S']) is spmatrix:
                    F['S'] = matrix(0.0, (n, n))
                base.syrk(F['Gs'], F['S'], trans='T')
                if mnl:
                    base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0)
                base.syrk(A, F['S'], trans='T', beta=1.0)
                if H is not None:
                    F['S'] += H
                if type(F['S']) is matrix:
                    lapack.potrf(F['S'])
                else:
                    F['Sf'] = cholmod.symbolic(F['S'])
                    cholmod.numeric(F['S'], F['Sf'])
            F['firstcall'] = False

        else:
            base.syrk(F['Gs'], F['S'], trans='T', partial=True)
            if mnl:
                base.syrk(F['Dfs'], F['S'], trans='T', beta=1.0,
                          partial=True)
            if H is not None:
                F['S'] += H
            if F['singular']:
                base.syrk(A, F['S'], trans='T', beta=1.0, partial=True)
            if type(F['S']) is matrix:
                lapack.potrf(F['S'])
            else:
                cholmod.numeric(F['S'], F['Sf'])

        if type(F['S']) is matrix:
            # Asct := L^{-1}*A'.  Factor K = Asct'*Asct.
            if type(A) is matrix:
                Asct = A.T
            else:
                Asct = matrix(A.T)
            blas.trsm(F['S'], Asct)
            blas.syrk(Asct, F['K'], trans='T')
            lapack.potrf(F['K'])

        else:
            # Asct := L^{-1}*P*A'.  Factor K = Asct'*Asct.
            if type(A) is matrix:
                Asct = A.T
                cholmod.solve(F['Sf'], Asct, sys=7)
                cholmod.solve(F['Sf'], Asct, sys=4)
                blas.syrk(Asct, F['K'], trans='T')
                lapack.potrf(F['K'])
            else:
                Asct = cholmod.spsolve(F['Sf'], A.T, sys=7)
                Asct = cholmod.spsolve(F['Sf'], Asct, sys=4)
                base.syrk(Asct, F['K'], trans='T')
                Kf = cholmod.symbolic(F['K'])
                cholmod.numeric(F['K'], Kf)

        def solve(x, y, z):

            # Solve
            #
            #     [ H          A'  GG'*W^{-1} ]   [ ux   ]   [ bx        ]
            #     [ A          0   0          ] * [ uy   ] = [ by        ]
            #     [ W^{-T}*GG  0   -I         ]   [ W*uz ]   [ W^{-T}*bz ]
            #
            # and return ux, uy, W*uz.
            #
            # If not F['singular']:
            #
            #     K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz ) - by
            #     S*ux = bx + GG'*W^{-1}*W^{-T}*bz - A'*uy
            #     W*uz = W^{-T} * ( GG*ux - bz ).
            #
            # If F['singular']:
            #
            #     K*uy = A * S^{-1} * ( bx + GG'*W^{-1}*W^{-T}*bz + A'*by )
            #            - by
            #     S*ux = bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y.
            #     W*uz = W^{-T} * ( GG*ux - bz ).

            # z := W^{-1} * z = W^{-1} * bz
            scale(z, W, trans='T', inverse='I')

            # If not F['singular']:
            #     x := L^{-1} * P * (x + GGs'*z)
            #        = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz)
            #
            # If F['singular']:
            #     x := L^{-1} * P * (x + GGs'*z + A'*y))
            #        = L^{-1} * P * (x + GG'*W^{-1}*W^{-T}*bz + A'*y)

            if mnl:
                base.gemv(F['Dfs'], z, x, trans='T', beta=1.0)
            base.gemv(F['Gs'], z, x, offsetx=mnl, trans='T',
                      beta=1.0)
            if F['singular']:
                base.gemv(A, y, x, trans='T', beta=1.0)
            if type(F['S']) is matrix:
                blas.trsv(F['S'], x)
            else:
                cholmod.solve(F['Sf'], x, sys=7)
                cholmod.solve(F['Sf'], x, sys=4)

            # y := K^{-1} * (Asc*x - y)
            #    = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz) - by)
            #      (if not F['singular'])
            #    = K^{-1} * (A * S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz +
            #      A'*by) - by)
            #      (if F['singular']).

            base.gemv(Asct, x, y, trans='T', beta=-1.0)
            if type(F['K']) is matrix:
                lapack.potrs(F['K'], y)
            else:
                cholmod.solve(Kf, y)

            # x := P' * L^{-T} * (x - Asc'*y)
            #    = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz - A'*y)
            #      (if not F['singular'])
            #    = S^{-1} * (bx + GG'*W^{-1}*W^{-T}*bz + A'*by - A'*y)
            #      (if F['singular'])

            base.gemv(Asct, y, x, alpha=-1.0, beta=1.0)
            if type(F['S']) is matrix:
                blas.trsv(F['S'], x, trans='T')
            else:
                cholmod.solve(F['Sf'], x, sys=5)
                cholmod.solve(F['Sf'], x, sys=8)

            # W*z := GGs*x - z = W^{-T} * (GG*x - bz)
            if mnl:
                base.gemv(F['Dfs'], x, z, beta=-1.0)
            base.gemv(F['Gs'], x, z, beta=-1.0, offsety=mnl)

        return solve
Esempio n. 27
0
def mcsvm(X, labels, gamma, kernel='linear', sigma=1.0, degree=1):
    """
    Solves the Crammer and Singer multiclass SVM training problem

        maximize    -(1/2) * tr(U' * Q * U) + tr(E' * U)  
        subject to  U <= gamma * E
                    U * 1_m = 0.

    The variable is an (N x m)-matrix U if N is the number of training
    examples and m the number of classes. 

    Q is a positive definite matrix of order N with Q[i,j] = K(xi, xj) 
    where K is a kernel function and xi is the ith row of X.

    The matrix E is an N x m matrix with E[i,j] = 1 if labels[i] = j
    and E[i,j] = 0 otherwise.

    Input arguments.

        X is a N x n matrix.  The rows are the training vectors.

        labels is a list of integers of length N with values 0, ..., m-1.
        labels[i] is the class of training example i.

        gamma is a positive parameter.

        kernel is a string with values 'linear' or 'poly'. 
        'linear':  K(u,v) = u'*v.
        'poly':    K(u,v) = (u'*v / sigma)**degree.

        sigma is a positive number.

        degree is a positive integer.


    Output.

        Returns a function classifier().  If Y is M x n then classifier(Y)
        returns a list with as its kth element

            argmax { j = 0, ..., m-1 | sum_{i=1}^N U[i,j] * K(xi, yk) }

        where yk' = Y[k, :], xi' = X[i, :], and U is the optimal solution
        of the QP.
    """

    N, n = X.size

    m = max(labels) + 1
    E = matrix(0.0, (N, m))
    E[matrix(range(N)) + N * matrix(labels)] = 1.0

    def G(x, y, alpha=1.0, beta=0.0, trans='N'):
        """
        If trans is 'N', x is an N x m matrix, and y is an N*m-vector.

            y := alpha * x[:] + beta * y.

        If trans is 'T', x is an N*m vector, and y is an N x m matrix.

            y[:] := alpha * x + beta * y[:].

        """

        blas.scal(beta, y)
        blas.axpy(x, y, alpha)

    h = matrix(gamma * E, (N * m, 1))

    ones = matrix(1.0, (m, 1))

    def A(x, y, alpha=1.0, beta=0.0, trans='N'):
        """
        If trans is 'N', x is an N x m matrix and y an N-vector.

            y := alpha * x * 1_m + beta y.

        If trans is 'T', x is an N vector and y an N x m matrix.

            y := alpha * x * 1_m' + beta y.
        """

        if trans == 'N':
            blas.gemv(x, ones, y, alpha=alpha, beta=beta)

        else:
            blas.scal(beta, y)
            blas.ger(x, ones, y, alpha=alpha)

    b = matrix(0.0, (N, 1))

    if kernel == 'linear' and N > n:

        def P(x, y, alpha=1.0, beta=0.0):
            """
            x and y are N x m matrices.   

                y =  alpha * X * X' * x + beta * y.

            """

            z = matrix(0.0, (n, m))
            blas.gemm(X, x, z, transA='T')
            blas.gemm(X, z, y, alpha=alpha, beta=beta)

    else:

        if kernel == 'linear':
            # Q = X * X'
            Q = matrix(0.0, (N, N))
            blas.syrk(X, Q)

        elif kernel == 'poly':
            # Q = (X * X' / sigma) ** degree
            Q = matrix(0.0, (N, N))
            blas.syrk(X, Q, alpha=1.0 / sigma)
            Q = Q**degree

        else:
            raise ValueError("invalid kernel type")

        def P(x, y, alpha=1.0, beta=0.0):
            """
            x and y are N x m matrices.   

                y =  alpha * Q * x + beta * y.

            """

            blas.symm(Q, x, y, alpha=alpha, beta=beta)

    if kernel == 'linear' and N > n:  # add separate code for n <= N <= m*n

        H = [matrix(0.0, (n, n)) for k in range(m)]
        S = matrix(0.0, (m * n, m * n))
        Xs = matrix(0.0, (N, n))
        wnm = matrix(0.0, (m * n, 1))
        wN = matrix(0.0, (N, 1))
        D = matrix(0.0, (N, 1))

        def kkt(W):
            """
            KKT solver for

                X*X' * ux  + uy * 1_m' + mat(uz) = bx
                                       ux * 1_m  = by
                            ux - d.^2 .* mat(uz) = mat(bz).

            ux and bx are N x m matrices.
            uy and by are N-vectors.
            uz and bz are N*m-vectors.  mat(uz) is the N x m matrix that 
                satisfies mat(uz)[:] = uz.
            d = mat(W['d']) a positive N x m matrix.

            If we eliminate uz from the last equation using 

                mat(uz) = (ux - mat(bz)) ./ d.^2
        
            we get two equations in ux, uy:

                X*X' * ux + ux ./ d.^2 + uy * 1_m' = bx + mat(bz) ./ d.^2
                                          ux * 1_m = by.

            From the 1st equation,

                uxk = (X*X' + Dk^-2)^-1 * (-uy + bxk + Dk^-2 * bzk)
                    = Dk * (I + Xk*Xk')^-1 * Dk * (-uy + bxk + Dk^-2 * bzk)

            for k = 1, ..., m, where Dk = diag(d[:,k]), Xk = Dk * X, 
            uxk is column k of ux, and bzk is column k of mat(bz).  

            We use the matrix inversion lemma

                ( I + Xk * Xk' )^-1 = I - Xk * (I + Xk' * Xk)^-1 * Xk'
                                    = I - Xk * Hk^-1 * Xk'
                                    = I - Xk * Lk^-T * Lk^-1 *  Xk'

            where Hk = I + Xk' * Xk = Lk * Lk' to write this as

                uxk = Dk * (I - Xk * Hk^-1 * Xk') * Dk *
                      (-uy + bxk + Dk^-2 * bzk)
                    = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) *
                      (-uy + bxk + Dk^-2 * bzk).

            Substituting this in the second equation gives an equation 
            for uy:

                sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2 ) * uy 
                    = -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) *
                      ( bxk + Dk^-2 * bzk ),

            i.e., with D = (sum_k Dk^2)^1/2,  Yk = D^-1 * Dk^2 * X * Lk^-T,

                D * ( I - sum_k Yk * Yk' ) * D * uy  
                    = -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                      ( bxk + Dk^-2 *bzk ).

            Another application of the matrix inversion lemma gives

                uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                     ( -by + sum_k ( Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2 ) *
                     ( bxk + Dk^-2 *bzk ) )

            with S = I - Y' * Y,  Y = [ Y1 ... Ym ].  


            Summary:

            1. Compute 

                   uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                        ( -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2)
                        * ( bxk + Dk^-2 *bzk ) )
 
            2. For k = 1, ..., m:

                   uxk = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                         (-uy + bxk + Dk^-2 * bzk)

            3. Solve for uz

                   d .* uz = ( ux - mat(bz) ) ./ d.
        
            Return ux, uy, d .* uz.

            """
            ###
            utime0, stime0 = cputime()
            ###

            d = matrix(W['d'], (N, m))
            dsq = matrix(W['d']**2, (N, m))

            # Factor the matrices
            #
            #     H[k] = I + Xk' * Xk
            #          = I + X' * Dk^2 * X.
            #
            # Dk = diag(d[:,k]).

            for k in range(m):

                # H[k] = I
                blas.scal(0.0, H[k])
                H[k][::n + 1] = 1.0

                # Xs = Dk * X
                #    = diag(d[:,k]]) * X
                blas.copy(X, Xs)
                for j in range(n):
                    blas.tbmv(d,
                              Xs,
                              n=N,
                              k=0,
                              ldA=1,
                              offsetA=k * N,
                              offsetx=j * N)

                # H[k] := H[k] + Xs' * Xs
                #       = I + Xk' * Xk
                blas.syrk(Xs, H[k], trans='T', beta=1.0)

                # Factorization H[k] = Lk * Lk'
                lapack.potrf(H[k])

###
            utime, stime = cputime()
            print("Factor Hk's: utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # diag(D) = ( sum_k d[:,k]**2 ) ** 1/2
            #         = ( sum_k Dk^2) ** 1/2.

            blas.gemv(dsq, ones, D)
            D[:] = sqrt(D)

            ###
            #            utime, stime = cputime()
            #            print("Compute D:  utime = %.2f, stime = %.2f" \
            #                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # S = I - Y'* Y is an m x m block matrix.
            # The i,j block of Y' * Y is
            #
            #     Yi' * Yj = Li^-1 * X' * Di^2 * D^-1 * Dj^2 * X * Lj^-T.
            #
            # We compute only the lower triangular blocks in Y'*Y.

            blas.scal(0.0, S)
            for i in range(m):
                for j in range(i + 1):

                    # Xs = Di * Dj * D^-1 * X
                    blas.copy(X, Xs)
                    blas.copy(d, wN, n=N, offsetx=i * N)
                    blas.tbmv(d, wN, n=N, k=0, ldA=1, offsetA=j * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)
                    for k in range(n):
                        blas.tbmv(wN, Xs, n=N, k=0, ldA=1, offsetx=k * N)

                    # block i, j of S is Xs' * Xs (as nonsymmetric matrix so we
                    # get the correct multiple after scaling with Li, Lj)
                    blas.gemm(Xs,
                              Xs,
                              S,
                              transA='T',
                              ldC=m * n,
                              offsetC=(j * n) * m * n + i * n)

###
            utime, stime = cputime()
            print("Form S:      utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            for i in range(m):

                # multiply block row i of S on the left with Li^-1
                blas.trsm(H[i],
                          S,
                          m=n,
                          n=(i + 1) * n,
                          ldB=m * n,
                          offsetB=i * n)

                # multiply block column i of S on the right with Li^-T
                blas.trsm(H[i],
                          S,
                          side='R',
                          transA='T',
                          m=(m - i) * n,
                          n=n,
                          ldB=m * n,
                          offsetB=i * n * (m * n + 1))

            blas.scal(-1.0, S)
            S[::(m * n + 1)] += 1.0

            ###
            utime, stime = cputime()
            print("Form S (2):  utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # S = L*L'
            lapack.potrf(S)

            ###
            utime, stime = cputime()
            print("Factor S:    utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()

            ###

            def f(x, y, z):
                """
                1. Compute 

                   uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                        ( -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2)
                        * ( bxk + Dk^-2 *bzk ) )
 
                2. For k = 1, ..., m:

                   uxk = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                         (-uy + bxk + Dk^-2 * bzk)

                3. Solve for uz

                   d .* uz = ( ux - mat(bz) ) ./ d.
        
                Return ux, uy, d .* uz.
                """

                ###
                utime0, stime0 = cputime()
                ###

                # xk := Dk^2 * xk + zk
                #     = Dk^2 * bxk + bzk
                blas.tbmv(dsq, x, n=N * m, k=0, ldA=1)
                blas.axpy(z, x)

                # y := -y + sum_k ( I - Dk^2 * X * Hk^-1 * X' ) * xk
                #    = -y + x*ones - sum_k Dk^2 * X * Hk^-1 * X' * xk

                # y := -y + x*ones
                blas.gemv(x, ones, y, alpha=1.0, beta=-1.0)

                # wnm = X' * x  (wnm interpreted as an n x m matrix)
                blas.gemm(X, x, wnm, m=n, k=N, n=m, transA='T', ldB=N, ldC=n)

                # wnm[:,k] = Hk \ wnm[:,k] (for wnm as an n x m matrix)
                for k in range(m):
                    lapack.potrs(H[k], wnm, offsetB=k * n)

                for k in range(m):

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=n * k)

                    # wN = Dk^2 * wN
                    blas.tbmv(dsq[:, k], wN, n=N, k=0, ldA=1)

                    # y := y - wN
                    blas.axpy(wN, y, -1.0)

                # y = D^-1 * (I + Y * S^-1 * Y') * D^-1 * y
                #
                # Y = [Y1 ... Ym ], Yk = D^-1 * Dk^2 * X * Lk^-T.

                # y := D^-1 * y
                blas.tbsv(D, y, n=N, k=0, ldA=1)

                # wnm =  Y' * y  (interpreted as an Nm vector)
                #     = [ L1^-1 * X' * D1^2 * D^-1 * y;
                #         L2^-1 * X' * D2^2 * D^-1 * y;
                #         ...
                #         Lm^-1 * X' * Dm^2 * D^-1 * y ]

                for k in range(m):

                    # wN = D^-1 * Dk^2 * y
                    blas.copy(y, wN)
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)

                    # wnm[:,k] = X' * wN
                    blas.gemv(X, wN, wnm, trans='T', offsety=k * n)

                    # wnm[:,k] = Lk^-1 * wnm[:,k]
                    blas.trsv(H[k], wnm, offsetx=k * n)

                # wnm := S^-1 * wnm  (an mn-vector)
                lapack.potrs(S, wnm)

                # y := y + Y * wnm
                #    = y + D^-1 * [ D1^2 * X * L1^-T ... D2^k * X * Lk^-T]
                #      * wnm

                for k in range(m):

                    # wnm[:,k] = Lk^-T * wnm[:,k]
                    blas.trsv(H[k], wnm, trans='T', offsetx=k * n)

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=k * n)

                    # wN = D^-1 * Dk^2 * wN
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)

                    # y += wN
                    blas.axpy(wN, y)

                # y := D^-1 *  y
                blas.tbsv(D, y, n=N, k=0, ldA=1)

                # For k = 1, ..., m:
                #
                # xk = (I - Dk^2 * X * Hk^-1 * X') * (-Dk^2 * y + xk)

                # x = x - [ D1^2 * y ... Dm^2 * y] (as an N x m matrix)
                for k in range(m):
                    blas.copy(y, wN)
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.axpy(wN, x, -1.0, offsety=k * N)

                # wnm  = X' * x (as an n x m matrix)
                blas.gemm(X, x, wnm, transA='T', m=n, n=m, k=N, ldB=N, ldC=n)

                # wnm[:,k] = Hk^-1 * wnm[:,k]
                for k in range(m):
                    lapack.potrs(H[k], wnm, offsetB=n * k)

                for k in range(m):

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=k * n)

                    # wN = Dk^2 * wN
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)

                    # x[:,k] := x[:,k] - wN
                    blas.axpy(wN, x, -1.0, n=N, offsety=k * N)

                # z := ( x - z ) ./ d
                blas.axpy(x, z, -1.0)
                blas.scal(-1.0, z)
                blas.tbsv(d, z, n=N * m, k=0, ldA=1)

                ###
                utime, stime = cputime()
                print("Solve:       utime = %.2f, stime = %.2f" \
                    %(utime-utime0, stime-stime0))


###

            return f

    else:

        H = [matrix(0.0, (N, N)) for k in range(m)]
        S = matrix(0.0, (N, N))

        def kkt(W):
            """
            KKT solver for

                Q * ux  + uy * 1_m' + mat(uz) = bx
                                    ux * 1_m  = by
                         ux - d.^2 .* mat(uz) = mat(bz).

            ux and bx are N x m matrices.
            uy and by are N-vectors.
            uz and bz are N*m-vectors.  mat(uz) is the N x m matrix that 
                satisfies mat(uz)[:] = uz.
            d = mat(W['d']) a positive N x m matrix.

            If we eliminate uz from the last equation using 

                mat(uz) = (ux - mat(bz)) ./ d.^2
        
            we get two equations in ux, uy:

                Q * ux + ux ./ d.^2 + uy * 1_m' = bx + mat(bz) ./ d.^2
                                       ux * 1_m = by.

            From the 1st equation 

                uxk = -(Q + Dk)^-1 * uy + (Q + Dk)^-1 * (bxk + Dk * bzk)

            where uxk is column k of ux, Dk = diag(d[:,k].^-2), and bzk is 
            column k of mat(bz).  Substituting this in the second equation
            gives an equation for uy.

            1. Solve for uy

                   sum_k (Q + Dk)^-1 * uy = 
                       sum_k (Q + Dk)^-1 * (bxk + Dk * bzk) - by.
 
            2. Solve for ux (column by column)

                   Q * ux + ux ./ d.^2 = bx + mat(bz) ./ d.^2 - uy * 1_m'.

            3. Solve for uz

                   mat(uz) = ( ux - mat(bz) ) ./ d.^2.
        
            Return ux, uy, d .* uz.
            """

            # D = d.^-2
            D = matrix(W['di']**2, (N, m))

            blas.scal(0.0, S)
            for k in range(m):

                # Hk := Q + Dk
                blas.copy(Q, H[k])
                H[k][::N + 1] += D[:, k]

                # Hk := Hk^-1
                #     = (Q + Dk)^-1
                lapack.potrf(H[k])
                lapack.potri(H[k])

                # S := S + Hk
                #    = S + (Q + Dk)^-1
                blas.axpy(H[k], S)

            # Factor S = sum_k (Q + Dk)^-1
            lapack.potrf(S)

            def f(x, y, z):

                # z := mat(z)
                #    = mat(bz)
                z.size = N, m

                # x := x + D .* z
                #    = bx + mat(bz) ./ d.^2
                x += mul(D, z)

                # y := y - sum_k (Q + Dk)^-1 * X[:,k]
                #    = by - sum_k (Q + Dk)^-1 * (bxk + Dk * bzk)
                for k in range(m):
                    blas.symv(H[k], x[:, k], y, alpha=-1.0, beta=1.0)

                # y := H^-1 * y
                #    = -uy
                lapack.potrs(S, y)

                # x[:,k] := H[k] * (x[:,k] + y)
                #         = (Q + Dk)^-1 * (bxk + bzk ./ d.^2 + y)
                #         = ux[:,k]
                w = matrix(0.0, (N, 1))
                for k in range(m):

                    # x[:,k] := x[:,k] + y
                    blas.axpy(y, x, offsety=N * k, n=N)

                    # w := H[k] * x[:,k]
                    #    = (Q + Dk)^-1 * (bxk + bzk ./ d.^2 + y)
                    blas.symv(H[k], x, w, offsetx=N * k)

                    # x[:,k] := w
                    #         = ux[:,k]
                    blas.copy(w, x, offsety=N * k)

                # y := -y
                #    = uy
                blas.scal(-1.0, y)

                # z := (x - z) ./ d
                blas.axpy(x, z, -1.0)
                blas.tbsv(W['d'], z, n=m * N, k=0, ldA=1)
                blas.scal(-1.0, z)
                z.size = N * m, 1

            return f

    utime0, stime0 = cputime()
    #    solvers.options['debug'] = True
    #    solvers.options['maxiters'] = 1
    solvers.options['refinement'] = 1
    sol = solvers.coneqp(P,
                         -E,
                         G,
                         h,
                         A=A,
                         b=b,
                         kktsolver=kkt,
                         xnewcopy=matrix,
                         xdot=blas.dot,
                         xaxpy=blas.axpy,
                         xscal=blas.scal)
    utime, stime = cputime()
    utime -= utime0
    stime -= stime0
    print("utime = %.2f, stime = %.2f" % (utime, stime))
    U = sol['x']

    if kernel == 'linear':

        # W = X' * U
        W = matrix(0.0, (n, m))
        blas.gemm(X, U, W, transA='T')

        def classifier(Y):
            # return [ argmax of Y[k,:] * W  for k in range(M) ]
            M = Y.size[0]
            S = Y * W
            c = []
            for i in range(M):
                a = zip(list(S[i, :]), range(m))
                a.sort(reverse=True)
                c += [a[0][1]]
            return c

    elif kernel == 'poly':

        def classifier(Y):
            M = Y.size[0]

            # K = Y * X' / sigma
            K = matrix(0.0, (M, N))
            blas.gemm(Y, X, K, transB='T', alpha=1.0 / sigma)

            S = K**degree * U

            c = []
            for i in range(M):
                a = zip(list(S[i, :]), range(m))
                a.sort(reverse=True)
                c += [a[0][1]]
            return c

    else:
        pass

    return classifier  #, utime, sol['iterations']
Esempio n. 28
0
        def kkt(W):
            """
            KKT solver for

                X*X' * ux  + uy * 1_m' + mat(uz) = bx
                                       ux * 1_m  = by
                            ux - d.^2 .* mat(uz) = mat(bz).

            ux and bx are N x m matrices.
            uy and by are N-vectors.
            uz and bz are N*m-vectors.  mat(uz) is the N x m matrix that 
                satisfies mat(uz)[:] = uz.
            d = mat(W['d']) a positive N x m matrix.

            If we eliminate uz from the last equation using 

                mat(uz) = (ux - mat(bz)) ./ d.^2
        
            we get two equations in ux, uy:

                X*X' * ux + ux ./ d.^2 + uy * 1_m' = bx + mat(bz) ./ d.^2
                                          ux * 1_m = by.

            From the 1st equation,

                uxk = (X*X' + Dk^-2)^-1 * (-uy + bxk + Dk^-2 * bzk)
                    = Dk * (I + Xk*Xk')^-1 * Dk * (-uy + bxk + Dk^-2 * bzk)

            for k = 1, ..., m, where Dk = diag(d[:,k]), Xk = Dk * X, 
            uxk is column k of ux, and bzk is column k of mat(bz).  

            We use the matrix inversion lemma

                ( I + Xk * Xk' )^-1 = I - Xk * (I + Xk' * Xk)^-1 * Xk'
                                    = I - Xk * Hk^-1 * Xk'
                                    = I - Xk * Lk^-T * Lk^-1 *  Xk'

            where Hk = I + Xk' * Xk = Lk * Lk' to write this as

                uxk = Dk * (I - Xk * Hk^-1 * Xk') * Dk *
                      (-uy + bxk + Dk^-2 * bzk)
                    = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) *
                      (-uy + bxk + Dk^-2 * bzk).

            Substituting this in the second equation gives an equation 
            for uy:

                sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2 ) * uy 
                    = -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) *
                      ( bxk + Dk^-2 * bzk ),

            i.e., with D = (sum_k Dk^2)^1/2,  Yk = D^-1 * Dk^2 * X * Lk^-T,

                D * ( I - sum_k Yk * Yk' ) * D * uy  
                    = -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                      ( bxk + Dk^-2 *bzk ).

            Another application of the matrix inversion lemma gives

                uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                     ( -by + sum_k ( Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2 ) *
                     ( bxk + Dk^-2 *bzk ) )

            with S = I - Y' * Y,  Y = [ Y1 ... Ym ].  


            Summary:

            1. Compute 

                   uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                        ( -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2)
                        * ( bxk + Dk^-2 *bzk ) )
 
            2. For k = 1, ..., m:

                   uxk = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                         (-uy + bxk + Dk^-2 * bzk)

            3. Solve for uz

                   d .* uz = ( ux - mat(bz) ) ./ d.
        
            Return ux, uy, d .* uz.

            """
            ###
            utime0, stime0 = cputime()
            ###

            d = matrix(W['d'], (N, m))
            dsq = matrix(W['d']**2, (N, m))

            # Factor the matrices
            #
            #     H[k] = I + Xk' * Xk
            #          = I + X' * Dk^2 * X.
            #
            # Dk = diag(d[:,k]).

            for k in range(m):

                # H[k] = I
                blas.scal(0.0, H[k])
                H[k][::n + 1] = 1.0

                # Xs = Dk * X
                #    = diag(d[:,k]]) * X
                blas.copy(X, Xs)
                for j in range(n):
                    blas.tbmv(d,
                              Xs,
                              n=N,
                              k=0,
                              ldA=1,
                              offsetA=k * N,
                              offsetx=j * N)

                # H[k] := H[k] + Xs' * Xs
                #       = I + Xk' * Xk
                blas.syrk(Xs, H[k], trans='T', beta=1.0)

                # Factorization H[k] = Lk * Lk'
                lapack.potrf(H[k])

###
            utime, stime = cputime()
            print("Factor Hk's: utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # diag(D) = ( sum_k d[:,k]**2 ) ** 1/2
            #         = ( sum_k Dk^2) ** 1/2.

            blas.gemv(dsq, ones, D)
            D[:] = sqrt(D)

            ###
            #            utime, stime = cputime()
            #            print("Compute D:  utime = %.2f, stime = %.2f" \
            #                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # S = I - Y'* Y is an m x m block matrix.
            # The i,j block of Y' * Y is
            #
            #     Yi' * Yj = Li^-1 * X' * Di^2 * D^-1 * Dj^2 * X * Lj^-T.
            #
            # We compute only the lower triangular blocks in Y'*Y.

            blas.scal(0.0, S)
            for i in range(m):
                for j in range(i + 1):

                    # Xs = Di * Dj * D^-1 * X
                    blas.copy(X, Xs)
                    blas.copy(d, wN, n=N, offsetx=i * N)
                    blas.tbmv(d, wN, n=N, k=0, ldA=1, offsetA=j * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)
                    for k in range(n):
                        blas.tbmv(wN, Xs, n=N, k=0, ldA=1, offsetx=k * N)

                    # block i, j of S is Xs' * Xs (as nonsymmetric matrix so we
                    # get the correct multiple after scaling with Li, Lj)
                    blas.gemm(Xs,
                              Xs,
                              S,
                              transA='T',
                              ldC=m * n,
                              offsetC=(j * n) * m * n + i * n)

###
            utime, stime = cputime()
            print("Form S:      utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            for i in range(m):

                # multiply block row i of S on the left with Li^-1
                blas.trsm(H[i],
                          S,
                          m=n,
                          n=(i + 1) * n,
                          ldB=m * n,
                          offsetB=i * n)

                # multiply block column i of S on the right with Li^-T
                blas.trsm(H[i],
                          S,
                          side='R',
                          transA='T',
                          m=(m - i) * n,
                          n=n,
                          ldB=m * n,
                          offsetB=i * n * (m * n + 1))

            blas.scal(-1.0, S)
            S[::(m * n + 1)] += 1.0

            ###
            utime, stime = cputime()
            print("Form S (2):  utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()
            ###

            # S = L*L'
            lapack.potrf(S)

            ###
            utime, stime = cputime()
            print("Factor S:    utime = %.2f, stime = %.2f" \
                %(utime-utime0, stime-stime0))
            utime0, stime0 = cputime()

            ###

            def f(x, y, z):
                """
                1. Compute 

                   uy = D^-1 * (I + Y * S^-1 * Y') * D^-1 * 
                        ( -by + sum_k (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2)
                        * ( bxk + Dk^-2 *bzk ) )
 
                2. For k = 1, ..., m:

                   uxk = (Dk^2 - Dk^2 * X * Hk^-1 * X' * Dk^2) * 
                         (-uy + bxk + Dk^-2 * bzk)

                3. Solve for uz

                   d .* uz = ( ux - mat(bz) ) ./ d.
        
                Return ux, uy, d .* uz.
                """

                ###
                utime0, stime0 = cputime()
                ###

                # xk := Dk^2 * xk + zk
                #     = Dk^2 * bxk + bzk
                blas.tbmv(dsq, x, n=N * m, k=0, ldA=1)
                blas.axpy(z, x)

                # y := -y + sum_k ( I - Dk^2 * X * Hk^-1 * X' ) * xk
                #    = -y + x*ones - sum_k Dk^2 * X * Hk^-1 * X' * xk

                # y := -y + x*ones
                blas.gemv(x, ones, y, alpha=1.0, beta=-1.0)

                # wnm = X' * x  (wnm interpreted as an n x m matrix)
                blas.gemm(X, x, wnm, m=n, k=N, n=m, transA='T', ldB=N, ldC=n)

                # wnm[:,k] = Hk \ wnm[:,k] (for wnm as an n x m matrix)
                for k in range(m):
                    lapack.potrs(H[k], wnm, offsetB=k * n)

                for k in range(m):

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=n * k)

                    # wN = Dk^2 * wN
                    blas.tbmv(dsq[:, k], wN, n=N, k=0, ldA=1)

                    # y := y - wN
                    blas.axpy(wN, y, -1.0)

                # y = D^-1 * (I + Y * S^-1 * Y') * D^-1 * y
                #
                # Y = [Y1 ... Ym ], Yk = D^-1 * Dk^2 * X * Lk^-T.

                # y := D^-1 * y
                blas.tbsv(D, y, n=N, k=0, ldA=1)

                # wnm =  Y' * y  (interpreted as an Nm vector)
                #     = [ L1^-1 * X' * D1^2 * D^-1 * y;
                #         L2^-1 * X' * D2^2 * D^-1 * y;
                #         ...
                #         Lm^-1 * X' * Dm^2 * D^-1 * y ]

                for k in range(m):

                    # wN = D^-1 * Dk^2 * y
                    blas.copy(y, wN)
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)

                    # wnm[:,k] = X' * wN
                    blas.gemv(X, wN, wnm, trans='T', offsety=k * n)

                    # wnm[:,k] = Lk^-1 * wnm[:,k]
                    blas.trsv(H[k], wnm, offsetx=k * n)

                # wnm := S^-1 * wnm  (an mn-vector)
                lapack.potrs(S, wnm)

                # y := y + Y * wnm
                #    = y + D^-1 * [ D1^2 * X * L1^-T ... D2^k * X * Lk^-T]
                #      * wnm

                for k in range(m):

                    # wnm[:,k] = Lk^-T * wnm[:,k]
                    blas.trsv(H[k], wnm, trans='T', offsetx=k * n)

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=k * n)

                    # wN = D^-1 * Dk^2 * wN
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.tbsv(D, wN, n=N, k=0, ldA=1)

                    # y += wN
                    blas.axpy(wN, y)

                # y := D^-1 *  y
                blas.tbsv(D, y, n=N, k=0, ldA=1)

                # For k = 1, ..., m:
                #
                # xk = (I - Dk^2 * X * Hk^-1 * X') * (-Dk^2 * y + xk)

                # x = x - [ D1^2 * y ... Dm^2 * y] (as an N x m matrix)
                for k in range(m):
                    blas.copy(y, wN)
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)
                    blas.axpy(wN, x, -1.0, offsety=k * N)

                # wnm  = X' * x (as an n x m matrix)
                blas.gemm(X, x, wnm, transA='T', m=n, n=m, k=N, ldB=N, ldC=n)

                # wnm[:,k] = Hk^-1 * wnm[:,k]
                for k in range(m):
                    lapack.potrs(H[k], wnm, offsetB=n * k)

                for k in range(m):

                    # wN = X * wnm[:,k]
                    blas.gemv(X, wnm, wN, offsetx=k * n)

                    # wN = Dk^2 * wN
                    blas.tbmv(dsq, wN, n=N, k=0, ldA=1, offsetA=k * N)

                    # x[:,k] := x[:,k] - wN
                    blas.axpy(wN, x, -1.0, n=N, offsety=k * N)

                # z := ( x - z ) ./ d
                blas.axpy(x, z, -1.0)
                blas.scal(-1.0, z)
                blas.tbsv(d, z, n=N * m, k=0, ldA=1)

                ###
                utime, stime = cputime()
                print("Solve:       utime = %.2f, stime = %.2f" \
                    %(utime-utime0, stime-stime0))


###

            return f
Esempio n. 29
0
    def Fgp(x=None, z=None):

        if x is None: return mnl, matrix(0.0, (n, 1))

        f = matrix(0.0, (mnl + 1, 1))
        Df = matrix(0.0, (mnl + 1, n))

        # y = F*x+g
        blas.copy(g, y)
        base.gemv(F, x, y, beta=1.0)

        if z is not None: H = matrix(0.0, (n, n))

        for i, start, stop in ind:

            # yi := exp(yi) = exp(Fi*x+gi)
            ymax = max(y[start:stop])
            y[start:stop] = base.exp(y[start:stop] - ymax)

            # fi = log sum yi = log sum exp(Fi*x+gi)
            ysum = blas.asum(y, n=stop - start, offset=start)
            f[i] = ymax + math.log(ysum)

            # yi := yi / sum(yi) = exp(Fi*x+gi) / sum(exp(Fi*x+gi))
            blas.scal(1.0 / ysum, y, n=stop - start, offset=start)

            # gradfi := Fi' * yi
            #        = Fi' * exp(Fi*x+gi) / sum(exp(Fi*x+gi))
            base.gemv(F,
                      y,
                      Df,
                      trans='T',
                      m=stop - start,
                      incy=mnl + 1,
                      offsetA=start,
                      offsetx=start,
                      offsety=i)

            if z is not None:

                # Hi = Fi' * (diag(yi) - yi*yi') * Fi
                #    = Fisc' * Fisc
                # where
                # Fisc = diag(yi)^1/2 * (I - 1*yi') * Fi
                #      = diag(yi)^1/2 * (Fi - 1*gradfi')

                Fsc[:K[i], :] = F[start:stop, :]
                for k in range(start, stop):
                    blas.axpy(Df,
                              Fsc,
                              n=n,
                              alpha=-1.0,
                              incx=mnl + 1,
                              incy=Fsc.size[0],
                              offsetx=i,
                              offsety=k - start)
                    blas.scal(math.sqrt(y[k]),
                              Fsc,
                              inc=Fsc.size[0],
                              offset=k - start)

                # H += z[i]*Hi = z[i] * Fisc' * Fisc
                blas.syrk(Fsc,
                          H,
                          trans='T',
                          k=stop - start,
                          alpha=z[i],
                          beta=1.0)

        if z is None:
            return f, Df
        return f, Df, H
Esempio n. 30
0
def completion(X, factored_updates = True):
    """
    Supernodal multifrontal maximum determinant positive definite
    matrix completion. The routine computes the Cholesky factor
    :math:`L` of the inverse of the maximum determinant positive
    definite matrix completion of :math:`X`:, i.e.,

    .. math::
         P( S^{-1} ) = X

    where :math:`S = LL^T`. On exit, the argument `X` contains the
    lower-triangular Cholesky factor :math:`L`.

    The optional argument `factored_updates` can be used to enable (if
    True) or disable (if False) updating of intermediate
    factorizations.

    :param X:                 :py:class:`cspmatrix`
    :param factored_updates:  boolean
    """

    assert isinstance(X, cspmatrix) and X.is_factor is False, "X must be a cspmatrix"

    n = X.symb.n
    snpost = X.symb.snpost
    snptr = X.symb.snptr
    chptr = X.symb.chptr
    chidx = X.symb.chidx

    relptr = X.symb.relptr
    relidx = X.symb.relidx
    blkptr = X.symb.blkptr
    blkval = X.blkval

    stack = []

    for k in reversed(list(snpost)):

        nn = snptr[k+1]-snptr[k]       # |Nk|
        na = relptr[k+1]-relptr[k]     # |Ak|
        nj = na + nn

        # allocate F and copy X_{Jk,Nk} to leading columns of F
        F = matrix(0.0, (nj,nj))
        lapack.lacpy(blkval, F, offsetA = blkptr[k], ldA = nj, m = nj, n = nn, uplo = 'L')

        # if supernode k is not a root node:
        if na > 0:
            # copy Vk to 2,2 block of F
            Vk = stack.pop()
            lapack.lacpy(Vk, F, offsetB = nn*nj+nn, m = na, n = na, uplo = 'L')

        # if supernode k has any children:
        for ii in range(chptr[k],chptr[k+1]):
            i = chidx[ii]       
            if factored_updates:
                r = relidx[relptr[i]:relptr[i+1]]
                stack.append(frontal_get_update_factor(F,r,nn,na))
            else:
                stack.append(frontal_get_update(F,relidx,relptr,i))

        # if supernode k is not a root node:
        if na > 0:
            if factored_updates:
                # In this case we have Vk = Lk'*Lk
                trL1 = 'T'
                trL2 = 'N'                
            else:
                # factorize Vk 
                lapack.potrf(F, offsetA = nj*nn+nn, n = na, ldA = nj)
                # In this case we have Vk = Lk*Lk'
                trL1 = 'N'  
                trL2 = 'T'  

            # compute L_{Ak,Nk} and inv(D_{Nk,Nk}) = S_{Nk,Nk} - S_{Ak,Nk}'*L_{Ak,Nk}
            lapack.trtrs(F, blkval, offsetA = nj*nn+nn, trans = trL1,\
                         offsetB = blkptr[k]+nn, ldB = nj, n = na, nrhs = nn)
            blas.syrk(blkval, blkval, n = nn, k = na, trans= 'T', alpha = -1.0, beta = 1.0,
                      offsetA = blkptr[k]+nn, offsetC = blkptr[k], ldA = nj, ldC = nj)
            lapack.trtrs(F, blkval, offsetA = nj*nn+nn, trans = trL2,\
                         offsetB = blkptr[k]+nn, ldB = nj, n = na, nrhs = nn)                
            for i in range(nn):
                blas.scal(-1.0, blkval, n = na, offset = blkptr[k] + i*nj + nn)


        # factorize inv(D_{Nk,Nk}) as R*R' so that D_{Nk,Nk} = L*L' with L = inv(R)'
        lapack.lacpy(blkval, F, offsetA = blkptr[k], ldA = nj,\
                     ldB = nj, m = nn, n = nn, uplo = 'L') # copy    -- FIX!
        F[:nn,:nn] = matrix(F[:nn,:nn][::-1],(nn,nn))      # reverse -- FIX!
        lapack.potrf(F, ldA = nj, n = nn, uplo = 'U')      # factorize
        F[:nn,:nn] = matrix(F[:nn,:nn][::-1],(nn,nn))      # reverse -- FIX!
        lapack.lacpy(F, blkval, offsetB = blkptr[k], ldA = nj,\
                     ldB = nj, m = nn, n = nn, uplo = 'L') # copy    -- FIX!

        # compute L = inv(R')
        lapack.trtri(blkval, offsetA = blkptr[k], ldA = nj, n = nn)

    X._is_factor = True

    return
Esempio n. 31
0
def robsvm(X, d, gamma, P, e):
    """
    Solves the following robust SVM training problem:

       minimize    (1/2) w'*w + gamma*sum(v)
       subject to  diag(d)*(X*w + b*1) >= 1 - v + E*u
                   || S_j*w ||_2 <= u_j,  j = 1...t
                   v >= 0

    The variables are w, b, v, and u. The matrix E is a selector
    matrix with zeros and one '1' per row.  E_ij = 1 means that the
    i'th training vector is associated with the j'th uncertainty
    ellipsoid.

    A custom KKT solver that exploits low-rank structure is used, and
    a positive definite system of equations of order n is
    formed and solved at each iteration.

    ARGUMENTS

    X             m-by-n matrix with training vectors as rows

    d             m-vector with training labels (-1,+1)

    P             list of t symmetric matrices of order n

    e             m-vector where e[i] is the index of the uncertainty
                  ellipsoid associated with the i'th training vector

    RETURNS

    w        n-vector

    b        scalar

    u        t-vector

    v        m-vector

    iters    number of interior-point iterations

    """

    m, n = X.size
    assert type(
        P
    ) is list, "P must be a list of t symmtric positive definite matrices of order n."
    k = len(P)
    if k > 0:
        assert e.size == (m, 1), "e must be an m-vector."
        assert max(e) < k and min(e) >= 0, "e[i] must be in {0,1,...,k-1}."

    E = spmatrix(1., e, range(m), (k, m)).T
    d = matrix(d, tc='d')
    q = matrix(0.0, (n + k + 1 + m, 1))
    q[n + k + 1:] = gamma
    h = matrix(0.0, (2 * m + k * (n + 1), 1))
    h[:m] = -1.0

    # linear operators Q and G
    def Q(x, y, alpha=1.0, beta=0.0, trans='N'):
        y[:n] = alpha * x[:n] + beta * y[:n]

    def G(x, y, alpha=1.0, beta=0.0, trans='N'):
        """
        Implements the linear operator

               [ -DX    E   -d   -I ]  
               [  0     0    0   -I ]  
               [  0   -e_1'  0    0 ]
          G =  [ -P_1'  0    0    0 ]     
               [  .     .    .    . ]    
               [  0   -e_k'  0    0 ]        
               [ -P_k'  0    0    0 ]       

        and its adjoint G'.

        """
        if trans == 'N':
            tmp = +y[:m]
            # y[:m] = alpha*(-DXw + Et - d*b - v) + beta*y[:m]
            base.gemv(E, x[n:n + k], tmp, alpha=alpha, beta=beta)
            blas.axpy(x[n + k + 1:], tmp, alpha=-alpha)
            blas.axpy(d, tmp, alpha=-alpha * x[n + k])
            y[:m] = tmp

            base.gemv(X, x[:n], tmp, alpha=alpha, beta=0.0)
            tmp = mul(d, tmp)
            y[:m] -= tmp

            # y[m:2*m] = -v
            y[m:2 * m] = -alpha * x[n + k + 1:] + beta * y[m:2 * m]

            # SOC 1,...,k
            for i in range(k):
                l = 2 * m + i * (n + 1)
                y[l] = -alpha * x[n + i] + beta * y[l]
                y[l + 1:l + 1 +
                  n] = -alpha * P[i] * x[:n] + beta * y[l + 1:l + 1 + n]

        else:
            tmp1 = mul(d, x[:m])
            tmp2 = y[:n]
            blas.gemv(X, tmp1, tmp2, trans='T', alpha=-alpha, beta=beta)
            for i in range(k):
                l = 2 * m + 1 + i * (n + 1)
                blas.gemv(P[i],
                          x[l:l + n],
                          tmp2,
                          trans='T',
                          alpha=-alpha,
                          beta=1.0)
            y[:n] = tmp2

            tmp2 = y[n:n + k]
            base.gemv(E, x[:m], tmp2, trans='T', alpha=alpha, beta=beta)
            blas.axpy(x[2 * m:2 * m + k * (1 + n):n + 1], tmp2, alpha=-alpha)
            y[n:n + k] = tmp2

            y[n + k] = -alpha * blas.dot(d, x[:m]) + beta * y[n + k]
            y[n + k +
              1:] = -alpha * (x[:m] + x[m:2 * m]) + beta * y[n + k + 1:]

    # precompute products Pi'*Pi
    Pt = []
    for p in P:
        y = matrix(0.0, (n, n))
        blas.syrk(p, y, trans='T')
        Pt.append(y)

    # scaled hyperbolic Householder transformations
    def qscal(u, beta, v, inv=False):
        """
        Transforms the vector u as
           u := beta * (2*v*v' - J) * u
        if 'inv' is False and as
           u := (1/beta) * (2*J*v*v'*J - J) * u
        if 'inv' is True.
        """
        if not inv:
            tmp = blas.dot(u, v)
            u[0] *= -1
            u += 2 * v * tmp
            u *= beta
        else:
            u[0] *= -1.0
            tmp = blas.dot(v, u)
            u[0] -= 2 * v[0] * tmp
            u[1:] += 2 * v[1:] * tmp
            u /= beta

    # custom KKT solver
    def F(W):
        """
        Custom solver for the system

        [  It  0   0    Xt'     0     At1' ...  Atk' ][ dwt  ]   [ rwt ]
        [  0   0   0    -d'     0      0   ...   0   ][ db   ]   [ rb  ]
        [  0   0   0    -I     -I      0   ...   0   ][ dv   ]   [ rv  ]
        [  Xt -d  -I  -Wl1^-2                        ][ dzl1 ]   [ rl1 ]
        [  0   0  -I         -Wl2^-2                 ][ dzl2 ] = [ rl2 ]
        [ At1  0   0                -W1^-2           ][ dz1  ]   [ r1  ] 
        [  |   |   |                       .         ][  |   ]   [  |  ]
        [ Atk  0   0                          -Wk^-2 ][ dzk  ]   [ rk  ]

        where

        It = [ I 0 ]  Xt = [ -D*X E ]  Ati = [ 0   -e_i' ]  
             [ 0 0 ]                         [ -Pi   0   ] 

        dwt = [ dw ]  rwt = [ rw ]
              [ dt ]        [ rt ].

        """

        # scalings and 'intermediate' vectors
        # db = inv(Wl1)^2 + inv(Wl2)^2
        db = W['di'][:m]**2 + W['di'][m:2 * m]**2
        dbi = div(1.0, db)

        # dt = I - inv(Wl1)*Dbi*inv(Wl1)
        dt = 1.0 - mul(W['di'][:m]**2, dbi)
        dtsqrt = sqrt(dt)

        # lam = Dt*inv(Wl1)*d
        lam = mul(dt, mul(W['di'][:m], d))

        # lt = E'*inv(Wl1)*lam
        lt = matrix(0.0, (k, 1))
        base.gemv(E, mul(W['di'][:m], lam), lt, trans='T')

        # Xs = sqrt(Dt)*inv(Wl1)*X
        tmp = mul(dtsqrt, W['di'][:m])
        Xs = spmatrix(tmp, range(m), range(m)) * X

        # Es = D*sqrt(Dt)*inv(Wl1)*E
        Es = spmatrix(mul(d, tmp), range(m), range(m)) * E

        # form Ab = I + sum((1/bi)^2*(Pi'*Pi + 4*(v'*v + 1)*Pi'*y*y'*Pi)) + Xs'*Xs
        #  and Bb = -sum((1/bi)^2*(4*ui*v'*v*Pi'*y*ei')) - Xs'*Es
        #  and D2 = Es'*Es + sum((1/bi)^2*(1+4*ui^2*(v'*v - 1))
        Ab = matrix(0.0, (n, n))
        Ab[::n + 1] = 1.0
        base.syrk(Xs, Ab, trans='T', beta=1.0)
        Bb = matrix(0.0, (n, k))
        Bb = -Xs.T * Es  # inefficient!?
        D2 = spmatrix(0.0, range(k), range(k))
        base.syrk(Es, D2, trans='T', partial=True)
        d2 = +D2.V
        del D2
        py = matrix(0.0, (n, 1))
        for i in range(k):
            binvsq = (1.0 / W['beta'][i])**2
            Ab += binvsq * Pt[i]
            dvv = blas.dot(W['v'][i], W['v'][i])
            blas.gemv(P[i], W['v'][i][1:], py, trans='T', alpha=1.0, beta=0.0)
            blas.syrk(py, Ab, alpha=4 * binvsq * (dvv + 1), beta=1.0)
            Bb[:, i] -= 4 * binvsq * W['v'][i][0] * dvv * py
            d2[i] += binvsq * (1 + 4 * (W['v'][i][0]**2) * (dvv - 1))

        d2i = div(1.0, d2)
        d2isqrt = sqrt(d2i)

        # compute a = alpha - lam'*inv(Wl1)*E*inv(D2)*E'*inv(Wl1)*lam
        alpha = blas.dot(lam, mul(W['di'][:m], d))
        tmp = matrix(0.0, (k, 1))
        base.gemv(E, mul(W['di'][:m], lam), tmp, trans='T')
        tmp = mul(tmp, d2isqrt)  #tmp = inv(D2)^(1/2)*E'*inv(Wl1)*lam
        a = alpha - blas.dot(tmp, tmp)

        # compute M12 = X'*D*inv(Wl1)*lam + Bb*inv(D2)*E'*inv(Wl1)*lam
        tmp = mul(tmp, d2isqrt)
        M12 = matrix(0.0, (n, 1))
        blas.gemv(Bb, tmp, M12, alpha=1.0)
        tmp = mul(d, mul(W['di'][:m], lam))
        blas.gemv(X, tmp, M12, trans='T', alpha=1.0, beta=1.0)

        # form and factor M
        sBb = Bb * spmatrix(d2isqrt, range(k), range(k))
        base.syrk(sBb, Ab, alpha=-1.0, beta=1.0)
        M = matrix([[Ab, M12.T], [M12, a]])
        lapack.potrf(M)

        def f(x, y, z):

            # residuals
            rwt = x[:n + k]
            rb = x[n + k]
            rv = x[n + k + 1:n + k + 1 + m]
            iw_rl1 = mul(W['di'][:m], z[:m])
            iw_rl2 = mul(W['di'][m:2 * m], z[m:2 * m])
            ri = [
                z[2 * m + i * (n + 1):2 * m + (i + 1) * (n + 1)]
                for i in range(k)
            ]

            # compute 'derived' residuals
            # rbwt = rwt + sum(Ai'*inv(Wi)^2*ri) + [-X'*D; E']*inv(Wl1)^2*rl1
            rbwt = +rwt
            for i in range(k):
                tmp = +ri[i]
                qscal(tmp, W['beta'][i], W['v'][i], inv=True)
                qscal(tmp, W['beta'][i], W['v'][i], inv=True)
                rbwt[n + i] -= tmp[0]
                blas.gemv(P[i], tmp[1:], rbwt, trans='T', alpha=-1.0, beta=1.0)
            tmp = mul(W['di'][:m], iw_rl1)
            tmp2 = matrix(0.0, (k, 1))
            base.gemv(E, tmp, tmp2, trans='T')
            rbwt[n:] += tmp2
            tmp = mul(d, tmp)  # tmp = D*inv(Wl1)^2*rl1
            blas.gemv(X, tmp, rbwt, trans='T', alpha=-1.0, beta=1.0)

            # rbb = rb - d'*inv(Wl1)^2*rl1
            rbb = rb - sum(tmp)

            # rbv = rv - inv(Wl2)*rl2 - inv(Wl1)^2*rl1
            rbv = rv - mul(W['di'][m:2 * m], iw_rl2) - mul(W['di'][:m], iw_rl1)

            # [rtw;rtt] = rbwt + [-X'*D; E']*inv(Wl1)^2*inv(Db)*rbv
            tmp = mul(W['di'][:m]**2, mul(dbi, rbv))
            rtt = +rbwt[n:]
            base.gemv(E, tmp, rtt, trans='T', alpha=1.0, beta=1.0)
            rtw = +rbwt[:n]
            tmp = mul(d, tmp)
            blas.gemv(X, tmp, rtw, trans='T', alpha=-1.0, beta=1.0)

            # rtb = rbb - d'*inv(Wl1)^2*inv(Db)*rbv
            rtb = rbb - sum(tmp)

            # solve M*[dw;db] = [rtw - Bb*inv(D2)*rtt; rtb + lt'*inv(D2)*rtt]
            tmp = mul(d2i, rtt)
            tmp2 = matrix(0.0, (n, 1))
            blas.gemv(Bb, tmp, tmp2)
            dwdb = matrix([rtw - tmp2, rtb + blas.dot(mul(d2i, lt), rtt)])
            lapack.potrs(M, dwdb)

            # compute dt = inv(D2)*(rtt - Bb'*dw + lt*db)
            tmp2 = matrix(0.0, (k, 1))
            blas.gemv(Bb, dwdb[:n], tmp2, trans='T')
            dt = mul(d2i, rtt - tmp2 + lt * dwdb[-1])

            # compute dv = inv(Db)*(rbv + inv(Wl1)^2*(E*dt - D*X*dw - d*db))
            dv = matrix(0.0, (m, 1))
            blas.gemv(X, dwdb[:n], dv, alpha=-1.0)
            dv = mul(d, dv) - d * dwdb[-1]
            base.gemv(E, dt, dv, beta=1.0)
            tmp = +dv  # tmp = E*dt - D*X*dw - d*db
            dv = mul(dbi, rbv + mul(W['di'][:m]**2, dv))

            # compute wdz1 = inv(Wl1)*(E*dt - D*X*dw - d*db - dv - rl1)
            wdz1 = mul(W['di'][:m], tmp - dv) - iw_rl1

            # compute wdz2 = - inv(Wl2)*(dv + rl2)
            wdz2 = -mul(W['di'][m:2 * m], dv) - iw_rl2

            # compute wdzi = inv(Wi)*([-ei'*dt; -Pi*dw] - ri)
            wdzi = []
            tmp = matrix(0.0, (n, 1))
            for i in range(k):
                blas.gemv(P[i], dwdb[:n], tmp, alpha=-1.0, beta=0.0)
                tmp1 = matrix([-dt[i], tmp])
                blas.axpy(ri[i], tmp1, alpha=-1.0)
                qscal(tmp1, W['beta'][i], W['v'][i], inv=True)
                wdzi.append(tmp1)

            # solution
            x[:n] = dwdb[:n]
            x[n:n + k] = dt
            x[n + k] = dwdb[-1]
            x[n + k + 1:] = dv
            z[:m] = wdz1
            z[m:2 * m] = wdz2
            for i in range(k):
                z[2 * m + i * (n + 1):2 * m + (i + 1) * (n + 1)] = wdzi[i]

        return f

    # solve cone QP and return solution
    sol = solvers.coneqp(Q,
                         q,
                         G,
                         h,
                         dims={
                             'l': 2 * m,
                             'q': [n + 1 for i in range(k)],
                             's': []
                         },
                         kktsolver=F)
    return sol['x'][:n], sol['x'][
        n + k], sol['x'][n:n + k], sol['x'][n + k + 1:], sol['iterations']
Esempio n. 32
0
def cholesky(X):
    """
    Supernodal multifrontal Cholesky factorization:

    .. math::
         X = LL^T

    where :math:`L` is lower-triangular. On exit, the argument :math:`X`
    contains the Cholesky factor :math:`L`.

    :param X:    :py:class:`cspmatrix`
    """

    assert isinstance(X, cspmatrix) and X.is_factor is False, "X must be a cspmatrix"

    n = X.symb.n
    snpost = X.symb.snpost
    snptr = X.symb.snptr
    chptr = X.symb.chptr
    chidx = X.symb.chidx

    relptr = X.symb.relptr
    relidx = X.symb.relidx
    blkptr = X.symb.blkptr
    blkval = X.blkval

    stack = []

    for k in snpost:

        nn = snptr[k+1]-snptr[k]       # |Nk|
        na = relptr[k+1]-relptr[k]     # |Ak|
        nj = na + nn                   

        # build frontal matrix
        F = matrix(0.0, (nj, nj))
        lapack.lacpy(blkval, F, offsetA = blkptr[k], m = nj, n = nn, ldA = nj, uplo = 'L')

        # add update matrices from children to frontal matrix
        for i in range(chptr[k+1]-1,chptr[k]-1,-1):
            Ui = stack.pop()
            frontal_add_update(F, Ui, relidx, relptr, chidx[i])

        # factor L_{Nk,Nk}
        lapack.potrf(F, n = nn, ldA = nj)

        # if supernode k is not a root node, compute and push update matrix onto stack
        if na > 0:   
            # compute L_{Ak,Nk} := A_{Ak,Nk}*inv(L_{Nk,Nk}')
            blas.trsm(F, F, m = na, n = nn, ldA = nj, 
                      ldB = nj, offsetB = nn, transA = 'T', side = 'R')

            # compute Uk = Uk - L_{Ak,Nk}*inv(D_{Nk,Nk})*L_{Ak,Nk}'
            if nn == 1:
                blas.syr(F, F, n = na, offsetx = nn, \
                         offsetA = nn*nj+nn, ldA = nj, alpha = -1.0)
            else:
                blas.syrk(F, F, k = nn, n = na, offsetA = nn, ldA = nj,
                          offsetC = nn*nj+nn, ldC = nj, alpha = -1.0, beta = 1.0)

            # compute L_{Ak,Nk} := L_{Ak,Nk}*inv(L_{Nk,Nk})
            blas.trsm(F, F, m = na, n = nn,\
                      ldA = nj, ldB = nj, offsetB = nn, side = 'R')

            # add Uk to stack
            Uk = matrix(0.0,(na,na))
            lapack.lacpy(F, Uk, m = na, n = na, uplo = 'L', offsetA = nn*nj+nn, ldA = nj)
            stack.append(Uk)

        # copy the leading Nk columns of frontal matrix to blkval
        lapack.lacpy(F, blkval, uplo = "L", offsetB = blkptr[k], m = nj, n = nn, ldB = nj)        

    X.is_factor = True

    return
Esempio n. 33
0
    def F(W):
        """
        Custom solver for the system

        [  It  0   0    Xt'     0     At1' ...  Atk' ][ dwt  ]   [ rwt ]
        [  0   0   0    -d'     0      0   ...   0   ][ db   ]   [ rb  ]
        [  0   0   0    -I     -I      0   ...   0   ][ dv   ]   [ rv  ]
        [  Xt -d  -I  -Wl1^-2                        ][ dzl1 ]   [ rl1 ]
        [  0   0  -I         -Wl2^-2                 ][ dzl2 ] = [ rl2 ]
        [ At1  0   0                -W1^-2           ][ dz1  ]   [ r1  ] 
        [  |   |   |                       .         ][  |   ]   [  |  ]
        [ Atk  0   0                          -Wk^-2 ][ dzk  ]   [ rk  ]

        where

        It = [ I 0 ]  Xt = [ -D*X E ]  Ati = [ 0   -e_i' ]  
             [ 0 0 ]                         [ -Pi   0   ] 

        dwt = [ dw ]  rwt = [ rw ]
              [ dt ]        [ rt ].

        """

        # scalings and 'intermediate' vectors
        # db = inv(Wl1)^2 + inv(Wl2)^2
        db = W['di'][:m]**2 + W['di'][m:2 * m]**2
        dbi = div(1.0, db)

        # dt = I - inv(Wl1)*Dbi*inv(Wl1)
        dt = 1.0 - mul(W['di'][:m]**2, dbi)
        dtsqrt = sqrt(dt)

        # lam = Dt*inv(Wl1)*d
        lam = mul(dt, mul(W['di'][:m], d))

        # lt = E'*inv(Wl1)*lam
        lt = matrix(0.0, (k, 1))
        base.gemv(E, mul(W['di'][:m], lam), lt, trans='T')

        # Xs = sqrt(Dt)*inv(Wl1)*X
        tmp = mul(dtsqrt, W['di'][:m])
        Xs = spmatrix(tmp, range(m), range(m)) * X

        # Es = D*sqrt(Dt)*inv(Wl1)*E
        Es = spmatrix(mul(d, tmp), range(m), range(m)) * E

        # form Ab = I + sum((1/bi)^2*(Pi'*Pi + 4*(v'*v + 1)*Pi'*y*y'*Pi)) + Xs'*Xs
        #  and Bb = -sum((1/bi)^2*(4*ui*v'*v*Pi'*y*ei')) - Xs'*Es
        #  and D2 = Es'*Es + sum((1/bi)^2*(1+4*ui^2*(v'*v - 1))
        Ab = matrix(0.0, (n, n))
        Ab[::n + 1] = 1.0
        base.syrk(Xs, Ab, trans='T', beta=1.0)
        Bb = matrix(0.0, (n, k))
        Bb = -Xs.T * Es  # inefficient!?
        D2 = spmatrix(0.0, range(k), range(k))
        base.syrk(Es, D2, trans='T', partial=True)
        d2 = +D2.V
        del D2
        py = matrix(0.0, (n, 1))
        for i in range(k):
            binvsq = (1.0 / W['beta'][i])**2
            Ab += binvsq * Pt[i]
            dvv = blas.dot(W['v'][i], W['v'][i])
            blas.gemv(P[i], W['v'][i][1:], py, trans='T', alpha=1.0, beta=0.0)
            blas.syrk(py, Ab, alpha=4 * binvsq * (dvv + 1), beta=1.0)
            Bb[:, i] -= 4 * binvsq * W['v'][i][0] * dvv * py
            d2[i] += binvsq * (1 + 4 * (W['v'][i][0]**2) * (dvv - 1))

        d2i = div(1.0, d2)
        d2isqrt = sqrt(d2i)

        # compute a = alpha - lam'*inv(Wl1)*E*inv(D2)*E'*inv(Wl1)*lam
        alpha = blas.dot(lam, mul(W['di'][:m], d))
        tmp = matrix(0.0, (k, 1))
        base.gemv(E, mul(W['di'][:m], lam), tmp, trans='T')
        tmp = mul(tmp, d2isqrt)  #tmp = inv(D2)^(1/2)*E'*inv(Wl1)*lam
        a = alpha - blas.dot(tmp, tmp)

        # compute M12 = X'*D*inv(Wl1)*lam + Bb*inv(D2)*E'*inv(Wl1)*lam
        tmp = mul(tmp, d2isqrt)
        M12 = matrix(0.0, (n, 1))
        blas.gemv(Bb, tmp, M12, alpha=1.0)
        tmp = mul(d, mul(W['di'][:m], lam))
        blas.gemv(X, tmp, M12, trans='T', alpha=1.0, beta=1.0)

        # form and factor M
        sBb = Bb * spmatrix(d2isqrt, range(k), range(k))
        base.syrk(sBb, Ab, alpha=-1.0, beta=1.0)
        M = matrix([[Ab, M12.T], [M12, a]])
        lapack.potrf(M)

        def f(x, y, z):

            # residuals
            rwt = x[:n + k]
            rb = x[n + k]
            rv = x[n + k + 1:n + k + 1 + m]
            iw_rl1 = mul(W['di'][:m], z[:m])
            iw_rl2 = mul(W['di'][m:2 * m], z[m:2 * m])
            ri = [
                z[2 * m + i * (n + 1):2 * m + (i + 1) * (n + 1)]
                for i in range(k)
            ]

            # compute 'derived' residuals
            # rbwt = rwt + sum(Ai'*inv(Wi)^2*ri) + [-X'*D; E']*inv(Wl1)^2*rl1
            rbwt = +rwt
            for i in range(k):
                tmp = +ri[i]
                qscal(tmp, W['beta'][i], W['v'][i], inv=True)
                qscal(tmp, W['beta'][i], W['v'][i], inv=True)
                rbwt[n + i] -= tmp[0]
                blas.gemv(P[i], tmp[1:], rbwt, trans='T', alpha=-1.0, beta=1.0)
            tmp = mul(W['di'][:m], iw_rl1)
            tmp2 = matrix(0.0, (k, 1))
            base.gemv(E, tmp, tmp2, trans='T')
            rbwt[n:] += tmp2
            tmp = mul(d, tmp)  # tmp = D*inv(Wl1)^2*rl1
            blas.gemv(X, tmp, rbwt, trans='T', alpha=-1.0, beta=1.0)

            # rbb = rb - d'*inv(Wl1)^2*rl1
            rbb = rb - sum(tmp)

            # rbv = rv - inv(Wl2)*rl2 - inv(Wl1)^2*rl1
            rbv = rv - mul(W['di'][m:2 * m], iw_rl2) - mul(W['di'][:m], iw_rl1)

            # [rtw;rtt] = rbwt + [-X'*D; E']*inv(Wl1)^2*inv(Db)*rbv
            tmp = mul(W['di'][:m]**2, mul(dbi, rbv))
            rtt = +rbwt[n:]
            base.gemv(E, tmp, rtt, trans='T', alpha=1.0, beta=1.0)
            rtw = +rbwt[:n]
            tmp = mul(d, tmp)
            blas.gemv(X, tmp, rtw, trans='T', alpha=-1.0, beta=1.0)

            # rtb = rbb - d'*inv(Wl1)^2*inv(Db)*rbv
            rtb = rbb - sum(tmp)

            # solve M*[dw;db] = [rtw - Bb*inv(D2)*rtt; rtb + lt'*inv(D2)*rtt]
            tmp = mul(d2i, rtt)
            tmp2 = matrix(0.0, (n, 1))
            blas.gemv(Bb, tmp, tmp2)
            dwdb = matrix([rtw - tmp2, rtb + blas.dot(mul(d2i, lt), rtt)])
            lapack.potrs(M, dwdb)

            # compute dt = inv(D2)*(rtt - Bb'*dw + lt*db)
            tmp2 = matrix(0.0, (k, 1))
            blas.gemv(Bb, dwdb[:n], tmp2, trans='T')
            dt = mul(d2i, rtt - tmp2 + lt * dwdb[-1])

            # compute dv = inv(Db)*(rbv + inv(Wl1)^2*(E*dt - D*X*dw - d*db))
            dv = matrix(0.0, (m, 1))
            blas.gemv(X, dwdb[:n], dv, alpha=-1.0)
            dv = mul(d, dv) - d * dwdb[-1]
            base.gemv(E, dt, dv, beta=1.0)
            tmp = +dv  # tmp = E*dt - D*X*dw - d*db
            dv = mul(dbi, rbv + mul(W['di'][:m]**2, dv))

            # compute wdz1 = inv(Wl1)*(E*dt - D*X*dw - d*db - dv - rl1)
            wdz1 = mul(W['di'][:m], tmp - dv) - iw_rl1

            # compute wdz2 = - inv(Wl2)*(dv + rl2)
            wdz2 = -mul(W['di'][m:2 * m], dv) - iw_rl2

            # compute wdzi = inv(Wi)*([-ei'*dt; -Pi*dw] - ri)
            wdzi = []
            tmp = matrix(0.0, (n, 1))
            for i in range(k):
                blas.gemv(P[i], dwdb[:n], tmp, alpha=-1.0, beta=0.0)
                tmp1 = matrix([-dt[i], tmp])
                blas.axpy(ri[i], tmp1, alpha=-1.0)
                qscal(tmp1, W['beta'][i], W['v'][i], inv=True)
                wdzi.append(tmp1)

            # solution
            x[:n] = dwdb[:n]
            x[n:n + k] = dt
            x[n + k] = dwdb[-1]
            x[n + k + 1:] = dv
            z[:m] = wdz1
            z[m:2 * m] = wdz2
            for i in range(k):
                z[2 * m + i * (n + 1):2 * m + (i + 1) * (n + 1)] = wdzi[i]

        return f
Esempio n. 34
0
    def F(W):
        # SVD R[j] = U[j] * diag(sig[j]) * Vt[j]
        lapack.gesvd(+W['r'][0], sv, jobu='A', jobvt='A', U=U, Vt=Vt)

        # Vt[j] := diag(sig[j])^-1 * Vt[j]
        for k in xrange(ns):
            blas.tbsv(sv, Vt, n=ns, k=0, ldA=1, offsetx=k * ns)

        # Gamma[j] is an ns[j] x ns[j] symmetric matrix
        #
        #  (sig[j] * sig[j]') ./  sqrt(1 + rho * (sig[j] * sig[j]').^2)

        # S = sig[j] * sig[j]'
        S = matrix(0.0, (ns, ns))
        blas.syrk(sv, S)
        Gamma = div(S, sqrt(1.0 + rho * S**2))
        symmetrize(Gamma, ns)

        # As represents the scaled mapping
        #
        #     As(x) = A(u * (Gamma .* x) * u')
        #    As'(y) = Gamma .* (u' * A'(y) * u)
        #
        # stored in a similar format as A, except that we use packed
        # storage for the columns of As[i][j].

        if type(A) is spmatrix:
            blas.scal(0.0, As)
            try:
                As[VecAIndex] = +A['s'][VecAIndex]
            except:
                As[VecAIndex] = +A[VecAIndex]
        else:
            blas.copy(A, As)

        # As[i][j][:,k] = diag( diag(Gamma[j]))*As[i][j][:,k]
        # As[i][j][l,:] = Gamma[j][l,l]*As[i][j][l,:]
        for k in xrange(ms):
            cngrnc(U, As, trans='T', offsetx=k * (ns2))
            blas.tbmv(Gamma, As, n=ns2, k=0, ldA=1, offsetx=k * (ns2))

        misc.pack(As, Aspkd, {'l': 0, 'q': [], 's': [ns] * ms})

        # H is an m times m block matrix with i, k block
        #
        #      Hik = sum_j As[i,j]' * As[k,j]
        #
        # of size ms[i] x ms[k].  Hik = 0 if As[i,j] or As[k,j]
        # are zero for all j
        H = matrix(0.0, (ms, ms))
        blas.syrk(Aspkd, H, trans='T', beta=1.0, k=ns * (ns + 1) / 2)

        lapack.potrf(H)

        def solve(x, y, z):
            """
            Returns solution of 

                rho * ux + A'(uy) - r^-T * uz * r^-1 = bx
                A(ux)                                = by
                -ux               - r * uz * r'      = bz.

            On entry, x = bx, y = by, z = bz.
            On exit, x = ux, y = uy, z = uz.
            """

            # bz is a copy of z in the format of x
            blas.copy(z, bz)
            blas.axpy(bz, x, alpha=rho)

            # x := Gamma .* (u' * x * u)
            #    = Gamma .* (u' * (bx + rho * bz) * u)

            cngrnc(U, x, trans='T', offsetx=0)
            blas.tbmv(Gamma, x, n=ns2, k=0, ldA=1, offsetx=0)

            # y := y - As(x)
            #   := by - As( Gamma .* u' * (bx + rho * bz) * u)
            #blas.copy(x,xp)
            #pack_ip(xp,n = ns,m=1,nl=nl)
            misc.pack(x, xp, {'l': 0, 'q': [], 's': [ns]})

            blas.gemv(Aspkd, xp, y, trans = 'T',alpha = -1.0, beta = 1.0, \
                m = ns*(ns+1)/2, n = ms,offsetx = 0)

            # y := -y - A(bz)
            #    = -by - A(bz) + As(Gamma .*  (u' * (bx + rho * bz) * u)
            Af(bz, y, alpha=-1.0, beta=-1.0)

            # y := H^-1 * y
            #    = H^-1 ( -by - A(bz) + As(Gamma.* u'*(bx + rho*bz)*u) )
            #    = uy

            blas.trsv(H, y)
            blas.trsv(H, y, trans='T')

            # bz = Vt' * vz * Vt
            #    = uz where
            # vz := Gamma .* ( As'(uy)  - x )
            #     = Gamma .* ( As'(uy)  - Gamma .* (u'*(bx + rho *bz)*u) )
            #     = Gamma.^2 .* ( u' * (A'(uy) - bx - rho * bz) * u ).
            #blas.copy(x,xp)
            #pack_ip(xp,n=ns,m=1,nl=nl)

            misc.pack(x, xp, {'l': 0, 'q': [], 's': [ns]})
            blas.scal(-1.0, xp)

            blas.gemv(Aspkd,
                      y,
                      xp,
                      alpha=1.0,
                      beta=1.0,
                      m=ns * (ns + 1) / 2,
                      n=ms,
                      offsety=0)

            # bz[j] is xp unpacked and multiplied with Gamma
            misc.unpack(xp, bz, {'l': 0, 'q': [], 's': [ns]})
            blas.tbmv(Gamma, bz, n=ns2, k=0, ldA=1, offsetx=0)

            # bz = Vt' * bz * Vt
            #    = uz
            cngrnc(Vt, bz, trans='T', offsetx=0)

            symmetrize(bz, ns, offset=0)

            # x = -bz - r * uz * r'
            # z contains r.h.s. bz;  copy to x
            blas.copy(z, x)
            blas.copy(bz, z)

            cngrnc(W['r'][0], bz, offsetx=0)
            blas.axpy(bz, x)
            blas.scal(-1.0, x)

        return solve
Esempio n. 35
0
    def F(W):

        for j in xrange(N):

            # SVD R[j] = U[j] * diag(sig[j]) * Vt[j]
            lapack.gesvd(+W['r'][j],
                         sv[j],
                         jobu='A',
                         jobvt='A',
                         U=U[j],
                         Vt=Vt[j])

            # Vt[j] := diag(sig[j])^-1 * Vt[j]
            for k in xrange(ns[j]):
                blas.tbsv(sv[j], Vt[j], n=ns[j], k=0, ldA=1, offsetx=k * ns[j])

            # Gamma[j] is an ns[j] x ns[j] symmetric matrix
            #
            #  (sig[j] * sig[j]') ./  sqrt(1 + rho * (sig[j] * sig[j]').^2)

            # S = sig[j] * sig[j]'
            S = matrix(0.0, (ns[j], ns[j]))
            blas.syrk(sv[j], S)
            Gamma[j][:] = div(S, sqrt(1.0 + rho * S**2))[:]
            symmetrize(Gamma[j], ns[j])

            # As represents the scaled mapping
            #
            #     As(x) = A(u * (Gamma .* x) * u')
            #    As'(y) = Gamma .* (u' * A'(y) * u)
            #
            # stored in a similar format as A, except that we use packed
            # storage for the columns of As[i][j].

            for i in xrange(M):

                if (type(A[i][j]) is matrix) or (type(A[i][j]) is spmatrix):

                    # As[i][j][:,k] = vec(
                    #     (U[j]' * mat( A[i][j][:,k] ) * U[j]) .* Gamma[j])

                    copy(A[i][j], As[i][j])
                    As[i][j] = matrix(As[i][j])
                    for k in xrange(ms[i]):
                        cngrnc(U[j],
                               As[i][j],
                               trans='T',
                               offsetx=k * (ns[j]**2),
                               n=ns[j])
                        blas.tbmv(Gamma[j],
                                  As[i][j],
                                  n=ns[j]**2,
                                  k=0,
                                  ldA=1,
                                  offsetx=k * (ns[j]**2))

                    # pack As[i][j] in place
                    #pack_ip(As[i][j], ns[j])
                    for k in xrange(As[i][j].size[1]):
                        tmp = +As[i][j][:, k]
                        misc.pack2(tmp, {'l': 0, 'q': [], 's': [ns[j]]})
                        As[i][j][:, k] = tmp

                else:
                    As[i][j] = 0.0

        # H is an m times m block matrix with i, k block
        #
        #      Hik = sum_j As[i,j]' * As[k,j]
        #
        # of size ms[i] x ms[k].  Hik = 0 if As[i,j] or As[k,j]
        # are zero for all j

        H = spmatrix([], [], [], (sum(ms), sum(ms)))
        rowid = 0
        for i in xrange(M):
            colid = 0
            for k in xrange(i + 1):
                sparse_block = True
                Hik = matrix(0.0, (ms[i], ms[k]))
                for j in xrange(N):
                    if (type(As[i][j]) is matrix) and \
                        (type(As[k][j]) is matrix):
                        sparse_block = False
                        # Hik += As[i,j]' * As[k,j]
                        if i == k:
                            blas.syrk(As[i][j],
                                      Hik,
                                      trans='T',
                                      beta=1.0,
                                      k=ns[j] * (ns[j] + 1) / 2,
                                      ldA=ns[j]**2)
                        else:
                            blas.gemm(As[i][j],
                                      As[k][j],
                                      Hik,
                                      transA='T',
                                      beta=1.0,
                                      k=ns[j] * (ns[j] + 1) / 2,
                                      ldA=ns[j]**2,
                                      ldB=ns[j]**2)
                if not (sparse_block):
                    H[rowid:rowid+ms[i], colid:colid+ms[k]] \
                        = sparse(Hik)
                colid += ms[k]
            rowid += ms[i]

        HF = cholmod.symbolic(H)
        cholmod.numeric(H, HF)

        def solve(x, y, z):
            """
            Returns solution of 

                rho * ux + A'(uy) - r^-T * uz * r^-1 = bx
                A(ux)                                = by
                -ux               - r * uz * r'      = bz.

            On entry, x = bx, y = by, z = bz.
            On exit, x = ux, y = uy, z = uz.
            """

            # bz is a copy of z in the format of x
            blas.copy(z, bz)
            # x := x + rho * bz
            #    = bx + rho * bz
            blas.axpy(bz, x, alpha=rho)

            # x := Gamma .* (u' * x * u)
            #    = Gamma .* (u' * (bx + rho * bz) * u)
            offsetj = 0
            for j in xrange(N):
                cngrnc(U[j], x, trans='T', offsetx=offsetj, n=ns[j])
                blas.tbmv(Gamma[j], x, n=ns[j]**2, k=0, ldA=1, offsetx=offsetj)
                offsetj += ns[j]**2

            # y := y - As(x)
            #   := by - As( Gamma .* u' * (bx + rho * bz) * u)

            blas.copy(x, xp)

            offsetj = 0
            for j in xrange(N):
                misc.pack2(xp, {'l': offsetj, 'q': [], 's': [ns[j]]})
                offsetj += ns[j]**2

            offseti = 0
            for i in xrange(M):
                offsetj = 0
                for j in xrange(N):
                    if type(As[i][j]) is matrix:
                        blas.gemv(As[i][j],
                                  xp,
                                  y,
                                  trans='T',
                                  alpha=-1.0,
                                  beta=1.0,
                                  m=ns[j] * (ns[j] + 1) / 2,
                                  n=ms[i],
                                  ldA=ns[j]**2,
                                  offsetx=offsetj,
                                  offsety=offseti)
                    offsetj += ns[j]**2
                offseti += ms[i]
            # y := -y - A(bz)
            #    = -by - A(bz) + As(Gamma .*  (u' * (bx + rho * bz) * u)

            Af(bz, y, alpha=-1.0, beta=-1.0)

            # y := H^-1 * y
            #    = H^-1 ( -by - A(bz) + As(Gamma.* u'*(bx + rho*bz)*u) )
            #    = uy

            cholmod.solve(HF, y)

            # bz = Vt' * vz * Vt
            #    = uz where
            # vz := Gamma .* ( As'(uy)  - x )
            #     = Gamma .* ( As'(uy)  - Gamma .* (u'*(bx + rho *bz)*u) )
            #     = Gamma.^2 .* ( u' * (A'(uy) - bx - rho * bz) * u ).
            blas.copy(x, xp)

            offsetj = 0
            for j in xrange(N):

                # xp is -x[j] = -Gamma .* (u' * (bx + rho*bz) * u)
                # in packed storage
                misc.pack2(xp, {'l': offsetj, 'q': [], 's': [ns[j]]})
                offsetj += ns[j]**2
            blas.scal(-1.0, xp)

            offsetj = 0
            for j in xrange(N):
                # xp +=  As'(uy)

                offseti = 0
                for i in xrange(M):
                    if type(As[i][j]) is matrix:
                        blas.gemv(As[i][j], y, xp, alpha = 1.0,
                             beta = 1.0, m = ns[j]*(ns[j]+1)/2, \
                                n = ms[i],ldA = ns[j]**2, \
                                offsetx = offseti, offsety = offsetj)
                    offseti += ms[i]

                # bz[j] is xp unpacked and multiplied with Gamma
                #unpack(xp, bz[j], ns[j])

                misc.unpack(xp,
                            bz, {
                                'l': 0,
                                'q': [],
                                's': [ns[j]]
                            },
                            offsetx=offsetj,
                            offsety=offsetj)

                blas.tbmv(Gamma[j],
                          bz,
                          n=ns[j]**2,
                          k=0,
                          ldA=1,
                          offsetx=offsetj)

                # bz = Vt' * bz * Vt
                #    = uz

                cngrnc(Vt[j], bz, trans='T', offsetx=offsetj, n=ns[j])
                symmetrize(bz, ns[j], offset=offsetj)
                offsetj += ns[j]**2

            # x = -bz - r * uz * r'
            blas.copy(z, x)
            blas.copy(bz, z)
            offsetj = 0
            for j in xrange(N):
                cngrnc(+W['r'][j], bz, offsetx=offsetj, n=ns[j])
                offsetj += ns[j]**2
            blas.axpy(bz, x)
            blas.scal(-1.0, x)

        return solve
Esempio n. 36
0
    def F(W):
        """
        Generate a solver for

                                             A'(uz0) = bx[0]
                                          -uz0 - uz1 = bx[1] 
            A(ux[0]) - ux[1] - r0*r0' * uz0 * r0*r0' = bz0 
                     - ux[1] - r1*r1' * uz1 * r1*r1' = bz1.

        uz0, uz1, bz0, bz1 are symmetric m x m-matrices.
        ux[0], bx[0] are n-vectors.
        ux[1], bx[1] are symmetric m x m-matrices.

        We first calculate a congruence that diagonalizes r0*r0' and r1*r1':
 
            U' * r0 * r0' * U = I,  U' * r1 * r1' * U = S.

        We then make a change of variables

            usx[0] = ux[0],  
            usx[1] = U' * ux[1] * U  
              usz0 = U^-1 * uz0 * U^-T  
              usz1 = U^-1 * uz1 * U^-T 

        and define 

              As() = U' * A() * U'  
            bsx[1] = U^-1 * bx[1] * U^-T
              bsz0 = U' * bz0 * U  
              bsz1 = U' * bz1 * U.  

        This gives

                             As'(usz0) = bx[0]
                          -usz0 - usz1 = bsx[1] 
            As(usx[0]) - usx[1] - usz0 = bsz0 
                -usx[1] - S * usz1 * S = bsz1.


        1. Eliminate usz0, usz1 using equations 3 and 4,

               usz0 = As(usx[0]) - usx[1] - bsz0
               usz1 = -S^-1 * (usx[1] + bsz1) * S^-1.

           This gives two equations in usx[0] an usx[1].

               As'(As(usx[0]) - usx[1]) = bx[0] + As'(bsz0)

               -As(usx[0]) + usx[1] + S^-1 * usx[1] * S^-1
                   = bsx[1] - bsz0 - S^-1 * bsz1 * S^-1.


        2. Eliminate usx[1] using equation 2:

               usx[1] + S * usx[1] * S 
                   = S * ( As(usx[0]) + bsx[1] - bsz0 ) * S - bsz1

           i.e., with Gamma[i,j] = 1.0 + S[i,i] * S[j,j],
 
               usx[1] = ( S * As(usx[0]) * S ) ./ Gamma 
                        + ( S * ( bsx[1] - bsz0 ) * S - bsz1 ) ./ Gamma.

           This gives an equation in usx[0].

               As'( As(usx[0]) ./ Gamma ) 
                   = bx0 + As'(bsz0) + 
                     As'( (S * ( bsx[1] - bsz0 ) * S - bsz1) ./ Gamma )
                   = bx0 + As'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ Gamma ).

        """

        # Calculate U s.t. 
        # 
        #     U' * r0*r0' * U = I,   U' * r1*r1' * U = diag(s).
 
        # Cholesky factorization r0 * r0' = L * L'
        blas.syrk(W['r'][0], L)
        lapack.potrf(L)

        # SVD L^-1 * r1 = U * diag(s) * V'  
        blas.copy(W['r'][1], U)
        blas.trsm(L, U) 
        lapack.gesvd(U, s, jobu = 'O')

        # s := s**2
        s[:] = s**2

        # Uti := U
        blas.copy(U, Uti)

        # U := L^-T * U
        blas.trsm(L, U, transA = 'T')

        # Uti := L * Uti = U^-T 
        blas.trmm(L, Uti)

        # Us := U * diag(s)^-1
        blas.copy(U, Us)
        for i in range(m):
            blas.tbsv(s, Us, n = m, k = 0, ldA = 1, incx = m, offsetx = i)

        # S is m x m with lower triangular entries s[i] * s[j] 
        # sqrtG is m x m with lower triangular entries sqrt(1.0 + s[i]*s[j])
        # Upper triangular entries are undefined but nonzero.

        blas.scal(0.0, S)
        blas.syrk(s, S)
        Gamma = 1.0 + S
        sqrtG = sqrt(Gamma)


        # Asc[i] = (U' * Ai * * U ) ./  sqrtG,  for i = 1, ..., n
        #        = Asi ./ sqrt(Gamma)
        blas.copy(A, Asc)
        misc.scale(Asc,   # only 'r' part of the dictionary is used   
            {'dnl': matrix(0.0, (0, 1)), 'dnli': matrix(0.0, (0, 1)),
             'd': matrix(0.0, (0, 1)), 'di': matrix(0.0, (0, 1)),
             'v': [], 'beta': [], 'r': [ U ], 'rti': [ U ]}) 
        for i in range(n):
            blas.tbsv(sqrtG, Asc, n = msq, k = 0, ldA = 1, offsetx = i*msq)

        # Convert columns of Asc to packed storage
        misc.pack2(Asc, {'l': 0, 'q': [], 's': [ m ]})

        # Cholesky factorization of Asc' * Asc.
        H = matrix(0.0, (n, n))
        blas.syrk(Asc, H, trans = 'T', k = mpckd)
        lapack.potrf(H)


        def solve(x, y, z):
            """

            1. Solve for usx[0]:

               Asc'(Asc(usx[0]))
                   = bx0 + Asc'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ sqrtG)
                   = bx0 + Asc'( ( bsz0 + S * ( bsx[1] - bssz1) S ) 
                     ./ sqrtG)

               where bsx[1] = U^-1 * bx[1] * U^-T, bsz0 = U' * bz0 * U, 
               bsz1 = U' * bz1 * U, bssz1 = S^-1 * bsz1 * S^-1 

            2. Solve for usx[1]:

               usx[1] + S * usx[1] * S 
                   = S * ( As(usx[0]) + bsx[1] - bsz0 ) * S - bsz1 

               usx[1] 
                   = ( S * (As(usx[0]) + bsx[1] - bsz0) * S - bsz1) ./ Gamma
                   = -bsz0 + (S * As(usx[0]) * S) ./ Gamma
                     + (bsz0 - bsz1 + S * bsx[1] * S ) . / Gamma
                   = -bsz0 + (S * As(usx[0]) * S) ./ Gamma
                     + (bsz0 + S * ( bsx[1] - bssz1 ) * S ) . / Gamma

               Unscale ux[1] = Uti * usx[1] * Uti'

            3. Compute usz0, usz1

               r0' * uz0 * r0 = r0^-1 * ( A(ux[0]) - ux[1] - bz0 ) * r0^-T
               r1' * uz1 * r1 = r1^-1 * ( -ux[1] - bz1 ) * r1^-T

            """

            # z0 := U' * z0 * U 
            #     = bsz0
            __cngrnc(U, z, trans = 'T')

            # z1 := Us' * bz1 * Us 
            #     = S^-1 * U' * bz1 * U * S^-1
            #     = S^-1 * bsz1 * S^-1
            __cngrnc(Us, z, trans = 'T', offsetx = msq)

            # x[1] := Uti' * x[1] * Uti 
            #       = bsx[1]
            __cngrnc(Uti, x[1], trans = 'T')
        
            # x[1] := x[1] - z[msq:] 
            #       = bsx[1] - S^-1 * bsz1 * S^-1
            blas.axpy(z, x[1], alpha = -1.0, offsetx = msq)


            # x1 = (S * x[1] * S + z[:msq] ) ./ sqrtG
            #    = (S * ( bsx[1] - S^-1 * bsz1 * S^-1) * S + bsz0 ) ./ sqrtG
            #    = (S * bsx[1] * S - bsz1 + bsz0 ) ./ sqrtG
            # in packed storage
            blas.copy(x[1], x1)
            blas.tbmv(S, x1, n = msq, k = 0, ldA = 1)
            blas.axpy(z, x1, n = msq)
            blas.tbsv(sqrtG, x1, n = msq, k = 0, ldA = 1)
            misc.pack2(x1, {'l': 0, 'q': [], 's': [m]})

            # x[0] := x[0] + Asc'*x1 
            #       = bx0 + Asc'( ( bsz0 - bsz1 + S * bsx[1] * S ) ./ sqrtG)
            #       = bx0 + As'( ( bz0 - bz1 + S * bx[1] * S ) ./ Gamma )
            blas.gemv(Asc, x1, x[0], m = mpckd, trans = 'T', beta = 1.0)

            # x[0] := H^-1 * x[0]
            #       = ux[0]
            lapack.potrs(H, x[0])


            # x1 = Asc(x[0]) .* sqrtG  (unpacked)
            #    = As(x[0])  
            blas.gemv(Asc, x[0], tmp, m = mpckd)
            misc.unpack(tmp, x1, {'l': 0, 'q': [], 's': [m]})
            blas.tbmv(sqrtG, x1, n = msq, k = 0, ldA = 1)


            # usx[1] = (x1 + (x[1] - z[:msq])) ./ sqrtG**2 
            #        = (As(ux[0]) + bsx[1] - bsz0 - S^-1 * bsz1 * S^-1) 
            #           ./ Gamma

            # x[1] := x[1] - z[:msq] 
            #       = bsx[1] - bsz0 - S^-1 * bsz1 * S^-1
            blas.axpy(z, x[1], -1.0, n = msq)

            # x[1] := x[1] + x1
            #       = As(ux) + bsx[1] - bsz0 - S^-1 * bsz1 * S^-1 
            blas.axpy(x1, x[1])

            # x[1] := x[1] / Gammma
            #       = (As(ux) + bsx[1] - bsz0 + S^-1 * bsz1 * S^-1 ) / Gamma
            #       = S^-1 * usx[1] * S^-1
            blas.tbsv(Gamma, x[1], n = msq, k = 0, ldA = 1)
            

            # z[msq:] := r1' * U * (-z[msq:] - x[1]) * U * r1
            #         := -r1' * U * S^-1 * (bsz1 + ux[1]) * S^-1 *  U * r1
            #         := -r1' * uz1 * r1
            blas.axpy(x[1], z, n = msq, offsety = msq)
            blas.scal(-1.0, z, offset = msq)
            __cngrnc(U, z, offsetx = msq)
            __cngrnc(W['r'][1], z, trans = 'T', offsetx = msq)

            # x[1] :=  S * x[1] * S
            #       =  usx1 
            blas.tbmv(S, x[1], n = msq, k = 0, ldA = 1)

            # z[:msq] = r0' * U' * ( x1 - x[1] - z[:msq] ) * U * r0
            #         = r0' * U' * ( As(ux) - usx1 - bsz0 ) * U * r0
            #         = r0' * U' *  usz0 * U * r0
            #         = r0' * uz0 * r0
            blas.axpy(x1, z, -1.0, n = msq)
            blas.scal(-1.0, z, n = msq)
            blas.axpy(x[1], z, -1.0, n = msq)
            __cngrnc(U, z)
            __cngrnc(W['r'][0], z, trans = 'T')

            # x[1] := Uti * x[1] * Uti'
            #       = ux[1]
            __cngrnc(Uti, x[1])


        return solve
Esempio n. 37
0
def projected_inverse(L):
    """
    Supernodal multifrontal projected inverse. The routine computes the projected inverse

    .. math::
         Y = P(L^{-T}L^{-1}) 

    where :math:`L` is a Cholesky factor. On exit, the argument :math:`L` contains the
    projected inverse :math:`Y`.

    :param L:                 :py:class:`cspmatrix` (factor)
    """

    assert isinstance(L, cspmatrix) and L.is_factor is True, "L must be a cspmatrix factor"

    n = L.symb.n
    snpost = L.symb.snpost
    snptr = L.symb.snptr
    chptr = L.symb.chptr
    chidx = L.symb.chidx

    relptr = L.symb.relptr
    relidx = L.symb.relidx
    blkptr = L.symb.blkptr
    blkval = L.blkval

    stack = []

    for k in reversed(list(snpost)):

        nn = snptr[k+1]-snptr[k]       # |Nk|
        na = relptr[k+1]-relptr[k]     # |Ak|
        nj = na + nn

        # invert factor of D_{Nk,Nk}
        lapack.trtri(blkval, offsetA = blkptr[k], ldA = nj, n = nn)

        # zero-out strict upper triangular part of {Nj,Nj} block (just in case!)
        for i in range(1,nn): blas.scal(0.0, blkval, offset = blkptr[k] + nj*i, n = i)   

        # compute inv(D_{Nk,Nk}) (store in 1,1 block of F)
        F = matrix(0.0, (nj,nj))
        blas.syrk(blkval, F, trans = 'T', offsetA = blkptr[k], ldA = nj, n = nn, k = nn)   

        # if supernode k is not a root node:
        if na > 0:

            # copy "update matrix" to 2,2 block of F
            Vk = stack.pop()
            lapack.lacpy(Vk, F, ldB = nj, offsetB = nn*nj+nn, m = na, n = na, uplo = 'L')

            # compute S_{Ak,Nk} = -Vk*L_{Ak,Nk}; store in 2,1 block of F
            blas.symm(Vk, blkval, F, m = na, n = nn, offsetB = blkptr[k]+nn,\
                      ldB = nj, offsetC = nn, ldC = nj, alpha = -1.0)

            # compute S_nn = inv(D_{Nk,Nk}) - S_{Ak,Nk}'*L_{Ak,Nk}; store in 1,1 block of F
            blas.gemm(F, blkval, F, transA = 'T', m = nn, n = nn, k = na,\
                      offsetA = nn, alpha = -1.0, beta = 1.0,\
                      offsetB = blkptr[k]+nn, ldB = nj)

        # extract update matrices if supernode k has any children
        for ii in range(chptr[k],chptr[k+1]):
            i = chidx[ii]
            stack.append(frontal_get_update(F, relidx, relptr, i))

        # copy S_{Jk,Nk} (i.e., 1,1 and 2,1 blocks of F) to blkval
        lapack.lacpy(F, blkval, m = nj, n = nn, offsetB = blkptr[k], ldB = nj, uplo = 'L')

    L._is_factor = False

    return