from numpy import *

def activation_sigmoid(x, derivative = False):
    '''Activation Function - Sigmoid'''
    if(derivative == True):
        return x*(1-x)
    return 1/(1+exp(-x))

# Input dataset array
# Assumed logic - if middle bit is 1 output is 1 else output is 0
inputs = array([[0,0,1],
               [1,0,1],
               [1,1,1],
               [1,1,0]])

# Output dataset for the above inputs
outputs = array([[0,0,1,1]]).T

# Seeding the random numbers
random.seed(1)

# First and only Synapse
# Initilizing the weights randomly
synapse1 = 2 * random.random((3,1))-1
# (3,1) because we have 3 input features for each output

# Training begins here
for Try in range(20000):
    # First layer (l1) is the input layer
    # Output layer is the second layer(l2)

    # Initilizing input layer with previous inputs
    # Forward Propagation
    l1 = inputs

    # Calculating the outputs generated by the neural net
    l2 = activation_sigmoid(dot(l1,synapse1))

    # Calculating error for l2 (Only one error since there is only one output layer)
    # Error = Actual outputs - Calculated outputs
    l2_error = outputs - l2

    # Delta calculation for modification of weights (Elements of Synapse))
    l2_delta = l2_error * activation_sigmoid(l2,True)

    # Updating Weights
    # Gradient Descent Algorithm
    synapse1 += dot(l1.T,l2_delta)

print("Output layer after training : ")
print(l2)

# Check for a new condition which is not present in the input dataset [0,1,0]
print("Input: [0,1,0]")
print("Output : ", end="")
print(round(float(activation_sigmoid(dot(array([[0,1,0]]),synapse1)))))
