
import numpy as np
import matplotlib.pyplot as plt
import time
import random
import sklearn.svm as SVM
from sklearn.model_selection import RepeatedKFold
import cvxopt


def data(N):
#     w = np.random.uniform(-1, 1, (1, 2))
#     b = np.random.uniform(-1, 1)
    #x = np.random.uniform(0, 10, (N, 2))
    mean1 = (-2, -2)
    cov1 = [[0.5, 0], [0, 0.5]]
    size = N // 2
    mean2 = (2, 2)
    x1 = np.random.multivariate_normal(mean1, cov1, size)
    x2 = np.random.multivariate_normal(mean2, cov1, size)
    x = np.vstack((x1, x2))
    
    
    #cov2 = [[0.5, 0], [0, 0.5]]
    #print(x)
    #数据归一化
#     u = np.mean(x, axis=0)
#     v = np.std(x, axis=0)
#     x = (x - u) / v
#     w = 0
#     b = 0
#     z = np.sum(w * x, axis = 1) + b
    y1 = np.ones([size, 1])
    y2 = np.ones([size, 1]) * (-1)
    y = np.vstack((y1, y2))


    # z = np.random.uniform(-1, 1, [N, 1])
    # z = z.flatten()
    # y = np.ones([N, 1])
    # y[z < 0] = -1
    # x1 = x[z >= 0, 0]
    # x2 = x[z < 0, 0]
    # y1 = x[z >= 0, 1]
    # y2 = x[z < 0, 1]


    plt.scatter(x1[:,0], x1[:,1], color='blue', marker='o', label='Positive')
    plt.scatter(x2[:,0], x2[:,1], color='red', marker='x', label='Negative')
    plt.legend(loc = 'upper right')
    plt.show()


    return x, y, x1, y1, x2, y2


def ndata(N):
#     w = np.random.uniform(-1, 1, (1, 2))
#     b = np.random.uniform(-1, 1)
    #x = np.random.uniform(0, 10, (N, 2))
    mean1 = (-1, -1)
    cov1 = [[0.5, 0], [0, 0.5]]
    cov2 = [[1, 0], [0, 1]]
    size = N // 2
    mean2 = (1, 1)
    x1 = np.random.multivariate_normal(mean1, cov1, size)
    x2 = np.random.multivariate_normal(mean2, cov2, size)
    x = np.vstack((x1, x2))
    
    #cov2 = [[0.5, 0], [0, 0.5]]
    #print(x)
    #数据归一化
#     u = np.mean(x, axis=0)
#     v = np.std(x, axis=0)
#     x = (x - u) / v
#     w = 0
#     b = 0
#     z = np.sum(w * x, axis = 1) + b
    y1 = np.ones([size, 1])
    y2 = np.ones([size, 1]) * (-1)
    y = np.vstack((y1, y2))


    # z = np.random.uniform(-1, 1, [N, 1])
    # z = z.flatten()
    # y = np.ones([N, 1])
    # y[z < 0] = -1
    # x1 = x[z >= 0, 0]
    # x2 = x[z < 0, 0]
    # y1 = x[z >= 0, 1]
    # y2 = x[z < 0, 1]


    plt.scatter(x1[:,0], x1[:,1], color='blue', marker='o', label='Positive')
    plt.scatter(x2[:,0], x2[:,1], color='red', marker='x', label='Negative')
    plt.legend(loc = 'upper right')
    plt.show()


    return x, y, x1, y1, x2, y2


# 绘制：决策边界、支撑向量所在的直线
def plot_svc_decision_boundary(x1, x2, y1, y2, w, b):
    v = 1.5
    x3 = v
    y3 = (1 - x3 * w[0] -b) / w[1]
    x4 = -1 * v
    y4 = (1 - x4 * w[0] -b) / w[1]


    x5 = v
    y5 = (-1 - x5 * w[0] -b) / w[1]
    x6 = -1 * v
    y6 = (-1 - x6 * w[0] -b) / w[1]
    
    x7 = v
    y7 = (0 - x7 * w[0] -b) / w[1]
    x8 = -1 * v
    y8 = (0 - x8 * w[0] -b) / w[1]
    # 作图
    plt.scatter(x1[:,0], x1[:,1], color='blue', marker='o', label='Positive')
    plt.scatter(x2[:,0], x2[:,1], color='red', marker='x', label='Negative')
    plt.plot([x3,x4], [y3,y4], 'k')
    plt.plot([x5,x6], [y5,y6], 'k')
    plt.plot([x7,x8], [y7,y8], 'k')
    plt.legend(loc = 'upper right')
    plt.show()


