text: ###############################################################################
  # Copyright (c) Microsoft Corporation. All rights reserved.
  # Licensed under the MIT License. See License.txt in the project root for
  # license information.
  ###############################################################################
  import os
  import sys
  import unittest
  import keras2onnx
  import onnx
  import numpy as np
  from keras2onnx.proto import keras
  from os.path import dirname, abspath
  sys.path.insert(0, os.path.join(dirname(abspath(__file__)), '../../tests/'))
  from test_utils import run_onnx_runtime
  from keras.layers.merge import _Merge
  
  Activation = keras.layers.Activation
  BatchNormalization = keras.layers.BatchNormalization
  Conv2D = keras.layers.Conv2D
  Dense = keras.layers.Dense
  Dropout = keras.layers.Dropout
  Embedding = keras.layers.Embedding
  Flatten = keras.layers.Flatten
  Input = keras.layers.Input
  LeakyReLU = keras.layers.LeakyReLU
  multiply = keras.layers.multiply
  Reshape = keras.layers.Reshape
  UpSampling2D = keras.layers.UpSampling2D
  ZeroPadding2D = keras.layers.ZeroPadding2D
  
  Sequential = keras.models.Sequential
  Model = keras.models.Model
  K = keras.backend
  
  class RandomWeightedAverage(_Merge):
      """Provides a (random) weighted average between real and generated image samples"""
      def _merge_function(self, inputs):
          alpha = K.random_uniform((32, 1, 1, 1))
          return (alpha * inputs[0]) + ((1 - alpha) * inputs[1])
  
  # From https://github.com/eriklindernoren/Keras-GAN/blob/master/wgan_gp/wgan_gp.py
  class WGANGP():
      def __init__(self):
          self.img_rows = 28
          self.img_cols = 28
          self.channels = 1
          self.img_shape = (self.img_rows, self.img_cols, self.channels)
          self.latent_dim = 100
  
          # Following parameter and optimizer set as recommended in paper
          self.n_critic = 5
  
          # Build the generator and critic
          self.generator = self.build_generator()
          self.critic = self.build_critic()
  
          #-------------------------------
          # Construct Computational Graph
          #       for the Critic
          #-------------------------------
  
          # Freeze generator's layers while training critic
          self.generator.trainable = False
  
          # Image input (real sample)
          real_img = Input(shape=self.img_shape)
  
          # Noise input
          z_disc = Input(shape=(self.latent_dim,))
          # Generate image based of noise (fake sample)
          fake_img = self.generator(z_disc)
  
          # Discriminator determines validity of the real and fake images
          fake = self.critic(fake_img)
          valid = self.critic(real_img)
  
          # Construct weighted average between real and fake images
          interpolated_img = RandomWeightedAverage()([real_img, fake_img])
          # Determine validity of weighted sample
          validity_interpolated = self.critic(interpolated_img)
  
          self.critic_model = Model(inputs=[real_img, z_disc],
                              outputs=[valid, fake, validity_interpolated])
          #-------------------------------
          # Construct Computational Graph
          #         for Generator
          #-------------------------------
  
          # For the generator we freeze the critic's layers
          self.critic.trainable = False
          self.generator.trainable = True
  
          # Sampled noise for input to generator
          z_gen = Input(shape=(self.latent_dim,))
          # Generate images based of noise
          img = self.generator(z_gen)
          # Discriminator determines validity
          valid = self.critic(img)
          # Defines generator model
          self.generator_model = Model(z_gen, valid)
  
  
      def build_generator(self):
  
          model = Sequential()
  
          model.add(Dense(128 * 7 * 7, activation="relu", input_dim=self.latent_dim))
          model.add(Reshape((7, 7, 128)))
          model.add(UpSampling2D())
          model.add(Conv2D(128, kernel_size=4, padding="same"))
          model.add(BatchNormalization(momentum=0.8))
          model.add(Activation("relu"))
          model.add(UpSampling2D())
          model.add(Conv2D(64, kernel_size=4, padding="same"))
          model.add(BatchNormalization(momentum=0.8))
          model.add(Activation("relu"))
          model.add(Conv2D(self.channels, kernel_size=4, padding="same"))
          model.add(Activation("tanh"))
  
          noise = Input(shape=(self.latent_dim,))
          img = model(noise)
  
          return Model(noise, img)
  
      def build_critic(self):
  
          model = Sequential()
  
          model.add(Conv2D(16, kernel_size=3, strides=2, input_shape=self.img_shape, padding="same"))
          model.add(LeakyReLU(alpha=0.2))
          model.add(Dropout(0.25))
          model.add(Conv2D(32, kernel_size=3, strides=2, padding="same"))
          model.add(ZeroPadding2D(padding=((0,1),(0,1))))
          model.add(BatchNormalization(momentum=0.8))
          model.add(LeakyReLU(alpha=0.2))
          model.add(Dropout(0.25))
          model.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
          model.add(BatchNormalization(momentum=0.8))
          model.add(LeakyReLU(alpha=0.2))
          model.add(Dropout(0.25))
          model.add(Conv2D(128, kernel_size=3, strides=1, padding="same"))
          model.add(BatchNormalization(momentum=0.8))
          model.add(LeakyReLU(alpha=0.2))
          model.add(Dropout(0.25))
          model.add(Flatten())
          model.add(Dense(1))
  
          img = Input(shape=self.img_shape)
          validity = model(img)
  
          return Model(img, validity)
  
  
  class TestWGANGP(unittest.TestCase):
  
      def setUp(self):
          self.model_files = []
  
      def tearDown(self):
          for fl in self.model_files:
              os.remove(fl)
  
      def test_WGANGP(self):
          keras_model = WGANGP().generator_model
          x = np.random.rand(5, 100).astype(np.float32)
          expected = keras_model.predict(x)
          onnx_model = keras2onnx.convert_keras(keras_model, keras_model.name)
          self.assertTrue(run_onnx_runtime(onnx_model.graph.name, onnx_model, x, expected, self.model_files))
  
  
  if __name__ == "__main__":
      unittest.main()
  