# Path: lxmls/deep_learning/rnn.py
# class RNN(Model):
#     def __init__(self, **config):
# 
#         # CHECK THE PARAMETERS ARE VALID
#         self.sanity_checks(config)
# 
#         # OPTIONAL MODEL LOADING
#         model_folder = config.get('model_folder', None)
#         if model_folder is not None:
#             saved_config, loaded_parameters = self.load(model_folder)
#             # Note that if a config is given this is used instead of the saved
#             # one (must be consistent)
#             if config is None:
#                 config = saved_config
#         else:
#             loaded_parameters = None
# 
#         # Class variables
#         self.config = config
#         self.parameters = initialize_rnn_parameters(
#             config['input_size'],
#             config['embedding_size'],
#             config['hidden_size'],
#             config['output_size'],
#             loaded_parameters=loaded_parameters
#         )
# 
#     def sanity_checks(self, config):
# 
#         model_folder = config.get('model_folder', None)
# 
#         assert bool(config is None) or bool(model_folder is None), \
#             "Need to specify config, model_folder or both"
# 
#         if config is not None:
#             pass
# 
#         if model_folder is not None:
#             model_file = "%s/config.yml" % model_folder
#             assert os.path.isfile(model_file), "Need to provide %s" % model_file
# 
#     def load(self, model_folder):
#         """
#         Load model
#         """
# 
#         # Configuration un yaml format
#         config_file = "%s/config.yml" % model_folder
#         config = load_config(config_file)
# 
#         # Computation graph parameters as pickle file
#         parameter_file = "%s/parameters.pkl" % model_folder
#         loaded_parameters = load_parameters(parameter_file)
# 
#         return config, loaded_parameters
# 
#     def save(self, model_folder):
#         """
#         Save model
#         """
# 
#         # Create folder if it does not exist
#         if not os.path.isdir(model_folder):
#             os.mkdir(model_folder)
# 
#         # Configuration un yaml format
#         config_file = "%s/config.yml" % model_folder
#         save_config(config_file, self.config)
# 
#         # Computation graph parameters as pickle file
#         parameter_file = "%s/parameters.pkl" % model_folder
#         with open(parameter_file, 'wb') as fid:
#             pickle.dump(self.parameters, fid, pickle.HIGHEST_PROTOCOL)
# 
#     def plot_weights(self, show=True, aspect='auto'):
#         """
#         Plots the weights of the newtwork
# 
#         Use show = False to plot various models one after the other
#         """
#         import matplotlib.pyplot as plt
#         plt.figure()
#         for n in range(self.n_layers):
# 
#             # Get weights and bias
#             weight, bias = self.parameters[n]
# 
#             # Plot them
#             plt.subplot(2, self.n_layers, n+1)
#             plt.imshow(weight, aspect=aspect, interpolation='nearest')
#             plt.title('Layer %d Weight' % n)
#             plt.colorbar()
#             plt.subplot(2, self.n_layers, self.n_layers+(n+1))
#             plt.plot(bias)
#             plt.title('Layer %d Bias' % n)
#             plt.colorbar()
# 
#         if show:
#             plt.show()


import numpy as np
import scipy
import scipy.linalg
import torch
from torch.autograd import Variable
from torch.distributions import Categorical
from lxmls.deep_learning.rnn import RNN
from itertools import chain

    def backpropagation(self, input, output):
        """
        Computes the gradients of the network with respect to cross entropy
        error cost
        """

        # Ensure the type matches torch type
        output = Variable(torch.from_numpy(output).long())

        # Zero gradients
        for parameter in self.parameters:
            if parameter.grad is not None:
                parameter.grad.data.zero_()

        # Compute negative log-likelihood loss
        log_p_y = self._log_forward(input)
        cost = self.loss(log_p_y, output)
        # Use autograd to compute the backward pass.
        cost.backward()

        num_parameters = len(self.parameters)
        # Update parameters
        gradient_parameters = []
        for index in range(0, num_parameters):
            gradient_parameters.append(self.parameters[index].grad.data)

        return gradient_parameters



