def classifier2(Y, soft=False):

            M = Y.size[0]

            # K = Y*X' / sigma
            K = matrix(theta, (width, M))
            blas.gemm(X,
                      Y,
                      K,
                      transB='T',
                      alpha=1.0 / sigma,
                      beta=-1.0,
                      m=width)

            K = exp(K)
            K = div(K - K**-1, K + K**-1)

            # complete K
            lapack.potrs(L11, K)
            K = matrix([K, matrix(0., (N - width, M))], (N, M))
            chompack.solve(Lc, K, mode=0)
            chompack.solve(Lc, K, mode=1)

            x = matrix(b, (M, 1))
            blas.gemv(K, z, x, trans='T', beta=1.0)

            if soft: return x
            else: return matrix([2 * (xk > 0.0) - 1 for xk in x])
        def classifier2(Y, soft=False):

            M = Y.size[0]

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

            # c[i] = ||Yi||^2 / sigma
            ones = matrix(1.0, (max(width, n, M), 1))
            c = Y**2 * ones[:n]
            blas.scal(1.0 / sigma, c)

            # Kij := Kij - 0.5 * (ci + aj)
            #      = || yi - xj ||^2 / (2*sigma)
            blas.ger(ones[:width], c, K, alpha=-0.5)
            blas.ger(a[:width], ones[:M], K, alpha=-0.5)
            # Kij = exp(Kij)
            K = exp(K)

            # complete K
            lapack.potrs(L11, K)
            K = matrix([K, matrix(0., (N - width, M))], (N, M))
            chompack.solve(Lc, K, mode=0)
            chompack.solve(Lc, K, mode=1)

            x = matrix(b, (M, 1))
            blas.gemv(K, z, x, trans='T', beta=1.0)

            if soft: return x
            else: return matrix([2 * (xk > 0.0) - 1 for xk in x])
        def classifier2(Y, soft=False):
            M = Y.size[0]
            W = matrix(0., (width, M))
            blas.gemm(X, Y, W, transB='T', alpha=1.0 / sigma, m=width)
            lapack.potrs(L11, W)
            W = matrix([W, matrix(0., (N - width, M))])
            chompack.solve(Lc, W, mode=0)
            chompack.solve(Lc, W, mode=1)

            x = matrix(b, (M, 1))
            blas.gemv(W, z, x, trans='T', beta=1.0)
            if soft: return x
            else: return matrix([2 * (xk > 0.0) - 1 for xk in x])
        def g(x, y, z):

            # Solve
            #
            #     [ K    d3    ] [ ux_y ]
            #     [            ] [      ] =
            #     [ d3'  1'*d3 ] [ ux_b ]
            #
            #         [ bx_y ]   [ D  ]
            #         [      ] - [    ] * D3 * (D2 * bx_v + bx_z - bx_w).
            #         [ bx_b ]   [ d' ]

            x[:N] -= mul(d, mul(d3, mul(d2, x[-N:]) + z[:N] - z[-N:]))
            x[N] -= blas.dot(d, mul(d3, mul(d2, x[-N:]) + z[:N] - z[-N:]))

            # Solve dy1 := K^-1 * x[:N]
            blas.copy(x, dy1, n=N)
            chompack.solve(L, dy1, mode=0)
            chompack.solve(L, dy1, mode=1)

            # Find ux_y = dy1 - ux_b * dy2 s.t
            #
            #     d3' * ( dy1 - ux_b * dy2 + ux_b ) = x[N]
            #
            # i.e.  x[N] := ( x[N] - d3'* dy1 ) / ( d3'* ( 1 - dy2 ) ).

            x[N] = ( x[N] - blas.dot(d3, dy1) ) / \
                ( blas.asum(d3) - blas.dot(d3, dy2) )
            x[:N] = dy1 - x[N] * dy2

            # ux_v = D4 * ( bx_v -  D1^-1 (bz_z + D * (ux_y + ux_b))
            #     - D2^-1 * bz_w )

            x[-N:] = mul(
                d4, x[-N:] - div(z[:N] + mul(d, x[:N] + x[N]), d1) -
                div(z[N:], d2))

            # uz_z = - D1^-1 * ( bx_z - D * ( ux_y + ux_b ) - ux_v )
            # uz_w = - D2^-1 * ( bx_w - uz_w )
            z[:N] += base.mul(d, x[:N] + x[N]) + x[-N:]
            z[-N:] += x[-N:]
            blas.scal(-1.0, z)

            # Return W['di'] * uz
            blas.tbmv(W['di'], z, n=2 * N, k=0, ldA=1)
    def Fkkt(W):
        """
        Custom KKT solver for

            [  Qinv  0   0  -D    0  ] [ ux_y ]   [ bx_y ]
            [  0     0   0  -d'   0  ] [ ux_b ]   [ bx_b ]
            [  0     0   0  -I   -I  ] [ ux_v ] = [ bx_v ]
            [ -D    -d  -I  -D1   0  ] [ uz_z ]   [ bz_z ]
            [  0     0  -I   0   -D2 ] [ uz_w ]   [ bz_w ]

        with D1 = diag(d1), D2 = diag(d2), d1 = W['d'][:N]**2, 
        d2 = W['d'][N:])**2.
        """

        d1, d2 = W['d'][:N]**2, W['d'][N:]**2
        d3, d4 = (d1 + d2)**-1, (d1**-1 + d2**-1)**-1

        # Factor the chordal matrix K = Qinv + (D_1+D_2)^-1.
        K.V = Qinv.V
        K[::N + 1] = K[::N + 1] + d3
        L = chompack.project(Qc, K)
        L = chompack.cholesky(L)

        # Solve (Qinv + (D1+D2)^-1) * dy2 = (D1 + D2)^{-1} * 1
        blas.copy(d3, dy2)
        chompack.solve(L, dy2, mode=0)
        chompack.solve(L, dy2, mode=1)

        def g(x, y, z):

            # Solve
            #
            #     [ K    d3    ] [ ux_y ]
            #     [            ] [      ] =
            #     [ d3'  1'*d3 ] [ ux_b ]
            #
            #         [ bx_y ]   [ D  ]
            #         [      ] - [    ] * D3 * (D2 * bx_v + bx_z - bx_w).
            #         [ bx_b ]   [ d' ]

            x[:N] -= mul(d, mul(d3, mul(d2, x[-N:]) + z[:N] - z[-N:]))
            x[N] -= blas.dot(d, mul(d3, mul(d2, x[-N:]) + z[:N] - z[-N:]))

            # Solve dy1 := K^-1 * x[:N]
            blas.copy(x, dy1, n=N)
            chompack.solve(L, dy1, mode=0)
            chompack.solve(L, dy1, mode=1)

            # Find ux_y = dy1 - ux_b * dy2 s.t
            #
            #     d3' * ( dy1 - ux_b * dy2 + ux_b ) = x[N]
            #
            # i.e.  x[N] := ( x[N] - d3'* dy1 ) / ( d3'* ( 1 - dy2 ) ).

            x[N] = ( x[N] - blas.dot(d3, dy1) ) / \
                ( blas.asum(d3) - blas.dot(d3, dy2) )
            x[:N] = dy1 - x[N] * dy2

            # ux_v = D4 * ( bx_v -  D1^-1 (bz_z + D * (ux_y + ux_b))
            #     - D2^-1 * bz_w )

            x[-N:] = mul(
                d4, x[-N:] - div(z[:N] + mul(d, x[:N] + x[N]), d1) -
                div(z[N:], d2))

            # uz_z = - D1^-1 * ( bx_z - D * ( ux_y + ux_b ) - ux_v )
            # uz_w = - D2^-1 * ( bx_w - uz_w )
            z[:N] += base.mul(d, x[:N] + x[N]) + x[-N:]
            z[-N:] += x[-N:]
            blas.scal(-1.0, z)

            # Return W['di'] * uz
            blas.tbmv(W['di'], z, n=2 * N, k=0, ldA=1)

        return g