def sklearn_svm(x, y, x1, y1, x2, y2):
    y = y.ravel()
    clf = SVM.LinearSVC(C=10**9, class_weight=None, dual=True, fit_intercept=True,
        intercept_scaling=1, loss='squared_hinge', max_iter=1000,
        multi_class='ovr', penalty='l2', random_state=None, tol=0.0001,
        verbose=0)
    clf.fit(x, y)
    w = clf.coef_[0]
    b = clf.intercept_[0]
    print(w)
    print(b)
    plot_svc_decision_boundary(x1, x2, y1, y2, w, b)
    
def sklearn_n_svm(X, Y, train_index, test_index, C):
    x_train = X[train_index]
    y_train = Y[train_index]
    x_test = X[test_index]
    y_test = Y[test_index]
    
    clf = SVM.SVC(C, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True,
        probability=False, tol=0.001, cache_size=200, class_weight=None,
        verbose=False, max_iter=-1, decision_function_shape='ovr',
        random_state=None)
    clf.fit(x_train, y_train.ravel())
    accu = clf.score(x_test, y_test)
    print('sklearn正确率：', accu)
    return accu


def cvx_svm(x, y, x1, y1, x2, y2, N):
    K = np.zeros((N, N))
    for i in range(N):
        for j in range(N):
            K[i,j] = np.dot(x[i], x[j])
    p = cvxopt.matrix(np.outer(y, y) * K)
    Q = cvxopt.matrix(np.ones(N) * -1)
    #print(Q)
    G = cvxopt.matrix(np.identity(N) * -1)
    h = cvxopt.matrix(np.zeros(N))
    #print(h)
    A = cvxopt.matrix(y, (1, N))
    b = cvxopt.matrix(0.0)
    sol = cvxopt.solvers.qp(p, Q, G, h, A, b)
    a = np.ravel(sol['x'])
    #print(a)
    w = 0.0
    k = 0.0
    for i in range(N):
        w += a[i] * y[i] * x[i,:]
    for i in range(N):
        if a[i] > 1e-5:
            for j in range(N):
                k += a[j] * y[j] * np.sum(x[i] * x[j])
            b = y[i] - k           
            break
    print(w)
    print(b)
    plot_svc_decision_boundary(x1, x2, y1, y2, w, b)


def cvx_nsvm(X, Y, train_index, C):
    N = len(train_index)
    x = X[train_index]
    y = Y[train_index]
    K = np.zeros((N, N))
    for i in range(N):
        for j in range(N):
            K[i,j] = np.dot(x[i], x[j])
    p = cvxopt.matrix(np.outer(y, y) * K)
    Q = cvxopt.matrix(np.ones(N) * -1)
    #print(Q)
    G = cvxopt.matrix(np.vstack((np.identity(N), np.identity(N) * -1)))
    #print(G)
    h = cvxopt.matrix(np.hstack((np.ones(N) * C, np.zeros(N))))
    #print(h)
    A = cvxopt.matrix(y, (1, N))
    b = cvxopt.matrix(0.0)
    sol = cvxopt.solvers.qp(p, Q, G, h, A, b)
    a = np.ravel(sol['x'])
    #print(a)
    w = 0.0
    k = 0.0
    for i in range(N):
        w += a[i] * y[i] * x[i,:]
    for i in range(N):
        if a[i] > 1e-5 and a[i] < C - 1e-5:
            for j in range(N):
                k += a[j] * y[j] * np.sum(x[i] * x[j])
            b = y[i] - k           
            break
    print(w)
    print(b)


    #plot_svc_decision_boundary(x1, x2, y1, y2, w, b)
    return w, b


def svm_test(X, Y, test_index, w, b):
    N = len(test_index)
    x = X[test_index]
    y = Y[test_index]
    z = np.sum(w * x, axis = 1) + b
    tmp = np.ones_like(z)
    tmp[z < 0] = -1
    
    count = np.sum(tmp == y.flatten())
    accu = count / N
    print('self正确率：', accu)
    return accu
    
if __name__ == "__main__":
    N = 100
    C = 1.0
    x, y, x1, y1, x2, y2 = ndata(N)
    #sklearn_svm(x, y, x1, y1, x2, y2)
    #sklearn_n_svm(x, y, x1, y1, x2, y2, C)
    #cvx_svm(x, y, x1, y1, x2, y2, N)
    #w, b = cvx_nsvm(x, y, x1, y1, x2, y2, N, C)
    #svm_test(x1, x2, y1, y2, w, b)
    
    kf = RepeatedKFold(n_splits=5, n_repeats=5, random_state=0)
    for train_index, test_index in kf.split(x):
        #print('train_index', train_index, 'test_index', test_index)
        sklearn_n_svm(x, y, train_index, test_index, C)
        w, b = cvx_nsvm(x, y, train_index, C)
        svm_test(x, y, test_index, w, b)