# Path: lxmls/deep_learning/rnn.py
# class RNN(Model):
#     def __init__(self, **config):
# 
#         # CHECK THE PARAMETERS ARE VALID
#         self.sanity_checks(config)
# 
#         # OPTIONAL MODEL LOADING
#         model_folder = config.get('model_folder', None)
#         if model_folder is not None:
#             saved_config, loaded_parameters = self.load(model_folder)
#             # Note that if a config is given this is used instead of the saved
#             # one (must be consistent)
#             if config is None:
#                 config = saved_config
#         else:
#             loaded_parameters = None
# 
#         # Class variables
#         self.config = config
#         self.parameters = initialize_rnn_parameters(
#             config['input_size'],
#             config['embedding_size'],
#             config['hidden_size'],
#             config['output_size'],
#             loaded_parameters=loaded_parameters
#         )
# 
#     def sanity_checks(self, config):
# 
#         model_folder = config.get('model_folder', None)
# 
#         assert bool(config is None) or bool(model_folder is None), \
#             "Need to specify config, model_folder or both"
# 
#         if config is not None:
#             pass
# 
#         if model_folder is not None:
#             model_file = "%s/config.yml" % model_folder
#             assert os.path.isfile(model_file), "Need to provide %s" % model_file
# 
#     def load(self, model_folder):
#         """
#         Load model
#         """
# 
#         # Configuration un yaml format
#         config_file = "%s/config.yml" % model_folder
#         config = load_config(config_file)
# 
#         # Computation graph parameters as pickle file
#         parameter_file = "%s/parameters.pkl" % model_folder
#         loaded_parameters = load_parameters(parameter_file)
# 
#         return config, loaded_parameters
# 
#     def save(self, model_folder):
#         """
#         Save model
#         """
# 
#         # Create folder if it does not exist
#         if not os.path.isdir(model_folder):
#             os.mkdir(model_folder)
# 
#         # Configuration un yaml format
#         config_file = "%s/config.yml" % model_folder
#         save_config(config_file, self.config)
# 
#         # Computation graph parameters as pickle file
#         parameter_file = "%s/parameters.pkl" % model_folder
#         with open(parameter_file, 'wb') as fid:
#             pickle.dump(self.parameters, fid, pickle.HIGHEST_PROTOCOL)
# 
#     def plot_weights(self, show=True, aspect='auto'):
#         """
#         Plots the weights of the newtwork
# 
#         Use show = False to plot various models one after the other
#         """
#         import matplotlib.pyplot as plt
#         plt.figure()
#         for n in range(self.n_layers):
# 
#             # Get weights and bias
#             weight, bias = self.parameters[n]
# 
#             # Plot them
#             plt.subplot(2, self.n_layers, n+1)
#             plt.imshow(weight, aspect=aspect, interpolation='nearest')
#             plt.title('Layer %d Weight' % n)
#             plt.colorbar()
#             plt.subplot(2, self.n_layers, self.n_layers+(n+1))
#             plt.plot(bias)
#             plt.title('Layer %d Bias' % n)
#             plt.colorbar()
# 
#         if show:
#             plt.show()


# Path: lxmls/deep_learning/pytorch_models/rnn.py
import numpy as np
import scipy
import scipy.linalg
import torch
from torch.autograd import Variable
from torch.distributions import Categorical
from lxmls.deep_learning.rnn import RNN
from itertools import chain


    def backpropagation(self, input, output):
        """
        Computes the gradients of the network with respect to cross entropy
        error cost
        """

        # Ensure the type matches torch type
        output = Variable(torch.from_numpy(output).long())

        # Zero gradients
        for parameter in self.parameters:
            if parameter.grad is not None:
                parameter.grad.data.zero_()

        # Compute negative log-likelihood loss
        log_p_y = self._log_forward(input)
        cost = self.loss(log_p_y, output)
        # Use autograd to compute the backward pass.
        cost.backward()

        num_parameters = len(self.parameters)
        # Update parameters
        gradient_parameters = []
        for index in range(0, num_parameters):
            gradient_parameters.append(self.parameters[index].grad.data)

        return gradient_parameters



class FastPytorchRNN(RNN):