# Linear classifier - Monte-Karlo

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets


POINT_N = 300
DIM_N = 2
CLUST_N = 2

data, target, Center = datasets.make_blobs(n_samples=POINT_N, centers=CLUST_N, cluster_std=[0.5,1.5], n_features=DIM_N, center_box=(-10,10), random_state=0, return_centers=True)

slope = (Center[1][1]-Center[0][1])/(Center[1][0]-Center[0][0])
C0 = [(Center[0][0]+Center[1][0])/2, (Center[0][1]+Center[1][1])/2]


print("w1=", Center[1][0]-Center[0][0])
print("w2=", Center[1][1]-Center[0][1])
print("b=", -(Center[1][0]-Center[0][0])*C0[0] - (Center[1][1]-Center[0][1])*C0[1])

plt.figure(figsize=(7,9))
for i in range(POINT_N):
    if target[i]:
        plt.scatter(data[i][0], data[i][1], c='blue', marker='o')
    else:
        plt.scatter(data[i][0], data[i][1], c='green', marker='o')
plt.scatter(Center[:,0], Center[:,1], c='red', marker="*", s=100)

plt.axline((C0[0], C0[1]), slope=-1.0/slope, color="blue", linestyle=(0, (5, 5)))
#plt.savefig('Pic1.png')
plt.show()

#----------------------------------------------------------
# network training
# indicator function

print('\nMonte Carlo - indicator')

class NNET1:
    def __init__(self):
        self.input_nodes = DIM_N
        self.weights_input_to_output = np.random.rand(self.input_nodes)
        self.output_bias = 0.0
    def activation_function(self, x):
        return (1 if x>0 else 0)
    def run(self, features):
        input_output = np.dot(features, self.weights_input_to_output)
        return self.activation_function(input_output+self.output_bias)
    def dev(self, features, dw, db):      # deviation
        input_output = np.dot(features, (self.weights_input_to_output+dw))
        return self.activation_function(input_output+self.output_bias+db)

network = NNET1()

a0 = 0.0
pred = []
for i in range(1000):
    dw = np.random.normal( 0.0, network.input_nodes**-0.5, (network.input_nodes) )
    db = np.random.normal( 0.0, 1.0)
    pred.clear()
    a = 0.0
    for n in range(POINT_N):
        pred.append( network.dev(data[n], dw, db) )
        a += (1.0 if target[n]==pred[n] else 0.0)
    a /= POINT_N
    if a>a0:
        network.weights_input_to_output += dw
        network.output_bias += db
        a0 = a

pred = []
for n in range(POINT_N):
    res = network.run(data[n])
    pred.append(res)

print(1.0*sum(pred==target)/POINT_N)
print(network.weights_input_to_output, network.output_bias)

plt.figure(figsize=(7,9))
for i in range(POINT_N):
    if pred[i]:
        plt.scatter(data[i][0], data[i][1], c='blue', marker='o')
    else:
        plt.scatter(data[i][0], data[i][1], c='green', marker='o')
plt.scatter(Center[:,0], Center[:,1], c='red', marker="*", s=100)


slope = network.weights_input_to_output[1] / network.weights_input_to_output[0]
C0 = [0, -network.output_bias/network.weights_input_to_output[1]]

plt.axline((C0[0], C0[1]), slope=-1.0/slope, color="blue", linestyle=(0, (5, 5)))
#plt.savefig('Pic2.png')
plt.show()
