Can you modify the following code so that it runs on cuda with numba. 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler

# Load the dataset
data = pd.read_excel('/home/pradyumnas/Desktop/Mahindra/Sem6/NN/CCPP/Data.ods', sheet_name=None)
full_data = pd.concat(data.values())

# Preprocess the data
X = full_data.iloc[:, :-1].values
y = full_data.iloc[:, -1].values

# Normalize the data
scaler_x = MinMaxScaler(feature_range=(-1, 1))
scaler_y = MinMaxScaler(feature_range=(-1, 1))
X_normalized = scaler_x.fit_transform(X)
y_normalized = scaler_y.fit_transform(y.reshape(-1, 1)).flatten()

# Split the data
X_train, X_temp, y_train, y_temp = train_test_split(X_normalized, y_normalized, test_size=0.28, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=(10/28), random_state=42)

# ANN architecture parameters
n_input = X_train.shape[1]
n_hidden = 10  # Example: 10 hidden nodes
n_output = 1
learning_rate = 0.001
lambda_reg = 0.01  # Regularization parameter

# Weight initialization
np.random.seed(42)
W1 = np.random.randn(n_input, n_hidden) * 0.1
b1 = np.zeros((1, n_hidden))
W2 = np.random.randn(n_hidden, n_output) * 0.1
b2 = np.zeros((1, n_output))

# Activation functions
def tanh(x):
    return np.tanh(x)

def tanh_derivative(x):
    return 1 - np.tanh(x)**2

def logistic(x):
    return 1 / (1 + np.exp(-x))

def logistic_derivative(x):
    return logistic(x) * (1 - logistic(x))

# Forward propagation
def forward_propagation(X, W1, b1, W2, b2):
    Z1 = np.dot(X, W1) + b1
    A1 = tanh(Z1)
    Z2 = np.dot(A1, W2) + b2
    A2 = logistic(Z2)
    return Z1, A1, Z2, A2

# Back-propagation
def back_propagation(X, y, Z1, A1, Z2, A2, W1, W2):
    m = y.size
    
    # Compute gradients for the output layer
    dZ2 = A2 - y.reshape(-1, 1)
    dW2 = np.dot(A1.T, dZ2) / m + lambda_reg * W2 / m
    db2 = np.sum(dZ2, axis=0, keepdims=True) / m
    
    # Compute gradients for the hidden layer
    dA1 = np.dot(dZ2, W2.T)
    dZ1 = dA1 * tanh_derivative(Z1)
    dW1 = np.dot(X.T, dZ1) / m + lambda_reg * W1 / m
    db1 = np.sum(dZ1, axis=0, keepdims=True) / m
    
    return dW1, db1, dW2, db2


# Initialize lists to store loss values for plotting
training_loss_history = []
validation_loss_history = []

# Training the ANN
for epoch in range(1000):  # Example: 1000 epochs
    Z1, A1, Z2, A2 = forward_propagation(X_train, W1, b1, W2, b2)
    dW1, db1, dW2, db2 = back_propagation(X_train, y_train, Z1, A1, Z2, A2, W1, W2)
    W1 -= learning_rate * dW1
    b1 -= learning_rate * db1
    W2 -= learning_rate * dW2
    b2 -= learning_rate * db2

    # Calculate training loss
    training_loss = np.mean((A2 - y_train)**2)
    training_loss_history.append(training_loss)

    # Forward propagation for validation set
    Z1_val, A1_val, Z2_val, A2_val = forward_propagation(X_val, W1, b1, W2, b2)
    validation_loss = np.mean((A2_val - y_val)**2)
    validation_loss_history.append(validation_loss)

    # Print the training loss every 100 epochs
    if epoch % 100 == 0:
        print(f'Epoch {epoch}, Training Loss: {training_loss}, Validation Loss: {validation_loss}')

# Plotting the training and validation loss
plt.figure(figsize=(10, 5))
plt.plot(training_loss_history, label='Training Loss')
plt.plot(validation_loss_history, label='Validation Loss')
plt.title('Training and Validation Loss Over Epochs')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

# Calculate MAPE (Mean Absolute Percentage Error)
def calculate_mape(y_true, y_pred):
    return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

# Testing the ANN
Z1, A1, Z2, A2 = forward_propagation(X_test, W1, b1, W2, b2)
test_loss = np.mean((A2 - y_test)**2)
print(f'Test Loss: {test_loss}')

mape = calculate_mape(y_test, A2)
print(f'MAPE: {mape}%')