import pymysql as my
import torch
import os.path
import torch.nn as nn

Batch_Size = 50
Input_Neurons = 20

# ------------------------------------------------------------- Bereich 1: Erstellen der X und Y Tensoren aus der Datenbank

# Leere Tensoren erstellen, weil er sonst undefiniert Meldet
Stacked_Input = torch.ones(Input_Neurons)
Stacked_Output = torch.ones(1)

db = my.connect(host="localhost", user="daxminute", passwd="daxminute", db="daxminute")

for Row in range(Batch_Size):
cursorInput = db.cursor()
sqlInput = "SELECT daxsteigung FROM daxminute LIMIT "+str(Row)+","+str(Input_Neurons)+""
print(cursorInput.execute(sqlInput))

Input = torch.tensor(cursorInput.fetchall()) # Unter 1 = negative Steigung
Input = Input - 0.5 # uter 0,5 = negative Steigung
Input = Input.flatten()
#print(Input)

Stacked_Input = torch.cat([Stacked_Input,Input],0)

cursorOutput = db.cursor()
sqlOutput = "SELECT daxsteigung FROM daxminute LIMIT "+str(Row+Input_Neurons)+",1" #Output_Neurons immer = 1
print(cursorOutput.execute(sqlOutput))

Output = torch.tensor(cursorOutput.fetchall())
Output = Output - 0.5
Output = Output.flatten()
#print(Output)

Stacked_Output = torch.cat([Stacked_Output, Output], 0) # fügt aktuellen tensor hinzu

Stacked_Input = Stacked_Input.reshape(Batch_Size+1,Input_Neurons)
Stacked_Input = Stacked_Input.narrow(0,1,Batch_Size) #Löscht die erste Zeile Dimension 0
#print(Stacked_Input)

Stacked_Output = Stacked_Output.reshape(Batch_Size+1,1)
Stacked_Output = Stacked_Output.narrow(0,1,Batch_Size)
#print(Stacked_Output)

x = Stacked_Input
y = Stacked_Output







# ------------------------------------------------------------- Bereich 2: Neuronales Netz durchlaufen

# Diverse Vorgaben zum Neuronalen Netz
Number_Input_Neurons = Input_Neurons # Muss x Tensor Dimension entsprechen
Number_Hidden_Neurons = 10000 # Je mehr, desto genauer (aber zu viel bringt nichts)
Number_Output_Neurons = 1 # Muss y Tensor Dimension entsprechen
Batch_Size = Batch_Size
Learning_Rate = 0.01 # Muss individuell angepasst werden
Number_Epochs = 100 # Je höher, desto genauer
Model_Parameter_File = "20181108_daxminute_status.pt" # Status in diese Datei schreiben/lesen

# --------------- Hier kommen die Trainingsdaten rein

# Vorgabe der Training Input-Data x (random)
x = x
print("Input Tensor: ", x)

# Vorgabe der Training Output-Data y (random)
y = y
print("Output Tensor: ", y)



# --------------- Ab hier braucht man nichts mehr zu ändern !

# Neural Network Struktur vorgeben (input -> linear -> relu -> linear -> sigmoid)
model = nn.Sequential(
nn.Linear(Number_Input_Neurons, Number_Hidden_Neurons),
nn.ReLU(),
nn.Linear(Number_Hidden_Neurons, Number_Output_Neurons),
nn.Sigmoid()
)

# Lädt die gespeicherten Weights, wenn Datei vorhanden
#if os.path.isfile(Model_Parameter_File):
# model.load_state_dict(torch.load(Model_Parameter_File))

# Loss Function vorgeben (Mean Squared Error Loss)
criterion = torch.nn.MSELoss()

# Optimizer vorgeben (SGD Stochastic Gradient Descent)
optimizer = torch.optim.SGD(model.parameters(), lr = Learning_Rate)

# Das ist jetzt die eigentliche Ausführung des Prozesses
# This does the forward propagation, loss computation, backward propagation and parameter updation in that sequence
for epoch in range(Number_Epochs):
# Forward Propagation
y_predicted = model(x)

# Compute and print loss
loss = criterion(y_predicted, y)
print('epoch: ', epoch, ' loss: ', loss.item())

# Zero the gradients
optimizer.zero_grad()

# perform a backward pass (backpropagation)
loss.backward()

# Update the parameters
optimizer.step()

# --------------- Ab hier nur noch Auswertungen

# Beide Tensoren für Training und Prediction anzeigen
print("y training: ",y)
print("y predicted: ",y_predicted)

# Predicted Tensor runden, um auf 1 und 0 zu kommen
y_predicted_round = torch.round(y_predicted)
print("y predicted round: ",y_predicted_round)

# Vergleichs-Tensor aus den beiden Tensoren erstellen (1 = gleich, 0 = falsch) dann Durchschnitt ausgeben
y_predicted_equal = torch.eq(y, y_predicted_round)
y_predicted_equal = y_predicted_equal.double()
print("y predicted/training % OK: ",y_predicted_equal.mean())

# Alle Weights nach dem Training anzeigen
for param in model.parameters():
print("Alle weight Values: ",param.data)

# Speichert alle Model Parameter in eine Datei
torch.save(model.state_dict(), Model_Parameter_File)