# -*- coding: utf-8 -*-
"""h4_2.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1OM6DjnR0yQBTljAjxNZRpbzCU7tS6Pv6
"""

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from numpy import shape
from numpy import mat
from numpy import arange
from matplotlib.colors import ListedColormap

"""Q1.Generate two clusters of data points with 100 points each, by sampling from Gaussian distributions centered at (0.5, 0.5) and (−0.5, −0.5)."""

cmap_bold = ListedColormap(['darkorange', 'c'])
standard_deviation = [[0.1,0],[0,0.1]]

mean1 = [0.5,0.5]
data1 = np.random.multivariate_normal(mean1, standard_deviation,100)
mean2 = [-0.5,-0.5]
data2 = np.random.multivariate_normal(mean2, standard_deviation, 100)

plt.ylim(-1.0, 1.0)
plt.xlim(-1.0, 1.0)

plt.scatter(data1[:, 0], data1[:, 1], c='blue')
plt.scatter(data2[:, 0], data2[:, 1], c='red')
plt.show()

"""Q2.Implement the Perceptron algorithm as discussed in class. Choose the initial weights to be zero and the maximum number of epochs as T = 100, and the learning rate α = 1. How quickly does your implementation converge?"""

# add label to data1, data2
d1 = np.insert(data1, 2, values=1, axis=1)
d2 = np.insert(data2, 2, values=-1, axis=1)
data = np.vstack((d1, d2))
X = data[:, :-1] # except last col
Y = data[:, -1] # last col

# split data into 4 parts
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30, random_state=43)
# print(X_test[0].shape)
# print(Y_test.shape)

# initial w to 1 row 2 cols [w1, w2]
W = np.zeros((1, 2))

def perceptron(W, X_train, X_test, Y_train, Y_test, epochs, learning_rate):
  # loop and compare
  for epoch in range(epochs):
    for idx in range(len(X_train)):
      # update weights if the prediction is wrong
      # W [1,2] * [2,1] we need to use np.dot to do matrix calculation but not *
      if (np.dot(np.sign(np.dot(W, X_train[idx])), Y_train[idx])) <= 0:
          W = W + np.dot(learning_rate, np.dot(Y_train[idx], X_train[idx].T))

    if epoch % 10 == 0:
      Y_test_pred = np.dot(X_test, W.T)
      acc = 0
      for _ in range(len(Y_test)):
        if Y_test[_] == np.sign(Y_test_pred[_]):
          acc = acc + 1
      f_acc = acc / len(Y_test)
      print(f"Epoch: {epoch}, Test Accuracy: {f_acc}")

perceptron(W, X_train, X_test, Y_train, Y_test, 100, 1)

"""according to the result, the converge speed is very fast. (The dataset has low variance)

Q3. Now, repeat the above experiment with a second synthetic dataset; this time, increase the variance (radius) of the two Gaussians such that the generated data points from different classes now overlap. What happens to the behavior of the algorithm? Does it converge? Show the classification regions obtained at the end of T epochs.
"""

standard_deviation = [[0.5,0],[0,0.5]]

mean1 = [0.5,0.5]
data1 = np.random.multivariate_normal(mean1, standard_deviation,100)
mean2 = [-0.5,-0.5]
data2 = np.random.multivariate_normal(mean2, standard_deviation, 100)

plt.ylim(-1.0, 1.0)
plt.xlim(-1.0, 1.0)

plt.scatter(data1[:, 0], data1[:, 1], c='blue')
plt.scatter(data2[:, 0], data2[:, 1], c='red')
plt.show()

# add label to data1, data2
d1 = np.insert(data1, 2, values=1, axis=1)
d2 = np.insert(data2, 2, values=-1, axis=1)
data = np.vstack((d1, d2))
X = data[:, :-1] # except last col
Y = data[:, -1] # last col

# split data into 4 parts
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30, random_state=43)
W = np.zeros((1, 2))

perceptron(W, X_train, X_test, Y_train, Y_test, 100, 1)

"""according to the result, we can find that when the dataset has a lot of noise, it is more difficult to converge."""