text: """
  tybalt/util/base.py
  2018 Gregory Way
  
  Base models used as abstracted base classes for front-facing tybalt/models.py
  """
  
  import numpy as np
  import pandas as pd
  
  import tensorflow as tf
  from keras import backend as K
  from keras.utils import plot_model
  
  
  class BaseModel():
      def __init__(self):
          pass
  
      def get_summary(self):
          self.full_model.summary()
  
      def visualize_architecture(self, output_file):
          # Visualize the connections of the custom VAE model
          plot_model(self.full_model, to_file=output_file)
  
      def visualize_training(self, output_file=None):
          # Visualize training performance
          history_df = pd.DataFrame(self.hist.history)
          ax = history_df.plot()
          ax.set_xlabel('Epochs')
          ax.set_ylabel('Loss')
          fig = ax.get_figure()
          if output_file:
              fig.savefig(output_file)
          else:
              fig.show()
  
      def get_decoder_weights(self):
          # build a generator that can sample from the learned distribution
          # can generate from any sampled z vector
          weights = []
          for layer in self.decoder.layers:
              weights.append(layer.get_weights())
          return weights
  
      def save_models(self, encoder_file, decoder_file):
          self.encoder.save(encoder_file)
          self.decoder.save(decoder_file)
  
  
  class VAE(BaseModel):
      def __init__(self):
          BaseModel.__init__(self)
  
      def _sampling(self, args):
          """
          Function for reparameterization trick to make model differentiable
          """
          # Function with args required for Keras Lambda function
          z_mean, z_log_var = args
  
          # Draw epsilon of the same shape from a standard normal distribution
          epsilon = K.random_normal(shape=tf.shape(z_mean), mean=0.,
                                    stddev=self.epsilon_std)
  
          # The latent vector is non-deterministic and differentiable
          # in respect to z_mean and z_log_var
          z = z_mean + K.exp(z_log_var / 2) * epsilon
          return z
  
      def initialize_model(self):
          """
          Helper function to run that builds and compiles Keras layers
          """
          self._build_encoder_layer()
          self._build_decoder_layer()
          self._compile_vae()
          self._connect_layers()
  
      def compress(self, df):
          # Encode rnaseq into the hidden/latent representation - and save output
          # a cVAE expects a list of [rnaseq_df, y_df]
          encoded_df = self.encoder.predict_on_batch(df)
  
          if self.model_name == 'cTybalt':
              named_index = df[0].index
          else:
              named_index = df.index
  
          encoded_df = pd.DataFrame(encoded_df,
                                    columns=range(1, self.latent_dim + 1),
                                    index=named_index)
          return encoded_df
  