# -*- coding: utf-8 -*-
"""P7-1-ANN Backpropagation algorithm.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1m_PxuQ8-4tq235R3C2j8vSIODh7TXBmq
"""

import numpy as np

X = np.array(([2, 9], [1, 5], [3, 6]), dtype=float)#two inputs [sleep,study]
y = np.array(([92], [86], [89]), dtype=float)#one output expected perecntage in exam
X = X/np.amax(X,axis=0) #maximum of X array longitudinally
y = y/100 #Convert to fraction

#Variable initialization
epoch=5 #Setting training iterations
lr=0.1 #Setting learning rate
inputlayer_neurons = 2 #number of features in data set
hiddenlayer_neurons = 3 #number of hidden layers neurons
output_neurons = 1 #number of neurons at output layer

#weight and bias initialization
wh=np.random.uniform(size=(inputlayer_neurons,hiddenlayer_neurons))
bh=np.random.uniform(size=(1,hiddenlayer_neurons))
wout=np.random.uniform(size=(hiddenlayer_neurons,output_neurons))
bout=np.random.uniform(size=(1,output_neurons))

#Sigmoid Function
def sigmoid (x):
    return 1/(1 + np.exp(-x))

#Derivative of Sigmoid Function
def derivatives_sigmoid(x):
    return x * (1 - x)

#draws a random range of numbers uniformly of dim x*y
for i in range(epoch):
    #Forward Propogation
    hinp1=np.dot(X,wh)
    hinp=hinp1 + bh
    hlayer_act = sigmoid(hinp)
    outinp1=np.dot(hlayer_act,wout)
    outinp= outinp1+bout
    output = sigmoid(outinp)
    #Backpropagation
    EO = y-output
    outgrad = derivatives_sigmoid(output)
    d_output = EO * outgrad
    EH = d_output.dot(wout.T)
    hiddengrad = derivatives_sigmoid(hlayer_act)#how much hidden layer wts contributed to error
    d_hiddenlayer = EH * hiddengrad

    wout += hlayer_act.T.dot(d_output) *lr   # dotproduct of nextlayererror and currentlayerop
    wh += X.T.dot(d_hiddenlayer) *lr

    print ("-----------Epoch-", i+1, "Starts----------")
    print("Input: \n" + str(X))
    print("Actual Output: \n" + str(y))
    print("Predicted Output: \n" ,output)
    print ("-----------Epoch-", i+1, "Ends----------\n")

print("Input: \n" + str(X))
print("Actual Output: \n" + str(y))
print("Predicted Output: \n" ,output)

import numpy as np

# Input (Sleep, Study hours) and Output (Exam %)
X = np.array([[2, 9], [1, 5], [3, 6]], dtype=float)
y = np.array([[92], [86], [89]], dtype=float)

# Normalize input and output
X = X / np.max(X, axis=0)
y = y / 100

# Hyperparameters
epochs = 5
lr = 0.1
input_neurons, hidden_neurons, output_neurons = 2, 3, 1

# Weight & Bias initialization
wh = np.random.rand(input_neurons, hidden_neurons)
bh = np.random.rand(1, hidden_neurons)
wout = np.random.rand(hidden_neurons, output_neurons)
bout = np.random.rand(1, output_neurons)

# Activation functions
def sigmoid(x): return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x): return x * (1 - x)

# Training loop
for epoch in range(epochs):
    # Forward Propagation
    hidden_input = np.dot(X, wh) + bh
    hidden_output = sigmoid(hidden_input)
    final_input = np.dot(hidden_output, wout) + bout
    output = sigmoid(final_input)

    # Backpropagation
    error = y - output
    d_output = error * sigmoid_derivative(output)
    d_hidden = d_output.dot(wout.T) * sigmoid_derivative(hidden_output)

    # Weight & bias updates
    wout += hidden_output.T.dot(d_output) * lr
    wh += X.T.dot(d_hidden) * lr

    # Display progress
    print(f"\n---- Epoch {epoch+1} ----")
    print("Predicted Output:\n", output)

# Final results
print("\nFinal Predicted Output:\n", output)

